/* Reflow Oven Controller * * Copyright (C) 2020 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 . */ /** * @addtogroup oven-driver * @{ */ #include #include #include #include #include #include #include #include #include /** * @brief PID controller instance of the oven driver */ static struct pid_controller IN_SECTION(.ccm.bss) oven_pid; /** * @brief Oven PID is currently running */ static bool IN_SECTION(.ccm.bss) oven_pid_running; /** * @brief Oven PID has been aborted / abnormally stopped. */ static bool IN_SECTION(.ccm.bss) oven_pid_aborted; /** * @brief Power level [0..100] of the oven to be applied */ static uint8_t IN_SECTION(.ccm.bss) oven_driver_power_level; /** * @brief Current target temperature of the oven PID controller in degC */ static float IN_SECTION(.ccm.bss) target_temp; /** * @brief The millisecond timestamp of the last run of the PID controller */ static uint64_t IN_SECTION(.ccm.bss) timestamp_last_run; /** * @brief Enable or disable the safety enable line of the oven control relay. * @param enable * @note This function is only working for hardware revisions >= v1.3. Below, * the safety enable is unavailable. */ static void ssr_safety_en(bool enable) { if (get_pcb_hardware_version() >= HW_REV_V1_3) { if (enable) SSR_SAFETY_EN_PORT->ODR |= (1<ODR &= ~(1<AHB1ENR, BITMASK_TO_BITNO(OVEN_CONTROLLER_PORT_RCC_MASK)); rcc_manager_enable_clock(&RCC->APB1ENR, BITMASK_TO_BITNO(OVEN_CONTROLLER_TIM_RCC_MASK)); OVEN_CONTROLLER_PORT->MODER &= MODER_DELETE(OVEN_CONTROLLER_PIN); OVEN_CONTROLLER_PORT->MODER |= ALTFUNC(OVEN_CONTROLLER_PIN); SETAF(OVEN_CONTROLLER_PORT, OVEN_CONTROLLER_PIN, OVEN_CONTROLLER_PIN_ALTFUNC); OVEN_CONTROLLER_PWM_TIMER->CR2 = 0UL; OVEN_CONTROLLER_PWM_TIMER->CCMR2 = TIM_CCMR2_OC3M; OVEN_CONTROLLER_PWM_TIMER->CCER = TIM_CCER_CC3E | TIM_CCER_CC3P; OVEN_CONTROLLER_PWM_TIMER->ARR = 1000U; OVEN_CONTROLLER_PWM_TIMER->PSC = 42000U - 1U; OVEN_CONTROLLER_PWM_TIMER->CR1 = TIM_CR1_CMS | TIM_CR1_CEN; /* Explicitly init global variables */ oven_pid_aborted = false; oven_pid_running = false; if (get_pcb_hardware_version() >= HW_REV_V1_3) { /* Init the safety SSR enable signal */ rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(SSR_SAFETY_EN_PORT_RCC_MASK)); SSR_SAFETY_EN_PORT->MODER &= MODER_DELETE(SSR_SAFETY_EN_PIN); SSR_SAFETY_EN_PORT->MODER |= OUTPUT(SSR_SAFETY_EN_PIN); ssr_safety_en(false); } oven_driver_set_power(0U); oven_driver_apply_power_level(); } void oven_driver_set_power(uint8_t power) { if (power > 100U) power = 100U; oven_driver_power_level = power; } void oven_driver_apply_power_level(void) { OVEN_CONTROLLER_PWM_TIMER->CCR3 = oven_driver_power_level * 10; } void oven_driver_disable(void) { OVEN_CONTROLLER_PWM_TIMER->CR1 = 0UL; OVEN_CONTROLLER_PWM_TIMER->CR2 = 0UL; rcc_manager_disable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(OVEN_CONTROLLER_PORT_RCC_MASK)); rcc_manager_disable_clock(&RCC->APB1ENR, BITMASK_TO_BITNO(OVEN_CONTROLLER_TIM_RCC_MASK)); } void oven_pid_init(struct pid_controller *controller_to_copy) { pid_copy(&oven_pid, controller_to_copy); oven_pid.output_sat_min = 0.0f; oven_pid.output_sat_max = 100.0f; oven_pid_running = true; oven_pid_aborted = false; safety_controller_report_timing(ERR_TIMING_PID); timestamp_last_run = systick_get_global_tick(); ssr_safety_en(true); } void oven_pid_set_target_temperature(float temp) { target_temp = temp; } void oven_pid_handle(void) { float pid_out; float current_temp; if (oven_pid_running && !oven_pid_aborted) { if (systick_ticks_have_passed(timestamp_last_run, (uint64_t)(oven_pid.sample_period * 1000))) { /* No need to check. Safety controller will monitor this */ (void)adc_pt1000_get_current_resistance(¤t_temp); (void)temp_converter_convert_resistance_to_temp(current_temp, ¤t_temp); pid_out = pid_sample(&oven_pid, target_temp - current_temp); oven_driver_set_power((uint8_t)pid_out); timestamp_last_run = systick_get_global_tick(); safety_controller_report_timing(ERR_TIMING_PID); } } } void oven_pid_stop(void) { oven_pid_running = false; oven_driver_set_power(0U); safety_controller_enable_timing_mon(ERR_TIMING_PID, false); ssr_safety_en(false); } void oven_pid_abort(void) { oven_pid_aborted = true; oven_pid_stop(); } enum oven_pid_status oven_pid_get_status(void) { enum oven_pid_status ret = OVEN_PID_ABORTED; if (oven_pid_running && !oven_pid_aborted) ret = OVEN_PID_RUNNING; else if (!oven_pid_running && !oven_pid_aborted) ret = OVEN_PID_DEACTIVATED; return ret; } /** @} */