Files
uDCCD/firmware/src/hw/hb_control.c
2024-03-12 21:22:26 +02:00

400 lines
16 KiB
C

/**** Includes ****/
#include "board/utils/utils.h"
#include "board/halfbridge.h"
#include "hb_control.h"
/**** Private definitions ****/
/**** Private constants ****/
// Fault condition thresholds
static const uint16_t SUPPLY_VOLT_MIN_W = HW_HB_SUPPLY_VOLT_MIN_W;
static const uint16_t SUPPLY_VOLT_MIN_F = HW_HB_SUPPLY_VOLT_MIN_F;
static const uint16_t SUPPLY_VOLT_MAX_W = HW_HB_SUPPLY_VOLT_MAX_W;
static const uint16_t SUPPLY_VOLT_MAX_F = HW_HB_SUPPLY_VOLT_MAX_F;
static const uint16_t SUPPLY_CURRENT_MAX_W = HW_HB_SUPPLY_CURRENT_MAX_W;
static const uint16_t SUPPLY_CURRENT_MAX_F = HW_HB_SUPPLY_CURRENT_MAX_F;
static const uint16_t SUPPLY_POWER_MAX_W = HW_HB_SUPPLY_POWER_MAX_W;
static const uint16_t SUPPLY_POWER_MAX_F = HW_HB_SUPPLY_POWER_MAX_F;
static const uint16_t OUT_CURRENT_MAX_W = HW_HB_OUT_CURRENT_MAX_W;
static const uint16_t OUT_CURRENT_MAX_F = HW_HB_OUT_CURRENT_MAX_F;
static const uint16_t OUT_VOLTAGE_MAX_W = HW_HB_OUT_VOLTAGE_MAX_W;
static const uint16_t OUT_VOLTAGE_MAX_F = HW_HB_OUT_VOLTAGE_MAX_F;
static const uint16_t OUT_POWER_MAX_W = HW_HB_OUT_POWER_MAX_W;
static const uint16_t OUT_POWER_MAX_F = HW_HB_OUT_POWER_MAX_F;
static const uint16_t OUT_RESISTANCE_MIN_W = HW_HB_OUT_RESISTANCE_MIN_W;
static const uint16_t OUT_RESISTANCE_MIN_F = HW_HB_OUT_RESISTANCE_MIN_F;
static const uint16_t OUT_RESISTANCE_MAX_W = HW_HB_OUT_RESISTANCE_MAX_W;
static const uint16_t OUT_RESISTANCE_MAX_F = HW_HB_OUT_RESISTANCE_MAX_F;
static const uint16_t FAULT_DELAY = HW_HB_FAULT_DELAY;
static const uint16_t WTOF_DELAY = HW_HB_WTOF_DELAY;
/**** Private variables ****/
/**** Private function declarations ****/
// Protection functions
static void faults_chs_reset(hb_fault_chs_t* ch_list);
static void faults_init(hb_faults_t* faults, hb_fault_chs_t* en_list);
static void faults_logic(hb_meas_t* measurements, hb_faults_t* faults);
static uint8_t faults_check(hb_faults_t* faults, hb_fault_chs_t* en_list, hb_fault_chs_t* fb_list, fault_lvl_t check);
static uint8_t warnings_feedback(hb_faults_t* faults, hb_fault_chs_t* en_list, hb_fault_chs_t* fb_list);
static uint8_t faults_feedback(hb_faults_t* faults, hb_fault_chs_t* en_list, hb_fault_chs_t* fb_list);
static uint8_t faults_is_any(hb_fault_chs_t* fb_list);
static uint8_t is_outside(uint16_t meas, uint16_t min, uint16_t max);
static uint8_t fuse_subprocess(hb_feedback_t* hb_fb, hb_control_t* hb_ctrl);
static void target_to_control(int16_t target, uint16_t ref_voltage, uint16_t* pwm_out, uint8_t* low_out, hb_fault_chs_t* fault_ch_en);
/**** Public function definitions ****/
void hb_init(hb_feedback_t* hb_fb, hb_control_t* hb_ctrl)
{
// Initialize control
hb_ctrl->enabled = 0;
// Initialize protection structures
faults_init(&hb_ctrl->out_faults, &hb_ctrl->out_faults_en);
fuse_reset(&hb_ctrl->out_fuse);
hb_ctrl->out_fuse_cfg.cooldown_time = 1000;
hb_ctrl->out_fuse_cfg.retry_time = 1000;
// Initialize feedback structure
bsp_hb_read_meas(&hb_fb->analog);
faults_chs_reset(&hb_fb->faults);
faults_chs_reset(&hb_fb->warnings);
hb_fb->enabled = 0;
hb_fb->warning_act = 0;
hb_fb->fault_act = 0;
hb_fb->fused = 0;
bsp_hb_write_pwm(0);
bsp_hb_write_low(0);
}
void hb_enable(hb_control_t* hb_ctrl)
{
bsp_hb_write_pwm(0);
bsp_hb_write_low(1);
hb_ctrl->enabled = 1;
}
void hb_disable(hb_control_t* hb_ctrl)
{
hb_ctrl->enabled = 0;
bsp_hb_write_pwm(0);
bsp_hb_write_low(0);
}
void hb_process(int16_t target, hb_feedback_t* hb_fb, hb_control_t* hb_ctrl)
{
uint16_t out_pwm = 0;
uint8_t low_on = 0;
// Read feedback
bsp_hb_read_meas(&hb_fb->analog);
// Process fuse
uint8_t fuse_act = fuse_subprocess(hb_fb, hb_ctrl);
// Act on fuse state
if((fuse_act)||(!hb_ctrl->enabled))
{
// Turn off output
bsp_hb_write_pwm(0);
bsp_hb_write_low(0);
return;
};
// Process new target
target_to_control(target, hb_fb->analog.sup_voltage, &out_pwm, &low_on, &hb_ctrl->out_faults_en);
// Apply new controls
bsp_hb_write_low(low_on);
bsp_hb_write_pwm(out_pwm);
}
/**** Private function definitions ****/
static void target_to_control(int16_t target, uint16_t ref_voltage, uint16_t* pwm_out, uint8_t* low_out, hb_fault_chs_t* fault_ch_en)
{
if(target < 0)
{
// Fast decay
*pwm_out = 0;
*low_out = 0;
// Set appropriate fault channels
fault_ch_en->out_short = 0;
fault_ch_en->out_ovp = 0;
}
else if(target == 0)
{
// Slow decay
*pwm_out = 0;
*low_out = 1;
// Set appropriate fault channels
fault_ch_en->out_short = 0;
fault_ch_en->out_ovp = 1;
}
else
{
// Calculate target PWM
*pwm_out = util_sat_ratio_16b((uint16_t)target, ref_voltage);
*low_out = 1;
fault_ch_en->out_short = 1;
fault_ch_en->out_ovp = 1;
}
}
static uint8_t fuse_subprocess(hb_feedback_t* hb_fb, hb_control_t* hb_ctrl)
{
// Process faults
faults_logic(&hb_fb->analog, &hb_ctrl->out_faults);
// Check if any enabled fault is active
uint8_t warn_act = warnings_feedback(&hb_ctrl->out_faults, &hb_ctrl->out_faults_en, &hb_fb->warnings);
uint8_t fault_act = faults_feedback(&hb_ctrl->out_faults, &hb_ctrl->out_faults_en, &hb_fb->faults);
// Process fuse state
uint8_t fuse_act = fuse_process(&hb_ctrl->out_fuse, fault_act, &hb_ctrl->out_fuse_cfg);
// Copy feedback data
hb_fb->enabled = hb_ctrl->enabled;
hb_fb->warning_act = warn_act;
hb_fb->fault_act = fault_act;
hb_fb->fused = fuse_act;
return fuse_act;
}
// Fault logic functions
static void faults_chs_reset(hb_fault_chs_t* ch_list)
{
// Zero all channels
ch_list->sup_uvp = 0;
ch_list->sup_ovp = 0;
ch_list->sup_ocp = 0;
ch_list->sup_opp = 0;
ch_list->out_ovp = 0;
ch_list->out_ocp = 0;
ch_list->out_opp = 0;
ch_list->out_short = 0;
ch_list->out_open = 0;
}
static void faults_init(hb_faults_t* faults, hb_fault_chs_t* en_list)
{
// Init state
fault_reset(&faults->sup_uvp);
fault_reset(&faults->sup_ovp);
fault_reset(&faults->sup_ocp);
fault_reset(&faults->sup_opp);
fault_reset(&faults->out_ovp);
fault_reset(&faults->out_ocp);
fault_reset(&faults->out_opp);
fault_reset(&faults->out_short);
fault_reset(&faults->out_open);
// Init enabled channels
faults_chs_reset(en_list);
en_list->sup_ocp = 1;
en_list->out_ocp = 1;
en_list->out_short = 1;
}
static void faults_logic(hb_meas_t* measurements, hb_faults_t* faults)
{
uint8_t w_trig = 0;
uint8_t f_trig = 0;
fault_cfg_t fault_cfg;
fault_cfg.delay = FAULT_DELAY;
fault_cfg.wtof = WTOF_DELAY;
// Check supply voltage
w_trig = is_outside(measurements->sup_voltage, SUPPLY_VOLT_MIN_W, 0);
f_trig = is_outside(measurements->sup_voltage, SUPPLY_VOLT_MIN_F, 0);
fault_process(&faults->sup_uvp, w_trig, f_trig, &fault_cfg);
w_trig = is_outside(measurements->sup_voltage, 0, SUPPLY_VOLT_MAX_W);
f_trig = is_outside(measurements->sup_voltage, 0, SUPPLY_VOLT_MAX_F);
fault_process(&faults->sup_ovp, w_trig, f_trig, &fault_cfg);
// Check supply current
w_trig = is_outside(measurements->sup_current, 0, SUPPLY_CURRENT_MAX_W);
f_trig = is_outside(measurements->sup_current, 0, SUPPLY_CURRENT_MAX_F);
fault_process(&faults->sup_ocp, w_trig, f_trig, &fault_cfg);
// Check supply power
w_trig = is_outside(measurements->sup_power, 0, SUPPLY_POWER_MAX_W);
f_trig = is_outside(measurements->sup_power, 0, SUPPLY_POWER_MAX_F);
fault_process(&faults->sup_opp, w_trig, f_trig, &fault_cfg);
// Check output voltage
w_trig = is_outside(measurements->out_voltage, 0, OUT_VOLTAGE_MAX_W);
f_trig = is_outside(measurements->out_voltage, 0, OUT_VOLTAGE_MAX_F);
fault_process(&faults->out_ovp, w_trig, f_trig, &fault_cfg);
// Check output current
w_trig = is_outside(measurements->out_current, 0, OUT_CURRENT_MAX_W);
f_trig = is_outside(measurements->out_current, 0, OUT_CURRENT_MAX_F);
fault_process(&faults->out_ocp, w_trig, f_trig, &fault_cfg);
// Check output power
w_trig = is_outside(measurements->out_power, 0, OUT_POWER_MAX_W);
f_trig = is_outside(measurements->out_power, 0, OUT_POWER_MAX_F);
fault_process(&faults->out_opp, w_trig, f_trig, &fault_cfg);
// Check output resistance
w_trig = is_outside(measurements->out_impedance, OUT_RESISTANCE_MIN_W, 0);
f_trig = is_outside(measurements->out_impedance, OUT_RESISTANCE_MIN_F, 0);
fault_process(&faults->out_short, w_trig, f_trig, &fault_cfg);
w_trig = is_outside(measurements->out_impedance, 0, OUT_RESISTANCE_MAX_W);
f_trig = is_outside(measurements->out_impedance, 0, OUT_RESISTANCE_MAX_F);
fault_process(&faults->out_open, w_trig, f_trig, &fault_cfg);
}
static uint8_t warnings_feedback(hb_faults_t* faults, hb_fault_chs_t* en_list, hb_fault_chs_t* fb_list)
{
return faults_check(faults, en_list, fb_list, FAULT_LVL_WARNING);
}
static uint8_t faults_feedback(hb_faults_t* faults, hb_fault_chs_t* en_list, hb_fault_chs_t* fb_list)
{
return faults_check(faults, en_list, fb_list, FAULT_LVL_FAULT);
}
// Utilities
static uint8_t is_outside(uint16_t meas, uint16_t min, uint16_t max)
{
if((meas < min)||((max!=0)&&(meas > max))) return 1;
else return 0;
}
static uint8_t faults_check(hb_faults_t* faults, hb_fault_chs_t* en_list, hb_fault_chs_t* fb_list, fault_lvl_t check)
{
if((en_list->sup_uvp)&&(faults->sup_uvp.severity == check)) fb_list->sup_uvp = 1;
if((en_list->sup_ovp)&&(faults->sup_ovp.severity == check)) fb_list->sup_ovp = 1;
if((en_list->sup_ocp)&&(faults->sup_ocp.severity == check)) fb_list->sup_ocp = 1;
if((en_list->sup_opp)&&(faults->sup_opp.severity == check)) fb_list->sup_opp = 1;
if((en_list->out_ovp)&&(faults->out_ovp.severity == check)) fb_list->out_ovp = 1;
if((en_list->out_ocp)&&(faults->out_ocp.severity == check)) fb_list->out_ocp = 1;
if((en_list->out_opp)&&(faults->out_opp.severity == check)) fb_list->out_opp = 1;
if((en_list->out_short)&&(faults->out_short.severity == check)) fb_list->out_short = 1;
if((en_list->out_open)&&(faults->out_open.severity == check)) fb_list->out_open = 1;
return faults_is_any(fb_list);
}
static uint8_t faults_is_any(hb_fault_chs_t* fb_list)
{
if(fb_list->sup_uvp) return 1;
if(fb_list->sup_ovp) return 1;
if(fb_list->sup_ocp) return 1;
if(fb_list->sup_opp) return 1;
if(fb_list->out_ovp) return 1;
if(fb_list->out_ocp) return 1;
if(fb_list->out_opp) return 1;
if(fb_list->out_short) return 1;
if(fb_list->out_open) return 1;
return 0;
}
#ifdef TESTING
uint8_t hb_is_equal_fb_struct(hb_feedback_t* f1, hb_feedback_t* f2)
{
if(f1->analog.out_voltage != f2->analog.out_voltage ) return 0;
if(f1->analog.out_current != f2->analog.out_current ) return 0;
if(f1->analog.sup_voltage != f2->analog.sup_voltage ) return 0;
if(f1->analog.sup_current != f2->analog.sup_current ) return 0;
if(f1->analog.out_power != f2->analog.out_power ) return 0;
if(f1->analog.sup_power != f2->analog.sup_power ) return 0;
if(f1->analog.out_impedance != f2->analog.out_impedance ) return 0;
if(f1->analog.low_side_ctrl != f2->analog.low_side_ctrl ) return 0;
if(f1->analog.pwm != f2->analog.pwm ) return 0;
if(f1->enabled != f2->enabled ) return 0;
if(f1->warning_act != f2->warning_act ) return 0;
if(f1->fault_act != f2->fault_act ) return 0;
if(f1->fused != f2->fused ) return 0;
if(f1->warnings.sup_uvp != f2->warnings.sup_uvp ) return 0;
if(f1->warnings.sup_ovp != f2->warnings.sup_ovp ) return 0;
if(f1->warnings.sup_ocp != f2->warnings.sup_ocp ) return 0;
if(f1->warnings.sup_opp != f2->warnings.sup_opp ) return 0;
if(f1->warnings.out_ovp != f2->warnings.out_ovp ) return 0;
if(f1->warnings.out_ocp != f2->warnings.out_ocp ) return 0;
if(f1->warnings.out_opp != f2->warnings.out_opp ) return 0;
if(f1->warnings.out_short != f2->warnings.out_short ) return 0;
if(f1->warnings.out_open != f2->warnings.out_open ) return 0;
if(f1->faults.sup_uvp != f2->faults.sup_uvp ) return 0;
if(f1->faults.sup_ovp != f2->faults.sup_ovp ) return 0;
if(f1->faults.sup_ocp != f2->faults.sup_ocp ) return 0;
if(f1->faults.sup_opp != f2->faults.sup_opp ) return 0;
if(f1->faults.out_ovp != f2->faults.out_ovp ) return 0;
if(f1->faults.out_ocp != f2->faults.out_ocp ) return 0;
if(f1->faults.out_opp != f2->faults.out_opp ) return 0;
if(f1->faults.out_short != f2->faults.out_short ) return 0;
if(f1->faults.out_open != f2->faults.out_open ) return 0;
return 1;
}
uint8_t hb_is_equal_ctrl_struct(hb_control_t* c1, hb_control_t* c2)
{
if(c1->enabled != c2->enabled ) return 0;
if(c1->out_faults.sup_uvp.severity != c2->out_faults.sup_uvp.severity ) return 0;
if(c1->out_faults.sup_uvp.w_time != c2->out_faults.sup_uvp.w_time ) return 0;
if(c1->out_faults.sup_uvp.f_time != c2->out_faults.sup_uvp.f_time ) return 0;
if(c1->out_faults.sup_ovp.severity != c2->out_faults.sup_ovp.severity ) return 0;
if(c1->out_faults.sup_ovp.w_time != c2->out_faults.sup_ovp.w_time ) return 0;
if(c1->out_faults.sup_ovp.f_time != c2->out_faults.sup_ovp.f_time ) return 0;
if(c1->out_faults.sup_ocp.severity != c2->out_faults.sup_ocp.severity ) return 0;
if(c1->out_faults.sup_ocp.w_time != c2->out_faults.sup_ocp.w_time ) return 0;
if(c1->out_faults.sup_ocp.f_time != c2->out_faults.sup_ocp.f_time ) return 0;
if(c1->out_faults.sup_opp.severity != c2->out_faults.sup_opp.severity ) return 0;
if(c1->out_faults.sup_opp.w_time != c2->out_faults.sup_opp.w_time ) return 0;
if(c1->out_faults.sup_opp.f_time != c2->out_faults.sup_opp.f_time ) return 0;
if(c1->out_faults.out_ovp.severity != c2->out_faults.out_ovp.severity ) return 0;
if(c1->out_faults.out_ovp.w_time != c2->out_faults.out_ovp.w_time ) return 0;
if(c1->out_faults.out_ovp.f_time != c2->out_faults.out_ovp.f_time ) return 0;
if(c1->out_faults.out_ocp.severity != c2->out_faults.out_ocp.severity ) return 0;
if(c1->out_faults.out_ocp.w_time != c2->out_faults.out_ocp.w_time ) return 0;
if(c1->out_faults.out_ocp.f_time != c2->out_faults.out_ocp.f_time ) return 0;
if(c1->out_faults.out_opp.severity != c2->out_faults.out_opp.severity ) return 0;
if(c1->out_faults.out_opp.w_time != c2->out_faults.out_opp.w_time ) return 0;
if(c1->out_faults.out_opp.f_time != c2->out_faults.out_opp.f_time ) return 0;
if(c1->out_faults.out_short.severity != c2->out_faults.out_short.severity ) return 0;
if(c1->out_faults.out_short.w_time != c2->out_faults.out_short.w_time ) return 0;
if(c1->out_faults.out_short.f_time != c2->out_faults.out_short.f_time ) return 0;
if(c1->out_faults.out_open.severity != c2->out_faults.out_open.severity ) return 0;
if(c1->out_faults.out_open.w_time != c2->out_faults.out_open.w_time ) return 0;
if(c1->out_faults.out_open.f_time != c2->out_faults.out_open.f_time ) return 0;
if(c1->out_faults_en.sup_uvp != c2->out_faults_en.sup_uvp ) return 0;
if(c1->out_faults_en.sup_ovp != c2->out_faults_en.sup_ovp ) return 0;
if(c1->out_faults_en.sup_ocp != c2->out_faults_en.sup_ocp ) return 0;
if(c1->out_faults_en.sup_opp != c2->out_faults_en.sup_opp ) return 0;
if(c1->out_faults_en.out_ovp != c2->out_faults_en.out_ovp ) return 0;
if(c1->out_faults_en.out_ocp != c2->out_faults_en.out_ocp ) return 0;
if(c1->out_faults_en.out_opp != c2->out_faults_en.out_opp ) return 0;
if(c1->out_faults_en.out_short != c2->out_faults_en.out_short ) return 0;
if(c1->out_faults_en.out_open != c2->out_faults_en.out_open ) return 0;
if(c1->out_fuse.state != c2->out_fuse.state ) return 0;
if(c1->out_fuse.count != c2->out_fuse.count ) return 0;
if(c1->out_fuse.timer != c2->out_fuse.timer ) return 0;
if(c1->out_fuse_cfg.cooldown_time != c2->out_fuse_cfg.cooldown_time ) return 0;
if(c1->out_fuse_cfg.retry_time != c2->out_fuse_cfg.retry_time ) return 0;
return 1;
}
#endif