ESPHome  2024.8.3
gpio_one_wire.cpp
Go to the documentation of this file.
1 #include "gpio_one_wire.h"
2 #include "esphome/core/log.h"
3 #include "esphome/core/helpers.h"
4 
5 namespace esphome {
6 namespace gpio {
7 
8 static const char *const TAG = "gpio.one_wire";
9 
11  ESP_LOGCONFIG(TAG, "Setting up 1-wire bus...");
12  this->t_pin_->setup();
13  // clear bus with 480µs high, otherwise initial reset in search might fail
15  delayMicroseconds(480);
16  this->search();
17 }
18 
20  ESP_LOGCONFIG(TAG, "GPIO 1-wire bus:");
21  LOG_PIN(" Pin: ", this->t_pin_);
22  this->dump_devices_(TAG);
23 }
24 
25 bool HOT IRAM_ATTR GPIOOneWireBus::reset() {
26  // See reset here:
27  // https://www.maximintegrated.com/en/design/technical-documents/app-notes/1/126.html
28  // Wait for communication to clear (delay G)
30  uint8_t retries = 125;
31  do {
32  if (--retries == 0)
33  return false;
35  } while (!pin_.digital_read());
36 
37  bool r;
38 
39  // Send 480µs LOW TX reset pulse (drive bus low, delay H)
41  pin_.digital_write(false);
42  delayMicroseconds(480);
43 
44  // Release the bus, delay I
47 
48  // sample bus, 0=device(s) present, 1=no device present
49  r = !pin_.digital_read();
50  // delay J
51  delayMicroseconds(410);
52  return r;
53 }
54 
55 void HOT IRAM_ATTR GPIOOneWireBus::write_bit_(bool bit) {
56  // drive bus low
58  pin_.digital_write(false);
59 
60  // from datasheet:
61  // write 0 low time: t_low0: min=60µs, max=120µs
62  // write 1 low time: t_low1: min=1µs, max=15µs
63  // time slot: t_slot: min=60µs, max=120µs
64  // recovery time: t_rec: min=1µs
65  // ds18b20 appears to read the bus after roughly 14µs
66  uint32_t delay0 = bit ? 6 : 60;
67  uint32_t delay1 = bit ? 59 : 5;
68 
69  // delay A/C
70  delayMicroseconds(delay0);
71  // release bus
72  pin_.digital_write(true);
73  // delay B/D
74  delayMicroseconds(delay1);
75 }
76 
77 bool HOT IRAM_ATTR GPIOOneWireBus::read_bit_() {
78  // drive bus low
80  pin_.digital_write(false);
81 
82  // note: for reading we'll need very accurate timing, as the
83  // timing for the digital_read() is tight; according to the datasheet,
84  // we should read at the end of 16µs starting from the bus low
85  // typically, the ds18b20 pulls the line high after 11µs for a logical 1
86  // and 29µs for a logical 0
87 
88  uint32_t start = micros();
89  // datasheet says >1µs
91 
92  // release bus, delay E
94 
95  // measure from start value directly, to get best accurate timing no matter
96  // how long pin_mode/delayMicroseconds took
97  uint32_t now = micros();
98  if (now - start < 12)
99  delayMicroseconds(12 - (now - start));
100 
101  // sample bus to read bit from peer
102  bool r = pin_.digital_read();
103 
104  // read slot is at least 60µs; get as close to 60µs to spend less time with interrupts locked
105  now = micros();
106  if (now - start < 60)
107  delayMicroseconds(60 - (now - start));
108 
109  return r;
110 }
111 
112 void IRAM_ATTR GPIOOneWireBus::write8(uint8_t val) {
113  for (uint8_t i = 0; i < 8; i++) {
114  this->write_bit_(bool((1u << i) & val));
115  }
116 }
117 
118 void IRAM_ATTR GPIOOneWireBus::write64(uint64_t val) {
119  for (uint8_t i = 0; i < 64; i++) {
120  this->write_bit_(bool((1ULL << i) & val));
121  }
122 }
123 
124 uint8_t IRAM_ATTR GPIOOneWireBus::read8() {
125  uint8_t ret = 0;
126  for (uint8_t i = 0; i < 8; i++) {
127  ret |= (uint8_t(this->read_bit_()) << i);
128  }
129  return ret;
130 }
131 
132 uint64_t IRAM_ATTR GPIOOneWireBus::read64() {
133  uint64_t ret = 0;
134  for (uint8_t i = 0; i < 8; i++) {
135  ret |= (uint64_t(this->read_bit_()) << i);
136  }
137  return ret;
138 }
139 
141  this->last_discrepancy_ = 0;
142  this->last_device_flag_ = false;
143  this->address_ = 0;
144 }
145 
146 uint64_t IRAM_ATTR GPIOOneWireBus::search_int() {
147  if (this->last_device_flag_)
148  return 0u;
149 
150  uint8_t last_zero = 0;
151  uint64_t bit_mask = 1;
152  uint64_t address = this->address_;
153 
154  // Initiate search
155  for (int bit_number = 1; bit_number <= 64; bit_number++, bit_mask <<= 1) {
156  // read bit
157  bool id_bit = this->read_bit_();
158  // read its complement
159  bool cmp_id_bit = this->read_bit_();
160 
161  if (id_bit && cmp_id_bit) {
162  // No devices participating in search
163  return 0;
164  }
165 
166  bool branch;
167 
168  if (id_bit != cmp_id_bit) {
169  // only chose one branch, the other one doesn't have any devices.
170  branch = id_bit;
171  } else {
172  // there are devices with both 0s and 1s at this bit
173  if (bit_number < this->last_discrepancy_) {
174  branch = (address & bit_mask) > 0;
175  } else {
176  branch = bit_number == this->last_discrepancy_;
177  }
178 
179  if (!branch) {
180  last_zero = bit_number;
181  }
182  }
183 
184  if (branch) {
185  address |= bit_mask;
186  } else {
187  address &= ~bit_mask;
188  }
189 
190  // choose/announce branch
191  this->write_bit_(branch);
192  }
193 
194  this->last_discrepancy_ = last_zero;
195  if (this->last_discrepancy_ == 0) {
196  // we're at root and have no choices left, so this was the last one.
197  this->last_device_flag_ = true;
198  }
199 
200  this->address_ = address;
201  return address;
202 }
203 
204 } // namespace gpio
205 } // namespace esphome
virtual void pin_mode(gpio::Flags flags)=0
mopeka_std_values val[4]
virtual void setup()=0
uint32_t IRAM_ATTR HOT micros()
Definition: core.cpp:27
void search()
Search for 1-Wire devices on the bus.
void dump_devices_(const char *tag)
log the found devices
void write64(uint64_t val) override
void pin_mode(gpio::Flags flags)
Definition: gpio.cpp:128
uint64_t search_int() override
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition: core.cpp:28
void write8(uint8_t val) override
void digital_write(bool value)
Definition: gpio.cpp:121