/* 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 safety-controller * @{ */ #include #include #include #include #include #include #include #include #include struct error_flag { const char *name; enum safety_flag flag; bool error_state; bool persistent; }; struct timing_mon { const char *name; enum timing_monitor monitor; enum safety_flag associated_flag; uint64_t min_delta; uint64_t max_delta; uint64_t last; bool enabled; }; struct analog_mon { const char *name; enum analog_value_monitor monitor; enum safety_flag associated_flag; float min; float max; float value; bool valid; }; #ifdef COUNT_OF #undef COUNT_OF #endif #define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) #define ERR_FLAG_ENTRY(errflag, persistency) {.name=#errflag, .flag = (errflag), .error_state = false, .persistent = (persistency)} #define TIM_MON_ENTRY(mon, min, max, flag) {.name=#mon, .monitor = (mon), .associated_flag=(flag), .min_delta = (min), .max_delta = (max), .last = 0ULL, .enabled= false} #define ANA_MON_ENTRY(mon, min_value, max_value, flag) {.name=#mon, .monitor = (mon), .associated_flag=(flag), .min = (min_value), .max = (max_value), .value = 0.0f, .valid = false} static struct error_flag flags[] = { ERR_FLAG_ENTRY(ERR_FLAG_MEAS_ADC_OFF, false), ERR_FLAG_ENTRY(ERR_FLAG_MEAS_ADC_WATCHDOG, false), ERR_FLAG_ENTRY(ERR_FLAG_MEAS_ADC_UNSTABLE, false), ERR_FLAG_ENTRY(ERR_FLAG_MEAS_ADC_OVERFLOW, true), ERR_FLAG_ENTRY(ERR_FLAG_TIMING_MEAS_ADC, false), ERR_FLAG_ENTRY(ERR_FLAG_TIMING_PID, false), ERR_FLAG_ENTRY(ERR_FLAG_AMON_UC_TEMP, true), ERR_FLAG_ENTRY(ERR_FLAG_AMON_VREF, false), ERR_FLAG_ENTRY(ERR_FLAG_STACK, true), }; static struct timing_mon timings[] = { TIM_MON_ENTRY(ERR_TIMING_PID, 1, 800, ERR_FLAG_TIMING_PID), TIM_MON_ENTRY(ERR_TIMING_MEAS_ADC, 1, 50, ERR_FLAG_TIMING_MEAS_ADC), }; static struct analog_mon analog_mons[] = { ANA_MON_ENTRY(ERR_AMON_VREF, 2480.0f, 2520.0f, ERR_FLAG_AMON_VREF), ANA_MON_ENTRY(ERR_AMON_UC_TEMP, 0.0f, 55.0f, ERR_FLAG_AMON_UC_TEMP), }; static struct analog_mon *find_analog_mon(enum analog_value_monitor mon) { uint32_t i; struct analog_mon *ret = NULL; for (i = 0; i < COUNT_OF(analog_mons); i++) { if (analog_mons[i].monitor == mon) ret = &analog_mons[i]; } return ret; } static struct timing_mon *find_timing_mon(enum timing_monitor mon) { uint32_t i; struct timing_mon *ret = NULL; for (i = 0; i < COUNT_OF(timings); i++) { if (timings[i].monitor == mon) ret = &timings[i]; } return ret; } static struct error_flag *find_error_flag(enum safety_flag flag) { uint32_t i; struct error_flag *ret = NULL; for (i = 0; i < COUNT_OF(flags); i++) { if (flags[i].flag == flag) ret = &flags[i]; } return ret; } static void safety_controller_process_checks() { // TODO: Implement } int safety_controller_report_error(enum safety_flag flag) { uint32_t i; int ret = -1; for (i = 0; i < COUNT_OF(flags); i++) { if (flags[i].flag & flag) { flags[i].error_state = true; ret = 0; } } safety_controller_process_checks(); return ret; } void safety_controller_report_timing(enum timing_monitor monitor) { uint32_t i; struct timing_mon *tim; uint64_t timestamp; timestamp = systick_get_global_tick(); tim = find_timing_mon(monitor); if (tim) { tim->last = timestamp; tim->enabled = true; } safety_controller_process_checks(); } void safety_controller_report_analog_value(enum analog_value_monitor monitor, float value) { struct analog_mon *ana; /* Return if not a power of two */ if (!is_power_of_two(monitor)) return; ana = find_analog_mon(monitor); if (ana) { ana->valid = true; ana->value = value; } safety_controller_process_checks(); } void safety_controller_init() { safety_adc_init(); watchdog_setup(WATCHDOG_PRESCALER); } int safety_controller_handle() { static int ret = 0; /* TODO: Handle safety ADC */ /* TODO: Check flags for PID and HALT */ ret |= watchdog_ack(WATCHDOG_MAGIC_KEY); return (ret ? -1 : 0); } int safety_controller_enable_timing_mon(enum timing_monitor monitor, bool enable) { struct timing_mon *tim; if (enable) { safety_controller_report_timing(monitor); } else { tim = find_timing_mon(monitor); if (!tim) return -1; tim->enabled = false; } return 0; } enum analog_monitor_status safety_controller_get_analog_mon_value(enum analog_value_monitor monitor, float *value) { struct analog_mon *mon; int ret = ANALOG_MONITOR_ERROR; if (!is_power_of_two(monitor)) goto go_out; if (!value) goto go_out; mon = find_analog_mon(monitor); if (mon) { if (!mon->valid) { ret = ANALOG_MONITOR_INACTIVE; goto go_out; } *value = mon->value; if (mon->value < mon->min) ret = ANALOG_MONITOR_UNDER; else if (mon->value > mon->max) ret = ANALOG_MONITOR_OVER; else ret = ANALOG_MONITOR_OK; } go_out: return ret; } /** @} */