From 392872d95e05ee0474ebd75df8c4c1161e68f08d Mon Sep 17 00:00:00 2001 From: Emanuele Trabattoni Date: Tue, 25 Jun 2024 13:19:26 +0200 Subject: [PATCH] Added all useful libraires --- lib/DS1820/DS1820.cpp | 419 ++++++++++++++++++++++++++ lib/DS1820/DS1820.h | 131 +++++++++ lib/DS1820/OneWire.cpp | 633 ++++++++++++++++++++++++++++++++++++++++ lib/DS1820/OneWire.h | 195 +++++++++++++ lib/MAX6675/max6675.cpp | 31 ++ lib/MAX6675/max6675.h | 49 ++++ src/main.cpp | 49 +++- 7 files changed, 1494 insertions(+), 13 deletions(-) create mode 100644 lib/DS1820/DS1820.cpp create mode 100644 lib/DS1820/DS1820.h create mode 100644 lib/DS1820/OneWire.cpp create mode 100644 lib/DS1820/OneWire.h create mode 100644 lib/MAX6675/max6675.cpp create mode 100644 lib/MAX6675/max6675.h diff --git a/lib/DS1820/DS1820.cpp b/lib/DS1820/DS1820.cpp new file mode 100644 index 0000000..ca4c39b --- /dev/null +++ b/lib/DS1820/DS1820.cpp @@ -0,0 +1,419 @@ +/* + * Dallas' DS1820 family temperature sensor. + * This library depends on the OneWire library (Dallas' 1-Wire bus protocol implementation) + * available at + * + * Example of use: + * + * Single sensor. + * + * #include "mbed.h" + * #include "DS1820.h" + * + * Serial pc(USBTX, USBRX); + * DigitalOut led(LED1); + * OneWire oneWire(D8); // substitute D8 with actual mbed pin name connected 1-wire bus + * float temp = 0; + * int result = 0; + * + * int main() + * { + * pc.printf("\r\n--Starting--\r\n"); + * if (ds1820.begin()) { + * while (1) { + * ds1820.startConversion(); // start temperature conversion from analog to digital + * ThisThread::sleep_for(1000);// let DS1820 complete the temperature conversion + * result = ds1820.read(temp); // read temperature from DS1820 and perform cyclic redundancy check (CRC) + * switch (result) { + * case 0: // no errors -> 'temp' contains the value of measured temperature + * pc.printf("temp = %3.1f%cC\r\n", temp, 176); + * break; + * + * case 1: // no sensor present -> 'temp' is not updated + * pc.printf("no sensor present\n\r"); + * break; + * + * case 2: // CRC error -> 'temp' is not updated + * pc.printf("CRC error\r\n"); + * } + * + * led = !led; + * } + * } + * else + * pc.printf("No DS1820 sensor found!\r\n"); + * } + * + * + * More sensors connected to the same 1-wire bus. + * + * #include "mbed.h" + * #include "DS1820.h" + * + * #define SENSORS_COUNT 64 // number of DS1820 sensors to be connected to the 1-wire bus (max 256) + * + * Serial pc(USBTX, USBRX); + * DigitalOut led(LED1); + * OneWire oneWire(D8); // substitute D8 with actual mbed pin name connected to the DS1820 data pin + * DS1820* ds1820[SENSORS_COUNT]; + * int sensors_found = 0; // counts the actually found DS1820 sensors + * float temp = 0; + * int result = 0; + * + * int main() { + * int i = 0; + * + * pc.printf("\r\n Starting \r\n"); + * //Enumerate (i.e. detect) DS1820 sensors on the 1-wire bus + * for(i = 0; i < SENSORS_COUNT; i++) { + * ds1820[i] = new DS1820(&oneWire); + * if(!ds1820[i]->begin()) { + * delete ds1820[i]; + * break; + * } + * } + * + * sensors_found = i; + * + * if (sensors_found == 0) { + * pc.printf("No DS1820 sensor found!\r\n"); + * return -1; + * } + * else + * pc.printf("Found %d sensors.\r\n", sensors_found); + * + * while(1) { + * pc.printf("-------------------\r\n"); + * for(i = 0; i < sensors_found; i++) + * ds1820[i]->startConversion(); // start temperature conversion from analog to digital + * ThisThread::sleep_for(1000); // let DS1820s complete the temperature conversion + * for(int i = 0; i < sensors_found; i++) { + * if(ds1820[i]->isPresent()) + * pc.printf("temp[%d] = %3.1f%cC\r\n", i, ds1820[i]->read(), 176); // read temperature + * } + * } + * } + * + */ + +#include "DS1820.h" + +#define DEBUG 0 + +//* Initializing static members +uint8_t DS1820::lastAddr[8] = {0, 0, 0, 0, 0, 0, 0, 0}; +/** + * @brief Constructs a generic DS1820 sensor + * @note begin() must be called to detect and initialize the actual model + * @param pin: Name of data pin + * @retval + */ +DS1820::DS1820(PinName pin, int sample_point_us /* = 13 */) { + oneWire = new OneWire(pin, sample_point_us); + present = false; + model_s = false; +} + +/** + * @brief Constructs a generic DS1820 sensor + * @note begin() must be called to detect and initialize the actual model + * @param pin: Name of data pin + * @retval + */ +DS1820::DS1820(OneWire* wire) : + oneWire(wire) { + present = false; + model_s = false; +} + +/** + * @brief Detects and initializes the actual DS1820 model + * @note + * @param + * @retval true: if a DS1820 family sensor was detected and initialized + false: otherwise + */ +bool DS1820::begin(void) { +#if DEBUG + printf("lastAddr ="); + for(uint8_t i = 0; i < 8; i++) { + printf(" %x", lastAddr[i]); + } + printf("\r\n"); +#endif + if(!oneWire->search(lastAddr)) { +#if DEBUG + printf("No addresses.\r\n"); +#endif + oneWire->reset_search(); + ThisThread::sleep_for(250); + return false; + } + + for (int i = 0; i < 8; i++) + addr[i] = lastAddr[i]; + +#if DEBUG + printf("ROM ="); + for(uint8_t i = 0; i < 8; i++) { + printf(" %x", addr[i]); + } + printf("\r\n"); +#endif + + if(OneWire::crc8(addr, 7) == addr[7]) { + present = true; + + // the first ROM byte indicates which chip + switch(addr[0]) { + case 0x10: + model_s = true; +#if DEBUG + printf("DS18S20 or old DS1820\r\n"); +#endif + break; + + case 0x28: + model_s = false; +#if DEBUG + printf("DS18B20\r\n"); +#endif + break; + + case 0x22: + model_s = false; +#if DEBUG + printf("DS1822\r\n"); +#endif + break; + + default: + present = false; +#if DEBUG + printf("Device doesn't belong to the DS1820 family\r\n"); +#endif + return false; + } + return true; + } + else { +#if DEBUG + printf("Invalid CRC!\r\n"); +#endif + return false; + } +} + +/** + * @brief Informs about presence of a DS1820 sensor. + * @note begin() shall be called before using this function + * if a generic DS1820 instance was created by the user. + * No need to call begin() for a specific DS1820 instance. + * @param + * @retval true: when a DS1820 sensor is present + * false: otherwise + */ +bool DS1820::isPresent(void) { + return present; +} + +/** + * @brief Sets temperature-to-digital conversion resolution. + * @note The configuration register allows the user to set the resolution + * of the temperature-to-digital conversion to 9, 10, 11, or 12 bits. + * Defaults to 12-bit resolution for DS18B20. + * DS18S20 allows only 9-bit resolution. + * @param res: Resolution of the temperature-to-digital conversion in bits. + * @retval + */ +void DS1820::setResolution(uint8_t res) { + // keep resolution within limits + if(res > 12) + res = 12; + if(res < 9) + res = 9; + if(model_s) + res = 9; + + oneWire->reset(); + oneWire->select(addr); + oneWire->write_byte(0xBE); // to read Scratchpad + for(uint8_t i = 0; i < 9; i++) // read Scratchpad bytes + data[i] = oneWire->read_byte(); + + data[4] |= (res - 9) << 5; // update configuration byte (set resolution) + oneWire->reset(); + oneWire->select(addr); + oneWire->write_byte(0x4E); // to write into Scratchpad + for(uint8_t i = 2; i < 5; i++) // write three bytes (2nd, 3rd, 4th) into Scratchpad + oneWire->write_byte(data[i]); +} + +/** + * @brief Starts temperature conversion + * @note The time to complete the converion depends on the selected resolution: + * 9-bit resolution -> max conversion time = 93.75ms + * 10-bit resolution -> max conversion time = 187.5ms + * 11-bit resolution -> max conversion time = 375ms + * 12-bit resolution -> max conversion time = 750ms + * @param + * @retval + */ +void DS1820::startConversion(void) { + if(present) { + oneWire->reset(); + oneWire->select(addr); + oneWire->write_byte(0x44); //start temperature conversion + } +} + +/** + * @brief Reads temperature from the chip's Scratchpad + * @note + * @param + * @retval Floating point temperature value + */ +float DS1820::read(void) { + if(present) { + oneWire->reset(); + oneWire->select(addr); + oneWire->write_byte(0xBE); // to read Scratchpad + for(uint8_t i = 0; i < 9; i++) // reading scratchpad registers + data[i] = oneWire->read_byte(); + + // Convert the raw bytes to a 16-bit unsigned value + uint16_t* p_word = reinterpret_cast < uint16_t * > (&data[0]); + +#if DEBUG + printf("raw = %#x\r\n", *p_word); +#endif + + if(model_s) { + *p_word = *p_word << 3; // 9-bit resolution + if(data[7] == 0x10) { + + // "count remain" gives full 12-bit resolution + *p_word = (*p_word & 0xFFF0) + 12 - data[6]; + } + } + else { + uint8_t cfg = (data[4] & 0x60); // default 12-bit resolution + + // at lower resolution, the low bits are undefined, so let's clear them + if(cfg == 0x00) + *p_word = *p_word &~7; // 9-bit resolution + else + if(cfg == 0x20) + *p_word = *p_word &~3; // 10-bit resolution + else + if(cfg == 0x40) + *p_word = *p_word &~1; // 11-bit resolution + + } + + // Convert the raw bytes to a 16-bit signed fixed point value : + // 1 sign bit, 7 integer bits, 8 fractional bits (two’s compliment + // and the LSB of the 16-bit binary number represents 1/256th of a unit). + *p_word = *p_word << 4; + + // Convert to floating point value + return(toFloat(*p_word)); + } + else + return 0; +} + +/** + * @brief Reads temperature from chip's scratchpad. + * @note Verifies data integrity by calculating cyclic redundancy check (CRC). + * If the calculated CRC dosn't match the one stored in chip's scratchpad register + * the temperature variable is not updated and CRC error code is returned. + * @param temp: The temperature variable to be updated by this routine. + * (It's passed as reference to floating point.) + * @retval error code: + * 0 - no errors ('temp' contains the temperature measured) + * 1 - sensor not present ('temp' is not updated) + * 2 - CRC error ('temp' is not updated) + */ +uint8_t DS1820::read(float& temp) { + if(present) { + oneWire->reset(); + oneWire->select(addr); + oneWire->write_byte(0xBE); // to read Scratchpad + for(uint8_t i = 0; i < 9; i++) // reading scratchpad registers + data[i] = oneWire->read_byte(); + + if(oneWire->crc8(data, 8) != data[8]) // if calculated CRC does not match the stored one + { +#if DEBUG + for(uint8_t i = 0; i < 9; i++) + printf("data[%d]=0x%.2x\r\n", i, data[i]); +#endif + return 2; // return with CRC error + } + + // Convert the raw bytes to a 16bit unsigned value + uint16_t* p_word = reinterpret_cast < uint16_t * > (&data[0]); + +#if DEBUG + printf("raw = %#x\r\n", *p_word); +#endif + + if(model_s) { + *p_word = *p_word << 3; // 9 bit resolution, max conversion time = 750ms + if(data[7] == 0x10) { + + // "count remain" gives full 12 bit resolution + *p_word = (*p_word & 0xFFF0) + 12 - data[6]; + } + + // Convert the raw bytes to a 16bit signed fixed point value : + // 1 sign bit, 7 integer bits, 8 fractional bits (two's compliment + // and the LSB of the 16bit binary number represents 1/256th of a unit). + *p_word = *p_word << 4; + // Convert to floating point value + temp = toFloat(*p_word); + return 0; // return with no errors + } + else { + uint8_t cfg = (data[4] & 0x60); // default 12bit resolution, max conversion time = 750ms + + // at lower resolution, the low bits are undefined, so let's clear them + if(cfg == 0x00) + *p_word = *p_word &~7; // 9bit resolution, max conversion time = 93.75ms + else + if(cfg == 0x20) + *p_word = *p_word &~3; // 10bit resolution, max conversion time = 187.5ms + else + if(cfg == 0x40) + *p_word = *p_word &~1; // 11bit resolution, max conversion time = 375ms + + // Convert the raw bytes to a 16bit signed fixed point value : + // 1 sign bit, 7 integer bits, 8 fractional bits (two's complement + // and the LSB of the 16bit binary number represents 1/256th of a unit). + *p_word = *p_word << 4; + // Convert to floating point value + temp = toFloat(*p_word); + return 0; // return with no errors + } + } + else + return 1; // error, sensor is not present +} + +/** + * @brief Converts a 16-bit signed fixed point value to floating point value + * @note The 16-bit unsigned integer represnts actually + * a 16-bit signed fixed point value: + * 1 sign bit, 7 integer bits, 8 fractional bits (two’s complement + * and the LSB of the 16-bit binary number represents 1/256th of a unit). + * @param 16-bit unsigned integer + * @retval Floating point value + */ +float DS1820::toFloat(uint16_t word) { + if(word & 0x8000) + return (-float(uint16_t(~word + 1)) / 256.0f); + else + return (float(word) / 256.0f); +} + diff --git a/lib/DS1820/DS1820.h b/lib/DS1820/DS1820.h new file mode 100644 index 0000000..3d4b4d0 --- /dev/null +++ b/lib/DS1820/DS1820.h @@ -0,0 +1,131 @@ +#ifndef DS1820_H_ +#define DS1820_H_ + + #include + +/** + * Dallas' DS1820 family temperature sensor. + * This library depends on the OneWire library (Dallas' 1-Wire bus protocol implementation) + * available at + * + * Example of use: + * + * @code + * + * Single sensor. + * + * #include "mbed.h" + * #include "DS1820.h" + * + * Serial pc(USBTX, USBRX); + * DigitalOut led(LED1); + * DS1820 ds1820(D8); // substitute D8 with actual mbed pin name connected to 1-wire bus + * float temp = 0; + * int result = 0; + * + * int main() + * { + * pc.printf("\r\n--Starting--\r\n"); + * if (ds1820.begin()) { + * while (1) { + * ds1820.startConversion(); // start temperature conversion from analog to digital + * wait(1.0); // let DS1820 complete the temperature conversion + * result = ds1820.read(temp); // read temperature from DS1820 and perform cyclic redundancy check (CRC) + * switch (result) { + * case 0: // no errors -> 'temp' contains the value of measured temperature + * pc.printf("temp = %3.1f%cC\r\n", temp, 176); + * break; + * + * case 1: // no sensor present -> 'temp' is not updated + * pc.printf("no sensor present\n\r"); + * break; + * + * case 2: // CRC error -> 'temp' is not updated + * pc.printf("CRC error\r\n"); + * } + * + * led = !led; + * } + * } + * else + * pc.printf("No DS1820 sensor found!\r\n"); + * } + * + * + * More sensors connected to the same 1-wire bus. + * + * #include "mbed.h" + * #include "DS1820.h" + * + * #define SENSORS_COUNT 64 // number of DS1820 sensors to be connected to the 1-wire bus (max 256) + * + * Serial pc(USBTX, USBRX); + * DigitalOut led(LED1); + * OneWire oneWire(D8); // substitute D8 with actual mbed pin name connected to the DS1820 data pin + * DS1820* ds1820[SENSORS_COUNT]; + * int sensors_found = 0; // counts the actually found DS1820 sensors + * float temp = 0; + * int result = 0; + * + * int main() { + * int i = 0; + * + * pc.printf("\r\n Starting \r\n"); + * //Enumerate (i.e. detect) DS1820 sensors on the 1-wire bus + * for(i = 0; i < SENSORS_COUNT; i++) { + * ds1820[i] = new DS1820(&oneWire); + * if(!ds1820[i]->begin()) { + * delete ds1820[i]; + * break; + * } + * } + * + * sensors_found = i; + * + * if (sensors_found == 0) { + * pc.printf("No DS1820 sensor found!\r\n"); + * return -1; + * } + * else + * pc.printf("Found %d sensors.\r\n", sensors_found); + * + * while(1) { + * pc.printf("-------------------\r\n"); + * for(i = 0; i < sensors_found; i++) + * ds1820[i]->startConversion(); // start temperature conversion from analog to digital + * wait(1.0); // let DS1820s complete the temperature conversion + * for(int i = 0; i < sensors_found; i++) { + * if(ds1820[i]->isPresent()) + * pc.printf("temp[%d] = %3.1f%cC\r\n", i, ds1820[i]->read(), 176); // read temperature + * } + * } + * } + * + * @endcode + * + * Note: Don't forget to connect a 4.7k Ohm resistor + * between the DS1820's data pin and the +3.3V pin + * + */ +class DS1820 +{ + OneWire *oneWire; + bool present; + bool model_s; + uint8_t data[12]; + uint8_t addr[8]; + float toFloat(uint16_t word); + static uint8_t lastAddr[8]; + +public: + DS1820(PinName pin, int sample_point_us = 13); +// DS1820(char model, PinName pin); + DS1820(OneWire* wire); + bool begin(void); + bool isPresent(); + void setResolution(uint8_t res); + void startConversion(void); + float read(void); + uint8_t read(float& temp); +}; +#endif /* DS1820_H_ */ diff --git a/lib/DS1820/OneWire.cpp b/lib/DS1820/OneWire.cpp new file mode 100644 index 0000000..8df5c2e --- /dev/null +++ b/lib/DS1820/OneWire.cpp @@ -0,0 +1,633 @@ +/* +Copyright (c) 2007, Jim Studt (original old version - many contributors since) + +The latest version of this library may be found at: + http://www.pjrc.com/teensy/td_libs_Onehtml + +OneWire has been maintained by Paul Stoffregen (paul@pjrc.com) since +January 2010. At the time, it was in need of many bug fixes, but had +been abandoned the original author (Jim Studt). None of the known +contributors were interested in maintaining One Paul typically +works on OneWire every 6 to 12 months. Patches usually wait that +long. If anyone is interested in more actively maintaining OneWire, +please contact Paul. + +Version 2.2: + Teensy 3.0 compatibility, Paul Stoffregen, paul@pjrc.com + Arduino Due compatibility, http://arduino.cc/forum/index.php?topic=141030 + Fix DS18B20 example negative temperature + Fix DS18B20 example's low res modes, Ken Butcher + Improve reset timing, Mark Tillotson + Add const qualifiers, Bertrik Sikken + Add initial value input to crc16, Bertrik Sikken + Add target_search() function, Scott Roberts + +Version 2.1: + Arduino 1.0 compatibility, Paul Stoffregen + Improve temperature example, Paul Stoffregen + DS250x_PROM example, Guillermo Lovato + PIC32 (chipKit) compatibility, Jason Dangel, dangel.jason AT gmail.com + Improvements from Glenn Trewitt: + - crc16() now works + - check_crc16() does all of calculation/checking work. + - Added read_bytes() and write_bytes(), to reduce tedious loops. + - Added ds2408 example. + Delete very old, out-of-date readme file (info is here) + +Version 2.0: Modifications by Paul Stoffregen, January 2010: +http://www.pjrc.com/teensy/td_libs_Onehtml + Search fix from Robin James + http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1238032295/27#27 + Use direct optimized I/O in all cases + Disable interrupts during timing critical sections + (this solves many random communication errors) + Disable interrupts during read-modify-write I/O + Reduce RAM consumption by eliminating unnecessary + variables and trimming many to 8 bits + Optimize both crc8 - table version moved to flash + +Modified to work with larger numbers of devices - avoids loop. +Tested in Arduino 11 alpha with 12 sensors. +26 Sept 2008 -- Robin James +http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1238032295/27#27 + +Updated to work with arduino-0008 and to include skip() as of +2007/07/06. --RJL20 + +Modified to calculate the 8-bit CRC directly, avoiding the need for +the 256-byte lookup table to be loaded in RAM. Tested in arduino-0010 +-- Tom Pollard, Jan 23, 2008 + +Jim Studt's original library was modified by Josh Larios. + +Tom Pollard, pollard@alum.mit.edu, contributed around May 20, 2008 + +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 "OneWire.h" + +/** + * @brief Constructs a OneWire object. + * @note GPIO is configured as output and an internal pull up resistor is connected. + * An addition 4.7k Ohm resistor can connected between the 1-wire data bus/line + * and the +3.3V pin, + * + * ---------------- + * | | -----------------------> +3.3V + * | MBED BOARD | | + * | | | ------ + * | +3.3V |--o--| 4.7k |------- + * | | ------ | + * | | | + * | | | + * | | | + * | | | + * | GPIO |--------------------o-----> 1-wire bus/line + * | | + * | | + * | GND |--------------------------> GND + * | | + * ---------------- + * + * @param gpioPin GPIO pin to be used as 1-wire bus/line + * @retval + */ +OneWire::OneWire(PinName gpioPin, int samplePoint_us /*= 13*/) : + _gpio(new DigitalInOut(gpioPin)), + _uart(NULL), + _samplePoint_us(samplePoint_us) +{ + Timer timer; + + MODE(); // set mode to either OpenDrain for STM or PullUp for others + + // Measure bus transition time from ouput to input + timer.reset(); + OUTPUT(); // set as output + WRITE(0); // pull the line down + timer.start(); + INPUT(); // set as input (and release the bus) + timer.stop(); +#if (MBED_MAJOR_VERSION > 5) + _outToInTransition_us = timer.elapsed_time().count(); +#else + _outToInTransition_us = timer.read_us(); +#endif + + MBED_ASSERT(_outToInTransition_us < _samplePoint_us); + + INIT_WAIT; +#if ONEWIRE_SEARCH + reset_search(); +#endif +} + +/** + * @brief Constructs a OneWire object. + * @note UART is used to implement a 1-Wire Bus Master according to Maxim Integrated application note + * + * https://www.maximintegrated.com/en/design/technical-documents/tutorials/2/214.html + * + * In addition to the 4.7k Ohm resistor between the 1-wire data bus/line and the +3.3V pin, + * a 470 Ohm resistor shall be tied to the UART's tx and rx pin. UART's rx pin is then used + * as 1-wire data bus/line. + * + * ---------------- + * | | -----------------------> +3.3V + * | MBED BOARD | | + * | | | ------ + * | +3.3V |--o--| 4.7k |------- + * | | ------ | + * | | ------ | + * | UART TX |-----| 470 |--- | + * | | ------ | | + * | | | | + * | UART RX |----------------o---o-----> 1-wire bus/line + * | | + * | | + * | GND |--------------------------> GND + * | | + * ---------------- + * + * @param txPin UART's Tx pin name + * @param rxPin UART's Rx pin name + * @retval + */ +OneWire::OneWire(PinName txPin, PinName rxPin, int baud /*=115200*/) : + _gpio(NULL), + _uart(new UART(txPin, rxPin, baud)) +{ +#if ONEWIRE_SEARCH + reset_search(); +#endif +} + +OneWire::~OneWire() +{ + if (_gpio != NULL) + delete _gpio; + if (_uart != NULL) + delete _uart; +} + +/** + * @brief Performs the onewire reset function. + * @note We will wait up to 250uS for the bus to come high, + * if it doesn't then it is broken or shorted and we return a 0; + * @param + * @retval 1 if a device asserted a presence pulse, 0 otherwise. + */ +uint8_t OneWire::reset(void) +{ + uint8_t present; + + if (_gpio != NULL) { + OUTPUT(); + WRITE(0); // pull down the 1-wire bus do create reset pulse + WAIT_US(500); // wait at least 480 us + INPUT(); // release the 1-wire bus and go into receive mode + WAIT_US(90); // DS1820 waits about 15 to 60 us and generates a 60 to 240 us presence pulse + present = !READ(); // read the presence pulse + WAIT_US(420); + } + else { + _uart->baud(9600); + #if (MBED_MAJOR_VERSION > 5) + ThisThread::sleep_for(10ms); + #else + wait_ms(10); + #endif + _uart->_base_putc(0xF0); + present = _uart->_base_getc(); + wait_us(420); + _uart->baud(115200); + #if (MBED_MAJOR_VERSION > 5) + ThisThread::sleep_for(10ms); + #else + wait_ms(10); +#endif + present = (present >= 0x10); + } + + return present; +} + +/** + * @brief Writes a bit. + * @note GPIO registers are used for STM chips to cut time. + * @param + * @retval + */ +void OneWire::write_bit(uint8_t v) +{ + if (v & 1) { + if (_gpio != NULL) { + OUTPUT(); + WRITE(0); // drive output low + WAIT_US(1); + WRITE(1); // drive output high + WAIT_US(60); + } + else { + _uart->_base_putc(0xFF); + } + } + else { + if (_gpio != NULL) { + OUTPUT(); + WRITE(0); // drive output low + WAIT_US(60); + WRITE(1); // drive output high + WAIT_US(1); + } + else { + _uart->_base_putc(0x00); + } + } +} + +/** + * @brief Reads a bit. + * @note GPIO registers are used for STM chips to cut time. + * @param + * @retval + */ +uint8_t OneWire::read_bit(void) +{ + uint8_t r; + + if (_gpio != NULL) { + OUTPUT(); + WRITE(0); + INPUT(); + wait_us(_samplePoint_us - _outToInTransition_us); // wait till sample point + r = READ(); + WAIT_US(55); + } + else { + _uart->_base_putc(0xFF); + do { + r = _uart->_base_getc(); + wait_us(100); + } while(_uart->readable()); + + r = r & 0x01; + } + + return r; +} + +/** + * @brief Writes a byte. + * @note The writing code uses the active drivers to raise the + pin high, if you need power after the write (e.g. DS18S20 in + parasite power mode) then set 'power' to 1, otherwise the pin will + go tri-state at the end of the write to avoid heating in a short or + other mishap. + * @param + * @retval + */ +void OneWire::write_byte(uint8_t v, uint8_t power /* = 0 */ ) +{ + uint8_t bitMask; + + for (bitMask = 0x01; bitMask; bitMask <<= 1) + write_bit((bitMask & v) ? 1 : 0); + if ((!power) && (_gpio != NULL)) + INPUT(); +} + +/** + * @brief Writes bytes. + * @note + * @param + * @retval + */ +void OneWire::write_bytes(const uint8_t* buf, uint16_t count, bool power /* = 0 */ ) +{ + for (uint16_t i = 0; i < count; i++) + write_byte(buf[i]); + if ((!power) && (_gpio != NULL)) + INPUT(); +} + +/** + * @brief Reads a byte. + * @note + * @param + * @retval + */ +uint8_t OneWire::read_byte() +{ + uint8_t bitMask; + uint8_t r = 0; + + for (bitMask = 0x01; bitMask; bitMask <<= 1) { + if (read_bit()) + r |= bitMask; + } + + return r; +} + +/** + * @brief Reads bytes. + * @note + * @param + * @retval + */ +void OneWire::read_bytes(uint8_t* buf, uint16_t count) +{ + for (uint16_t i = 0; i < count; i++) + buf[i] = read_byte(); +} + +/** + * @brief Selects ROM. + * @note + * @param + * @retval + */ +void OneWire::select(const uint8_t rom[8]) +{ + uint8_t i; + + write_byte(0x55); // Choose ROM + for (i = 0; i < 8; i++) + write_byte(rom[i]); +} + +/** + * @brief Skips ROM select. + * @note + * @param + * @retval + */ +void OneWire::skip() +{ + write_byte(0xCC); // Skip ROM +} + +/** + * @brief Unpowers the chip. + * @note + * @param + * @retval + */ +void OneWire::depower() +{ + if (_gpio != NULL) + INPUT(); +} + +#if ONEWIRE_SEARCH +// + +/** + * @brief Resets the search state. + * @note We need to use this function to start a search again from the beginning. + * We do not need to do it for the first search, though we could. + * @param + * @retval + */ +void OneWire::reset_search() +{ + // reset the search state + + LastDiscrepancy = 0; + LastDeviceFlag = false; + LastFamilyDiscrepancy = 0; + for (int i = 7;; i--) { + ROM_NO[i] = 0; + if (i == 0) + break; + } +} + +/** + * @brief Sets the search state to find SearchFamily type devices. + * @note + * @param + * @retval + */ +void OneWire::target_search(uint8_t family_code) +{ + // set the search state to find SearchFamily type devices + + ROM_NO[0] = family_code; + for (uint8_t i = 1; i < 8; i++) + ROM_NO[i] = 0; + LastDiscrepancy = 64; + LastFamilyDiscrepancy = 0; + LastDeviceFlag = false; +} + +/** + * @brief Performs a search. + * @note Perform a search. If this function returns a '1' then it has + enumerated the next device and you may retrieve the ROM from the + OneWire::address variable. If there are no devices, no further + devices, or something horrible happens in the middle of the + enumeration then a 0 is returned. If a new device is found then + its address is copied to newAddr. Use OneWire::reset_search() to + start over. + + --- Replaced by the one from the Dallas Semiconductor web site --- + ------------------------------------------------------------------------- + Perform the 1-Wire Search Algorithm on the 1-Wire bus using the existing + search state. + * @param + * @retval true : device found, ROM number in ROM_NO buffer + * false : device not found, end of search + */ +uint8_t OneWire::search(uint8_t* newAddr) +{ + uint8_t id_bit_number; + uint8_t last_zero, rom_byte_number, search_result; + uint8_t id_bit, cmp_id_bit; + + unsigned char rom_byte_mask, search_direction; + + // initialize for search + + id_bit_number = 1; + last_zero = 0; + rom_byte_number = 0; + rom_byte_mask = 1; + search_result = 0; + + // if the last call was not the last one + if (!LastDeviceFlag) { + // 1-Wire reset + if (!reset()) { + // reset the search + LastDiscrepancy = 0; + LastDeviceFlag = false; + LastFamilyDiscrepancy = 0; + return false; + } + + // issue the search command + write_byte(0xF0); + + // loop to do the search + do + { + // read a bit and its complement + id_bit = read_bit(); + cmp_id_bit = read_bit(); + + // check for no devices on 1-wire + if ((id_bit == 1) && (cmp_id_bit == 1)) + break; + else { + // all devices coupled have 0 or 1 + if (id_bit != cmp_id_bit) + search_direction = id_bit; // 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 < LastDiscrepancy) + search_direction = ((ROM_NO[rom_byte_number] & rom_byte_mask) > 0); + else + // if equal to last pick 1, if not then pick 0 + search_direction = (id_bit_number == LastDiscrepancy); + + // 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) + LastFamilyDiscrepancy = last_zero; + } + } + + // set or clear the bit in the ROM byte rom_byte_number + // with mask rom_byte_mask + if (search_direction == 1) + ROM_NO[rom_byte_number] |= rom_byte_mask; + else + ROM_NO[rom_byte_number] &= ~rom_byte_mask; + + // serial number search direction write bit + write_bit(search_direction); + + // 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) { + 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)) { + // search successful so set LastDiscrepancy,LastDeviceFlag,search_result + LastDiscrepancy = last_zero; + + // check for last device + if (LastDiscrepancy == 0) + LastDeviceFlag = true; + + search_result = true; + } + } + + // if no device found then reset counters so next 'search' will be like a first + if (!search_result || !ROM_NO[0]) { + LastDiscrepancy = 0; + LastDeviceFlag = false; + LastFamilyDiscrepancy = 0; + search_result = false; + } + + for (int i = 0; i < 8; i++) + newAddr[i] = ROM_NO[i]; + return search_result; +} +#endif +// +#if ONEWIRE_CRC +// + +/** + * @brief Computes a Dallas Semiconductor 8 bit CRC directly. + * @note The 1-Wire CRC scheme is described in Maxim Application Note 27: + "Understanding and Using Cyclic Redundancy Checks with Maxim iButton Products" + * @param + * @retval + */ +uint8_t OneWire::crc8(const uint8_t* addr, uint8_t len) +{ + uint8_t crc = 0; + + while (len--) { + uint8_t inbyte = *addr++; + for (uint8_t i = 8; i; i--) { + uint8_t mix = (crc ^ inbyte) & 0x01; + crc >>= 1; + if (mix) + crc ^= 0x8C; + inbyte >>= 1; + } + } + + return crc; +} +#endif diff --git a/lib/DS1820/OneWire.h b/lib/DS1820/OneWire.h new file mode 100644 index 0000000..45bf540 --- /dev/null +++ b/lib/DS1820/OneWire.h @@ -0,0 +1,195 @@ +#ifndef OneWire_h +#define OneWire_h + +#include +#include +#include "SerialBase.h" + +#define MODE() _gpio->mode(PullUp) +#define INPUT() _gpio->input() +#define OUTPUT() _gpio->output() +#define READ() _gpio->read() +#define WRITE(x) _gpio->write(x) + +#ifdef TARGET_NORDIC +//NORDIC targets (NRF) use software delays since their ticker uses a 32kHz clock + static uint32_t loops_per_us = 0; + + #define INIT_WAIT init_soft_delay() + #define WAIT_US(x) for(int cnt = 0; cnt < (x * loops_per_us) >> 5; cnt++) {__NOP(); __NOP(); __NOP();} + +void init_soft_delay( void ) { + if (loops_per_us == 0) { + loops_per_us = 1; + Timer timey; + timey.start(); + ONEWIRE_DELAY_US(320000); + timey.stop(); + loops_per_us = (320000 + timey.read_us() / 2) / timey.read_us(); + } +} +#else + #define INIT_WAIT + #define WAIT_US(x) wait_us(x) +#endif + +// You can exclude certain features from OneWire. In theory, this +// might save some space. In practice, the compiler automatically +// removes unused code (technically, the linker, using -fdata-sections +// and -ffunction-sections when compiling, and Wl,--gc-sections +// when linking), so most of these will not result in any code size +// reduction. Well, unless you try to use the missing features +// and redesign your program to not need them! ONEWIRE_CRC8_TABLE +// is the exception, because it selects a fast but large algorithm +// or a small but slow algorithm. + +// you can exclude onewire_search by defining that to 0 +#ifndef ONEWIRE_SEARCH +#define ONEWIRE_SEARCH 1 +#endif + +// You can exclude CRC checks altogether by defining this to 0 +#ifndef ONEWIRE_CRC +#define ONEWIRE_CRC 1 +#endif + +class UART : + public SerialBase, + private NonCopyable +{ + UART(const UART&); +public: + UART(PinName tx, PinName rx, int baud) : SerialBase(tx, rx, baud) {} + + using SerialBase::_base_getc; + using SerialBase::_base_putc; +}; + +class OneWire +{ + DigitalInOut* _gpio; + UART* _uart; + + int _samplePoint_us; + int _outToInTransition_us; + +#if ONEWIRE_SEARCH + // global search state + unsigned char ROM_NO[8]; + uint8_t LastDiscrepancy; + uint8_t LastFamilyDiscrepancy; + uint8_t LastDeviceFlag; +#endif + +public: + + // Constructors + OneWire(PinName gpioPin, int samplePoint_us = 13); // GPIO + OneWire(PinName txPin, PinName rxPin, int baud = 115200); // UART + + // Destructor + ~OneWire(); + + // Perform a 1-Wire reset cycle. Returns 1 if a device responds + // with a presence pulse. Returns 0 if there is no device or the + // bus is shorted or otherwise held low for more than 250uS + uint8_t reset(void); + + // Issue a 1-Wire rom select command, you do the reset first. + void select(const uint8_t rom[8]); + + // Issue a 1-Wire rom skip command, to address all on bus. + void skip(void); + + // Write a byte. If 'power' is one then the wire is held high at + // the end for parasitically powered devices. You are responsible + // for eventually depowering it by calling depower() or doing + // another read or write. + void write_byte(uint8_t v, uint8_t power = 0); + + void write_bytes(const uint8_t *buf, uint16_t count, bool power = 0); + + // Read a byte. + uint8_t read_byte(void); + + void read_bytes(uint8_t *buf, uint16_t count); + + // Write a bit. The bus is always left powered at the end, see + // note in write() about that. + void write_bit(uint8_t v); + + // Read a bit. + uint8_t read_bit(void); + + // Stop forcing power onto the bus. You only need to do this if + // you used the 'power' flag to write() or used a write_bit() call + // and aren't about to do another read or write. You would rather + // not leave this powered if you don't have to, just in case + // someone shorts your bus. + void depower(void); + +#if ONEWIRE_SEARCH + // Clear the search state so that if will start from the beginning again. + void reset_search(); + + // Setup the search to find the device type 'family_code' on the next call + // to search(*newAddr) if it is present. + void target_search(uint8_t family_code); + + // Look for the next device. Returns 1 if a new address has been + // returned. A zero might mean that the bus is shorted, there are + // no devices, or you have already retrieved all of them. It + // might be a good idea to check the CRC to make sure you didn't + // get garbage. The order is deterministic. You will always get + // the same devices in the same order. + uint8_t search(uint8_t *newAddr); +#endif + +#if ONEWIRE_CRC + // Compute a Dallas Semiconductor 8 bit CRC, these are used in the + // ROM and scratchpad registers. + static uint8_t crc8(const uint8_t *addr, uint8_t len); + +#if ONEWIRE_CRC16 + // Compute the 1-Wire CRC16 and compare it against the received CRC. + // Example usage (reading a DS2408): + // // Put everything in a buffer so we can compute the CRC easily. + // uint8_t buf[13]; + // buf[0] = 0xF0; // Read PIO Registers + // buf[1] = 0x88; // LSB address + // buf[2] = 0x00; // MSB address + // WriteBytes(net, buf, 3); // Write 3 cmd bytes + // ReadBytes(net, buf+3, 10); // Read 6 data bytes, 2 0xFF, 2 CRC16 + // if (!CheckCRC16(buf, 11, &buf[11])) { + // // Handle error. + // } + // + // @param input - Array of bytes to checksum. + // @param len - How many bytes to use. + // @param inverted_crc - The two CRC16 bytes in the received data. + // This should just point into the received data, + // *not* at a 16-bit integer. + // @param crc - The crc starting value (optional) + // @return True, iff the CRC matches. + static bool check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inverted_crc, uint16_t crc = 0); + + // Compute a Dallas Semiconductor 16 bit CRC. This is required to check + // the integrity of data received from many 1-Wire devices. Note that the + // CRC computed here is *not* what you'll get from the 1-Wire network, + // for two reasons: + // 1) The CRC is transmitted bitwise inverted. + // 2) Depending on the endian-ness of your processor, the binary + // representation of the two-byte return value may have a different + // byte order than the two bytes you get from 1-Wire. + // @param input - Array of bytes to checksum. + // @param len - How many bytes to use. + // @param crc - The crc starting value (optional) + // @return The CRC16, as defined by Dallas Semiconductor. + static uint16_t crc16(const uint8_t* input, uint16_t len, uint16_t crc = 0); +#endif +#endif +}; + +#endif + + diff --git a/lib/MAX6675/max6675.cpp b/lib/MAX6675/max6675.cpp new file mode 100644 index 0000000..9214316 --- /dev/null +++ b/lib/MAX6675/max6675.cpp @@ -0,0 +1,31 @@ + +#include "max6675.h" + +max6675::max6675(PinName miso, PinName sclk, PinName cs) : + max(NC, miso, sclk), _cs(cs) +{ + max.format(16,1); // set 16 bit SPI format + max.frequency(400000); +} + +float max6675::gettemp(int cf) +{ + float temp = 0; + int tempByte= 0; + + _cs = 0; + wait_us(1); // wait to stablize + tempByte = max.write(0); + wait_us(1); // wait to finish + _cs = 1; + + if (tempByte & (1<<2)) { // faulty or no sensor connected + return -99; + } else { + temp = (tempByte)/32.0f; + } + if(cf) { + temp = (temp*9.0f/5.0f) + 32.0f; // Convert value to ˚F + } + return temp; +} diff --git a/lib/MAX6675/max6675.h b/lib/MAX6675/max6675.h new file mode 100644 index 0000000..4b71a48 --- /dev/null +++ b/lib/MAX6675/max6675.h @@ -0,0 +1,49 @@ +#ifndef max6675_h +#define max6675_h + +#include "mbed.h" + +/* +#include "mbed.h" +#include "max6675.h" + +max6675 sensor(D5,D3,D6); //miso, sclk, cs +Serial pc(USBTX,USBRX); + +int main() +{ + pc.baud(921600); + pc.printf("\033[0m\033[2J\033[HMAX6675 Thermocouple!\r\n\n\n"); + + int cf = 0; // 0 Centigrade, 1 Fahrenheit + + while (1) { + + float temp = sensor.gettemp(cf); + if (cf) { + printf(" Temp: %4.2f%cF \n\033[2K\033[1A",temp,176); + } else { + printf(" Temp: %4.2f%cC \n\033[2K\033[1A",temp,176); + } + wait_ms(250); // requires 250mS for temperature conversion process + } +} +*/ + + +class max6675 +{ + public: + + max6675(PinName miso, PinName sclk, PinName cs); + + // read temperature 0 Centigrade, 1 Fahrenheit + float gettemp(int cf); + + private: + SPI max; + DigitalOut _cs; + Timer t; +}; + +#endif diff --git a/src/main.cpp b/src/main.cpp index 21ae4b7..119f9f8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,32 +1,55 @@ #include #include #include +#include #include -auto t_controller = PID(1, 1, 0, 2); -auto t_sensor = DHT(PC_10, AM2302); +auto period = 2; + +auto pid = PID(8, 120, 0.0, period); +auto dht = DHT(PC_10, AM2302); +auto tCouple = new max6675(SPI_MISO, SPI_SCK, D10); auto led = DigitalOut(LED1); auto lcd = LiquidCrystal_I2C(0x20, 20, 4, I2C_SDA, I2C_SCL); +auto pwm = PwmOut(PA_10); int main() { - printf("Start\n"); - t_controller.setInputLimits(10, 30); - t_controller.setOutputLimits(0, 100); - t_controller.setSetPoint(29); + printf("Kp:%ef;TauI:%e;TauD:%e\n", pid.getPParam(), pid.getIParam(), pid.getDParam()); + printf("SP;AT;T;H;CV\n"); + + pid.setInputLimits(0, 60); + pid.setOutputLimits(0.0f, 1.0f); + pwm.period_ms(10); + pwm.write(1.0f); + + unsigned i(0); + float setPoint = 40.0f; + pid.setSetPoint(setPoint); + while (true) { - if (eError::ERROR_NONE != t_sensor.readData()) + if (eError::ERROR_NONE != dht.readData()) { printf("Error\n"); continue; } - auto t = t_sensor.ReadTemperature(CELCIUS); - auto h = t_sensor.ReadHumidity(); - t_controller.setProcessValue(t); - auto c = t_controller.compute(); - printf("T:%3.1f\tH:%3.1f\tCV:%3.1f\n", t, h, c); + + if (++i >= 900) + { + setPoint = setPoint == 40.0f ? 45.0f : 40.0f; + pid.setSetPoint(setPoint); + i = 0; + } + + auto t = dht.ReadTemperature(CELCIUS); + auto tc = tCouple->gettemp(0); + auto h = dht.ReadHumidity(); + pid.setProcessValue(t); + auto c = pid.compute(); + printf("%3.1f;%3.1f;%3.1f;%3.1f;%3.2f\n", setPoint, tc, t, h, c); led = !led; - ThisThread::sleep_for(2s); + pwm.write(1.0 - c); + ThisThread::sleep_for(period * 1s); } } \ No newline at end of file