/* 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 . */ #include #include #include #include #include #include static struct pid_controller oven_pid; static struct oven_pid_status oven_pid_current_status = { .active = false, .aborted = false, .target_temp = 0.0f, .current_temp = 0.0f, .timestamp_last_run = 0ULL }; void oven_driver_init() { rcc_manager_enable_clock(&RCC->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; } void oven_driver_set_power(uint8_t power) { if (power > 100U) power = 100U; OVEN_CONTROLLER_PWM_TIMER->CCR3 = power * 10; } void oven_driver_disable() { 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_current_status.timestamp_last_run = 0ULL; oven_pid_current_status.active = true; oven_pid_current_status.error_flags.vref_tol = false; oven_pid_current_status.error_flags.pt1000_other = false; oven_pid_current_status.error_flags.generic_error = false; oven_pid_current_status.error_flags.pt1000_adc_off = false; oven_pid_current_status.error_flags.controller_overtemp = false; oven_pid_current_status.error_flags.pt1000_adc_watchdog = false; } void oven_pid_handle(float target_temp) { float pid_out; float current_temp; int resistance_status; enum adc_pt1000_error pt1000_error; if (oven_pid_current_status.active && !oven_pid_current_status.aborted) { if (systick_ticks_have_passed(oven_pid_current_status.timestamp_last_run, (uint64_t)(oven_pid.sample_period * 1000))) { resistance_status = adc_pt1000_get_current_resistance(¤t_temp); if (resistance_status < 0) { oven_driver_set_power(0); pt1000_error = adc_pt1000_check_error(); if (pt1000_error & ADC_PT1000_WATCHDOG_ERROR) oven_pid_report_error(OVEN_PID_ERR_PT1000_ADC_WATCHDOG); if (pt1000_error & ADC_PT1000_INACTIVE) oven_pid_report_error(OVEN_PID_ERR_PT1000_ADC_OFF); if (pt1000_error & ADC_PT1000_OVERFLOW) oven_pid_report_error(OVEN_PID_ERR_PT1000_OTHER); return; } (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); oven_pid_current_status.timestamp_last_run = systick_get_global_tick(); oven_pid_current_status.target_temp = target_temp; oven_pid_current_status.current_temp = current_temp; } } } void oven_pid_report_error(enum oven_pid_error_report report) { struct oven_pid_errors *e = &oven_pid_current_status.error_flags; oven_pid_current_status.active = false; oven_pid_current_status.aborted = true; if (report == 0) { e->generic_error = true; } if (report & OVEN_PID_ERR_OVERTEMP) e->controller_overtemp = true; if (report & OVEN_PID_ERR_VREF_TOL) e->controller_overtemp = true; if (report & OVEN_PID_ERR_PT1000_OTHER) e->pt1000_other = true; if (report & OVEN_PID_ERR_PT1000_ADC_OFF) e->pt1000_adc_off = true; if (report & OVEN_PID_ERR_PT1000_ADC_WATCHDOG) e->pt1000_adc_watchdog = true; } const struct oven_pid_status *oven_pid_get_status() { return &oven_pid_current_status; } void oven_pid_stop() { oven_pid_current_status.active = false; oven_pid_current_status.target_temp = 0.0f; oven_pid_current_status.current_temp = 0.0f; }