@@ -335,7 +335,7 @@ void display_process(therm_settings_t* s
status->state = STATE_IDLE;
}
else {
user_input_signed((int16_t*)(&set->val.temp_offset));
user_input_signed(&set->val.temp_offset);
// Event Handler
@@ -354,7 +354,7 @@ void display_process(therm_settings_t* s
draw_setpoint(status);
status->pid_enabled = 1;
status->setpoint = set->val.setpoint_brew;
// Button handler
if(SW_BTN_PRESSED) {
@@ -380,7 +380,7 @@ void display_process(therm_settings_t* s
//ssd1306_drawlogo();
@@ -567,4 +567,10 @@ static void draw_setpoint(therm_status_t
temp_last = status->temp;
void display_startup_screen() {
ssd1306_clearscreen();
ssd1306_drawstring("therm v0.2", 1, 40);
ssd1306_drawstring("protofusion.org/therm", 3, 0);
// vim:softtabstop=4 shiftwidth=4 expandtab
@@ -15,6 +15,7 @@
#include "max31865.h"
#endif
void display_startup_screen();
void display_process(therm_settings_t* set, therm_status_t* status);
@@ -21,17 +21,19 @@ void user_input(uint16_t* to_modify)
// Increment/decrement signed variable with up/down buttons
void user_input_signed(int16_t* to_modify)
void user_input_signed(int32_t* to_modify)
{
// TODO: Bounds check on int16_t
//fixme: need to cast to 16/32 bits correctly
if(CHANGE_ELAPSED) {
if(!HAL_GPIO_ReadPin(SW_UP) ) {
CHANGE_RESET;
(*to_modify)++;
if (*to_modify < 32768)
else if(!HAL_GPIO_ReadPin(SW_DOWN)) {
(*to_modify)--;
if (*to_modify >= -32768)
@@ -12,7 +12,7 @@
void user_input(uint16_t* to_modify);
void user_input_signed(int16_t* to_modify);
void user_input_signed(int32_t* to_modify);
void gpio_init(void);
@@ -5,11 +5,10 @@
#include "pid.h"
#include "states.h"
#include "ssd1306.h"
#ifdef MAX31855_TC_SENSOR
#include "max31855.h"
#ifdef MAX31865_RTD_SENSOR
#else
#include "gpio.h"
#include "spi.h"
@@ -22,6 +21,7 @@
therm_settings_t set;
therm_status_t status;
pid_state_t pid_state;
int main(void)
@@ -53,13 +53,16 @@ int main(void)
// Init SPI busses
spi_init();
// Init RTD chip
max31865_config(spi_get());
// Init OLED over SPI
ssd1306_init();
// Startup screen
display_startup_screen();
// Default status
status.temp = 0;
@@ -69,20 +72,16 @@ int main(void)
status.setpoint = 70;
status.pid_enabled = 0;
pid_init(&pid_state);
// Go to brew instead of idle if configured thusly
if(set.val.boottobrew)
status.state = STATE_PREHEAT;
HAL_Delay(1000);
// Restore settings from flash memory
flash_restore(&set);
HAL_Delay(2000);
// Soft timers
@@ -104,21 +103,27 @@ int main(void)
if((HAL_GetTick() - last_pid > PID_PERIOD))
max31855_readtemp(spi_get(), &set, &status); // Read MAX31855
max31865_readtemp(spi_get(), &set, &status);
if(status.pid_enabled)
// Get ssr output for next time
int16_t power_percent = pid_update(set.val.k_p, set.val.k_i, set.val.k_d, status.temp, status.temp_frac, status.setpoint, &set, &status);
int16_t power_percent = pid_update(&set, &status, &pid_state);
//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
@@ -138,12 +143,10 @@ int main(void)
// Every 200ms, set the SSR on unless output is 0
if(HAL_GetTick() - last_ssr_on > SSR_PERIOD)
// Only support heating (ssr_output > 0) right now
if(ssr_output > 0)
HAL_GPIO_WritePin(SSR_PIN, 1);
HAL_GPIO_WritePin(LED_POWER, 1);
@@ -157,7 +160,7 @@ int main(void)
// Transmit temperature over USB-CDC on a regulat basis
// Transmit temperature over USB-CDC on a regular basis
if(HAL_GetTick() - last_vcp_tx > VCP_TX_FREQ)
// Print temp to cdc
@@ -53,14 +53,8 @@ void max31865_clear_errors(SPI_HandleTyp
// Grab temperature reading from MAX31865
void max31865_readtemp(SPI_HandleTypeDef* hspi1, therm_settings_t* set, therm_status_t* status)
///////////////////////////////////
// This is duplicated from MAX31855, update for MAX31865 registers/etc
// TODO: Set configuration register based on params in config.h (2-wire, 4-wire, etc RTD). This is register 0x00.
// 2-wire RTC or 2-wire (duh) NTC thermistor will be the only options
// Need option for resistance of RTD
// These options should be stored in the set structure and should be menu-selectable
// TODO: Set RTD ref resistance in set struct
// Assert CS
HAL_GPIO_WritePin(MAX_CS, 0);
#include "stm32f0xx_hal.h"
// PID implementation
// TODO: Make struct that has the last_temp and i_state in it, pass by ref. Make struct that has other input values maybe.
static int16_t last_pid_temp = 0;
static uint8_t last_pid_temp_frac = 0;
static int32_t i_state = 0;
int16_t pid_update(uint16_t k_p, uint16_t k_i, uint16_t k_d, int16_t temp, uint8_t temp_frac, int16_t setpoint, therm_settings_t* set, therm_status_t* status)
void pid_init(pid_state_t* state)
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)
// Calculate instantaneous error
int16_t error = setpoint - temp; // TODO: Use fixed point fraction
int16_t error = status->setpoint - status->temp; // TODO: Use fixed point fraction
// Proportional component
int32_t p_term = k_p * error;
int32_t p_term = set->val.k_p * error;
// Error accumulator (integrator)
i_state += error;
state->i_state += error;
// to prevent the iTerm getting huge from lots of
// error, we use a "windup guard"
@@ -24,22 +26,22 @@ int16_t pid_update(uint16_t k_p, uint16_
// it cant help be cold despite its best efforts)
// not necessary, but this makes windup guard values
// relative to the current iGain
int32_t windup_guard_res = set->val.windup_guard / k_i;
int32_t windup_guard_res = set->val.windup_guard / set->val.k_i;
// Calculate integral term with windup guard
if (i_state > windup_guard_res)
i_state = windup_guard_res;
else if (i_state < -windup_guard_res)
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 = k_i * i_state;
int32_t i_term = set->val.k_i * state->i_state;
// Calculate differential term (slope since last iteration)
int32_t d_term = (k_d * (status->temp - last_pid_temp));
int32_t d_term = (set->val.k_d * (status->temp - state->last_pid_temp));
// Save temperature for next iteration
last_pid_temp = status->temp;
last_pid_temp_frac = status->temp_frac;
state->last_pid_temp = status->temp;
state->last_pid_temp_frac = status->temp_frac;
int16_t result = p_term + i_term - d_term;
#ifndef PIDS_H
#define PIDS_H
int16_t pid_update(uint16_t k_p, uint16_t k_i, uint16_t k_d, int16_t temp, uint8_t temp_frac, int16_t setpoint, therm_settings_t* set, therm_status_t* status);
typedef struct {
int16_t last_pid_temp;
uint8_t last_pid_temp_frac;
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);
Status change: