You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
359 lines
9.9 KiB
359 lines
9.9 KiB
/**
|
|
* TODO file description
|
|
*/
|
|
|
|
#include "main.h"
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include "app_temp.h"
|
|
#include "adc.h"
|
|
#include "snprintf.h"
|
|
#include "app_safety.h"
|
|
#include "eeprom_emul_types.h"
|
|
#include "eeprom_emul.h"
|
|
#include "ee_addresses.h"
|
|
#include "transmute.h"
|
|
|
|
/* DMA dest */
|
|
static volatile uint16_t adc_values[4];
|
|
|
|
const float V_REFINT = 1.23f;
|
|
|
|
#define AVERAGEBUF_DEPTH 16
|
|
#define OVENTEMP_HISTORY_DEPTH 20
|
|
|
|
static struct App {
|
|
float oven_temp;
|
|
float oven_temp_raw;
|
|
float soc_temp;
|
|
float cal_a;
|
|
float cal_b;
|
|
float oventemp_history[OVENTEMP_HISTORY_DEPTH]; // raw temp
|
|
uint16_t adc_averagebuf[AVERAGEBUF_DEPTH * 4];
|
|
uint8_t averagebuf_ptr;
|
|
uint8_t oventemp_history_ptr;
|
|
} s_analog = {
|
|
// Ax + B = y ... X = raw sample (ratio 0-1 of 3.3), Y = corrected sample
|
|
.cal_a = 1.0f, // safe default calibration constants
|
|
.cal_b = 0.0f,
|
|
};
|
|
|
|
#define TSENSE_LOOKUP_LEN 101
|
|
#define TSENSE_T_STEP 5.0f
|
|
#define TSENSE_T_MIN 0.0f
|
|
#define TSENSE_T_MAX 500.0f
|
|
static const float TSENSE_LOOKUP[TSENSE_LOOKUP_LEN] = {
|
|
0.0909090909090909f,
|
|
0.0925200328471449f,
|
|
0.0941228958173389f,
|
|
0.0957178169362106f,
|
|
0.0973046872002769f,
|
|
0.0988837241910161f,
|
|
0.100454819038946f,
|
|
0.102018187184848f,
|
|
0.103573800259031f,
|
|
0.105121710606384f,
|
|
0.106661970090864f,
|
|
0.108194709632656f,
|
|
0.109719820815089f,
|
|
0.111237512887056f,
|
|
0.112747677594865f,
|
|
0.114250522193605f,
|
|
0.115746016789507f,
|
|
0.117234210034522f,
|
|
0.118715150141415f,
|
|
0.120188962295434f,
|
|
0.121655538774297f,
|
|
0.123115081061434f,
|
|
0.124567481621389f,
|
|
0.126012940077612f,
|
|
0.127451425220842f,
|
|
0.128882982328346f,
|
|
0.13030765627567f,
|
|
0.131725566931108f,
|
|
0.133136607355006f,
|
|
0.134540971782401f,
|
|
0.135938553479917f,
|
|
0.137329544944786f,
|
|
0.138713913835562f,
|
|
0.140091702340679f,
|
|
0.141462952280536f,
|
|
0.142827778585901f,
|
|
0.144186075171443f,
|
|
0.145538029496198f,
|
|
0.146883535696824f,
|
|
0.148222779606307f,
|
|
0.149555727912261f,
|
|
0.150882419971517f,
|
|
0.152202894803216f,
|
|
0.153517262745702f,
|
|
0.154825418625535f,
|
|
0.156127543558165f,
|
|
0.157423532604634f,
|
|
0.158713565356767f,
|
|
0.159997607673187f,
|
|
0.161275696311014f,
|
|
0.162547867717066f,
|
|
0.163814227951767f,
|
|
0.165074672799026f,
|
|
0.166329377427284f,
|
|
0.167578237864685f,
|
|
0.168821427850084f,
|
|
0.170058912538152f,
|
|
0.171290726295724f,
|
|
0.172516903204089f,
|
|
0.17373754533289f,
|
|
0.174952549458501f,
|
|
0.176162085166716f,
|
|
0.177366049485545f,
|
|
0.178564610657697f,
|
|
0.179757733244091f,
|
|
0.180945449410727f,
|
|
0.182127791060477f,
|
|
0.183304856534839f,
|
|
0.184476543628915f,
|
|
0.185643016681207f,
|
|
0.186804173743842f,
|
|
0.18796017789194f,
|
|
0.18911099318983f,
|
|
0.190256649774341f,
|
|
0.191397177539504f,
|
|
0.192532671339319f,
|
|
0.193663030006298f,
|
|
0.194788412940845f,
|
|
0.195908719236171f,
|
|
0.197024107102852f,
|
|
0.198134540194308f,
|
|
0.19924004677399f,
|
|
0.200340654881021f,
|
|
0.201436456102762f,
|
|
0.202527350321362f,
|
|
0.203613492284647f,
|
|
0.204694782137667f,
|
|
0.205771373506423f,
|
|
0.206843229708988f,
|
|
0.20791037727704f,
|
|
0.208972842534733f,
|
|
0.210030714005839f,
|
|
0.211083892628833f,
|
|
0.212132528763072f,
|
|
0.213176523610881f,
|
|
0.214216026472748f,
|
|
0.215251000398025f,
|
|
0.216281470315524f,
|
|
0.21730746096184f,
|
|
0.218329057984116f,
|
|
0.219346163379138f,
|
|
};
|
|
|
|
/// if the calibration constants are zero, reset to defaults
|
|
static void correct_invalid_calib() {
|
|
if (s_analog.cal_a == 0.0f || s_analog.cal_b == 0.0f) {
|
|
PRINTF("ADC invalid calib, reset\r\n");
|
|
s_analog.cal_a = 1.0f;
|
|
s_analog.cal_b = 0.0f;
|
|
}
|
|
}
|
|
|
|
/// Set and persist calibration constants
|
|
void app_temp_set_calib(float a, float b) {
|
|
s_analog.cal_a = a;
|
|
s_analog.cal_b = b;
|
|
correct_invalid_calib();
|
|
|
|
EE_Status st = EE_WriteVariable32bits(EE_ADDR_CAL_A, ((x32_t) { .f = a }).u);
|
|
if (st == EE_CLEANUP_REQUIRED) {
|
|
EE_CleanUp();
|
|
} else if (st != EE_OK) {
|
|
PRINTF("EE write err %d!\r\n", st);
|
|
}
|
|
st = EE_WriteVariable32bits(EE_ADDR_CAL_B, ((x32_t) { .f = b }).u);
|
|
if (st == EE_CLEANUP_REQUIRED) {
|
|
EE_CleanUp();
|
|
} else if (st != EE_OK) {
|
|
PRINTF("EE write err %d!\r\n", st);
|
|
}
|
|
}
|
|
|
|
void app_analog_init()
|
|
{
|
|
// read calibration constants
|
|
uint32_t c = 0;
|
|
if (EE_OK == EE_ReadVariable32bits(EE_ADDR_CAL_A, &c)) {
|
|
s_analog.cal_a = ((x32_t) { .u = c }).f;
|
|
PRINTF("ADC calib a read from EE: %f\r\n", s_analog.cal_a);
|
|
}
|
|
if (EE_OK == EE_ReadVariable32bits(EE_ADDR_CAL_B, &c)) {
|
|
s_analog.cal_b = ((x32_t) { .u = c }).f;
|
|
PRINTF("ADC calib b read from EE: %f\r\n", s_analog.cal_b);
|
|
}
|
|
|
|
correct_invalid_calib();
|
|
|
|
LL_ADC_Enable(ADC_TEMP);
|
|
|
|
LL_ADC_StartCalibration(ADC_TEMP);
|
|
while (LL_ADC_IsCalibrationOnGoing(ADC_TEMP)) {}
|
|
|
|
LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_1,
|
|
LL_ADC_DMA_GetRegAddr(ADC1, LL_ADC_DMA_REG_REGULAR_DATA),
|
|
(uint32_t) (void *) adc_values,
|
|
LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
|
|
|
|
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, 4);
|
|
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
|
|
|
|
LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1);
|
|
|
|
LL_ADC_REG_StartConversionExtTrig(ADC_TEMP, LL_ADC_REG_TRIG_EXT_RISING);
|
|
// LL_ADC_REG_StartConversionSWStart(ADC_TEMP);
|
|
|
|
// this timer runs with 1 kHz clock
|
|
LL_TIM_SetTriggerOutput(TIM1, LL_TIM_TRGO_CC1IF);
|
|
LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH1);
|
|
LL_TIM_EnableAllOutputs(TIM1); // TIM1 needs outputs specially enabled - it's "advanced timer"
|
|
LL_TIM_EnableCounter(TIM1);
|
|
}
|
|
|
|
float val_to_c(float val)
|
|
{
|
|
// TODO use binary search.. lol
|
|
for (int i = 1; i < TSENSE_LOOKUP_LEN; i++) {
|
|
float cur = TSENSE_LOOKUP[i];
|
|
if (cur >= val) {
|
|
float prev = TSENSE_LOOKUP[i - 1];
|
|
|
|
float ratio = (val - prev) / (cur - prev);
|
|
return TSENSE_T_MIN + ((float) i + ratio) * TSENSE_T_STEP;
|
|
}
|
|
}
|
|
return TSENSE_T_MAX;
|
|
}
|
|
|
|
float c_to_val(float cf)
|
|
{
|
|
int lower = (int) (cf / TSENSE_T_STEP);
|
|
|
|
if (lower < 0) {
|
|
lower = 0;
|
|
}
|
|
if (lower >= TSENSE_LOOKUP_LEN - 1) {
|
|
lower = TSENSE_LOOKUP_LEN - 2;
|
|
}
|
|
int upper = lower + 1;
|
|
|
|
float ratio = (cf - ((float)lower * TSENSE_T_STEP)) / 5.0f;
|
|
return TSENSE_LOOKUP[lower] + (TSENSE_LOOKUP[upper] - TSENSE_LOOKUP[lower]) * ratio;
|
|
}
|
|
|
|
void app_temp_sample()
|
|
{
|
|
uint32_t sums[4] = {};
|
|
int count = 0;
|
|
for (int i = 0; i < AVERAGEBUF_DEPTH * 4; i += 4) {
|
|
if (s_analog.adc_averagebuf[i + 3] != 0) {
|
|
sums[0] += s_analog.adc_averagebuf[i];
|
|
sums[1] += s_analog.adc_averagebuf[i + 1];
|
|
sums[2] += s_analog.adc_averagebuf[i + 2];
|
|
sums[3] += s_analog.adc_averagebuf[i + 3];
|
|
count++;
|
|
}
|
|
}
|
|
if (count == 0) {
|
|
return;
|
|
}
|
|
|
|
float adc_averages[4];
|
|
|
|
adc_averages[0] = (float) sums[0] / count;
|
|
adc_averages[1] = (float) sums[1] / count;
|
|
adc_averages[2] = (float) sums[2] / count;
|
|
adc_averages[3] = (float) sums[3] / count;
|
|
|
|
// PRINTF("%f\t%f\t%f\t%f\r\n",
|
|
// adc_averages[0],
|
|
// adc_averages[1],
|
|
// adc_averages[2],
|
|
// adc_averages[3]
|
|
// );
|
|
|
|
/* r_pt100, r_ref, internal_temp, v_ref_int */
|
|
float refint = adc_averages[3];
|
|
float scale = V_REFINT / refint;
|
|
|
|
const float avg_slope = 4.3f * scale;
|
|
const float v25 = 1.43f;
|
|
const float v_tsen = adc_averages[2] * scale;
|
|
|
|
s_analog.soc_temp = (v25 - v_tsen) / avg_slope + 25.f;
|
|
//s_analog.v_sensor = adc_averages[0] * scale; // good for debug/tuning
|
|
|
|
|
|
// using a voltage divider, so assuming the reference resistor is measured well,
|
|
// we can just use the ratio and the exact voltage value is not important.
|
|
|
|
float oventemp_sample = adc_averages[0] / adc_averages[1];
|
|
|
|
s_analog.oventemp_history[s_analog.oventemp_history_ptr] = oventemp_sample;
|
|
s_analog.oventemp_history_ptr = (s_analog.oventemp_history_ptr + 1) % OVENTEMP_HISTORY_DEPTH;
|
|
|
|
float sum = 0;
|
|
int depth = 0;
|
|
for (int i = 0; i < OVENTEMP_HISTORY_DEPTH; i++) {
|
|
if (s_analog.oventemp_history[i] > 0.0f) {
|
|
sum += s_analog.oventemp_history[i];
|
|
depth++;
|
|
}
|
|
}
|
|
if (depth > 0) {
|
|
sum /= depth;
|
|
}
|
|
|
|
float y = s_analog.cal_a * sum + s_analog.cal_b;
|
|
float actual_temp = val_to_c(y);
|
|
PRINTF("T raw %f %f -> comp %f, temp %f°C\r\n", sum, y, actual_temp);
|
|
|
|
s_analog.oven_temp = actual_temp;
|
|
s_analog.oven_temp_raw = sum;
|
|
|
|
app_safety_pass_temp_calculation();
|
|
|
|
if (s_analog.oven_temp_raw >= 0.05 && s_analog.oven_temp_raw <= 0.22) {
|
|
app_safety_pass_temp_normal();
|
|
}
|
|
|
|
if (s_analog.soc_temp >= 2.0 && s_analog.soc_temp <= 80.0) {
|
|
app_safety_pass_soc_temp_ok();
|
|
}
|
|
}
|
|
|
|
float app_temp_read_oven()
|
|
{
|
|
return s_analog.oven_temp;
|
|
}
|
|
|
|
float app_temp_read_oven_raw()
|
|
{
|
|
return s_analog.oven_temp_raw;
|
|
}
|
|
|
|
float app_temp_read_soc()
|
|
{
|
|
return s_analog.soc_temp;
|
|
}
|
|
|
|
void app_temp_adc_eos()
|
|
{
|
|
app_safety_pass_adc_sampling();
|
|
|
|
// notify
|
|
memcpy((void *) &s_analog.adc_averagebuf[s_analog.averagebuf_ptr * 4], (const void *) adc_values, 4 * sizeof(uint16_t));
|
|
s_analog.averagebuf_ptr = (s_analog.averagebuf_ptr + 1) % AVERAGEBUF_DEPTH;
|
|
}
|
|
|
|
void app_temp_show_buf()
|
|
{
|
|
PRINTF("%d,%d,%d,%d\r\n", adc_values[0], adc_values[1], adc_values[2], adc_values[3]);
|
|
}
|
|
|