diff options
Diffstat (limited to 'sw')
-rw-r--r-- | sw/Makefile | 11 | ||||
-rw-r--r-- | sw/README.rst | 1 | ||||
-rw-r--r-- | sw/lib/SPI.h | 2 | ||||
-rw-r--r-- | sw/ltc2400.cpp | 154 | ||||
-rw-r--r-- | sw/ltc2400.h | 36 | ||||
-rw-r--r-- | sw/main.cpp | 129 | ||||
-rw-r--r-- | sw/pins.h | 74 |
7 files changed, 346 insertions, 61 deletions
diff --git a/sw/Makefile b/sw/Makefile index 5f8af16..e8057b2 100644 --- a/sw/Makefile +++ b/sw/Makefile @@ -26,22 +26,19 @@ HEADERS = \ lib/Arduino.h \ lib/DallasTemperature.h \ lib/delay.h \ - lib/LTC24XX_general.h \ lib/OneWire.h \ lib/pins_arduino.h \ - lib/SPI.h \ lib/uart.h \ lib/util/OneWire_direct_gpio.h \ - lib/util/OneWire_direct_regtype.h \ - lib/wiring_private.h + lib/util/OneWire_direct_regtype.h # Application object files -APP_CXX_OBJECTS = main.o +APP_CXX_OBJECTS = main.o ltc2400.o APP_OBJECTS = # Library object files to build and use -LIB_OBJECTS = uart.o wiring_analog.o wiring_digital.o -LIB_CXX_OBJECTS = OneWire.o DallasTemperature.o LTC24XX_general.o SPI.o +LIB_OBJECTS = uart.o +LIB_CXX_OBJECTS = OneWire.o DallasTemperature.o LIB_ASM_OBJECTS = LIB_DIR = lib diff --git a/sw/README.rst b/sw/README.rst index 5232b93..f18e668 100644 --- a/sw/README.rst +++ b/sw/README.rst @@ -33,6 +33,7 @@ TODO - Configurer SPI pour LTC2400 - Le code exemple utilise arduino et Wire.h - Initialiser entrées analogiques + - Mesurer tension batterie - Initialiser DS18B20 diff --git a/sw/lib/SPI.h b/sw/lib/SPI.h index 5206a09..eac4c35 100644 --- a/sw/lib/SPI.h +++ b/sw/lib/SPI.h @@ -14,6 +14,8 @@ #ifndef _SPI_H_INCLUDED #define _SPI_H_INCLUDED +#error "do not use" + #include <Arduino.h> // SPI_HAS_TRANSACTION means SPI has beginTransaction(), endTransaction(), diff --git a/sw/ltc2400.cpp b/sw/ltc2400.cpp new file mode 100644 index 0000000..2a76d87 --- /dev/null +++ b/sw/ltc2400.cpp @@ -0,0 +1,154 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Matthias P. Braendli + * + * 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. +*/ + +/* Essential information from datasheet: + * + * SCK mode: + * The internal or external SCK mode is selected on power-up and then + * reselected every time a HIGH-to-LOW transition is detected at the CS pin. If + * SCK is HIGH or floating at power-up or during this transition, the + * converter enters the internal SCK mode. If SCK is LOW at power-up or + * during this transition, the converter enters the external SCK mode. + * + * CS: + * At any time during the conversion cycle, CS may be pulled LOW in order to + * monitor the state of the converter. While CS is pulled LOW, EOC is output + * to the SDO pin. EOC = 1 while a conversion is in progress and EOC = 0 if the + * device is in the sleep state. Independent of CS, the device automatically + * enters the low power sleep state once the conversion is complete. + * + * The device remains in the sleep state until the first rising edge of SCK is + * seen while CS is LOW. + * + * We must latch incoming data on the rising edge of SCK. + * + * On the 32nd falling edge of SCK, the device begins a new conversion. + * + * The sub LSBs are valid conversion results beyond the 24-bit level that may + * be included in averaging or discarded without loss of resolution. + * + * Output format, 32 bits in total: + * EOC, DMY, SIG, EXR, MSB..LSB (24 bits), SUB-LSB(4 bits) + * + * - EOC goes low when the conversion is complete + * - DMY is always low + * - SIG is 1 when Vin > 0 + * - EXR is 0 when 0 <= Vin <= Vref + * + * + * We will use External SCK, Single Cycle Conversion (see table 4): + * - SCK must be low on falling edge of CSn + * - We will discard the sub LSB + * - We will check that DMY is always 0, SIG always 1 and EXR always 0 + * +*/ + +#include <avr/io.h> +#include <avr/interrupt.h> +#include "ltc2400.h" + +static void cs_low() +{ + cli(); + PORTB &= ~PINB_SPI_LTC_CSn; + sei(); +} + +static void cs_high() +{ + cli(); + PORTB |= PINB_SPI_LTC_CSn; + sei(); +} + +void ltc2400_init() +{ + cli(); + + // Set bit order=MSB first (DORD=0) + // And SPI mode=0 (CPOL=0, CPHA=0) + SPCR &= ~(_BV(DORD) | _BV(CPOL) | _BV(CPHA)); + + // clock divider: + // SPR1 and SPR0 are the two LSB bits in SPCR + // SPR1 SPR0 ~SPI2X Freq + // 0 0 0 fosc/2 + // 0 1 0 fosc/8 + // 1 0 0 fosc/32 + // 1 1 0 fosc/64 + // double speed mode: + // 0 0 1 fosc/4 + // 0 1 1 fosc/16 + // 1 0 1 fosc/64 + // 1 1 1 fosc/128 + + constexpr uint8_t SPI_CLOCK_MASK = 0x03; // SPR1 = bit 1, SPR0 = bit 0 on SPCR + constexpr uint8_t SPI_2XCLOCK_MASK = 0x01; // SPI2X = bit 0 on SPSR + SPCR = (SPCR & ~SPI_CLOCK_MASK) | (SPR1 & SPI_CLOCK_MASK); + SPSR = (SPSR & ~SPI_2XCLOCK_MASK); // clear SPI2X + + sei(); +} + +int ltc2400_conversion_ready() +{ + cs_low(); + // EOC == 0 means conversion is complete and device in sleep mode + const int eoc = (PORTB & PINB_SPI_MISO) ? 0 : 1; + cs_high(); + + return eoc; +} + +float ltc2400_get_conversion_result(bool& dmy_fault, bool& exr_fault) +{ + cs_low(); + + uint8_t data[4] = {}; + + for (int i = 0; i < 4; i++) { + SPDR = 0x0; // always output 0 + + while (!(SPSR & _BV(SPIF))) { /* wait */ } + data[i] = SPDR; + } + + cs_high(); + + const uint32_t result = + ((uint32_t)data[3] << 24) | + ((uint32_t)data[2] << 16) | + ((uint32_t)data[1] << 8) | + ((uint32_t)data[0]); + + dmy_fault = result & _BV(30); + exr_fault = not (result & _BV(28)); + + // Mask 4 MSB status bits, and shift out 4 sub-LSB bits + const uint32_t adc_value = (result >> 4) & 0x00FFFFFF; + + // Convert ADC value to voltage + return ((float)adc_value) / ((float)0x00FFFFFF) / 5.0f; +} + diff --git a/sw/ltc2400.h b/sw/ltc2400.h new file mode 100644 index 0000000..9a046b2 --- /dev/null +++ b/sw/ltc2400.h @@ -0,0 +1,36 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Matthias P. Braendli + * + * 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. +*/ + +#pragma once + +#include <stdlib.h> +#include <stdint.h> + +#include "pins.h" + +void ltc2400_init(); + +int ltc2400_conversion_ready(); + +float ltc2400_get_conversion_result(bool& dmy_fault, bool& exr_fault); diff --git a/sw/main.cpp b/sw/main.cpp index e849dea..c68ba33 100644 --- a/sw/main.cpp +++ b/sw/main.cpp @@ -33,57 +33,44 @@ #include <avr/eeprom.h> #include <avr/wdt.h> -#include "SPI.h" +#include "pins.h" +#include "ltc2400.h" extern "C" { #include "uart.h" } -// Definitions allocation pin -#define PIN(x) (1 << x) - // UART endline is usually CR LF #define ENDL "\r\n" -/* All relay signals PINx_Kx have external pulldown. - * All pins whose name ends in 'n' are active low */ - -// PORT B -constexpr uint8_t PINB_STATUSn = PIN(0); -// Pins 2,3,4,5 = SPI -constexpr uint8_t PINB_SPI_LTC_CSn = PIN(2); // with external pullup -constexpr uint8_t PINB_SPI_MOSI = PIN(3); -constexpr uint8_t PINB_SPI_MISO = PIN(4); -constexpr uint8_t PINB_SPI_SCK = PIN(5); - -constexpr uint8_t PINB_OUTPUTS = - PINB_STATUSn | PINB_SPI_SCK | PINB_SPI_MOSI | PINB_SPI_LTC_CSn; - -// PORT C -constexpr uint8_t PINC_ADC0 = PIN(0); -constexpr uint8_t PINC_ADC1 = PIN(1); -constexpr uint8_t PINC_K3_RESET = PIN(2); -constexpr uint8_t PINC_K3_SET = PIN(3); -constexpr uint8_t PINC_K2_RESET = PIN(4); -constexpr uint8_t PINC_K2_SET = PIN(5); - -constexpr uint8_t PINC_OUTPUTS = - PINC_K3_RESET | PINC_K3_SET | - PINC_K2_RESET | PINC_K2_SET; - -// PORT D -// Pins 0,1 = UART RX,TX -constexpr uint8_t PIND_UART_RX = PIN(0); -constexpr uint8_t PIND_UART_TX = PIN(1); +struct timer_t { + uint32_t seconds = 0; /* Timer in seconds */ + uint8_t ticks = 0; /* Timer in 100ms steps */ -constexpr uint8_t PIND_ONEWIRE = PIN(4); // with exteral pullup + bool operator>(const timer_t& rhs) { + return (seconds > rhs.seconds) or + (seconds == rhs.seconds and ticks > rhs.ticks); + } -constexpr uint8_t PIND_K1_RESET = PIN(5); -constexpr uint8_t PIND_K1_SET = PIN(6); + timer_t operator+(int ticks) { + timer_t t; + t.seconds = this->seconds; + t.ticks = this->ticks + ticks; + while (t.ticks >= 10) { + t.seconds++; + t.ticks -= 10; + } + return t; + } -constexpr uint8_t PIND_OUTPUTS = - PIND_UART_TX | - PIND_K1_RESET | PIND_K1_SET; + void operator+=(int ticks) { + this->ticks += ticks; + while (this->ticks >= 10) { + seconds++; + this->ticks -= 10; + } + } +}; /* Storage of battery capacity in mC. * 3600 mC = 1mAh */ @@ -94,11 +81,12 @@ uint32_t EEMEM stored_capacity2; uint32_t EEMEM stored_capacity3; uint32_t last_store_time; /* In seconds */ +timer_t last_ltc2400_measure; + uint32_t current_capacity; /* Timer at approximately 100ms */ -volatile uint8_t timer_counter; /* Timer in 100ms steps */ -volatile uint32_t timer_seconds; /* Timer in seconds */ +static timer_t system_timer; /* At reset, save the mcusr register to find out why we got reset. * Datasheet 11.9.1, example code from wdt.h */ @@ -106,11 +94,12 @@ uint8_t mcusr_mirror __attribute__ ((section (".noinit"))); ISR(TIMER0_COMPA_vect) { - timer_counter++; - - if (timer_counter >= 10) { - timer_seconds++; - timer_counter = 0; + if (system_timer.ticks == 9) { + system_timer.seconds++; + system_timer.ticks = 0; + } + else { + system_timer.ticks++; } } @@ -118,6 +107,8 @@ enum class error_type_t { EEPROM_READ_WARNING, EEPROM_READ_ERROR, EEPROM_WRITE_ERROR, + LTC2400_DMY_BIT_FAULT, + LTC2400_EXTENDED_RANGE_ERROR, }; static void flag_error(const error_type_t e); @@ -168,7 +159,7 @@ static void store_capacity_to_eeprom() static char timestamp_buf[16]; static void send_message(const char *message) { - snprintf(timestamp_buf, 15, "TEXT,%ld,", timer_seconds); + snprintf(timestamp_buf, 15, "TEXT,%ld,", system_timer.seconds); uart_puts(timestamp_buf); uart_puts(message); uart_puts_P(ENDL); @@ -176,7 +167,7 @@ static void send_message(const char *message) static void flag_error(const error_type_t e) { - snprintf(timestamp_buf, 15, "ERROR,%ld,", timer_seconds); + snprintf(timestamp_buf, 15, "ERROR,%ld,", system_timer.seconds); uart_puts(timestamp_buf); switch (e) { case error_type_t::EEPROM_READ_WARNING: @@ -188,6 +179,12 @@ static void flag_error(const error_type_t e) case error_type_t::EEPROM_WRITE_ERROR: uart_puts_P("EEPRON write error" ENDL); break; + case error_type_t::LTC2400_DMY_BIT_FAULT: + uart_puts_P("LTC2400 DMY bit error" ENDL); + break; + case error_type_t::LTC2400_EXTENDED_RANGE_ERROR: + uart_puts_P("LTC2400 extended range error" ENDL); + break; } } @@ -203,6 +200,7 @@ int main() /* Setup GPIO */ // Active-low outputs must be high + // PINB_SPI_SCK must be low (See ltc2400.h) PORTB = PINB_STATUSn | PINB_SPI_LTC_CSn; PORTC = 0; PORTD = 0; @@ -212,7 +210,11 @@ int main() DDRC = PINC_OUTPUTS; DDRD = PIND_OUTPUTS; - SPI.begin(); + // Initialise SPI and LTC2400 + ltc2400_init(); + + // Use the LDO on Vref as ADC reference, set REFS1..REFS0 = 0b00 + ADMUX &= ~(_BV(REFS0) | _BV(REFS1)); // Warning: Bi-stable relays are still in unknown state! @@ -243,8 +245,8 @@ int main() * interval [s] = overflow [ticks] / (F_CPU [ticks/s] / prescaler [unit-less]) * = 99.84 ms */ - timer_seconds = 0; - timer_counter = 0; + system_timer.seconds = 0; + system_timer.ticks = 0; TCCR0B |= (1 << WGM02); // Set timer mode to CTC (datasheet 15.7.2) TIMSK0 |= (1 << TOIE0); // enable overflow interrupt OCR0A = (uint8_t)(F_CPU / 1024 / 10); // Overflow at 99.84 ms @@ -252,7 +254,7 @@ int main() /* Load capacity stored in EEPROM */ load_capacity_from_eeprom(); - last_store_time = timer_seconds; + last_store_time = system_timer.seconds; /* Enable interrupts */ sei(); @@ -262,9 +264,28 @@ int main() while (true) { sleep_mode(); - if (last_store_time + 3600 * 5 >= timer_seconds) { + if (last_store_time + 3600 * 5 >= system_timer.seconds) { store_capacity_to_eeprom(); } + + if (last_ltc2400_measure + 100 > system_timer) { + last_ltc2400_measure += 100; + + if (ltc2400_conversion_ready()) { + bool dmy_fault = false; + bool exr_fault = false; + float adc_voltage = ltc2400_get_conversion_result(dmy_fault, exr_fault); +#error "convert to mAh and integrate" + + if (dmy_fault) { + flag_error(error_type_t::LTC2400_DMY_BIT_FAULT); + } + + if (exr_fault) { + flag_error(error_type_t::LTC2400_EXTENDED_RANGE_ERROR); + } + } + } } return 0; diff --git a/sw/pins.h b/sw/pins.h new file mode 100644 index 0000000..6dc75bc --- /dev/null +++ b/sw/pins.h @@ -0,0 +1,74 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Matthias P. Braendli + * + * 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. +*/ + +#pragma once + +#include <stdlib.h> +#include <stdint.h> + +// Definitions allocation pin +#define PIN(x) (1 << x) + +/* All relay signals PINx_Kx have external pulldown. + * All pins whose name ends in 'n' are active low */ + +// For arduino pin numbers (not used in this file), see +// lib/pins_arduino.h + +// PORT B +constexpr uint8_t PINB_STATUSn = PIN(0); +// Pins 2,3,4,5 = SPI +constexpr uint8_t PINB_SPI_LTC_CSn = PIN(2); // with external pullup +constexpr uint8_t PINB_SPI_MOSI = PIN(3); +constexpr uint8_t PINB_SPI_MISO = PIN(4); +constexpr uint8_t PINB_SPI_SCK = PIN(5); + +constexpr uint8_t PINB_OUTPUTS = + PINB_STATUSn | PINB_SPI_SCK | PINB_SPI_MOSI | PINB_SPI_LTC_CSn; + +// PORT C +constexpr uint8_t PINC_ADC0 = PIN(0); +constexpr uint8_t PINC_ADC1 = PIN(1); +constexpr uint8_t PINC_K3_RESET = PIN(2); +constexpr uint8_t PINC_K3_SET = PIN(3); +constexpr uint8_t PINC_K2_RESET = PIN(4); +constexpr uint8_t PINC_K2_SET = PIN(5); + +constexpr uint8_t PINC_OUTPUTS = + PINC_K3_RESET | PINC_K3_SET | + PINC_K2_RESET | PINC_K2_SET; + +// PORT D +// Pins 0,1 = UART RX,TX +constexpr uint8_t PIND_UART_RX = PIN(0); +constexpr uint8_t PIND_UART_TX = PIN(1); + +constexpr uint8_t PIND_ONEWIRE = PIN(4); // with exteral pullup + +constexpr uint8_t PIND_K1_RESET = PIN(5); +constexpr uint8_t PIND_K1_SET = PIN(6); + +constexpr uint8_t PIND_OUTPUTS = + PIND_UART_TX | + PIND_K1_RESET | PIND_K1_SET; |