LNSC-2420-Firmware/src/measurement.c
Thomas Kolb 49bd9247e0 Average the measurements
The measurement module now provides averaged measurements. These are
used in some places where accuracy is more important than latency (for
example for the temperature limit, where noise triggered the limit too
early).
2021-06-18 23:37:16 +02:00

240 lines
8.2 KiB
C

#include <libopencm3/stm32/adc.h>
#include <libopencm3/stm32/dma.h>
#include <fxp.h>
#include "libopencm3/stm32/f0/adc.h"
#include "pinout.h"
#include "measurement.h"
#include "calibration.h"
#include "config.h"
#define ADC_NUM_CHANNELS 6
static volatile int16_t adc_values[ADC_NUM_CHANNELS];
static fxp_t calibration_factors[ADC_NUM_CHANNELS-1]; // all except temperature; filled in measurement_init()
fxp_t avg_alpha_i_solar;
fxp_t avg_alpha_i_load;
fxp_t avg_alpha_u_bat;
fxp_t avg_alpha_u_sw;
fxp_t avg_alpha_u_solar;
fxp_t avg_alpha_temp;
fxp_t avg_alpha_i_solar_inv;
fxp_t avg_alpha_i_load_inv;
fxp_t avg_alpha_u_bat_inv;
fxp_t avg_alpha_u_sw_inv;
fxp_t avg_alpha_u_solar_inv;
fxp_t avg_alpha_temp_inv;
/* Temperature sensor calibration value address */
#define TEMP110_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FFFF7C2))
#define TEMP30_CAL_ADDR ((uint16_t*) ((uint32_t) 0x1FFFF7B8))
#define VDD_CALIB ((int32_t) (330)) /* calibration voltage = 3,30V - DO NOT CHANGE */
#define VDD_APPLI ((int32_t) (330)) /* actual supply voltage */
/* function for temperature conversion */
static fxp_t calc_temperature(uint16_t adc_val)
{
int32_t temperature_raw = ((int32_t)adc_val * VDD_APPLI / VDD_CALIB)
- (int32_t)(*TEMP30_CAL_ADDR);
fxp_t temperature = FXP_FROM_INT(temperature_raw);
fxp_t scale_dividend = FXP_FROM_INT(110 - 30);
fxp_t scale_divisor = FXP_FROM_INT((int32_t)(*TEMP110_CAL_ADDR - *TEMP30_CAL_ADDR));
fxp_t scale = fxp_div(scale_dividend, scale_divisor);
return fxp_add(fxp_mult(temperature, scale), FXP_FROM_INT(30));
}
static fxp_t adc_val_to_pin_voltage(uint16_t adc_val)
{
return fxp_div(
fxp_mult(FXP_FROM_INT(adc_val), fxp_div(FXP_FROM_INT(33), FXP_FROM_INT(10))),
FXP_FROM_INT(4096));
}
static fxp_t convert_voltage_divider(uint16_t adc_val, fxp_t r1, fxp_t r2, fxp_t cal)
{
fxp_t pin_voltage = adc_val_to_pin_voltage(adc_val);
fxp_t meas_voltage = fxp_mult(pin_voltage, fxp_div(fxp_add(r1, r2), r2));
return fxp_mult(meas_voltage, cal);
}
static fxp_t convert_ina139(uint16_t adc_val, fxp_t rshunt, fxp_t vgain, fxp_t cal)
{
fxp_t pin_voltage = adc_val_to_pin_voltage(adc_val);
fxp_t shunt_voltage = fxp_div(pin_voltage, vgain);
fxp_t current = fxp_div(shunt_voltage, rshunt);
return fxp_mult(current, cal);
}
void measurement_init(void)
{
uint8_t channels[ADC_NUM_CHANNELS] = {
ANALOG_INPUT_U_BAT, // U_Bat
ANALOG_INPUT_U_SOLAR, // U_Solar
ANALOG_INPUT_U_SW, // U_SW
ANALOG_INPUT_I_SOLAR, // I_Solar
ANALOG_INPUT_I_LOAD, // I_Load
ADC_CHANNEL_TEMP // Temperature sensor
};
// Convert calibration factors to fixed-point numbers for direct use
calibration_factors[ANALOG_INPUT_U_BAT] =
fxp_div(FXP_FROM_INT(CAL_FACTOR_U_BAT), FXP_FROM_INT(1000));
calibration_factors[ANALOG_INPUT_U_SOLAR] =
fxp_div(FXP_FROM_INT(CAL_FACTOR_U_SOLAR), FXP_FROM_INT(1000));
calibration_factors[ANALOG_INPUT_U_SW] =
fxp_div(FXP_FROM_INT(CAL_FACTOR_U_SW), FXP_FROM_INT(1000));
calibration_factors[ANALOG_INPUT_I_SOLAR] =
fxp_div(FXP_FROM_INT(CAL_FACTOR_I_SOLAR), FXP_FROM_INT(1000));
calibration_factors[ANALOG_INPUT_I_LOAD] =
fxp_div(FXP_FROM_INT(CAL_FACTOR_I_LOAD), FXP_FROM_INT(1000));
// Convert and precalculate coefficients for exponential averaging
avg_alpha_i_solar = fxp_div(FXP_FROM_INT(AVG_ALPHA_I_SOLAR), FXP_FROM_INT(1000));
avg_alpha_i_load = fxp_div(FXP_FROM_INT(AVG_ALPHA_I_LOAD), FXP_FROM_INT(1000));
avg_alpha_u_bat = fxp_div(FXP_FROM_INT(AVG_ALPHA_U_BAT), FXP_FROM_INT(1000));
avg_alpha_u_sw = fxp_div(FXP_FROM_INT(AVG_ALPHA_U_SW), FXP_FROM_INT(1000));
avg_alpha_u_solar = fxp_div(FXP_FROM_INT(AVG_ALPHA_U_SOLAR), FXP_FROM_INT(1000));
avg_alpha_temp = fxp_div(FXP_FROM_INT(AVG_ALPHA_TEMP), FXP_FROM_INT(1000));
// Inverse (1 - alpha) exponential averaging coefficients
avg_alpha_i_solar_inv = fxp_sub(FXP_FROM_INT(1), avg_alpha_i_solar);
avg_alpha_i_load_inv = fxp_sub(FXP_FROM_INT(1), avg_alpha_i_load);
avg_alpha_u_bat_inv = fxp_sub(FXP_FROM_INT(1), avg_alpha_u_bat);
avg_alpha_u_sw_inv = fxp_sub(FXP_FROM_INT(1), avg_alpha_u_sw);
avg_alpha_u_solar_inv = fxp_sub(FXP_FROM_INT(1), avg_alpha_u_solar);
avg_alpha_temp_inv = fxp_sub(FXP_FROM_INT(1), avg_alpha_temp);
// Prepare the ADC
adc_power_off(ADC1);
// enable the temperature sensor
adc_enable_temperature_sensor();
// configure ADC
//adc_enable_scan_mode(ADC1);
adc_set_clk_source(ADC1, ADC_CLKSOURCE_PCLK_DIV4); // -> 12 MHz @ 48 MHz ABP
adc_set_single_conversion_mode(ADC1);
adc_set_resolution(ADC1, ADC_RESOLUTION_12BIT);
adc_set_sample_time_on_all_channels(ADC1, ADC_SMPR_SMP_239DOT5);
adc_disable_external_trigger_regular(ADC1);
adc_set_right_aligned(ADC1);
adc_set_regular_sequence(ADC1, ADC_NUM_CHANNELS, channels);
adc_calibrate(ADC1);
// configure DMA for ADC
//nvic_enable_irq(NVIC_DMA1_STREAM5_IRQ);
dma_channel_reset(DMA1, DMA_CHANNEL1);
dma_set_priority(DMA1, DMA_CHANNEL1, DMA_CCR_PL_LOW);
dma_set_memory_size(DMA1, DMA_CHANNEL1, DMA_CCR_MSIZE_16BIT);
dma_set_peripheral_size(DMA1, DMA_CHANNEL1, DMA_CCR_PSIZE_16BIT);
dma_enable_memory_increment_mode(DMA1, DMA_CHANNEL1);
dma_enable_circular_mode(DMA1, DMA_CHANNEL1);
dma_set_read_from_peripheral(DMA1, DMA_CHANNEL1);
dma_set_peripheral_address(DMA1, DMA_CHANNEL1, (uint32_t) &ADC1_DR);
/* The array adc_values[] is filled with the waveform data to be output */
dma_set_memory_address(DMA1, DMA_CHANNEL1, (uint32_t) adc_values);
dma_set_number_of_data(DMA1, DMA_CHANNEL1, ADC_NUM_CHANNELS);
//dma_enable_transfer_complete_interrupt(DMA1, DMA_CHANNEL1);
dma_enable_channel(DMA1, DMA_CHANNEL1);
adc_enable_dma(ADC1);
adc_power_on(ADC1);
}
void measurement_start(void)
{
// start the ADC conversion sequency. The result will be transferred to RAM
// by the DMA.
adc_start_conversion_regular(ADC1);
}
void measurement_wait_for_completion(void)
{
// wait for DMA transfer to complete
while(!dma_get_interrupt_flag(DMA1, DMA_CHANNEL1, DMA_TCIF));
dma_clear_interrupt_flags(DMA1, DMA_CHANNEL1, DMA_TCIF);
}
void measurement_finalize(struct MeasurementResult *result)
{
result->u_bat = convert_voltage_divider(adc_values[ANALOG_INPUT_U_BAT],
FXP_FROM_INT(220),
FXP_FROM_INT(22),
calibration_factors[ANALOG_INPUT_U_BAT]);
result->u_solar = convert_voltage_divider(adc_values[ANALOG_INPUT_U_SOLAR],
FXP_FROM_INT(330),
FXP_FROM_INT(22),
calibration_factors[ANALOG_INPUT_U_SOLAR]);
result->u_sw = convert_voltage_divider(adc_values[ANALOG_INPUT_U_SW],
FXP_FROM_INT(1000),
FXP_FROM_INT(47),
calibration_factors[ANALOG_INPUT_U_SW]);
result->i_solar = convert_ina139(adc_values[ANALOG_INPUT_I_SOLAR],
fxp_div(FXP_FROM_INT(2), FXP_FROM_INT(1000)),
FXP_FROM_INT(56),
calibration_factors[ANALOG_INPUT_I_SOLAR]);
result->i_load = convert_ina139(adc_values[ANALOG_INPUT_I_LOAD],
fxp_div(FXP_FROM_INT(5), FXP_FROM_INT(1000)),
FXP_FROM_INT(56),
calibration_factors[ANALOG_INPUT_I_LOAD]);
result->temperature = calc_temperature(adc_values[5]);
/* calculate exponentially averaged values */
result->avg_u_bat =
fxp_add(
fxp_mult(avg_alpha_u_bat, result->u_bat),
fxp_mult(avg_alpha_u_bat_inv, result->avg_u_bat));
result->avg_u_solar =
fxp_add(
fxp_mult(avg_alpha_u_solar, result->u_solar),
fxp_mult(avg_alpha_u_solar_inv, result->avg_u_solar));
result->avg_u_sw =
fxp_add(
fxp_mult(avg_alpha_u_sw, result->u_sw),
fxp_mult(avg_alpha_u_sw_inv, result->avg_u_sw));
result->avg_i_solar =
fxp_add(
fxp_mult(avg_alpha_i_solar, result->i_solar),
fxp_mult(avg_alpha_i_solar_inv, result->avg_i_solar));
result->avg_i_load =
fxp_add(
fxp_mult(avg_alpha_i_load, result->i_load),
fxp_mult(avg_alpha_i_load_inv, result->avg_i_load));
result->avg_temperature =
fxp_add(
fxp_mult(avg_alpha_temp, result->temperature),
fxp_mult(avg_alpha_temp_inv, result->avg_temperature));
}