Changeset - 953c718ee4cf
[Not reviewed]
default
0 8 0
Ethan Zonca (ethanzonca) - 8 years ago 2017-07-07 14:33:37
e@ethanzonca.com
Various changes forgotten to commit
8 files changed with 141 insertions and 89 deletions:
0 comments (0 inline, 0 general)
inc/pwmout.h
Show inline comments
 
#ifndef PWMOUT_H
 
#define PWMOUT_H
 

	
 

	
 
#define SSR_PIN GPIO_PIN_7
 
#define SSR_GPIO_Port GPIOB
 
#define SSR SSR_GPIO_Port, SSR_PIN
 

	
 

	
 

	
 
void pwmout_init(void);
 
void pwmout_process(float duty);
 

	
 

	
 
#endif
inc/system/gpio.h
Show inline comments
 
#ifndef __gpio_H
 
#define __gpio_H
 
 
#include "stm32f3xx_hal.h"
 
 
 
// Define button push
 
#define SW_D_Pin GPIO_PIN_4
 
#define SW_D_GPIO_Port GPIOA
 
#define SW_RIGHT SW_D_GPIO_Port, SW_D_Pin
 
 
#define SW_B_Pin GPIO_PIN_5
 
#define SW_B_GPIO_Port GPIOA
 
#define SW_UP SW_B_GPIO_Port, SW_B_Pin
 
 
#define SW_A_Pin GPIO_PIN_6
 
#define SW_A_GPIO_Port GPIOA
 
#define SW_LEFT SW_A_GPIO_Port, SW_A_Pin
 
 
#define SW_C_Pin GPIO_PIN_7
 
#define SW_C_GPIO_Port GPIOA
 
#define SW_DOWN SW_C_GPIO_Port, SW_C_Pin
 
 
#define SW_BTN_Pin GPIO_PIN_0
 
#define SW_BTN_GPIO_Port GPIOB
 
#define SW_BTN SW_BTN_GPIO_Port , SW_BTN_Pin
 
 
#define LED_PIN GPIO_PIN_6
 
#define LED_GPIO_Port GPIOB
 
#define LED LED_GPIO_Port, LED_PIN
 
 
/// SSR PIN ???///
 
 
void user_input(uint16_t* to_modify);
 
void user_input_signed(int32_t* to_modify);
 
 
void gpio_init(void);
 
void gpio_led_blueblink(uint8_t num_blinks);
 
 
#endif
src/display.c
Show inline comments
 
//
 
// Display: render menus on OLED display
 
//
 

	
 
#include "display.h"
 
#include "gpio.h"
 
#include "ssd1306/ssd1306.h"
 
#include "system/stringhelpers.h"
 
#include "flash.h"
 

	
 
// Private function prototypes
 
static void draw_setpoint(therm_status_t* status);
 

	
 

	
 
// Button transition variables
 
static uint8_t sw_btn_last = 0;
 
static uint8_t sw_up_last = 0;
 
static uint8_t sw_down_last = 0;
 
static uint8_t sw_left_last = 0;
 
static uint8_t sw_right_last = 0;
 

	
 

	
 
// Buttonpress macros
 
#define SW_BTN_PRESSED (sw_btn_last == 0 && sw_btn == 1) // rising edge on buttonpress
 
#define SW_UP_PRESSED (sw_up_last == 0 && sw_up == 1)
 
#define SW_DOWN_PRESSED (sw_down_last == 0 && sw_down == 1)
 
#define SW_LEFT_PRESSED (sw_left_last == 0 && sw_left == 1)
 
#define SW_RIGHT_PRESSED (sw_right_last == 0 && sw_right == 1)
 

	
 

	
 
// States
 
static uint8_t trigger_drawsetpoint = 1;
 
static int16_t last_temp = 21245;
 
static int16_t last_temp_frac = 21245;
 
static int16_t last_state = 123;
 
static uint8_t goto_mode = MODE_HEAT;
 
static uint8_t reset_mode = RESET_REBOOT;
 

	
 
static char* sensor_lookup[] = {"NTC", "K  ", "E  ", "N  ", "R  ", "S  ", "T  "};
 

	
 
static uint8_t toggle = 0;
 

	
 
void display_1hz(void)
 
{
 
	toggle = !toggle;
 
}
 

	
 
