add 1wire components

This commit is contained in:
2022-08-22 00:45:21 +02:00
parent 0753dce6b1
commit 937ba25b84
24 changed files with 8146 additions and 0 deletions
+39
View File
@@ -0,0 +1,39 @@
# Build and deploy doxygen documention to GitHub Pages
sudo: false
dist: trusty
# Blacklist
branches:
only:
- master
# Environment variables
env:
global:
- GH_REPO_REF: github.com/DavidAntliff/esp32-owb.git
# Install dependencies
addons:
apt:
packages:
- doxygen
- doxygen-doc
- doxygen-latex
- doxygen-gui
- graphviz
# Build the docs
script:
- cd doc
- doxygen
# Deploy using Travis-CI/GitHub Pages integration support
deploy:
provider: pages
skip-cleanup: true
local-dir: doc/html
github-token: $GITHUB_TOKEN
on:
branch: master
target-branch: gh-pages
+5
View File
@@ -0,0 +1,5 @@
set(COMPONENT_ADD_INCLUDEDIRS include)
set(COMPONENT_SRCS "owb.c" "owb_gpio.c" "owb_rmt.c")
register_component()
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 David Antliff
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.
+60
View File
@@ -0,0 +1,60 @@
# esp32-owb
This is a ESP32-compatible C component for the Maxim Integrated "1-Wire" protocol.
It is written and tested for version 3.0-3.3 and 4.1-beta1 of the [ESP-IDF](https://github.com/espressif/esp-idf)
environment, using the xtensa-esp32-elf toolchain (gcc version 5.2.0, crosstool-ng-1.22.0-80-g6c4433a).
Support for v2.1 is available on the [ESP-IDF_v2.1](https://github.com/DavidAntliff/esp32-owb/tree/ESP-IDF_v2.1) branch.
## Features
This library includes:
* External power supply mode.
* Parasitic power mode.
* Static (stack-based) or dynamic (malloc-based) memory model.
* No globals - support any number of 1-Wire buses simultaneously.
* 1-Wire device detection and validation, including search for multiple devices on a single bus.
* Addressing optimisation for a single (solo) device on a bus.
* 1-Wire bus operations including multi-byte read and write operations.
* CRC checks on ROM code.
This component includes two methods of bus access - delay-driven GPIO and RMT-driven slots.
The original implementation used CPU delays to construct the 1-Wire read/write timeslots
however this proved to be too unreliable. A second method, using the ESP32's RMT peripheral,
results in very accurate read/write timeslots and more reliable operation.
Therefore I highly recommend that you use the RMT driver. *The GPIO driver is deprecated and will be removed.*
See documentation for [esp32-ds18b20](https://www.github.com/DavidAntliff/esp32-ds18b20-example#parasitic-power-mode)
for further information about parasitic power mode, including strong pull-up configuration.
## Documentation
Automatically generated API documentation (doxygen) is available [here](https://davidantliff.github.io/esp32-owb/index.html).
## Source Code
The source is available from [GitHub](https://www.github.com/DavidAntliff/esp32-owb).
## License
The code in this project is licensed under the MIT license - see LICENSE for details.
## Links
* [esp32-ds18b20](https://github.com/DavidAntliff/esp32-ds18b20) - ESP32-compatible DS18B20 Digital Thermometer
component for ESP32
* [1-Wire Communication Through Software](https://www.maximintegrated.com/en/app-notes/index.mvp/id/126)
* [1-Wire Search Algorithm](https://www.maximintegrated.com/en/app-notes/index.mvp/id/187)
* [Espressif IoT Development Framework for ESP32](https://github.com/espressif/esp-idf)
## Acknowledgements
Thank you to [Chris Morgan](https://github.com/chmorgan) for his contribution of adding RMT peripheral support for more
reliable operation.
Parts of this code are based on references provided to the public domain by Maxim Integrated.
"1-Wire" is a registered trademark of Maxim Integrated.
+1
View File
@@ -0,0 +1 @@
# Use defaults.
+3
View File
@@ -0,0 +1,3 @@
html/
latex/
File diff suppressed because it is too large Load Diff
+336
View File
@@ -0,0 +1,336 @@
/*
* MIT License
*
* Copyright (c) 2017 David Antliff
* Copyright (c) 2017 Chris Morgan <chmorgan@gmail.com>
*
* 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
* @brief Interface definitions for the 1-Wire bus component.
*
* This component provides structures and functions that are useful for communicating
* with devices connected to a Maxim Integrated 1-Wire® bus via a single GPIO.
*
* Externally powered and "parasite-powered" devices are supported.
* Please consult your device's datasheet for power requirements.
*/
#ifndef ONE_WIRE_BUS_H
#define ONE_WIRE_BUS_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "driver/gpio.h"
#ifdef __cplusplus
extern "C" {
#endif
// ROM commands
#define OWB_ROM_SEARCH 0xF0 ///< Perform Search ROM cycle to identify devices on the bus
#define OWB_ROM_READ 0x33 ///< Read device ROM (single device on bus only)
#define OWB_ROM_MATCH 0x55 ///< Address a specific device on the bus by ROM
#define OWB_ROM_SKIP 0xCC ///< Address all devices on the bus simultaneously
#define OWB_ROM_SEARCH_ALARM 0xEC ///< Address all devices on the bus with a set alarm flag
#define OWB_ROM_CODE_STRING_LENGTH (17) ///< Typical length of OneWire bus ROM ID as ASCII hex string, including null terminator
#ifndef GPIO_NUM_NC
# define GPIO_NUM_NC (-1) ///< ESP-IDF prior to v4.x does not define GPIO_NUM_NC
#endif
struct owb_driver;
/**
* @brief Structure containing 1-Wire bus information relevant to a single instance.
*/
typedef struct
{
const struct _OneWireBus_Timing * timing; ///< Pointer to timing information
bool use_crc; ///< True if CRC checks are to be used when retrieving information from a device on the bus
bool use_parasitic_power; ///< True if parasitic-powered devices are expected on the bus
gpio_num_t strong_pullup_gpio; ///< Set if an external strong pull-up circuit is required
const struct owb_driver * driver; ///< Pointer to hardware driver instance
} OneWireBus;
/**
* @brief Represents a 1-Wire ROM Code. This is a sequence of eight bytes, where
* the first byte is the family number, then the following 6 bytes form the
* serial number. The final byte is the CRC8 check byte.
*/
typedef union
{
/// Provides access via field names
struct fields
{
uint8_t family[1]; ///< family identifier (1 byte, LSB - read/write first)
uint8_t serial_number[6]; ///< serial number (6 bytes)
uint8_t crc[1]; ///< CRC check byte (1 byte, MSB - read/write last)
} fields; ///< Provides access via field names
uint8_t bytes[8]; ///< Provides raw byte access
} OneWireBus_ROMCode;
/**
* @brief Represents the state of a device search on the 1-Wire bus.
*
* Pass a pointer to this structure to owb_search_first() and
* owb_search_next() to iterate through detected devices on the bus.
*/
typedef struct
{
OneWireBus_ROMCode rom_code; ///< Device ROM code
int last_discrepancy; ///< Bit index that identifies from which bit the next search discrepancy check should start
int last_family_discrepancy; ///< Bit index that identifies the last discrepancy within the first 8-bit family code of the ROM code
int last_device_flag; ///< Flag to indicate previous search was the last device detected
} OneWireBus_SearchState;
/**
* @brief Represents the result of OWB API functions.
*/
typedef enum
{
OWB_STATUS_NOT_SET = -1, ///< A status value has not been set
OWB_STATUS_OK = 0, ///< Operation succeeded
OWB_STATUS_NOT_INITIALIZED, ///< Function was passed an uninitialised variable
OWB_STATUS_PARAMETER_NULL, ///< Function was passed a null pointer
OWB_STATUS_DEVICE_NOT_RESPONDING, ///< No response received from the addressed device or devices
OWB_STATUS_CRC_FAILED, ///< CRC failed on data received from a device or devices
OWB_STATUS_TOO_MANY_BITS, ///< Attempt to write an incorrect number of bits to the One Wire Bus
OWB_STATUS_HW_ERROR ///< A hardware error occurred
} owb_status;
/** NOTE: Driver assumes that (*init) was called prior to any other methods */
struct owb_driver
{
/** Driver identification **/
const char* name;
/** Pointer to driver uninitialization function **/
owb_status (*uninitialize)(const OneWireBus * bus);
/** Pointer to driver reset functio **/
owb_status (*reset)(const OneWireBus * bus, bool *is_present);
/** NOTE: The data is shifted out of the low bits, eg. it is written in the order of lsb to msb */
owb_status (*write_bits)(const OneWireBus *bus, uint8_t out, int number_of_bits_to_write);
/** NOTE: Data is read into the high bits, eg. each bit read is shifted down before the next bit is read */
owb_status (*read_bits)(const OneWireBus *bus, uint8_t *in, int number_of_bits_to_read);
};
/// @cond ignore
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
/// @endcond
/**
* @brief call to release resources after completing use of the OneWireBus
* @param[in] bus Pointer to initialised bus instance.
* @return status
*/
owb_status owb_uninitialize(OneWireBus * bus);
/**
* @brief Enable or disable use of CRC checks on device communications.
* @param[in] bus Pointer to initialised bus instance.
* @param[in] use_crc True to enable CRC checks, false to disable.
* @return status
*/
owb_status owb_use_crc(OneWireBus * bus, bool use_crc);
/**
* @brief Enable or disable use of parasitic power on the One Wire Bus.
* This affects how devices signal on the bus, as devices cannot
* signal by pulling the bus low when it is pulled high.
* @param[in] bus Pointer to initialised bus instance.
* @param[in] use_parasitic_power True to enable parasitic power, false to disable.
* @return status
*/
owb_status owb_use_parasitic_power(OneWireBus * bus, bool use_parasitic_power);
/**
* @brief Enable or disable use of extra GPIO to activate strong pull-up circuit.
* This only has effect if parasitic power mode is enabled.
* signal by pulling the bus low when it is pulled high.
* @param[in] bus Pointer to initialised bus instance.
* @param[in] gpio Set to GPIO number to use, or GPIO_NUM_NC to disable.
* @return status
*/
owb_status owb_use_strong_pullup_gpio(OneWireBus * bus, gpio_num_t gpio);
/**
* @brief Read ROM code from device - only works when there is a single device on the bus.
* @param[in] bus Pointer to initialised bus instance.
* @param[out] rom_code the value read from the device's rom
* @return status
*/
owb_status owb_read_rom(const OneWireBus * bus, OneWireBus_ROMCode * rom_code);
/**
* @brief Verify the device specified by ROM code is present.
* @param[in] bus Pointer to initialised bus instance.
* @param[in] rom_code ROM code to verify.
* @param[out] is_present Set to true if a device is present, false if not
* @return status
*/
owb_status owb_verify_rom(const OneWireBus * bus, OneWireBus_ROMCode rom_code, bool * is_present);
/**
* @brief Reset the 1-Wire bus.
* @param[in] bus Pointer to initialised bus instance.
* @param[out] is_present set to true if at least one device is present on the bus
* @return status
*/
owb_status owb_reset(const OneWireBus * bus, bool * is_present);
/**
* @brief Read a single bit from the 1-Wire bus.
* @param[in] bus Pointer to initialised bus instance.
* @param[out] out The bit value read from the bus.
* @return status
*/
owb_status owb_read_bit(const OneWireBus * bus, uint8_t * out);
/**
* @brief Read a single byte from the 1-Wire bus.
* @param[in] bus Pointer to initialised bus instance.
* @param[out] out The byte value read from the bus (lsb only).
* @return status
*/
owb_status owb_read_byte(const OneWireBus * bus, uint8_t * out);
/**
* @brief Read a number of bytes from the 1-Wire bus.
* @param[in] bus Pointer to initialised bus instance.
* @param[in, out] buffer Pointer to buffer to receive read data.
* @param[in] len Number of bytes to read, must not exceed length of receive buffer.
* @return status.
*/
owb_status owb_read_bytes(const OneWireBus * bus, uint8_t * buffer, unsigned int len);
/**
* @brief Write a bit to the 1-Wire bus.
* @param[in] bus Pointer to initialised bus instance.
* @param[in] bit Value to write (lsb only).
* @return status
*/
owb_status owb_write_bit(const OneWireBus * bus, uint8_t bit);
/**
* @brief Write a single byte to the 1-Wire bus.
* @param[in] bus Pointer to initialised bus instance.
* @param[in] data Byte value to write to bus.
* @return status
*/
owb_status owb_write_byte(const OneWireBus * bus, uint8_t data);
/**
* @brief Write a number of bytes to the 1-Wire bus.
* @param[in] bus Pointer to initialised bus instance.
* @param[in] buffer Pointer to buffer to write data from.
* @param[in] len Number of bytes to write.
* @return status
*/
owb_status owb_write_bytes(const OneWireBus * bus, const uint8_t * buffer, size_t len);
/**
* @brief Write a ROM code to the 1-Wire bus ensuring LSB is sent first.
* @param[in] bus Pointer to initialised bus instance.
* @param[in] rom_code ROM code to write to bus.
* @return status
*/
owb_status owb_write_rom_code(const OneWireBus * bus, OneWireBus_ROMCode rom_code);
/**
* @brief 1-Wire 8-bit CRC lookup.
* @param[in] crc Starting CRC value. Pass in prior CRC to accumulate.
* @param[in] data Byte to feed into CRC.
* @return Resultant CRC value.
* Should be zero if last byte was the CRC byte and the CRC matches.
*/
uint8_t owb_crc8_byte(uint8_t crc, uint8_t data);
/**
* @brief 1-Wire 8-bit CRC lookup with accumulation over a block of bytes.
* @param[in] crc Starting CRC value. Pass in prior CRC to accumulate.
* @param[in] data Array of bytes to feed into CRC.
* @param[in] len Length of data array in bytes.
* @return Resultant CRC value.
* Should be zero if last byte was the CRC byte and the CRC matches.
*/
uint8_t owb_crc8_bytes(uint8_t crc, const uint8_t * data, size_t len);
/**
* @brief Locates the first device on the 1-Wire bus, if present.
* @param[in] bus Pointer to initialised bus instance.
* @param[in,out] state Pointer to an existing search state structure.
* @param[out] found_device True if a device is found, false if no devices are found.
* If a device is found, the ROM Code can be obtained from the state.
* @return status
*/
owb_status owb_search_first(const OneWireBus * bus, OneWireBus_SearchState * state, bool *found_device);
/**
* @brief Locates the next device on the 1-Wire bus, if present, starting from
* the provided state. Further calls will yield additional devices, if present.
* @param[in] bus Pointer to initialised bus instance.
* @param[in,out] state Pointer to an existing search state structure.
* @param[out] found_device True if a device is found, false if no devices are found.
* If a device is found, the ROM Code can be obtained from the state.
* @return status
*/
owb_status owb_search_next(const OneWireBus * bus, OneWireBus_SearchState * state, bool *found_device);
/**
* @brief Create a string representation of a ROM code, most significant byte (CRC8) first.
* @param[in] rom_code The ROM code to convert to string representation.
* @param[out] buffer The destination for the string representation. It will be null terminated.
* @param[in] len The length of the buffer in bytes. 64-bit ROM codes require 16 characters
* to represent as a string, plus a null terminator, for 17 bytes.
* See OWB_ROM_CODE_STRING_LENGTH.
* @return pointer to the byte beyond the last byte written
*/
char * owb_string_from_rom_code(OneWireBus_ROMCode rom_code, char * buffer, size_t len);
/**
* @brief Enable or disable the strong-pullup GPIO, if configured.
* @param[in] bus Pointer to initialised bus instance.
* @param[in] enable If true, enable the external strong pull-up by setting the GPIO high.
* If false, disable the external strong pull-up by setting the GPIO low.
* @return status
*/
owb_status owb_set_strong_pullup(const OneWireBus * bus, bool enable);
#include "owb_gpio.h"
#include "owb_rmt.h"
#ifdef __cplusplus
}
#endif
#endif // ONE_WIRE_BUS_H
+70
View File
@@ -0,0 +1,70 @@
/*
* MIT License
*
* Copyright (c) 2017 David Antliff
* Copyright (c) 2017 Chris Morgan <chmorgan@gmail.com>
*
* 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
* @brief Interface definitions for the ESP32 GPIO driver used to communicate with devices
* on the One Wire Bus.
*
* @deprecated
* This driver is deprecated and may be removed at some stage. It is not recommended for use
* due to issues with imprecise timing.
*/
#pragma once
#ifndef OWB_GPIO_H
#define OWB_GPIO_H
#include "owb.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief GPIO driver information
*/
typedef struct
{
int gpio; ///< Value of the GPIO connected to the 1-Wire bus
OneWireBus bus; ///< OneWireBus instance
} owb_gpio_driver_info;
/**
* @brief Initialise the GPIO driver.
* @return OneWireBus*, pass this into the other OneWireBus public API functions
*/
OneWireBus * owb_gpio_initialize(owb_gpio_driver_info *driver_info, int gpio);
/**
* @brief Clean up after a call to owb_gpio_initialize()
*/
void owb_gpio_uninitialize(owb_gpio_driver_info *driver_info);
#ifdef __cplusplus
}
#endif
#endif // OWB_GPIO_H
+75
View File
@@ -0,0 +1,75 @@
/*
* MIT License
*
* Copyright (c) 2017 David Antliff
* Copyright (c) 2017 Chris Morgan <chmorgan@gmail.com>
*
* 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
* @brief Interface definitions for ESP32 RMT driver used to communicate with devices
* on the One Wire Bus.
*
* This is the recommended driver.
*/
#pragma once
#ifndef OWB_RMT_H
#define OWB_RMT_H
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/ringbuf.h"
#include "driver/rmt.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief RMT driver information
*/
typedef struct
{
int tx_channel; ///< RMT channel to use for TX
int rx_channel; ///< RMT channel to use for RX
RingbufHandle_t rb; ///< Ring buffer handle
int gpio; ///< OneWireBus GPIO
OneWireBus bus; ///< OneWireBus instance
} owb_rmt_driver_info;
/**
* @brief Initialise the RMT driver.
* @param[in] info Pointer to an uninitialized owb_rmt_driver_info structure.
* Note: the structure must remain in scope for the lifetime of this component.
* @param[in] gpio_num The GPIO number to use as the One Wire bus data line.
* @param[in] tx_channel The RMT channel to use for transmitting data to bus devices.
* @param[in] rx_channel the RMT channel to use for receiving data from bus devices.
* @return OneWireBus *, pass this into the other OneWireBus public API functions
*/
OneWireBus* owb_rmt_initialize(owb_rmt_driver_info * info, gpio_num_t gpio_num,
rmt_channel_t tx_channel, rmt_channel_t rx_channel);
#ifdef __cplusplus
}
#endif
#endif // OWB_RMT_H
+30
View File
@@ -0,0 +1,30 @@
{
"name": "esp32-owb",
"keywords": "onewire, 1-wire, bus, sensor, temperature",
"description": "ESP32-compatible C component for the Maxim Integrated 1-Wire protocol.",
"repository":
{
"type": "git",
"url": "https://github.com/DavidAntliff/esp32-owb.git"
},
"authors":
[
{
"name": "David Antliff",
"url": "https://github.com/DavidAntliff",
"maintainer": true
},
{
"name": "Chris Morgan",
"url": "https://github.com/chmorgan"
}
],
"version": "0.1",
"frameworks": "espidf",
"platforms": "espressif32",
"build": {
"flags": [
"-I include/"
]
}
}
+744
View File
@@ -0,0 +1,744 @@
/*
* MIT License
*
* Copyright (c) 2017 David Antliff
* Copyright (c) 2017 Chris Morgan <chmorgan@gmail.com>
*
* 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 "owb.h"
#include "owb_gpio.h"
static const char * TAG = "owb";
static bool _is_init(const OneWireBus * bus)
{
bool ok = false;
if (bus != NULL)
{
if (bus->driver)
{
// OK
ok = true;
}
else
{
ESP_LOGE(TAG, "bus is not initialised");
}
}
else
{
ESP_LOGE(TAG, "bus is NULL");
}
return ok;
}
/**
* @brief 1-Wire 8-bit CRC lookup.
* @param[in] crc Starting CRC value. Pass in prior CRC to accumulate.
* @param[in] data Byte to feed into CRC.
* @return Resultant CRC value.
*/
static uint8_t _calc_crc(uint8_t crc, uint8_t data)
{
// https://www.maximintegrated.com/en/app-notes/index.mvp/id/27
static const uint8_t table[256] = {
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
};
return table[crc ^ data];
}
static uint8_t _calc_crc_block(uint8_t crc, const uint8_t * buffer, size_t len)
{
do
{
crc = _calc_crc(crc, *buffer++);
ESP_LOGD(TAG, "buffer 0x%02x, crc 0x%02x, len %d", (uint8_t)*(buffer - 1), (int)crc, (int)len);
}
while (--len > 0);
return crc;
}
/**
* @param[out] is_found true if a device was found, false if not
* @return status
*/
static owb_status _search(const OneWireBus * bus, OneWireBus_SearchState * state, bool * is_found)
{
// Based on https://www.maximintegrated.com/en/app-notes/index.mvp/id/187
// initialize for search
int id_bit_number = 1;
int last_zero = 0;
int rom_byte_number = 0;
uint8_t id_bit = 0;
uint8_t cmp_id_bit = 0;
uint8_t rom_byte_mask = 1;
uint8_t search_direction = 0;
bool search_result = false;
uint8_t crc8 = 0;
owb_status status = OWB_STATUS_NOT_SET;
// if the last call was not the last one
if (!state->last_device_flag)
{
// 1-Wire reset
bool is_present;
bus->driver->reset(bus, &is_present);
if (!is_present)
{
// reset the search
state->last_discrepancy = 0;
state->last_device_flag = false;
state->last_family_discrepancy = 0;
*is_found = false;
return OWB_STATUS_OK;
}
// issue the search command
bus->driver->write_bits(bus, OWB_ROM_SEARCH, 8);
// loop to do the search
do
{
id_bit = cmp_id_bit = 0;
// read a bit and its complement
bus->driver->read_bits(bus, &id_bit, 1);
bus->driver->read_bits(bus, &cmp_id_bit, 1);
// check for no devices on 1-wire (signal level is high in both bit reads)
if (id_bit && cmp_id_bit)
{
break;
}
else
{
// all devices coupled have 0 or 1
if (id_bit != cmp_id_bit)
{
search_direction = (id_bit) ? 1 : 0; // bit write value for search
}
else
{
// if this discrepancy if before the Last Discrepancy
// on a previous next then pick the same as last time
if (id_bit_number < state->last_discrepancy)
{
search_direction = ((state->rom_code.bytes[rom_byte_number] & rom_byte_mask) > 0);
}
else
{
// if equal to last pick 1, if not then pick 0
search_direction = (id_bit_number == state->last_discrepancy);
}
// if 0 was picked then record its position in LastZero
if (search_direction == 0)
{
last_zero = id_bit_number;
// check for Last discrepancy in family
if (last_zero < 9)
{
state->last_family_discrepancy = last_zero;
}
}
}
// set or clear the bit in the ROM byte rom_byte_number
// with mask rom_byte_mask
if (search_direction == 1)
{
state->rom_code.bytes[rom_byte_number] |= rom_byte_mask;
}
else
{
state->rom_code.bytes[rom_byte_number] &= ~rom_byte_mask;
}
// serial number search direction write bit
bus->driver->write_bits(bus, search_direction, 1);
// increment the byte counter id_bit_number
// and shift the mask rom_byte_mask
id_bit_number++;
rom_byte_mask <<= 1;
// if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask
if (rom_byte_mask == 0)
{
crc8 = owb_crc8_byte(crc8, state->rom_code.bytes[rom_byte_number]); // accumulate the CRC
rom_byte_number++;
rom_byte_mask = 1;
}
}
}
while (rom_byte_number < 8); // loop until through all ROM bytes 0-7
// if the search was successful then
if (!((id_bit_number < 65) || (crc8 != 0)))
{
// search successful so set LastDiscrepancy,LastDeviceFlag,search_result
state->last_discrepancy = last_zero;
// check for last device
if (state->last_discrepancy == 0)
{
state->last_device_flag = true;
}
search_result = true;
}
}
// if no device found then reset counters so next 'search' will be like a first
if (!search_result || !state->rom_code.bytes[0])
{
state->last_discrepancy = 0;
state->last_device_flag = false;
state->last_family_discrepancy = 0;
search_result = false;
}
status = OWB_STATUS_OK;
*is_found = search_result;
return status;
}
// Public API
owb_status owb_uninitialize(OneWireBus * bus)
{
owb_status status = OWB_STATUS_NOT_SET;
if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
bus->driver->uninitialize(bus);
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_use_crc(OneWireBus * bus, bool use_crc)
{
owb_status status = OWB_STATUS_NOT_SET;
if (!bus)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
bus->use_crc = use_crc;
ESP_LOGD(TAG, "use_crc %d", bus->use_crc);
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_use_parasitic_power(OneWireBus * bus, bool use_parasitic_power)
{
owb_status status = OWB_STATUS_NOT_SET;
if (!bus)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
bus->use_parasitic_power = use_parasitic_power;
ESP_LOGD(TAG, "use_parasitic_power %d", bus->use_parasitic_power);
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_use_strong_pullup_gpio(OneWireBus * bus, gpio_num_t gpio)
{
owb_status status = OWB_STATUS_NOT_SET;
if (!bus)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
if (gpio != GPIO_NUM_NC) {
// The strong GPIO pull-up is only activated if parasitic-power mode is enabled
if (!bus->use_parasitic_power) {
ESP_LOGW(TAG, "Strong pull-up GPIO set with parasitic-power disabled");
}
gpio_pad_select_gpio(gpio);
gpio_set_direction(gpio, GPIO_MODE_OUTPUT);
}
else
{
gpio_reset_pin(bus->strong_pullup_gpio);
}
bus->strong_pullup_gpio = gpio;
ESP_LOGD(TAG, "use_strong_pullup_gpio %d", bus->strong_pullup_gpio);
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_read_rom(const OneWireBus * bus, OneWireBus_ROMCode *rom_code)
{
owb_status status = OWB_STATUS_NOT_SET;
memset(rom_code, 0, sizeof(OneWireBus_ROMCode));
if (!bus || !rom_code)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
bool is_present;
bus->driver->reset(bus, &is_present);
if (is_present)
{
uint8_t value = OWB_ROM_READ;
bus->driver->write_bits(bus, value, 8);
owb_read_bytes(bus, rom_code->bytes, sizeof(OneWireBus_ROMCode));
if (bus->use_crc)
{
if (owb_crc8_bytes(0, rom_code->bytes, sizeof(OneWireBus_ROMCode)) != 0)
{
ESP_LOGE(TAG, "CRC failed");
memset(rom_code->bytes, 0, sizeof(OneWireBus_ROMCode));
status = OWB_STATUS_CRC_FAILED;
}
else
{
status = OWB_STATUS_OK;
}
}
else
{
status = OWB_STATUS_OK;
}
char rom_code_s[OWB_ROM_CODE_STRING_LENGTH];
owb_string_from_rom_code(*rom_code, rom_code_s, sizeof(rom_code_s));
ESP_LOGD(TAG, "rom_code %s", rom_code_s);
}
else
{
status = OWB_STATUS_DEVICE_NOT_RESPONDING;
ESP_LOGE(TAG, "ds18b20 device not responding");
}
}
return status;
}
owb_status owb_verify_rom(const OneWireBus * bus, OneWireBus_ROMCode rom_code, bool * is_present)
{
owb_status status = OWB_STATUS_NOT_SET;
bool result = false;
if (!bus || !is_present)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
OneWireBus_SearchState state = {
.rom_code = rom_code,
.last_discrepancy = 64,
.last_device_flag = false,
};
bool is_found = false;
_search(bus, &state, &is_found);
if (is_found)
{
result = true;
for (int i = 0; i < sizeof(state.rom_code.bytes) && result; ++i)
{
result = rom_code.bytes[i] == state.rom_code.bytes[i];
ESP_LOGD(TAG, "%02x %02x", rom_code.bytes[i], state.rom_code.bytes[i]);
}
is_found = result;
}
ESP_LOGD(TAG, "state.last_discrepancy %d, state.last_device_flag %d, is_found %d",
state.last_discrepancy, state.last_device_flag, is_found);
ESP_LOGD(TAG, "rom code %sfound", result ? "" : "not ");
*is_present = result;
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_reset(const OneWireBus * bus, bool * a_device_present)
{
owb_status status = OWB_STATUS_NOT_SET;
if (!bus || !a_device_present)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if(!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
status = bus->driver->reset(bus, a_device_present);
}
return status;
}
owb_status owb_read_bit(const OneWireBus * bus, uint8_t * out)
{
owb_status status = OWB_STATUS_NOT_SET;
if (!bus || !out)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
bus->driver->read_bits(bus, out, 1);
ESP_LOGD(TAG, "owb_read_bit: %02x", *out);
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_read_byte(const OneWireBus * bus, uint8_t * out)
{
owb_status status = OWB_STATUS_NOT_SET;
if (!bus || !out)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
bus->driver->read_bits(bus, out, 8);
ESP_LOGD(TAG, "owb_read_byte: %02x", *out);
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_read_bytes(const OneWireBus * bus, uint8_t * buffer, unsigned int len)
{
owb_status status = OWB_STATUS_NOT_SET;
if (!bus || !buffer)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
for (int i = 0; i < len; ++i)
{
uint8_t out;
bus->driver->read_bits(bus, &out, 8);
buffer[i] = out;
}
ESP_LOGD(TAG, "owb_read_bytes, len %d:", len);
ESP_LOG_BUFFER_HEX_LEVEL(TAG, buffer, len, ESP_LOG_DEBUG);
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_write_bit(const OneWireBus * bus, const uint8_t bit)
{
owb_status status = OWB_STATUS_NOT_SET;
if (!bus)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
ESP_LOGD(TAG, "owb_write_bit: %02x", bit);
bus->driver->write_bits(bus, bit & 0x01u, 1);
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_write_byte(const OneWireBus * bus, uint8_t data)
{
owb_status status = OWB_STATUS_NOT_SET;
if (!bus)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
ESP_LOGD(TAG, "owb_write_byte: %02x", data);
bus->driver->write_bits(bus, data, 8);
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_write_bytes(const OneWireBus * bus, const uint8_t * buffer, size_t len)
{
owb_status status = OWB_STATUS_NOT_SET;
if (!bus || !buffer)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
ESP_LOGD(TAG, "owb_write_bytes, len %d:", len);
ESP_LOG_BUFFER_HEX_LEVEL(TAG, buffer, len, ESP_LOG_DEBUG);
for (int i = 0; i < len; i++)
{
bus->driver->write_bits(bus, buffer[i], 8);
}
status = OWB_STATUS_OK;
}
return status;
}
owb_status owb_write_rom_code(const OneWireBus * bus, OneWireBus_ROMCode rom_code)
{
owb_status status = OWB_STATUS_NOT_SET;
if (!bus)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
owb_write_bytes(bus, (uint8_t*)&rom_code, sizeof(rom_code));
status = OWB_STATUS_OK;
}
return status;
}
uint8_t owb_crc8_byte(uint8_t crc, uint8_t data)
{
return _calc_crc(crc, data);
}
uint8_t owb_crc8_bytes(uint8_t crc, const uint8_t * data, size_t len)
{
return _calc_crc_block(crc, data, len);
}
owb_status owb_search_first(const OneWireBus * bus, OneWireBus_SearchState * state, bool * found_device)
{
bool result;
owb_status status = OWB_STATUS_NOT_SET;
if (!bus || !state || !found_device)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
memset(&state->rom_code, 0, sizeof(state->rom_code));
state->last_discrepancy = 0;
state->last_family_discrepancy = 0;
state->last_device_flag = false;
_search(bus, state, &result);
status = OWB_STATUS_OK;
*found_device = result;
}
return status;
}
owb_status owb_search_next(const OneWireBus * bus, OneWireBus_SearchState * state, bool * found_device)
{
owb_status status = OWB_STATUS_NOT_SET;
bool result = false;
if (!bus || !state || !found_device)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
_search(bus, state, &result);
status = OWB_STATUS_OK;
*found_device = result;
}
return status;
}
char * owb_string_from_rom_code(OneWireBus_ROMCode rom_code, char * buffer, size_t len)
{
for (int i = sizeof(rom_code.bytes) - 1; i >= 0; i--)
{
sprintf(buffer, "%02x", rom_code.bytes[i]);
buffer += 2;
}
return buffer;
}
owb_status owb_set_strong_pullup(const OneWireBus * bus, bool enable)
{
owb_status status = OWB_STATUS_NOT_SET;
if (!bus)
{
status = OWB_STATUS_PARAMETER_NULL;
}
else if (!_is_init(bus))
{
status = OWB_STATUS_NOT_INITIALIZED;
}
else
{
if (bus->use_parasitic_power && bus->strong_pullup_gpio != GPIO_NUM_NC)
{
gpio_set_level(bus->strong_pullup_gpio, enable ? 1 : 0);
ESP_LOGD(TAG, "strong pullup GPIO %d", enable);
} // else ignore
status = OWB_STATUS_OK;
}
return status;
}
+287
View File
@@ -0,0 +1,287 @@
/*
* MIT License
*
* Copyright (c) 2017 David Antliff
* Copyright (c) 2017 Chris Morgan <chmorgan@gmail.com>
*
* 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 "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);
}
+469
View File
@@ -0,0 +1,469 @@
/*
Created by Chris Morgan based on the nodemcu project driver.
Copyright 2017 Chris Morgan <chmorgan@gmail.com>
Ported to ESP32 RMT peripheral for low-level signal generation by Arnim Laeuger.
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.
Much of the code was inspired by Derek Yerger's code, though I don't
think much of that remains. In any event that was..
(copyleft) 2006 by Derek Yerger - Free to distribute freely.
The CRC code was excerpted and inspired by the Dallas Semiconductor
sample code bearing this copyright.
//---------------------------------------------------------------------------
// Copyright (C) 2000 Dallas Semiconductor Corporation, All Rights Reserved.
//
// 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 DALLAS SEMICONDUCTOR 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.
//
// Except as contained in this notice, the name of Dallas Semiconductor
// shall not be used except as stated in the Dallas Semiconductor
// Branding Policy.
//--------------------------------------------------------------------------
*/
#include "owb.h"
#include "driver/rmt.h"
#include "driver/gpio.h"
#include "esp_log.h"
#undef OW_DEBUG
// bus reset: duration of low phase [us]
#define OW_DURATION_RESET 480
// overall slot duration
#define OW_DURATION_SLOT 75
// write 1 slot and read slot durations [us]
#define OW_DURATION_1_LOW 2
#define OW_DURATION_1_HIGH (OW_DURATION_SLOT - OW_DURATION_1_LOW)
// write 0 slot durations [us]
#define OW_DURATION_0_LOW 65
#define OW_DURATION_0_HIGH (OW_DURATION_SLOT - OW_DURATION_0_LOW)
// sample time for read slot
#define OW_DURATION_SAMPLE (15-2)
// RX idle threshold
// needs to be larger than any duration occurring during write slots
#define OW_DURATION_RX_IDLE (OW_DURATION_SLOT + 2)
// maximum number of bits that can be read or written per slot
#define MAX_BITS_PER_SLOT (8)
static const char * TAG = "owb_rmt";
#define info_of_driver(owb) container_of(owb, owb_rmt_driver_info, bus)
// flush any pending/spurious traces from the RX channel
static void onewire_flush_rmt_rx_buf(const OneWireBus * bus)
{
void * p = NULL;
size_t s = 0;
owb_rmt_driver_info * i = info_of_driver(bus);
while ((p = xRingbufferReceive(i->rb, &s, 0)))
{
ESP_LOGD(TAG, "flushing entry");
vRingbufferReturnItem(i->rb, p);
}
}
static owb_status _reset(const OneWireBus * bus, bool * is_present)
{
rmt_item32_t tx_items[1] = {0};
bool _is_present = false;
int res = OWB_STATUS_OK;
owb_rmt_driver_info * i = info_of_driver(bus);
tx_items[0].duration0 = OW_DURATION_RESET;
tx_items[0].level0 = 0;
tx_items[0].duration1 = 0;
tx_items[0].level1 = 1;
uint16_t old_rx_thresh = 0;
rmt_get_rx_idle_thresh(i->rx_channel, &old_rx_thresh);
rmt_set_rx_idle_thresh(i->rx_channel, OW_DURATION_RESET + 60);
onewire_flush_rmt_rx_buf(bus);
rmt_rx_start(i->rx_channel, true);
if (rmt_write_items(i->tx_channel, tx_items, 1, true) == ESP_OK)
{
size_t rx_size = 0;
rmt_item32_t * rx_items = (rmt_item32_t *)xRingbufferReceive(i->rb, &rx_size, 100 / portTICK_PERIOD_MS);
if (rx_items)
{
if (rx_size >= (1 * sizeof(rmt_item32_t)))
{
#ifdef OW_DEBUG
ESP_LOGI(TAG, "rx_size: %d", rx_size);
for (int i = 0; i < (rx_size / sizeof(rmt_item32_t)); i++)
{
ESP_LOGI(TAG, "i: %d, level0: %d, duration %d", i, rx_items[i].level0, rx_items[i].duration0);
ESP_LOGI(TAG, "i: %d, level1: %d, duration %d", i, rx_items[i].level1, rx_items[i].duration1);
}
#endif
// parse signal and search for presence pulse
if ((rx_items[0].level0 == 0) && (rx_items[0].duration0 >= OW_DURATION_RESET - 2))
{
if ((rx_items[0].level1 == 1) && (rx_items[0].duration1 > 0))
{
if (rx_items[1].level0 == 0)
{
_is_present = true;
}
}
}
}
vRingbufferReturnItem(i->rb, (void *)rx_items);
}
else
{
// time out occurred, this indicates an unconnected / misconfigured bus
ESP_LOGE(TAG, "rx_items == 0");
res = OWB_STATUS_HW_ERROR;
}
}
else
{
// error in tx channel
ESP_LOGE(TAG, "Error tx");
res = OWB_STATUS_HW_ERROR;
}
rmt_rx_stop(i->rx_channel);
rmt_set_rx_idle_thresh(i->rx_channel, old_rx_thresh);
*is_present = _is_present;
ESP_LOGD(TAG, "_is_present %d", _is_present);
return res;
}
static rmt_item32_t _encode_write_slot(uint8_t val)
{
rmt_item32_t item = {0};
item.level0 = 0;
item.level1 = 1;
if (val)
{
// write "1" slot
item.duration0 = OW_DURATION_1_LOW;
item.duration1 = OW_DURATION_1_HIGH;
}
else
{
// write "0" slot
item.duration0 = OW_DURATION_0_LOW;
item.duration1 = OW_DURATION_0_HIGH;
}
return item;
}
/** NOTE: The data is shifted out of the low bits, eg. it is written in the order of lsb to msb */
static owb_status _write_bits(const OneWireBus * bus, uint8_t out, int number_of_bits_to_write)
{
rmt_item32_t tx_items[MAX_BITS_PER_SLOT + 1] = {0};
owb_rmt_driver_info * info = info_of_driver(bus);
if (number_of_bits_to_write > MAX_BITS_PER_SLOT)
{
return OWB_STATUS_TOO_MANY_BITS;
}
// write requested bits as pattern to TX buffer
for (int i = 0; i < number_of_bits_to_write; i++)
{
tx_items[i] = _encode_write_slot(out & 0x01);
out >>= 1;
}
// end marker
tx_items[number_of_bits_to_write].level0 = 1;
tx_items[number_of_bits_to_write].duration0 = 0;
owb_status status = OWB_STATUS_NOT_SET;
if (rmt_write_items(info->tx_channel, tx_items, number_of_bits_to_write+1, true) == ESP_OK)
{
status = OWB_STATUS_OK;
}
else
{
status = OWB_STATUS_HW_ERROR;
ESP_LOGE(TAG, "rmt_write_items() failed");
}
return status;
}
static rmt_item32_t _encode_read_slot(void)
{
rmt_item32_t item = {0};
// construct pattern for a single read time slot
item.level0 = 0;
item.duration0 = OW_DURATION_1_LOW; // shortly force 0
item.level1 = 1;
item.duration1 = OW_DURATION_1_HIGH; // release high and finish slot
return item;
}
/** NOTE: Data is read into the high bits, eg. each bit read is shifted down before the next bit is read */
static owb_status _read_bits(const OneWireBus * bus, uint8_t *in, int number_of_bits_to_read)
{
rmt_item32_t tx_items[MAX_BITS_PER_SLOT + 1] = {0};
uint8_t read_data = 0;
int res = OWB_STATUS_OK;
owb_rmt_driver_info *info = info_of_driver(bus);
if (number_of_bits_to_read > MAX_BITS_PER_SLOT)
{
ESP_LOGE(TAG, "_read_bits() OWB_STATUS_TOO_MANY_BITS");
return OWB_STATUS_TOO_MANY_BITS;
}
// generate requested read slots
for (int i = 0; i < number_of_bits_to_read; i++)
{
tx_items[i] = _encode_read_slot();
}
// end marker
tx_items[number_of_bits_to_read].level0 = 1;
tx_items[number_of_bits_to_read].duration0 = 0;
onewire_flush_rmt_rx_buf(bus);
rmt_rx_start(info->rx_channel, true);
if (rmt_write_items(info->tx_channel, tx_items, number_of_bits_to_read+1, true) == ESP_OK)
{
size_t rx_size = 0;
rmt_item32_t *rx_items = (rmt_item32_t *)xRingbufferReceive(info->rb, &rx_size, 100 / portTICK_PERIOD_MS);
if (rx_items)
{
#ifdef OW_DEBUG
for (int i = 0; i < rx_size / 4; i++)
{
ESP_LOGI(TAG, "level: %d, duration %d", rx_items[i].level0, rx_items[i].duration0);
ESP_LOGI(TAG, "level: %d, duration %d", rx_items[i].level1, rx_items[i].duration1);
}
#endif
if (rx_size >= number_of_bits_to_read * sizeof(rmt_item32_t))
{
for (int i = 0; i < number_of_bits_to_read; i++)
{
read_data >>= 1;
// parse signal and identify logical bit
if (rx_items[i].level1 == 1)
{
if ((rx_items[i].level0 == 0) && (rx_items[i].duration0 < OW_DURATION_SAMPLE))
{
// rising edge occured before 15us -> bit 1
read_data |= 0x80;
}
}
}
read_data >>= 8 - number_of_bits_to_read;
}
vRingbufferReturnItem(info->rb, (void *)rx_items);
}
else
{
// time out occurred, this indicates an unconnected / misconfigured bus
ESP_LOGE(TAG, "rx_items == 0");
res = OWB_STATUS_HW_ERROR;
}
}
else
{
// error in tx channel
ESP_LOGE(TAG, "Error tx");
res = OWB_STATUS_HW_ERROR;
}
rmt_rx_stop(info->rx_channel);
*in = read_data;
return res;
}
static owb_status _uninitialize(const OneWireBus *bus)
{
owb_rmt_driver_info * info = info_of_driver(bus);
rmt_driver_uninstall(info->tx_channel);
rmt_driver_uninstall(info->rx_channel);
return OWB_STATUS_OK;
}
static struct owb_driver rmt_function_table =
{
.name = "owb_rmt",
.uninitialize = _uninitialize,
.reset = _reset,
.write_bits = _write_bits,
.read_bits = _read_bits
};
static owb_status _init(owb_rmt_driver_info *info, gpio_num_t gpio_num,
rmt_channel_t tx_channel, rmt_channel_t rx_channel)
{
owb_status status = OWB_STATUS_HW_ERROR;
// Ensure the RMT peripheral is not already running
// Note: if using RMT elsewhere, don't call this here, call it at the start of your program instead.
//periph_module_disable(PERIPH_RMT_MODULE);
//periph_module_enable(PERIPH_RMT_MODULE);
info->bus.driver = &rmt_function_table;
info->tx_channel = tx_channel;
info->rx_channel = rx_channel;
info->gpio = gpio_num;
#ifdef OW_DEBUG
ESP_LOGI(TAG, "RMT TX channel: %d", info->tx_channel);
ESP_LOGI(TAG, "RMT RX channel: %d", info->rx_channel);
#endif
rmt_config_t rmt_tx = {0};
rmt_tx.channel = info->tx_channel;
rmt_tx.gpio_num = gpio_num;
rmt_tx.mem_block_num = 1;
rmt_tx.clk_div = 80;
rmt_tx.tx_config.loop_en = false;
rmt_tx.tx_config.carrier_en = false;
rmt_tx.tx_config.idle_level = 1;
rmt_tx.tx_config.idle_output_en = true;
rmt_tx.rmt_mode = RMT_MODE_TX;
if (rmt_config(&rmt_tx) == ESP_OK)
{
rmt_set_source_clk(info->tx_channel, RMT_BASECLK_APB); // only APB is supported by IDF 4.2
if (rmt_driver_install(rmt_tx.channel, 0, ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_SHARED) == ESP_OK)
{
rmt_config_t rmt_rx = {0};
rmt_rx.channel = info->rx_channel;
rmt_rx.gpio_num = gpio_num;
rmt_rx.clk_div = 80;
rmt_rx.mem_block_num = 1;
rmt_rx.rmt_mode = RMT_MODE_RX;
rmt_rx.rx_config.filter_en = true;
rmt_rx.rx_config.filter_ticks_thresh = 30;
rmt_rx.rx_config.idle_threshold = OW_DURATION_RX_IDLE;
if (rmt_config(&rmt_rx) == ESP_OK)
{
rmt_set_source_clk(info->rx_channel, RMT_BASECLK_APB); // only APB is supported by IDF 4.2
if (rmt_driver_install(rmt_rx.channel, 512, ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_SHARED) == ESP_OK)
{
rmt_get_ringbuf_handle(info->rx_channel, &info->rb);
status = OWB_STATUS_OK;
}
else
{
ESP_LOGE(TAG, "failed to install rx driver");
}
}
else
{
status = OWB_STATUS_HW_ERROR;
ESP_LOGE(TAG, "failed to configure rx, uninstalling rmt driver on tx channel");
rmt_driver_uninstall(rmt_tx.channel);
}
}
else
{
ESP_LOGE(TAG, "failed to install tx driver");
}
}
else
{
ESP_LOGE(TAG, "failed to configure tx");
}
// attach GPIO to previous pin
if (gpio_num < 32)
{
GPIO.enable_w1ts = (0x1 << gpio_num);
}
else
{
GPIO.enable1_w1ts.data = (0x1 << (gpio_num - 32));
}
// attach RMT channels to new gpio pin
// ATTENTION: set pin for rx first since gpio_output_disable() will
// remove rmt output signal in matrix!
rmt_set_gpio(info->rx_channel, RMT_MODE_RX, gpio_num, false);
rmt_set_gpio(info->tx_channel, RMT_MODE_TX, gpio_num, false);
// force pin direction to input to enable path to RX channel
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[gpio_num]);
// enable open drain
GPIO.pin[gpio_num].pad_driver = 1;
return status;
}
OneWireBus * owb_rmt_initialize(owb_rmt_driver_info * info, gpio_num_t gpio_num,
rmt_channel_t tx_channel, rmt_channel_t rx_channel)
{
ESP_LOGD(TAG, "%s: gpio_num: %d, tx_channel: %d, rx_channel: %d",
__func__, gpio_num, tx_channel, rx_channel);
owb_status status = _init(info, gpio_num, tx_channel, rx_channel);
if (status != OWB_STATUS_OK)
{
ESP_LOGE(TAG, "_init() failed with status %d", status);
}
info->bus.strong_pullup_gpio = GPIO_NUM_NC;
return &(info->bus);
}