-
Notifications
You must be signed in to change notification settings - Fork 14
/
OWI.h
384 lines (355 loc) · 10 KB
/
OWI.h
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
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
/**
* @file OWI.h
* @version 1.2
*
* @section License
* Copyright (C) 2017, Mikael Patel
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
#ifndef OWI_H
#define OWI_H
#ifndef CHARBITS
#define CHARBITS 8
#endif
/**
* One Wire Interface (OWI) Bus Manager abstract class.
*/
class OWI {
public:
/** One Wire device identity ROM size in bytes. */
static const size_t ROM_MAX = 8;
/** One Wire device identity ROM size in bits. */
static const size_t ROMBITS = ROM_MAX * CHARBITS;
/**
* @override{OWI}
* Reset the one wire bus and check that at least one device is
* presence.
* @return true(1) if successful otherwise false(0).
*/
virtual bool reset() = 0;
/**
* @override{OWI}
* Read the given number of bits from the one wire bus. Default
* number of bits is CHARBITS (8).
* @param[in] bits to be read (default CHARBITS).
* @return value read.
*/
virtual uint8_t read(uint8_t bits = CHARBITS) = 0;
/**
* Read given number of bytes from one wire bus (device) to given
* buffer. Calculates 8-bit Cyclic Redundancy Check sum and return
* result of check.
* @param[in] buf buffer pointer.
* @param[in] count number of bytes to read.
* @return true(1) if check sum is correct otherwise false(0).
*/
bool read(void* buf, size_t count)
{
uint8_t* bp = (uint8_t*) buf;
uint8_t crc = 0;
while (count--) {
uint8_t value = read();
*bp++ = value;
crc = crc_update(crc, value);
}
return (crc == 0);
}
/**
* @override{OWI}
* Write the given value to the one wire bus. The bits are written
* from LSB to MSB.
* @param[in] value to write.
* @param[in] bits to be written (default CHARBITS).
*/
virtual void write(uint8_t value, uint8_t bits = CHARBITS) = 0;
/**
* Write the given command and given number of bytes from buffer to
* the one wire bus (device).
* @param[in] cmd command to write.
* @param[in] buf buffer pointer.
* @param[in] count number of bytes to write.
*/
void write(uint8_t cmd, const void* buf, size_t count)
{
write(cmd);
const uint8_t* bp = (const uint8_t*) buf;
while (count--) write(*bp++);
}
/**
* @override{OWI}
* Search (rom and alarm) support function. Reads 2-bits and writes
* given direction 1-bit value when discrepancy 0b00 read. Writes
* one(1) when 0b01 read, zero(0) on 0b10. Reading 0b11 is an error
* state.
* @param[in,out] dir bit to write when discrepancy read.
* @return 2-bits read and bit written.
*/
virtual int8_t triplet(uint8_t& dir)
{
switch (read(2)) {
case 0b00:
write(dir, 1);
return (0b00);
case 0b01:
write(1, 1);
dir = 1;
return (0b01);
case 0b10:
write(0, 1);
dir = 0;
return (0b10);
default:
return (0b11);
}
}
/**
* Standard 1-Wire ROM Commands.
*/
enum {
SEARCH_ROM = 0xF0, //!< Initiate device search.
READ_ROM = 0x33, //!< Read device family code and serial number.
MATCH_ROM = 0x55, //!< Select device with 64-bit rom code.
SKIP_ROM = 0xCC, //!< Broadcast or single device.
ALARM_SEARCH = 0xEC, //!< Initiate device alarm search.
LABEL_ROM = 0x15, //!< Set short address (8-bit).
MATCH_LABEL = 0x51 //!< Select device with 8-bit short address.
} __attribute__((packed));
/**
* Optimized Dallas/Maxim iButton 8-bit Cyclic Redundancy Check
* calculation. Polynomial: x^8 + x^5 + x^4 + 1 (0x8C).
* See http://www.maxim-ic.com/appnotes.cfm/appnote_number/27
* @param[in] crc cyclic redundancy check sum.
* @param[in] data to append.
* @return crc.
*/
static inline uint8_t crc_update(uint8_t crc, uint8_t data)
__attribute__((always_inline))
{
crc = crc ^ data;
for (uint8_t i = 0; i < 8; i++) {
if (crc & 0x01)
crc = (crc >> 1) ^ 0x8C;
else
crc >>= 1;
}
return (crc);
}
/**
* Optimized Dallas/Maxim iButton 8-bit Cyclic Redundancy Check
* calculation. Polynomial: x^8 + x^5 + x^4 + 1 (0x8C).
* @param[in] buf buffer pointer.
* @param[in] count number of bytes.
* @return crc.
*/
static inline uint8_t crc(const void* buf, size_t count)
{
const uint8_t* bp = (const uint8_t*) buf;
uint8_t crc = 0;
while (count--) crc = crc_update(crc, *bp++);
return (crc);
}
/**
* Optimized Dallas/Maxim iButton 8-bit Cyclic Redundancy Check
* calculation. Polynomial: x^8 + x^5 + x^4 + 1 (0x8C).
* @param[in] buf buffer pointer (program memory).
* @param[in] count number of bytes.
* @return crc.
*/
static inline uint8_t crc_P(const void* buf, size_t count)
{
const uint8_t* bp = (const uint8_t*) buf;
uint8_t crc = 0;
while (count--) crc = crc_update(crc, pgm_read_byte(bp++));
return (crc);
}
/** Search position and return values. */
enum {
FIRST = -1, //!< Start position of search.
ERROR = -1, //!< Error during search.
LAST = ROMBITS //!< Last position, search completed.
} __attribute__((packed));
/**
* Search device rom given the last position of discrepancy.
* Return position of difference or negative error code.
* @param[in] family code.
* @param[in] code device identity.
* @param[in] last position of discrepancy (default FIRST).
* @return position of difference or negative error code.
*/
int8_t search_rom(uint8_t family, uint8_t* code, int8_t last = FIRST)
{
do {
if (!reset()) return (ERROR);
write(SEARCH_ROM);
last = search(code, last);
if (last == ERROR) return (ERROR);
} while ((last != LAST) && (family != 0) && (code[0] != family));
if (family != 0 && code[0] != family) return (ERROR);
return (last);
}
/**
* Read device rom. This can only be used when there is only
* one device on the bus.
* @param[in] code device identity.
* @return true(1) if successful otherwise false(0).
*/
bool read_rom(uint8_t* code)
{
if (!reset()) return (false);
write(READ_ROM);
return (read(code, ROM_MAX));
}
/**
* Match device rom. Address the device with the rom code. Device
* specific function command should follow. May be used to verify
* rom code.
* @param[in] code device identity.
* @return true(1) if successful otherwise false(0).
*/
bool match_rom(uint8_t* code)
{
if (!reset()) return (false);
write(MATCH_ROM, code, ROM_MAX);
return (true);
}
/**
* Skip device rom for boardcast or single device access.
* Device specific function command should follow.
* @return true(1) if successful otherwise false(0).
*/
bool skip_rom()
{
if (!reset()) return (false);
write(SKIP_ROM);
return (true);
}
/**
* Search alarming device given the last position of discrepancy.
* @param[in] code device identity.
* @param[in] last position of discrepancy (default FIRST).
* @return position of difference or negative error code.
*/
int8_t alarm_search(uint8_t* code, int8_t last = FIRST)
{
if (!reset()) return (ERROR);
write(ALARM_SEARCH);
return (search(code, last));
}
/**
* Match device label. Address the device with the given label. Device
* specific function command should follow.
* @param[in] label device short address.
* @return true(1) if successful otherwise false(0).
*/
bool match_label(uint8_t label)
{
if (!reset()) return (false);
write(MATCH_LABEL);
write(label);
return (true);
}
/**
* One-Wire Interface (OWI) Device Driver abstract class.
*/
class Device {
public:
/**
* Construct One-Wire Interface (OWI) Device Driver with given bus
* and device address.
* @param[in] owi bus manager.
* @param[in] rom code (default NULL).
*/
Device(OWI& owi, const uint8_t* rom = NULL) :
m_owi(owi)
{
if (rom != NULL) this->rom(rom);
}
/**
* Set device rom code.
* @param[in] rom code.
*/
void rom(const uint8_t* rom)
{
uint8_t crc = 0;
for (size_t i = 0; i < ROM_MAX - 1; i++) {
uint8_t data = *rom++;
m_rom[i] = data;
crc = OWI::crc_update(crc, data);
}
m_rom[ROM_MAX - 1] = crc;
}
/**
* Set device rom code.
* @param[in] rom code in program memory.
*/
void rom_P(const uint8_t* rom)
{
uint8_t crc = 0;
for (size_t i = 0; i < ROM_MAX - 1; i++) {
uint8_t data = pgm_read_byte(rom++);
m_rom[i] = data;
crc = OWI::crc_update(crc, data);
}
m_rom[ROM_MAX - 1] = crc;
}
/**
* Get device rom code.
* @return rom code.
*/
uint8_t* rom()
{
return (m_rom);
}
protected:
/** One-Wire Bus Manager. */
OWI& m_owi;
/** Device rom idenity code. */
uint8_t m_rom[ROM_MAX];
};
protected:
/** Maximum number of reset retries. */
static const uint8_t RESET_RETRY_MAX = 4;
/**
* Search device rom given the last position of discrepancy and
* partial or full rom code.
* @param[in] code device identity rom.
* @param[in] last position of discrepancy (default FIRST).
* @return position of difference or negative error code.
*/
int8_t search(uint8_t* code, int8_t last = FIRST)
{
uint8_t pos = 0;
int8_t next = LAST;
for (uint8_t i = 0; i < 8; i++) {
uint8_t data = 0;
for (uint8_t j = 0; j < 8; j++) {
uint8_t dir = (pos == last) || ((pos < last) && (code[i] & (1 << j)));
switch (triplet(dir)) {
case 0b00:
if (pos == last)
last = FIRST;
else if (pos > last || (code[i] & (1 << j)) == 0)
next = pos;
break;
case 0b11:
return (ERROR);
}
data >>= 1;
if (dir) data |= 0x80;
pos += 1;
}
code[i] = data;
}
return (next);
}
};
#endif