-
Notifications
You must be signed in to change notification settings - Fork 52
/
owb_gpio.c
289 lines (239 loc) · 8.45 KB
/
owb_gpio.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
/*
* MIT License
*
* Copyright (c) 2017 David Antliff
* Copyright (c) 2017 Chris Morgan <[email protected]>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* @file
*/
#include <stddef.h>
#include <stdbool.h>
#include <inttypes.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "sdkconfig.h"
#include "driver/gpio.h"
#include "rom/ets_sys.h" // for ets_delay_us()
#include "rom/gpio.h" // for gpio_pad_select_gpio()
#include "owb.h"
#include "owb_gpio.h"
static const char * TAG = "owb_gpio";
// Define PHY_DEBUG to enable GPIO output around when the bus is sampled
// by the master (this library). This GPIO output makes it possible to
// validate the master's sampling using an oscilloscope.
//
// For the debug GPIO the idle state is low and made high before the 1-wire sample
// point and low again after the sample point
#undef PHY_DEBUG
#ifdef PHY_DEBUG
// Update these defines to a pin that you can access
#define PHY_DEBUG_GPIO GPIO_NUM_27
#define PHY_DEBUG_GPIO_MASK GPIO_SEL_27
#endif
/// @cond ignore
struct _OneWireBus_Timing
{
uint32_t A, B, C, D, E, F, G, H, I, J;
};
/// @endcond
// 1-Wire timing delays (standard) in microseconds.
// Labels and values are from https://www.maximintegrated.com/en/app-notes/index.mvp/id/126
static const struct _OneWireBus_Timing _StandardTiming = {
6, // A - read/write "1" master pull DQ low duration
64, // B - write "0" master pull DQ low duration
60, // C - write "1" master pull DQ high duration
10, // D - write "0" master pull DQ high duration
9, // E - read master pull DQ high duration
55, // F - complete read timeslot + 10ms recovery
0, // G - wait before reset
480, // H - master pull DQ low duration
70, // I - master pull DQ high duration
410, // J - complete presence timeslot + recovery
};
static void _us_delay(uint32_t time_us)
{
ets_delay_us(time_us);
}
/// @cond ignore
#define info_from_bus(owb) container_of(owb, owb_gpio_driver_info, bus)
/// @endcond
/**
* @brief Generate a 1-Wire reset (initialization).
* @param[in] bus Initialised bus instance.
* @param[out] is_present true if device is present, otherwise false.
* @return status
*/
static owb_status _reset(const OneWireBus * bus, bool * is_present)
{
bool present = false;
portMUX_TYPE timeCriticalMutex = portMUX_INITIALIZER_UNLOCKED;
portENTER_CRITICAL(&timeCriticalMutex);
owb_gpio_driver_info *i = info_from_bus(bus);
gpio_set_direction(i->gpio, GPIO_MODE_OUTPUT);
_us_delay(bus->timing->G);
gpio_set_level(i->gpio, 0); // Drive DQ low
_us_delay(bus->timing->H);
gpio_set_direction(i->gpio, GPIO_MODE_INPUT); // Release the bus
gpio_set_level(i->gpio, 1); // Reset the output level for the next output
_us_delay(bus->timing->I);
#ifdef PHY_DEBUG
gpio_set_level(PHY_DEBUG_GPIO, 1);
#endif
int level1 = gpio_get_level(i->gpio);
#ifdef PHY_DEBUG
gpio_set_level(PHY_DEBUG_GPIO, 0);
#endif
_us_delay(bus->timing->J); // Complete the reset sequence recovery
#ifdef PHY_DEBUG
gpio_set_level(PHY_DEBUG_GPIO, 1);
#endif
int level2 = gpio_get_level(i->gpio);
#ifdef PHY_DEBUG
gpio_set_level(PHY_DEBUG_GPIO, 0);
#endif
portEXIT_CRITICAL(&timeCriticalMutex);
present = (level1 == 0) && (level2 == 1); // Sample for presence pulse from slave
ESP_LOGD(TAG, "reset: level1 0x%x, level2 0x%x, present %d", level1, level2, present);
*is_present = present;
return OWB_STATUS_OK;
}
/**
* @brief Send a 1-Wire write bit, with recovery time.
* @param[in] bus Initialised bus instance.
* @param[in] bit The value to send.
*/
static void _write_bit(const OneWireBus * bus, int bit)
{
int delay1 = bit ? bus->timing->A : bus->timing->C;
int delay2 = bit ? bus->timing->B : bus->timing->D;
owb_gpio_driver_info *i = info_from_bus(bus);
portMUX_TYPE timeCriticalMutex = portMUX_INITIALIZER_UNLOCKED;
portENTER_CRITICAL(&timeCriticalMutex);
gpio_set_direction(i->gpio, GPIO_MODE_OUTPUT);
gpio_set_level(i->gpio, 0); // Drive DQ low
_us_delay(delay1);
gpio_set_level(i->gpio, 1); // Release the bus
_us_delay(delay2);
portEXIT_CRITICAL(&timeCriticalMutex);
}
/**
* @brief Read a bit from the 1-Wire bus and return the value, with recovery time.
* @param[in] bus Initialised bus instance.
*/
static int _read_bit(const OneWireBus * bus)
{
int result = 0;
owb_gpio_driver_info *i = info_from_bus(bus);
portMUX_TYPE timeCriticalMutex = portMUX_INITIALIZER_UNLOCKED;
portENTER_CRITICAL(&timeCriticalMutex);
gpio_set_direction(i->gpio, GPIO_MODE_OUTPUT);
gpio_set_level(i->gpio, 0); // Drive DQ low
_us_delay(bus->timing->A);
gpio_set_direction(i->gpio, GPIO_MODE_INPUT); // Release the bus
gpio_set_level(i->gpio, 1); // Reset the output level for the next output
_us_delay(bus->timing->E);
#ifdef PHY_DEBUG
gpio_set_level(PHY_DEBUG_GPIO, 1);
#endif
int level = gpio_get_level(i->gpio);
#ifdef PHY_DEBUG
gpio_set_level(PHY_DEBUG_GPIO, 0);
#endif
_us_delay(bus->timing->F); // Complete the timeslot and 10us recovery
portEXIT_CRITICAL(&timeCriticalMutex);
result = level & 0x01;
return result;
}
/**
* @brief Write 1-Wire data byte.
* NOTE: The data is shifted out of the low bits, eg. it is written in the order of lsb to msb
* @param[in] bus Initialised bus instance.
* @param[in] data Value to write.
* @param[in] number_of_bits_to_read bits to write
*/
static owb_status _write_bits(const OneWireBus * bus, uint8_t data, int number_of_bits_to_write)
{
ESP_LOGD(TAG, "write 0x%02x", data);
for (int i = 0; i < number_of_bits_to_write; ++i)
{
_write_bit(bus, data & 0x01);
data >>= 1;
}
return OWB_STATUS_OK;
}
/**
* @brief Read 1-Wire data byte from bus.
* NOTE: Data is read into the high bits, eg. each bit read is shifted down before the next bit is read
* @param[in] bus Initialised bus instance.
* @return Byte value read from bus.
*/
static owb_status _read_bits(const OneWireBus * bus, uint8_t *out, int number_of_bits_to_read)
{
uint8_t result = 0;
for (int i = 0; i < number_of_bits_to_read; ++i)
{
result >>= 1;
if (_read_bit(bus))
{
result |= 0x80;
}
}
ESP_LOGD(TAG, "read 0x%02x", result);
*out = result;
return OWB_STATUS_OK;
}
static owb_status _uninitialize(const OneWireBus * bus)
{
// Nothing to do here for this driver_info
return OWB_STATUS_OK;
}
static const struct owb_driver gpio_function_table =
{
.name = "owb_gpio",
.uninitialize = _uninitialize,
.reset = _reset,
.write_bits = _write_bits,
.read_bits = _read_bits
};
OneWireBus* owb_gpio_initialize(owb_gpio_driver_info * driver_info, int gpio)
{
ESP_LOGD(TAG, "%s(): gpio %d\n", __func__, gpio);
driver_info->gpio = gpio;
driver_info->bus.driver = &gpio_function_table;
driver_info->bus.timing = &_StandardTiming;
driver_info->bus.strong_pullup_gpio = GPIO_NUM_NC;
// platform specific:
gpio_pad_select_gpio(driver_info->gpio);
#ifdef PHY_DEBUG
gpio_config_t io_conf;
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = PHY_DEBUG_GPIO_MASK;
io_conf.pull_down_en = GPIO_PULLDOWN_ENABLE;
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
ESP_ERROR_CHECK(gpio_config(&io_conf));
#endif
return &(driver_info->bus);
}