static char updown(void)
 
{
 
	if(toggle)
 
		return '\x8f';
 
	else
 
		return '\x90';
 
}
 

	
 
// Display state machine
 
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;
 
    last_temp = status->temp;
 

	
 
    uint8_t sw_btn = !HAL_GPIO_ReadPin(SW_BTN);
 
    uint8_t sw_up = !HAL_GPIO_ReadPin(SW_UP);
 
    uint8_t sw_down = !HAL_GPIO_ReadPin(SW_DOWN);
 
    uint8_t sw_left = !HAL_GPIO_ReadPin(SW_LEFT);
 
    uint8_t sw_right = !HAL_GPIO_ReadPin(SW_RIGHT);
 

	
 
    switch(status->state)
 
    {
 
        // Idle state
 
        case STATE_IDLE:
 
        {
 
            // Write text to OLED
 
            // [ therm :: idle ]
 
            ssd1306_drawstring("therm \x87 idle ", 0, 40);
 
            status->pid_enabled = 0;
 

	
 
            if(temp_changed || state_changed) {
 
                char tempstr[16];
 
                snprintf(tempstr, 16, "Temp: %g", status->temp);
 
                ssd1306_drawstring("             ", 3, 40);
 

	
 
                ssd1306_drawstring(tempstr, 3, 40);
 
            }
 

	
 
            if (state_changed) {
 
            	ssd1306_drawlogo();
 
            }
 

	
 
            switch(goto_mode) {
 

	
 
                case MODE_HEAT:
 
                {
 
					if(set->val.plant_type == PLANT_HEATER)
 
						ssd1306_drawstring("\x83 heat      ", 1, 40);
 
					else
 
@@ -120,403 +128,415 @@ void display_process(void)
 
                switch(goto_mode) {
 
                    case MODE_HEAT:
 
                        status->state = STATE_PREHEAT;
 
                        break;
 
                    case MODE_SETUP:
 
                        status->state = STATE_SETSENSORTYPE;
 
                        break;
 
                    case MODE_RESET:
 
                        status->state = STATE_RESET;
 
                        reset_mode = RESET_REBOOT;
 
                        break;
 
					#ifdef BOOTLOADER_SHORTCUT
 
                    case MODE_BOOTLOADER:
 
                        ssd1306_clearscreen();
 
                        ssd1306_drawstring("Bootloader Entered", 0, 0);
 
                        ssd1306_drawstring("Device won't boot", 2, 0);
 
                        ssd1306_drawstring("until reflashed!", 3, 0);
 
//                        bootloader_enter(); // Resets into bootloader
 
                        status->state = STATE_RESET; // Just in case
 
                        break;
 
					#endif
 
                    default:
 
                        status->state = STATE_PREHEAT;
 
                }
 
            }
 
            else if(SW_DOWN_PRESSED && goto_mode < (MODE_SIZE - 1)) {
 
                goto_mode++;
 
            }
 
            else if(SW_UP_PRESSED && goto_mode > 0) {
 
                goto_mode--;
 
            }
 

	
 

	
 
            // Event Handler
 
            // N/A
 

	
 
        } break;
 

	
 

	
 

	
 
        case STATE_SETSENSORTYPE:
 
        {
 
            // Write text to OLED
 
            // [ therm :: set mode ]
 
            // [ m =          ]
 
            ssd1306_drawstring("Sensor Type", 0, 40);
 
            ssd1306_drawlogo();
 

	
 
            ssd1306_drawchar(updown(), 1, 52);
 
			ssd1306_drawstring(sensor_lookup[set->val.sensor_type], 1, 60);
 
            ssd1306_drawstring("Press to accept", 3, 40);
 

	
 
            // Button handler
 
            if(SW_BTN_PRESSED) {
 
                status->state = STATE_SETMODE;
 
            }
 
            else
 
            {
 
            	user_input((uint16_t*)&set->val.sensor_type);
 
                if(set->val.sensor_type > 6)
 
                	set->val.sensor_type = 6;
 

	
 
            }
 
            // Event Handler
 
            // N/A
 

	
 
        } break;
 

	
 
        case STATE_SETMODE:
 
        {
 
            // Write text to OLED
 
            // [ therm :: set mode ]
 
            // [ m =          ]
 
            ssd1306_drawstring("Control Mode", 0, 40);
 
            ssd1306_drawlogo();
 

	
 
            ssd1306_drawchar(updown(), 1, 52);
 

	
 

	
 
            if(set->val.control_mode == MODE_PID)
 
                ssd1306_drawstring("PID       ", 1, 60);
 
            else
 
                ssd1306_drawstring("Thermostat", 1, 60);
 

	
 
            ssd1306_drawstring("Press to accept", 3, 40);
 
            
 
            // Button handler
 
            if(SW_BTN_PRESSED) {
 
                status->state = STATE_SETPLANTTYPE;
 
            }
 
            else if (!HAL_GPIO_ReadPin(SW_UP)) {
 
                set->val.control_mode = MODE_PID;
 
            }
 
            else if(!HAL_GPIO_ReadPin(SW_DOWN)) {
 
                set->val.control_mode = MODE_THERMOSTAT;
 
            }
 
            // Event Handler
 
            // N/A
 
 
 
        } break;
 

	
 

	
 
        case STATE_SETPLANTTYPE:
 
        {
 
            // Write text to OLED
 
            // [ therm :: set mode ]
 
            // [ m =          ]
 
            ssd1306_drawstring("Plant Type", 0, 40);
 
            ssd1306_drawlogo();
 

	
 
            ssd1306_drawchar(updown(), 1, 52	);
 

	
 
            if(set->val.plant_type == PLANT_HEATER)
 
                ssd1306_drawstring("Heater", 1, 60);
 
            else
 
                ssd1306_drawstring("Cooler", 1, 60);
 

	
 
            ssd1306_drawstring("Press to accept", 3, 40);
 
            
 
            // Button handler
 
            if(SW_BTN_PRESSED) {
 
                if(set->val.control_mode == MODE_PID)
 
                    status->state = STATE_SETP;
 
                else
 
                    status->state = STATE_SETHYSTERESIS;
 
            }
 
            else if (!HAL_GPIO_ReadPin(SW_UP)) {
 
                set->val.plant_type = PLANT_COOLER;
 
            }
 
            else if(!HAL_GPIO_ReadPin(SW_DOWN)) {
 
                set->val.plant_type = PLANT_HEATER;
 
            }
 
            // Event Handler
 
            // N/A
 
 
 
        } break;
 

	
 

	
 

	
 
        case STATE_SETHYSTERESIS:
 
        {
 
            // Write text to OLED
 
            ssd1306_drawstring("Hysteresis", 0, 40);
 
            ssd1306_drawlogo();
 

	
 
            char tempstr[6];
 
            itoa(set->val.hysteresis, tempstr, 10);
 
            ssd1306_drawstring("H=", 1, 45);
 
            ssd1306_drawstring("    ", 1, 57);
 
            ssd1306_drawstring(tempstr, 1, 57);
 

	
 
            ssd1306_drawstring("Press to accept", 3, 40);
 
            
 
            // Button handler
 
            if(SW_BTN_PRESSED) {
 
                status->state = STATE_SETBOOTTOBREW;
 
            }
 
            else {
 
                user_input((uint16_t*)&set->val.hysteresis);
 
            }
 

	
 
            // Event Handler
 
            // N/A
 
 
 
        } break;
 

	
 

	
 
        case STATE_SETP:
 
        {
 
            // Write text to OLED
 
            // [ therm :: set p ]
 
            // [ p = 12         ]
 
            ssd1306_drawstring("Proportional", 0, 40);
 
            ssd1306_drawlogo();
 

	
 
            char tempstr[6];
 
            itoa(set->val.k_p, tempstr, 10);
 
            ssd1306_drawstring("P=", 1, 45);
 
            ssd1306_drawstring("    ", 1, 57);
 
            ssd1306_drawstring(tempstr, 1, 57);
 
            ssd1306_drawchar(updown(), 1, 52);
 

	
 
            char tempstr[12];
 
            snprintf(tempstr, 12, "P=%d", set->val.k_p);
 
            ssd1306_drawstring(tempstr, 1, 60);
 
            ssd1306_drawstring("Press to accept", 3, 40);
 
            
 
            // Button handler
 
            if(SW_BTN_PRESSED) {
 
                status->state = STATE_SETI;
 
            }
 
            else {
 
                user_input((uint16_t*)&set->val.k_p);
 
            }
 

	
 
            // Event Handler
 
            // N/A
 
 
 
        } break;
 

	
 
        case STATE_SETI:
 
        {
 
            // Write text to OLED
 
            // [ therm :: set i ]
 
            // [ i = 12         ]
 
            ssd1306_drawstring("Integral", 0, 40);
 
            ssd1306_drawlogo();
 

	
 
            char tempstr[6];
 
            itoa(set->val.k_i, tempstr, 10);
 
            ssd1306_drawstring("I=", 1, 45);
 
            ssd1306_drawstring("    ", 1, 57);
 
            ssd1306_drawstring(tempstr, 1, 57);
 
            ssd1306_drawchar(updown(), 1, 52);
 

	
 
            char tempstr[12];
 
            snprintf(tempstr, 12, "I=%d", set->val.k_i);
 
            ssd1306_drawstring(tempstr, 1, 60);
 

	
 
            ssd1306_drawstring("Press to accept", 3, 40);
 
            
 
            // Button handler
 
            if(SW_BTN_PRESSED) {
 
                status->state = STATE_SETD;
 
            }
 
            else {
 
                user_input((uint16_t*)&set->val.k_i);
 
            }
 

	
 
            // Event Handler
 
            // N/A
 
 
 
        } break;
 

	
 
        case STATE_SETD:
 
        {
 
            // Write text to OLED
 
            // [ therm :: set d ]
 
            // [ d = 12         ]
 
            ssd1306_drawstring("Derivative", 0, 40);
 
            ssd1306_drawlogo();
 

	
 
            char tempstr[6];
 
            itoa(set->val.k_d, tempstr, 10);
 
            ssd1306_drawstring("D=", 1, 45);
 
            ssd1306_drawstring("    ", 1, 57);
 
            ssd1306_drawstring(tempstr, 1, 57);
 
            ssd1306_drawchar(updown(), 1, 52);
 

	
 
            char tempstr[12];
 
            snprintf(tempstr, 12, "D=%d", set->val.k_d);
 
            ssd1306_drawstring(tempstr, 1, 60);
 

	
 
            ssd1306_drawstring("Press to accept", 3, 40);
 

	
 
            // Button handler
 
            if(SW_BTN_PRESSED) {
 
                status->state = STATE_SETWINDUP;
 
            }
 
            else {
 
                user_input((uint16_t*)&set->val.k_d);
 
            }
 

	
 
            // Event Handler
 
            // N/A
 
 
 
        } break;
 

	
 

	
 

	
 
        case STATE_SETHYSTERESIS:
 
        {
 
            // Write text to OLED
 
            ssd1306_drawstring("Hysteresis", 0, 40);
 
            ssd1306_drawlogo();
 

	
 
            ssd1306_drawchar(updown(), 1, 52);
 

	
 
            char tempstr[12];
 
            snprintf(tempstr, 12, "H=%d", set->val.hysteresis);
 
            ssd1306_drawstring(tempstr, 1, 60);
 

	
 
            ssd1306_drawstring("Press to accept", 3, 40);
 

	
 
            // Button handler
 
            if(SW_BTN_PRESSED) {
 
                status->state = STATE_SETBOOTTOBREW;
 
            }
 
            else {
 
                user_input((uint16_t*)&set->val.hysteresis);
 
            }
 

	
 
            // Event Handler
 
            // N/A
 

	
 
        } break;
 

	
 

	
 
        case STATE_SETWINDUP:
 
        {
 
            // Write text to OLED
 
            // [ therm :: set windup ]
 
            // [ g = 12         ]
 
            ssd1306_drawstring("Windup Guard", 0, 40);
 
            ssd1306_drawlogo();
 

	
 
            char tempstr[6];
 
            itoa(set->val.windup_guard, tempstr, 10);
 
            ssd1306_drawstring("G=", 1, 45);
 
            ssd1306_drawstring("    ", 1, 57);
 
            ssd1306_drawstring(tempstr, 1, 57);
 
            ssd1306_drawchar(updown(), 1, 52);
 

	
 
            char tempstr[12];
 
            snprintf(tempstr, 12, "G=%d", set->val.windup_guard);
 
            ssd1306_drawstring(tempstr, 1, 60);
 

	
 
            ssd1306_drawstring("Press to accept", 3, 40);
 

	
 
            // Button handler
 
            if(SW_BTN_PRESSED) {
 
                status->state = STATE_SETBOOTTOBREW;
 
            }
 
            else {
 
                user_input((uint16_t*)&set->val.windup_guard);
 
            }
 

	
 
            // Event Handler
 
            // N/A
 
 
 
        } break;
 

	
 
        case STATE_SETBOOTTOBREW:
 
        {
 
            // Write text to OLED
 
            // [ therm :: set windup ]
 
            // [ g = 12         ]
 
            ssd1306_drawstring("Start on Boot", 0, 40);
 
            ssd1306_drawlogo();
 

	
 
            ssd1306_drawstring("sob=", 1, 45);
 
            ssd1306_drawstring("sob=", 1, 50);
 
            
 
            ssd1306_drawchar(updown(), 1, 43);
 

	
 
            if(set->val.boottobrew)
 
                ssd1306_drawstring("Enabled ", 1, 70);
 
                ssd1306_drawstring("Enable ", 1, 75);
 
            else
 
                ssd1306_drawstring("Disabled", 1, 70);
 
                ssd1306_drawstring("Disable", 1, 75);
 

	
 
            ssd1306_drawstring("Press to accept", 3, 40);
 

	
 
            // Button handler
 
            if(SW_BTN_PRESSED) {
 
                status->state = STATE_SETUNITS;
 
            }
 
            else if(!HAL_GPIO_ReadPin(SW_UP)) {
 
                set->val.boottobrew = 1;
 
            }
 
            else if(!HAL_GPIO_ReadPin(SW_DOWN)) {
 
                set->val.boottobrew = 0;
 
            }
 

	
 
            // Event Handler
 
            // N/A
 
 
 
        } break;
 

	
 
        case STATE_SETUNITS:
 
        {
 
            // Write text to OLED
 
            // [ therm :: set windup ]
 
            // [ g = 12         ]
 
            ssd1306_drawstring("Units: ", 0, 40);
 
            ssd1306_drawlogo();
 

	
 
            ssd1306_drawchar(updown(), 1, 52);
 

	
 
            if(set->val.temp_units == TEMP_UNITS_FAHRENHEIT)
 
                ssd1306_drawstring("Fahrenheit", 1, 60);
 
            else
 
                ssd1306_drawstring("Celsius   ", 1, 60);
 

	
 
            ssd1306_drawstring("Press to accept", 3, 40);
 

	
 
            // Button handler
 
            if(SW_BTN_PRESSED) {
 
                status->state = STATE_SETTEMPOFFSET;
 
            }
 
            else if(!HAL_GPIO_ReadPin(SW_UP)) {
 
                set->val.temp_units = TEMP_UNITS_FAHRENHEIT;
 
            }
 
            else if(!HAL_GPIO_ReadPin(SW_DOWN)) {
 
                set->val.temp_units = TEMP_UNITS_CELSIUS;
 
            }
 

	
 
            // Event Handler
 
            // N/A
 
 
 
        } break;
 

	
 

	
 
        case STATE_SETTEMPOFFSET:
 
        {
 
            // Write text to OLED
 
            // [ therm :: set temp offset ]
 
            // [ g = 12         ]
 
            ssd1306_drawstring("Temp Cal Offset", 0, 40);
 
            ssd1306_drawlogo();
 

	
 
            char tempstr[6];
 
            itoa(set->val.temp_offset, tempstr, 10);
 
            ssd1306_drawstring("O=", 1, 45);
 
            ssd1306_drawstring("    ", 1, 57);
 
            ssd1306_drawstring(tempstr, 1, 57);
 
            ssd1306_drawchar(updown(), 1, 52);
 

	
 
            char tempstr[12];
 
            snprintf(tempstr, 12, "O=%d", set->val.temp_offset);
 
            ssd1306_drawstring(tempstr, 1, 60);
 

	
 
            ssd1306_drawstring("Press to accept", 3, 40);
 

	
 
            // Button handler
 
            if(SW_BTN_PRESSED) {
 
//                flash_save(set);
 
                status->state = STATE_IDLE;
 
            }
 
            else {
 
                user_input_signed(&set->val.temp_offset);
 
            }
 

	
 
            // Event Handler
 
            // N/A
 
 
 
        } break;
 

	
 

	
 
        case STATE_PREHEAT:
 
        {
 
            // Write text to OLED
 
            // [ therm : preheating brew ]
 
            // [ 30 => 120 C             ]
 
            if(set->val.plant_type == PLANT_HEATER)
 
                ssd1306_drawstring("Preheating...", 0, 0);
 
            else
 
                ssd1306_drawstring("Precooling...", 0, 0);
 

	
 
            //ssd1306_drawlogo();
 
            draw_setpoint(status);
 

	
 
            status->pid_enabled = 1;
 
            status->setpoint = set->val.setpoint_brew;
 

	
 
            // Button handler
 
            if(SW_BTN_PRESSED) {
 
                status->state = STATE_IDLE;
 
            }
 
            else {
 
                user_input_signed(&set->val.setpoint_brew);
 
            }
 

	
 
            // Event Handler
 
            if(status->temp >= status->setpoint) {
 
                status->state = STATE_MAINTAIN;
 
            }
 
 
 
        } break;
 
@@ -664,65 +684,65 @@ void display_process(void)
 

	
 
            // Event Handler
 
            // N/A
 

	
 
        } break;
 

	
 
        // Something is terribly wrong
 
        default:
 
        {
 
            status->state = STATE_IDLE;
 
            status->pid_enabled = 0;
 

	
 
        } break;
 
            
 
    }
 

	
 
    if(last_state != status->state) {
 
        // Clear screen on state change
 
        goto_mode = MODE_HEAT;
 
        trigger_drawsetpoint = 1;
 
        ssd1306_clearscreen();
 
    }
 

	
 
    // Last buttonpress
 
    sw_btn_last = sw_btn;
 
    sw_up_last = sw_up;
 
    sw_down_last = sw_down;
 
    sw_left_last = sw_left;
 
    sw_right_last = sw_right;
 
}
 

	
 

	
 
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 || trigger_drawsetpoint) {
 
        char tempstr[8];
 
        snprintf(tempstr, 8, "%g     ", status->temp);
 
        ssd1306_drawstringbig(tempstr, 3, 0);
 
    }
 

	
 
    if(trigger_drawsetpoint) 
 
        ssd1306_drawstringbig(">", 3, 74);
 

	
 
    if(status->setpoint != setpoint_last || trigger_drawsetpoint) {
 
        char tempstr[5];
 
        snprintf(tempstr, 5, "%g     ", status->setpoint);
 
        char tempstr[4];
 
        snprintf(tempstr, 4, "%g     ", status->setpoint);
 
        ssd1306_drawstringbig(tempstr, 3, 90);
 
    }
 

	
 
    trigger_drawsetpoint = 0;
 
    setpoint_last = status->setpoint;
 
    temp_last = status->temp;
 
}
 

	
 
