/** * Main task */ #include #include #include "FreeRTOS.h" #include "task.h" #include "main.h" #include "app.h" #include "ufb/framebuffer.h" #include "iwdg.h" #include "tim.h" #include "adc.h" #include "oled.h" #include "ufb/fb_text.h" /* DMA dest */ static volatile uint16_t adc_values[4]; const float V_REFINT = 1.23f; #define AVERAGEBUF_DEPTH 16 #define OVENTEMP_HISTORY_DEPTH 10 static struct App { bool heating; int16_t set_temp; int16_t wheel_normed; float oven_temp; float soc_temp; float v_sensor; uint16_t wheel; bool push; uint16_t adc_averagebuf[AVERAGEBUF_DEPTH * 4]; uint8_t averagebuf_ptr; float adc_averages[4]; float oventemp_history[OVENTEMP_HISTORY_DEPTH]; uint8_t oventemp_history_ptr; } s_app = {}; #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.092678405931418f, 0.0943174479327356f, 0.095948157844312f, 0.0975706768542549f, 0.0991848957506647f, 0.100791037522732f, 0.102388993070241f, 0.103978983136042f, 0.105560980458654f, 0.107135039851509f, 0.108701215616829f, 0.110259642413441f, 0.111810211533421f, 0.113353137226489f, 0.114888310929339f, 0.11641594480226f, 0.117936009906507f, 0.119448557132363f, 0.120953636903929f, 0.122451377845456f, 0.12394167187544f, 0.125424725109556f, 0.126900429638119f, 0.128368989630084f, 0.129830374697352f, 0.131284632150064f, 0.132731808872517f, 0.134172027901771f, 0.135605181883591f, 0.13703146935069f, 0.138450783142958f, 0.139863319976468f, 0.14126904821384f, 0.142668011892657f, 0.144060254660872f, 0.145445894373796f, 0.146824824486877f, 0.14819723645253f, 0.149563023938454f, 0.150922376699229f, 0.15227526202401f, 0.153621720954182f, 0.15496179417407f, 0.156295594725426f, 0.157623016940038f, 0.158944245649448f, 0.160259175412251f, 0.16156798947087f, 0.162870654195634f, 0.164167207880495f, 0.165457688491696f, 0.166742204592451f, 0.168020651444079f, 0.169293207677971f, 0.170559768793747f, 0.171820511933356f, 0.173075402684405f, 0.174324476817747f, 0.175567769803026f, 0.176805386030345f, 0.178037221732226f, 0.179263449725904f, 0.180483966491086f, 0.181698943447122f, 0.182908345518766f, 0.184112206156428f, 0.185310558533273f, 0.186503503145257f, 0.187690937227925f, 0.188873028139146f, 0.190049673368296f, 0.191221038959601f, 0.192387089280576f, 0.193547855644572f, 0.194703369109397f, 0.195853726532112f, 0.196998826174689f, 0.19813883026229f, 0.199273637315452f, 0.200403408323351f, 0.201528107189346f, 0.20264776325594f, 0.203762405629782f, 0.204872127762998f, 0.205976828960191f, 0.207076666615101f, 0.208171540293999f, 0.209261606226334f, 0.210346827933364f, 0.211427232937629f, 0.212502848543705f, 0.213573765013592f, 0.214639882704581f, 0.215701354457324f, 0.216758080892489f, 0.217810213752734f, 0.218857716249547f, 0.219900614222686f, 0.220938933310224f, 0.221972760781578f, 0.223001998051553f, }; static 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; } void calculate_analog_values() { uint32_t sums[4] = {}; for (int i = 0; i < AVERAGEBUF_DEPTH * 4; i += 4) { sums[0] += s_app.adc_averagebuf[i]; sums[1] += s_app.adc_averagebuf[i + 1]; sums[2] += s_app.adc_averagebuf[i + 2]; sums[3] += s_app.adc_averagebuf[i + 3]; } s_app.adc_averages[0] = (float) sums[0] / AVERAGEBUF_DEPTH; s_app.adc_averages[1] = (float) sums[1] / AVERAGEBUF_DEPTH; s_app.adc_averages[2] = (float) sums[2] / AVERAGEBUF_DEPTH; s_app.adc_averages[3] = (float) sums[3] / AVERAGEBUF_DEPTH; /* r_pt100, r_ref, internal_temp, v_ref_int */ float refint = s_app.adc_averages[3]; float scale = V_REFINT / refint; const float avg_slope = 4.3f * scale; const float v25 = 1.43f; const float v_tsen = s_app.adc_averages[2] * scale; s_app.soc_temp = (v25 - v_tsen) / avg_slope + 25.f; s_app.v_sensor = s_app.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 actual_temp = val_to_c(s_app.adc_averages[0] / s_app.adc_averages[1]); s_app.oventemp_history[s_app.oventemp_history_ptr] = actual_temp; s_app.oventemp_history_ptr = (s_app.oventemp_history_ptr + 1) % OVENTEMP_HISTORY_DEPTH; float sum = 0; for (int i = 0; i < OVENTEMP_HISTORY_DEPTH; i++) { sum += s_app.oventemp_history[i]; } sum /= OVENTEMP_HISTORY_DEPTH; s_app.oven_temp = sum; } void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) { // notify memcpy((void *) &s_app.adc_averagebuf[s_app.averagebuf_ptr * 4], (const void *) adc_values, 8); s_app.averagebuf_ptr = (s_app.averagebuf_ptr + 1) % AVERAGEBUF_DEPTH; } static void hw_init() { HAL_ADCEx_Calibration_Start(&hadc1); /* Start periodic reading of the ADC channels */ HAL_ADC_Start_DMA(&hadc1, (uint32_t *) (void *) adc_values, 4); /* Enable the rotary encoder */ HAL_TIM_Encoder_Start(&htim4, TIM_CHANNEL_ALL); /* Enable buzzer PWM */ HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); /* Prepare the framebuffer and OLED interface */ oled_init(); fb_clear(); } void app_main_task(void *argument) { hw_init(); /* Infinite loop */ for (;;) { //HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); // printf("Knob %d (P=%d), ADC %.2f %.2f %.2f %.2f, oven %.2f°C, soc %.2f°C, divider %.3f V \r\n", // (int) s_app.wheel, s_app.push, // s_app.adc_averages[0], s_app.adc_averages[1], s_app.adc_averages[2], s_app.adc_averages[3], // // s_app.oven_temp, // s_app.soc_temp, // s_app.v_sensor // ); calculate_analog_values(); for (int i = 0; i < 50; i++) { uint16_t old_wheel = s_app.wheel; s_app.wheel = htim4.Instance->CNT; int16_t wheel_change = (int16_t)(s_app.wheel - old_wheel); if (wheel_change != 0) { s_app.wheel_normed += wheel_change; if (s_app.wheel_normed < 0) { s_app.wheel_normed = 0; } if (s_app.wheel_normed > 500) { s_app.wheel_normed = 500; } s_app.set_temp = (s_app.wheel_normed / 2) * 5; } s_app.push = 0 == HAL_GPIO_ReadPin(KNOB_PUSH_GPIO_Port, KNOB_PUSH_Pin); if (wheel_change != 0 || i == 0) { fb_clear(); char tmp[100]; sprintf(tmp, "Mereni: %d°C", (int) s_app.oven_temp); fb_text(10, 10, tmp, 0, 1); sprintf(tmp, " Cil: %d°C", s_app.set_temp); fb_text(10, 25, tmp, 0, 1); if (s_app.heating) { fb_frame(0, 0, FBW, FBH, 2, 1); } fb_blit(); } vTaskDelay(10); } // regulation float set_f = (float) s_app.set_temp; if (!s_app.heating && s_app.oven_temp < set_f - 5.0f) { /* hysteresis */ s_app.heating = true; } if (s_app.heating && s_app.oven_temp >= set_f) { s_app.heating = false; } HAL_GPIO_WritePin(HEATER_GPIO_Port, HEATER_Pin, s_app.heating); /* // beep htim2.Instance->ARR = 12000 + (int16_t)s_app.wheel * 100; htim2.Instance->CCR1 = htim2.Instance->ARR/2; vTaskDelay(50); htim2.Instance->ARR = 0; */ // feed dogs HAL_IWDG_Refresh(&hiwdg); } }