diff options
author | jaseg <git@jaseg.net> | 2018-12-24 19:52:13 +0900 |
---|---|---|
committer | jaseg <git@jaseg.net> | 2018-12-24 19:55:28 +0900 |
commit | c339384cbede3ddf3e88d27229baefd1d42daa86 (patch) | |
tree | 1ddb1f7da2afb3ddfca0463cb5fb716e8bdd9c5d | |
parent | f5d7b0428db5257d62c080daa2757199c27ef784 (diff) | |
download | 8seg-c339384cbede3ddf3e88d27229baefd1d42daa86.tar.gz 8seg-c339384cbede3ddf3e88d27229baefd1d42daa86.tar.bz2 8seg-c339384cbede3ddf3e88d27229baefd1d42daa86.zip |
Pimp ADC measurements with voltage means
-rw-r--r-- | fw/adc.c | 69 | ||||
-rw-r--r-- | fw/adc.h | 29 | ||||
-rw-r--r-- | fw/global.h | 7 | ||||
-rw-r--r-- | fw/main.c | 2 |
4 files changed, 69 insertions, 38 deletions
@@ -18,24 +18,18 @@ #include "adc.h" #include <stdbool.h> +#include <stdlib.h> -enum adc_channels { - VREF_CH, - VMEAS_A, - VMEAS_B, - TEMP_CH, - NCH -}; - volatile uint16_t adc_buf[ADC_BUFSIZE]; -volatile struct adc_measurements adc_data = {0}; -enum adc_mode adc_mode = ADC_UNINITIALIZED; -int adc_oversampling = 0; +volatile struct adc_state adc_state = {0}; +#define st adc_state +volatile struct adc_measurements adc_data; static void adc_dma_init(int burstlen, bool enable_interrupt); static void adc_timer_init(int psc, int ivl); + void adc_configure_scope_mode(uint8_t channel_mask, int sampling_interval_ns) { /* The constant SAMPLE_FAST (0) when passed in as sampling_interval_ns is handled specially in that we turn the ADC to continuous mode to get the highest possible sampling rate. */ @@ -46,7 +40,7 @@ void adc_configure_scope_mode(uint8_t channel_mask, int sampling_interval_ns) { DMA1_Channel1->CCR &= ~DMA_CCR_EN; /* Enable channel */ /* keep track of current mode in global variable */ - adc_mode = ADC_SCOPE; + st.adc_mode = ADC_SCOPE; adc_dma_init(sizeof(adc_buf)/sizeof(adc_buf[0]), false); @@ -79,15 +73,21 @@ void adc_configure_scope_mode(uint8_t channel_mask, int sampling_interval_ns) { adc_timer_init(12/*250ns/tick*/, cycles); } -void adc_configure_monitor_mode(int oversampling) { +void adc_configure_monitor_mode(int oversampling, int ivl_us, int mean_aggregate_len) { /* First, disable trigger timer, DMA and ADC in case we're reconfiguring on the fly. */ TIM1->CR1 &= ~TIM_CR1_CEN; ADC1->CR &= ~ADC_CR_ADSTART; DMA1_Channel1->CCR &= ~DMA_CCR_EN; /* Enable channel */ /* keep track of current mode in global variable */ - adc_mode = ADC_MONITOR; - adc_oversampling = oversampling; + st.adc_mode = ADC_MONITOR; + + st.adc_oversampling = oversampling; + st.ovs_count = 0; + for (int i=0; i<NCH; i++) + st.adc_aggregate[i] = 0; + st.mean_aggregator[0] = st.mean_aggregator[1] = st.mean_aggregator[2] = 0; + st.mean_aggregate_ctr = 0; adc_dma_init(NCH, true); @@ -109,7 +109,7 @@ void adc_configure_monitor_mode(int oversampling) { ADC1->CR |= ADC_CR_ADEN; ADC1->CR |= ADC_CR_ADSTART; - adc_timer_init(SystemCoreClock/1000000/*1.0us/tick*/, 20/*us*/); + adc_timer_init(SystemCoreClock/1000000/*1.0us/tick*/, ivl_us); } static void adc_dma_init(int burstlen, bool enable_interrupt) { @@ -150,42 +150,49 @@ static void adc_timer_init(int psc, int ivl) { TIM1->CR1 = TIM_CR1_ARPE; /* And... go! */ TIM1->CR1 |= TIM_CR1_CEN; - } void DMA1_Channel1_IRQHandler(void) { - /* This interrupt takes either 1.2us or 13us. It can be pre-empted by the more timing-critical UART and LED timer - * interrupts. */ - static int count = 0; /* oversampling accumulator sample count */ - static uint32_t adc_aggregate[NCH] = {0}; /* oversampling accumulator */ - /* Clear the interrupt flag */ DMA1->IFCR |= DMA_IFCR_CGIF1; for (int i=0; i<NCH; i++) - adc_aggregate[i] += adc_buf[i]; + st.adc_aggregate[i] += adc_buf[i]; - if (++count == (1<<adc_oversampling)) { + if (++st.ovs_count == (1<<st.adc_oversampling)) { for (int i=0; i<NCH; i++) - adc_aggregate[i] >>= adc_oversampling; + st.adc_aggregate[i] >>= st.adc_oversampling; /* This has been copied from the code examples to section 12.9 ADC>"Temperature sensor and internal reference * voltage" in the reference manual with the extension that we actually measure the supply voltage instead of * hardcoding it. This is not strictly necessary since we're running off a bored little LDO but it's free and * the current supply voltage is a nice health value. */ - adc_data.adc_vcc_mv = (3300 * VREFINT_CAL)/(adc_aggregate[VREF_CH]); + adc_data.adc_vcc_mv = (3300 * VREFINT_CAL)/(st.adc_aggregate[VREF_CH]); - int64_t read = adc_aggregate[TEMP_CH] * 10 * 10000; + int64_t read = st.adc_aggregate[TEMP_CH] * 10 * 10000; int64_t vcc = adc_data.adc_vcc_mv; int64_t cal = TS_CAL1 * 10 * 10000; adc_data.adc_temp_celsius_tenths = 300 + ((read/4096 * vcc) - (cal/4096 * 3300))/43000; - adc_data.adc_vmeas_a_mv = (adc_aggregate[VMEAS_A]*13300L)/4096 * vcc / 3300; - adc_data.adc_vmeas_b_mv = (adc_aggregate[VMEAS_B]*13300L)/4096 * vcc / 3300; + const long vmeas_r_total = VMEAS_R_HIGH + VMEAS_R_LOW; + int a = adc_data.adc_vmeas_a_mv = (st.adc_aggregate[VMEAS_A]*vmeas_r_total)/4096 * vcc / VMEAS_R_LOW; + int b = adc_data.adc_vmeas_b_mv = (st.adc_aggregate[VMEAS_B]*vmeas_r_total)/4096 * vcc / VMEAS_R_LOW; + + st.mean_aggregator[0] += a; + st.mean_aggregator[1] += b; + st.mean_aggregator[2] += abs(b-a); + if (++st.mean_aggregate_ctr == st.mean_aggregate_len) { + adc_data.adc_mean_a_mv = st.mean_aggregator[0] / st.mean_aggregate_len; + adc_data.adc_mean_b_mv = st.mean_aggregator[1] / st.mean_aggregate_len; + adc_data.adc_mean_diff_mv = st.mean_aggregator[2] / st.mean_aggregate_len; + + st.mean_aggregate_ctr = 0; + st.mean_aggregator[0] = st.mean_aggregator[1] = st.mean_aggregator[2] = 0; + } - count = 0; + st.ovs_count = 0; for (int i=0; i<NCH; i++) - adc_aggregate[i] = 0; + st.adc_aggregate[i] = 0; } } @@ -25,6 +25,9 @@ struct adc_measurements { int16_t adc_temp_celsius_tenths; int16_t adc_vmeas_a_mv; int16_t adc_vmeas_b_mv; + int16_t adc_mean_a_mv; + int16_t adc_mean_b_mv; + int16_t adc_mean_diff_mv; }; enum channel_mask { @@ -42,14 +45,32 @@ enum sampling_mode { SAMPLE_FAST = 0 }; +/* The weird order is to match the channels' order in the DMA buffer. Due to some configuration mistake I can't be +bothered to fix the DMA controller outputs ADC measurements off-by-one into the output buffer. */ +enum adc_channels { + VREF_CH, + VMEAS_A, + VMEAS_B, + TEMP_CH, + NCH +}; -extern volatile struct adc_measurements adc_data; +struct adc_state { + enum adc_mode adc_mode; + int adc_oversampling; + int mean_aggregate_len; + int ovs_count; /* oversampling accumulator sample count */ + uint32_t adc_aggregate[NCH]; /* oversampling accumulator */ + uint32_t mean_aggregate_ctr; + uint32_t mean_aggregator[3]; +}; + +extern volatile struct adc_state adc_state; extern volatile uint16_t adc_buf[ADC_BUFSIZE]; -extern enum adc_mode adc_mode; -extern int adc_oversampling; +extern volatile struct adc_measurements adc_data; void adc_init(void); void adc_configure_scope_mode(uint8_t channel_mask, int sampling_interval_ns); -void adc_configure_monitor_mode(int oversampling); +void adc_configure_monitor_mode(int oversampling, int ivl_us, int mean_aggregate_len); #endif/*__ADC_H__*/ diff --git a/fw/global.h b/fw/global.h index 03d3920..15a5e87 100644 --- a/fw/global.h +++ b/fw/global.h @@ -36,12 +36,15 @@ /* Microcontroller part number: STM32F030F4C6 */ /* Things used for module status reporting. */ -#define FIRMWARE_VERSION 2 -#define HARDWARE_VERSION 4 +#define FIRMWARE_VERSION 1 +#define HARDWARE_VERSION 0 #define TS_CAL1 (*(uint16_t *)0x1FFFF7B8) #define VREFINT_CAL (*(uint16_t *)0x1FFFF7BA) +#define VMEAS_R_HIGH 10000 /* kiloohms */ +#define VMEAS_R_LOW 3300 /* kiloohms */ + extern volatile unsigned int sys_time; extern volatile unsigned int sys_time_seconds; @@ -69,7 +69,7 @@ int main(void) { } set_outputs(0); - adc_configure_monitor_mode(0 /*no oversampling*/); + adc_configure_monitor_mode(0 /*no oversampling*/, 20 /*us*/, 10000/20 /*mean window size*/); uint8_t out_state = 0x01; #define DEBOUNCE 100 |