Pimp ADC measurements with voltage means
This commit is contained in:
parent
f5d7b0428d
commit
c339384cbe
4 changed files with 69 additions and 38 deletions
69
fw/adc.c
69
fw/adc.c
|
|
@ -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;
|
||||
|
||||
count = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
st.ovs_count = 0;
|
||||
for (int i=0; i<NCH; i++)
|
||||
adc_aggregate[i] = 0;
|
||||
st.adc_aggregate[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
29
fw/adc.h
29
fw/adc.h
|
|
@ -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__*/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue