add rudimentary calib param tuning screen (TODO negative offset support!)

master
Ondřej Hruška 12 months ago
parent 6b94dcb5ee
commit 1ea4cd124d
  1. 3
      Core/Src/Gui/app_gui.c
  2. 1
      Core/Src/Gui/app_gui.h
  3. 217
      Core/Src/Gui/screen_calib_edit.c
  4. 2
      Core/Src/Gui/screen_calibration.c
  5. 7
      Core/Src/Gui/screen_home.c
  6. 9
      Core/Src/Gui/screen_pid_tuning.c
  7. 4
      Core/Src/app_heater.c
  8. 2
      Core/Src/app_heater.h
  9. 2
      Core/Src/app_safety.c
  10. 200
      Core/Src/app_temp.c
  11. 5
      Core/Src/app_temp.h
  12. 3
      Core/Src/ee_addresses.h
  13. 4
      Core/Src/freertos.c
  14. 1
      Makefile

@ -146,6 +146,9 @@ static void draw_common_overlay() {
if (app_heater_get_state()) {
fb_frame(0, 0, FBW, 11, 1, 1);
float perc = app_heater_get_percent();
fb_hline(0, 12, (fbpos_t) ((float)FBW * (perc / 100.0f)), 1);
}
}

@ -43,6 +43,7 @@ void screen_manual(GuiEvent event);
void screen_manual_menu(GuiEvent event);
void screen_calibration(GuiEvent event);
void screen_pid_tuning(GuiEvent event);
void screen_calib_edit(GuiEvent event);
struct State {
/// Latest oven temp readout

@ -0,0 +1,217 @@
//
// Created by MightyPork on 2023/05/06
//
#include <string.h>
#include "app_gui.h"
#include "app_heater.h"
#include "ufb/fb_text.h"
#include "snprintf.h"
#include "app_temp.h"
static void apply_calib(bool temporary);
static struct calib_tuning_state {
uint8_t digits[6 * 4]; // a b l r
int cursor; // the digit under cursor (range 0-17)
bool digit_editing; // true if we are editing a digit
} s_tuning;
static void draw_digit_row(int row) {
fbpos_t x = (FBW - (6 * 7 - 1)) / 2;
fbpos_t y = 30 + row * 10;
char buf2[2] = {};
int numofs = row * 6;
const char * labels[4] = {"a","b","L", "R"};
const int decimal_pos[4] = {2, 0, 3, 3};
fb_text(FBW - 4, y + 2, labels[row], FONT_3X5, 1);
bool significant = row == 1;
for (int i = 0; i < 6; i++) {
if (i >= decimal_pos[row]) significant = 1;
int val = s_tuning.digits[numofs + i];
if (val != 0) {
significant = true;
}
buf2[0] = '0' + val;
if (s_tuning.cursor == numofs + i) {
if (s_tuning.digit_editing) {
fb_rect(x-1, y-1, 5+2, 7+2, 1);
fb_text(x, y, buf2, 0, 0);
} else {
fb_hline(x, y + 8, 5, 1);
if (significant) fb_text(x, y, buf2, 0, 1);
}
} else {
if (significant) fb_text(x, y, buf2, 0, 1);
}
x += 6;
if (i == decimal_pos[row]) {
fb_text(x, y, ".", 0, 1);
x += 6;
}
}
}
void screen_calib_edit(GuiEvent event)
{
float A, B, L, R;
uint32_t Ai, Bi, Li, Ri;
if (event == GUI_EVENT_SCREEN_INIT) {
memset(&s_tuning, 0, sizeof(s_tuning));
app_temp_get_calib(&A, &B, &L, &R);
app_temp_backup_calib();
Ai = (uint32_t)(A * 1000.f);
Bi = (uint32_t)(B * 100000.f);
Li = (uint32_t)(L * 100.f);
Ri = (uint32_t)(R * 100.f);
char buf[25];
SNPRINTF(buf, 25, "%06lu%06lu%06lu%06lu", Ai, Bi, Li, Ri);
for(int i = 0; i < 6*4; i++) {
s_tuning.digits[i] = buf[i] - '0';
}
}
switch (event) {
case GUI_EVENT_PAINT: {
fb_text(FBW / 2, 16, "Kalibrace", TEXT_CENTER, 1);
draw_digit_row(0);
draw_digit_row(1);
draw_digit_row(2);
draw_digit_row(3);
//68
#define BTNS_TOP 72
if (s_tuning.cursor == 24) {
fb_rect(0, BTNS_TOP, FBW, 9, 1);
}
fb_text(FBW / 2, BTNS_TOP + 1, "Zrušit", TEXT_CENTER, s_tuning.cursor != 24);
if (s_tuning.cursor == 25) {
fb_rect(0, BTNS_TOP + 9, FBW, 9, 1);
}
fb_text(FBW / 2, BTNS_TOP + 1 + 9, "Uložit", TEXT_CENTER, s_tuning.cursor != 25);
fb_text(2, FBH - 8 * (1 + (s_tuning.cursor < 24)), s_tuning.digit_editing ? "←→Hodnota" : "←→Kurzor", 0, 1);
if (s_tuning.cursor < 24) {
fb_text(2, FBH - 8 * 1, s_tuning.digit_editing ? "> Potvrdit" : "> Změnit", 0, 1);
}
return;
}
case GUI_EVENT_KNOB_PLUS: {
input_sound_effect();
request_paint();
if (s_tuning.digit_editing) {
if (s_tuning.digits[s_tuning.cursor] == 9) {
s_tuning.digits[s_tuning.cursor] = 0;
} else {
s_tuning.digits[s_tuning.cursor]++;
}
apply_calib(false);
} else {
// 24 - cancel
// 25 - save
s_tuning.cursor++;
if (s_tuning.cursor >= 24) {
s_tuning.digit_editing = false;
}
if (s_tuning.cursor > 25) {
s_tuning.cursor = 0;
}
}
break;
}
case GUI_EVENT_KNOB_MINUS: {
input_sound_effect();
request_paint();
if (s_tuning.digit_editing) {
if (s_tuning.digits[s_tuning.cursor] == 0) {
s_tuning.digits[s_tuning.cursor] = 9;
} else {
s_tuning.digits[s_tuning.cursor]--;
}
apply_calib(false);
} else {
// 24 - cancel
// 25 - save
if (s_tuning.cursor == 0) {
s_tuning.cursor = 25;
s_tuning.digit_editing = false;
} else {
s_tuning.cursor--;
}
}
break;
}
case GUI_EVENT_KNOB_RELEASE: {
if (s_tuning.cursor < 24) {
s_tuning.digit_editing = !s_tuning.digit_editing;
} else if (s_tuning.cursor == 24) {
// cancel
app_temp_restore_calib();
switch_screen(screen_home, true);
} else if (s_tuning.cursor == 25) {
// save
apply_calib(true);
switch_screen(screen_home, true);
}
break;
}
}
}
static void apply_calib(bool temporary) {
float A, B, L, R;
uint32_t Ai, Bi, Li, Ri;
Ai = Bi = Li = Ri = 0;
for(int i = 0; i < 6; i++) {
Ai *= 10;
Ai += s_tuning.digits[i];
}
for(int i = 6; i < 12; i++) {
Bi *= 10;
Bi += s_tuning.digits[i];
}
for(int i = 12; i < 18; i++) {
Li *= 10;
Li += s_tuning.digits[i];
}
for(int i = 18; i < 24; i++) {
Ri *= 10;
Ri += s_tuning.digits[i];
}
A = ((float) Ai) / 1000.f;
B = ((float) Bi) / 100000.f;
L = ((float) Li) / 100.f;
R = ((float) Ri) / 100.f;
if (temporary) {
app_temp_set_calib_temporary(A, B);
app_temp_set_calib_temporary_r(L, R);
} else {
app_temp_set_calib_persistent(A, B);
app_temp_set_calib_persistent_r(L, R);
}
}

@ -215,7 +215,7 @@ void screen_calibration(GuiEvent event)
float a = (corrected1 - corrected2) / (s_calib.sample1 - s_calib.sample2);
float b = corrected1 - a * s_calib.sample1;
app_temp_set_persistent_calib(a, b);
app_temp_set_calib_persistent(a, b);
} else if (s_calib.phase == PH_SAMPLE1) {
app_heater_set_target(100);
app_heater_enable(true);

@ -12,8 +12,8 @@
static const char* main_menu_opts[] = {
"Ruční režim",
"Ladění PID",
"Kalibrace",
// "Diagnostika",
"Auto kalibrace",
"Ruční kalibrace",
NULL
};
@ -28,6 +28,9 @@ static void main_menu_cb(int opt) {
case 2:
switch_screen(screen_calibration, true);
break;
case 3:
switch_screen(screen_calib_edit, true);
break;
}
}

