Browse Source

Add support for setting and retrieving temperature measurement resolution.

main
David Antliff 8 years ago
parent
commit
92a23f8699
  1. 6
      README.md
  2. 154
      main/ds18b20.c
  3. 20
      main/ds18b20.h
  4. 8
      main/ds18b20_main.c

6
README.md

@ -15,7 +15,8 @@ It is written and tested for the [ESP-IDF](https://github.com/espressif/esp-idf)
* 1-Wire device detection and validation, including search for multiple devices on a single bus. * 1-Wire device detection and validation, including search for multiple devices on a single bus.
* 1-Wire bus operations including multi-byte read and write operations. * 1-Wire bus operations including multi-byte read and write operations.
* CRC checks on ROM code and temperature data. * CRC checks on ROM code and temperature data.
* Temperature convertion and retrieval. * Programmable temperature measurement resolution (9, 10, 11 or 12-bit resolution).
* Temperature conversion and retrieval.
## Documentation ## Documentation
@ -47,9 +48,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: The following features are anticipated but not yet implemented:
* Simultaneous temperature conversion from multiple devices. * Simultaneous temperature conversion from multiple devices.
* Device configuration including resolution.
* Alarm support. * Alarm support.
* EEPROM support.
* Single device optimisations - avoid ROM addressing when only one device exists. * Single device optimisations - avoid ROM addressing when only one device exists.
* Parasitic power support. * Parasitic power support.
* EEPROM support.

154
main/ds18b20.c

@ -24,6 +24,11 @@
/** /**
* @file ds18b20.c * @file ds18b20.c
*
* Resolution is cached in the DS18B20_Info object to avoid querying the hardware
* every time a temperature conversion is required. However this can result in the
* cached value becoming inconsistent with the hardware value, so care must be taken.
*
*/ */
#include <stddef.h> #include <stddef.h>
@ -31,6 +36,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <math.h>
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
@ -43,7 +49,7 @@
#include "owb.h" #include "owb.h"
static const char * TAG = "ds18b20"; static const char * TAG = "ds18b20";
static const int T_CONV = 750; // maximum conversion time at 12-bit resolution in milliseconds
// Function commands // Function commands
#define DS18B20_FUNCTION_TEMP_CONVERT 0x44 #define DS18B20_FUNCTION_TEMP_CONVERT 0x44
@ -54,6 +60,16 @@ static const char * TAG = "ds18b20";
#define DS18B20_FUNCTION_POWER_SUPPLY_READ 0xB4 #define DS18B20_FUNCTION_POWER_SUPPLY_READ 0xB4
typedef struct
{
uint8_t temperature[2]; // [0] is LSB, [1] is MSB
uint8_t trigger_high;
uint8_t trigger_low;
uint8_t configuration;
uint8_t reserved[3];
uint8_t crc;
} Scratchpad;
static bool _is_init(const DS18B20_Info * ds18b20_info) static bool _is_init(const DS18B20_Info * ds18b20_info)
{ {
bool ok = false; bool ok = false;
@ -99,6 +115,58 @@ static float _decode_temp(uint8_t lsb, uint8_t msb, DS18B20_RESOLUTION resolutio
return result; return result;
} }
static size_t _min(size_t x, size_t y)
{
return x > y ? y : x;
}
static Scratchpad _read_scratchpad(const DS18B20_Info * ds18b20_info, size_t count)
{
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);
return scratchpad;
}
static bool _write_scratchpad(const DS18B20_Info * ds18b20_info, const Scratchpad * scratchpad, bool verify)
{
bool result = false;
// Only bytes 2, 3 and 4 (trigger and configuration) can be written.
// 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)
{
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;
}
}
}
return result;
}
DS18B20_Info * ds18b20_malloc(void) DS18B20_Info * ds18b20_malloc(void)
{ {
DS18B20_Info * ds18b20_info = malloc(sizeof(*ds18b20_info)); DS18B20_Info * ds18b20_info = malloc(sizeof(*ds18b20_info));
@ -132,8 +200,11 @@ void ds18b20_init(DS18B20_Info * ds18b20_info, OneWireBus * bus, OneWireBus_ROMC
ds18b20_info->bus = bus; ds18b20_info->bus = bus;
ds18b20_info->rom_code = rom_code; ds18b20_info->rom_code = rom_code;
ds18b20_info->use_crc = false; ds18b20_info->use_crc = false;
ds18b20_info->resolution = DS18B20_RESOLUTION_12_BIT; ds18b20_info->resolution = DS18B20_RESOLUTION_INVALID;
ds18b20_info->init = true; ds18b20_info->init = true;
// read current resolution from device as it may not be power-on or factory default
ds18b20_info->resolution = ds18b20_read_resolution(ds18b20_info);
} }
else else
{ {
@ -150,6 +221,82 @@ void ds18b20_use_crc(DS18B20_Info * ds18b20_info, bool use_crc)
} }
} }
bool ds18b20_set_resolution(DS18B20_Info * ds18b20_info, DS18B20_RESOLUTION resolution)
{
bool result = false;
if (_is_init(ds18b20_info))
{
if (_check_resolution(ds18b20_info->resolution))
{
// read scratchpad up to and including configuration register
Scratchpad scratchpad = _read_scratchpad(ds18b20_info,
offsetof(Scratchpad, configuration) - offsetof(Scratchpad, temperature) + 1);
// modify configuration register to set resolution
uint8_t value = (((resolution - 1) & 0x03) << 5) | 0x1f;
scratchpad.configuration = value;
ESP_LOGD(TAG, "configuration value 0x%02x", value);
// write bytes 2, 3 and 4 of scratchpad
result = _write_scratchpad(ds18b20_info, &scratchpad, /* verify */ true);
if (result)
{
ds18b20_info->resolution = resolution;
ESP_LOGI(TAG, "Resolution set to %d bits", (int)resolution);
}
else
{
// Resolution change failed - update the info resolution with the value read from configuration
ds18b20_info->resolution = ds18b20_read_resolution(ds18b20_info);
ESP_LOGW(TAG, "Resolution consistency lost - refreshed from device: %d", ds18b20_info->resolution);
}
}
else
{
ESP_LOGE(TAG, "Unsupported resolution %d", resolution);
}
}
return result;
}
DS18B20_RESOLUTION ds18b20_read_resolution(DS18B20_Info * ds18b20_info)
{
DS18B20_RESOLUTION resolution = DS18B20_RESOLUTION_INVALID;
if (_is_init(ds18b20_info))
{
// read scratchpad up to and including configuration register
Scratchpad scratchpad = _read_scratchpad(ds18b20_info,
offsetof(Scratchpad, configuration) - offsetof(Scratchpad, temperature) + 1);
resolution = ((scratchpad.configuration >> 5) & 0x03) + DS18B20_RESOLUTION_9_BIT;
if (!_check_resolution(resolution))
{
ESP_LOGE(TAG, "invalid resolution read from device: 0x%02x", scratchpad.configuration);
resolution = DS18B20_RESOLUTION_INVALID;
}
else
{
ESP_LOGI(TAG, "Resolution read as %d", resolution);
}
}
return resolution;
}
static void _wait_for_conversion(DS18B20_RESOLUTION resolution)
{
if (_check_resolution(resolution))
{
int divisor = 1 << (DS18B20_RESOLUTION_12_BIT - resolution);
ESP_LOGD(TAG, "divisor %d", divisor);
float max_conversion_time = (float)T_CONV / (float)divisor;
int ticks = ceil(max_conversion_time / portTICK_PERIOD_MS);
ESP_LOGD(TAG, "wait for conversion: %.3f ms, %d ticks", max_conversion_time, ticks);
// wait at least this maximum conversion time
vTaskDelay(ticks);
}
}
float ds18b20_get_temp(DS18B20_Info * ds18b20_info) float ds18b20_get_temp(DS18B20_Info * ds18b20_info)
{ {
float temp = 0.0f; float temp = 0.0f;
@ -162,7 +309,8 @@ float ds18b20_get_temp(DS18B20_Info * ds18b20_info)
owb_write_byte(bus, OWB_ROM_MATCH); owb_write_byte(bus, OWB_ROM_MATCH);
owb_write_rom_code(bus, ds18b20_info->rom_code); owb_write_rom_code(bus, ds18b20_info->rom_code);
owb_write_byte(bus, DS18B20_FUNCTION_TEMP_CONVERT); owb_write_byte(bus, DS18B20_FUNCTION_TEMP_CONVERT);
vTaskDelay(750 / portTICK_PERIOD_MS);
_wait_for_conversion(ds18b20_info->resolution);
// reset // reset
owb_reset(bus); owb_reset(bus);

20
main/ds18b20.h

@ -45,6 +45,7 @@ extern "C" {
*/ */
typedef enum typedef enum
{ {
DS18B20_RESOLUTION_INVALID = -1, ///< Invalid resolution
DS18B20_RESOLUTION_9_BIT = 9, ///< 9-bit resolution, LSB bits 2,1,0 undefined DS18B20_RESOLUTION_9_BIT = 9, ///< 9-bit resolution, LSB bits 2,1,0 undefined
DS18B20_RESOLUTION_10_BIT = 10, ///< 10-bit resolution, LSB bits 1,0 undefined DS18B20_RESOLUTION_10_BIT = 10, ///< 10-bit resolution, LSB bits 1,0 undefined
DS18B20_RESOLUTION_11_BIT = 11, ///< 11-bit resolution, LSB bit 0 undefined DS18B20_RESOLUTION_11_BIT = 11, ///< 11-bit resolution, LSB bit 0 undefined
@ -93,6 +94,25 @@ void ds18b20_init(DS18B20_Info * ds18b20_info, OneWireBus * bus, OneWireBus_ROMC
*/ */
void ds18b20_use_crc(DS18B20_Info * ds18b20_info, bool use_crc); void ds18b20_use_crc(DS18B20_Info * ds18b20_info, bool use_crc);
/**
* @brief Set temperature measurement resolution.
*
* This programs the hardware to the specified resolution and sets the cached value to be the same.
* If the program fails, the value currently in hardware is used to refresh the cache.
*
* @param[in] ds18b20_info Pointer to device info instance.
* @param[in] resolution Selected resolution.
* @return True if successful, otherwise false.
*/
bool ds18b20_set_resolution(DS18B20_Info * ds18b20_info, DS18B20_RESOLUTION resolution);
/**
* @brief Update and return the current temperature measurement resolution from the device.
* @param[in] ds18b20_info Pointer to device info instance.
* @return The currently configured temperature measurement resolution.
*/
DS18B20_RESOLUTION ds18b20_read_resolution(DS18B20_Info * ds18b20_info);
/** /**
* @brief Read 64-bit ROM code from device - only works when there is a single device on the bus. * @brief Read 64-bit ROM code from device - only works when there is a single device on the bus.
* @param[in] ds18b20_info Pointer to device info instance. * @param[in] ds18b20_info Pointer to device info instance.

8
main/ds18b20_main.c

@ -42,7 +42,7 @@
void app_main() void app_main()
{ {
esp_log_level_set("*", ESP_LOG_DEBUG); esp_log_level_set("*", ESP_LOG_INFO);
// Create a 1-Wire bus // Create a 1-Wire bus
#ifdef USE_STATIC #ifdef USE_STATIC
@ -112,6 +112,10 @@ void app_main()
ds18b20_init(ds18b20_info, owb, device_rom_codes[i]); // associate with bus and device 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 //ds18b20_init_solo(ds18b20_info, owb); // only one device on bus
ds18b20_use_crc(ds18b20_info, true); // enable CRC check for temperature readings 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);
} }
// read temperatures from all sensors // read temperatures from all sensors
@ -121,7 +125,7 @@ void app_main()
for (int i = 0; i < num_devices; ++i) for (int i = 0; i < num_devices; ++i)
{ {
float temp = ds18b20_get_temp(devices[i]); float temp = ds18b20_get_temp(devices[i]);
printf(" %d: %.2f\n", i, temp); printf(" %d: %.3f\n", i, temp);
} }
vTaskDelay(1000 / portTICK_PERIOD_MS); vTaskDelay(1000 / portTICK_PERIOD_MS);
} }

Loading…
Cancel
Save