diff --git a/CMakeLists.txt b/CMakeLists.txt index a52e83b..c7b5df3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,8 @@ set(SOURCE_FILES lib/usart.h lcd.c lcd.h + onewire.c + onewire.h ) include_directories(lib diff --git a/Makefile b/Makefile index d3f8255..b568d43 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,7 @@ OBJS += lib/iopins.o OBJS += lib/spi.o OBJS += lib/adc.o OBJS += lcd.o +OBJS += onewire.o # Dirs with header files INCL_DIRS = . lib/ diff --git a/main.c b/main.c index 0792990..1ee3bff 100644 --- a/main.c +++ b/main.c @@ -6,19 +6,24 @@ #include // C header for int types like uint8_t #include // C header for the bool type #include +#include // Include stuff from the library #include "lib/iopins.h" #include "lib/usart.h" #include "lcd.h" +#include "onewire.h" // Pins #define LED 13 +#define OW_PIN D9 + void _lcd_wait_bf(); void _lcd_write_byte(uint8_t bb); +#if 0 // UART receive handler ISR(USART_RX_vect) { @@ -26,31 +31,81 @@ ISR(USART_RX_vect) uint8_t b = usart_rx(); usart_tx(b); // send back } - +#endif void main() { +#if 0 usart_init(BAUD_115200); usart_isr_rx_enable(true); // enable RX interrupt handler +#endif // configure pins as_output(LED); lcd_init(); + lcd_clear(); + lcd_puts(""); + // globally enable interrupts (for the USART_RX handler) sei(); - uint8_t cnt = 0; + + uint8_t addr[8]; + char charbuf[21]; + + int dots = 0; while (1) { - lcd_clear(); + bool signal = ow_reset(OW_PIN); + if (!signal) { + lcd_clear(); + lcd_puts("No 1-Wire detected..."); + dots = 0; + } else { + ow_write(OW_PIN, 0x33); + ow_read_arr(OW_PIN, addr, 8); - lcd_xy(cnt, 0); - lcd_putc('A'+cnt); + lcd_clear(); - cnt = (cnt+1)%20; + char *p = charbuf; + for(uint8_t i = 0; i < 4; i++) { + p += sprintf(p, "%02X", addr[i]); + if (i < 3) *p++ = ':'; + } + lcd_puts(charbuf); + lcd_xy(0, 1); - pin_toggle(13); // blink the LED + p = charbuf; + for(uint8_t i = 0; i < 4; i++) { + p += sprintf(p, "%02X", addr[4+i]); + if (i < 3) *p++ = ':'; + } + lcd_puts(charbuf); + + ds1820_single_measure(OW_PIN); + uint16_t cels = ds1820_read_temp_c(OW_PIN); + + if (cels == 850) { + lcd_xy(12, 0); + for(uint8_t i = 0; i <= dots; i++) { + lcd_putc('.'); + } + dots = (dots+1)%3; + } else { + p = charbuf; + p += sprintf(p, "%d", cels/10); + *p++ = '.'; + p += sprintf(p, "%d", cels%10); + *p++ = 0xDF; + *p++ = 'C'; + *p++ = 0; - _delay_ms(100); + lcd_xy(12, 0); + lcd_puts(charbuf); + } + } + + pin_toggle(13); // blink the LED + _delay_ms(100); } } diff --git a/onewire.c b/onewire.c new file mode 100644 index 0000000..0cca651 --- /dev/null +++ b/onewire.c @@ -0,0 +1,248 @@ +#include +#include +#include +#include + +#include "iopins.h" +#include "onewire.h" + + +/** Perform bus reset. Returns true if any device is connected */ +bool ow_reset(const uint8_t pin) +{ + as_output_n(pin); + pin_down_n(pin); + _delay_us(480); + + as_input_pu_n(pin); + _delay_us(70); + + const bool a = ! pin_read_n(pin); + + _delay_us(410); + + return a; +} + + +/** Send a single bit */ +void _ow_tx_bit(const uint8_t pin, const bool bit) +{ + as_output_n(pin); + pin_down_n(pin); + + if (bit) + { + _delay_us(6); + as_input_pu_n(pin); + _delay_us(64); + } + else + { + _delay_us(60); + as_input_pu_n(pin); + _delay_us(10); + } +} + + +/** Send a single byte */ +void ow_write(const uint8_t pin, const uint8_t byte) +{ + for (uint8_t i = 0; i < 8; i++) + { + _ow_tx_bit(pin, (byte >> i) & 0x01); + } +} + + +/** Read a single bit */ +bool _ow_rx_bit(const uint8_t pin) +{ + as_output_n(pin); + pin_down_n(pin); + _delay_us(6); + + as_input_pu_n(pin); + _delay_us(9); + + const bool a = pin_read_n(pin); + + _delay_us(55); + + return a; +} + + +/** Read a single byte */ +uint8_t ow_read(const uint8_t pin) +{ + uint8_t byte = 0; + + for (uint8_t i = 0; i < 8; i++) + { + byte = (byte >> 1) | (_ow_rx_bit(pin) << 7); + } + + return byte; +} + + +/** Wait until the device is ready. Returns false on timeout */ +bool ow_wait_ready(const uint8_t pin) +{ + uint16_t timeout = 700; + as_input_pu_n(pin); + + while (--timeout > 0) + { + if (pin_is_high_n(pin)) return true; + _delay_ms(1); + } + + return false; +} + + +/** Read bytes into an array */ +void ow_read_arr(const uint8_t pin, uint8_t* array, const uint8_t count) +{ + for (uint8_t i = 0; i < count; i++) + { + array[i] = ow_read(pin); + } +} + + + +// ---------- CRC utils ---------- + +/* + Dallas 1-wire CRC routines for Arduino with examples of usage. + The 16-bit routine is new. + The 8-bit routine is from http://github.com/paeaetech/paeae/tree/master/Libraries/ds2482/ + + Copyright (C) 2010 Kairama Inc + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +// Dallas 1-wire 16-bit CRC calculation. Developed from Maxim Application Note 27. + +/** Compute a CRC16 checksum */ +uint16_t crc16(uint8_t *data, uint8_t len) +{ + uint16_t crc = 0; + + for (uint8_t i = 0; i < len; i++) + { + uint8_t inbyte = data[i]; + for (uint8_t j = 0; j < 8; j++) + { + uint8_t mix = (crc ^ inbyte) & 0x01; + crc = crc >> 1; + if (mix) + crc = crc ^ 0xA001; + + inbyte = inbyte >> 1; + } + } + return crc; +} + + +// The 1-Wire CRC scheme is described in Maxim Application Note 27: +// "Understanding and Using Cyclic Redundancy Checks with Maxim iButton Products" + +/** Compute a CRC8 checksum */ +uint8_t crc8(uint8_t *addr, uint8_t len) +{ + uint8_t crc = 0; + + for (uint8_t i = 0; i < len; i++) + { + uint8_t inbyte = addr[i]; + for (uint8_t j = 0; j < 8; j++) + { + uint8_t mix = (crc ^ inbyte) & 0x01; + crc >>= 1; + if (mix) + crc ^= 0x8C; + + inbyte >>= 1; + } + } + + return crc; +} + + +// --- utils for DS1820 --- + + +/** Read temperature in 0.0625°C, or TEMP_ERROR on error */ +int16_t ds1820_read_temp(uint8_t pin) +{ + ow_write(pin, READ_SCRATCHPAD); + uint8_t bytes[9]; + ow_read_arr(pin, bytes, 9); + + uint8_t crc = crc8(bytes, 8); + if (crc != bytes[8]) + { + return TEMP_ERROR; + } + else + { + int16_t a = ((bytes[1] << 8) | bytes[0]) >> 1; + a = a << 4; + a += (16 - bytes[6]) & 0x0F; + a -= 0x04; + + return a; + } +} + +/** Read temperature in 0.1°C, or TEMP_ERROR on error */ +int16_t ds1820_read_temp_c(uint8_t pin) +{ + int32_t temp = ds1820_read_temp(pin); + + if (temp == TEMP_ERROR) + return TEMP_ERROR; + + temp *= 625; + uint16_t rem = temp % 1000; + temp /= 1000; + if (rem >= 500) temp++; + + return (int16_t) temp; +} + + +bool ds1820_single_measure(uint8_t pin) +{ + ow_reset(pin); + ow_write(pin, SKIP_ROM); + ow_write(pin, CONVERT_T); + + if (!ow_wait_ready(pin)) + { + return false; + } + + ow_reset(pin); + ow_write(pin, SKIP_ROM); + + return true; +} diff --git a/onewire.h b/onewire.h new file mode 100644 index 0000000..32a31fe --- /dev/null +++ b/onewire.h @@ -0,0 +1,58 @@ +#pragma once + +// +// Utils for Dallas OneWire bus (DS1820 etc) +// + +#include +#include + +#define SKIP_ROM 0xCC +#define CONVERT_T 0x44 +#define READ_SCRATCHPAD 0xBE + +/** Perform bus reset. Returns true if any device is connected */ +bool ow_reset(uint8_t pin); + +/** Send a single byte */ +void ow_write(const uint8_t pin, const uint8_t byte); + +/** Read a single byte */ +uint8_t ow_read(uint8_t pin); + +/** Wait until the device is ready. Returns false on timeout */ +bool ow_wait_ready(uint8_t pin); + +/** Read bytes into an array */ +void ow_read_arr(uint8_t pin, uint8_t* array, uint8_t count); + +/** Compute a CRC16 checksum */ +uint16_t crc16(uint8_t *data, uint8_t len); + +/** Compute a CRC8 checksum */ +uint8_t crc8(uint8_t *addr, uint8_t len); + +// --- utils for DS1820 --- + +#define TEMP_ERROR (-32768) + +/** + * Read temperature in 0.0625°C, or TEMP_ERROR on error + * Use this where you'd normally use READ_SCRATCHPAD + */ +int16_t ds1820_read_temp(uint8_t pin); + + +/** + * Read temperature in 0.1°C, or TEMP_ERROR on error + * Use this where you'd normally use READ_SCRATCHPAD + */ +int16_t ds1820_read_temp_c(uint8_t pin); + +/** + * Perform a temperature measurement with single DS1820 device on the line + * Can be followed by a call to read temperature (READ_SCRATCHPAD). + * + * Returns false on failure (device not connected) + */ +bool ds1820_single_measure(uint8_t pin);