-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathonewire.c
287 lines (230 loc) · 6.5 KB
/
onewire.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
/*
Access Dallas 1-Wire Devices with ATMEL AVRs
Author of the initial code: Peter Dannegger (danni(at)specs.de)
modified by Martin Thomas (mthomas(at)rhrk.uni-kl.de)
9/2004 - use of delay.h, optional bus configuration at runtime
10/2009 - additional delay in ow_bit_io for recovery
5/2010 - timing modifcations, additonal config-values and comments,
use of atomic.h macros, internal pull-up support
7/2010 - added method to skip recovery time after last bit transfered
via ow_command_skip_last_recovery
*/
#include <avr/io.h>
#include <util/delay.h>
#include <util/atomic.h>
#include "onewire.h"
#ifdef OW_ONE_BUS
#define OW_GET_IN() ( OW_IN & (1<<OW_PIN))
#define OW_OUT_LOW() ( OW_OUT &= (~(1 << OW_PIN)) )
#define OW_OUT_HIGH() ( OW_OUT |= (1 << OW_PIN) )
#define OW_DIR_IN() ( OW_DDR &= (~(1 << OW_PIN )) )
#define OW_DIR_OUT() ( OW_DDR |= (1 << OW_PIN) )
#else
/* set bus-config with ow_set_bus() */
uint8_t OW_PIN_MASK;
volatile uint8_t* OW_IN;
volatile uint8_t* OW_OUT;
volatile uint8_t* OW_DDR;
#define OW_GET_IN() ( *OW_IN & OW_PIN_MASK )
#define OW_OUT_LOW() ( *OW_OUT &= (uint8_t) ~OW_PIN_MASK )
#define OW_OUT_HIGH() ( *OW_OUT |= (uint8_t) OW_PIN_MASK )
#define OW_DIR_IN() ( *OW_DDR &= (uint8_t) ~OW_PIN_MASK )
#define OW_DIR_OUT() ( *OW_DDR |= (uint8_t) OW_PIN_MASK )
void ow_set_bus(volatile uint8_t* in,
volatile uint8_t* out,
volatile uint8_t* ddr,
uint8_t pin)
{
OW_DDR=ddr;
OW_OUT=out;
OW_IN=in;
OW_PIN_MASK = (1 << pin);
ow_reset();
}
#endif
uint8_t ow_input_pin_state()
{
return OW_GET_IN();
}
void ow_parasite_enable(void)
{
OW_OUT_HIGH();
OW_DIR_OUT();
}
void ow_parasite_disable(void)
{
OW_DIR_IN();
#if (!OW_USE_INTERNAL_PULLUP)
OW_OUT_LOW();
#endif
}
uint8_t ow_reset(void)
{
uint8_t err;
OW_OUT_LOW();
OW_DIR_OUT(); // pull OW-Pin low for 480us
_delay_us(480);
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
// set Pin as input - wait for clients to pull low
OW_DIR_IN(); // input
#if OW_USE_INTERNAL_PULLUP
OW_OUT_HIGH();
#endif
_delay_us(64); // was 66
err = OW_GET_IN(); // no presence detect
// if err!=0: nobody pulled to low, still high
}
// after a delay the clients should release the line
// and input-pin gets back to high by pull-up-resistor
_delay_us(480 - 64); // was 480-66
if( OW_GET_IN() == 0 ) {
err = 1; // short circuit, expected low but got high
}
return err;
}
/* Timing issue when using runtime-bus-selection (!OW_ONE_BUS):
The master should sample at the end of the 15-slot after initiating
the read-time-slot. The variable bus-settings need more
cycles than the constant ones so the delays had to be shortened
to achive a 15uS overall delay
Setting/clearing a bit in I/O Register needs 1 cyle in OW_ONE_BUS
but around 14 cyles in configureable bus (us-Delay is 4 cyles per uS) */
static uint8_t ow_bit_io_intern( uint8_t b, uint8_t with_parasite_enable )
{
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
#if OW_USE_INTERNAL_PULLUP
OW_OUT_LOW();
#endif
OW_DIR_OUT(); // drive bus low
_delay_us(2); // T_INT > 1usec accoding to timing-diagramm
if ( b ) {
OW_DIR_IN(); // to write "1" release bus, resistor pulls high
#if OW_USE_INTERNAL_PULLUP
OW_OUT_HIGH();
#endif
}
// "Output data from the DS18B20 is valid for 15usec after the falling
// edge that initiated the read time slot. Therefore, the master must
// release the bus and then sample the bus state within 15ussec from
// the start of the slot."
_delay_us(15-2-OW_CONF_DELAYOFFSET);
if( OW_GET_IN() == 0 ) {
b = 0; // sample at end of read-timeslot
}
_delay_us(60-15-2+OW_CONF_DELAYOFFSET);
#if OW_USE_INTERNAL_PULLUP
OW_OUT_HIGH();
#endif
OW_DIR_IN();
if ( with_parasite_enable ) {
ow_parasite_enable();
}
} /* ATOMIC_BLOCK */
_delay_us(OW_RECOVERY_TIME); // may be increased for longer wires
return b;
}
uint8_t ow_bit_io( uint8_t b )
{
return ow_bit_io_intern( b & 1, 0 );
}
uint8_t ow_byte_wr( uint8_t b )
{
uint8_t i = 8, j;
do {
j = ow_bit_io( b & 1 );
b >>= 1;
if( j ) {
b |= 0x80;
}
} while( --i );
return b;
}
uint8_t ow_byte_wr_with_parasite_enable( uint8_t b )
{
uint8_t i = 8, j;
do {
if ( i != 1 ) {
j = ow_bit_io_intern( b & 1, 0 );
} else {
j = ow_bit_io_intern( b & 1, 1 );
}
b >>= 1;
if( j ) {
b |= 0x80;
}
} while( --i );
return b;
}
uint8_t ow_byte_rd( void )
{
// read by sending only "1"s, so bus gets released
// after the init low-pulse in every slot
return ow_byte_wr( 0xFF );
}
uint8_t ow_rom_search( uint8_t diff, uint8_t *id )
{
uint8_t i, j, next_diff;
uint8_t b;
if( ow_reset() ) {
return OW_PRESENCE_ERR; // error, no device found <--- early exit!
}
ow_byte_wr( OW_SEARCH_ROM ); // ROM search command
next_diff = OW_LAST_DEVICE; // unchanged on last device
i = OW_ROMCODE_SIZE * 8; // 8 bytes
do {
j = 8; // 8 bits
do {
b = ow_bit_io( 1 ); // read bit
if( ow_bit_io( 1 ) ) { // read complement bit
if( b ) { // 0b11
return OW_DATA_ERR; // data error <--- early exit!
}
}
else {
if( !b ) { // 0b00 = 2 devices
if( diff > i || ((*id & 1) && diff != i) ) {
b = 1; // now 1
next_diff = i; // next pass 0
}
}
}
ow_bit_io( b ); // write bit
*id >>= 1;
if( b ) {
*id |= 0x80; // store bit
}
i--;
} while( --j );
id++; // next byte
} while( i );
return next_diff; // to continue search
}
static void ow_command_intern( uint8_t command, uint8_t *id, uint8_t with_parasite_enable )
{
uint8_t i;
ow_reset();
if( id ) {
ow_byte_wr( OW_MATCH_ROM ); // to a single device
i = OW_ROMCODE_SIZE;
do {
ow_byte_wr( *id );
id++;
} while( --i );
}
else {
ow_byte_wr( OW_SKIP_ROM ); // to all devices
}
if ( with_parasite_enable ) {
ow_byte_wr_with_parasite_enable( command );
} else {
ow_byte_wr( command );
}
}
void ow_command( uint8_t command, uint8_t *id )
{
ow_command_intern( command, id, 0);
}
void ow_command_with_parasite_enable( uint8_t command, uint8_t *id )
{
ow_command_intern( command, id, 1 );
}