From 6bca5e6a1ca991bf1435eb3eefaf4d92cd61361a Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Sun, 17 Nov 2019 12:17:19 +0100 Subject: Add ADC measurements --- sw/README.rst | 8 +++--- sw/main.cpp | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 83 insertions(+), 10 deletions(-) (limited to 'sw') diff --git a/sw/README.rst b/sw/README.rst index 4446c26..dc59298 100644 --- a/sw/README.rst +++ b/sw/README.rst @@ -10,6 +10,8 @@ Au démarrage, avant de passer à la mesure régulière, le code doit: - Faire une vérification lecture après écriture et communiquer le problème - Mettre en place un watchdog - Initialiser UART (uniquement TX, on verra si on a besoin du RX plus tard) +- Configurer SPI pour le LTC2400 +- Initialiser entrées analogiques et mesurer tension batterie Protocole du port série ----------------------- @@ -23,6 +25,8 @@ secondes, une virgule, un champ de données, et termine par CR LF. | `TEXT` | Un message informatif | | `ERROR` | Erreur ou avertissement | | `CAPACITY` | Une valeur en mAh | +| `VBAT+` | Une valeur en mV | +| `VBAT-` | Une valeur en mV | +--------------------+-----------------------------------+ Par exemple: `TEXT,12,Startup\r\n` @@ -31,10 +35,6 @@ TODO ---- - Definir le comportement par defaut au démarrage, pas de glitch! -- 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/main.cpp b/sw/main.cpp index 6b7f0cf..3a6b63c 100644 --- a/sw/main.cpp +++ b/sw/main.cpp @@ -84,10 +84,32 @@ uint32_t EEMEM stored_capacity3; * be stored as timer_t, to save some space and simplify * the comparison. */ -uint32_t last_store_time_seconds; -uint32_t last_threshold_calculation_seconds; -uint32_t last_ltc2400_print_time_seconds; -timer_t last_ltc2400_measure; +static uint32_t last_store_time_seconds; +static uint32_t last_threshold_calculation_seconds; +static uint32_t last_ltc2400_print_time_seconds; +static timer_t last_ltc2400_measure; + +enum class adc_state_t { + IDLE, + PENDING_ADC0, + PENDING_ADC1, +}; + +static adc_state_t adc_state; +static uint32_t last_adc_measure_time_seconds; + +/* Raw values from the ADC. + * The ADC converts an analog input voltage to a 10-bit digital value through + * successive approximation. The minimum value represents GND and the maximum + * value represents the voltage on the AREF pin minus 1 LSB. + * (datasheet 24.2) + */ +const uint32_t V_REF_mV = 5000; + +#define ADC_VALUE_TO_MILLIVOLT(val) ((uint32_t)val * V_REF_mV) / (uint32_t)(1<<10) + +// Use the LDO on Vref as ADC reference, set REFS1..REFS0 = 0b00, and ADC input 0 +#define SET_ADMUX(input) ADMUX = _BV(REFS0) | _BV(REFS1) | input /* Timer at approximately 100ms. * @@ -243,6 +265,14 @@ static void send_capacity(uint32_t capacity, const timer_t& time) uart_puts(timestamp_buf); } +static void send_voltage(uint32_t millivolts, bool bat_plus, const timer_t& time) +{ + snprintf(timestamp_buf, 15, "VBAT%c,%ld,%ld" ENDL, + bat_plus ? '+' : '-', + time.get_seconds_atomic(), millivolts); + uart_puts(timestamp_buf); +} + static void flag_error(const error_type_t e) { snprintf(timestamp_buf, 15, "ERROR,%ld,", system_timer.get_seconds_atomic()); @@ -298,8 +328,9 @@ int main() // Initialise SPI and LTC2400 ltc2400_init(); - // Use the LDO on Vref as ADC reference, set REFS1..REFS0 = 0b00 - ADMUX &= ~(_BV(REFS0) | _BV(REFS1)); + // Enable ADC + ADCSRA |= _BV(ADEN); + SET_ADMUX(0); // Warning: Bi-stable relays are still in unknown state! @@ -330,8 +361,11 @@ int main() last_store_time_seconds = last_ltc2400_print_time_seconds = + last_adc_measure_time_seconds = last_threshold_calculation_seconds = system_timer.get_seconds_atomic(); + adc_state = adc_state_t::IDLE; + /* Enable timer and interrupts */ TCCR0B |= _BV(WGM02); // Set timer mode to CTC (datasheet 15.7.2) TIMSK0 |= _BV(TOIE0); // enable overflow interrupt @@ -402,6 +436,45 @@ int main() send_capacity(current_capacity, time_now); } + + // Input is divided by 4 by LM324. ADC is 10-bit, + // value 0 is GND, value (1<<10) is Vref + constexpr auto adc_interval_s = 20; + switch (adc_state) { + case adc_state_t::IDLE: + if (last_adc_measure_time_seconds + adc_interval_s > time_now.seconds_) { + last_adc_measure_time_seconds += adc_interval_s; + SET_ADMUX(0); + // Start ADC conversion + ADCSRA |= _BV(ADSC); + adc_state = adc_state_t::PENDING_ADC0; + } + break; + case adc_state_t::PENDING_ADC0: + // ADSC is cleared when the conversion finishes + if ((ADCSRA & ADSC) == 0) { + // BAT+ + const uint16_t adc_value_0 = ((uint16_t)ADCH << 8) | ADCL; + send_voltage(ADC_VALUE_TO_MILLIVOLT(adc_value_0) * 4, true, time_now); + SET_ADMUX(1); + // Start ADC conversion + ADCSRA |= _BV(ADSC); + + adc_state = adc_state_t::PENDING_ADC1; + } + break; + case adc_state_t::PENDING_ADC1: + // ADSC is cleared when the conversion finishes + if ((ADCSRA & ADSC) == 0) { + // BAT- + const uint16_t adc_value_1 = ((uint16_t)ADCH << 8) | ADCL; + adc_state = adc_state_t::IDLE; + + send_voltage(ADC_VALUE_TO_MILLIVOLT(adc_value_1) * 4, false, time_now); + } + break; + } + relays_handle(time_now); } -- cgit v1.2.3