void display_startup_screen() {
 
    ssd1306_clearscreen();
 
    ssd1306_drawstring("therm v0.4", 1, 40);
 
    ssd1306_drawstring("protofusion.org/therm", 3, 0);
 
}
 

	
 
// vim:softtabstop=4 shiftwidth=4 expandtab 
src/main.c
Show inline comments
 
//
 
// Therm Firmware
 
// Copyright 2017 Ethan Zonca
 
// Author(s): Ethan Zonca
 
//
 
 
#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 "pwmout.h"
 
 
 
int main(void)
 
{
 
	sysclock_init();
 
	hal_init();
 
	gpio_init();
 
 
	ssd1306_init();
 
 
	// Startup screen
 
    display_startup_screen();
 
    HAL_Delay(2000);
 
 
    ssd1306_clearscreen();
 
	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;
 
 
    pid_init();
 
    pwmout_init();
 
    flash_init();
 
	watchdog_init();
 
	tempsense_init();
 
 
	// Soft timers
 
    uint32_t last_pid = 0;
 
    uint32_t last_thermostat = 0;
 
    uint32_t last_1hz = 0;
 
    uint32_t last_5hz = 0;
 
 
	while (1)
 
	{
 
		float duty = 0.0;
 
 
		if(HAL_GetTick() - last_1hz > 750)
 
		{
 
			display_1hz();
 
			last_1hz = HAL_GetTick();
 
		}
 
 
		if(HAL_GetTick() - last_5hz > 200)
 
		{
 
			tempsense_readtemp();
 
//			runtime_status = tempsense_gettemp();
 
			last_5hz = HAL_GetTick();
 
		}
 
 
        if(flash_getsettings()->val.control_mode == MODE_PID && (HAL_GetTick() - last_pid > PID_PERIOD))
 
        {
 
//        	runtime_status()->temp = tempsense_readtemp();
 
//        	duty = pid_process();
 
        	duty = pid_process();
 
            last_pid = HAL_GetTick();
 
        }
 
 
        // Thermostatic control
 
        if(flash_getsettings()->val.control_mode == MODE_THERMOSTAT && HAL_GetTick() - last_thermostat > SSR_PERIOD)
 
        {
 
//        	runtime_status()->temp = tempsense_readtemp();
 
//        	duty = thermostat_process();
 
        	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)
 
//        {
 
//            // Print temp to cdc
 
//            char tempstr[16];
 
//            itoa_fp(status.temp, status.temp_frac, tempstr);
 
//            uint8_t numlen = strlen(tempstr);
 
//            tempstr[numlen] = '\r';
 
//            tempstr[numlen+1] = '\n';
 
//
 
//    //        if(set.val.usb_plugged)
 
//    //            CDC_Transmit_FS(tempstr, numlen+2);
 
//           // while(CDC_Transmit_FS("\r\n", 2) == USBD_BUSY);
 
//
 
//            last_vcp_tx = HAL_GetTick();
 
//        }
 
 
	}
 
}
 
 
src/pid.c
Show inline comments
 
