/** * TODO file description */ #include "main.h" #include #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]); }