components/esp32-owb/owb_rmt.c

changeset 72
acc1904cd70d
parent 0
88d965579617
equal deleted inserted replaced
71:995557380e5f 72:acc1904cd70d
1 /* 1 /**
2 Created by Chris Morgan based on the nodemcu project driver. 2 * Copyright (c) 2023 mjcross
3 Copyright 2017 Chris Morgan <chmorgan@gmail.com> 3 *
4 4 * SPDX-License-Identifier: MIT
5 Ported to ESP32 RMT peripheral for low-level signal generation by Arnim Laeuger. 5 **/
6 6
7 Permission is hereby granted, free of charge, to any person obtaining 7 #include "esp_log.h"
8 a copy of this software and associated documentation files (the 8 #include "driver/rmt_tx.h"
9 "Software"), to deal in the Software without restriction, including 9 #include "driver/rmt_rx.h"
10 without limitation the rights to use, copy, modify, merge, publish, 10
11 distribute, sublicense, and/or sell copies of the Software, and to 11 #include "owb.h"
12 permit persons to whom the Software is furnished to do so, subject to 12 #include "owb_rmt_bus_timings.h"
13 the following conditions: 13 #include "owb_rmt_bus_symbols.h"
14 14
15 The above copyright notice and this permission notice shall be 15 #define OWB_RMT_CLK_HZ 1000000 // run the RMT at 1MHz to get 1us ticks
16 included in all copies or substantial portions of the Software. 16 #define OWB_RMT_TX_MEM_BLOCK_SYMBOLS 64 // size of TX memory block in units of rmt_symbol_word_t (must be even)
17 17 #define OWB_RMT_TX_QUEUE_DEPTH 4 // max pending TX transfers
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 #define OWB_RMT_MAX_READ_BITS 64 // maximum number of bits that will be read at once (used to calculate buffer size)
19 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 #define OWB_RMT_RX_MEM_BLOCK_SYMBOLS (OWB_RMT_MAX_READ_BITS + 2) // size of RX memory block in units of rmt_symbol_word_t (must be even)
20 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 #define OWB_RMT_RX_MIN_NS 1000 // RMT receive channel glitch rejection threshold (ns)
21 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 21 #define OWB_RMT_TIMEOUT_MS 1000 // timeout threshold for an RMT task (ms)
22 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 #define OWB_TIMING_MARGIN 3 // timing variation permitted by our event parsing functions (in microsec)
23 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23
24 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 // debug parsing of RMT raw symbols
25 25 //#define OWB_RMT_DEBUG
26 Much of the code was inspired by Derek Yerger's code, though I don't 26
27 think much of that remains. In any event that was.. 27 // tag for log messages
28 (copyleft) 2006 by Derek Yerger - Free to distribute freely. 28 static const char * TAG = "owb_rmt";
29 29
30 The CRC code was excerpted and inspired by the Dallas Semiconductor 30
31 sample code bearing this copyright. 31 //------
32 //--------------------------------------------------------------------------- 32 // private API functions and constants
33 // Copyright (C) 2000 Dallas Semiconductor Corporation, All Rights Reserved. 33 //------
34
35 // onewire bus symbols as rmt_symbol_word_t
36 static const rmt_symbol_word_t owb_rmt_symbol_0bit = OWB_RMT_SYMBOL_0BIT;
37 static const rmt_symbol_word_t owb_rmt_symbol_1bit = OWB_RMT_SYMBOL_1BIT;
38 static const rmt_symbol_word_t owb_rmt_symbol_reset = OWB_RMT_SYMBOL_RESET;
39
40 // RMT transmit configuration for the OWB: transmit symbols once then release the bus
41 static const rmt_transmit_config_t owb_rmt_transmit_config = {
42 .loop_count = 0, // don't send any repeats
43 .flags = {
44 .eot_level = OWB_RMT_BUS_RELEASED // release the bus after the transmission
45 }
46 };
47
48 // RMT receiver configuration for a onewire reset pulse
49 static const rmt_receive_config_t rx_config_owb_reset = {
50 .signal_range_min_ns = OWB_RMT_RX_MIN_NS, // glitch rejection threshold (ns)
51 .signal_range_max_ns = (OWB_TIMING_PARAM_H + OWB_TIMING_PARAM_I) * 1000 // stop condition (ns)
52 };
53
54 // RMT receiver configuration for a sequence of onewire data bits
55 static const rmt_receive_config_t rx_config_owb_bits = {
56 .signal_range_min_ns = OWB_RMT_RX_MIN_NS, // glitch rejection threshold (ns)
57 .signal_range_max_ns = (OWB_TIMING_PARAM_A + OWB_TIMING_PARAM_B) * 1000 // stop condition (ns)
58 };
59
60
61 /**
62 * @brief Uninstalls a onewire bus driver and releases the associated resources.
63 * @param bus A previously-initialised OneWireBus.
64 * @return owb_status OWB_STATUS_OK on success, otherwise an error code (see owb.h)
65 */
66 static owb_status _uninitialize(const OneWireBus *bus) {
67
68 // fetch the parent `owb_rmt_driver_info` structure for `bus`
69 owb_rmt_driver_info *info = __containerof(bus, owb_rmt_driver_info, bus); // (pointer, type, member)
70 if (info == NULL) {
71 ESP_LOGE(TAG, "err uninitialize: no bus container");
72 return OWB_STATUS_PARAMETER_NULL;
73 }
74
75 // release RMT device symbol buffer and queue
76 free (info->rx_buffer);
77 vQueueDelete (info->rx_queue);
78
79 // disable and release RMT resources
80 if (rmt_disable (info->rx_channel_handle) == ESP_OK &&
81 rmt_del_channel (info->rx_channel_handle) == ESP_OK &&
82 rmt_disable (info->tx_channel_handle) == ESP_OK &&
83 rmt_del_channel (info->tx_channel_handle) == ESP_OK &&
84 rmt_del_encoder (info->copy_encoder_handle) == ESP_OK &&
85 rmt_del_encoder (info->bytes_encoder_handle) == ESP_OK ) {
86 // all resources successfully released
87 return OWB_STATUS_OK;
88 }
89
90 // an error occurred
91 ESP_LOGE(TAG, "err uninitializing");
92 return OWB_STATUS_HW_ERROR;
93 }
94
95
96 /**
97 * @brief Parses the RMT symbols received during a onewire bus reset.
98 * @param[in] num_symbols The number of symbols passed.
99 * @param[in] symbols An array of RMT symbols.
100 * @param[out] slave_is_present Whether a slave presence signal was detected.
101 * @return OWB_STATUS_OK if the symbols pass basic valdation; otherwise an error code (see owb.h).
102 */
103 static owb_status _parse_reset_symbols (size_t num_symbols, rmt_symbol_word_t *symbols, bool *slave_is_present) {
104 *slave_is_present = false;
105
106 if (num_symbols == 0 || symbols == NULL) {
107 return OWB_STATUS_PARAMETER_NULL;
108 }
109
110 #ifdef OWB_RMT_DEBUG
111 // display raw RMT symbols
112 ESP_LOGI(TAG, "parse reset: %d symbols", (int)num_symbols);
113 for (int i = 0; i < num_symbols; i += 1) {
114 ESP_LOGI (TAG, "\t%u (%uus), %u (%uus)", symbols->level0, symbols->duration0,
115 symbols->level1, symbols->duration1);
116 }
117 #endif
118
119 // check the duration of the reset pulse
120 if (abs (symbols[0].duration0 - OWB_TIMING_PARAM_H) > OWB_TIMING_MARGIN) {
121 return OWB_STATUS_HW_ERROR;
122 }
123
124 // check for a valid 'no slave' event
125 if (num_symbols == 1 && symbols[0].duration1 == 0) {
126 *slave_is_present = false;
127 return OWB_STATUS_OK;
128 }
129
130 // check for a valid 'slave present' event
131 if (num_symbols == 2 && // no 'extra' symbols after the presence pulse
132 symbols[0].duration1 < OWB_TIMING_PARAM_I && // presence pulse must arrive before the sample point
133 (symbols[1].duration0 + symbols[0].duration1) >= OWB_TIMING_PARAM_I // presence pulse must not finish before the sample point
134 ) {
135 *slave_is_present = true;
136 return OWB_STATUS_OK;
137 }
138
139 // anything else is invalid
140 return OWB_STATUS_HW_ERROR;
141 }
142
143
144 /**
145 * @brief Parses the RMT symbols received during the transmission of up to 64 onewire bits.
146 * @param[in] num_symbols The number of symbols passed.
147 * @param[in] symbols An array of RMT symbols.
148 * @param[out] result The decoded bits (max 64, lsb first)
149 * @return int The number of bits decoded
150 */
151 static int _parse_bit_symbols (size_t num_symbols, rmt_symbol_word_t *p_symbol, uint64_t *result) {
152 *result = 0;
153 int bit_count = 0;
154 rmt_symbol_word_t *p_last_symbol = p_symbol + num_symbols;
155
156 #ifdef OWB_RMT_DEBUG
157 // display raw RMT symbols
158 ESP_LOGI(TAG, "parse bits: %d symbols", (int)num_symbols);
159 #endif
160
161 while (p_symbol < p_last_symbol && bit_count < 64) {
162 #ifdef OWB_RMT_DEBUG
163 ESP_LOGI (TAG, "\t%u (%uus), %u (%uus)", p_symbol->level0, p_symbol->duration0,
164 p_symbol->level1, p_symbol->duration1);
165 #endif
166 if (abs (p_symbol->duration0 - OWB_TIMING_PARAM_A) <= OWB_TIMING_MARGIN &&
167 (p_symbol->duration1 == 0 || p_symbol->duration1 >= OWB_TIMING_PARAM_E)) {
168 // bus was released at the sample point: detect a '1'
169 *result |= (1ull << bit_count);
170 bit_count += 1;
171
172 #ifdef OWB_RMT_DEBUG
173 ESP_LOGI (TAG, "\t\tdetect '1' -> 0x%llx", *result);
174 #endif
175
176 } else if (p_symbol->duration0 >= (OWB_TIMING_PARAM_A + OWB_TIMING_PARAM_E)) {
177 // bus was asserted at the sample point: detect a '0'
178 bit_count += 1;
179
180 #ifdef OWB_RMT_DEBUG
181 ESP_LOGI (TAG, "\t\tdetect '0' -> 0x%llx", *result);
182 #endif
183 }
184 p_symbol += 1; // next symbol
185 }
186
187 return bit_count;
188 }
189
190
191 /**
192 * @brief Sends a onewire bus reset pulse and listens for slave presence responses.
193 * @param[in] bus Points to the OneWireBus structure (see owb.h).
194 * @param[out] is_present Points to a bool that will receive the detection result.
195 * @return OWB_STATUS_OK if the call succeeded; otherwise an owb_status error code (see owb.h).
196 */
197 static owb_status _reset (const OneWireBus *bus, bool *is_present) {
198
199 esp_err_t esp_status;
200
201 // identify the rmt_driver_info structure that contains `bus`
202 owb_rmt_driver_info *info = __containerof(bus, owb_rmt_driver_info, bus);
203
204 // start the receiver before the transmitter so that it sees the leading edge of the pulse
205 esp_status = rmt_receive (
206 info->rx_channel_handle,
207 info->rx_buffer,
208 info->rx_buffer_size_in_bytes,
209 &rx_config_owb_reset);
210 if (esp_status != ESP_OK) {
211 ESP_LOGE(TAG, "owb_reset: rx err");
212 return OWB_STATUS_HW_ERROR;
213 }
214
215 // encode and transmit the reset pulse using the RMT 'copy' encoder
216 esp_status = rmt_transmit (
217 info->tx_channel_handle,
218 info->copy_encoder_handle,
219 &owb_rmt_symbol_reset,
220 sizeof (owb_rmt_symbol_reset),
221 &owb_rmt_transmit_config);
222 if (esp_status != ESP_OK) {
223 ESP_LOGE(TAG, "owb_reset: tx err");
224 return OWB_STATUS_HW_ERROR;
225 }
226
227 // wait for the transmission to finish (or timeout with an error)
228 if (rmt_tx_wait_all_done (info->tx_channel_handle, OWB_RMT_TIMEOUT_MS) != ESP_OK) {
229 ESP_LOGE(TAG, "owb_reset: tx timeout");
230 return OWB_STATUS_DEVICE_NOT_RESPONDING; // tx timeout
231 }
232
233 // wait for the recv_done event data from our callback
234 rmt_rx_done_event_data_t rx_done_event_data;
235 if (xQueueReceive (info->rx_queue, &rx_done_event_data, pdMS_TO_TICKS(OWB_RMT_TIMEOUT_MS)) != pdTRUE) {
236 ESP_LOGE(TAG, "owb_reset: no rx symbol"); // rx timeout
237 return OWB_STATUS_DEVICE_NOT_RESPONDING;
238 }
239
240 // parse the event data and return the result
241 return _parse_reset_symbols (rx_done_event_data.num_symbols, rx_done_event_data.received_symbols, is_present);
242 }
243
244 /**
245 * @brief Writes a number of bytes to the onewire bus (slightly more efficient than sending them individually).
246 * @param bus A previously-initialised OneWireBus.
247 * @param bytes The bytes to be sent.
248 * @param number_of_bytes_to_write How many bytes to send.
249 * @return owb_status OWB_STATUS_OK on success, otherwise an error code (see owb.h).
250 */
251 static owb_status _write_bytes(const OneWireBus *bus, uint8_t *bytes, int number_of_bytes_to_write) {
252 esp_err_t esp_status;
253
254 // identify the rmt_driver_info structure that contains `bus`
255 owb_rmt_driver_info *info = __containerof(bus, owb_rmt_driver_info, bus);
256
257 // encode and transmit the bits using the RMT 'bytes' encoder
258 esp_status = rmt_transmit (
259 info->tx_channel_handle,
260 info->bytes_encoder_handle,
261 bytes,
262 (size_t)number_of_bytes_to_write,
263 &owb_rmt_transmit_config);
264 if (esp_status != ESP_OK) {
265 ESP_LOGE(TAG, "owb_write: tx err");
266 return OWB_STATUS_HW_ERROR;
267 }
268
269 // wait for the transmission to finish (or timeout with an error)
270 if (rmt_tx_wait_all_done (info->tx_channel_handle, OWB_RMT_TIMEOUT_MS) != ESP_OK) {
271 return OWB_STATUS_DEVICE_NOT_RESPONDING; // tx timeout
272 }
273 return OWB_STATUS_OK;
274 }
275
276
277 /**
278 * @brief Writes 1-8 bits to the onewire bus.
279 * @param bus A previously-initialised OneWireBus.
280 * @param bytes A byte with the bits to be sent (lsb first).
281 * @param number_of_bits_to_write How many bits to send (maximum 8).
282 * @return owb_status OWB_STATUS_OK on success, otherwise an error code (see owb.h).
283 */
284 static owb_status _write_bits(const OneWireBus *bus, uint8_t out, int number_of_bits_to_write) {
285
286 // send 8 bits as a byte instead
287 if (number_of_bits_to_write == 8) {
288 return _write_bytes (bus, &out, 1);
289 }
290
291 if (number_of_bits_to_write < 1 || number_of_bits_to_write > 8) {
292 ESP_LOGE(TAG, "owb_write_bits: bad num of bits (%d)", number_of_bits_to_write);
293 return OWB_STATUS_TOO_MANY_BITS;
294 }
295
296 // identify the rmt_driver_info structure that contains `bus`
297 owb_rmt_driver_info *info = __containerof(bus, owb_rmt_driver_info, bus);
298
299 // send data as individual bits using the `copy` encoder
300 const rmt_symbol_word_t *symbol_ptr;
301 esp_err_t esp_status;
302 for (int b = 0; b < number_of_bits_to_write; b += 1) {
303 if ((out & (1 << b)) == 0) {
304 symbol_ptr = &owb_rmt_symbol_0bit;
305 } else {
306 symbol_ptr = &owb_rmt_symbol_1bit;
307 }
308
309 // send bit symbol
310 esp_status = rmt_transmit (
311 info->tx_channel_handle,
312 info->copy_encoder_handle,
313 symbol_ptr,
314 sizeof (rmt_symbol_word_t),
315 &owb_rmt_transmit_config);
316 if (esp_status != ESP_OK) {
317 ESP_LOGE(TAG, "owb_write_bit: tx err");
318 return OWB_STATUS_HW_ERROR;
319 }
320 }
321
322 // wait for the transmission to finish (or timeout with an error)
323 if (rmt_tx_wait_all_done (info->tx_channel_handle, OWB_RMT_TIMEOUT_MS) != ESP_OK) {
324 return OWB_STATUS_DEVICE_NOT_RESPONDING; // tx timeout
325 }
326
327 return OWB_STATUS_OK;
328 }
329
330
331 /**
332 * @brief Reads up to 8 bytes from the onewire bus (this is faster than reading individual bits).
333 * @param bus A previously-initialised OneWireBus.
334 * @param result The resulting data, stored lsb first in a uint64_t.
335 * @param number_of_bytes_to_read The number of bytes to read.
336 * @return owb_status OWB_STATUS_OK on success, otherwise and error code (see owb.h)
337 */
338 static owb_status _read_bytes(const OneWireBus *bus, uint64_t *result_ptr, int number_of_bytes_to_read) {
339 static uint8_t ff_bytes[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
340 esp_err_t esp_status;
341
342 if (number_of_bytes_to_read > 8) {
343 ESP_LOGE(TAG, "owb_read_bytes: max 8");
344 return OWB_STATUS_TOO_MANY_BITS;
345 }
346
347 // identify the rmt_driver_info structure that contains `bus`
348 owb_rmt_driver_info *info = __containerof(bus, owb_rmt_driver_info, bus);
349
350 // start the receiver before the transmitter so that it sees the first edge
351 esp_status = rmt_receive (
352 info->rx_channel_handle,
353 info->rx_buffer,
354 info->rx_buffer_size_in_bytes,
355 &rx_config_owb_bits);
356 if (esp_status != ESP_OK) {
357 ESP_LOGE(TAG, "owb_read_bytes: rx err");
358 return OWB_STATUS_HW_ERROR;
359 }
360
361 // generate read slots
362 esp_status = rmt_transmit (
363 info->tx_channel_handle,
364 info->bytes_encoder_handle,
365 ff_bytes,
366 (size_t)number_of_bytes_to_read,
367 &owb_rmt_transmit_config);
368 if (esp_status != ESP_OK) {
369 ESP_LOGE(TAG, "owb_read_bytes: tx err");
370 return OWB_STATUS_HW_ERROR;
371 }
372
373 // wait for the transmission to finish (or timeout with an error)
374 if (rmt_tx_wait_all_done (info->tx_channel_handle, OWB_RMT_TIMEOUT_MS) != ESP_OK) {
375 return OWB_STATUS_DEVICE_NOT_RESPONDING; // tx timeout
376 }
377
378 // wait for the recv_done event data from our callback
379 rmt_rx_done_event_data_t rx_done_event_data;
380 if (xQueueReceive (info->rx_queue, &rx_done_event_data, pdMS_TO_TICKS(OWB_RMT_TIMEOUT_MS)) != pdTRUE) {
381 ESP_LOGE(TAG, "owb_read_bytes: no rx symbols"); // rx timeout
382 return OWB_STATUS_DEVICE_NOT_RESPONDING;
383 }
384
385 // decode upto 64 data bits from the received RMT symbols
386 if (_parse_bit_symbols(rx_done_event_data.num_symbols, rx_done_event_data.received_symbols, result_ptr) == 0) {
387 ESP_LOGE(TAG, "owb_read_bytes: no bits");
388 }
389
390 return OWB_STATUS_OK;
391 }
392
393
394 /**
395 * @brief Reads up to 8 bits from the onewire bus.
396 * @param bus A previously-initialised OneWireBus.
397 * @param result A byte containing the bits read (lsb first).
398 * @param number_of_bits_to_read The number of bits to read.
399 * @return owb_status OWB_STATUS_OK on success, otherwise an error code (see owb.h)
400 */
401 static owb_status _read_bits(const OneWireBus *bus, uint8_t *result, int number_of_bits_to_read) {
402 esp_err_t esp_status;
403
404 if (number_of_bits_to_read > 8) {
405 ESP_LOGE(TAG, "owb_read_bits: max 8");
406 return OWB_STATUS_TOO_MANY_BITS;
407 }
408
409 // it's quicker to read 8 bits as a whole byte
410 if (number_of_bits_to_read == 8) {
411 uint64_t result_64;
412 owb_status status;
413 status = _read_bytes (bus, &result_64, 1);
414 *result = (uint8_t)result_64;
415 return status;
416 }
417
418 // identify the rmt_driver_info structure that contains `bus`
419 owb_rmt_driver_info *info = __containerof(bus, owb_rmt_driver_info, bus);
420
421 // with the copy encoder then it's most efficient to receive each bit individually
422 // because we don't accurately know the interval between bits.
423 // It would be nice to use `rmt_transmit_config.loop_count` here, but it's not supported
424 // on all chips. In any case the user almost certainly only wants a single bit.
425 *result = 0;
426 for (int bit_index = 0; bit_index < number_of_bits_to_read; bit_index += 1) {
427
428 // start the receiver before the transmitter so that it sees the first edge
429 esp_status = rmt_receive (
430 info->rx_channel_handle,
431 info->rx_buffer,
432 info->rx_buffer_size_in_bytes,
433 &rx_config_owb_bits);
434 if (esp_status != ESP_OK) {
435 ESP_LOGE(TAG, "owb_read_bits: rx err");
436 return OWB_STATUS_HW_ERROR;
437 }
438
439 // send a '1' symbol to generate a read slot
440 esp_status = rmt_transmit (
441 info->tx_channel_handle,
442 info->copy_encoder_handle,
443 &owb_rmt_symbol_1bit,
444 sizeof (rmt_symbol_word_t),
445 &owb_rmt_transmit_config);
446 if (esp_status != ESP_OK) {
447 ESP_LOGE(TAG, "owb_read_bits: tx err");
448 return OWB_STATUS_HW_ERROR;
449 }
450
451 // wait for the transmission to finish (or timeout with an error)
452 if (rmt_tx_wait_all_done (info->tx_channel_handle, OWB_RMT_TIMEOUT_MS) != ESP_OK) {
453 return OWB_STATUS_DEVICE_NOT_RESPONDING; // tx timeout
454 }
455
456 // wait for the recv_done event data from our callback
457 rmt_rx_done_event_data_t rx_done_event_data;
458 if (xQueueReceive (info->rx_queue, &rx_done_event_data, pdMS_TO_TICKS(OWB_RMT_TIMEOUT_MS)) != pdTRUE) {
459 ESP_LOGE(TAG, "owb_read_bits: no rx symbol"); // rx timeout
460 return OWB_STATUS_DEVICE_NOT_RESPONDING;
461 }
462
463 // parse the event data
464 uint64_t bits = 0;
465 if (_parse_bit_symbols (rx_done_event_data.num_symbols, rx_done_event_data.received_symbols, &bits) == 0) {
466 ESP_LOGE(TAG, "owb_read_bits: no bits");
467 return OWB_STATUS_HW_ERROR;
468 }
469
470 // add the bit to `result` (lsb is received first)
471 if ((bits & 1) != 0) {
472 *result |= (1 << bit_index);
473 }
474 }
475
476 return OWB_STATUS_OK;
477 }
478
479
480 /**
481 * @brief Handle the RMT `recv_done` event by copying the event data structure to the specified queue.
482 * @param[in] channel The handle of the RMT channel that generated the event.
483 * @param[in] edata A pointer to the RMT event data structure (the pointer is valid only within this function).
484 * @param[in] context A pointer to the user-provided context, in this case the queue handle.
485 * @return True if sending to the queue caused a higher priority task to unblock; otherwise False.
486 */
487 static bool IRAM_ATTR _recv_done_callback (rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *event_data, void *user_data) {
488 // Copy a pointer to the event data structure to the queue identified in the user_data.
489 //* NOTE: this is an interrupt handler so it needs IRAM_ATTR, may only use `ISR` calls and must return promptly.
490 //
491 BaseType_t pxHigherPriorityTaskWoken = pdFALSE;
492
493 xQueueSendFromISR ((QueueHandle_t)user_data, event_data, &pxHigherPriorityTaskWoken);
494 if (pxHigherPriorityTaskWoken == pdTRUE) {
495 return true;
496 }
497 return false;
498 }
499
500
501 //-----
502 // Public API functions
503 //-----
504
505 // RMT version of the OWB driver api (will be stored as info->bus->driver)
34 // 506 //
35 // Permission is hereby granted, free of charge, to any person obtaining a 507 static struct owb_driver rmt_driver_functions = {
36 // copy of this software and associated documentation files (the "Software"),
37 // to deal in the Software without restriction, including without limitation
38 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
39 // and/or sell copies of the Software, and to permit persons to whom the
40 // Software is furnished to do so, subject to the following conditions:
41 //
42 // The above copyright notice and this permission notice shall be included
43 // in all copies or substantial portions of the Software.
44 //
45 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
46 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
47 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
48 // IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES
49 // OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
50 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
51 // OTHER DEALINGS IN THE SOFTWARE.
52 //
53 // Except as contained in this notice, the name of Dallas Semiconductor
54 // shall not be used except as stated in the Dallas Semiconductor
55 // Branding Policy.
56 //--------------------------------------------------------------------------
57 */
58
59 #include "owb.h"
60
61 #include "driver/rmt.h"
62 #include "driver/gpio.h"
63 #include "esp_log.h"
64
65 #undef OW_DEBUG
66
67
68 // bus reset: duration of low phase [us]
69 #define OW_DURATION_RESET 480
70 // overall slot duration
71 #define OW_DURATION_SLOT 75
72 // write 1 slot and read slot durations [us]
73 #define OW_DURATION_1_LOW 2
74 #define OW_DURATION_1_HIGH (OW_DURATION_SLOT - OW_DURATION_1_LOW)
75 // write 0 slot durations [us]
76 #define OW_DURATION_0_LOW 65
77 #define OW_DURATION_0_HIGH (OW_DURATION_SLOT - OW_DURATION_0_LOW)
78 // sample time for read slot
79 #define OW_DURATION_SAMPLE (15-2)
80 // RX idle threshold
81 // needs to be larger than any duration occurring during write slots
82 #define OW_DURATION_RX_IDLE (OW_DURATION_SLOT + 2)
83
84
85 static const char * TAG = "owb_rmt";
86
87 #define info_of_driver(owb) container_of(owb, owb_rmt_driver_info, bus)
88
89 // flush any pending/spurious traces from the RX channel
90 static void onewire_flush_rmt_rx_buf(const OneWireBus * bus)
91 {
92 void *p;
93 size_t s;
94
95 owb_rmt_driver_info *i = info_of_driver(bus);
96
97 while ((p = xRingbufferReceive(i->rb, &s, 0)))
98 {
99 ESP_LOGD(TAG, "flushing entry");
100 vRingbufferReturnItem(i->rb, p);
101 }
102 }
103
104 static owb_status _reset(const OneWireBus *bus, bool *is_present)
105 {
106 rmt_item32_t tx_items[1];
107 bool _is_present = false;
108 int res = OWB_STATUS_OK;
109
110 owb_rmt_driver_info *i = info_of_driver(bus);
111
112 tx_items[0].duration0 = OW_DURATION_RESET;
113 tx_items[0].level0 = 0;
114 tx_items[0].duration1 = 0;
115 tx_items[0].level1 = 1;
116
117 uint16_t old_rx_thresh;
118 rmt_get_rx_idle_thresh(i->rx_channel, &old_rx_thresh);
119 rmt_set_rx_idle_thresh(i->rx_channel, OW_DURATION_RESET+60);
120
121 onewire_flush_rmt_rx_buf(bus);
122 rmt_rx_start(i->rx_channel, true);
123 if (rmt_write_items(i->tx_channel, tx_items, 1, true) == ESP_OK)
124 {
125 size_t rx_size;
126 rmt_item32_t* rx_items = (rmt_item32_t *)xRingbufferReceive(i->rb, &rx_size, 100 / portTICK_PERIOD_MS);
127
128 if (rx_items)
129 {
130 if (rx_size >= (1 * sizeof(rmt_item32_t)))
131 {
132 #ifdef OW_DEBUG
133 ESP_LOGI(TAG, "rx_size: %d", rx_size);
134
135 for (int i = 0; i < (rx_size / sizeof(rmt_item32_t)); i++)
136 {
137 ESP_LOGI(TAG, "i: %d, level0: %d, duration %d", i, rx_items[i].level0, rx_items[i].duration0);
138 ESP_LOGI(TAG, "i: %d, level1: %d, duration %d", i, rx_items[i].level1, rx_items[i].duration1);
139 }
140 #endif
141
142 // parse signal and search for presence pulse
143 if ((rx_items[0].level0 == 0) && (rx_items[0].duration0 >= OW_DURATION_RESET - 2))
144 {
145 if ((rx_items[0].level1 == 1) && (rx_items[0].duration1 > 0))
146 {
147 if (rx_items[1].level0 == 0)
148 {
149 _is_present = true;
150 }
151 }
152 }
153 }
154
155 vRingbufferReturnItem(i->rb, (void *)rx_items);
156 }
157 else
158 {
159 // time out occurred, this indicates an unconnected / misconfigured bus
160 ESP_LOGE(TAG, "rx_items == 0");
161 res = OWB_STATUS_HW_ERROR;
162 }
163 }
164 else
165 {
166 // error in tx channel
167 ESP_LOGE(TAG, "Error tx");
168 res = OWB_STATUS_HW_ERROR;
169 }
170
171 rmt_rx_stop(i->rx_channel);
172 rmt_set_rx_idle_thresh(i->rx_channel, old_rx_thresh);
173
174 *is_present = _is_present;
175
176 ESP_LOGD(TAG, "_is_present %d", _is_present);
177
178 return res;
179 }
180
181 static rmt_item32_t _encode_write_slot(uint8_t val)
182 {
183 rmt_item32_t item;
184
185 item.level0 = 0;
186 item.level1 = 1;
187 if (val)
188 {
189 // write "1" slot
190 item.duration0 = OW_DURATION_1_LOW;
191 item.duration1 = OW_DURATION_1_HIGH;
192 }
193 else
194 {
195 // write "0" slot
196 item.duration0 = OW_DURATION_0_LOW;
197 item.duration1 = OW_DURATION_0_HIGH;
198 }
199
200 return item;
201 }
202
203 /** NOTE: The data is shifted out of the low bits, eg. it is written in the order of lsb to msb */
204 static owb_status _write_bits(const OneWireBus * bus, uint8_t out, int number_of_bits_to_write)
205 {
206 rmt_item32_t tx_items[number_of_bits_to_write + 1];
207 owb_rmt_driver_info *info = info_of_driver(bus);
208
209 if (number_of_bits_to_write > 8)
210 {
211 return OWB_STATUS_TOO_MANY_BITS;
212 }
213
214 // write requested bits as pattern to TX buffer
215 for (int i = 0; i < number_of_bits_to_write; i++)
216 {
217 tx_items[i] = _encode_write_slot(out & 0x01);
218 out >>= 1;
219 }
220
221 // end marker
222 tx_items[number_of_bits_to_write].level0 = 1;
223 tx_items[number_of_bits_to_write].duration0 = 0;
224
225 owb_status status;
226
227 if (rmt_write_items(info->tx_channel, tx_items, number_of_bits_to_write+1, true) == ESP_OK)
228 {
229 status = OWB_STATUS_OK;
230 }
231 else
232 {
233 status = OWB_STATUS_HW_ERROR;
234 ESP_LOGE(TAG, "rmt_write_items() failed");
235 }
236
237 return status;
238 }
239
240 static rmt_item32_t _encode_read_slot(void)
241 {
242 rmt_item32_t item;
243
244 // construct pattern for a single read time slot
245 item.level0 = 0;
246 item.duration0 = OW_DURATION_1_LOW; // shortly force 0
247 item.level1 = 1;
248 item.duration1 = OW_DURATION_1_HIGH; // release high and finish slot
249 return item;
250 }
251
252 /** NOTE: Data is read into the high bits, eg. each bit read is shifted down before the next bit is read */
253 static owb_status _read_bits(const OneWireBus * bus, uint8_t *in, int number_of_bits_to_read)
254 {
255 rmt_item32_t tx_items[number_of_bits_to_read + 1];
256 uint8_t read_data = 0;
257 int res = OWB_STATUS_OK;
258
259 owb_rmt_driver_info *info = info_of_driver(bus);
260
261 if (number_of_bits_to_read > 8)
262 {
263 ESP_LOGE(TAG, "_read_bits() OWB_STATUS_TOO_MANY_BITS");
264 return OWB_STATUS_TOO_MANY_BITS;
265 }
266
267 // generate requested read slots
268 for (int i = 0; i < number_of_bits_to_read; i++)
269 {
270 tx_items[i] = _encode_read_slot();
271 }
272
273 // end marker
274 tx_items[number_of_bits_to_read].level0 = 1;
275 tx_items[number_of_bits_to_read].duration0 = 0;
276
277 onewire_flush_rmt_rx_buf(bus);
278 rmt_rx_start(info->rx_channel, true);
279 if (rmt_write_items(info->tx_channel, tx_items, number_of_bits_to_read+1, true) == ESP_OK)
280 {
281 size_t rx_size;
282 rmt_item32_t* rx_items = (rmt_item32_t *)xRingbufferReceive(info->rb, &rx_size, portMAX_DELAY);
283
284 if (rx_items)
285 {
286 #ifdef OW_DEBUG
287 for (int i = 0; i < rx_size / 4; i++)
288 {
289 ESP_LOGI(TAG, "level: %d, duration %d", rx_items[i].level0, rx_items[i].duration0);
290 ESP_LOGI(TAG, "level: %d, duration %d", rx_items[i].level1, rx_items[i].duration1);
291 }
292 #endif
293
294 if (rx_size >= number_of_bits_to_read * sizeof(rmt_item32_t))
295 {
296 for (int i = 0; i < number_of_bits_to_read; i++)
297 {
298 read_data >>= 1;
299 // parse signal and identify logical bit
300 if (rx_items[i].level1 == 1)
301 {
302 if ((rx_items[i].level0 == 0) && (rx_items[i].duration0 < OW_DURATION_SAMPLE))
303 {
304 // rising edge occured before 15us -> bit 1
305 read_data |= 0x80;
306 }
307 }
308 }
309 read_data >>= 8 - number_of_bits_to_read;
310 }
311
312 vRingbufferReturnItem(info->rb, (void *)rx_items);
313 }
314 else
315 {
316 // time out occurred, this indicates an unconnected / misconfigured bus
317 ESP_LOGE(TAG, "rx_items == 0");
318 res = OWB_STATUS_HW_ERROR;
319 }
320 }
321 else
322 {
323 // error in tx channel
324 ESP_LOGE(TAG, "Error tx");
325 res = OWB_STATUS_HW_ERROR;
326 }
327
328 rmt_rx_stop(info->rx_channel);
329
330 *in = read_data;
331 return res;
332 }
333
334 static owb_status _uninitialize(const OneWireBus *bus)
335 {
336 owb_rmt_driver_info * info = info_of_driver(bus);
337
338 rmt_driver_uninstall(info->tx_channel);
339 rmt_driver_uninstall(info->rx_channel);
340
341 return OWB_STATUS_OK;
342 }
343
344 static struct owb_driver rmt_function_table =
345 {
346 .name = "owb_rmt", 508 .name = "owb_rmt",
347 .uninitialize = _uninitialize, 509 .uninitialize = _uninitialize,
348 .reset = _reset, 510 .reset = _reset,
349 .write_bits = _write_bits, 511 .write_bits = _write_bits,
350 .read_bits = _read_bits 512 .write_bytes = _write_bytes, // new addition to the API
513 .read_bits = _read_bits,
514 .read_bytes = _read_bytes // new addition to the API
351 }; 515 };
352 516
353 static owb_status _init(owb_rmt_driver_info *info, uint8_t gpio_num, 517
354 rmt_channel_t tx_channel, rmt_channel_t rx_channel) 518 // configure and allocate resources
519 //
520 OneWireBus* owb_rmt_initialize (owb_rmt_driver_info *info, gpio_num_t gpio_num, int tx_channel, int rx_channel)
355 { 521 {
356 owb_status status = OWB_STATUS_HW_ERROR; 522 //* The function now ignores tx_channel and rx_channel as the new RMT driver allocates channels on demand.
357 523 //* The parameters are kept in the call to preserve compatibility with previous versions.
358 // Ensure the RMT peripheral is not already running 524
359 // Note: if using RMT elsewhere, don't call this here, call it at the start of your prgoram instead. 525 // the steps to enable the RMT resources are documented in:
360 //periph_module_disable(PERIPH_RMT_MODULE); 526 // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/rmt.html
361 //periph_module_enable(PERIPH_RMT_MODULE); 527
362 528 // Note: keeping the TX and RX initialisations together in one function simplifies the error handling
363 info->bus.driver = &rmt_function_table; 529
364 info->tx_channel = tx_channel; 530 (void)tx_channel; // avoid compiler warning about unused parameter
365 info->rx_channel = rx_channel; 531 (void)rx_channel; // avoid compiler warning about unused parameter
532
533 // sanity check
534 if (info == NULL) {
535 ESP_LOGE(TAG, "info is NULL");
536 goto exit_err;
537 }
538
539 // ----- receive channel -----
540
541 // channel config
542 const rmt_rx_channel_config_t rx_channel_config = {
543 .gpio_num = (int)gpio_num,
544 .clk_src = RMT_CLK_SRC_APB, // use the APB clock (might reduce during light sleep)
545 .resolution_hz = OWB_RMT_CLK_HZ,
546 .mem_block_symbols = (size_t)OWB_RMT_RX_MEM_BLOCK_SYMBOLS,
547 .flags = {
548 .invert_in = 0, // don't hardware invert the input
549 .with_dma = 0, // don't enable DMA
550 .io_loop_back = 0 // we define the loopback in the tx config
551 }
552 };
553
554 // request channel
555 //* note: to get a wired-OR bus you must apply the rx_config first, _then_ the rx_config
556 if (rmt_new_rx_channel (&rx_channel_config, &(info->rx_channel_handle)) != ESP_OK) {
557 ESP_LOGE(TAG, "err requesting rx_channel");
558 goto exit_err;
559 }
560
561 // create queue for RMT `rx_done` event data struct (from callback)
562 info->rx_queue = xQueueCreate (1, sizeof (rmt_rx_done_event_data_t));
563 if (info->rx_queue == NULL) {
564 ESP_LOGE(TAG, "err creating rx_queue");
565 goto exit_delete_rx_channel;
566 }
567
568 // allocate rx symbol buffer for RMT driver
569 info->rx_buffer_size_in_bytes = OWB_RMT_MAX_READ_BITS * sizeof (rmt_symbol_word_t);
570 info->rx_buffer = (rmt_symbol_word_t *)malloc (info->rx_buffer_size_in_bytes);
571 if (info->rx_buffer == NULL) {
572 ESP_LOGE(TAG, "err allocating rx_buffer");
573 goto exit_delete_rx_queue;
574 }
575
576 // register rx channel callback (rx_queue is passed as user context)
577 const rmt_rx_event_callbacks_t rmt_rx_event_callbacks = {
578 .on_recv_done = _recv_done_callback
579 };
580 if (rmt_rx_register_event_callbacks (info->rx_channel_handle, &rmt_rx_event_callbacks, info->rx_queue) != ESP_OK) {
581 ESP_LOGE(TAG, "err registering rx_callbacks");
582 goto exit_release_rx_buffer;
583 }
584
585 // enable channel
586 if (rmt_enable (info->rx_channel_handle) != ESP_OK) {
587 ESP_LOGE(TAG, "err enabling rx_channel");
588 goto exit_release_rx_buffer;
589 }
590
591 // ----- transmit channel -----
592
593 // channel config
594 const rmt_tx_channel_config_t tx_channel_config = {
595 .gpio_num = (int)gpio_num,
596 .clk_src = RMT_CLK_SRC_APB, // use the APB clock (might reduce during light sleep)
597 .resolution_hz = OWB_RMT_CLK_HZ,
598 .mem_block_symbols = (size_t)OWB_RMT_TX_MEM_BLOCK_SYMBOLS,
599 .trans_queue_depth = OWB_RMT_TX_QUEUE_DEPTH,
600 .flags = {
601 .invert_out = 1, // invert the output (so that the bus is initially released)
602 .with_dma = 0, // don't enable DMA
603 .io_loop_back = 1, // enable reading of actual voltage of output pin
604 .io_od_mode = 1 // enable open-drain output, so as to achieve a 'wired-OR' bus
605 }
606 };
607
608 // request channel
609 if (rmt_new_tx_channel (&tx_channel_config, &(info->tx_channel_handle)) != ESP_OK) {
610 ESP_LOGE(TAG, "err requesting tx_channel");
611 goto exit_disable_rx_channel;
612 }
613
614 // enable channel
615 if (rmt_enable (info->tx_channel_handle) != ESP_OK) {
616 ESP_LOGE(TAG, "err enabling tx_channel");
617 goto exit_delete_tx_channel;
618 }
619
620 // obtain a 'copy' encoder (an RMT built-in used for sending fixed bit patterns)
621 const rmt_copy_encoder_config_t rmt_copy_encoder_config = {}; // config is "reserved for future expansion"
622 if (rmt_new_copy_encoder (&rmt_copy_encoder_config, &(info->copy_encoder_handle)) != ESP_OK) {
623 ESP_LOGE(TAG, "err requesting copy encoder");
624 goto exit_disable_tx_channel;
625 }
626
627 // otain a 'bytes' encoder (an RMT built-in used for sending variable bit patterns)
628 const rmt_bytes_encoder_config_t rmt_bytes_encoder_config = {
629 .bit0 = OWB_RMT_SYMBOL_0BIT,
630 .bit1 = OWB_RMT_SYMBOL_1BIT,
631 .flags = {
632 .msb_first = 0 // onewire bus on-the-wire bit order is lsb first
633 }
634 };
635 if (rmt_new_bytes_encoder(&rmt_bytes_encoder_config, &info->bytes_encoder_handle) != ESP_OK) {
636 ESP_LOGE(TAG, "err requesting bytes encoder");
637 goto exit_delete_copy_encoder;
638 }
639
640
641 // ----- success ------
366 info->gpio = gpio_num; 642 info->gpio = gpio_num;
367 643 info->bus.driver = &rmt_driver_functions; // route driver API calls to the functions in this file
368 #ifdef OW_DEBUG 644 ESP_LOGI(TAG, "%s: OK", __func__);
369 ESP_LOGI(TAG, "RMT TX channel: %d", info->tx_channel);
370 ESP_LOGI(TAG, "RMT RX channel: %d", info->rx_channel);
371 #endif
372
373 rmt_config_t rmt_tx;
374 rmt_tx.channel = info->tx_channel;
375 rmt_tx.gpio_num = gpio_num;
376 rmt_tx.mem_block_num = 1;
377 rmt_tx.clk_div = 80;
378 rmt_tx.tx_config.loop_en = false;
379 rmt_tx.tx_config.carrier_en = false;
380 rmt_tx.tx_config.idle_level = 1;
381 rmt_tx.tx_config.idle_output_en = true;
382 rmt_tx.rmt_mode = RMT_MODE_TX;
383 if (rmt_config(&rmt_tx) == ESP_OK)
384 {
385 if (rmt_driver_install(rmt_tx.channel, 0, ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_SHARED) == ESP_OK)
386 {
387 rmt_config_t rmt_rx;
388 rmt_rx.channel = info->rx_channel;
389 rmt_rx.gpio_num = gpio_num;
390 rmt_rx.clk_div = 80;
391 rmt_rx.mem_block_num = 1;
392 rmt_rx.rmt_mode = RMT_MODE_RX;
393 rmt_rx.rx_config.filter_en = true;
394 rmt_rx.rx_config.filter_ticks_thresh = 30;
395 rmt_rx.rx_config.idle_threshold = OW_DURATION_RX_IDLE;
396 if (rmt_config(&rmt_rx) == ESP_OK)
397 {
398 if (rmt_driver_install(rmt_rx.channel, 512, ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_SHARED) == ESP_OK)
399 {
400 rmt_get_ringbuf_handle(info->rx_channel, &info->rb);
401 status = OWB_STATUS_OK;
402 }
403 else
404 {
405 ESP_LOGE(TAG, "failed to install rx driver");
406 }
407 }
408 else
409 {
410 status = OWB_STATUS_HW_ERROR;
411 ESP_LOGE(TAG, "failed to configure rx, uninstalling rmt driver on tx channel");
412 rmt_driver_uninstall(rmt_tx.channel);
413 }
414 }
415 else
416 {
417 ESP_LOGE(TAG, "failed to install tx driver");
418 }
419 }
420 else
421 {
422 ESP_LOGE(TAG, "failed to configure tx");
423 }
424
425 // attach GPIO to previous pin
426 if (gpio_num < 32)
427 {
428 GPIO.enable_w1ts = (0x1 << gpio_num);
429 }
430 else
431 {
432 GPIO.enable1_w1ts.data = (0x1 << (gpio_num - 32));
433 }
434
435 // attach RMT channels to new gpio pin
436 // ATTENTION: set pin for rx first since gpio_output_disable() will
437 // remove rmt output signal in matrix!
438 rmt_set_pin(info->rx_channel, RMT_MODE_RX, gpio_num);
439 rmt_set_pin(info->tx_channel, RMT_MODE_TX, gpio_num);
440
441 // force pin direction to input to enable path to RX channel
442 PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[gpio_num]);
443
444 // enable open drain
445 GPIO.pin[gpio_num].pad_driver = 1;
446
447 return status;
448 }
449
450 OneWireBus * owb_rmt_initialize(owb_rmt_driver_info *info, uint8_t gpio_num,
451 rmt_channel_t tx_channel, rmt_channel_t rx_channel)
452 {
453 ESP_LOGD(TAG, "%s: gpio_num: %d, tx_channel: %d, rx_channel: %d",
454 __func__, gpio_num, tx_channel, rx_channel);
455
456 owb_status status = _init(info, gpio_num, tx_channel, rx_channel);
457 if(status != OWB_STATUS_OK)
458 {
459 ESP_LOGE(TAG, "_init() failed with status %d", status);
460 }
461
462 return &(info->bus); 645 return &(info->bus);
463 } 646
647 // ----- error: unwind allocated resources -----
648 exit_delete_copy_encoder:
649 ESP_ERROR_CHECK(rmt_del_encoder(info->copy_encoder_handle));
650 exit_disable_tx_channel:
651 ESP_ERROR_CHECK(rmt_disable (info->tx_channel_handle));
652 exit_delete_tx_channel:
653 ESP_ERROR_CHECK(rmt_del_channel (info->tx_channel_handle));
654 exit_disable_rx_channel:
655 ESP_ERROR_CHECK(rmt_disable (info->rx_channel_handle));
656 exit_release_rx_buffer:
657 free (info->rx_buffer);
658 exit_delete_rx_queue:
659 vQueueDelete (info->rx_queue);
660 exit_delete_rx_channel:
661 ESP_ERROR_CHECK(rmt_del_channel (info->rx_channel_handle));
662 exit_err:
663 ESP_LOGE(TAG, "%s: failed", __func__);
664 return NULL;
665 }

mercurial