//
 
// PID: proportional/integral/derivative controller
 
//
 

	
 
#include "pid.h"
 
#include "flash.h"
 

	
 
// PID implementation
 

	
 
static pid_state_t state;
 

	
 

	
 
void pid_init()
 
{
 
	state.i_state = 0;
 
	state.last_pid_temp = 0;
 
	state.last_pid_temp_frac = 0;
 
}
 

	
 
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);
 
		snprintf(tempstr, 6, "%g", ssr_output);
 
		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
 
	float temp = status->temp;
 

	
 
	// Calculate instantaneous error
 
	int16_t error = status->setpoint * 10 - temp; // TODO: Use fixed point fraction
 

	
 
	// Proportional component
 
	int32_t p_term = set->val.k_p * error;
 

	
 
	// Error accumulator (integrator)
 
	state.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 * 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;
 

	
 
	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));
 

	
 
	// Save temperature for next iteration
 
	state.last_pid_temp = temp;
src/pwmout.c
Show inline comments
 
//
 
// PWM Out: generate PWM waveform to control factory
 
//
 

	
 
#include "pwmout.h"
 
#include "gpio.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)
 
{
 
	GPIO_InitTypeDef GPIO_InitStruct;
 

	
 
	// Configure LED GPIO pins
 
	GPIO_InitStruct.Pin = SSR_PIN;
 
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
 
	GPIO_InitStruct.Pull = GPIO_NOPULL;
 
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
 
	HAL_GPIO_Init(SSR_GPIO_Port, &GPIO_InitStruct);
 

	
 
}
 

	
 

	
 
