/* Reflow Oven Controller * * Copyright (C) 2021 Mario Hüttel * * This file is part of the Reflow Oven Controller Project. * * The reflow oven controller is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * The Reflow Oven Control Firmware is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with the reflow oven controller project. * If not, see . */ #include #include #include #include #include #include #include static struct tpe_current_state IN_SECTION(.ccm.data) state = { .status = TPE_OFF, .start_timestamp = 0, }; static bool IN_SECTION(.ccm.bss) pid_should_run; struct pid_controller IN_SECTION(.ccm.bss) pid; static struct pl_command IN_SECTION(.ccm.bss) cmd_list[MAX_PROFILE_LENGTH]; static void abort(void) { temp_profile_executer_stop(); state.status = TPE_ABORT; } enum pl_ret_val temp_profile_executer_start(const char *filename) { uint32_t parsed_count = 0; enum pl_ret_val res; state.setpoint = 0.0f; state.start_timestamp = 0ULL; state.setpoint = 0.0f; state.step = 0; state.profile_steps = 0; oven_pid_stop(); pid_should_run = false; res = temp_profile_parse_from_file(filename, cmd_list, MAX_PROFILE_LENGTH, &parsed_count); if (res == PL_RET_SUCCESS) { state.profile_steps = parsed_count; state.status = TPE_RUNNING; state.start_timestamp = systick_get_global_tick(); } return res; } static bool cmd_wait_temp(struct pl_command *cmd, bool cmd_continue) { (void)cmd_continue; float resistance; int res; float temp; res = adc_pt1000_get_current_resistance(&resistance); if (res < 0) { abort(); return false; } (void)temp_converter_convert_resistance_to_temp(resistance, &temp); if (ABS(cmd->params[0] - temp) < 3.0f) return true; return false; } static bool cmd_wait_time(struct pl_command *cmd, bool cmd_continue) { static uint64_t temp_tick = 0UL; if (cmd_continue) { if (systick_ticks_have_passed(temp_tick, (uint64_t)(cmd->params[0] * 1000.0f))) return true; } else { temp_tick = systick_get_global_tick(); } return false; } static void reactivate_pid_if_suspended(void) { if (oven_pid_get_status() == OVEN_PID_DEACTIVATED) oven_pid_init(&pid); pid_should_run = true; } static bool cmd_set_temp(struct pl_command *cmd, bool cmd_continue) { (void)cmd_continue; reactivate_pid_if_suspended(); oven_pid_set_target_temperature(cmd->params[0]); return true; } int temp_profile_executer_handle(void) { struct pl_command *current_cmd; static uint64_t last_tick = 0UL; bool advance; static bool cmd_continue = false; uint32_t next_step; if (state.status != TPE_RUNNING) return -1; if (oven_pid_get_status() == OVEN_PID_ABORTED && pid_should_run) { abort(); oven_pid_stop(); return -1; } if (systick_ticks_have_passed(last_tick, 100)) { current_cmd = &cmd_list[state.step]; next_step = state.step; switch (current_cmd->cmd) { case PL_WAIT_FOR_TIME: advance = cmd_wait_time(current_cmd, cmd_continue); break; case PL_WAIT_FOR_TEMP: advance = cmd_wait_temp(current_cmd, cmd_continue); break; case PL_SET_TEMP: advance = cmd_set_temp(current_cmd, cmd_continue); break; case PL_LOUDSPEAKER_SET: loudspeaker_set((uint16_t)current_cmd->params[0]); advance = true; break; case PL_OFF: oven_pid_stop(); pid_should_run = false; advance = true; break; case PL_PID_CONF: pid_init(&pid, current_cmd->params[0], /* Kd */ current_cmd->params[1], /* Ki */ current_cmd->params[2], /* Kp */ 0.0f, 0.0f, current_cmd->params[3], /* Int max */ current_cmd->params[4], /* Kd tau */ current_cmd->params[5]); /* Period */ oven_pid_init(&pid); advance = true; pid_should_run = true; break; case PL_SET_RAMP: advance = true; break; default: abort(); advance = true; break; } if (advance) next_step++; if (next_step != state.step) { state.step = next_step; if (next_step >= state.profile_steps) { (void)temp_profile_executer_stop(); } else { cmd_continue = false; } } else { cmd_continue = true; } last_tick = systick_get_global_tick(); } return 0; } const struct tpe_current_state *temp_profile_executer_status(void) { return &state; } int temp_profile_executer_stop(void) { if (state.status == TPE_RUNNING) { state.status = TPE_OFF; oven_pid_stop(); } loudspeaker_set(0); return 0; }