# HG changeset patch # User Ethan Zonca # Date 2017-06-26 16:27:25 # Node ID 2b4eb31dd8da193e1aee1c699d8ccf1ef51184a2 # Parent 72630eaa81516e4241efb5cce34dc501e58d948e Rework some structure to segregate business logic of PID / thermostatic control / temp sensing diff --git a/.settings/language.settings.xml b/.settings/language.settings.xml --- a/.settings/language.settings.xml +++ b/.settings/language.settings.xml @@ -5,7 +5,7 @@ - + @@ -16,7 +16,7 @@ - + diff --git a/inc/display.h b/inc/display.h --- a/inc/display.h +++ b/inc/display.h @@ -7,7 +7,7 @@ #include "max31865.h" #endif -void display_startup_screen(); -void display_process(therm_status_t* status); +void display_startup_screen(void); +void display_process(void); #endif diff --git a/inc/pid.h b/inc/pid.h --- a/inc/pid.h +++ b/inc/pid.h @@ -10,7 +10,8 @@ typedef struct { int32_t i_state; } pid_state_t; -void pid_init(pid_state_t* state); -int16_t pid_update(therm_settings_t* set, therm_status_t* status, pid_state_t* state); +void pid_init(void); +float pid_process(void); +int16_t pid_update(void); #endif diff --git a/inc/pwmout.h b/inc/pwmout.h new file mode 100644 --- /dev/null +++ b/inc/pwmout.h @@ -0,0 +1,9 @@ +#ifndef PWMOUT_H +#define PWMOUT_H + + +void pwmout_init(void); +void pwmout_process(float duty); + + +#endif diff --git a/inc/states.h b/inc/states.h --- a/inc/states.h +++ b/inc/states.h @@ -5,11 +5,10 @@ #include "config.h" typedef struct { - int32_t temp; - uint8_t temp_frac; + float temp; uint8_t state_resume; uint8_t state; - int32_t setpoint; + float setpoint; uint8_t pid_enabled; uint8_t error_code; } therm_status_t; diff --git a/inc/system/flash.h b/inc/system/flash.h --- a/inc/system/flash.h +++ b/inc/system/flash.h @@ -13,4 +13,7 @@ void flash_savesettings(void); void flash_restoresettings(void); therm_settings_t* flash_getsettings(void); +therm_status_t* runtime_status(void); + + #endif // flash_h diff --git a/inc/tempsense.h b/inc/tempsense.h new file mode 100644 --- /dev/null +++ b/inc/tempsense.h @@ -0,0 +1,8 @@ +#ifndef TEMPSENSE_H +#define TEMPSENSE_H + + +void tempsense_init(void); +float tempsense_readtemp(void); + +#endif diff --git a/inc/thermostat.h b/inc/thermostat.h new file mode 100644 --- /dev/null +++ b/inc/thermostat.h @@ -0,0 +1,10 @@ +#ifndef THERMOSTAT_H +#define THERMOSTAT_H + +#include "stm32f3xx_hal.h" + + +float thermostat_process(void); + + +#endif diff --git a/src/display.c b/src/display.c --- a/src/display.c +++ b/src/display.c @@ -39,15 +39,17 @@ static uint8_t reset_mode = RESET_REBOOT // Display state machine -void display_process(therm_status_t* status) +void display_process(void) { + therm_status_t* status = runtime_status(); therm_settings_t* set = flash_getsettings(); + + uint8_t state_changed = status->state != last_state; last_state = status->state; - uint8_t temp_changed = status->temp != last_temp || status->temp_frac != last_temp_frac; + uint8_t temp_changed = status->temp != last_temp; last_temp = status->temp; - last_temp_frac = status->temp_frac; uint8_t sw_btn = !HAL_GPIO_ReadPin(SW_BTN); uint8_t sw_up = !HAL_GPIO_ReadPin(SW_UP); @@ -67,7 +69,7 @@ void display_process(therm_status_t* sta if(temp_changed || state_changed) { char tempstr[6]; - itoa_fp(status->temp, status->temp_frac, tempstr); +// itoa_fp(status->temp, status->temp_frac, tempstr); ssd1306_drawstring("Temp: ", 3, 40); ssd1306_drawstring(" ", 3, 72); ssd1306_drawstring(tempstr, 3, 72); @@ -656,19 +658,18 @@ void display_process(therm_status_t* sta } -static int32_t temp_last = 43002; -static int32_t temp_frac_last = 43002; -static int32_t setpoint_last = 10023; +static float temp_last = 43002.0; +static float setpoint_last = 10023.0; // Draw current setpoint on display static void draw_setpoint(therm_status_t* status) { // FIXME: need to do this when switching modes too - if(status->temp != temp_last || status->temp_frac != temp_frac_last || trigger_drawsetpoint) { - char tempstr[3]; - itoa_fp(status->temp, status->temp_frac, tempstr); - ssd1306_drawstringbig(" ", 3, 0); - ssd1306_drawstringbig(tempstr, 3, 0); - } +// if(status->temp != temp_last || trigger_drawsetpoint) { +// char tempstr[3]; +// itoa_fp(status->temp, status->temp_frac, tempstr); +// ssd1306_drawstringbig(" ", 3, 0); +// ssd1306_drawstringbig(tempstr, 3, 0); +// } if(trigger_drawsetpoint) ssd1306_drawstringbig(">", 3, 74); @@ -683,7 +684,6 @@ static void draw_setpoint(therm_status_t trigger_drawsetpoint = 0; setpoint_last = status->setpoint; temp_last = status->temp; - temp_frac_last = status->temp_frac; } void display_startup_screen() { diff --git a/src/main.c b/src/main.c --- a/src/main.c +++ b/src/main.c @@ -6,20 +6,17 @@ #include "stm32f3xx_hal.h" #include "config.h" - #include "watchdog.h" #include "system.h" #include "display.h" +#include "thermostat.h" #include "gpio.h" +#include "tempsense.h" #include "pid.h" #include "error.h" #include "flash.h" #include "ssd1306/ssd1306.h" -#include "interrupts.h" - - -therm_status_t status; -pid_state_t pid_state; +#include "pwmout.h" int main(void) @@ -28,7 +25,6 @@ int main(void) hal_init(); gpio_init(); - ssd1306_init(); // Startup screen @@ -37,149 +33,46 @@ int main(void) ssd1306_drawlogo(); - + // Default status + runtime_status()->temp = 0.0; + runtime_status()->state_resume = 0; + runtime_status()->state = STATE_IDLE; + runtime_status()->setpoint = 70; + runtime_status()->pid_enabled = 0; - // Default status - status.temp = 0; - status.temp_frac = 0; - status.state_resume = 0; - status.state = STATE_IDLE; - status.setpoint = 70; - status.pid_enabled = 0; - - pid_init(&pid_state); - + pid_init(); + pwmout_init(); flash_init(); watchdog_init(); - + tempsense_init(); // Soft timers - uint32_t last_ssr_on = 0; - uint32_t last_vcp_tx = 0; - uint32_t last_led = 0; uint32_t last_pid = 0; uint32_t last_thermostat = 0; - int16_t ssr_output = 0; // Duty cycle of ssr, 0 to SSR_PERIOD - uint8_t thermostat_plant_on = 0; - while (1) { + float duty = 0.0; if(flash_getsettings()->val.control_mode == MODE_PID && (HAL_GetTick() - last_pid > PID_PERIOD)) { - -// #ifdef MAX31865_RTD_SENSOR -// max31865_readtemp(spi_get(), &set, &status); -// #else -// max31855_readtemp(spi_get(), &set, &status); // Read MAX31855 -// #endif - - - if(status.pid_enabled) - { - // Get ssr output for next time - int16_t power_percent = pid_update(flash_getsettings(), &status, &pid_state); - - if(flash_getsettings()->val.plant_type == PLANT_HEATER) - power_percent *= -1; - - //power-percent is 0-1000? - ssr_output = power_percent; //(((uint32_t)SSR_PERIOD * (uint32_t)10 * (uint32_t)100) * power_percent) / (uint32_t)1000000; - - - // put ssr output on display - ssd1306_drawstring(" ", 0, 90); //fixme: this is bad, but I can't get the old digits to clear otherwise - char tempstr[6]; - itoa(ssr_output, tempstr, 10); - ssd1306_drawstring(tempstr, 0, 90); - } - else - { - ssr_output = 0; - } - + runtime_status()->temp = tempsense_readtemp(); + duty = pid_process(); last_pid = HAL_GetTick(); } -// // Kill SSR once the desired on-time has elapsed -// if(set.val.control_mode == MODE_PID && (HAL_GetTick() - last_ssr_on > ssr_output || ssr_output <= 0)) -// { -// HAL_GPIO_WritePin(SSR_PIN, 0); -// HAL_GPIO_WritePin(LED_POWER, 0); -// } -// -// -// // Every 200ms, set the SSR on unless output is 0 -// if(set.val.control_mode == MODE_PID && HAL_GetTick() - last_ssr_on > SSR_PERIOD) -// { -// // Heat or cool, if we need to -// if(ssr_output > 0) -// { -// HAL_GPIO_WritePin(SSR_PIN, 1); -// HAL_GPIO_WritePin(LED_POWER, 1); -// last_ssr_on = HAL_GetTick(); -// } -// else { -// // Make sure everything is off -// HAL_GPIO_WritePin(SSR_PIN, 0); -// HAL_GPIO_WritePin(LED_POWER, 0); -// } -// -// } - - // Thermostatic control if(flash_getsettings()->val.control_mode == MODE_THERMOSTAT && HAL_GetTick() - last_thermostat > SSR_PERIOD) { - -// #ifdef MAX31865_RTD_SENSOR -// max31865_readtemp(spi_get(), &set, &status); -// #else -// max31855_readtemp(spi_get(), &set, &status); // Read MAX31855 -// #endif - - // TODO: Migrate this FxP conversion to the readtemp code or similar - int8_t temp_frac = status.temp_frac > 9 ? status.temp_frac / 10 : status.temp_frac; - temp_frac = status.temp > 0 ? temp_frac : temp_frac * -1; - int32_t temp = (status.temp * 10) + temp_frac; - - - // EMZ FIXME: This could be way simpler - if(flash_getsettings()->val.plant_type == PLANT_HEATER && status.setpoint * 10 < temp - flash_getsettings()->val.hysteresis * 10) - thermostat_plant_on = 1; - else if(flash_getsettings()->val.plant_type == PLANT_HEATER && status.setpoint * 10 > temp + flash_getsettings()->val.hysteresis * 10) - thermostat_plant_on = 0; - - if(flash_getsettings()->val.plant_type == PLANT_COOLER && status.setpoint * 10 > temp + flash_getsettings()->val.hysteresis * 10) - thermostat_plant_on = 1; - else if(flash_getsettings()->val.plant_type == PLANT_COOLER && status.setpoint * 10 < temp - flash_getsettings()->val.hysteresis * 10) - thermostat_plant_on = 0; - - // EMZ: TODO: Refactor to output_enabled or something - if(status.pid_enabled && thermostat_plant_on) - { - // EMZ TODO: functionalize this - // put ssr output on display - ssd1306_drawstring(" ", 0, 90); //fixme: this is bad, but I can't get the old digits to clear otherwise - char tempstr[6]; - itoa(ssr_output, tempstr, 10); - ssd1306_drawstring(tempstr, 0, 90); - - - -// HAL_GPIO_WritePin(SSR_PIN, 1); - HAL_GPIO_WritePin(LED, 1); - } - else - { -// HAL_GPIO_WritePin(SSR_PIN, 0); - HAL_GPIO_WritePin(LED, 0); - } - + runtime_status()->temp = tempsense_readtemp(); + duty = thermostat_process(); last_thermostat = HAL_GetTick(); } + pwmout_process(duty); + display_process(); + watchdog_feed(); + // // Transmit temperature over USB-CDC on a regular basis // if(HAL_GetTick() - last_vcp_tx > VCP_TX_FREQ) @@ -198,10 +91,6 @@ int main(void) // last_vcp_tx = HAL_GetTick(); // } - // Run state machine - display_process(&status); - - watchdog_feed(); } } diff --git a/src/pid.c b/src/pid.c --- a/src/pid.c +++ b/src/pid.c @@ -3,23 +3,64 @@ // #include "pid.h" +#include "flash.h" // PID implementation -void pid_init(pid_state_t* state) +static pid_state_t state; + + +void pid_init() { - state->i_state = 0; - state->last_pid_temp = 0; - state->last_pid_temp_frac = 0; + state.i_state = 0; + state.last_pid_temp = 0; + state.last_pid_temp_frac = 0; } -int16_t pid_update(therm_settings_t* set, therm_status_t* status, pid_state_t *state) +float pid_process(void) { +// #ifdef MAX31865_RTD_SENSOR +// max31865_readtemp(spi_get(), &set, &status); +// #else +// max31855_readtemp(spi_get(), &set, &status); // Read MAX31855 +// #endif + + float ssr_output = 0; + + + if(runtime_status()->pid_enabled) + { + // Get ssr output for next time + int16_t power_percent = pid_update(); + + if(flash_getsettings()->val.plant_type == PLANT_HEATER) + power_percent *= -1; + + //power-percent is 0-1000? + ssr_output = power_percent; //(((uint32_t)SSR_PERIOD * (uint32_t)10 * (uint32_t)100) * power_percent) / (uint32_t)1000000; + + // put ssr output on display + ssd1306_drawstring(" ", 0, 90); //fixme: this is bad, but I can't get the old digits to clear otherwise + char tempstr[6]; + itoa(ssr_output, tempstr, 10); + ssd1306_drawstring(tempstr, 0, 90); + } + else + { + ssr_output = 0.0; + } + + return ssr_output; +} + +int16_t pid_update(void) +{ + therm_status_t* status = runtime_status(); + therm_settings_t* set = flash_getsettings(); + // Convert temperature to fixed point number with 1/10th resolution - int8_t temp_frac = status->temp_frac > 9 ? status->temp_frac / 10 : status->temp_frac; - temp_frac = status->temp > 0 ? temp_frac : temp_frac * -1; - int32_t temp = (status->temp * 10) + temp_frac; + float temp = status->temp; // Calculate instantaneous error int16_t error = status->setpoint * 10 - temp; // TODO: Use fixed point fraction @@ -28,7 +69,7 @@ int16_t pid_update(therm_settings_t* set int32_t p_term = set->val.k_p * error; // Error accumulator (integrator) - state->i_state += error; + state.i_state += error; // to prevent the iTerm getting huge from lots of // error, we use a "windup guard" @@ -39,18 +80,18 @@ int16_t pid_update(therm_settings_t* set int32_t windup_guard_res = (set->val.windup_guard * 10) / set->val.k_i; // Calculate integral term with windup guard - if (state->i_state > windup_guard_res) - state->i_state = windup_guard_res; - else if (state->i_state < -windup_guard_res) - state->i_state = -windup_guard_res; + if (state.i_state > windup_guard_res) + state.i_state = windup_guard_res; + else if (state.i_state < -windup_guard_res) + state.i_state = -windup_guard_res; - int32_t i_term = set->val.k_i * state->i_state; + int32_t i_term = set->val.k_i * state.i_state; // Calculate differential term (slope since last iteration) - int32_t d_term = (set->val.k_d * (temp - state->last_pid_temp)); + int32_t d_term = (set->val.k_d * (temp - state.last_pid_temp)); // Save temperature for next iteration - state->last_pid_temp = temp; + state.last_pid_temp = temp; int16_t result = (p_term + i_term - d_term) / 10; diff --git a/src/pwmout.c b/src/pwmout.c new file mode 100644 --- /dev/null +++ b/src/pwmout.c @@ -0,0 +1,49 @@ +// +// PWM Out: generate PWM waveform to control factory +// + +#include "pwmout.h" +#include "flash.h" + + +static uint32_t last_ssr_on = 0; +static uint32_t last_vcp_tx = 0; +static uint32_t last_led = 0; + + +void pwmout_init(void) +{ + +} + +void pwmout_process(float duty) +{ + + // // Kill SSR once the desired on-time has elapsed + // if(set.val.control_mode == MODE_PID && (HAL_GetTick() - last_ssr_on > ssr_output || ssr_output <= 0)) + // { + // HAL_GPIO_WritePin(SSR_PIN, 0); + // HAL_GPIO_WritePin(LED_POWER, 0); + // } + // + // + // // Every 200ms, set the SSR on unless output is 0 + // if(set.val.control_mode == MODE_PID && HAL_GetTick() - last_ssr_on > SSR_PERIOD) + // { + // // Heat or cool, if we need to + // if(ssr_output > 0) + // { + // HAL_GPIO_WritePin(SSR_PIN, 1); + // HAL_GPIO_WritePin(LED_POWER, 1); + // last_ssr_on = HAL_GetTick(); + // } + // else { + // // Make sure everything is off + // HAL_GPIO_WritePin(SSR_PIN, 0); + // HAL_GPIO_WritePin(LED_POWER, 0); + // } + // + // } +} + + diff --git a/src/system/flash.c b/src/system/flash.c --- a/src/system/flash.c +++ b/src/system/flash.c @@ -10,6 +10,7 @@ static __attribute__((__section__(".eeprom"))) uint16_t eeprom_emulation[512]; therm_settings_t settings; +therm_status_t status; // Initialize flash and restore settings void flash_init(void) @@ -78,3 +79,9 @@ inline therm_settings_t* flash_getsettin { return &settings; } + +inline therm_status_t* runtime_status(void) +{ + return &status; +} + diff --git a/src/tempsense.c b/src/tempsense.c new file mode 100644 --- /dev/null +++ b/src/tempsense.c @@ -0,0 +1,24 @@ +// +// TempSense: read temperature from TC, RTD, or NTC +// + +#include "tempsense.h" +#include "flash.h" + + +void tempsense_init(void) +{ + +} + +float tempsense_readtemp(void) +{ + // TODO: Support multiple temperature sensors + + + // either return latest reading from DMA loop (NTC, etc) + // or initiate a blocking read and return it. + // Need to gracefully handle the timeout... +} + + diff --git a/src/thermostat.c b/src/thermostat.c new file mode 100644 --- /dev/null +++ b/src/thermostat.c @@ -0,0 +1,62 @@ +// +// Thermostat: Thermostatic controller +// + +#include "thermostat.h" +#include "flash.h" +#include "gpio.h" + +// PID implementation + +float thermostat_process(void) +{ + +// #ifdef MAX31865_RTD_SENSOR +// max31865_readtemp(spi_get(), &set, &status); +// #else +// max31855_readtemp(spi_get(), &set, &status); // Read MAX31855 +// #endif + + float ssr_output = 0.0; + uint8_t thermostat_plant_on = 0; + + // TODO: Migrate this FxP conversion to the readtemp code or similar + float temp = runtime_status()->temp; + + + // EMZ FIXME: This could be way simpler + if(flash_getsettings()->val.plant_type == PLANT_HEATER && runtime_status()->setpoint * 10 < temp - flash_getsettings()->val.hysteresis * 10) + thermostat_plant_on = 1; + else if(flash_getsettings()->val.plant_type == PLANT_HEATER && runtime_status()->setpoint * 10 > temp + flash_getsettings()->val.hysteresis * 10) + thermostat_plant_on = 0; + + if(flash_getsettings()->val.plant_type == PLANT_COOLER && runtime_status()->setpoint * 10 > temp + flash_getsettings()->val.hysteresis * 10) + thermostat_plant_on = 1; + else if(flash_getsettings()->val.plant_type == PLANT_COOLER && runtime_status()->setpoint * 10 < temp - flash_getsettings()->val.hysteresis * 10) + thermostat_plant_on = 0; + + // EMZ: TODO: Refactor to output_enabled or something + if(runtime_status()->pid_enabled && thermostat_plant_on) + { + // EMZ TODO: functionalize this + // put ssr output on display + ssd1306_drawstring(" ", 0, 90); //fixme: this is bad, but I can't get the old digits to clear otherwise + char tempstr[6]; + itoa(ssr_output, tempstr, 10); + ssd1306_drawstring(tempstr, 0, 90); + + + +// HAL_GPIO_WritePin(SSR_PIN, 1); + HAL_GPIO_WritePin(LED, 1); + } + else + { +// HAL_GPIO_WritePin(SSR_PIN, 0); + HAL_GPIO_WritePin(LED, 0); + } + + return ssr_output; +} + +