// freaking integral term isn't compatible with both heating and cooling... due to the discard
 
// functionality. this is a problem.
 

	
 
// also duty cycling isn't working correctly...
 
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);
 
	//            }
 
	//
 
	//        }
 
	uint32_t ssr_output = duty; // meh
 

	
 
	// Kill SSR once the desired on-time has elapsed
 
	if(flash_getsettings()->val.control_mode == MODE_PID && ((HAL_GetTick() - last_ssr_on > ssr_output) || ssr_output <= 0))
 
	{
 
		HAL_GPIO_WritePin(SSR, 0);
 
		HAL_GPIO_WritePin(LED, 0);
 
	}
 

	
 

	
 
	// Every 200ms, set the SSR on unless output is 0
 
	if(flash_getsettings()->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, 1);
 
			HAL_GPIO_WritePin(LED, 1);
 
			last_ssr_on = HAL_GetTick();
 
		}
 
		else {
 
			// Make sure everything is off
 
			HAL_GPIO_WritePin(SSR, 0);
 
			HAL_GPIO_WritePin(LED, 0);
 
		}
 

	
 
	}
 
}
 

	
 

	
src/system/flash.c
Show inline comments
 
@@ -24,64 +24,68 @@ void flash_savesettings()
 
{
 
	// Unlock flash memory
 
	HAL_FLASH_Unlock();
 
 
	// Initialize eraser to erase one page of flash
 
	FLASH_EraseInitTypeDef eraser =
 
	{
 
			.TypeErase = TYPEERASE_PAGES,
 
			.PageAddress = eeprom_emulation,
 
			.NbPages = 1,
 
	};
 
	uint32_t errvar = 0;
 
 
	// Erase flash page
 
	HAL_FLASHEx_Erase(&eraser, &errvar);
 
 
	// Write new flash data
 
	uint16_t writectr;
 
	for(writectr = 0; writectr < 128; writectr++)// 128 bytes data
 
	{
 
		HAL_FLASH_Program(TYPEPROGRAM_HALFWORD, eeprom_emulation+writectr,settings.data[writectr]);
 
	}
 
 
	// Write magic value to flash
 
	HAL_FLASH_Program(TYPEPROGRAM_HALFWORD, eeprom_emulation+FLASH_MAGIC_LOC,FLASH_MAGIC_VALUE);
 
 
	// Lock flash memory
 
	HAL_FLASH_Lock();
 
	HAL_Delay(2);
 
}
 
 
 