@ -2,17 +2,13 @@
// Created by MightyPork on 2023/04/09.
//
#include <stddef.h>
#include <string.h>
#include "app_gui.h"
#include "app_heater.h"
#include "screen_menu.h"
#include "ufb/fb_text.h"
#include "snprintf.h"
#include "FreeRTOS.h"
struct pid_tuning_state {
static struct calib_tuning_state {
uint8_t digits[6 * 3];
// uint32_t Kp, Ki, Kd; // these are the float x 1000
@ -29,6 +25,9 @@ static void draw_digit_row(int row) {
int numofs = row * 6;
const char * labels[3] = {"P","I","D"};
fb_text(FBW - 4, y + 2, labels[row], FONT_3X5, 1);
bool significant = false;
for (int i = 0; i < 6; i++) {
if (i >= 2) significant = 1;

@ -140,6 +140,10 @@ bool app_heater_get_state() {
return state.pid.ctlMode == PID_AUTOMATIC;
}
float app_heater_get_percent() {
return state.pid.Output;
}
float app_heater_get_target() {
return state.pid.Setpoint;
}

@ -47,4 +47,6 @@ bool app_heater_get_state();
float app_heater_get_target();
float app_heater_get_percent();
#endif //BLUEPILLTROUBA_APP_HEATER_H

@ -46,7 +46,7 @@ void app_safety_pass_soc_temp_ok() {
}
void app_safety_poll() {
PRINTF("\r\n*** HB FLAGS %x ***\r\n", heartbeat_flags);
//PRINTF("\r\n*** HB FLAGS %x ***\r\n", heartbeat_flags);
if ((heartbeat_flags & HB_FLAG_ALL) == HB_FLAG_ALL) {
LL_IWDG_ReloadCounter(IWDG);
LL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);

@ -3,12 +3,8 @@
*/
#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"
@ -21,22 +17,32 @@ static volatile uint16_t adc_values[4];
const float V_REFINT = 1.23f;
#define AVERAGEBUF_DEPTH 16
#define OVENTEMP_HISTORY_DEPTH 20
#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,
@ -46,6 +52,8 @@ static struct App {
#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,
@ -149,18 +157,136 @@ static const float TSENSE_LOOKUP[TSENSE_LOOKUP_LEN] = {
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) {
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;
}
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_persistent_calib(float a, float b) {
void app_temp_set_calib_persistent(float a, float b) {
s_analog.cal_a = a;
s_analog.cal_b = b;
correct_invalid_calib();
@ -179,9 +305,30 @@ void app_temp_set_persistent_calib(float a, float b) {
}
}
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) {
@ -189,9 +336,16 @@ void app_temp_set_calib_temporary(float a, float b) {
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()
@ -206,6 +360,14 @@ void app_analog_init()
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();
@ -234,15 +396,19 @@ void app_analog_init()
LL_TIM_EnableCounter(TIM1);
}
float val_to_c(float val)
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 = TSENSE_LOOKUP[i];
if (cur >= val) {
float prev = TSENSE_LOOKUP[i - 1];
float cur = PT100_LOOKUP[i];
if (cur >= pt100r) {
float prev = PT100_LOOKUP[i - 1];
float ratio = (val - prev) / (cur - prev);
float ratio = (pt100r - prev) / (cur - prev);
return TSENSE_T_MIN + ((float) i + ratio) * TSENSE_T_STEP;
}
}
@ -262,7 +428,7 @@ float c_to_val(float cf)
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;
return PT100_LOOKUP[lower] + (PT100_LOOKUP[upper] - PT100_LOOKUP[lower]) * ratio;
}
void app_temp_sample()
@ -329,8 +495,10 @@ void app_temp_sample()
}
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 -> comp %f, temp %f°C\r\n", sum, y, actual_temp);
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;

@ -10,11 +10,14 @@ void app_analog_init();
float val_to_c(float val);
float c_to_val(float c);
void app_temp_set_persistent_calib(float a, float b);
void app_temp_set_calib_persistent(float a, float b);
void app_temp_set_calib_persistent_r(float lead, float reference);
void app_temp_backup_calib();
void app_temp_set_calib_temporary(float a, float b);
void app_temp_set_calib_temporary_r(float lead, float reference);
void app_temp_get_calib(float *a, float *b, float *l, float *r);
void app_temp_restore_calib();

@ -17,6 +17,9 @@ enum EEAddresses {
EE_ADDR_PID_I = 4,
// 100.0
EE_ADDR_PID_D = 5,
EE_ADDR_LEAD_R = 6,
EE_ADDR_REF_R = 7,
};
#endif //TOASTER_OVEN_BLUEPILL_EE_ADDRESSES_H

@ -66,7 +66,7 @@ const osThreadAttr_t mainTsk_attributes = {
};
/* Definitions for heaterTsk */
osThreadId_t heaterTskHandle;
uint32_t heaterTskBuffer[ 256 ];
uint32_t heaterTskBuffer[ 300 ];
osStaticThreadDef_t heaterTskControlBlock;
const osThreadAttr_t heaterTsk_attributes = {
.name = "heaterTsk",
@ -78,7 +78,7 @@ const osThreadAttr_t heaterTsk_attributes = {
};
/* Definitions for guiTsk */
osThreadId_t guiTskHandle;
uint32_t guiTskBuffer[ 256 ];
uint32_t guiTskBuffer[ 300 ];
osStaticThreadDef_t guiTskControlBlock;
const osThreadAttr_t guiTsk_attributes = {
.name = "guiTsk",

@ -54,6 +54,7 @@ Core/Src/Gui/screen_manual.c \
Core/Src/Gui/screen_calibration.c \
Core/Src/Gui/screen_manual_menu.c \
Core/Src/Gui/screen_pid_tuning.c \
Core/Src/Gui/screen_calib_edit.c \
Core/Src/app_temp.c \
Core/Src/app_knob.c \
Core/Src/app_buzzer.c \

Loading…
Cancel
Save