mirror of
https://github.com/espressif/esp-idf
synced 2025-03-10 09:39:10 -04:00
Merge branch 'example/rmt_ds18b20' into 'master'
example: simulate 1-wire protocol with RMT driver (take DS18B20 as example) Closes IDF-1457 See merge request espressif/esp-idf!18857
This commit is contained in:
commit
2e47a422e3
@ -38,7 +38,8 @@ typedef struct {
|
||||
struct {
|
||||
uint32_t invert_out: 1; /*!< Whether to invert the RMT channel signal before output to GPIO pad */
|
||||
uint32_t with_dma: 1; /*!< If set, the driver will allocate an RMT channel with DMA capability */
|
||||
uint32_t io_loop_back: 1; /*!< For debug/test, the signal output from the GPIO will be fed to the input path as well */
|
||||
uint32_t io_loop_back: 1; /*!< The signal output from the GPIO will be fed to the input path as well */
|
||||
uint32_t io_od_mode: 1; /*!< Configure the GPIO as open-drain mode */
|
||||
} flags; /*!< TX channel config flags */
|
||||
} rmt_tx_channel_config_t;
|
||||
|
||||
|
@ -276,8 +276,8 @@ esp_err_t rmt_new_tx_channel(const rmt_tx_channel_config_t *config, rmt_channel_
|
||||
tx_channel->base.gpio_num = config->gpio_num;
|
||||
gpio_config_t gpio_conf = {
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
// also enable the input path is `io_loop_back` is on, this is useful for debug
|
||||
.mode = GPIO_MODE_OUTPUT | (config->flags.io_loop_back ? GPIO_MODE_INPUT : 0),
|
||||
// also enable the input path is `io_loop_back` is on, this is useful for bi-directional buses
|
||||
.mode = (config->flags.io_od_mode ? GPIO_MODE_OUTPUT_OD : GPIO_MODE_OUTPUT) | (config->flags.io_loop_back ? GPIO_MODE_INPUT : 0),
|
||||
.pull_down_en = false,
|
||||
.pull_up_en = true,
|
||||
.pin_bit_mask = 1ULL << config->gpio_num,
|
||||
|
@ -82,7 +82,8 @@ To install an RMT TX channel, there's a configuration structure that needs to be
|
||||
- :cpp:member:`rmt_tx_channel_config_t::trans_queue_depth` sets the depth of internal transaction queue, the deeper the queue, the more transactions can be prepared in the backlog.
|
||||
- :cpp:member:`rmt_tx_channel_config_t::invert_out` is used to decide whether to invert the RMT signal before sending it to the GPIO pad.
|
||||
- :cpp:member:`rmt_tx_channel_config_t::with_dma` is used to indicate if the channel needs a DMA backend. A channel with DMA attached can offload the CPU by a lot. However, DMA backend is not available on all ESP chips, please refer to [`TRM <{IDF_TARGET_TRM_EN_URL}#rmt>`__] before you enable this option. Or you might encounter :c:macro:`ESP_ERR_NOT_SUPPORTED` error.
|
||||
- :cpp:member:`rmt_tx_channel_config_t::io_loop_back` is for debugging purposes only. It enables both the GPIO's input and output ability through the GPIO matrix peripheral. Meanwhile, if both TX and RX channels are bound to the same GPIO, then monitoring of the data transmission line can be realized.
|
||||
- :cpp:member:`rmt_tx_channel_config_t::io_loop_back` enables both the GPIO's input and output ability through the GPIO matrix peripheral. Meanwhile, if both TX and RX channels are bound to the same GPIO, then monitoring of the data transmission line can be realized.
|
||||
- :cpp:member:`rmt_tx_channel_config_t::io_od_mode` configures the GPIO as open-drain mode. It is useful for simulating bi-directional buses, sucn as 1-wire bus, combined with :cpp:member:`rmt_tx_channel_config_t::io_loop_back`.
|
||||
|
||||
Once the :cpp:type:`rmt_tx_channel_config_t` structure is populated with mandatory parameters, users can call :cpp:func:`rmt_new_tx_channel` to allocate and initialize a TX channel. This function will return an RMT channel handle if it runs correctly. Specifically, when there are no more free channels in the RMT resource pool, this function will return :c:macro:`ESP_ERR_NOT_FOUND` error. If some feature (e.g. DMA backend) is not supported by hardware, it will return :c:macro:`ESP_ERR_NOT_SUPPORTED` error.
|
||||
|
||||
@ -517,6 +518,7 @@ Application Examples
|
||||
* RMT transactions in queue: :example:`peripherals/rmt/musical_buzzer`
|
||||
* RMT based stepper motor with S-curve algorithm: : :example:`peripherals/rmt/stepper_motor`
|
||||
* RMT infinite loop for driving DShot ESC: :example:`peripherals/rmt/dshot_esc`
|
||||
* RMT simulate 1-wire protocol (take DS18B20 as example): :example:`peripherals/rmt/onewire_ds18b20`
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
@ -84,6 +84,10 @@ examples/peripherals/rmt/musical_buzzer:
|
||||
enable:
|
||||
- if: SOC_RMT_SUPPORT_TX_LOOP_COUNT == 1
|
||||
|
||||
examples/peripherals/rmt/onewire_ds18b20:
|
||||
disable:
|
||||
- if: SOC_RMT_SUPPORTED != 1
|
||||
|
||||
examples/peripherals/rmt/stepper_motor:
|
||||
enable:
|
||||
- if: SOC_RMT_SUPPORT_TX_LOOP_AUTO_STOP == 1
|
||||
|
6
examples/peripherals/rmt/onewire_ds18b20/CMakeLists.txt
Normal file
6
examples/peripherals/rmt/onewire_ds18b20/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(onewire_ds18b20)
|
90
examples/peripherals/rmt/onewire_ds18b20/README.md
Normal file
90
examples/peripherals/rmt/onewire_ds18b20/README.md
Normal file
@ -0,0 +1,90 @@
|
||||
| Supported Targets | ESP32 | ESP32-C3 | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- |
|
||||
|
||||
# RMT Transmit & Receive Example -- 1-Wire bus
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
RMT peripheral has both transmit and receive channels. Connecting one transmit channel and one receive channel to the same GPIO and put the GPIO in open-drain mode can simulate bi-directional single wire protocols, such as [1-Wire protocol](https://www.maximintegrated.com/en/design/technical-documents/tutorials/1/1796.html).
|
||||
|
||||
This example demonstrates how to use RMT to simulate 1-Wire bus and read temperatrue from [DS18B20](https://datasheets.maximintegrated.com/en/ds/DS18B20.pdf).
|
||||
|
||||
## How to Use Example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board with any supported Espressif SOC chip (see `Supported Targets` table above)
|
||||
* Several DS18B20 temperature sensors and a 4.7kohm pullup resistor
|
||||
|
||||
Connection :
|
||||
|
||||
```
|
||||
┌──────────────────────────┐
|
||||
│ 3.3V├───────┬─────────────┬──────────────────────┐
|
||||
│ │ ┌┴┐ │VDD │VDD
|
||||
│ ESP32 Board │ 4.7k│ │ ┌──────┴──────┐ ┌──────┴──────┐
|
||||
│ │ └┬┘ DQ│ │ DQ│ │
|
||||
│ ONEWIRE_GPIO_PIN├───────┴──┬───┤ DS18B20 │ ┌───┤ DS18B20 │ ......
|
||||
│ │ └───│-------------│────┴───│-------------│──
|
||||
│ │ └──────┬──────┘ └──────┬──────┘
|
||||
│ │ │GND │GND
|
||||
│ GND├─────────────────────┴──────────────────────┘
|
||||
└──────────────────────────┘
|
||||
```
|
||||
|
||||
The GPIO number used in this example can be changed according to your board, by the macro `EXAMPLE_ONEWIRE_GPIO_PIN` defined in [onewire_ds18b20_example_main.c](main/onewire_ds18b20_example_main.c).
|
||||
|
||||
*Note*: Parasite power mode is not supported currently by this example, you have to connect VDD pin to make DS18B20 functional.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Console Output
|
||||
|
||||
If there are some DS18B20s on the bus:
|
||||
|
||||
```
|
||||
I (327) cpu_start: Starting scheduler on PRO CPU.
|
||||
I (0) cpu_start: Starting scheduler on APP CPU.
|
||||
I (338) gpio: GPIO[5]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (348) onewire_rmt: RMT Tx channel created for 1-wire bus
|
||||
I (358) gpio: GPIO[5]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (358) onewire_rmt: RMT Rx channel created for 1-wire bus
|
||||
I (368) example: 1-wire bus installed
|
||||
I (418) example: found device with rom id 28FF30BE21170317
|
||||
I (458) example: found device with rom id 28FF297E211703A1
|
||||
I (498) example: found device with rom id 28FF6F7921170352
|
||||
I (508) example: 3 devices found on 1-wire bus
|
||||
I (2518) example: temperature of device 28FF30BE21170317: 27.00C
|
||||
I (2528) example: temperature of device 28FF297E211703A1: 26.81C
|
||||
I (2538) example: temperature of device 28FF6F7921170352: 26.50C
|
||||
I (3548) example: temperature of device 28FF30BE21170317: 26.94C
|
||||
I (3558) example: temperature of device 28FF297E211703A1: 26.75C
|
||||
I (3568) example: temperature of device 28FF6F7921170352: 26.44C
|
||||
```
|
||||
|
||||
If there is no DS18B20 on the bus:
|
||||
|
||||
```
|
||||
I (327) cpu_start: Starting scheduler on PRO CPU.
|
||||
I (0) cpu_start: Starting scheduler on APP CPU.
|
||||
I (337) gpio: GPIO[5]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (347) onewire_rmt: RMT Tx channel created for 1-wire bus
|
||||
I (357) gpio: GPIO[5]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (357) onewire_rmt: RMT Rx channel created for 1-wire bus
|
||||
I (367) example: 1-wire bus installed
|
||||
E (377) onewire_rmt: no device present on 1-wire bus
|
||||
I (377) example: 0 device found on 1-wire bus
|
||||
I (387) gpio: GPIO[5]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (397) gpio: GPIO[5]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (397) example: 1-wire bus deleted
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
For any technical queries, please open an [issue] (https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "onewire_bus_rmt.c" "onewire_bus.c"
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES driver)
|
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "esp_check.h"
|
||||
#include "esp_log.h"
|
||||
#include "onewire_bus.h"
|
||||
|
||||
static const char *TAG = "onewire";
|
||||
|
||||
struct onewire_rom_search_context_t {
|
||||
onewire_bus_handle_t bus_handle;
|
||||
|
||||
uint8_t last_device_flag;
|
||||
uint16_t last_discrepancy;
|
||||
uint8_t rom_number[8];
|
||||
};
|
||||
|
||||
// Algorithm inspired by https://www.maximintegrated.com/en/design/technical-documents/app-notes/1/187.html
|
||||
|
||||
static const uint8_t dscrc_table[] = {
|
||||
0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65,
|
||||
157,195, 33,127,252,162, 64, 30, 95, 1,227,189, 62, 96,130,220,
|
||||
35,125,159,193, 66, 28,254,160,225,191, 93, 3,128,222, 60, 98,
|
||||
190,224, 2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255,
|
||||
70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89, 7,
|
||||
219,133,103, 57,186,228, 6, 88, 25, 71,165,251,120, 38,196,154,
|
||||
101, 59,217,135, 4, 90,184,230,167,249, 27, 69,198,152,122, 36,
|
||||
248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91, 5,231,185,
|
||||
140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205,
|
||||
17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80,
|
||||
175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238,
|
||||
50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115,
|
||||
202,148,118, 40,171,245, 23, 73, 8, 86,180,234,105, 55,213,139,
|
||||
87, 9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22,
|
||||
233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168,
|
||||
116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53
|
||||
};
|
||||
|
||||
uint8_t onewire_check_crc8(uint8_t *input, size_t input_size)
|
||||
{
|
||||
uint8_t crc8 = 0;
|
||||
|
||||
for (size_t i = 0; i < input_size; i ++) {
|
||||
crc8 = dscrc_table[crc8 ^ input[i]];
|
||||
}
|
||||
|
||||
return crc8;
|
||||
}
|
||||
|
||||
esp_err_t onewire_rom_search_context_create(onewire_bus_handle_t handle, onewire_rom_search_context_handler_t *context_out)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
|
||||
ESP_RETURN_ON_FALSE(context_out, ESP_ERR_INVALID_ARG, TAG, "invalid context handler pointer");
|
||||
|
||||
struct onewire_rom_search_context_t *context = calloc(1, sizeof(struct onewire_rom_search_context_t));
|
||||
if (!context) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
context->bus_handle = handle;
|
||||
*context_out = context;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t onewire_rom_search_context_delete(onewire_rom_search_context_handler_t context)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(context, ESP_ERR_INVALID_ARG, TAG, "invalid context handler pointer");
|
||||
|
||||
free(context);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t onewire_rom_search(onewire_rom_search_context_handler_t context)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(context, ESP_ERR_INVALID_ARG, TAG, "invalid context handler pointer");
|
||||
|
||||
uint8_t last_zero = 0;
|
||||
|
||||
if (!context->last_device_flag) {
|
||||
if (onewire_bus_reset(context->bus_handle) != ESP_OK) { // no device present
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
// send rom search command and start search algorithm
|
||||
ESP_RETURN_ON_ERROR(onewire_bus_write_bytes(context->bus_handle, (uint8_t[]){ONEWIRE_CMD_SEARCH_ROM}, 1),
|
||||
TAG, "error while sending search rom command");
|
||||
|
||||
for (uint16_t rom_bit_index = 0; rom_bit_index < 64; rom_bit_index ++) {
|
||||
uint8_t rom_byte_index = rom_bit_index / 8;
|
||||
uint8_t rom_bit_mask = 1 << (rom_bit_index % 8); // calculate byte index and bit mask in advance for convenience
|
||||
|
||||
uint8_t rom_bit, rom_bit_complement;
|
||||
ESP_RETURN_ON_ERROR(onewire_bus_read_bit(context->bus_handle, &rom_bit), TAG, "error while reading rom bit"); // write 1 bit to read from the bus
|
||||
ESP_RETURN_ON_ERROR(onewire_bus_read_bit(context->bus_handle, &rom_bit_complement),
|
||||
TAG, "error while reading rom bit"); // read a bit and its complement
|
||||
|
||||
uint8_t search_direction;
|
||||
if (rom_bit && rom_bit_complement) { // No devices participating in search.
|
||||
ESP_LOGE(TAG, "no devices participating in search");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
} else {
|
||||
if (rom_bit != rom_bit_complement) { // There are only 0s or 1s in the bit of the participating ROM numbers.
|
||||
search_direction = rom_bit; // just go ahead
|
||||
} else { // There are both 0s and 1s in the current bit position of the participating ROM numbers. This is a discrepancy.
|
||||
if (rom_bit_index < context->last_discrepancy) { // current id bit is before the last discrepancy bit
|
||||
search_direction = (context->rom_number[rom_byte_index] & rom_bit_mask) ? 0x01 : 0x00; // follow previous way
|
||||
} else {
|
||||
search_direction = (rom_bit_index == context->last_discrepancy) ? 0x01 : 0x00; // search for 0 bit first
|
||||
}
|
||||
|
||||
if (search_direction == 0) { // record zero's position in last zero
|
||||
last_zero = rom_bit_index;
|
||||
}
|
||||
}
|
||||
|
||||
if (search_direction == 1) { // set corrsponding rom bit by serach direction
|
||||
context->rom_number[rom_byte_index] |= rom_bit_mask;
|
||||
} else {
|
||||
context->rom_number[rom_byte_index] &= ~rom_bit_mask;
|
||||
}
|
||||
|
||||
ESP_RETURN_ON_ERROR(onewire_bus_write_bit(context->bus_handle, search_direction),
|
||||
TAG, "error while writing direction bit"); // set search direction
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ESP_LOGD(TAG, "1-wire rom search finished");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
// if the search was successful
|
||||
context->last_discrepancy = last_zero;
|
||||
if (context->last_discrepancy == 0) { // last zero loops back to the first bit
|
||||
context->last_device_flag = true;
|
||||
}
|
||||
|
||||
if (onewire_check_crc8(context->rom_number, 7) != context->rom_number[7]) { // check crc
|
||||
ESP_LOGE(TAG, "bad crc checksum of device with id " ONEWIRE_ROM_ID_STR, ONEWIRE_ROM_ID(context->rom_number));
|
||||
return ESP_ERR_INVALID_CRC;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t onewire_rom_get_number(onewire_rom_search_context_handler_t context, uint8_t *rom_number_out)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(context, ESP_ERR_INVALID_ARG, TAG, "invalid context pointer");
|
||||
ESP_RETURN_ON_FALSE(rom_number_out, ESP_ERR_INVALID_ARG, TAG, "invalid rom_number pointer");
|
||||
|
||||
memcpy(rom_number_out, context->rom_number, sizeof(context->rom_number));
|
||||
|
||||
return ESP_OK;
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "onewire_bus_rmt.h"
|
||||
|
||||
#define ONEWIRE_CMD_SEARCH_ROM 0xF0
|
||||
#define ONEWIRE_CMD_READ_ROM 0x33
|
||||
#define ONEWIRE_CMD_MATCH_ROM 0x55
|
||||
#define ONEWIRE_CMD_SKIP_ROM 0xCC
|
||||
#define ONEWIRE_CMD_ALARM_SEARCH_ROM 0xEC
|
||||
|
||||
#define ONEWIRE_ROM_ID(id) (id)[0], (id)[1], (id)[2], (id)[3], (id)[4], (id)[5], (id)[6], (id)[7]
|
||||
#define ONEWIRE_ROM_ID_STR "%02X%02X%02X%02X%02X%02X%02X%02X"
|
||||
|
||||
/**
|
||||
* @brief Type of 1-wire ROM search algorithm context
|
||||
*
|
||||
*/
|
||||
typedef struct onewire_rom_search_context_t *onewire_rom_search_context_handler_t;
|
||||
|
||||
/**
|
||||
* @brief Calculate Dallas CRC value of given buffer
|
||||
*
|
||||
* @param[in] input Input buffer to calculate CRC value
|
||||
* @param[in] input_size Size of input buffer
|
||||
* @return CRC result of input buffer
|
||||
*/
|
||||
uint8_t onewire_check_crc8(uint8_t *input, size_t input_size);
|
||||
|
||||
/**
|
||||
* @brief Create context for 1-wire ROM search algorithm
|
||||
*
|
||||
* @param[in] handle 1-wire handle used for ROM search
|
||||
* @param[out] context_out Created context for ROM search algorithm
|
||||
* @return
|
||||
* - ESP_OK 1-wire ROM search context is created successfully.
|
||||
* - ESP_ERR_INVALID_ARG Invalid argument.
|
||||
*/
|
||||
esp_err_t onewire_rom_search_context_create(onewire_bus_handle_t handle, onewire_rom_search_context_handler_t *context_out);
|
||||
|
||||
/**
|
||||
* @brief Delete context for 1-wire ROM search algorithm
|
||||
*
|
||||
* @param[in] context Context for ROM search algorithm
|
||||
* @return
|
||||
* - ESP_OK 1-wire ROM search context is deleted successfully.
|
||||
* - ESP_ERR_INVALID_ARG Invalid argument.
|
||||
*/
|
||||
esp_err_t onewire_rom_search_context_delete(onewire_rom_search_context_handler_t context);
|
||||
|
||||
/**
|
||||
* @brief Search next device on 1-wire bus
|
||||
*
|
||||
* @param[in] context Context for ROM search algorithm
|
||||
* @return
|
||||
* - ESP_OK Successfully found a device
|
||||
* - ESP_ERR_NOT_FOUND There are no device on the bus
|
||||
* - ESP_ERR_INVALID_CRC Bad CRC value of found device
|
||||
* - ESP_FAIL Reached last device on the bus, search algorighm finishes
|
||||
*/
|
||||
esp_err_t onewire_rom_search(onewire_rom_search_context_handler_t context);
|
||||
|
||||
/**
|
||||
* @brief Get device ROM number from ROM search context
|
||||
*
|
||||
* @param[in] context Context for ROM search algorithm
|
||||
* @param[out] rom_number_out Device ROM number
|
||||
* @return
|
||||
* - ESP_OK Get ROM numbuer from 1-wire ROM search context success.
|
||||
* - ESP_ERR_INVALID_ARG Invalid argument.
|
||||
*/
|
||||
esp_err_t onewire_rom_get_number(onewire_rom_search_context_handler_t context, uint8_t *rom_number_out);
|
@ -0,0 +1,438 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "esp_check.h"
|
||||
#include "driver/rmt_tx.h"
|
||||
#include "driver/rmt_rx.h"
|
||||
#include "driver/rmt_types.h"
|
||||
#include "driver/rmt_encoder.h"
|
||||
#include "onewire_bus_rmt.h"
|
||||
|
||||
static const char *TAG = "onewire_rmt";
|
||||
|
||||
/**
|
||||
* @brief RMT resolution for 1-wire bus, in Hz
|
||||
*
|
||||
*/
|
||||
#define ONEWIRE_RMT_RESOLUTION_HZ 1000000
|
||||
|
||||
/**
|
||||
* @brief 1-wire bus timing parameters, in us (1/ONEWIRE_RMT_RESOLUTION_HZ)
|
||||
*
|
||||
*/
|
||||
#define ONEWIRE_RESET_PULSE_DURATION 500 // duration of reset bit
|
||||
#define ONEWIRE_RESET_WAIT_DURATION 200 // how long should master wait for device to show its presence
|
||||
#define ONEWIRE_RESET_PRESENSE_WAIT_DURATION_MIN 15 // minimum duration for master to wait device to show its presence
|
||||
#define ONEWIRE_RESET_PRESENSE_DURATION_MIN 60 // minimum duration for master to recognize device as present
|
||||
|
||||
#define ONEWIRE_SLOT_START_DURATION 2 // bit start pulse duration
|
||||
#define ONEWIRE_SLOT_BIT_DURATION 60 // duration for each bit to transmit
|
||||
// refer to https://www.maximintegrated.com/en/design/technical-documents/app-notes/3/3829.html for more information
|
||||
#define ONEWIRE_SLOT_RECOVERY_DURATION 2 // recovery time between each bit, should be longer in parasite power mode
|
||||
#define ONEWIRE_SLOT_BIT_SAMPLE_TIME 15 // how long after bit start pulse should the master sample from the bus
|
||||
|
||||
/*
|
||||
Reset Pulse:
|
||||
|
||||
| RESET_PULSE | RESET_WAIT_DURATION |
|
||||
| _DURATION | |
|
||||
| | | | RESET | |
|
||||
| | * | | _PRESENSE | |
|
||||
| | | | _DURATION | |
|
||||
------────┐ ┌─────┐ ┌───────-------
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
└─────────────┘ └───────────┘
|
||||
*: RESET_PRESENSE_WAIT_DURATION
|
||||
|
||||
Write 1 bit:
|
||||
|
||||
| SLOT_START | SLOT_BIT | SLOT_RECOVERY | NEXT
|
||||
| _DURATION | _DURATION | _DURATION | SLOT
|
||||
| | | |
|
||||
------────┐ ┌───────────────────────────────------
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└────────────┘
|
||||
|
||||
Write 0 bit:
|
||||
|
||||
| SLOT_START | SLOT_BIT | SLOT_RECOVERY | NEXT
|
||||
| _DURATION | _DURATION | _DURATION | SLOT
|
||||
| | | |
|
||||
------────┐ ┌───────────────────------
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└────────────────────────┘
|
||||
|
||||
Read 1 bit:
|
||||
|
||||
|
||||
| SLOT_START | SLOT_BIT_DURATION | SLOT_RECOVERY | NEXT
|
||||
| _DURATION | | _DURATION | SLOT
|
||||
| | SLOT_BIT_ | | |
|
||||
| | SAMPLE_TIME | | |
|
||||
------────┐ ┌────────────────────────────────────────------
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└────────────┘
|
||||
|
||||
Read 0 bit:
|
||||
|
||||
| SLOT_START | SLOT_BIT_DURATION | SLOT_RECOVERY | NEXT
|
||||
| _DURATION | | _DURATION | SLOT
|
||||
| | SLOT_BIT_ | | |
|
||||
| | SAMPLE_TIME | | |
|
||||
------────┐ | | ┌───────────────────────------
|
||||
│ | │
|
||||
│ | PULLED DOWN │
|
||||
│ | BY DEVICE │
|
||||
└─────────────────────────────┘
|
||||
*/
|
||||
|
||||
struct onewire_bus_t {
|
||||
rmt_channel_handle_t tx_channel; /*!< rmt tx channel handler */
|
||||
rmt_encoder_handle_t tx_bytes_encoder; /*!< used to encode commands and data */
|
||||
rmt_encoder_handle_t tx_copy_encoder; /*!< used to encode reset pulse and bits */
|
||||
|
||||
rmt_channel_handle_t rx_channel; /*!< rmt rx channel handler */
|
||||
rmt_symbol_word_t *rx_symbols; /*!< hold rmt raw symbols */
|
||||
|
||||
size_t max_rx_bytes; /*!< buffer size in byte for single receive transaction */
|
||||
|
||||
QueueHandle_t receive_queue;
|
||||
};
|
||||
|
||||
const static rmt_symbol_word_t onewire_bit0_symbol = {
|
||||
.level0 = 0,
|
||||
.duration0 = ONEWIRE_SLOT_START_DURATION + ONEWIRE_SLOT_BIT_DURATION,
|
||||
.level1 = 1,
|
||||
.duration1 = ONEWIRE_SLOT_RECOVERY_DURATION
|
||||
};
|
||||
|
||||
const static rmt_symbol_word_t onewire_bit1_symbol = {
|
||||
.level0 = 0,
|
||||
.duration0 = ONEWIRE_SLOT_START_DURATION,
|
||||
.level1 = 1,
|
||||
.duration1 = ONEWIRE_SLOT_BIT_DURATION + ONEWIRE_SLOT_RECOVERY_DURATION
|
||||
};
|
||||
|
||||
const static rmt_symbol_word_t onewire_reset_pulse_symbol = {
|
||||
.level0 = 0,
|
||||
.duration0 = ONEWIRE_RESET_PULSE_DURATION,
|
||||
.level1 = 1,
|
||||
.duration1 = ONEWIRE_RESET_WAIT_DURATION
|
||||
};
|
||||
|
||||
const static rmt_transmit_config_t onewire_rmt_tx_config = {
|
||||
.loop_count = 0, // no transfer loop
|
||||
.flags.eot_level = 1 // onewire bus should be released in IDLE
|
||||
};
|
||||
|
||||
const static rmt_receive_config_t onewire_rmt_rx_config = {
|
||||
.signal_range_min_ns = 1000000000 / ONEWIRE_RMT_RESOLUTION_HZ,
|
||||
.signal_range_max_ns = (ONEWIRE_RESET_PULSE_DURATION + ONEWIRE_RESET_WAIT_DURATION) * 1000
|
||||
};
|
||||
|
||||
static bool onewire_rmt_rx_done_callback(rmt_channel_handle_t channel, rmt_rx_done_event_data_t *edata, void *user_data)
|
||||
{
|
||||
BaseType_t task_woken = pdFALSE;
|
||||
struct onewire_bus_t *handle = (struct onewire_bus_t *)user_data;
|
||||
|
||||
xQueueSendFromISR(handle->receive_queue, edata, &task_woken);
|
||||
|
||||
return task_woken;
|
||||
}
|
||||
|
||||
/*
|
||||
[0].0 means symbol[0].duration0
|
||||
|
||||
First reset pulse after rmt channel init:
|
||||
|
||||
Bus is low | Reset | Wait | Device | Bus Idle
|
||||
after init | Pulse | | Presense |
|
||||
┌──────┐ ┌─────------
|
||||
│ │ │
|
||||
│ │ │
|
||||
│ │ │
|
||||
------─────────────┘ └──────────┘
|
||||
1 2 3
|
||||
|
||||
[0].1 [0].0 [1].1 [1].0
|
||||
|
||||
|
||||
Following reset pulses:
|
||||
|
||||
Bus is high | Reset | Wait | Device | Bus Idle
|
||||
after init | Pulse | | Presense |
|
||||
------──────┐ ┌──────┐ ┌─────------
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
└───────┘ └──────────┘
|
||||
1 2 3 4
|
||||
|
||||
[0].0 [0].1 [1].0 [1].1
|
||||
*/
|
||||
|
||||
static bool onewire_rmt_check_presence_pulse(rmt_symbol_word_t *rmt_symbols, size_t symbol_num)
|
||||
{
|
||||
if (symbol_num >= 2) { // there should be at lease 2 symbols(3 or 4 edges)
|
||||
if (rmt_symbols[0].level1 == 1) { // bus is high before reset pulse
|
||||
if (rmt_symbols[0].duration1 > ONEWIRE_RESET_PRESENSE_WAIT_DURATION_MIN &&
|
||||
rmt_symbols[1].duration0 > ONEWIRE_RESET_PRESENSE_DURATION_MIN) {
|
||||
return true;
|
||||
}
|
||||
} else { // bus is low before reset pulse(first pulse after rmt channel init)
|
||||
if (rmt_symbols[0].duration0 > ONEWIRE_RESET_PRESENSE_WAIT_DURATION_MIN &&
|
||||
rmt_symbols[1].duration1 > ONEWIRE_RESET_PRESENSE_DURATION_MIN) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGE(TAG, "no device present on 1-wire bus");
|
||||
return false;
|
||||
}
|
||||
|
||||
static void onewire_rmt_decode_data(rmt_symbol_word_t *rmt_symbols, size_t symbol_num, uint8_t *decoded_bytes)
|
||||
{
|
||||
size_t byte_pos = 0, bit_pos = 0;
|
||||
for (size_t i = 0; i < symbol_num; i ++) {
|
||||
if (rmt_symbols[i].duration0 > ONEWIRE_SLOT_BIT_SAMPLE_TIME) { // 0 bit
|
||||
decoded_bytes[byte_pos] &= ~(1 << bit_pos); // LSB first
|
||||
} else { // 1 bit
|
||||
decoded_bytes[byte_pos] |= 1 << bit_pos;
|
||||
}
|
||||
|
||||
bit_pos ++;
|
||||
if (bit_pos >= 8) {
|
||||
bit_pos = 0;
|
||||
byte_pos ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t onewire_new_bus_rmt(onewire_rmt_config_t *config, onewire_bus_handle_t *handle_out)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(config, ESP_ERR_INVALID_ARG, TAG, "invalid config pointer");
|
||||
ESP_RETURN_ON_FALSE(handle_out, ESP_ERR_INVALID_ARG, TAG, "invalid handle pointer");
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
struct onewire_bus_t *handle = calloc(1, sizeof(struct onewire_bus_t));
|
||||
ESP_GOTO_ON_FALSE(handle, ESP_ERR_NO_MEM, err, TAG, "memory allocation for 1-wire bus handler failed");
|
||||
|
||||
// create rmt bytes encoder to transmit 1-wire commands and data
|
||||
rmt_bytes_encoder_config_t bytes_encoder_config = {
|
||||
.bit0 = onewire_bit0_symbol,
|
||||
.bit1 = onewire_bit1_symbol,
|
||||
.flags.msb_first = 0
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(rmt_new_bytes_encoder(&bytes_encoder_config, &handle->tx_bytes_encoder),
|
||||
err, TAG, "create data tx encoder failed");
|
||||
|
||||
// create rmt copy encoder to transmit 1-wire reset pulse or bits
|
||||
rmt_copy_encoder_config_t copy_encoder_config = {};
|
||||
ESP_GOTO_ON_ERROR(rmt_new_copy_encoder(©_encoder_config, &handle->tx_copy_encoder),
|
||||
err, TAG, "create reset pulse tx encoder failed");
|
||||
|
||||
// create rmt rx channel
|
||||
rmt_rx_channel_config_t onewire_rx_channel_cfg = {
|
||||
.clk_src = RMT_CLK_SRC_DEFAULT,
|
||||
.gpio_num = config->gpio_pin,
|
||||
#if SOC_RMT_SUPPORT_RX_PINGPONG
|
||||
.mem_block_symbols = 64, // when the chip is ping-pong capable, we can use less rx memory blocks
|
||||
#else
|
||||
.mem_block_symbols = config->max_rx_bytes * 8,
|
||||
#endif
|
||||
.resolution_hz = ONEWIRE_RMT_RESOLUTION_HZ, // in us
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(rmt_new_rx_channel(&onewire_rx_channel_cfg, &handle->rx_channel),
|
||||
err, TAG, "create rmt rx channel failed");
|
||||
ESP_LOGI(TAG, "RMT Tx channel created for 1-wire bus");
|
||||
|
||||
// create rmt tx channel after rx channel
|
||||
rmt_tx_channel_config_t onewire_tx_channel_cfg = {
|
||||
.clk_src = RMT_CLK_SRC_DEFAULT,
|
||||
.gpio_num = config->gpio_pin,
|
||||
.mem_block_symbols = 64, // ping-pong is always avaliable on tx channel, save hardware memory blocks
|
||||
.resolution_hz = ONEWIRE_RMT_RESOLUTION_HZ, // in us
|
||||
.trans_queue_depth = 4,
|
||||
.flags.io_loop_back = true, // make tx channel coexist with rx channel on the same gpio pin
|
||||
.flags.io_od_mode = true // enable open-drain mode for 1-wire bus
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(rmt_new_tx_channel(&onewire_tx_channel_cfg, &handle->tx_channel),
|
||||
err, TAG, "create rmt tx channel failed");
|
||||
ESP_LOGI(TAG, "RMT Rx channel created for 1-wire bus");
|
||||
|
||||
// allocate rmt rx symbol buffer
|
||||
handle->rx_symbols = malloc(config->max_rx_bytes * sizeof(rmt_symbol_word_t) * 8);
|
||||
ESP_GOTO_ON_FALSE(handle->rx_symbols, ESP_ERR_NO_MEM, err, TAG, "memory allocation for rx symbol buffer failed");
|
||||
handle->max_rx_bytes = config->max_rx_bytes;
|
||||
|
||||
handle->receive_queue = xQueueCreate(1, sizeof(rmt_rx_done_event_data_t));
|
||||
ESP_GOTO_ON_FALSE(handle->receive_queue, ESP_ERR_NO_MEM, err, TAG, "receive queue creation failed");
|
||||
|
||||
// register rmt rx done callback
|
||||
rmt_rx_event_callbacks_t cbs = {
|
||||
.on_recv_done = onewire_rmt_rx_done_callback
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(rmt_rx_register_event_callbacks(handle->rx_channel, &cbs, handle),
|
||||
err, TAG, "enable rmt rx channel failed");
|
||||
|
||||
// enable rmt channels
|
||||
ESP_GOTO_ON_ERROR(rmt_enable(handle->rx_channel), err, TAG, "enable rmt rx channel failed");
|
||||
ESP_GOTO_ON_ERROR(rmt_enable(handle->tx_channel), err, TAG, "enable rmt tx channel failed");
|
||||
|
||||
*handle_out = handle;
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (handle) {
|
||||
onewire_del_bus(handle);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t onewire_del_bus(onewire_bus_handle_t handle)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
|
||||
|
||||
if (handle->tx_bytes_encoder) {
|
||||
rmt_del_encoder(handle->tx_bytes_encoder);
|
||||
}
|
||||
if (handle->tx_copy_encoder) {
|
||||
rmt_del_encoder(handle->tx_copy_encoder);
|
||||
}
|
||||
if (handle->rx_channel) {
|
||||
rmt_disable(handle->rx_channel);
|
||||
rmt_del_channel(handle->rx_channel);
|
||||
}
|
||||
if (handle->tx_channel) {
|
||||
rmt_disable(handle->tx_channel);
|
||||
rmt_del_channel(handle->tx_channel);
|
||||
}
|
||||
if(handle->receive_queue) {
|
||||
vQueueDelete(handle->receive_queue);
|
||||
}
|
||||
if (handle->rx_symbols) {
|
||||
free(handle->rx_symbols);
|
||||
}
|
||||
free(handle);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t onewire_bus_reset(onewire_bus_handle_t handle)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
|
||||
|
||||
// send reset pulse while receive presence pulse
|
||||
ESP_RETURN_ON_ERROR(rmt_receive(handle->rx_channel, handle->rx_symbols, sizeof(rmt_symbol_word_t)*2, &onewire_rmt_rx_config),
|
||||
TAG, "1-wire reset pulse receive failed");
|
||||
ESP_RETURN_ON_ERROR(rmt_transmit(handle->tx_channel, handle->tx_copy_encoder, &onewire_reset_pulse_symbol, sizeof(onewire_reset_pulse_symbol), &onewire_rmt_tx_config),
|
||||
TAG, "1-wire reset pulse transmit failed");
|
||||
|
||||
// wait and check presence pulse
|
||||
bool is_present = false;
|
||||
rmt_rx_done_event_data_t rmt_rx_evt_data;
|
||||
if (xQueueReceive(handle->receive_queue, &rmt_rx_evt_data, pdMS_TO_TICKS(1000)) == pdPASS) {
|
||||
is_present = onewire_rmt_check_presence_pulse(rmt_rx_evt_data.received_symbols, rmt_rx_evt_data.num_symbols);
|
||||
}
|
||||
|
||||
return is_present ? ESP_OK : ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
esp_err_t onewire_bus_write_bytes(onewire_bus_handle_t handle, const uint8_t *tx_data, uint8_t tx_data_size)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
|
||||
ESP_RETURN_ON_FALSE(tx_data && tx_data_size != 0, ESP_ERR_INVALID_ARG, TAG, "invalid tx buffer or buffer size");
|
||||
|
||||
// transmit data
|
||||
ESP_RETURN_ON_ERROR(rmt_transmit(handle->tx_channel, handle->tx_bytes_encoder, tx_data, tx_data_size, &onewire_rmt_tx_config),
|
||||
TAG, "1-wire data transmit failed");
|
||||
|
||||
// wait the transmission to complete
|
||||
ESP_RETURN_ON_ERROR(rmt_tx_wait_all_done(handle->tx_channel, 50), TAG, "wait for 1-wire data transmit failed");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t onewire_bus_read_bytes(onewire_bus_handle_t handle, uint8_t *rx_data, size_t rx_data_size)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
|
||||
ESP_RETURN_ON_FALSE(rx_data && rx_data_size != 0, ESP_ERR_INVALID_ARG, TAG, "invalid rx buffer or buffer size");
|
||||
ESP_RETURN_ON_FALSE(!(rx_data_size > handle->max_rx_bytes), ESP_ERR_INVALID_ARG,
|
||||
TAG, "rx_data_size too large for buffer to hold");
|
||||
|
||||
uint8_t tx_buffer[rx_data_size];
|
||||
memset(tx_buffer, 0xFF, rx_data_size); // transmit one bits to generate read clock
|
||||
|
||||
// transmit 1 bits while receiving
|
||||
ESP_RETURN_ON_ERROR(rmt_receive(handle->rx_channel, handle->rx_symbols, rx_data_size * 8 * sizeof(rmt_symbol_word_t), &onewire_rmt_rx_config),
|
||||
TAG, "1-wire data receive failed");
|
||||
ESP_RETURN_ON_ERROR(rmt_transmit(handle->tx_channel, handle->tx_bytes_encoder, tx_buffer, sizeof(tx_buffer), &onewire_rmt_tx_config),
|
||||
TAG, "1-wire data transmit failed");
|
||||
|
||||
// wait the transmission finishes and decode data
|
||||
rmt_rx_done_event_data_t rmt_rx_evt_data;
|
||||
if (xQueueReceive(handle->receive_queue, &rmt_rx_evt_data, pdMS_TO_TICKS(1000)) == pdPASS) {
|
||||
onewire_rmt_decode_data(rmt_rx_evt_data.received_symbols, rmt_rx_evt_data.num_symbols, rx_data);
|
||||
} else {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t onewire_bus_write_bit(onewire_bus_handle_t handle, uint8_t tx_bit)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
|
||||
|
||||
const rmt_symbol_word_t *symbol_to_transmit = tx_bit ? &onewire_bit1_symbol : &onewire_bit0_symbol;
|
||||
|
||||
// transmit bit
|
||||
ESP_RETURN_ON_ERROR(rmt_transmit(handle->tx_channel, handle->tx_copy_encoder, symbol_to_transmit, sizeof(onewire_bit1_symbol), &onewire_rmt_tx_config),
|
||||
TAG, "1-wire bit transmit failed");
|
||||
|
||||
// wait the transmission to complete
|
||||
ESP_RETURN_ON_ERROR(rmt_tx_wait_all_done(handle->tx_channel, 50), TAG, "wait for 1-wire bit transmit failed");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t onewire_bus_read_bit(onewire_bus_handle_t handle, uint8_t *rx_bit)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
|
||||
ESP_RETURN_ON_FALSE(rx_bit, ESP_ERR_INVALID_ARG, TAG, "invalid rx_bit pointer");
|
||||
|
||||
// transmit 1 bit while receiving
|
||||
ESP_RETURN_ON_ERROR(rmt_receive(handle->rx_channel, handle->rx_symbols, sizeof(rmt_symbol_word_t), &onewire_rmt_rx_config),
|
||||
TAG, "1-wire bit receive failed");
|
||||
ESP_RETURN_ON_ERROR(rmt_transmit(handle->tx_channel, handle->tx_copy_encoder, &onewire_bit1_symbol, sizeof(onewire_bit1_symbol), &onewire_rmt_tx_config),
|
||||
TAG, "1-wire bit transmit failed");
|
||||
|
||||
// wait the transmission finishes and decode data
|
||||
rmt_rx_done_event_data_t rmt_rx_evt_data;
|
||||
if (xQueueReceive(handle->receive_queue, &rmt_rx_evt_data, pdMS_TO_TICKS(1000)) == pdPASS) {
|
||||
uint8_t rx_buffer[1];
|
||||
onewire_rmt_decode_data(rmt_rx_evt_data.received_symbols, rmt_rx_evt_data.num_symbols, rx_buffer);
|
||||
*rx_bit = rx_buffer[0] & 0x01;
|
||||
} else {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/gpio.h"
|
||||
|
||||
/**
|
||||
* @brief 1-wire bus config
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
gpio_num_t gpio_pin; /*!< gpio used for 1-wire bus */
|
||||
uint8_t max_rx_bytes; /*!< should be larger than the largest possible single receive size */
|
||||
} onewire_rmt_config_t;
|
||||
|
||||
/**
|
||||
* @brief Type of 1-wire bus handle
|
||||
*
|
||||
*/
|
||||
typedef struct onewire_bus_t *onewire_bus_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Install new 1-wire bus
|
||||
*
|
||||
* @param[in] config 1-wire bus configurations
|
||||
* @param[out] handle_out Installed new 1-wire bus' handle
|
||||
* @return
|
||||
* - ESP_OK 1-wire bus is installed successfully.
|
||||
* - ESP_ERR_INVALID_ARG Invalid argument.
|
||||
* - ESP_ERR_NO_MEM Memory allocation failed.
|
||||
*/
|
||||
esp_err_t onewire_new_bus_rmt(onewire_rmt_config_t *config, onewire_bus_handle_t *handle_out);
|
||||
|
||||
/**
|
||||
* @brief Delete existing 1-wire bus
|
||||
*
|
||||
* @param[in] handle 1-wire bus handle to be deleted
|
||||
* @return
|
||||
* - ESP_OK 1-wire bus is deleted successfully.
|
||||
* - ESP_ERR_INVALID_ARG Invalid argument.
|
||||
*/
|
||||
esp_err_t onewire_del_bus(onewire_bus_handle_t handle);
|
||||
|
||||
/**
|
||||
* @brief Send reset pulse on 1-wire bus, and detect if there are devices on the bus
|
||||
*
|
||||
* @param[in] handle 1-wire bus handle
|
||||
* @return
|
||||
* - ESP_OK There are devices present on 1-wire bus.
|
||||
* - ESP_ERR_NOT_FOUND There is no device present on 1-wire bus.
|
||||
*/
|
||||
esp_err_t onewire_bus_reset(onewire_bus_handle_t handle);
|
||||
|
||||
/**
|
||||
* @brief Write bytes to 1-wire bus, this is a blocking function
|
||||
*
|
||||
* @param[in] handle 1-wire bus handle
|
||||
* @param[in] tx_data pointer to data to be sent
|
||||
* @param[in] tx_data_count number of data to be sent
|
||||
* @return
|
||||
* - ESP_OK Write bytes to 1-wire bus successfully.
|
||||
* - ESP_ERR_INVALID_ARG Invalid argument.
|
||||
*/
|
||||
esp_err_t onewire_bus_write_bytes(onewire_bus_handle_t handle, const uint8_t *tx_data, uint8_t tx_data_size);
|
||||
|
||||
/**
|
||||
* @brief Read bytes from 1-wire bus
|
||||
*
|
||||
* @note While receiving data, we use rmt transmit channel to send 0xFF to generate read pulse,
|
||||
* at the same time, receive channel is used to record weather the bus is pulled down by device.
|
||||
*
|
||||
* @param[in] handle 1-wire bus handle
|
||||
* @param[out] rx_data pointer to received data
|
||||
* @param[in] rx_data_count number of received data
|
||||
* @return
|
||||
* - ESP_OK Read bytes from 1-wire bus successfully.
|
||||
* - ESP_ERR_INVALID_ARG Invalid argument.
|
||||
*/
|
||||
esp_err_t onewire_bus_read_bytes(onewire_bus_handle_t handle, uint8_t *rx_data, size_t rx_data_size);
|
||||
|
||||
/**
|
||||
* @brief Write a bit to 1-wire bus, this is a blocking function
|
||||
*
|
||||
* @param[in] handle 1-wire bus handle
|
||||
* @param[in] tx_bit bit to transmit, 0 for zero bit, other for one bit
|
||||
* @return
|
||||
* - ESP_OK Write bit to 1-wire bus successfully.
|
||||
* - ESP_ERR_INVALID_ARG Invalid argument.
|
||||
*/
|
||||
esp_err_t onewire_bus_write_bit(onewire_bus_handle_t handle, uint8_t tx_bit);
|
||||
|
||||
/**
|
||||
* @brief Read a bit from 1-wire bus
|
||||
*
|
||||
* @param[in] handle 1-wire bus handle
|
||||
* @param[out] rx_bit received bit, 0 for zero bit, 1 for one bit
|
||||
* @return
|
||||
* - ESP_OK Read bit from 1-wire bus successfully.
|
||||
* - ESP_ERR_INVALID_ARG Invalid argument.
|
||||
*/
|
||||
esp_err_t onewire_bus_read_bit(onewire_bus_handle_t handle, uint8_t *rx_bit);
|
@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "onewire_ds18b20_example_main.c" "ds18b20.c"
|
||||
INCLUDE_DIRS ".")
|
106
examples/peripherals/rmt/onewire_ds18b20/main/ds18b20.c
Normal file
106
examples/peripherals/rmt/onewire_ds18b20/main/ds18b20.c
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "ds18b20.h"
|
||||
#include "esp_check.h"
|
||||
|
||||
static const char *TAG = "ds18b20";
|
||||
|
||||
esp_err_t ds18b20_trigger_temperature_conversion(onewire_bus_handle_t handle, const uint8_t *rom_number)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
|
||||
|
||||
ESP_RETURN_ON_ERROR(onewire_bus_reset(handle), TAG, "error while resetting bus"); // reset bus and check if the device is present
|
||||
|
||||
uint8_t tx_buffer[10];
|
||||
uint8_t tx_buffer_size;
|
||||
|
||||
if (rom_number) { // specify rom id
|
||||
tx_buffer[0] = ONEWIRE_CMD_MATCH_ROM;
|
||||
tx_buffer[9] = DS18B20_CMD_CONVERT_TEMP;
|
||||
memcpy(&tx_buffer[1], rom_number, 8);
|
||||
tx_buffer_size = 10;
|
||||
} else { // skip rom id
|
||||
tx_buffer[0] = ONEWIRE_CMD_SKIP_ROM;
|
||||
tx_buffer[1] = DS18B20_CMD_CONVERT_TEMP;
|
||||
tx_buffer_size = 2;
|
||||
}
|
||||
|
||||
ESP_RETURN_ON_ERROR(onewire_bus_write_bytes(handle, tx_buffer, tx_buffer_size),
|
||||
TAG, "error while triggering temperature convert");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t ds18b20_get_temperature(onewire_bus_handle_t handle, const uint8_t *rom_number, float *temperature)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
|
||||
ESP_RETURN_ON_FALSE(temperature, ESP_ERR_INVALID_ARG, TAG, "invalid temperature pointer");
|
||||
|
||||
ESP_RETURN_ON_ERROR(onewire_bus_reset(handle), TAG, "error while resetting bus"); // reset bus and check if the device is present
|
||||
|
||||
ds18b20_scratchpad_t scratchpad;
|
||||
|
||||
uint8_t tx_buffer[10];
|
||||
uint8_t tx_buffer_size;
|
||||
|
||||
if (rom_number) { // specify rom id
|
||||
tx_buffer[0] = ONEWIRE_CMD_MATCH_ROM;
|
||||
tx_buffer[9] = DS18B20_CMD_READ_SCRATCHPAD;
|
||||
memcpy(&tx_buffer[1], rom_number, 8);
|
||||
tx_buffer_size = 10;
|
||||
} else {
|
||||
tx_buffer[0] = ONEWIRE_CMD_SKIP_ROM;
|
||||
tx_buffer[1] = DS18B20_CMD_READ_SCRATCHPAD;
|
||||
tx_buffer_size = 2;
|
||||
}
|
||||
|
||||
ESP_RETURN_ON_ERROR(onewire_bus_write_bytes(handle, tx_buffer, tx_buffer_size),
|
||||
TAG, "error while sending read scratchpad command");
|
||||
ESP_RETURN_ON_ERROR(onewire_bus_read_bytes(handle, (uint8_t *)&scratchpad, sizeof(scratchpad)),
|
||||
TAG, "error while reading scratchpad command");
|
||||
|
||||
ESP_RETURN_ON_FALSE(onewire_check_crc8((uint8_t *)&scratchpad, 8) == scratchpad.crc_value, ESP_ERR_INVALID_CRC,
|
||||
TAG, "crc error");
|
||||
|
||||
static const uint8_t lsb_mask[4] = { 0x07, 0x03, 0x01, 0x00 };
|
||||
uint8_t lsb_masked = scratchpad.temp_lsb & (~lsb_mask[scratchpad.configuration >> 5]); // mask bits not used in low resolution
|
||||
*temperature = (((int16_t)scratchpad.temp_msb << 8) | lsb_masked) / 16.0f;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t ds18b20_set_resolution(onewire_bus_handle_t handle, const uint8_t *rom_number, ds18b20_resolution_t resolution)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "invalid 1-wire handle");
|
||||
|
||||
ESP_RETURN_ON_ERROR(onewire_bus_reset(handle), TAG, "error while resetting bus"); // reset bus and check if the device is present
|
||||
|
||||
uint8_t tx_buffer[10];
|
||||
uint8_t tx_buffer_size;
|
||||
|
||||
if (rom_number) { // specify rom id
|
||||
tx_buffer[0] = ONEWIRE_CMD_MATCH_ROM;
|
||||
tx_buffer[9] = DS18B20_CMD_WRITE_SCRATCHPAD;
|
||||
memcpy(&tx_buffer[1], rom_number, 8);
|
||||
tx_buffer_size = 10;
|
||||
} else {
|
||||
tx_buffer[0] = ONEWIRE_CMD_SKIP_ROM;
|
||||
tx_buffer[1] = DS18B20_CMD_WRITE_SCRATCHPAD;
|
||||
tx_buffer_size = 2;
|
||||
}
|
||||
|
||||
ESP_RETURN_ON_ERROR(onewire_bus_write_bytes(handle, tx_buffer, tx_buffer_size),
|
||||
TAG, "error while sending read scratchpad command");
|
||||
|
||||
tx_buffer[0] = 0;
|
||||
tx_buffer[1] = 0;
|
||||
tx_buffer[2] = resolution;
|
||||
ESP_RETURN_ON_ERROR(onewire_bus_write_bytes(handle, tx_buffer, 3),
|
||||
TAG, "error while sending write scratchpad command");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
78
examples/peripherals/rmt/onewire_ds18b20/main/ds18b20.h
Normal file
78
examples/peripherals/rmt/onewire_ds18b20/main/ds18b20.h
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "onewire_bus.h"
|
||||
|
||||
#define DS18B20_CMD_CONVERT_TEMP 0x44
|
||||
#define DS18B20_CMD_WRITE_SCRATCHPAD 0x4E
|
||||
#define DS18B20_CMD_READ_SCRATCHPAD 0xBE
|
||||
|
||||
/**
|
||||
* @brief Structure of DS18B20's scratchpad
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t temp_lsb; /*!< lsb of temperature */
|
||||
uint8_t temp_msb; /*!< msb of temperature */
|
||||
uint8_t th_user1; /*!< th register or user byte 1 */
|
||||
uint8_t tl_user2; /*!< tl register or user byte 2 */
|
||||
uint8_t configuration; /*!< configuration register */
|
||||
uint8_t _reserved1;
|
||||
uint8_t _reserved2;
|
||||
uint8_t _reserved3;
|
||||
uint8_t crc_value; /*!< crc value of scratchpad data */
|
||||
} ds18b20_scratchpad_t;
|
||||
|
||||
/**
|
||||
* @brief Enumeration of DS18B20's resolution config
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
DS18B20_RESOLUTION_12B = 0x7F, /*!< 750ms convert time */
|
||||
DS18B20_RESOLUTION_11B = 0x5F, /*!< 375ms convert time */
|
||||
DS18B20_RESOLUTION_10B = 0x3F, /*!< 187.5ms convert time */
|
||||
DS18B20_RESOLUTION_9B = 0x1F, /*!< 93.75ms convert time */
|
||||
} ds18b20_resolution_t;
|
||||
|
||||
/**
|
||||
* @brief Trigger temperature conversion of DS18B20
|
||||
*
|
||||
* @param[in] handle 1-wire handle with DS18B20 on
|
||||
* @param[in] rom_number ROM number to specify which DS18B20 to send command, NULL to skip ROM
|
||||
* @return
|
||||
* - ESP_OK Trigger tempreture convertsion success.
|
||||
* - ESP_ERR_INVALID_ARG Invalid argument.
|
||||
* - ESP_ERR_NOT_FOUND There is no device present on 1-wire bus.
|
||||
*/
|
||||
esp_err_t ds18b20_trigger_temperature_conversion(onewire_bus_handle_t handle, const uint8_t *rom_number);
|
||||
|
||||
/**
|
||||
* @brief Get temperature from DS18B20
|
||||
*
|
||||
* @param[in] handle 1-wire handle with DS18B20 on
|
||||
* @param[in] rom_number ROM number to specify which DS18B20 to read from, NULL to skip ROM
|
||||
* @param[out] temperature result from DS18B20
|
||||
* @return
|
||||
* - ESP_OK Get tempreture from DS18B20 success.
|
||||
* - ESP_ERR_INVALID_ARG Invalid argument.
|
||||
* - ESP_ERR_NOT_FOUND There is no device present on 1-wire bus.
|
||||
* - ESP_ERR_INVALID_CRC CRC check failed.
|
||||
*/
|
||||
esp_err_t ds18b20_get_temperature(onewire_bus_handle_t handle, const uint8_t *rom_number, float *temperature);
|
||||
|
||||
/**
|
||||
* @brief Set DS18B20's temperation conversion resolution
|
||||
*
|
||||
* @param[in] handle 1-wire handle with DS18B20 on
|
||||
* @param[in] rom_number ROM number to specify which DS18B20 to read from, NULL to skip ROM
|
||||
* @param[in] resolution resolution of DS18B20's temperation conversion
|
||||
* @return
|
||||
* - ESP_OK Set DS18B20 resolution success.
|
||||
* - ESP_ERR_INVALID_ARG Invalid argument.
|
||||
* - ESP_ERR_NOT_FOUND There is no device present on 1-wire bus.
|
||||
*/
|
||||
esp_err_t ds18b20_set_resolution(onewire_bus_handle_t handle, const uint8_t *rom_number, ds18b20_resolution_t resolution);
|
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
|
||||
#include "onewire_bus.h"
|
||||
#include "ds18b20.h"
|
||||
|
||||
static const char *TAG = "example";
|
||||
|
||||
#define EXAMPLE_ONEWIRE_GPIO_PIN GPIO_NUM_5
|
||||
#define EXAMPLE_ONEWIRE_MAX_DEVICES 5
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
onewire_rmt_config_t config = {
|
||||
.gpio_pin = EXAMPLE_ONEWIRE_GPIO_PIN,
|
||||
.max_rx_bytes = 10, // 10 tx bytes(1byte ROM command + 8byte ROM number + 1byte device command)
|
||||
};
|
||||
|
||||
// install new 1-wire bus
|
||||
onewire_bus_handle_t handle;
|
||||
ESP_ERROR_CHECK(onewire_new_bus_rmt(&config, &handle));
|
||||
ESP_LOGI(TAG, "1-wire bus installed");
|
||||
|
||||
// create 1-wire rom search context
|
||||
onewire_rom_search_context_handler_t context_handler;
|
||||
ESP_ERROR_CHECK(onewire_rom_search_context_create(handle, &context_handler));
|
||||
|
||||
uint8_t device_num = 0;
|
||||
uint8_t device_rom_id[EXAMPLE_ONEWIRE_MAX_DEVICES][8];
|
||||
|
||||
// search for devices on the bus
|
||||
do {
|
||||
esp_err_t search_result = onewire_rom_search(context_handler);
|
||||
|
||||
if (search_result == ESP_ERR_INVALID_CRC) {
|
||||
continue; // continue on crc error
|
||||
} else if (search_result == ESP_FAIL || search_result == ESP_ERR_NOT_FOUND) {
|
||||
break; // break on finish or no device
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(onewire_rom_get_number(context_handler, device_rom_id[device_num]));
|
||||
ESP_LOGI(TAG, "found device with rom id " ONEWIRE_ROM_ID_STR, ONEWIRE_ROM_ID(device_rom_id[device_num]));
|
||||
device_num ++;
|
||||
} while (device_num < EXAMPLE_ONEWIRE_MAX_DEVICES);
|
||||
|
||||
// delete 1-wire rom search context
|
||||
ESP_ERROR_CHECK(onewire_rom_search_context_delete(context_handler));
|
||||
ESP_LOGI(TAG, "%d device%s found on 1-wire bus", device_num, device_num > 1 ? "s" : "");
|
||||
|
||||
// convert and read temperature
|
||||
while (device_num > 0) {
|
||||
esp_err_t err;
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
|
||||
// set all sensors' temperature conversion resolution
|
||||
err = ds18b20_set_resolution(handle, NULL, DS18B20_RESOLUTION_12B);
|
||||
if (err != ESP_OK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// trigger all sensors to start temperature conversion
|
||||
err = ds18b20_trigger_temperature_conversion(handle, NULL); // skip rom to send command to all devices on the bus
|
||||
if (err != ESP_OK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(800)); // 12-bit resolution needs 750ms to convert
|
||||
|
||||
// get temperature from sensors
|
||||
for (uint8_t i = 0; i < device_num; i ++) {
|
||||
float temperature;
|
||||
err = ds18b20_get_temperature(handle, device_rom_id[i], &temperature); // read scratchpad and get temperature
|
||||
if (err != ESP_OK) {
|
||||
continue;
|
||||
}
|
||||
ESP_LOGI(TAG, "temperature of device " ONEWIRE_ROM_ID_STR ": %.2fC", ONEWIRE_ROM_ID(device_rom_id[i]), temperature);
|
||||
}
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(onewire_del_bus(handle));
|
||||
ESP_LOGI(TAG, "1-wire bus deleted");
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
|
||||
|
||||
@pytest.mark.esp32
|
||||
@pytest.mark.esp32s2
|
||||
@pytest.mark.esp32s3
|
||||
@pytest.mark.esp32c3
|
||||
@pytest.mark.generic
|
||||
def test_onewire_ds18b20_example(dut: Dut) -> None:
|
||||
dut.expect_exact('onewire_rmt: RMT Tx channel created for 1-wire bus')
|
||||
dut.expect_exact('onewire_rmt: RMT Rx channel created for 1-wire bus')
|
||||
dut.expect_exact('example: 1-wire bus installed')
|
||||
dut.expect_exact('example: 1-wire bus deleted')
|
Loading…
x
Reference in New Issue
Block a user