Tue, 26 Sep 2023 14:57:18 +0200
Migrated to isp-idf v5.1
72 | 1 | /** |
2 | * Copyright (c) 2023 mjcross | |
3 | * | |
4 | * SPDX-License-Identifier: MIT | |
5 | **/ | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
6 | |
72 | 7 | #include "esp_log.h" |
8 | #include "driver/rmt_tx.h" | |
9 | #include "driver/rmt_rx.h" | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
10 | |
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
11 | #include "owb.h" |
72 | 12 | #include "owb_rmt_bus_timings.h" |
13 | #include "owb_rmt_bus_symbols.h" | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
14 | |
72 | 15 | #define OWB_RMT_CLK_HZ 1000000 // run the RMT at 1MHz to get 1us ticks |
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 | #define OWB_RMT_TX_QUEUE_DEPTH 4 // max pending TX transfers | |
18 | #define OWB_RMT_MAX_READ_BITS 64 // maximum number of bits that will be read at once (used to calculate buffer size) | |
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 | #define OWB_RMT_RX_MIN_NS 1000 // RMT receive channel glitch rejection threshold (ns) | |
21 | #define OWB_RMT_TIMEOUT_MS 1000 // timeout threshold for an RMT task (ms) | |
22 | #define OWB_TIMING_MARGIN 3 // timing variation permitted by our event parsing functions (in microsec) | |
23 | ||
24 | // debug parsing of RMT raw symbols | |
25 | //#define OWB_RMT_DEBUG | |
26 | ||
27 | // tag for log messages | |
28 | static const char * TAG = "owb_rmt"; | |
29 | ||
30 | ||
31 | //------ | |
32 | // private API functions and constants | |
33 | //------ | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
34 | |
72 | 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 | }; | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
59 | |
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
60 | |
72 | 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 | } | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
94 | |
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
95 | |
72 | 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; | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
105 | |
72 | 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 | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
118 | |
72 | 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 | } | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
129 | |
72 | 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 | } | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
138 | |
72 | 139 | // anything else is invalid |
140 | return OWB_STATUS_HW_ERROR; | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
141 | } |
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
142 | |
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
143 | |
72 | 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; | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
155 | |
72 | 156 | #ifdef OWB_RMT_DEBUG |
157 | // display raw RMT symbols | |
158 | ESP_LOGI(TAG, "parse bits: %d symbols", (int)num_symbols); | |
159 | #endif | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
160 | |
72 | 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; | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
171 | |
72 | 172 | #ifdef OWB_RMT_DEBUG |
173 | ESP_LOGI (TAG, "\t\tdetect '1' -> 0x%llx", *result); | |
174 | #endif | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
175 | |
72 | 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 | ||
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
190 | |
72 | 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; | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
200 | |
72 | 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; | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
213 | } |
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
214 | |
72 | 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 | } | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
226 | |
72 | 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 | } | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
232 | |
72 | 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); | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
242 | } |
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
243 | |
72 | 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; | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
253 | |
72 | 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; | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
267 | } |
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
268 | |
72 | 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; | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
274 | } |
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
275 | |
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
276 | |
72 | 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); | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
293 | return OWB_STATUS_TOO_MANY_BITS; |
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
294 | } |
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
295 | |
72 | 296 | // identify the rmt_driver_info structure that contains `bus` |
297 | owb_rmt_driver_info *info = __containerof(bus, owb_rmt_driver_info, bus); | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
298 | |
72 | 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 | } | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
308 | |
72 | 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 | } | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
320 | } |
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
321 | |
72 | 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 | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
325 | } |
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
326 | |
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
327 | return OWB_STATUS_OK; |
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
328 | } |
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
329 | |
72 | 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) | |
506 | // | |
507 | static struct owb_driver rmt_driver_functions = { | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
508 | .name = "owb_rmt", |
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
509 | .uninitialize = _uninitialize, |
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
510 | .reset = _reset, |
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
511 | .write_bits = _write_bits, |
72 | 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 | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
515 | }; |
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
516 | |
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
517 | |
72 | 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) | |
521 | { | |
522 | //* The function now ignores tx_channel and rx_channel as the new RMT driver allocates channels on demand. | |
523 | //* The parameters are kept in the call to preserve compatibility with previous versions. | |
524 | ||
525 | // the steps to enable the RMT resources are documented in: | |
526 | // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/rmt.html | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
527 | |
72 | 528 | // Note: keeping the TX and RX initialisations together in one function simplifies the error handling |
529 | ||
530 | (void)tx_channel; // avoid compiler warning about unused parameter | |
531 | (void)rx_channel; // avoid compiler warning about unused parameter | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
532 | |
72 | 533 | // sanity check |
534 | if (info == NULL) { | |
535 | ESP_LOGE(TAG, "info is NULL"); | |
536 | goto exit_err; | |
537 | } | |
538 | ||
539 | // ----- receive channel ----- | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
540 | |
72 | 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 | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
551 | } |
72 | 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; | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
566 | } |
72 | 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; | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
589 | } |
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
590 | |
72 | 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; | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
612 | } |
72 | 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; | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
625 | } |
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
626 | |
72 | 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; | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
638 | } |
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
639 | |
72 | 640 | |
641 | // ----- success ------ | |
642 | info->gpio = gpio_num; | |
643 | info->bus.driver = &rmt_driver_functions; // route driver API calls to the functions in this file | |
644 | ESP_LOGI(TAG, "%s: OK", __func__); | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
645 | return &(info->bus); |
72 | 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; | |
0
88d965579617
Initial import of the CO2 meter application.
Michiel Broek <mbroek@mbse.eu>
parents:
diff
changeset
|
665 | } |