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

546 lines
14 KiB

/**
* TODO file description
*/
#include "main.h"
#include <string.h>
#include "app_temp.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 4
#define OVENTEMP_HISTORY_DEPTH 4
static struct App {
float oven_temp;
float oven_temp_raw;
float soc_temp;
float cal_a;
float cal_b;
float reference_resistor;
float lead_resistance;
// original values are copied here during calibration, so they can be reverted
float saved_cal_a;
float saved_cal_b;
float saved_lead_resistance;
float saved_reference_resistor;
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 = {
.reference_resistor = 1000.0f,
.lead_resistance = 0.0f,
// 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
#if 0 // Ratios
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,
};
#else
static const float PT100_LOOKUP[TSENSE_LOOKUP_LEN] = {
100.f, // 0 C
101.9527f,
103.9025f,
105.8495f,
107.7935f,
109.7347f,
111.6729f,
113.6083f,
115.5408f,
117.4704f,
119.3971f,
121.321f,
123.2419f,
125.16f,
127.0751f,
128.9874f,
130.8968f,
132.8033f,
134.7069f,
136.6077f,
138.5055f,
140.4005f,
142.2925f,
144.1817f,
146.068f,
147.9514f,
149.8319f,
151.7096f,
153.5843f,
155.4562f,
157.3251f,
159.1912f,
161.0544f,
162.9147f,
164.7721f,
166.6267f,
168.4783f,
170.3271f,
172.1729f,
174.0159f,
175.856f,
177.6932f,
179.5275f,
181.359f,
183.1875f,
185.0132f,
186.8359f,
188.6558f,
190.4728f,
192.2869f,
194.0981f,
195.9065f,
197.7119f,
199.5145f,
201.3141f,
203.1109f,
204.9048f,
206.6958f,
208.4839f,
210.2692f,
212.0515f,
213.831f,
215.6075f,
217.3812f,
219.152f,
220.9199f,
222.6849f,
224.4471f,
226.2063f,
227.9627f,
229.7161f,
231.4667f,
233.2144f,
234.9592f,
236.7011f,
238.4402f,
240.1763f,
241.9096f,
243.6399f,
245.3674f,
247.092f,
248.8137f,
250.5325f,
252.2485f,
253.9615f,
255.6717f,
257.3789f,
259.0833f,
260.7848f,
262.4834f,
264.1791f,
265.872f,
267.5619f,
269.249f,
270.9331f,
272.6144f,
274.2928f,
275.9683f,
277.6409f,
279.3107f,
280.9775f, // 500 C
};
#endif
void app_temp_get_calib(float *a, float *b, float *l, float *r) {
*a = s_analog.cal_a;
*b = s_analog.cal_b;
*l = s_analog.lead_resistance;
*r = s_analog.reference_resistor;
}
/// 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;
}
if (s_analog.reference_resistor == 0.0f) {
s_analog.reference_resistor = 1000.0f;
}
// 0 lead_r is a lie, but ok
// if (s_analog.lead_resistance == 0.0f) {
// s_analog.lead_resistance = 0.0f;
// }
}
/// Set and persist calibration constants
void app_temp_set_calib_persistent(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_temp_set_calib_persistent_r(float lead, float reference) {
s_analog.lead_resistance = lead;
s_analog.reference_resistor = reference;
correct_invalid_calib();
EE_Status st = EE_WriteVariable32bits(EE_ADDR_LEAD_R, ((x32_t) { .f = lead }).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_REF_R, ((x32_t) { .f = reference }).u);
if (st == EE_CLEANUP_REQUIRED) {
EE_CleanUp();
} else if (st != EE_OK) {
PRINTF("EE write err %d!\r\n", st);
}
}
void app_temp_backup_calib() {
s_analog.saved_cal_a = s_analog.cal_a;
s_analog.saved_cal_b = s_analog.cal_b;
s_analog.saved_lead_resistance = s_analog.lead_resistance;
s_analog.saved_reference_resistor = s_analog.reference_resistor;
}
void app_temp_set_calib_temporary(float a, float b) {
s_analog.cal_a = a;
s_analog.cal_b = b;
}
void app_temp_set_calib_temporary_r(float l, float r) {
s_analog.lead_resistance = l;
s_analog.reference_resistor = r;
}
void app_temp_restore_calib() {
s_analog.cal_a = s_analog.saved_cal_a;
s_analog.cal_b = s_analog.saved_cal_b;
s_analog.lead_resistance = s_analog.saved_lead_resistance;
s_analog.reference_resistor = s_analog.saved_reference_resistor;
}
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);
}
if (EE_OK == EE_ReadVariable32bits(EE_ADDR_LEAD_R, &c)) {
s_analog.lead_resistance = ((x32_t) { .u = c }).f;
PRINTF("ADC calib R_LEAD read from EE: %f\r\n", s_analog.lead_resistance);
}
if (EE_OK == EE_ReadVariable32bits(EE_ADDR_REF_R, &c)) {
s_analog.reference_resistor = ((x32_t) { .u = c }).f;
PRINTF("ADC calib R_REF read from EE: %f\r\n", s_analog.reference_resistor);
}
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 x)
{
// val is the ratio
float pt100r = (x * s_analog.lead_resistance + x * s_analog.reference_resistor - s_analog.lead_resistance) / (1 - x);
// TODO use binary search.. lol
for (int i = 1; i < TSENSE_LOOKUP_LEN; i++) {
float cur = PT100_LOOKUP[i];
if (cur >= pt100r) {
float prev = PT100_LOOKUP[i - 1];
float ratio = (pt100r - 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 PT100_LOOKUP[lower] + (PT100_LOOKUP[upper] - PT100_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 y = sum;
float actual_temp = val_to_c(y);
PRINTF("T Raw[%f] * A[%f] + B[%f]-> %f, temp %f°C\r\n", sum, s_analog.cal_a, s_analog.cal_b, 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 <= 90.0) {
app_safety_pass_soc_temp_ok();
} else {
PRINTF("SOC OVERHEAT!! %f\r\n", s_analog.soc_temp);
}
}
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]);
}