aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2019-10-25 09:45:54 +0200
committerMatthias P. Braendli <matthias.braendli@mpb.li>2019-10-25 09:45:54 +0200
commitf6c2c762afc1d527cf40f8925ecf22b8b5845b43 (patch)
tree3a7494db19550b80a2d2b76cdc8a3e23dfd276b3
parentf3a56d229e2c3d333d53b9e31d5ca49afc3a11fe (diff)
downloadglutte-batteries-f6c2c762afc1d527cf40f8925ecf22b8b5845b43.tar.gz
glutte-batteries-f6c2c762afc1d527cf40f8925ecf22b8b5845b43.tar.bz2
glutte-batteries-f6c2c762afc1d527cf40f8925ecf22b8b5845b43.zip
Add new LTC2400 library
-rw-r--r--sw/Makefile11
-rw-r--r--sw/README.rst1
-rw-r--r--sw/lib/SPI.h2
-rw-r--r--sw/ltc2400.cpp154
-rw-r--r--sw/ltc2400.h36
-rw-r--r--sw/main.cpp129
-rw-r--r--sw/pins.h74
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;