// Restore configuration from flash memory, if any was previously saved
 
void flash_restoresettings(void)
 
{
 
	// Check for magic flash value
 
	if(eeprom_emulation[FLASH_MAGIC_LOC] == FLASH_MAGIC_VALUE)
 
	{
 
		// Read page of flash into settings structure
 
		uint16_t readctr = 0;
 
		for(readctr = 0; readctr < 128; readctr++)
 
		{
 
			settings.data[readctr] = eeprom_emulation[readctr];
 
		}
 
	}
 
	// No data in flash! Set defaults here
 
	else
 
	{
 
		settings.val.k_p = 10;
 
		settings.val.k_i = 0;
 
		settings.val.k_d = 0;
 
		settings.val.windup_guard = 300;
 
		//torestore.values.can_id = 22;
 
	}
 
}
 
 
 
// Accessor to retrieve settings structure
 
inline therm_settings_t* flash_getsettings(void)
 
{
 
	return &settings;
 
}
 
 
inline therm_status_t* runtime_status(void)
 
{
 
	return &status;
 
}
 
src/tempsense.c
Show inline comments
 
@@ -5,48 +5,49 @@
 
#include "tempsense.h"
 
#include "flash.h"
 
#include "ssd1306/ssd1306.h"
 
#include "max31856/max31856.h"
 

	
 

	
 
