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.
 
 
 
toaster-oven-bluepill/Core/Src/app_temp.c

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 -> 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]);
}