diff options
Diffstat (limited to 'lib/chibios-contrib/os/hal/src/hal_onewire.c')
-rw-r--r-- | lib/chibios-contrib/os/hal/src/hal_onewire.c | 889 |
1 files changed, 889 insertions, 0 deletions
diff --git a/lib/chibios-contrib/os/hal/src/hal_onewire.c b/lib/chibios-contrib/os/hal/src/hal_onewire.c new file mode 100644 index 000000000..06e63e68d --- /dev/null +++ b/lib/chibios-contrib/os/hal/src/hal_onewire.c | |||
@@ -0,0 +1,889 @@ | |||
1 | /* | ||
2 | ChibiOS/RT - Copyright (C) 2014 Uladzimir Pylinsky aka barthess | ||
3 | |||
4 | Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | you may not use this file except in compliance with the License. | ||
6 | You may obtain a copy of the License at | ||
7 | |||
8 | http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | |||
10 | Unless required by applicable law or agreed to in writing, software | ||
11 | distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | See the License for the specific language governing permissions and | ||
14 | limitations under the License. | ||
15 | */ | ||
16 | |||
17 | /*===========================================================================*/ | ||
18 | /* Main ideas: */ | ||
19 | /*=========================================================================== | ||
20 | |||
21 | 1) switch PWM output pin to open drain mode. | ||
22 | 2) start 2 channels _simultaneously_. First (master channel) generates | ||
23 | pulses (read time slots) second (sample channel) generates interrupts | ||
24 | from where read pin function will be called. | ||
25 | |||
26 | - --------------------------------------- master channel generates pulses | ||
27 | | / . | ||
28 | --............................. <---------- slave (not)pulls down bus here | ||
29 | - -------------------------------- sample channel reads pad state | ||
30 | | | | ||
31 | ------------- | ||
32 | ^ | ||
33 | | read interrupt fires here | ||
34 | |||
35 | For data write it is only master channel needed. Data bit width updates | ||
36 | on every timer overflow event. | ||
37 | */ | ||
38 | |||
39 | /*===========================================================================*/ | ||
40 | /* General recommendations for strong pull usage */ | ||
41 | /*=========================================================================== | ||
42 | * 1) Use separate power rail instead of strong pull up whenever possible. | ||
43 | * Driver's strong pull up feature is very sensible to interrupt jitter. | ||
44 | * 2) Use specialized 1-wire bus master (DS2484 for example) if you are | ||
45 | * forced to handle bus requiring strong pull up feature. | ||
46 | */ | ||
47 | |||
48 | /** | ||
49 | * @file hal_onewire.c | ||
50 | * @brief 1-wire Driver code. | ||
51 | * | ||
52 | * @addtogroup onewire | ||
53 | * @{ | ||
54 | */ | ||
55 | |||
56 | #include "hal.h" | ||
57 | |||
58 | #if (HAL_USE_ONEWIRE == TRUE) || defined(__DOXYGEN__) | ||
59 | |||
60 | #include <string.h> | ||
61 | #include <limits.h> | ||
62 | |||
63 | /*===========================================================================*/ | ||
64 | /* Driver local definitions. */ | ||
65 | /*===========================================================================*/ | ||
66 | /** | ||
67 | * @brief 1MHz clock for PWM driver. | ||
68 | */ | ||
69 | #define ONEWIRE_PWM_FREQUENCY 1000000 | ||
70 | |||
71 | /** | ||
72 | * @brief Pulse width constants in microseconds. | ||
73 | * @details Inspired by Microchip's AN1199 | ||
74 | * "1-Wire® Communication with PIC® Microcontroller" | ||
75 | */ | ||
76 | #define ONEWIRE_ZERO_WIDTH 60 | ||
77 | #define ONEWIRE_ONE_WIDTH 6 | ||
78 | #define ONEWIRE_SAMPLE_WIDTH 15 | ||
79 | #define ONEWIRE_RECOVERY_WIDTH 10 | ||
80 | #define ONEWIRE_RESET_LOW_WIDTH 480 | ||
81 | #define ONEWIRE_RESET_SAMPLE_WIDTH 550 | ||
82 | #define ONEWIRE_RESET_TOTAL_WIDTH 960 | ||
83 | |||
84 | /** | ||
85 | * @brief Local function declarations. | ||
86 | */ | ||
87 | static void ow_reset_cb(PWMDriver *pwmp, onewireDriver *owp); | ||
88 | static void pwm_reset_cb(PWMDriver *pwmp); | ||
89 | static void ow_read_bit_cb(PWMDriver *pwmp, onewireDriver *owp); | ||
90 | static void pwm_read_bit_cb(PWMDriver *pwmp); | ||
91 | static void ow_write_bit_cb(PWMDriver *pwmp, onewireDriver *owp); | ||
92 | static void pwm_write_bit_cb(PWMDriver *pwmp); | ||
93 | #if ONEWIRE_USE_SEARCH_ROM | ||
94 | static void ow_search_rom_cb(PWMDriver *pwmp, onewireDriver *owp); | ||
95 | static void pwm_search_rom_cb(PWMDriver *pwmp); | ||
96 | #endif | ||
97 | |||
98 | /*===========================================================================*/ | ||
99 | /* Driver exported variables. */ | ||
100 | /*===========================================================================*/ | ||
101 | /** | ||
102 | * @brief 1-wire driver identifier. | ||
103 | */ | ||
104 | onewireDriver OWD1; | ||
105 | |||
106 | /*===========================================================================*/ | ||
107 | /* Driver local variables and types. */ | ||
108 | /*===========================================================================*/ | ||
109 | /** | ||
110 | * @brief Look up table for fast 1-wire CRC calculation | ||
111 | */ | ||
112 | static const uint8_t onewire_crc_table[256] = { | ||
113 | 0x0, 0x5e, 0xbc, 0xe2, 0x61, 0x3f, 0xdd, 0x83, | ||
114 | 0xc2, 0x9c, 0x7e, 0x20, 0xa3, 0xfd, 0x1f, 0x41, | ||
115 | 0x9d, 0xc3, 0x21, 0x7f, 0xfc, 0xa2, 0x40, 0x1e, | ||
116 | 0x5f, 0x1, 0xe3, 0xbd, 0x3e, 0x60, 0x82, 0xdc, | ||
117 | 0x23, 0x7d, 0x9f, 0xc1, 0x42, 0x1c, 0xfe, 0xa0, | ||
118 | 0xe1, 0xbf, 0x5d, 0x3, 0x80, 0xde, 0x3c, 0x62, | ||
119 | 0xbe, 0xe0, 0x2, 0x5c, 0xdf, 0x81, 0x63, 0x3d, | ||
120 | 0x7c, 0x22, 0xc0, 0x9e, 0x1d, 0x43, 0xa1, 0xff, | ||
121 | 0x46, 0x18, 0xfa, 0xa4, 0x27, 0x79, 0x9b, 0xc5, | ||
122 | 0x84, 0xda, 0x38, 0x66, 0xe5, 0xbb, 0x59, 0x7, | ||
123 | 0xdb, 0x85, 0x67, 0x39, 0xba, 0xe4, 0x6, 0x58, | ||
124 | 0x19, 0x47, 0xa5, 0xfb, 0x78, 0x26, 0xc4, 0x9a, | ||
125 | 0x65, 0x3b, 0xd9, 0x87, 0x4, 0x5a, 0xb8, 0xe6, | ||
126 | 0xa7, 0xf9, 0x1b, 0x45, 0xc6, 0x98, 0x7a, 0x24, | ||
127 | 0xf8, 0xa6, 0x44, 0x1a, 0x99, 0xc7, 0x25, 0x7b, | ||
128 | 0x3a, 0x64, 0x86, 0xd8, 0x5b, 0x5, 0xe7, 0xb9, | ||
129 | 0x8c, 0xd2, 0x30, 0x6e, 0xed, 0xb3, 0x51, 0xf, | ||
130 | 0x4e, 0x10, 0xf2, 0xac, 0x2f, 0x71, 0x93, 0xcd, | ||
131 | 0x11, 0x4f, 0xad, 0xf3, 0x70, 0x2e, 0xcc, 0x92, | ||
132 | 0xd3, 0x8d, 0x6f, 0x31, 0xb2, 0xec, 0xe, 0x50, | ||
133 | 0xaf, 0xf1, 0x13, 0x4d, 0xce, 0x90, 0x72, 0x2c, | ||
134 | 0x6d, 0x33, 0xd1, 0x8f, 0xc, 0x52, 0xb0, 0xee, | ||
135 | 0x32, 0x6c, 0x8e, 0xd0, 0x53, 0xd, 0xef, 0xb1, | ||
136 | 0xf0, 0xae, 0x4c, 0x12, 0x91, 0xcf, 0x2d, 0x73, | ||
137 | 0xca, 0x94, 0x76, 0x28, 0xab, 0xf5, 0x17, 0x49, | ||
138 | 0x8, 0x56, 0xb4, 0xea, 0x69, 0x37, 0xd5, 0x8b, | ||
139 | 0x57, 0x9, 0xeb, 0xb5, 0x36, 0x68, 0x8a, 0xd4, | ||
140 | 0x95, 0xcb, 0x29, 0x77, 0xf4, 0xaa, 0x48, 0x16, | ||
141 | 0xe9, 0xb7, 0x55, 0xb, 0x88, 0xd6, 0x34, 0x6a, | ||
142 | 0x2b, 0x75, 0x97, 0xc9, 0x4a, 0x14, 0xf6, 0xa8, | ||
143 | 0x74, 0x2a, 0xc8, 0x96, 0x15, 0x4b, 0xa9, 0xf7, | ||
144 | 0xb6, 0xe8, 0xa, 0x54, 0xd7, 0x89, 0x6b, 0x35 | ||
145 | }; | ||
146 | |||
147 | /*===========================================================================*/ | ||
148 | /* Driver local functions. */ | ||
149 | /*===========================================================================*/ | ||
150 | /** | ||
151 | * @brief Put bus in idle mode. | ||
152 | */ | ||
153 | static void ow_bus_idle(onewireDriver *owp) { | ||
154 | #if defined(STM32F1XX) | ||
155 | palSetPadMode(owp->config->port, owp->config->pad, | ||
156 | owp->config->pad_mode_idle); | ||
157 | #endif | ||
158 | pwmStop(owp->config->pwmd); | ||
159 | } | ||
160 | |||
161 | /** | ||
162 | * @brief Put bus in active mode. | ||
163 | */ | ||
164 | static void ow_bus_active(onewireDriver *owp) { | ||
165 | pwmStart(owp->config->pwmd, owp->config->pwmcfg); | ||
166 | #if defined(STM32F1XX) | ||
167 | palSetPadMode(owp->config->port, owp->config->pad, | ||
168 | owp->config->pad_mode_active); | ||
169 | #endif | ||
170 | } | ||
171 | |||
172 | /** | ||
173 | * @brief Function performing read of single bit. | ||
174 | * @note It must be callable from any context. | ||
175 | */ | ||
176 | static ioline_t ow_read_bit(onewireDriver *owp) { | ||
177 | #if ONEWIRE_SYNTH_SEARCH_TEST | ||
178 | (void)owp; | ||
179 | return _synth_ow_read_bit(); | ||
180 | #else | ||
181 | return palReadPad(owp->config->port, owp->config->pad); | ||
182 | #endif | ||
183 | } | ||
184 | |||
185 | /** | ||
186 | * @brief PWM adapter | ||
187 | */ | ||
188 | static void pwm_reset_cb(PWMDriver *pwmp) { | ||
189 | ow_reset_cb(pwmp, &OWD1); | ||
190 | } | ||
191 | |||
192 | /** | ||
193 | * @brief PWM adapter | ||
194 | */ | ||
195 | static void pwm_read_bit_cb(PWMDriver *pwmp) { | ||
196 | ow_read_bit_cb(pwmp, &OWD1); | ||
197 | } | ||
198 | |||
199 | /** | ||
200 | * @brief PWM adapter | ||
201 | */ | ||
202 | static void pwm_write_bit_cb(PWMDriver *pwmp) { | ||
203 | ow_write_bit_cb(pwmp, &OWD1); | ||
204 | } | ||
205 | |||
206 | #if ONEWIRE_USE_SEARCH_ROM | ||
207 | /** | ||
208 | * @brief PWM adapter | ||
209 | */ | ||
210 | static void pwm_search_rom_cb(PWMDriver *pwmp) { | ||
211 | ow_search_rom_cb(pwmp, &OWD1); | ||
212 | } | ||
213 | #endif /* ONEWIRE_USE_SEARCH_ROM */ | ||
214 | |||
215 | /** | ||
216 | * @brief Write bit routine. | ||
217 | * @details Switch PWM channel to 'width' or 'narrow' pulse depending | ||
218 | * on value of bit need to be transmitted. | ||
219 | * | ||
220 | * @param[in] owp pointer to the @p onewireDriver object | ||
221 | * @param[in] bit value to be written | ||
222 | * | ||
223 | * @notapi | ||
224 | */ | ||
225 | static void ow_write_bit_I(onewireDriver *owp, ioline_t bit) { | ||
226 | #if ONEWIRE_SYNTH_SEARCH_TEST | ||
227 | _synth_ow_write_bit(owp, bit); | ||
228 | #else | ||
229 | osalSysLockFromISR(); | ||
230 | if (0 == bit) { | ||
231 | pwmEnableChannelI(owp->config->pwmd, owp->config->master_channel, | ||
232 | ONEWIRE_ZERO_WIDTH); | ||
233 | } | ||
234 | else { | ||
235 | pwmEnableChannelI(owp->config->pwmd, owp->config->master_channel, | ||
236 | ONEWIRE_ONE_WIDTH); | ||
237 | } | ||
238 | osalSysUnlockFromISR(); | ||
239 | #endif | ||
240 | } | ||
241 | |||
242 | /** | ||
243 | * @brief 1-wire reset pulse callback. | ||
244 | * @note Must be called from PWM's ISR. | ||
245 | * | ||
246 | * @param[in] pwmp pointer to the @p PWMDriver object | ||
247 | * @param[in] owp pointer to the @p onewireDriver object | ||
248 | * | ||
249 | * @notapi | ||
250 | */ | ||
251 | static void ow_reset_cb(PWMDriver *pwmp, onewireDriver *owp) { | ||
252 | |||
253 | owp->reg.slave_present = (PAL_LOW == ow_read_bit(owp)); | ||
254 | osalSysLockFromISR(); | ||
255 | pwmDisableChannelI(pwmp, owp->config->sample_channel); | ||
256 | osalThreadResumeI(&owp->thread, MSG_OK); | ||
257 | osalSysUnlockFromISR(); | ||
258 | } | ||
259 | |||
260 | /** | ||
261 | * @brief 1-wire read bit callback. | ||
262 | * @note Must be called from PWM's ISR. | ||
263 | * | ||
264 | * @param[in] pwmp pointer to the @p PWMDriver object | ||
265 | * @param[in] owp pointer to the @p onewireDriver object | ||
266 | * | ||
267 | * @notapi | ||
268 | */ | ||
269 | static void ow_read_bit_cb(PWMDriver *pwmp, onewireDriver *owp) { | ||
270 | |||
271 | if (true == owp->reg.final_timeslot) { | ||
272 | osalSysLockFromISR(); | ||
273 | pwmDisableChannelI(pwmp, owp->config->sample_channel); | ||
274 | osalThreadResumeI(&owp->thread, MSG_OK); | ||
275 | osalSysUnlockFromISR(); | ||
276 | return; | ||
277 | } | ||
278 | else { | ||
279 | *owp->buf |= ow_read_bit(owp) << owp->reg.bit; | ||
280 | owp->reg.bit++; | ||
281 | if (8 == owp->reg.bit) { | ||
282 | owp->reg.bit = 0; | ||
283 | owp->buf++; | ||
284 | owp->reg.bytes--; | ||
285 | if (0 == owp->reg.bytes) { | ||
286 | owp->reg.final_timeslot = true; | ||
287 | osalSysLockFromISR(); | ||
288 | /* Only master channel must be stopped here. | ||
289 | Sample channel will be stopped in next ISR call. | ||
290 | It is still needed to generate final interrupt. */ | ||
291 | pwmDisableChannelI(pwmp, owp->config->master_channel); | ||
292 | osalSysUnlockFromISR(); | ||
293 | } | ||
294 | } | ||
295 | } | ||
296 | } | ||
297 | |||
298 | /** | ||
299 | * @brief 1-wire bit transmission callback. | ||
300 | * @note Must be called from PWM's ISR. | ||
301 | * | ||
302 | * @param[in] pwmp pointer to the @p PWMDriver object | ||
303 | * @param[in] owp pointer to the @p onewireDriver object | ||
304 | * | ||
305 | * @notapi | ||
306 | */ | ||
307 | static void ow_write_bit_cb(PWMDriver *pwmp, onewireDriver *owp) { | ||
308 | |||
309 | if (8 == owp->reg.bit) { | ||
310 | owp->buf++; | ||
311 | owp->reg.bit = 0; | ||
312 | owp->reg.bytes--; | ||
313 | |||
314 | if (0 == owp->reg.bytes) { | ||
315 | osalSysLockFromISR(); | ||
316 | pwmDisableChannelI(pwmp, owp->config->master_channel); | ||
317 | osalSysUnlockFromISR(); | ||
318 | /* used to prevent premature timer stop from userspace */ | ||
319 | owp->reg.final_timeslot = true; | ||
320 | return; | ||
321 | } | ||
322 | } | ||
323 | |||
324 | /* wait until timer generate last pulse */ | ||
325 | if (true == owp->reg.final_timeslot) { | ||
326 | #if ONEWIRE_USE_STRONG_PULLUP | ||
327 | if (owp->reg.need_pullup) { | ||
328 | owp->reg.state = ONEWIRE_PULL_UP; | ||
329 | owp->config->pullup_assert(); | ||
330 | owp->reg.need_pullup = false; | ||
331 | } | ||
332 | #endif | ||
333 | |||
334 | osalSysLockFromISR(); | ||
335 | osalThreadResumeI(&owp->thread, MSG_OK); | ||
336 | osalSysUnlockFromISR(); | ||
337 | return; | ||
338 | } | ||
339 | |||
340 | ow_write_bit_I(owp, (*owp->buf >> owp->reg.bit) & 1); | ||
341 | owp->reg.bit++; | ||
342 | } | ||
343 | |||
344 | #if ONEWIRE_USE_SEARCH_ROM | ||
345 | /** | ||
346 | * @brief Helper function for collision handler | ||
347 | * | ||
348 | * @param[in] sr pointer to the @p onewire_search_rom_t helper structure | ||
349 | * @param[in] bit discovered bit to be stored in helper structure | ||
350 | */ | ||
351 | static void store_bit(onewire_search_rom_t *sr, uint8_t bit) { | ||
352 | |||
353 | size_t rb = sr->reg.rombit; | ||
354 | |||
355 | sr->retbuf[rb / CHAR_BIT] |= bit << (rb % CHAR_BIT); | ||
356 | sr->reg.rombit++; | ||
357 | } | ||
358 | |||
359 | /** | ||
360 | * @brief Helper function for collision handler | ||
361 | * @details Extract bit from previous search path. | ||
362 | * | ||
363 | * @param[in] path pointer to the array with previous path stored in | ||
364 | * 'search ROM' helper structure | ||
365 | * @param[in] bit number of bit [0..63] | ||
366 | */ | ||
367 | static uint8_t extract_path_bit(const uint8_t *path, size_t bit) { | ||
368 | |||
369 | return (path[bit / CHAR_BIT] >> (bit % CHAR_BIT)) & 1; | ||
370 | } | ||
371 | |||
372 | /** | ||
373 | * @brief Collision handler for 'search ROM' procedure. | ||
374 | * @details You can find algorithm details in APPNOTE 187 | ||
375 | * "1-Wire Search Algorithm" from Maxim | ||
376 | * | ||
377 | * @param[in,out] sr pointer to the @p onewire_search_rom_t helper structure | ||
378 | */ | ||
379 | static uint8_t collision_handler(onewire_search_rom_t *sr) { | ||
380 | |||
381 | uint8_t bit; | ||
382 | |||
383 | switch(sr->reg.search_iter) { | ||
384 | case ONEWIRE_SEARCH_ROM_NEXT: | ||
385 | if ((int)sr->reg.rombit < sr->last_zero_branch) { | ||
386 | bit = extract_path_bit(sr->prev_path, sr->reg.rombit); | ||
387 | if (0 == bit) { | ||
388 | sr->prev_zero_branch = sr->reg.rombit; | ||
389 | sr->reg.result = ONEWIRE_SEARCH_ROM_SUCCESS; | ||
390 | } | ||
391 | store_bit(sr, bit); | ||
392 | return bit; | ||
393 | } | ||
394 | else if ((int)sr->reg.rombit == sr->last_zero_branch) { | ||
395 | sr->last_zero_branch = sr->prev_zero_branch; | ||
396 | store_bit(sr, 1); | ||
397 | return 1; | ||
398 | } | ||
399 | else { | ||
400 | /* found next branch some levels deeper */ | ||
401 | sr->prev_zero_branch = sr->last_zero_branch; | ||
402 | sr->last_zero_branch = sr->reg.rombit; | ||
403 | store_bit(sr, 0); | ||
404 | sr->reg.result = ONEWIRE_SEARCH_ROM_SUCCESS; | ||
405 | return 0; | ||
406 | } | ||
407 | break; | ||
408 | |||
409 | case ONEWIRE_SEARCH_ROM_FIRST: | ||
410 | /* always take 0-branch */ | ||
411 | sr->prev_zero_branch = sr->last_zero_branch; | ||
412 | sr->last_zero_branch = sr->reg.rombit; | ||
413 | store_bit(sr, 0); | ||
414 | sr->reg.result = ONEWIRE_SEARCH_ROM_SUCCESS; | ||
415 | return 0; | ||
416 | break; | ||
417 | |||
418 | default: | ||
419 | osalSysHalt("Unhandled case"); | ||
420 | return 0; /* warning supressor */ | ||
421 | break; | ||
422 | } | ||
423 | } | ||
424 | |||
425 | /** | ||
426 | * @brief 1-wire search ROM callback. | ||
427 | * @note Must be called from PWM's ISR. | ||
428 | * | ||
429 | * @param[in] pwmp pointer to the @p PWMDriver object | ||
430 | * @param[in] owp pointer to the @p onewireDriver object | ||
431 | * | ||
432 | * @notapi | ||
433 | */ | ||
434 | static void ow_search_rom_cb(PWMDriver *pwmp, onewireDriver *owp) { | ||
435 | |||
436 | onewire_search_rom_t *sr = &owp->search_rom; | ||
437 | |||
438 | if (0 == sr->reg.bit_step) { /* read direct bit */ | ||
439 | sr->reg.bit_buf |= ow_read_bit(owp); | ||
440 | sr->reg.bit_step++; | ||
441 | } | ||
442 | else if (1 == sr->reg.bit_step) { /* read complement bit */ | ||
443 | sr->reg.bit_buf |= ow_read_bit(owp) << 1; | ||
444 | sr->reg.bit_step++; | ||
445 | switch(sr->reg.bit_buf){ | ||
446 | case 0b11: | ||
447 | /* no one device on bus or any other fail happened */ | ||
448 | sr->reg.result = ONEWIRE_SEARCH_ROM_ERROR; | ||
449 | goto THE_END; | ||
450 | break; | ||
451 | case 0b01: | ||
452 | /* all slaves have 1 in this position */ | ||
453 | store_bit(sr, 1); | ||
454 | ow_write_bit_I(owp, 1); | ||
455 | break; | ||
456 | case 0b10: | ||
457 | /* all slaves have 0 in this position */ | ||
458 | store_bit(sr, 0); | ||
459 | ow_write_bit_I(owp, 0); | ||
460 | break; | ||
461 | case 0b00: | ||
462 | /* collision */ | ||
463 | sr->reg.single_device = false; | ||
464 | ow_write_bit_I(owp, collision_handler(sr)); | ||
465 | break; | ||
466 | } | ||
467 | } | ||
468 | else { /* start next step */ | ||
469 | #if !ONEWIRE_SYNTH_SEARCH_TEST | ||
470 | ow_write_bit_I(owp, 1); | ||
471 | #endif | ||
472 | sr->reg.bit_step = 0; | ||
473 | sr->reg.bit_buf = 0; | ||
474 | } | ||
475 | |||
476 | /* one ROM successfully discovered */ | ||
477 | if (64 == sr->reg.rombit) { | ||
478 | sr->reg.devices_found++; | ||
479 | sr->reg.search_iter = ONEWIRE_SEARCH_ROM_NEXT; | ||
480 | if (true == sr->reg.single_device) | ||
481 | sr->reg.result = ONEWIRE_SEARCH_ROM_LAST; | ||
482 | goto THE_END; | ||
483 | } | ||
484 | return; /* next search bit iteration */ | ||
485 | |||
486 | THE_END: | ||
487 | #if ONEWIRE_SYNTH_SEARCH_TEST | ||
488 | (void)pwmp; | ||
489 | return; | ||
490 | #else | ||
491 | osalSysLockFromISR(); | ||
492 | pwmDisableChannelI(pwmp, owp->config->master_channel); | ||
493 | pwmDisableChannelI(pwmp, owp->config->sample_channel); | ||
494 | osalThreadResumeI(&(owp)->thread, MSG_OK); | ||
495 | osalSysUnlockFromISR(); | ||
496 | #endif | ||
497 | } | ||
498 | |||
499 | /** | ||
500 | * @brief Helper function. Initialize structures required by 'search ROM'. | ||
501 | * @details Early reset. Call it once before 'search ROM' routine. | ||
502 | * | ||
503 | * @param[in] sr pointer to the @p onewire_search_rom_t helper structure | ||
504 | */ | ||
505 | static void search_clean_start(onewire_search_rom_t *sr) { | ||
506 | |||
507 | sr->reg.single_device = true; /* presume simplest way at beginning */ | ||
508 | sr->reg.result = ONEWIRE_SEARCH_ROM_LAST; | ||
509 | sr->reg.search_iter = ONEWIRE_SEARCH_ROM_FIRST; | ||
510 | sr->retbuf = NULL; | ||
511 | sr->reg.devices_found = 0; | ||
512 | memset(sr->prev_path, 0, 8); | ||
513 | |||
514 | sr->reg.rombit = 0; | ||
515 | sr->reg.bit_step = 0; | ||
516 | sr->reg.bit_buf = 0; | ||
517 | sr->last_zero_branch = -1; | ||
518 | sr->prev_zero_branch = -1; | ||
519 | } | ||
520 | |||
521 | /** | ||
522 | * @brief Helper function. Prepare structures required by 'search ROM'. | ||
523 | * | ||
524 | * @param[in] sr pointer to the @p onewire_search_rom_t helper structure | ||
525 | */ | ||
526 | static void search_clean_iteration(onewire_search_rom_t *sr) { | ||
527 | |||
528 | sr->reg.rombit = 0; | ||
529 | sr->reg.bit_step = 0; | ||
530 | sr->reg.bit_buf = 0; | ||
531 | sr->reg.result = ONEWIRE_SEARCH_ROM_LAST; | ||
532 | } | ||
533 | #endif /* ONEWIRE_USE_SEARCH_ROM */ | ||
534 | |||
535 | /*===========================================================================*/ | ||
536 | /* Driver exported functions. */ | ||
537 | /*===========================================================================*/ | ||
538 | |||
539 | /** | ||
540 | * @brief Calculates 1-wire CRC. | ||
541 | * | ||
542 | * @param[in] buf pointer to the data buffer | ||
543 | * @param[in] len lenght of data buffer | ||
544 | * | ||
545 | * @init | ||
546 | */ | ||
547 | uint8_t onewireCRC(const uint8_t *buf, size_t len) { | ||
548 | uint8_t ret = 0; | ||
549 | size_t i; | ||
550 | |||
551 | for (i=0; i<len; i++) | ||
552 | ret = onewire_crc_table[ret ^ buf[i]]; | ||
553 | |||
554 | return ret; | ||
555 | } | ||
556 | |||
557 | /** | ||
558 | * @brief Initializes @p onewireDriver structure. | ||
559 | * | ||
560 | * @param[out] owp pointer to the @p onewireDriver object | ||
561 | * | ||
562 | * @init | ||
563 | */ | ||
564 | void onewireObjectInit(onewireDriver *owp) { | ||
565 | |||
566 | osalDbgCheck(NULL != owp); | ||
567 | |||
568 | owp->config = NULL; | ||
569 | owp->reg.slave_present = false; | ||
570 | owp->reg.state = ONEWIRE_STOP; | ||
571 | owp->thread = NULL; | ||
572 | |||
573 | owp->reg.bytes = 0; | ||
574 | owp->reg.bit = 0; | ||
575 | owp->reg.final_timeslot = false; | ||
576 | owp->buf = NULL; | ||
577 | |||
578 | #if ONEWIRE_USE_STRONG_PULLUP | ||
579 | owp->reg.need_pullup = false; | ||
580 | #endif | ||
581 | } | ||
582 | |||
583 | /** | ||
584 | * @brief Configures and activates the 1-wire driver. | ||
585 | * | ||
586 | * @param[in] owp pointer to the @p onewireDriver object | ||
587 | * @param[in] config pointer to the @p onewireConfig object | ||
588 | * | ||
589 | * @api | ||
590 | */ | ||
591 | void onewireStart(onewireDriver *owp, const onewireConfig *config) { | ||
592 | |||
593 | osalDbgCheck((NULL != owp) && (NULL != config)); | ||
594 | osalDbgAssert(PWM_STOP == config->pwmd->state, | ||
595 | "PWM will be started by onewire driver internally"); | ||
596 | osalDbgAssert(ONEWIRE_STOP == owp->reg.state, "Invalid state"); | ||
597 | #if ONEWIRE_USE_STRONG_PULLUP | ||
598 | osalDbgCheck((NULL != config->pullup_assert) && | ||
599 | (NULL != config->pullup_release)); | ||
600 | #endif | ||
601 | |||
602 | owp->config = config; | ||
603 | owp->config->pwmcfg->frequency = ONEWIRE_PWM_FREQUENCY; | ||
604 | owp->config->pwmcfg->period = ONEWIRE_RESET_TOTAL_WIDTH; | ||
605 | |||
606 | #if !defined(STM32F1XX) | ||
607 | palSetPadMode(owp->config->port, owp->config->pad, | ||
608 | owp->config->pad_mode_active); | ||
609 | #endif | ||
610 | ow_bus_idle(owp); | ||
611 | owp->reg.state = ONEWIRE_READY; | ||
612 | } | ||
613 | |||
614 | /** | ||
615 | * @brief Deactivates the UART peripheral. | ||
616 | * | ||
617 | * @param[in] owp pointer to the @p onewireDriver object | ||
618 | * | ||
619 | * @api | ||
620 | */ | ||
621 | void onewireStop(onewireDriver *owp) { | ||
622 | osalDbgCheck(NULL != owp); | ||
623 | #if ONEWIRE_USE_STRONG_PULLUP | ||
624 | owp->config->pullup_release(); | ||
625 | #endif | ||
626 | ow_bus_idle(owp); | ||
627 | pwmStop(owp->config->pwmd); | ||
628 | owp->config = NULL; | ||
629 | owp->reg.state = ONEWIRE_STOP; | ||
630 | } | ||
631 | |||
632 | /** | ||
633 | * @brief Generate reset pulse on bus. | ||
634 | * | ||
635 | * @param[in] owp pointer to the @p onewireDriver object | ||
636 | * | ||
637 | * @return Bool flag denoting device presence. | ||
638 | * @retval true There is at least one device on bus. | ||
639 | */ | ||
640 | bool onewireReset(onewireDriver *owp) { | ||
641 | PWMDriver *pwmd; | ||
642 | PWMConfig *pwmcfg; | ||
643 | size_t mch, sch; | ||
644 | |||
645 | osalDbgCheck(NULL != owp); | ||
646 | osalDbgAssert(owp->reg.state == ONEWIRE_READY, "Invalid state"); | ||
647 | |||
648 | /* short circuit on bus or any other device transmit data */ | ||
649 | if (PAL_LOW == ow_read_bit(owp)) | ||
650 | return false; | ||
651 | |||
652 | pwmd = owp->config->pwmd; | ||
653 | pwmcfg = owp->config->pwmcfg; | ||
654 | mch = owp->config->master_channel; | ||
655 | sch = owp->config->sample_channel; | ||
656 | |||
657 | |||
658 | pwmcfg->period = ONEWIRE_RESET_LOW_WIDTH + ONEWIRE_RESET_SAMPLE_WIDTH; | ||
659 | pwmcfg->callback = NULL; | ||
660 | pwmcfg->channels[mch].callback = NULL; | ||
661 | pwmcfg->channels[mch].mode = owp->config->pwmmode; | ||
662 | pwmcfg->channels[sch].callback = pwm_reset_cb; | ||
663 | pwmcfg->channels[sch].mode = PWM_OUTPUT_DISABLED; | ||
664 | |||
665 | ow_bus_active(owp); | ||
666 | |||
667 | osalSysLock(); | ||
668 | pwmEnableChannelI(pwmd, mch, ONEWIRE_RESET_LOW_WIDTH); | ||
669 | pwmEnableChannelI(pwmd, sch, ONEWIRE_RESET_SAMPLE_WIDTH); | ||
670 | pwmEnableChannelNotificationI(pwmd, sch); | ||
671 | osalThreadSuspendS(&owp->thread); | ||
672 | osalSysUnlock(); | ||
673 | |||
674 | ow_bus_idle(owp); | ||
675 | |||
676 | /* wait until slave release bus to discriminate short circuit condition */ | ||
677 | osalThreadSleepMicroseconds(500); | ||
678 | return (PAL_HIGH == ow_read_bit(owp)) && (true == owp->reg.slave_present); | ||
679 | } | ||
680 | |||
681 | /** | ||
682 | * @brief Read some bytes from slave device. | ||
683 | * | ||
684 | * @param[in] owp pointer to the @p onewireDriver object | ||
685 | * @param[out] rxbuf pointer to the buffer for read data | ||
686 | * @param[in] rxbytes amount of data to be received | ||
687 | */ | ||
688 | void onewireRead(onewireDriver *owp, uint8_t *rxbuf, size_t rxbytes) { | ||
689 | PWMDriver *pwmd; | ||
690 | PWMConfig *pwmcfg; | ||
691 | size_t mch, sch; | ||
692 | |||
693 | osalDbgCheck((NULL != owp) && (NULL != rxbuf)); | ||
694 | osalDbgCheck((rxbytes > 0) && (rxbytes <= ONEWIRE_MAX_TRANSACTION_LEN)); | ||
695 | osalDbgAssert(owp->reg.state == ONEWIRE_READY, "Invalid state"); | ||
696 | |||
697 | /* Buffer zeroing. This is important because of driver collects | ||
698 | bits using |= operation.*/ | ||
699 | memset(rxbuf, 0, rxbytes); | ||
700 | |||
701 | pwmd = owp->config->pwmd; | ||
702 | pwmcfg = owp->config->pwmcfg; | ||
703 | mch = owp->config->master_channel; | ||
704 | sch = owp->config->sample_channel; | ||
705 | |||
706 | owp->reg.bit = 0; | ||
707 | owp->reg.final_timeslot = false; | ||
708 | owp->buf = rxbuf; | ||
709 | owp->reg.bytes = rxbytes; | ||
710 | |||
711 | pwmcfg->period = ONEWIRE_ZERO_WIDTH + ONEWIRE_RECOVERY_WIDTH; | ||
712 | pwmcfg->callback = NULL; | ||
713 | pwmcfg->channels[mch].callback = NULL; | ||
714 | pwmcfg->channels[mch].mode = owp->config->pwmmode; | ||
715 | pwmcfg->channels[sch].callback = pwm_read_bit_cb; | ||
716 | pwmcfg->channels[sch].mode = PWM_OUTPUT_DISABLED; | ||
717 | |||
718 | ow_bus_active(owp); | ||
719 | osalSysLock(); | ||
720 | pwmEnableChannelI(pwmd, mch, ONEWIRE_ONE_WIDTH); | ||
721 | pwmEnableChannelI(pwmd, sch, ONEWIRE_SAMPLE_WIDTH); | ||
722 | pwmEnableChannelNotificationI(pwmd, sch); | ||
723 | osalThreadSuspendS(&owp->thread); | ||
724 | osalSysUnlock(); | ||
725 | |||
726 | ow_bus_idle(owp); | ||
727 | } | ||
728 | |||
729 | /** | ||
730 | * @brief Write some bytes to slave device. | ||
731 | * | ||
732 | * @param[in] owp pointer to the @p onewireDriver object | ||
733 | * @param[in] txbuf pointer to the buffer with data to be written | ||
734 | * @param[in] txbytes amount of data to be written | ||
735 | * @param[in] pullup_time how long strong pull up must be activated. Set | ||
736 | * it to 0 if not needed. | ||
737 | */ | ||
738 | void onewireWrite(onewireDriver *owp, uint8_t *txbuf, | ||
739 | size_t txbytes, systime_t pullup_time) { | ||
740 | PWMDriver *pwmd; | ||
741 | PWMConfig *pwmcfg; | ||
742 | size_t mch, sch; | ||
743 | |||
744 | osalDbgCheck((NULL != owp) && (NULL != txbuf)); | ||
745 | osalDbgCheck((txbytes > 0) && (txbytes <= ONEWIRE_MAX_TRANSACTION_LEN)); | ||
746 | osalDbgAssert(owp->reg.state == ONEWIRE_READY, "Invalid state"); | ||
747 | #if !ONEWIRE_USE_STRONG_PULLUP | ||
748 | osalDbgAssert(0 == pullup_time, | ||
749 | "Non zero time is valid only when strong pull enabled"); | ||
750 | #endif | ||
751 | |||
752 | pwmd = owp->config->pwmd; | ||
753 | pwmcfg = owp->config->pwmcfg; | ||
754 | mch = owp->config->master_channel; | ||
755 | sch = owp->config->sample_channel; | ||
756 | |||
757 | owp->buf = txbuf; | ||
758 | owp->reg.bit = 0; | ||
759 | owp->reg.final_timeslot = false; | ||
760 | owp->reg.bytes = txbytes; | ||
761 | |||
762 | pwmcfg->period = ONEWIRE_ZERO_WIDTH + ONEWIRE_RECOVERY_WIDTH; | ||
763 | pwmcfg->callback = pwm_write_bit_cb; | ||
764 | pwmcfg->channels[mch].callback = NULL; | ||
765 | pwmcfg->channels[mch].mode = owp->config->pwmmode; | ||
766 | pwmcfg->channels[sch].callback = NULL; | ||
767 | pwmcfg->channels[sch].mode = PWM_OUTPUT_DISABLED; | ||
768 | |||
769 | #if ONEWIRE_USE_STRONG_PULLUP | ||
770 | if (pullup_time > 0) { | ||
771 | owp->reg.state = ONEWIRE_PULL_UP; | ||
772 | owp->reg.need_pullup = true; | ||
773 | } | ||
774 | #endif | ||
775 | |||
776 | ow_bus_active(owp); | ||
777 | osalSysLock(); | ||
778 | pwmEnablePeriodicNotificationI(pwmd); | ||
779 | osalThreadSuspendS(&owp->thread); | ||
780 | osalSysUnlock(); | ||
781 | |||
782 | pwmDisablePeriodicNotification(pwmd); | ||
783 | ow_bus_idle(owp); | ||
784 | |||
785 | #if ONEWIRE_USE_STRONG_PULLUP | ||
786 | if (pullup_time > 0) { | ||
787 | osalThreadSleep(pullup_time); | ||
788 | owp->config->pullup_release(); | ||
789 | owp->reg.state = ONEWIRE_READY; | ||
790 | } | ||
791 | #endif | ||
792 | } | ||
793 | |||
794 | #if ONEWIRE_USE_SEARCH_ROM | ||
795 | /** | ||
796 | * @brief Performs tree search on bus. | ||
797 | * @note This function does internal 1-wire reset calls every search | ||
798 | * iteration. | ||
799 | * | ||
800 | * @param[in] owp pointer to a @p OWDriver object | ||
801 | * @param[out] result pointer to buffer for discovered ROMs | ||
802 | * @param[in] max_rom_cnt buffer size in ROMs count for overflow prevention | ||
803 | * | ||
804 | * @return Count of discovered ROMs. May be more than max_rom_cnt. | ||
805 | * @retval 0 no ROMs found or communication error occurred. | ||
806 | */ | ||
807 | size_t onewireSearchRom(onewireDriver *owp, uint8_t *result, | ||
808 | size_t max_rom_cnt) { | ||
809 | PWMDriver *pwmd; | ||
810 | PWMConfig *pwmcfg; | ||
811 | uint8_t cmd; | ||
812 | size_t mch, sch; | ||
813 | |||
814 | osalDbgCheck(NULL != owp); | ||
815 | osalDbgAssert(ONEWIRE_READY == owp->reg.state, "Invalid state"); | ||
816 | osalDbgCheck((max_rom_cnt <= 256) && (max_rom_cnt > 0)); | ||
817 | |||
818 | pwmd = owp->config->pwmd; | ||
819 | pwmcfg = owp->config->pwmcfg; | ||
820 | cmd = ONEWIRE_CMD_SEARCH_ROM; | ||
821 | mch = owp->config->master_channel; | ||
822 | sch = owp->config->sample_channel; | ||
823 | |||
824 | search_clean_start(&owp->search_rom); | ||
825 | |||
826 | do { | ||
827 | /* every search must be started from reset pulse */ | ||
828 | if (false == onewireReset(owp)) | ||
829 | return 0; | ||
830 | |||
831 | /* initialize buffer to store result */ | ||
832 | if (owp->search_rom.reg.devices_found >= max_rom_cnt) | ||
833 | owp->search_rom.retbuf = result + 8*(max_rom_cnt-1); | ||
834 | else | ||
835 | owp->search_rom.retbuf = result + 8*owp->search_rom.reg.devices_found; | ||
836 | memset(owp->search_rom.retbuf, 0, 8); | ||
837 | |||
838 | /* clean iteration state */ | ||
839 | search_clean_iteration(&owp->search_rom); | ||
840 | |||
841 | /**/ | ||
842 | onewireWrite(&OWD1, &cmd, 1, 0); | ||
843 | |||
844 | /* Reconfiguration always needed because of previous call onewireWrite.*/ | ||
845 | pwmcfg->period = ONEWIRE_ZERO_WIDTH + ONEWIRE_RECOVERY_WIDTH; | ||
846 | pwmcfg->callback = NULL; | ||
847 | pwmcfg->channels[mch].callback = NULL; | ||
848 | pwmcfg->channels[mch].mode = owp->config->pwmmode; | ||
849 | pwmcfg->channels[sch].callback = pwm_search_rom_cb; | ||
850 | pwmcfg->channels[sch].mode = PWM_OUTPUT_DISABLED; | ||
851 | |||
852 | ow_bus_active(owp); | ||
853 | osalSysLock(); | ||
854 | pwmEnableChannelI(pwmd, mch, ONEWIRE_ONE_WIDTH); | ||
855 | pwmEnableChannelI(pwmd, sch, ONEWIRE_SAMPLE_WIDTH); | ||
856 | pwmEnableChannelNotificationI(pwmd, sch); | ||
857 | osalThreadSuspendS(&owp->thread); | ||
858 | osalSysUnlock(); | ||
859 | |||
860 | ow_bus_idle(owp); | ||
861 | |||
862 | if (ONEWIRE_SEARCH_ROM_ERROR != owp->search_rom.reg.result) { | ||
863 | /* check CRC and return 0 (0 == error) if mismatch */ | ||
864 | if (owp->search_rom.retbuf[7] != onewireCRC(owp->search_rom.retbuf, 7)) | ||
865 | return 0; | ||
866 | /* store cached result for usage in next iteration */ | ||
867 | memcpy(owp->search_rom.prev_path, owp->search_rom.retbuf, 8); | ||
868 | } | ||
869 | } | ||
870 | while (ONEWIRE_SEARCH_ROM_SUCCESS == owp->search_rom.reg.result); | ||
871 | |||
872 | /**/ | ||
873 | if (ONEWIRE_SEARCH_ROM_ERROR == owp->search_rom.reg.result) | ||
874 | return 0; | ||
875 | else | ||
876 | return owp->search_rom.reg.devices_found; | ||
877 | } | ||
878 | #endif /* ONEWIRE_USE_SEARCH_ROM */ | ||
879 | |||
880 | /* | ||
881 | * Include test code (if enabled). | ||
882 | */ | ||
883 | #if ONEWIRE_SYNTH_SEARCH_TEST | ||
884 | #include "synth_searchrom.c" | ||
885 | #endif | ||
886 | |||
887 | #endif /* HAL_USE_ONEWIRE */ | ||
888 | |||
889 | /** @} */ | ||