void tempsense_init(void)
 
{
 
	// TODO: Rework SPI stuff... init the port in a sharedlib then pass ref to display and tempsense
 

	
 
	max31856_init(spi_get(), TEMPSENSE_MAX_CS_PORT, TEMPSENSE_MAX_CS_PIN, 0);
 

	
 
	// Maybe don't perform temp sensor setup in here, but in readtemp?
 
	// what happens if the user changes the tempsense type while running?
 
	// we need to re-init...
 

	
 

	
 
}
 

	
 
// Returns the latest reading from the configured temperature sensor
 
float tempsense_readtemp(void)
 
{
 

	
 
	switch(flash_getsettings()->val.sensor_type)
 
	{
 
		case SENSOR_TC_K:
 
		case SENSOR_TC_E:
 
		case SENSOR_TC_N:
 
		case SENSOR_TC_R:
 
		case SENSOR_TC_S:
 
		case SENSOR_TC_T:
 
		{
 
			// Read MAX31856
 
		} break;
 

	
 
		case SENSOR_NTC:
 
		{
 
			// Read analog value from internal ADC, linearize the reading, etc
 
		} break;
 

	
 
	}
 

	
 
	// either return latest reading from DMA loop (NTC, etc)
 
	// or initiate a blocking read and return it.
 
	// Need to gracefully handle the timeout...
 
}
 

	
 

	
 

	
0 comments (0 inline, 0 general)