#include "stm32f0xx_hal.h"
#include "config.h"
#include "syslib.h"
#include "pid.h"
#include "states.h"
#include "ssd1306.h"
#include "max31855.h"
#include "gpio.h"
#include "spi.h"
#include "flash.h"
#include "stringhelpers.h"
#include "display.h"
#include "storage.h"
#include "usb_device.h"
#include "usbd_cdc_if.h"
// Prototypes
void process();
therm_settings_t set;
therm_status_t status;
int main(void)
{
// Initialize HAL
hal_init();
// Configure the system clock
systemclock_init();
// Unset bootloader option bytes (if set)
void bootloader_unset(void);
// FIXME this was never getting called. Try again sometime.
//bootloader_unset();
// Init GPIO
gpio_init();
// Init USB (TODO: Handle plugged/unplugged with external power)
MX_USB_DEVICE_Init();
// set.val.usb_plugged =
// USB startup delay
HAL_Delay(1000);
HAL_Delay(500);
HAL_GPIO_WritePin(LED_POWER, 1);
// Enter into bootloader if up button pressed on boot
if(!HAL_GPIO_ReadPin(SW_UP))
bootloader_enter();
// Init SPI busses
spi_init();
// Init OLED over SPI
ssd1306_Init();
ssd1306_clearscreen();
// Default settings
set.val.boottobrew = 0;
set.val.temp_units = TEMP_UNITS_CELSIUS;
set.val.windup_guard = 1;
set.val.k_p = 1;
set.val.k_i = 1;
set.val.k_d = 1;
set.val.ignore_tc_error = 0;
set.val.setpoint_brew = 0;
set.val.setpoint_steam = 0;
// Default status
status.temp = 0;
status.temp_frac = 0;
status.state_resume = 0;
status.state = STATE_IDLE;
status.setpoint = 0;
status.pid_enabled = 0;
// Load settings (if any) from EEPROM
restore_settings(&set);
// Go to brew instead of idle if configured thusly
if(set.val.boottobrew)
status.state = STATE_PREHEAT_BREW;
// Startup screen
ssd1306_DrawString("therm v0.2", 1, 40);
ssd1306_DrawString("protofusion.org/therm", 3, 0);
HAL_Delay(1500);
flash_restore(&set);
// Soft timers
uint32_t last_ssr_on = 0;
uint32_t last_vcp_tx = 0;
uint32_t last_led = 0;
uint32_t last_pid = 0;
int16_t ssr_output = 0; // Duty cycle of ssr, 0 to SSR_PERIOD
// Main loop
while(1)
// Process sensor inputs
if(HAL_GetTick() - last_led > 400)
last_led = HAL_GetTick();
}
if((HAL_GetTick() - last_pid > PID_PERIOD))
#ifdef MAX31855_TC_SENSOR
max31855_readtemp(spi_get(), &set, &status); // Read MAX31855
#endif
new file 100644
// 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)
// Calculate instantaneous error
int16_t error = setpoint - temp; // TODO: Use fixed point fraction
// Proportional component
int32_t p_term = k_p * error;
// Error accumulator (integrator)
i_state += error;
// to prevent the iTerm getting huge from lots of
// error, we use a "windup guard"
// (this happens when the machine is first turned on and
// 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;
// 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;
int32_t i_term = k_i * i_state;
// Calculate differential term (slope since last iteration)
int32_t d_term = (k_d * (status->temp - last_pid_temp));
// Save temperature for next iteration
last_pid_temp = status->temp;
last_pid_temp_frac = status->temp_frac;
int16_t result = p_term + i_term - d_term;
// Put out tenths of percent, 0-1000.
if(result > 1000)
result = 1000;
else if(result < -1000)
result = -1000;
// Return feedback
return result;
#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);
Status change: