From 1b4e7c14e73e986fa95bf6f62dfc2d7703ce8af8 Mon Sep 17 00:00:00 2001 From: mikaelpatel Date: Sun, 8 Oct 2017 15:04:02 +0200 Subject: [PATCH] Adding support for hardware (DS2482) based OWI bus manager --- README.md | 18 +- examples/Alarm/Alarm.ino | 25 +- examples/DS18B20/DS18B20.ino | 26 +- examples/DS2482/DS2482.ino | 122 ++++++++++ examples/Scanner/Scanner.ino | 22 +- examples/Search/Search.ino | 33 ++- library.properties | 4 +- mainpage.dox | 5 +- src/Driver/DS18B20.h | 57 +++-- src/Hardware/OWI.h | 449 +++++++++++++++++++++++++++++++++++ src/OWI.h | 68 +++--- src/Software/OWI.h | 2 +- 12 files changed, 741 insertions(+), 90 deletions(-) create mode 100644 examples/DS2482/DS2482.ino create mode 100644 src/Hardware/OWI.h diff --git a/README.md b/README.md index 4224429..16304d3 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,32 @@ # Arduino-OWI The OWI library has been developed to support the implementation of -1-wire device drivers. The library includes an example device driver -and a bus scanner. +1-wire bus managers and device drivers. The library includes a GPIO +based software and DS2482 based hardware bus manager, and device +driver for DS18B20. -Version: 1.5 +The examples directory contains device search, bus scanner, +thermometer alarm search, and example sketches for DS18B20, DS1990A +and DS2482. + +Version: 1.6 ## Classes * [Abstract One-Wire Bus Manager and Device Interface, OWI](./src/OWI.h) -* [Software One-Wire Interface, Software::OWI](./src/Software/OWI.h) +* [Software One-Wire Bus Manager, GPIO, Software::OWI](./src/Software/OWI.h) +* [Hardware One-Wire Bus Manager, DS2482, Hardware::OWI](./src/Hardware/OWI.h) * [Programmable Resolution 1-Wire Digital Thermometer, DS18B20](./src/Driver/DS18B20.h) ## Example Sketches * [DS18B20](./examples/DS18B20) * [DS1990A](./examples/DS1990A) +* [DS2482](./examples/DS2482) +* [Alarm](./examples/Alarm) * [Scanner](./examples/Scanner) * [Search](./examples/Search) -* [Alarm](./examples/Alarm) ## Dependencies * [Arduino-GPIO](https://github.com/mikaelpatel/Arduino-GPIO) +* [Arduino-TWI](https://github.com/mikaelpatel/Arduino-TWI) diff --git a/examples/Alarm/Alarm.ino b/examples/Alarm/Alarm.ino index 8148914..e80c5fc 100644 --- a/examples/Alarm/Alarm.ino +++ b/examples/Alarm/Alarm.ino @@ -1,14 +1,25 @@ #include "GPIO.h" #include "OWI.h" -#include "Software/OWI.h" #include "Driver/DS18B20.h" -#if defined(ARDUINO_attiny) -#include "Software/Serial.h" -Software::Serial Serial; -Software::OWI owi; -#else +// Configure: Software/Hardware OWI Bus Manager +#define USE_SOFTWARE_OWI +#if defined(USE_SOFTWARE_OWI) +#include "Software/OWI.h" Software::OWI owi; + +#else +#include "Hardware/OWI.h" +// Configure: Software/Hardware TWI Bus Manager +// #define USE_SOFTWARE_TWI +#if defined(USE_SOFTWARE_TWI) +#include "Software/TWI.h" +Software::TWI twi; +#else +#include "Hardware/TWI.h" +Hardware::TWI twi; +#endif +Hardware::OWI owi(twi); #endif DS18B20 sensor(owi); @@ -50,7 +61,7 @@ void loop() bool triggered = false; static uint32_t timestamp = 0; if (!sensor.convert_request(true)) return; - delay(1000); + delay(sensor.conversion_time()); do { last = owi.alarm_search(rom, last); if (last == owi.ERROR) break; diff --git a/examples/DS18B20/DS18B20.ino b/examples/DS18B20/DS18B20.ino index c5c04b9..c95ca7e 100644 --- a/examples/DS18B20/DS18B20.ino +++ b/examples/DS18B20/DS18B20.ino @@ -1,14 +1,25 @@ #include "GPIO.h" #include "OWI.h" -#include "Software/OWI.h" #include "Driver/DS18B20.h" -#if defined(ARDUINO_attiny) -#include "Software/Serial.h" -Software::Serial Serial; -Software::OWI owi; -#else +// Configure: Software/Hardware OWI Bus Manager +#define USE_SOFTWARE_OWI +#if defined(USE_SOFTWARE_OWI) +#include "Software/OWI.h" Software::OWI owi; + +#else +#include "Hardware/OWI.h" +// Configure: Software/Hardware TWI Bus Manager +// #define USE_SOFTWARE_TWI +#if defined(USE_SOFTWARE_TWI) +#include "Software/TWI.h" +Software::TWI twi; +#else +#include "Hardware/TWI.h" +Hardware::TWI twi; +#endif +Hardware::OWI owi(twi); #endif DS18B20 sensor(owi); @@ -25,6 +36,7 @@ void loop() // Print list of sensors, rom code, and temperature if (!sensor.convert_request(true)) return; + delay(sensor.conversion_time()); int8_t last = owi.FIRST; uint8_t* rom = sensor.rom(); @@ -79,5 +91,5 @@ void loop() } while (last != owi.LAST); Serial.println(); - delay(5000); + delay(4000); } diff --git a/examples/DS2482/DS2482.ino b/examples/DS2482/DS2482.ino new file mode 100644 index 0000000..83271a0 --- /dev/null +++ b/examples/DS2482/DS2482.ino @@ -0,0 +1,122 @@ +#include "GPIO.h" +#include "OWI.h" +#include "TWI.h" +#include "Hardware/OWI.h" +#include + +#define USE_SOFTWARE_TWI +#if defined(USE_SOFTWARE_TWI) +#include "Software/TWI.h" +Software::TWI twi; +#else +#include "Hardware/TWI.h" +Hardware::TWI twi; +#endif + +Hardware::OWI owi(twi); + +// DS18B20 Commands +enum { + CONVERT_T = 0x44, + READ_SCRATCHPAD = 0xbe +}; + +// DS18B20 Scratchpad Memory +struct scratchpad_t { + int16_t temperature; + int8_t high_trigger; + int8_t low_trigger; + uint8_t configuration; + uint8_t reserved[3]; + uint8_t crc; +}; + +#define TRACE(expr) \ + do { \ + Serial.print(#expr "="); \ + Serial.println(expr); \ + } while (0) + +void setup() +{ + Serial.begin(57600); + while (!Serial); + + TRACE(owi.device_reset()); + TRACE(owi.device_config()); + TRACE(owi.set_read_pointer(owi.READ_DATA_REGISTER)); + TRACE(owi.set_read_pointer(owi.CONFIGURATION_REGISTER)); + TRACE(owi.set_read_pointer(owi.CHANNEL_SELECTION_REGISTER)); + TRACE(owi.set_read_pointer(owi.STATUS_REGISTER)); + + uint8_t rom[owi.ROM_MAX] = { 0 }; + uint8_t crc = 0; + TRACE(owi.reset()); + Serial.print(F("rom=")); + owi.write(owi.READ_ROM); + for (size_t i = 0; i < sizeof(rom); i++) { + rom[i] = owi.read(8); + if (rom[i] < 0x10) Serial.print(0); + Serial.print(rom[i], HEX); + crc = _crc_ibutton_update(crc, rom[i]); + } + Serial.print(F(", crc=")); + Serial.println(crc); + + uint8_t value = 0; + uint8_t bits = 0; + uint8_t ix = 0; + uint8_t res = 0; + uint8_t dir = 0; + bool id; + bool nid; + crc = 0; + TRACE(owi.reset()); + Serial.print(F("rom=")); + owi.write(owi.SEARCH_ROM); + do { + res = owi.triplet(dir); + id = (res & 1) != 0; + nid = (res & 2) != 0; + value = (value >> 1); + if (dir) value |= 0x80; + bits += 1; + if (bits == CHARBITS) { + rom[ix] = value; + if (rom[ix] < 0x10) Serial.print(0); + Serial.print(rom[ix], HEX); + crc = _crc_ibutton_update(crc, rom[ix]); + ix += 1; + bits = 0; + value = 0; + } + } while (id != nid); + Serial.print(F(", crc=")); + Serial.println(crc); +} + +void loop() +{ + owi.reset(); + owi.write(owi.SKIP_ROM); + owi.write(CONVERT_T); + delay(750); + + owi.reset(); + owi.write(owi.SKIP_ROM); + owi.write(READ_SCRATCHPAD); + + scratchpad_t scratchpad; + uint8_t* p = (uint8_t*) &scratchpad; + uint8_t crc = 0; + for (size_t i = 0; i < sizeof(scratchpad); i++) { + p[i] = owi.read(); + crc = _crc_ibutton_update(crc, p[i]); + } + if (crc == 0) { + float temperature = scratchpad.temperature * 0.0625; + TRACE(temperature); + } else + TRACE(crc); + delay(2000); +} diff --git a/examples/Scanner/Scanner.ino b/examples/Scanner/Scanner.ino index ff265a0..b98fd23 100644 --- a/examples/Scanner/Scanner.ino +++ b/examples/Scanner/Scanner.ino @@ -1,13 +1,24 @@ #include "OWI.h" #include "GPIO.h" + +// Configure: Software/Hardware OWI Bus Manager +#define USE_SOFTWARE_OWI +#if defined(USE_SOFTWARE_OWI) #include "Software/OWI.h" +Software::OWI owi; -#if defined(ARDUINO_attiny) -#include "Software/Serial.h" -Software::Serial Serial; -Software::OWI owi; #else -Software::OWI owi; +#include "Hardware/OWI.h" +// Configure: Software/Hardware TWI Bus Manager +// #define USE_SOFTWARE_TWI +#if defined(USE_SOFTWARE_TWI) +#include "Software/TWI.h" +Software::TWI twi; +#else +#include "Hardware/TWI.h" +Hardware::TWI twi; +#endif +Hardware::OWI owi(twi); #endif void setup() @@ -23,7 +34,6 @@ void loop() uint8_t rom[owi.ROM_MAX] = { 0 }; int8_t last = owi.FIRST; int id = 0; - size_t i; do { last = owi.search_rom(0, rom, last); diff --git a/examples/Search/Search.ino b/examples/Search/Search.ino index 25dc6f3..33ea1cc 100644 --- a/examples/Search/Search.ino +++ b/examples/Search/Search.ino @@ -1,13 +1,24 @@ #include "OWI.h" #include "GPIO.h" + +// Configure: Software/Hardware OWI Bus Manager +#define USE_SOFTWARE_OWI +#if defined(USE_SOFTWARE_OWI) #include "Software/OWI.h" +Software::OWI owi; -#if defined(ARDUINO_attiny) -#include "Software/Serial.h" -Software::Serial Serial; -Software::OWI owi; #else -Software::OWI owi; +#include "Hardware/OWI.h" +// Configure: Software/Hardware TWI Bus Manager +// #define USE_SOFTWARE_TWI +#if defined(USE_SOFTWARE_TWI) +#include "Software/TWI.h" +Software::TWI twi; +#else +#include "Hardware/TWI.h" +Hardware::TWI twi; +#endif +Hardware::OWI owi(twi); #endif void setup() @@ -26,14 +37,16 @@ void loop() do { last = owi.search_rom(0, rom, last); if (last == owi.ERROR) break; - int pos = Serial.print(i++); - pos += Serial.print(':'); + Serial.print(i++); + Serial.print(':'); + int8_t pos = 0; for (size_t i = 0; i < sizeof(rom); i++) - for (uint8_t mask = 0x80; mask != 0; mask >>= 1) + for (uint8_t mask = 0x80; mask != 0; mask >>= 1, pos++) { Serial.print((rom[i] & mask) != 0); + if (pos == last) Serial.print('*'); + } + if (pos == last) Serial.print('*'); Serial.println(); - for (int i = 0; i < last - 1 + pos; i++) Serial.print('-'); - Serial.println('*'); } while (last != owi.LAST); Serial.println(); diff --git a/library.properties b/library.properties index ddc8a9d..4515910 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=Arduino-OWI -version=1.5 +version=1.6 author=Mikael Patel maintainer=Mikael Patel sentence=One-Wire Interface (OWI) library for Arduino. -paragraph=The OWI library has been developed to support the implementation of 1-wire device drivers. Includes abstract OWI bus manager class, GPIO based Software::OWI bus manager and device driver for DS18B20. +paragraph=The OWI library has been developed to support the implementation of 1-wire device drivers. Includes abstract OWI bus manager class, GPIO based Software::OWI bus manager, DS2482 based Hardware::OWI bus manager, and device driver for DS18B20. category=Communication url=https://github.com/mikaelpatel/Arduino-OWI architectures=avr,sam diff --git a/mainpage.dox b/mainpage.dox index 39a480a..45f3cfc 100644 --- a/mainpage.dox +++ b/mainpage.dox @@ -2,9 +2,10 @@ The OWI library has been developed to support the implementation of 1-wire device drivers. Includes abstract OWI bus manager class, GPIO -based Software::OWI bus manager and device driver for DS18B20. +based Software::OWI bus manager, DS2482 based Hardware::OWI bus +manager, and device driver for DS18B20. -Version: 1.5 +Version: 1.6 */ /** @page License diff --git a/src/Driver/DS18B20.h b/src/Driver/DS18B20.h index bb9808b..0243f37 100644 --- a/src/Driver/DS18B20.h +++ b/src/Driver/DS18B20.h @@ -1,6 +1,6 @@ /** * @file Driver/DS18B20.h - * @version 1.0 + * @version 1.1 * * @section License * Copyright (C) 2017, Mikael Patel @@ -48,6 +48,8 @@ class DS18B20 : public OWI::Device { /** * Construct a DS18B20 device connected to the given 1-Wire bus. + * Initiate with default resolution (12-bits) and triggers (70, 75), + * as hardware reset for device. * @param[in] owi bus manager. * @param[in] rom code (default NULL). */ @@ -55,7 +57,10 @@ class DS18B20 : public OWI::Device { OWI::Device(owi, rom), m_start(0), m_converting(false) - {} + { + resolution(12); + set_trigger(70, 75); + } /** * Set conversion resolution from 9..12 bits. Use write_scratchpad() @@ -81,9 +86,9 @@ class DS18B20 : public OWI::Device { } /** - * Get the latest temperature reading from the local memory scratchpad. - * Call convert_request() and read_scratchpad() before accessing the - * scratchpad. + * Get the latest temperature reading from the scratchpad copy. + * Call convert_request(), convert_await() and read_scratchpad() + * before accessing the scratchpad. * @return temperature */ float temperature() const @@ -92,8 +97,7 @@ class DS18B20 : public OWI::Device { } /** - * Get conversion resolution. Use connect(), or read_scratchpad() to - * read values from device before calling this method. + * Get conversion resolution. * @return number of bits. */ uint8_t resolution() const @@ -103,7 +107,6 @@ class DS18B20 : public OWI::Device { /** * Get alarm trigger values; low and high threshold values. - * Use connect(), or read_scratchpad() to read values from device. * @param[out] low threshold. * @param[out] high threshold. */ @@ -115,7 +118,7 @@ class DS18B20 : public OWI::Device { /** * Initiate temperature conversion. Call with broadcast parameter - * true(1) to issue skip_rom(). + * true(1) to issue skip_rom() and issue to command to all devices. * @param[in] broadcast flag (default false). * @return true(1) if successful otherwise false(0). */ @@ -133,6 +136,30 @@ class DS18B20 : public OWI::Device { return (true); } + /** + * Return remaining convertion time in milliseconds. + * @return milliseconds remaining. + */ + uint16_t conversion_time() + { + if (!m_converting) return (0); + uint16_t ms = millis() - m_start; + uint16_t conv_ms = (MAX_CONVERSION_TIME >> (12 - resolution())); + if (conv_ms > ms) return (conv_ms - ms); + return (0); + } + + /** + * Delay until the temperature conversion is completed. + * @return true(1) if successful otherwise false(0). + */ + bool convert_await() + { + if (!m_converting) return (false); + delay(conversion_time()); + return (true); + } + /** * Read the contents of the scratchpad to local memory. An internal * delay will occur if a convert_request() is pending. The delay is @@ -143,17 +170,7 @@ class DS18B20 : public OWI::Device { */ bool read_scratchpad(bool match = true) { - // Check if a conversion is in progress - if (m_converting) { - uint16_t ms = millis() - m_start; - uint16_t conv_time = (MAX_CONVERSION_TIME >> (12 - resolution())); - // May need to wait for the conversion to complete - if (ms < conv_time) { - ms = conv_time - ms; - delay(ms); - } - m_converting = false; - } + convert_await(); if (match && !m_owi.match_rom(m_rom)) return (false); m_owi.write(READ_SCRATCHPAD); return (m_owi.read(&m_scratchpad, sizeof(m_scratchpad))); diff --git a/src/Hardware/OWI.h b/src/Hardware/OWI.h new file mode 100644 index 0000000..5cd589b --- /dev/null +++ b/src/Hardware/OWI.h @@ -0,0 +1,449 @@ +/** + * @file Hardware/OWI.h + * @version 1.0 + * + * @section License + * Copyright (C) 2017, Mikael Patel + * + * This library is free hardware; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Hardware Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#ifndef HARDWARE_OWI_H +#define HARDWARE_OWI_H + +#include "OWI.h" +#include "TWI.h" + +/** + * One Wire Interface (OWI) Bus Manager class using DS2482, + * Single-Channel 1-Wire Master, TWI to OWI Bridge Device. + */ +namespace Hardware { +class OWI : public ::OWI, protected TWI::Device { +public: + /** + * Construct one wire bus manager for DS2482. + * @param[in] twi bus manager. + * @param[in] subaddr sub-address for device. + */ + OWI(TWI& twi, uint8_t subaddr = 0) : + TWI::Device(twi, 0x18 | (subaddr & 0x03)) + { + } + + /** + * @override{OWI} + * Reset the one wire bus and check that at least one device is + * presence. + * @return true(1) if successful otherwise false(0). + */ + virtual bool reset() + { + status_t status; + uint8_t cmd; + int count; + + // Issue one wire reset command + cmd = ONE_WIRE_RESET; + if (!TWI::Device::acquire()) return (false); + count = TWI::Device::write(&cmd, sizeof(cmd)); + if (count != sizeof(cmd)) goto error; + + // Wait for one wire operation to complete + for (int i = 0; i < POLL_MAX; i++) { + count = TWI::Device::read(&status, sizeof(status)); + if (count == sizeof(status) && !status.IWB) break; + } + if (!TWI::Device::release()) return (false); + return ((count == sizeof(status)) && status.PPD); + + error: + TWI::Device::release(); + return (false); + } + + /** + * @override{OWI} + * Read the given number of bits from the one wire bus. Default + * number of bits is 8. Calculate partial check-sum. + * @param[in] bits to be read. + * @return value read. + */ + virtual uint8_t read(uint8_t bits = CHARBITS) + { + status_t status; + uint8_t cmd; + int count; + + // Check for bit read + if (bits != CHARBITS) { + uint8_t adjust = CHARBITS - bits; + uint8_t res = 0; + while (bits--) { + res >>= 1; + if (read_bit()) res |= 0x80; + } + res >>= adjust; + return (res); + } + + // Issue one wire read byte command + cmd = ONE_WIRE_READ_BYTE; + if (!TWI::Device::acquire()) return (false); + count = TWI::Device::write(&cmd, sizeof(cmd)); + if (count != sizeof(cmd)) goto error; + + // Wait for one wire operation to complete + for (int i = 0; i < POLL_MAX; i++) { + count = TWI::Device::read(&status, sizeof(status)); + if (count == sizeof(status) && !status.IWB) break; + } + if (!TWI::Device::release()) return (false); + if ((count != sizeof(status)) || status.IWB) return (0); + + // Read data register value + return (set_read_pointer(READ_DATA_REGISTER)); + + error: + TWI::Device::release(); + return (0); + } + + /** + * @override{OWI} + * Write the given value to the one wire bus. The bits are written + * from LSB to MSB. + * @param[in] value to write. + * @param[in] bits to be written. + */ + virtual void write(uint8_t value, uint8_t bits = CHARBITS) + { + status_t status; + uint8_t cmd[2]; + int count; + + // Issue one wire write byte command with given data + if (bits != CHARBITS) { + while (bits--) { + write_bit(value & 0x01); + value >>= 1; + } + return; + } + + cmd[0] = ONE_WIRE_WRITE_BYTE; + cmd[1] = value; + if (!TWI::Device::acquire()) return; + count = TWI::Device::write(cmd, sizeof(cmd)); + if (count != sizeof(cmd)) goto error; + + // Wait for one wire operation to complete + for (int i = 0; i < POLL_MAX; i++) { + count = TWI::Device::read(&status, sizeof(status)); + if (count == sizeof(status) && !status.IWB) break; + } + + error: + TWI::Device::release(); + } + + /** + * @override{OWI} + * Search (rom and alarm) support function. Reads 2-bits and writes + * given direction 1-bit value when discrepancy 0b00 read. Writes + * one(1) when 0b01 read, zero(0) on 0b10. Reading 0b11 is an error + * state. + * @param[in,out] dir bit to write when discrepancy read. + * @return 2-bits read and bit written. + */ + virtual int8_t triplet(uint8_t& dir) + { + status_t status; + uint8_t cmd[2]; + int count; + + // Issue one wire single bit command with given data + cmd[0] = ONE_WIRE_TRIPLET; + cmd[1] = (dir ? 0x80 : 0x00); + if (!TWI::Device::acquire()) return (ERROR); + count = TWI::Device::write(cmd, sizeof(cmd)); + if (count != sizeof(cmd)) goto error; + + // Wait for one wire operation to complete + for (int i = 0; i < POLL_MAX; i++) { + count = TWI::Device::read(&status, sizeof(status)); + if (count == sizeof(status) && !status.IWB) break; + } + if (!TWI::Device::release()) return (ERROR); + if (count != sizeof(status) && status.IWB) return (ERROR); + dir = status.DIR; + return ((status >> 5) & 0x3); + + error: + TWI::Device::release(); + return (ERROR); + } + + /** + * Global reset of device state machine logic. Returns true if + * successful otherwise false. + * @return bool. + */ + bool device_reset() + { + status_t status; + uint8_t cmd; + int count; + + // Issue device reset command + cmd = DEVICE_RESET; + if (!TWI::Device::acquire()) return (false); + count = TWI::Device::write(&cmd, sizeof(cmd)); + if (count != sizeof(cmd)) goto error; + + // Check status register for device reset + count = TWI::Device::read(&status, sizeof(status)); + if (!TWI::Device::release()) return (false); + return ((count == sizeof(status)) && status.RST); + + error: + TWI::Device::release(); + return (false); + } + + /** + * Configure one wire bus master with given parameters. Returns true + * if successful otherwise false. + * @param[in] apu active pull-up (default true). + * @param[in] spu strong pull-up (default false). + * @param[in] iws one wire speed (default false). + * @return bool. + */ + bool device_config(bool apu = true, bool spu = false, bool iws = false) + { + config_t config; + status_t status; + uint8_t cmd[2]; + int count; + + // Set configuration bit-fields + config.APU = apu; + config.SPU = spu; + config.IWS = iws; + config.COMP = ~config; + + // Issue write configuration command with given setting + cmd[0] = WRITE_CONGIFURATION; + cmd[1] = config; + if (!TWI::Device::acquire()) return (false); + count = TWI::Device::write(cmd, sizeof(cmd)); + if (count != sizeof(cmd)) goto error; + + // Read status and check configuration + count = TWI::Device::read(&status, sizeof(status)); + if (!TWI::Device::release()) return (false); + return ((count == sizeof(status)) && !status.RST); + + error: + TWI::Device::release(); + return (false); + } + + /** + * Device Registers, pp. 5. Valid Pointer Codes, pp. 10. + */ + enum Register { + STATUS_REGISTER = 0xf0, + READ_DATA_REGISTER = 0xe1, + CHANNEL_SELECTION_REGISTER = 0xd2, + CONFIGURATION_REGISTER = 0xc3 + } __attribute__((packed)); + + /** + * Set the read pointer to the specified register. Return register + * value or negative error code. + * @param[in] addr register address. + * @return register value or negative error code. + */ + int set_read_pointer(Register addr) + { + uint8_t cmd[2]; + uint8_t reg; + int count; + + // Issue set read pointer command with given pointer + cmd[0] = SET_READ_POINTER; + cmd[1] = (uint8_t) addr; + if (!TWI::Device::acquire()) return (-1); + count = TWI::Device::write(cmd, sizeof(cmd)); + if (count != sizeof(cmd)) goto error; + + // Read register value + count = TWI::Device::read(®, sizeof(reg)); + if (!TWI::Device::release()) return (false); + return ((count == sizeof(reg)) ? reg : -1); + + error: + TWI::Device::release(); + return (-1); + } + + /** + * Select given channel (DS2482-800). Return true if successful + * otherwise false. + * @param[in] chan channel number (0..7). + * @return bool. + */ + bool channel_select(uint8_t chan) + { + uint8_t cmd[2]; + int count; + + // Check channel number + if (chan > 7) return (false); + + // Issue channel select command with channel code + cmd[0] = CHANNEL_SELECT; + cmd[1] = (~chan << 4) | chan; + if (!TWI::Device::acquire()) return (false); + count = TWI::Device::write(cmd, sizeof(cmd)); + if (!TWI::Device::release()) return (false); + return (count == sizeof(cmd)); + } + +protected: + /** + * Function Commands, pp. 9-15. + */ + enum { + DEVICE_RESET = 0xf0, //!< Device Reset. + SET_READ_POINTER = 0xe1, //!< Set Read Pointer. + WRITE_CONGIFURATION = 0xd2, //!< Write Configuration. + CHANNEL_SELECT = 0xc3, //!< Channel Select. + ONE_WIRE_RESET = 0xb4, //!< 1-Wire Reset. + ONE_WIRE_SINGLE_BIT = 0x87, //!< 1-Wire Single Bit. + ONE_WIRE_WRITE_BYTE = 0xa5, //!< 1-Wire Write Byte. + ONE_WIRE_READ_BYTE = 0x96, //!< 1-Wire Read Byte. + ONE_WIRE_TRIPLET = 0x78 //!< 1-Wire Triplet. + } __attribute__((packed)); + + /** + * Status Register, bit-fields, pp. 8-9. + */ + union status_t { + uint8_t as_uint8; //!< Unsigned byte access. + struct { //!< Bitfield access (little endian). + uint8_t IWB:1; //!< 1-Wire Busy. + uint8_t PPD:1; //!< Presence-Pulse Detect. + uint8_t SD:1; //!< Short Detected. + uint8_t LL:1; //!< Logic Level. + uint8_t RST:1; //!< Device Reset. + uint8_t SBR:1; //!< Single Bit Result. + uint8_t TSB:1; //!< Triplet Second Bit. + uint8_t DIR:1; //!< Branch Direction Taken. + }; + operator uint8_t() + { + return (as_uint8); + } + }; + + /** + * Configuration Register, bit-fields, pp. 5-6. + */ + union config_t { + uint8_t as_uint8; //!< Unsigned byte access. + struct { //!< Bitfield access (little endian). + uint8_t APU:1; //!< Active Pullup. + uint8_t ZERO:1; //!< Always Zero(0). + uint8_t SPU:1; //!< Strong Pullup. + uint8_t IWS:1; //!< 1-Wire Speed. + uint8_t COMP:4; //!< Complement of lower 4-bits. + }; + operator uint8_t() + { + return (as_uint8); + } + config_t() + { + as_uint8 = 0; + } + }; + + /** Number of one-wire polls */ + static const int POLL_MAX = 20; + + /** + * Read a single bit from one wire bus. Returns bit value (0 or 1) + * or a negative error code. + * @return bit read. + */ + bool read_bit() + { + status_t status; + uint8_t cmd[2]; + int count; + + // Issue one wire single bit command with read data time slot + cmd[0] = ONE_WIRE_SINGLE_BIT; + cmd[1] = 0x80; + if (!TWI::Device::acquire()) return (false); + count = TWI::Device::write(cmd, sizeof(cmd)); + if (count != sizeof(cmd)) goto error; + + // Wait for one wire operation to complete + for (int i = 0; i < POLL_MAX; i++) { + count = TWI::Device::read(&status, sizeof(status)); + if (count == sizeof(status) && !status.IWB) break; + } + if (!TWI::Device::release()) return (false); + if (count != sizeof(status) || status.IWB) return (false); + return (status.SBR); + + error: + TWI::Device::release(); + return (false); + } + + /** + * Write a single bit to one wire bus. Returns true if successful + * otherwise false. + * @param[in] value bit to write. + * @return bool. + */ + bool write_bit(bool value) + { + status_t status; + uint8_t cmd[2]; + int count; + + // Issue one wire single bit command with given data + cmd[0] = ONE_WIRE_SINGLE_BIT; + cmd[1] = (value ? 0x80 : 0x00); + if (!TWI::Device::acquire()) return (false); + count = TWI::Device::write(cmd, sizeof(cmd)); + if (count != sizeof(cmd)) goto error; + + // Wait for one wire operation to complete + for (int i = 0; i < POLL_MAX; i++) { + count = TWI::Device::read(&status, sizeof(status)); + if (count == sizeof(status) && !status.IWB) break; + } + if (!TWI::Device::release()) return (false); + return ((count == sizeof(status)) && !status.IWB); + + error: + TWI::Device::release(); + return (false); + } +}; +}; +#endif diff --git a/src/OWI.h b/src/OWI.h index 58a5f9c..615ec4e 100644 --- a/src/OWI.h +++ b/src/OWI.h @@ -1,6 +1,6 @@ /** * @file OWI.h - * @version 1.1 + * @version 1.2 * * @section License * Copyright (C) 2017, Mikael Patel @@ -89,6 +89,34 @@ class OWI { while (count--) write(*bp++); } + /** + * @override{OWI} + * Search (rom and alarm) support function. Reads 2-bits and writes + * given direction 1-bit value when discrepancy 0b00 read. Writes + * one(1) when 0b01 read, zero(0) on 0b10. Reading 0b11 is an error + * state. + * @param[in,out] dir bit to write when discrepancy read. + * @return 2-bits read and bit written. + */ + virtual int8_t triplet(uint8_t& dir) + { + switch (read(2)) { + case 0b00: + write(dir, 1); + return (0b00); + case 0b01: + write(1, 1); + dir = 1; + return (0b01); + case 0b10: + write(0, 1); + dir = 0; + return (0b10); + default: + return (0b11); + } + } + /** Search position and return values. */ enum { FIRST = -1, //!< Start position of search. @@ -218,7 +246,6 @@ class OWI { uint8_t m_rom[ROM_MAX]; }; -protected: /** * Standard ROM Commands. */ @@ -230,6 +257,7 @@ class OWI { ALARM_SEARCH = 0xEC //!< Initiate device alarm search. } __attribute__((packed)); +protected: /** Maximum number of reset retries. */ static const uint8_t RESET_RETRY_MAX = 4; @@ -250,37 +278,17 @@ class OWI { for (uint8_t i = 0; i < 8; i++) { uint8_t data = 0; for (uint8_t j = 0; j < 8; j++) { - data >>= 1; - switch (read(2)) { - case 0b00: // Discrepency between device roms - if (pos == last) { - write(1, 1); - data |= 0x80; - last = FIRST; - } - else if (pos > last) { - write(0, 1); - next = pos; - } - else if (code[i] & (1 << j)) { - write(1, 1); - data |= 0x80; - } - else { - write(0, 1); - next = pos; - } + uint8_t dir = (pos == last) || ((pos < last) && (code[i] & (1 << j))); + switch (triplet(dir)) { + case 0b00: + if (pos == last) last = FIRST; + else if (pos > last || (code[i] & (1 << j)) == 0) next = pos; break; - case 0b01: // Only one's at this position - write(1, 1); - data |= 0x80; - break; - case 0b10: // Only zero's at this position - write(0, 1); - break; - case 0b11: // No device detected + case 0b11: return (ERROR); } + data >>= 1; + if (dir) data |= 0x80; pos += 1; } code[i] = data; diff --git a/src/Software/OWI.h b/src/Software/OWI.h index 6371781..b4ffe94 100644 --- a/src/Software/OWI.h +++ b/src/Software/OWI.h @@ -23,7 +23,7 @@ #include "GPIO.h" /** - * One Wire Interface (OWI) template class using GPIO. + * One Wire Interface (OWI) Bus Manager template class using GPIO. * @param[in] PIN board pin for 1-wire bus. */ namespace Software {