From 149c6ce4dc890a501ddd4755020ce7adf64523ec Mon Sep 17 00:00:00 2001 From: David Antliff Date: Sat, 5 Aug 2017 17:37:44 +1200 Subject: [PATCH] Implement solo device optimisations - if there is only one device on the bus, addressing can be simplified. --- README.md | 11 ++-- main/ds18b20.c | 128 +++++++++++++++++++++++++++++++------------- main/ds18b20.h | 17 +++++- main/ds18b20_main.c | 23 ++++---- 4 files changed, 129 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index f9d7738..9ef756a 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,15 @@ It supports multiple devices on the same 1-Wire bus. It is written and tested for the [ESP-IDF](https://github.com/espressif/esp-idf) environment, using the xtensa-esp32-elf toolchain (gcc version 5.2.0). -## Supported Features +## Features + +This library includes: * External power supply mode (parasitic mode not yet supported). * 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 and temperature data. * Programmable temperature measurement resolution (9, 10, 11 or 12-bit resolution). @@ -47,9 +51,8 @@ Parts of this code are based on references provided to the public domain by Maxi The following features are anticipated but not yet implemented: - * Simultaneous temperature conversion from multiple devices. + * Simultaneous temperature conversion from multiple devices on the same bus - concurrency. * Alarm support. * EEPROM support. - * Single device optimisations - avoid ROM addressing when only one device exists. * Parasitic power support. - + * FreeRTOS event-based example. diff --git a/main/ds18b20.c b/main/ds18b20.c index cea3116..cbe8381 100644 --- a/main/ds18b20.c +++ b/main/ds18b20.c @@ -92,6 +92,36 @@ static bool _is_init(const DS18B20_Info * ds18b20_info) return ok; } +static bool _address_device(const DS18B20_Info * ds18b20_info) +{ + bool present = false; + if (_is_init(ds18b20_info)) + { + present = owb_reset(ds18b20_info->bus); + if (present) + { + if (ds18b20_info->solo) + { + // if there's only one device on the bus, we can skip + // sending the ROM code and instruct it directly + owb_write_byte(ds18b20_info->bus, OWB_ROM_SKIP); + } + else + { + // if there are multiple devices on the bus, a Match ROM command + // must be issued to address a specific slave + owb_write_byte(ds18b20_info->bus, OWB_ROM_MATCH); + owb_write_rom_code(ds18b20_info->bus, ds18b20_info->rom_code); + } + } + else + { + ESP_LOGE(TAG, "ds18b20 device not responding"); + } + } + return present; +} + static bool _check_resolution(DS18B20_RESOLUTION resolution) { return (resolution >= DS18B20_RESOLUTION_9_BIT) && (resolution <= DS18B20_RESOLUTION_12_BIT); @@ -125,12 +155,12 @@ static Scratchpad _read_scratchpad(const DS18B20_Info * ds18b20_info, size_t cou count = _min(sizeof(Scratchpad), count); // avoid overflow Scratchpad scratchpad = {0}; ESP_LOGD(TAG, "scratchpad read %d bytes: ", count); - owb_reset(ds18b20_info->bus); - owb_write_byte(ds18b20_info->bus, OWB_ROM_MATCH); - owb_write_rom_code(ds18b20_info->bus, ds18b20_info->rom_code); - owb_write_byte(ds18b20_info->bus, DS18B20_FUNCTION_SCRATCHPAD_READ); - owb_read_bytes(ds18b20_info->bus, (uint8_t *)&scratchpad, count); - esp_log_buffer_hex(TAG, &scratchpad, count); + if (_address_device(ds18b20_info)) + { + owb_write_byte(ds18b20_info->bus, DS18B20_FUNCTION_SCRATCHPAD_READ); + owb_read_bytes(ds18b20_info->bus, (uint8_t *)&scratchpad, count); + esp_log_buffer_hex(TAG, &scratchpad, count); + } return scratchpad; } @@ -141,26 +171,26 @@ static bool _write_scratchpad(const DS18B20_Info * ds18b20_info, const Scratchpa // All three bytes MUST be written before the next reset to avoid corruption. if (_is_init(ds18b20_info)) { - owb_reset(ds18b20_info->bus); - owb_write_byte(ds18b20_info->bus, OWB_ROM_MATCH); - owb_write_rom_code(ds18b20_info->bus, ds18b20_info->rom_code); - owb_write_byte(ds18b20_info->bus, DS18B20_FUNCTION_SCRATCHPAD_WRITE); - owb_write_bytes(ds18b20_info->bus, (uint8_t *)&scratchpad->trigger_high, 3); - ESP_LOGD(TAG, "scratchpad write 3 bytes:"); - esp_log_buffer_hex(TAG, &scratchpad->trigger_high, 3); - result = true; - - if (verify) + if (_address_device(ds18b20_info)) { - Scratchpad read = _read_scratchpad(ds18b20_info, offsetof(Scratchpad, configuration) + 1); - if (memcmp(&scratchpad->trigger_high, &read.trigger_high, 3) != 0) + owb_write_byte(ds18b20_info->bus, DS18B20_FUNCTION_SCRATCHPAD_WRITE); + owb_write_bytes(ds18b20_info->bus, (uint8_t *)&scratchpad->trigger_high, 3); + ESP_LOGD(TAG, "scratchpad write 3 bytes:"); + esp_log_buffer_hex(TAG, &scratchpad->trigger_high, 3); + result = true; + + if (verify) { - ESP_LOGE(TAG, "scratchpad verify failed: " - "wrote {0x%02x, 0x%02x, 0x%02x}, " - "read {0x%02x, 0x%02x, 0x%02x}", - scratchpad->trigger_high, scratchpad->trigger_low, scratchpad->configuration, - read.trigger_high, read.trigger_low, read.configuration); - result = false; + Scratchpad read = _read_scratchpad(ds18b20_info, offsetof(Scratchpad, configuration) + 1); + if (memcmp(&scratchpad->trigger_high, &read.trigger_high, 3) != 0) + { + ESP_LOGE(TAG, "scratchpad verify failed: " + "wrote {0x%02x, 0x%02x, 0x%02x}, " + "read {0x%02x, 0x%02x, 0x%02x}", + scratchpad->trigger_high, scratchpad->trigger_low, scratchpad->configuration, + read.trigger_high, read.trigger_low, read.configuration); + result = false; + } } } } @@ -193,15 +223,45 @@ void ds18b20_free(DS18B20_Info ** ds18b20_info) } } -void ds18b20_init(DS18B20_Info * ds18b20_info, OneWireBus * bus, OneWireBus_ROMCode rom_code) +static void _init(DS18B20_Info * ds18b20_info, OneWireBus * bus) { if (ds18b20_info != NULL) { ds18b20_info->bus = bus; - ds18b20_info->rom_code = rom_code; + memset(&ds18b20_info->rom_code, 0, sizeof(ds18b20_info->rom_code)); ds18b20_info->use_crc = false; ds18b20_info->resolution = DS18B20_RESOLUTION_INVALID; + ds18b20_info->solo = false; // assume multiple devices unless told otherwise ds18b20_info->init = true; + } + else + { + ESP_LOGE(TAG, "ds18b20_info is NULL"); + } +} + +void ds18b20_init(DS18B20_Info * ds18b20_info, OneWireBus * bus, OneWireBus_ROMCode rom_code) +{ + if (ds18b20_info != NULL) + { + _init(ds18b20_info, bus); + ds18b20_info->rom_code = rom_code; + + // read current resolution from device as it may not be power-on or factory default + ds18b20_info->resolution = ds18b20_read_resolution(ds18b20_info); + } + else + { + ESP_LOGE(TAG, "ds18b20_info is NULL"); + } +} + +void ds18b20_init_solo(DS18B20_Info * ds18b20_info, OneWireBus * bus) +{ + if (ds18b20_info != NULL) + { + _init(ds18b20_info, bus); + ds18b20_info->solo = true; // read current resolution from device as it may not be power-on or factory default ds18b20_info->resolution = ds18b20_read_resolution(ds18b20_info); @@ -297,26 +357,22 @@ static void _wait_for_conversion(DS18B20_RESOLUTION resolution) } } -float ds18b20_get_temp(DS18B20_Info * ds18b20_info) +float ds18b20_get_temp(const DS18B20_Info * ds18b20_info) { float temp = 0.0f; if (_is_init(ds18b20_info)) { OneWireBus * bus = ds18b20_info->bus; - if (owb_reset(bus)) + if (_address_device(ds18b20_info)) { - //owb_write_byte(bus, OWB_ROM_SKIP); - owb_write_byte(bus, OWB_ROM_MATCH); - owb_write_rom_code(bus, ds18b20_info->rom_code); + // initiate a temperature measurement owb_write_byte(bus, DS18B20_FUNCTION_TEMP_CONVERT); + // wait at least maximum conversion time _wait_for_conversion(ds18b20_info->resolution); - // reset - owb_reset(bus); - //owb_write_byte(bus, OWB_ROM_SKIP); - owb_write_byte(bus, OWB_ROM_MATCH); - owb_write_rom_code(bus, ds18b20_info->rom_code); + // read measurement + _address_device(ds18b20_info); owb_write_byte(bus, DS18B20_FUNCTION_SCRATCHPAD_READ); uint8_t temp_LSB = 0; diff --git a/main/ds18b20.h b/main/ds18b20.h index 1a77c0d..c3b09e2 100644 --- a/main/ds18b20.h +++ b/main/ds18b20.h @@ -59,6 +59,7 @@ typedef enum typedef struct { bool init; ///< True if struct has been initialised, otherwise false + bool solo; ///< True if device is intended to be the only one connected to the bus, otherwise false bool use_crc; ///< True if CRC checks are to be used when retrieving information from a device on the bus OneWireBus * bus; ///< Pointer to 1-Wire bus information relevant to this device OneWireBus_ROMCode rom_code; ///< The ROM code used to address this device on the bus @@ -87,6 +88,20 @@ void ds18b20_free(DS18B20_Info ** ds18b20_info); */ void ds18b20_init(DS18B20_Info * ds18b20_info, OneWireBus * bus, OneWireBus_ROMCode rom_code); +/** + * @brief Initialise a device info instance with the specified GPIO as a solo device on the bus. + * + * This is subject to the requirement that this device is the ONLY device on the bus. + * This allows for faster commands to be used without ROM code addressing. + * + * NOTE: if additional devices are added to the bus, operation will cease to work correctly. + * + * @param[in] ds18b20_info Pointer to device info instance. + * @param[in] bus Pointer to initialised 1-Wire bus instance. + * @param[in] rom_code Device-specific ROM code to identify a device on the bus. + */ +void ds18b20_init_solo(DS18B20_Info * ds18b20_info, OneWireBus * bus); + /** * @brief Enable or disable use of CRC checks on device communications. * @param[in] ds18b20_info Pointer to device info instance. @@ -125,7 +140,7 @@ OneWireBus_ROMCode ds18b20_read_rom(DS18B20_Info * ds18b20_info); * @param[in] ds18b20_info Pointer to device info instance. Must be initialised first. * @return The current temperature returned by the device, in degrees Celsius. */ -float ds18b20_get_temp(DS18B20_Info * ds18b20_info); +float ds18b20_get_temp(const DS18B20_Info * ds18b20_info); #ifdef __cplusplus diff --git a/main/ds18b20_main.c b/main/ds18b20_main.c index 47c1a23..3d58832 100644 --- a/main/ds18b20_main.c +++ b/main/ds18b20_main.c @@ -36,8 +36,9 @@ #include "ds18b20.h" -#define GPIO_DS18B20_0 (GPIO_NUM_4) -#define MAX_DEVICES (8) +#define GPIO_DS18B20_0 (GPIO_NUM_5) +#define MAX_DEVICES (8) +#define DS18B20_RESOLUTION (DS18B20_RESOLUTION_12_BIT) void app_main() @@ -109,16 +110,20 @@ void app_main() DS18B20_Info * ds18b20_info = ds18b20_malloc(); // heap allocation devices[i] = ds18b20_info; #endif - ds18b20_init(ds18b20_info, owb, device_rom_codes[i]); // associate with bus and device - //ds18b20_init_solo(ds18b20_info, owb); // only one device on bus + if (num_devices == 1) + { + printf("Single device optimisations enabled\n"); + ds18b20_init_solo(ds18b20_info, owb); // only one device on bus + } + else + { + ds18b20_init(ds18b20_info, owb, device_rom_codes[i]); // associate with bus and device + } ds18b20_use_crc(ds18b20_info, true); // enable CRC check for temperature readings -// ds18b20_set_resolution(ds18b20_info, DS18B20_RESOLUTION_9_BIT); -// ds18b20_set_resolution(ds18b20_info, DS18B20_RESOLUTION_10_BIT); -// ds18b20_set_resolution(ds18b20_info, DS18B20_RESOLUTION_11_BIT); - ds18b20_set_resolution(ds18b20_info, DS18B20_RESOLUTION_12_BIT); + ds18b20_set_resolution(ds18b20_info, DS18B20_RESOLUTION); } - // read temperatures from all sensors + // read temperatures from all sensors sequentially while (1) { printf("\nTemperature readings (degrees C):\n");