esp32-warm-water
esp32 based project for the control of a heating element based on temperature
owb_rmt.c
Go to the documentation of this file.
1 /*
2 Created by Chris Morgan based on the nodemcu project driver.
3 Copyright 2017 Chris Morgan <chmorgan@gmail.com>
4 
5 Ported to ESP32 RMT peripheral for low-level signal generation by Arnim Laeuger.
6 
7 Permission is hereby granted, free of charge, to any person obtaining
8 a copy of this software and associated documentation files (the
9 "Software"), to deal in the Software without restriction, including
10 without limitation the rights to use, copy, modify, merge, publish,
11 distribute, sublicense, and/or sell copies of the Software, and to
12 permit persons to whom the Software is furnished to do so, subject to
13 the following conditions:
14 
15 The above copyright notice and this permission notice shall be
16 included in all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 
26 Much of the code was inspired by Derek Yerger's code, though I don't
27 think much of that remains. In any event that was..
28  (copyleft) 2006 by Derek Yerger - Free to distribute freely.
29 
30 The CRC code was excerpted and inspired by the Dallas Semiconductor
31 sample code bearing this copyright.
32 //---------------------------------------------------------------------------
33 // Copyright (C) 2000 Dallas Semiconductor Corporation, All Rights Reserved.
34 //
35 // Permission is hereby granted, free of charge, to any person obtaining a
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 "sdkconfig.h"
64 #include "esp_log.h"
65 
66 #undef OW_DEBUG
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 // maximum number of bits that can be read or written per slot
85 #define MAX_BITS_PER_SLOT (8)
86 
87 static const char *TAG = CONFIG_OWB_RMT_TAG;
88 
89 #define info_of_driver(owb) container_of(owb, owb_rmt_driver_info, bus)
90 
91 // flush any pending/spurious traces from the RX channel
92 static void onewire_flush_rmt_rx_buf(const OneWireBus *bus)
93 {
94  void *p = NULL;
95  size_t s = 0;
96 
98 
99  while ((p = xRingbufferReceive(i->rb, &s, 0)))
100  {
101  ESP_LOGD(TAG, "flushing entry");
102  vRingbufferReturnItem(i->rb, p);
103  }
104 }
105 
106 static owb_status _reset(const OneWireBus *bus, bool *is_present)
107 {
108  rmt_item32_t tx_items[1] = {0};
109  bool _is_present = false;
110  int res = OWB_STATUS_OK;
111 
113 
114  tx_items[0].duration0 = OW_DURATION_RESET;
115  tx_items[0].level0 = 0;
116  tx_items[0].duration1 = 0;
117  tx_items[0].level1 = 1;
118 
119  uint16_t old_rx_thresh = 0;
120  rmt_get_rx_idle_thresh(i->rx_channel, &old_rx_thresh);
121  rmt_set_rx_idle_thresh(i->rx_channel, OW_DURATION_RESET + 60);
122 
123  onewire_flush_rmt_rx_buf(bus);
124  rmt_rx_start(i->rx_channel, true);
125  if (rmt_write_items(i->tx_channel, tx_items, 1, true) == ESP_OK)
126  {
127  size_t rx_size = 0;
128  rmt_item32_t *rx_items = (rmt_item32_t *)xRingbufferReceive(i->rb, &rx_size, 100 / portTICK_PERIOD_MS);
129 
130  if (rx_items)
131  {
132  if (rx_size >= (1 * sizeof(rmt_item32_t)))
133  {
134 #ifdef OW_DEBUG
135  ESP_LOGI(TAG, "rx_size: %d", rx_size);
136 
137  for (int i = 0; i < (rx_size / sizeof(rmt_item32_t)); i++)
138  {
139  ESP_LOGI(TAG, "i: %d, level0: %d, duration %d", i, rx_items[i].level0, rx_items[i].duration0);
140  ESP_LOGI(TAG, "i: %d, level1: %d, duration %d", i, rx_items[i].level1, rx_items[i].duration1);
141  }
142 #endif
143 
144  // parse signal and search for presence pulse
145  if ((rx_items[0].level0 == 0) && (rx_items[0].duration0 >= OW_DURATION_RESET - 2))
146  {
147  if ((rx_items[0].level1 == 1) && (rx_items[0].duration1 > 0))
148  {
149  if (rx_items[1].level0 == 0)
150  {
151  _is_present = true;
152  }
153  }
154  }
155  }
156 
157  vRingbufferReturnItem(i->rb, (void *)rx_items);
158  }
159  else
160  {
161  // time out occurred, this indicates an unconnected / misconfigured bus
162  ESP_LOGE(TAG, "rx_items == 0");
163  res = OWB_STATUS_HW_ERROR;
164  }
165  }
166  else
167  {
168  // error in tx channel
169  ESP_LOGE(TAG, "Error tx");
170  res = OWB_STATUS_HW_ERROR;
171  }
172 
173  rmt_rx_stop(i->rx_channel);
174  rmt_set_rx_idle_thresh(i->rx_channel, old_rx_thresh);
175 
176  *is_present = _is_present;
177 
178  ESP_LOGD(TAG, "_is_present %d", _is_present);
179 
180  return res;
181 }
182 
183 static rmt_item32_t _encode_write_slot(uint8_t val)
184 {
185  rmt_item32_t item = {0};
186 
187  item.level0 = 0;
188  item.level1 = 1;
189  if (val)
190  {
191  // write "1" slot
192  item.duration0 = OW_DURATION_1_LOW;
193  item.duration1 = OW_DURATION_1_HIGH;
194  }
195  else
196  {
197  // write "0" slot
198  item.duration0 = OW_DURATION_0_LOW;
199  item.duration1 = OW_DURATION_0_HIGH;
200  }
201 
202  return item;
203 }
204 
206 static owb_status _write_bits(const OneWireBus *bus, uint8_t out, int number_of_bits_to_write)
207 {
208  rmt_item32_t tx_items[MAX_BITS_PER_SLOT + 1] = {0};
209  owb_rmt_driver_info *info = info_of_driver(bus);
210 
211  if (number_of_bits_to_write > MAX_BITS_PER_SLOT)
212  {
214  }
215 
216  // write requested bits as pattern to TX buffer
217  for (int i = 0; i < number_of_bits_to_write; i++)
218  {
219  tx_items[i] = _encode_write_slot(out & 0x01);
220  out >>= 1;
221  }
222 
223  // end marker
224  tx_items[number_of_bits_to_write].level0 = 1;
225  tx_items[number_of_bits_to_write].duration0 = 0;
226 
228 
229  if (rmt_write_items(info->tx_channel, tx_items, number_of_bits_to_write + 1, true) == ESP_OK)
230  {
231  status = OWB_STATUS_OK;
232  }
233  else
234  {
235  status = OWB_STATUS_HW_ERROR;
236  ESP_LOGE(TAG, "rmt_write_items() failed");
237  }
238 
239  return status;
240 }
241 
242 static rmt_item32_t _encode_read_slot(void)
243 {
244  rmt_item32_t item = {0};
245 
246  // construct pattern for a single read time slot
247  item.level0 = 0;
248  item.duration0 = OW_DURATION_1_LOW; // shortly force 0
249  item.level1 = 1;
250  item.duration1 = OW_DURATION_1_HIGH; // release high and finish slot
251  return item;
252 }
253 
255 static owb_status _read_bits(const OneWireBus *bus, uint8_t *in, int number_of_bits_to_read)
256 {
257  rmt_item32_t tx_items[MAX_BITS_PER_SLOT + 1] = {0};
258  uint8_t read_data = 0;
259  int res = OWB_STATUS_OK;
260 
261  owb_rmt_driver_info *info = info_of_driver(bus);
262 
263  if (number_of_bits_to_read > MAX_BITS_PER_SLOT)
264  {
265  ESP_LOGE(TAG, "_read_bits() OWB_STATUS_TOO_MANY_BITS");
267  }
268 
269  // generate requested read slots
270  for (int i = 0; i < number_of_bits_to_read; i++)
271  {
272  tx_items[i] = _encode_read_slot();
273  }
274 
275  // end marker
276  tx_items[number_of_bits_to_read].level0 = 1;
277  tx_items[number_of_bits_to_read].duration0 = 0;
278 
279  onewire_flush_rmt_rx_buf(bus);
280  rmt_rx_start(info->rx_channel, true);
281  if (rmt_write_items(info->tx_channel, tx_items, number_of_bits_to_read + 1, true) == ESP_OK)
282  {
283  size_t rx_size = 0;
284  rmt_item32_t *rx_items = (rmt_item32_t *)xRingbufferReceive(info->rb, &rx_size, 100 / portTICK_PERIOD_MS);
285 
286  if (rx_items)
287  {
288 #ifdef OW_DEBUG
289  for (int i = 0; i < rx_size / 4; i++)
290  {
291  ESP_LOGI(TAG, "level: %d, duration %d", rx_items[i].level0, rx_items[i].duration0);
292  ESP_LOGI(TAG, "level: %d, duration %d", rx_items[i].level1, rx_items[i].duration1);
293  }
294 #endif
295 
296  if (rx_size >= number_of_bits_to_read * sizeof(rmt_item32_t))
297  {
298  for (int i = 0; i < number_of_bits_to_read; i++)
299  {
300  read_data >>= 1;
301  // parse signal and identify logical bit
302  if (rx_items[i].level1 == 1)
303  {
304  if ((rx_items[i].level0 == 0) && (rx_items[i].duration0 < OW_DURATION_SAMPLE))
305  {
306  // rising edge occured before 15us -> bit 1
307  read_data |= 0x80;
308  }
309  }
310  }
311  read_data >>= 8 - number_of_bits_to_read;
312  }
313 
314  vRingbufferReturnItem(info->rb, (void *)rx_items);
315  }
316  else
317  {
318  // time out occurred, this indicates an unconnected / misconfigured bus
319  ESP_LOGE(TAG, "rx_items == 0");
320  res = OWB_STATUS_HW_ERROR;
321  }
322  }
323  else
324  {
325  // error in tx channel
326  ESP_LOGE(TAG, "Error tx");
327  res = OWB_STATUS_HW_ERROR;
328  }
329 
330  rmt_rx_stop(info->rx_channel);
331 
332  *in = read_data;
333  return res;
334 }
335 
336 static owb_status _uninitialize(const OneWireBus *bus)
337 {
338  owb_rmt_driver_info *info = info_of_driver(bus);
339 
340  rmt_driver_uninstall(info->tx_channel);
341  rmt_driver_uninstall(info->rx_channel);
342 
343  return OWB_STATUS_OK;
344 }
345 
346 static struct owb_driver rmt_function_table =
347  {
348  .name = "owb_rmt",
349  .uninitialize = _uninitialize,
350  .reset = _reset,
351  .write_bits = _write_bits,
352  .read_bits = _read_bits};
353 
354 static owb_status _init(owb_rmt_driver_info *info, gpio_num_t gpio_num,
355  rmt_channel_t tx_channel, rmt_channel_t rx_channel)
356 {
358 
359  // Ensure the RMT peripheral is not already running
360  // Note: if using RMT elsewhere, don't call this here, call it at the start of your program instead.
361  //periph_module_disable(PERIPH_RMT_MODULE);
362  //periph_module_enable(PERIPH_RMT_MODULE);
363 
364  info->bus.driver = &rmt_function_table;
365  info->tx_channel = tx_channel;
366  info->rx_channel = rx_channel;
367  info->gpio = gpio_num;
368 
369 #ifdef OW_DEBUG
370  ESP_LOGI(TAG, "RMT TX channel: %d", info->tx_channel);
371  ESP_LOGI(TAG, "RMT RX channel: %d", info->rx_channel);
372 #endif
373 
374  rmt_config_t rmt_tx = {0};
375  rmt_tx.channel = info->tx_channel;
376  rmt_tx.gpio_num = gpio_num;
377  rmt_tx.mem_block_num = 1;
378  rmt_tx.clk_div = 80;
379  rmt_tx.tx_config.loop_en = false;
380  rmt_tx.tx_config.carrier_en = false;
381  rmt_tx.tx_config.idle_level = 1;
382  rmt_tx.tx_config.idle_output_en = true;
383  rmt_tx.rmt_mode = RMT_MODE_TX;
384  if (rmt_config(&rmt_tx) == ESP_OK)
385  {
386  rmt_set_source_clk(info->tx_channel, RMT_BASECLK_APB); // only APB is supported by IDF 4.2
387  if (rmt_driver_install(rmt_tx.channel, 0, ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_SHARED) == ESP_OK)
388  {
389  rmt_config_t rmt_rx = {0};
390  rmt_rx.channel = info->rx_channel;
391  rmt_rx.gpio_num = gpio_num;
392  rmt_rx.clk_div = 80;
393  rmt_rx.mem_block_num = 1;
394  rmt_rx.rmt_mode = RMT_MODE_RX;
395  rmt_rx.rx_config.filter_en = true;
396  rmt_rx.rx_config.filter_ticks_thresh = 30;
397  rmt_rx.rx_config.idle_threshold = OW_DURATION_RX_IDLE;
398  if (rmt_config(&rmt_rx) == ESP_OK)
399  {
400  rmt_set_source_clk(info->rx_channel, RMT_BASECLK_APB); // only APB is supported by IDF 4.2
401  if (rmt_driver_install(rmt_rx.channel, 512, ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_SHARED) == ESP_OK)
402  {
403  rmt_get_ringbuf_handle(info->rx_channel, &info->rb);
404  status = OWB_STATUS_OK;
405  }
406  else
407  {
408  ESP_LOGE(TAG, "failed to install rx driver");
409  }
410  }
411  else
412  {
413  status = OWB_STATUS_HW_ERROR;
414  ESP_LOGE(TAG, "failed to configure rx, uninstalling rmt driver on tx channel");
415  rmt_driver_uninstall(rmt_tx.channel);
416  }
417  }
418  else
419  {
420  ESP_LOGE(TAG, "failed to install tx driver");
421  }
422  }
423  else
424  {
425  ESP_LOGE(TAG, "failed to configure tx");
426  }
427 
428  // attach GPIO to previous pin
429  if (gpio_num < 32)
430  {
431  GPIO.enable_w1ts = (0x1 << gpio_num);
432  }
433  else
434  {
435  GPIO.enable1_w1ts.data = (0x1 << (gpio_num - 32));
436  }
437 
438  // attach RMT channels to new gpio pin
439  // ATTENTION: set pin for rx first since gpio_output_disable() will
440  // remove rmt output signal in matrix!
441  rmt_set_pin(info->rx_channel, RMT_MODE_RX, gpio_num);
442  rmt_set_pin(info->tx_channel, RMT_MODE_TX, gpio_num);
443 
444  // force pin direction to input to enable path to RX channel
445  PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[gpio_num]);
446 
447  // enable open drain
448  GPIO.pin[gpio_num].pad_driver = 1;
449 
450  return status;
451 }
452 
454  rmt_channel_t tx_channel, rmt_channel_t rx_channel)
455 {
456  ESP_LOGD(TAG, "%s: gpio_num: %d, tx_channel: %d, rx_channel: %d",
457  __func__, gpio_num, tx_channel, rx_channel);
458 
459  owb_status status = _init(info, gpio_num, tx_channel, rx_channel);
460  if (status != OWB_STATUS_OK)
461  {
462  ESP_LOGE(TAG, "_init() failed with status %d", status);
463  }
464 
466 
467  return &(info->bus);
468 }
Interface definitions for the 1-Wire bus component.
owb_status
Represents the result of OWB API functions.
Definition: owb.h:114
@ OWB_STATUS_OK
Operation succeeded.
Definition: owb.h:116
@ OWB_STATUS_TOO_MANY_BITS
Attempt to write an incorrect number of bits to the One Wire Bus.
Definition: owb.h:121
@ OWB_STATUS_HW_ERROR
A hardware error occurred.
Definition: owb.h:122
@ OWB_STATUS_NOT_SET
A status value has not been set.
Definition: owb.h:115
#define GPIO_NUM_NC
ESP-IDF prior to v4.x does not define GPIO_NUM_NC.
Definition: owb.h:60
#define OW_DURATION_RX_IDLE
Definition: owb_rmt.c:82
OneWireBus * owb_rmt_initialize(owb_rmt_driver_info *info, gpio_num_t gpio_num, rmt_channel_t tx_channel, rmt_channel_t rx_channel)
Initialise the RMT driver.
Definition: owb_rmt.c:453
#define OW_DURATION_RESET
Definition: owb_rmt.c:69
#define MAX_BITS_PER_SLOT
Definition: owb_rmt.c:85
#define OW_DURATION_1_HIGH
Definition: owb_rmt.c:74
#define OW_DURATION_SAMPLE
Definition: owb_rmt.c:79
#define info_of_driver(owb)
Definition: owb_rmt.c:89
#define OW_DURATION_1_LOW
Definition: owb_rmt.c:73
#define OW_DURATION_0_HIGH
Definition: owb_rmt.c:77
#define OW_DURATION_0_LOW
Definition: owb_rmt.c:76
Structure containing 1-Wire bus information relevant to a single instance.
Definition: owb.h:69
gpio_num_t strong_pullup_gpio
Set if an external strong pull-up circuit is required.
Definition: owb.h:73
const struct owb_driver * driver
Pointer to hardware driver instance.
Definition: owb.h:74
const char * name
Definition: owb.h:129
RMT driver information.
Definition: owb_rmt.h:51
int rx_channel
RMT channel to use for RX.
Definition: owb_rmt.h:53
int tx_channel
RMT channel to use for TX.
Definition: owb_rmt.h:52
OneWireBus bus
OneWireBus instance.
Definition: owb_rmt.h:56
int gpio
OneWireBus GPIO.
Definition: owb_rmt.h:55
RingbufHandle_t rb
Ring buffer handle.
Definition: owb_rmt.h:54
const char * TAG
Definition: wifi_sta.c:42