diff --git a/.vscode/settings.json b/.vscode/settings.json index 6e02378205..7b00bcc77e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,5 +11,12 @@ "--compile-commands-dir=build_fw_dev", // "--compile-commands-dir=build_fw_test", "--query-driver=/usr/local/bin/arm-none-eabi-gcc" - ] + ], + "files.associations": { + "io_efuses.h": "c", + "io_efuse.h": "c", + "io_efuse_datatypes.h": "c", + "app_vehicledynamicsconstants.h": "c", + "app_loadswitches.h": "c" + } } diff --git a/firmware/quintuna/VC/CMakeLists.txt b/firmware/quintuna/VC/CMakeLists.txt index 377cde7634..0c8962e18c 100644 --- a/firmware/quintuna/VC/CMakeLists.txt +++ b/firmware/quintuna/VC/CMakeLists.txt @@ -28,12 +28,21 @@ list(APPEND IO_SRCS "${SHARED_IO_INCLUDE_DIR}/io_canLogging.c" "${SHARED_IO_INCLUDE_DIR}/io_led.c" "${SHARED_IO_INCLUDE_DIR}/io_time.c" - "${SHARED_IO_INCLUDE_DIR}/io_loadswitch.c" + "${SHARED_IO_INCLUDE_DIR}/io_efuse/io_efuse.c" + "${SHARED_IO_INCLUDE_DIR}/io_efuse/io_efuse_ST_VND5/io_efuse_ST_VND5.c" + "${SHARED_IO_INCLUDE_DIR}/io_efuse/io_efuse_TI_TPS25/io_efuse_TI_TPS25.c" "${SHARED_IO_INCLUDE_DIR}/io_potentiometer.c" "${SHARED_IO_INCLUDE_DIR}/io_bootHandler.c" "${SHARED_IO_INCLUDE_DIR}/io_imu.c" ) -set(IO_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/src/io" "${SHARED_IO_INCLUDE_DIR}" "${SHARED_IO_INCLUDE_QUINTUNA_DIR}") +set(IO_INCLUDE_DIRS + "${CMAKE_CURRENT_SOURCE_DIR}/src/io" + "${SHARED_IO_INCLUDE_DIR}" + "${SHARED_IO_INCLUDE_DIR}/io_efuse" + "${SHARED_IO_INCLUDE_DIR}/io_efuse/io_efuse_TI_TPS25" + "${SHARED_IO_INCLUDE_DIR}/io_efuse/io_efuse_ST_VND5" + "${SHARED_IO_INCLUDE_QUINTUNA_DIR}" +) file(GLOB_RECURSE HW_SRCS CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/hw/*.c") list(APPEND HW_SRCS diff --git a/firmware/quintuna/VC/src/app/app_loadswitches.c b/firmware/quintuna/VC/src/app/app_loadswitches.c index 4b290b6b9a..f8157012ad 100644 --- a/firmware/quintuna/VC/src/app/app_loadswitches.c +++ b/firmware/quintuna/VC/src/app/app_loadswitches.c @@ -4,9 +4,8 @@ #include #include -#include "io_loadswitch.h" #include "app_canTx.h" -#include "io_loadswitches.h" +#include "io_efuses.h" static void (*const efuse_enabled_can_setters[NUM_EFUSE_CHANNELS])(bool) = { [EFUSE_CHANNEL_F_INV] = app_canTx_VC_FrontInvertersStatus_set, @@ -35,8 +34,8 @@ void app_efuse_broadcast(void) // run through each efuse, and broadcast the channel status and current for (int efuse = 0; efuse < NUM_EFUSE_CHANNELS; efuse += 1) { - const bool enabled = io_loadswitch_isChannelEnabled(efuse_channels[efuse]); - const float current = io_loadswitch_getChannelCurrent(efuse_channels[efuse]); + const bool enabled = io_efuse_isChannelEnabled(efuse_channels[efuse]); + const float current = io_efuse_getChannelCurrent(efuse_channels[efuse]); assert(efuse_enabled_can_setters[efuse] != NULL); efuse_enabled_can_setters[efuse](enabled); @@ -44,3 +43,5 @@ void app_efuse_broadcast(void) efuse_current_can_setters[efuse](current); } } + +// void app_loadswitch_enableFa diff --git a/firmware/quintuna/VC/src/app/app_loadswitches.h b/firmware/quintuna/VC/src/app/app_loadswitches.h index ec3fef3da2..70f88d4d47 100644 --- a/firmware/quintuna/VC/src/app/app_loadswitches.h +++ b/firmware/quintuna/VC/src/app/app_loadswitches.h @@ -1,6 +1,6 @@ #pragma once #include -#include "io_loadswitches.h" +#include "io_efuses.h" void app_efuse_broadcast(void); \ No newline at end of file diff --git a/firmware/quintuna/VC/src/app/app_powerManager.c b/firmware/quintuna/VC/src/app/app_powerManager.c index b2bb9b13b4..86ecf90619 100644 --- a/firmware/quintuna/VC/src/app/app_powerManager.c +++ b/firmware/quintuna/VC/src/app/app_powerManager.c @@ -1,7 +1,7 @@ #include "app_powerManager.h" #include "app_timer.h" -#include "io_loadswitches.h" +#include "io_efuses.h" #include "app_canAlerts.h" #include @@ -12,27 +12,9 @@ static PowerManagerConfig power_manager_state; static TimerChannel sequencing_timer; -typedef union -{ - const ST_LoadSwitch *st; - const TI_LoadSwitch *ti; -} LoadSwitch; - -typedef struct -{ - LoadSwitch loadswitch; - uint8_t retry_num; -} RetryProtocol; - -static RetryProtocol efuses_retry_state[NUM_EFUSE_CHANNELS] = { - [EFUSE_CHANNEL_F_INV] = { .loadswitch.st = &inv_rsm_loadswitch, .retry_num = 0 }, - [EFUSE_CHANNEL_RSM] = { .loadswitch.st = &inv_rsm_loadswitch, .retry_num = 0 }, - [EFUSE_CHANNEL_BMS] = { .loadswitch.st = &inv_bms_loadswitch, .retry_num = 0 }, - [EFUSE_CHANNEL_R_INV] = { .loadswitch.st = &inv_bms_loadswitch, .retry_num = 0 }, - [EFUSE_CHANNEL_DAM] = { .loadswitch.st = &front_loadswitch, .retry_num = 0 }, - [EFUSE_CHANNEL_FRONT] = { .loadswitch.st = &front_loadswitch, .retry_num = 0 }, - [EFUSE_CHANNEL_RL_PUMP] = { .loadswitch.ti = &rl_pump_loadswitch, .retry_num = 0 }, - [EFUSE_CHANNEL_R_RAD] = { .loadswitch.st = &rad_fan_loadswitch, .retry_num = 0 } +static uint8_t efuses_retry_num[NUM_EFUSE_CHANNELS] = { + [EFUSE_CHANNEL_F_INV] = 0, [EFUSE_CHANNEL_RSM] = 0, [EFUSE_CHANNEL_BMS] = 0, [EFUSE_CHANNEL_R_INV] = 0, + [EFUSE_CHANNEL_DAM] = 0, [EFUSE_CHANNEL_FRONT] = 0, [EFUSE_CHANNEL_RL_PUMP] = 0, [EFUSE_CHANNEL_R_RAD] = 0 }; void app_powerManager_updateConfig(const PowerManagerConfig new_power_manager_config) @@ -44,51 +26,6 @@ void app_powerManager_updateConfig(const PowerManagerConfig new_power_manager_co power_manager_state.efuse_configs[EFUSE_CHANNEL_R_RAD].efuse_enable = !app_canAlerts_VC_Info_PcmUnderVoltage_get(); } -static bool STLoadswitch_Status(const ST_LoadSwitch *loadswitch) -{ - assert(loadswitch->efuse1 != NULL && loadswitch->efuse2 != NULL); - - // Checking if ther is an overtemperature/short to ground condition - float vsenseh_efuse1 = io_loadswitch_getChannelCurrent(loadswitch->efuse1) / ADC_VOLTAGE_TO_CURRENT_A; - float vsenseh_efuse2 = io_loadswitch_getChannelCurrent(loadswitch->efuse2) / ADC_VOLTAGE_TO_CURRENT_A; - - if (io_loadswitch_isChannelEnabled(loadswitch->efuse1) && vsenseh_efuse1 >= 3.0f) - { - return false; - } - - if (io_loadswitch_isChannelEnabled(loadswitch->efuse2) && vsenseh_efuse2 >= 3.0f) - { - return false; - } - - // Checking if there is a short to VBAT condition - io_STloadswitch_reset_set(loadswitch, true); - - if (!io_loadswitch_isChannelEnabled(loadswitch->efuse1) && vsenseh_efuse1 > 3.0f) - { - return false; - } - - if (!io_loadswitch_isChannelEnabled(loadswitch->efuse2) && vsenseh_efuse2 > 3.0f) - { - return false; - } - - // reset the stby reset gpio to low - io_STloadswitch_reset_set(loadswitch, false); - return true; -} - -static bool is_efuse_ok(const uint8_t current_efuse_sequence) -{ - if (EFUSE_CHANNEL_RL_PUMP <= current_efuse_sequence) - { - return io_TILoadswitch_pgood(efuses_retry_state[current_efuse_sequence].loadswitch.ti); - } - return STLoadswitch_Status(efuses_retry_state[current_efuse_sequence].loadswitch.st); -} - void app_powerManager_EfuseProtocolTick_100Hz(void) { switch (app_timer_updateAndGetState(&sequencing_timer)) @@ -103,21 +40,23 @@ void app_powerManager_EfuseProtocolTick_100Hz(void) // TODO: what the fuck __attribute__((fallthrough)); case TIMER_STATE_IDLE: - for (LoadswitchChannel current_efuse_sequence = 0; + for (EfuseChannel current_efuse_sequence = 0; current_efuse_sequence < NUM_EFUSE_CHANNELS && app_timer_updateAndGetState(&sequencing_timer) == TIMER_STATE_IDLE; current_efuse_sequence++) { + const Efuse *channel = efuse_channels[current_efuse_sequence]; + // check if the efuse is supposed to be on or off const bool desired_efuse_state = power_manager_state.efuse_configs[current_efuse_sequence].efuse_enable; - if (io_loadswitch_isChannelEnabled(efuse_channels[current_efuse_sequence]) == desired_efuse_state) + if (io_efuse_isChannelEnabled(channel) == desired_efuse_state) { // efuse is fine continue; } // todo check this when incrementing the efuse failure - if (efuses_retry_state[current_efuse_sequence].retry_num > + if (efuses_retry_num[current_efuse_sequence] > power_manager_state.efuse_configs[current_efuse_sequence].max_retry) { // todo over the retry limit activities @@ -128,14 +67,14 @@ void app_powerManager_EfuseProtocolTick_100Hz(void) if (desired_efuse_state == false) { // case 1: on and trying to turn off - io_loadswitch_setChannel(efuse_channels[current_efuse_sequence], desired_efuse_state); + io_efuse_setChannel(channel, desired_efuse_state); continue; } // case 2: off and trying to turn on // we update the efuse blown status here because this only shows up when we want it on - if (!is_efuse_ok(current_efuse_sequence)) // todo remove this state? + if (!io_efuse_ok(channel)) // todo remove this state? { - efuses_retry_state[current_efuse_sequence].retry_num++; + efuses_retry_num[current_efuse_sequence]++; } // If we dont know the if the efuse is blown check if it is however if we know its already blown @@ -147,9 +86,7 @@ void app_powerManager_EfuseProtocolTick_100Hz(void) app_timer_init(&sequencing_timer, efuse_retry_timeout); app_timer_restart(&sequencing_timer); } - io_loadswitch_setChannel( - efuse_channels[current_efuse_sequence], - power_manager_state.efuse_configs[current_efuse_sequence].efuse_enable); + io_efuse_setChannel(channel, power_manager_state.efuse_configs[current_efuse_sequence].efuse_enable); } break; } diff --git a/firmware/quintuna/VC/src/app/app_powerManager.h b/firmware/quintuna/VC/src/app/app_powerManager.h index f15031f030..e2d1700e6e 100644 --- a/firmware/quintuna/VC/src/app/app_powerManager.h +++ b/firmware/quintuna/VC/src/app/app_powerManager.h @@ -1,7 +1,7 @@ #pragma once #include -#include "io_loadswitches.h" +#include "io_efuses.h" typedef struct { diff --git a/firmware/quintuna/VC/src/app/app_pumpControl.c b/firmware/quintuna/VC/src/app/app_pumpControl.c index 00277cbff9..54f6f03e76 100644 --- a/firmware/quintuna/VC/src/app/app_pumpControl.c +++ b/firmware/quintuna/VC/src/app/app_pumpControl.c @@ -1,7 +1,6 @@ #include "app_pumpControl.h" -#include "io_loadswitch.h" +#include "io_efuses.h" #include "io_time.h" -#include "io_loadswitches.h" #include #define SLOPE (0.5f) @@ -40,11 +39,11 @@ static void pumpControl_stopFlow(void) void app_pumpControl_MonitorPumps(void) { time += 10; - const bool pumps_ok = io_TILoadswitch_pgood(&rl_pump_loadswitch); - const bool pumps_enabled = io_loadswitch_isChannelEnabled(efuse_channels[EFUSE_CHANNEL_RL_PUMP]); + const bool pumps_ok = io_efuse_pgood(&rl_pump_efuse); + const bool pumps_enabled = io_efuse_isChannelEnabled(&rl_pump_efuse); - bool ramp_up_pumps = pumps_ok && pumps_enabled; + const bool ramp_up_pumps = pumps_ok && pumps_enabled; if (ramp_up_pumps) pumpControl_rampUp(); diff --git a/firmware/quintuna/VC/src/app/states/app_hvInitState.c b/firmware/quintuna/VC/src/app/states/app_hvInitState.c index f0dcdf2ce5..ffde4ed739 100644 --- a/firmware/quintuna/VC/src/app/states/app_hvInitState.c +++ b/firmware/quintuna/VC/src/app/states/app_hvInitState.c @@ -4,7 +4,6 @@ #include "app_timer.h" #include "app_canAlerts.h" #include "app_warningHandling.h" -#include "io_loadswitches.h" #include "app_canTx.h" #include "app_canRx.h" #include "app_canUtils.h" diff --git a/firmware/quintuna/VC/src/app/states/app_hvState.c b/firmware/quintuna/VC/src/app/states/app_hvState.c index e71948cae6..22162a9b20 100644 --- a/firmware/quintuna/VC/src/app/states/app_hvState.c +++ b/firmware/quintuna/VC/src/app/states/app_hvState.c @@ -3,7 +3,6 @@ #include "app_powerManager.h" #include "app_startSwitch.h" #include "app_warningHandling.h" -#include "io_loadswitches.h" #include #include #include diff --git a/firmware/quintuna/VC/src/io/io_efuses.c b/firmware/quintuna/VC/src/io/io_efuses.c new file mode 100644 index 0000000000..610f0e9e47 --- /dev/null +++ b/firmware/quintuna/VC/src/io/io_efuses.c @@ -0,0 +1,62 @@ +#include "hw_adcs.h" +#include "hw_gpios.h" +#include "hw_i2cs.h" +#include "io_efuses.h" +#include "io_efuse/io_efuse_ST_VND5/io_efuse_ST_VND5.h" +#include "io_efuse/io_efuse_TI_TPS25/io_efuse_TI_TPS25.h" + +static ST_VND5_Efuse f_inv_ST_VND5_Efuse = { .stby_reset_gpio = &fr_stby_inv }; +static ST_VND5_Efuse rsm_ST_VND5_Efuse = { .stby_reset_gpio = &fr_stby_inv }; +static ST_VND5_Efuse bms_ST_VND5_Efuse = { .stby_reset_gpio = &fr_stby_front }; +static ST_VND5_Efuse r_inv_ST_VND5_Efuse = { .stby_reset_gpio = &fr_stby_front }; +static ST_VND5_Efuse dam_ST_VND5_Efuse = { .stby_reset_gpio = &fr_stby_rear }; +static ST_VND5_Efuse front_ST_VND5_Efuse = { .stby_reset_gpio = &fr_stby_rear }; +static TI_TPS25_Efuse rl_pump_TI_TPS25_Efuse = { .pgood = &rl_pump_pgood }; +static ST_VND5_Efuse r_rad_fan_ST_VND5_Efuse = { .stby_reset_gpio = &fr_stby_rad }; + +const Efuse f_inv_efuse = { .enable_gpio = &f_inv_en, + .sns_adc_channel = &inv_f_pwr_i_sns, + .st_vnd5 = &f_inv_ST_VND5_Efuse, + .efuse_functions = &ST_VND5_Efuse_functions }; + +const Efuse rsm_efuse = { .enable_gpio = &rsm_en, + .sns_adc_channel = &rsm_i_sns, + .st_vnd5 = &f_inv_ST_VND5_Efuse, + .efuse_functions = &ST_VND5_Efuse_functions }; + +const Efuse bms_efuse = { .enable_gpio = &bms_en, + .sns_adc_channel = &bms_i_sns, + .st_vnd5 = &bms_ST_VND5_Efuse, + .efuse_functions = &ST_VND5_Efuse_functions }; + +const Efuse r_inv_efuse = { .enable_gpio = &r_inv_en, + .sns_adc_channel = &inv_r_pwr_i_sns, + .st_vnd5 = &r_inv_ST_VND5_Efuse, + .efuse_functions = &ST_VND5_Efuse_functions }; + +const Efuse dam_efuse = { .enable_gpio = &dam_en, + .sns_adc_channel = &dam_i_sns, + .st_vnd5 = &dam_ST_VND5_Efuse, + .efuse_functions = &ST_VND5_Efuse_functions }; + +const Efuse front_efuse = { .enable_gpio = &front_en, + .sns_adc_channel = &front_i_sns, + .st_vnd5 = &front_ST_VND5_Efuse, + .efuse_functions = &ST_VND5_Efuse_functions }; + +const Efuse rl_pump_efuse = { .enable_gpio = &rl_pump_en, + .sns_adc_channel = &pump_rl_pwr_i_sns, + .ti_tps25 = &rl_pump_TI_TPS25_Efuse, + .efuse_functions = &TI_TPS25_Efuse_functions }; + +const Efuse r_rad_fan_efuse = { .enable_gpio = &rr_rad_fan_en, + .sns_adc_channel = &r_rad_fan_i_sns, + .st_vnd5 = &r_rad_fan_ST_VND5_Efuse, + .efuse_functions = &TI_TPS25_Efuse_functions }; + +const Efuse *const efuse_channels[NUM_EFUSE_CHANNELS] = { + [EFUSE_CHANNEL_F_INV] = &f_inv_efuse, [EFUSE_CHANNEL_RSM] = &rsm_efuse, + [EFUSE_CHANNEL_BMS] = &bms_efuse, [EFUSE_CHANNEL_R_INV] = &r_inv_efuse, + [EFUSE_CHANNEL_DAM] = &dam_efuse, [EFUSE_CHANNEL_FRONT] = &front_efuse, + [EFUSE_CHANNEL_RL_PUMP] = &rl_pump_efuse, [EFUSE_CHANNEL_R_RAD] = &r_rad_fan_efuse +}; diff --git a/firmware/quintuna/VC/src/io/io_efuses.h b/firmware/quintuna/VC/src/io/io_efuses.h new file mode 100644 index 0000000000..76e67b5722 --- /dev/null +++ b/firmware/quintuna/VC/src/io/io_efuses.h @@ -0,0 +1,37 @@ +#pragma once + +#include "io_efuse/io_efuse.h" + +typedef enum +{ + // INV_RSM loadswitch + EFUSE_CHANNEL_F_INV = 0u, + EFUSE_CHANNEL_RSM, + + // INV_BMS loadswitch + EFUSE_CHANNEL_BMS, + EFUSE_CHANNEL_R_INV, + + // Front loadswitch + EFUSE_CHANNEL_DAM, + EFUSE_CHANNEL_FRONT, + + // TI loadswitches + EFUSE_CHANNEL_RL_PUMP, + + // Radiator Fan loadswitches + EFUSE_CHANNEL_R_RAD, + + NUM_EFUSE_CHANNELS +} EfuseChannel; + +extern const Efuse *const efuse_channels[NUM_EFUSE_CHANNELS]; + +extern const Efuse f_inv_efuse; +extern const Efuse rsm_efuse; +extern const Efuse bms_efuse; +extern const Efuse r_inv_efuse; +extern const Efuse dam_efuse; +extern const Efuse front_efuse; +extern const Efuse rl_pump_efuse; +extern const Efuse r_rad_fan_efuse; diff --git a/firmware/quintuna/VC/src/io/io_loadswitches.c b/firmware/quintuna/VC/src/io/io_loadswitches.c deleted file mode 100644 index 4eee59564f..0000000000 --- a/firmware/quintuna/VC/src/io/io_loadswitches.c +++ /dev/null @@ -1,35 +0,0 @@ -#include "io_loadswitches.h" -#include "hw_adcs.h" -#include "hw_gpios.h" -#include "hw_i2cs.h" -#include "io_loadswitch.h" - -static const Efuse f_inv_efuse = { .enable_gpio = &f_inv_en, .sns_adc_channel = &inv_f_pwr_i_sns }; -static const Efuse rsm_efuse = { .enable_gpio = &rsm_en, .sns_adc_channel = &rsm_i_sns }; -static const Efuse bms_efuse = { .enable_gpio = &bms_en, .sns_adc_channel = &bms_i_sns }; -static const Efuse r_inv_efuse = { .enable_gpio = &r_inv_en, .sns_adc_channel = &inv_r_pwr_i_sns }; -static const Efuse dam_efuse = { .enable_gpio = &dam_en, .sns_adc_channel = &dam_i_sns }; -static const Efuse front_efuse = { .enable_gpio = &front_en, .sns_adc_channel = &front_i_sns }; -static const Efuse rl_pump_efuse = { .enable_gpio = &rl_pump_en, .sns_adc_channel = &pump_rl_pwr_i_sns }; -static const Efuse r_rad_fan_efuse = { .enable_gpio = &rr_rad_fan_en, .sns_adc_channel = &r_rad_fan_i_sns }; - -const ST_LoadSwitch inv_rsm_loadswitch = { .efuse1 = &f_inv_efuse, - .efuse2 = &rsm_efuse, - .stby_reset_gpio = &fr_stby_inv }; -const ST_LoadSwitch inv_bms_loadswitch = { .efuse1 = &bms_efuse, - .efuse2 = &r_inv_efuse, - .stby_reset_gpio = &fr_stby_front }; -const ST_LoadSwitch front_loadswitch = { .efuse1 = &dam_efuse, - .efuse2 = &front_efuse, - .stby_reset_gpio = &fr_stby_rear }; -const TI_LoadSwitch rl_pump_loadswitch = { .efuse = &rl_pump_efuse, .pgood = &rl_pump_pgood }; -const ST_LoadSwitch rad_fan_loadswitch = { .efuse1 = NULL, - .efuse2 = &r_rad_fan_efuse, - .stby_reset_gpio = &fr_stby_rad }; - -const Efuse *const efuse_channels[NUM_EFUSE_CHANNELS] = { - [EFUSE_CHANNEL_F_INV] = &f_inv_efuse, [EFUSE_CHANNEL_RSM] = &rsm_efuse, - [EFUSE_CHANNEL_BMS] = &bms_efuse, [EFUSE_CHANNEL_R_INV] = &r_inv_efuse, - [EFUSE_CHANNEL_DAM] = &dam_efuse, [EFUSE_CHANNEL_FRONT] = &front_efuse, - [EFUSE_CHANNEL_RL_PUMP] = &rl_pump_efuse, [EFUSE_CHANNEL_R_RAD] = &r_rad_fan_efuse -}; diff --git a/firmware/quintuna/VC/src/io/io_loadswitches.h b/firmware/quintuna/VC/src/io/io_loadswitches.h deleted file mode 100644 index bb69e76a90..0000000000 --- a/firmware/quintuna/VC/src/io/io_loadswitches.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include "io_loadswitch.h" - -typedef enum -{ - // INV_RSM loadswitch - EFUSE_CHANNEL_F_INV = 0u, - EFUSE_CHANNEL_RSM, - - // INV_BMS loadswitch - EFUSE_CHANNEL_BMS, - EFUSE_CHANNEL_R_INV, - - // Front loadswitch - EFUSE_CHANNEL_DAM, - EFUSE_CHANNEL_FRONT, - - // TI loadswitches - EFUSE_CHANNEL_RL_PUMP, - - // Radiator Fan loadswitches - EFUSE_CHANNEL_R_RAD, - - NUM_EFUSE_CHANNELS -} LoadswitchChannel; - -#ifdef TARGET_EMBEDDED -extern const ST_LoadSwitch inv_rsm_loadswitch; -extern const ST_LoadSwitch inv_bms_loadswitch; -extern const ST_LoadSwitch front_loadswitch; -extern const TI_LoadSwitch rl_pump_loadswitch; -extern const TI_LoadSwitch rr_pump_loadswitch; -extern const TI_LoadSwitch f_pump_loadswitch; -extern const ST_LoadSwitch rad_fan_loadswitch; -#elif TARGET_TEST -extern ST_LoadSwitch inv_rsm_loadswitch; -extern ST_LoadSwitch inv_bms_loadswitch; -extern ST_LoadSwitch front_loadswitch; -extern TI_LoadSwitch rl_pump_loadswitch; -extern TI_LoadSwitch rr_pump_loadswitch; -extern TI_LoadSwitch f_pump_loadswitch; -extern ST_LoadSwitch rad_fan_loadswitch; -#endif - -extern const Efuse *const efuse_channels[NUM_EFUSE_CHANNELS]; diff --git a/firmware/shared/src/app/app_ekf.h b/firmware/shared/src/app/app_ekf.h new file mode 100644 index 0000000000..b87c007aee --- /dev/null +++ b/firmware/shared/src/app/app_ekf.h @@ -0,0 +1,102 @@ +#pragma once + +#include "app_math.h" + +/** + * General Extended Kalman Filter API + * + * How to use: + * + * 1) Include in your file like this along with the dimensions + * #define DIM x + * #include "app_ekf.h" + * + * 2) Define the functions in the function pointers + * + * 3) Define your measurement functions in your estimator api + * (They are not stored in the ekf struct which will allow multiple + * measurement functions and thus multiple sensors if required) + * + * Note: If you have more than 2 sensors for the update/measurement step, + * usually you will need the same amount of measurement functions. + * Thus, the user must swap the measurement functions in the ekf struct + * before calling the update step again with the new measurements + * Explain why velocity estimator is an exception + */ + +typedef struct +{ + // estimations + Matrix state_estimate; + Matrix covariance_estimate; + // internal variables + Matrix F_jacobian; + Matrix H_jacobian; + Matrix measurement_pred; + // user implementable functions functions + void (*state_transition)(Matrix *state_estimate, Matrix *prev_state, Matrix *control_input); + void (*state_jacobian)(Matrix *F_jacobian, Matrix *prev_state, Matrix *control_input); + void (*measurement_jacobian)(Matrix *H_jacobian, Matrix *prev_state); + void (*measurement_func)(Matrix *measurement_pred, Matrix *prev_state); +} EKF; + +static inline void predict(EKF *ekf, Matrix *prev_state, Matrix *control_input, Matrix *process_noise_cov) +{ + Matrix temp_cov = { .mat = (float[DIM * DIM]){ 0.0f }, .rows = DIM, .cols = DIM }; + Matrix F_transpose = { .mat = (float[DIM * DIM]){ 0.0f }, .rows = DIM, .cols = DIM }; + + // state prediction + ekf->state_transition(&ekf->state_estimate, prev_state, control_input); + ekf->state_jacobian(&ekf->F_jacobian, prev_state, control_input); + + // prediction covariance prediction + mult(&temp_cov, &ekf->F_jacobian, &ekf->covariance_estimate); + transpose(&F_transpose, &ekf->F_jacobian); + mult(&ekf->covariance_estimate, &temp_cov, &F_transpose); + add(&ekf->covariance_estimate, &ekf->covariance_estimate, process_noise_cov); +} + +/** + * Updates the prediction, gives the final, corrected state and covariance estimate + * with measured values given previous state and noise covariance + * + * measurement pred is obtained from your measurement function + */ +static inline void update(EKF *ekf, Matrix *measurement, Matrix *prev_state, Matrix *measurement_noise_cov) +{ + Matrix PH_transpose = { .mat = (float[DIM * DIM]){ 0.0f }, .rows = DIM, .cols = DIM }; + Matrix Ky = { .mat = (float[DIM * 1U]){ 0.0f }, .rows = DIM, .cols = 1 }; + Matrix H_transpose = { .mat = (float[DIM * DIM]){ 0.0f }, .rows = DIM, .cols = DIM }; + Matrix S = { .mat = (float[DIM * DIM]){ 0.0f }, .rows = DIM, .cols = DIM }; + Matrix S_inv = { .mat = (float[DIM * DIM]){ 0.0f }, .rows = DIM, .cols = DIM }; + Matrix K = { .mat = (float[DIM * DIM]){ 0.0f }, .rows = DIM, .cols = DIM }; + Matrix residual = { .mat = (float[DIM * 1U]){ 0.0f }, .rows = DIM, .cols = 1 }; + Matrix KH = { .mat = (float[DIM * DIM]){ 0.0f }, .rows = DIM, .cols = DIM }; + Matrix KHP = { .mat = (float[DIM * DIM]){ 0.0f }, .rows = DIM, .cols = DIM }; + + // measurement prediction + ekf->measurement_func(&ekf->measurement_pred, prev_state); + ekf->measurement_jacobian(&ekf->H_jacobian, prev_state); + + // measurement covariance prediction + transpose(&H_transpose, &ekf->H_jacobian); + mult(&PH_transpose, &ekf->covariance_estimate, &H_transpose); + mult(&S, &ekf->H_jacobian, &PH_transpose); + add(&S, &S, measurement_noise_cov); + + // kalman gain calculation + inverse2x2(&S_inv, &S); + mult(&K, &PH_transpose, &S_inv); + + // measurement residual calculation + sub(&residual, measurement, &ekf->measurement_pred); + + // state estimation + mult(&Ky, &K, &residual); + add(&ekf->state_estimate, prev_state, &Ky); + + // covariance estimation + mult(&KH, &K, &ekf->H_jacobian); + mult(&KHP, &KH, &ekf->covariance_estimate); + sub(&ekf->covariance_estimate, &ekf->covariance_estimate, &KHP); +} \ No newline at end of file diff --git a/firmware/shared/src/io/io_efuse/io_efuse.c b/firmware/shared/src/io/io_efuse/io_efuse.c new file mode 100644 index 0000000000..e69d89addb --- /dev/null +++ b/firmware/shared/src/io/io_efuse/io_efuse.c @@ -0,0 +1,60 @@ +#include "io_efuse.h" +// #include "io_efuse_datatypes.h" +#include "hw_gpio.h" +#include +#include + +void io_efuse_setChannel(const Efuse *channel, bool enabled) +{ + assert(channel != NULL); + + channel->efuse_functions->set_channel(channel, enabled); +} + +bool io_efuse_isChannelEnabled(const Efuse *channel) +{ + assert(channel != NULL); + return channel->efuse_functions->is_channel_enabled(channel); +} + +float io_efuse_getChannelCurrent(const Efuse *channel) +{ + assert(channel != NULL); + return channel->efuse_functions->get_channel_current(channel); +} + +void io_efuse_reset_set(const Efuse *channel, bool set) +{ + assert(channel != NULL); + assert(channel->efuse_functions->loadswitch_reset_set != NULL); + + channel->efuse_functions->loadswitch_reset_set(channel, set); +} + +void io_efuse_reset(const Efuse *channel) +{ + assert(channel != NULL); + channel->efuse_functions->reset_efuse(channel); +} + +bool io_efuse_pgood(const Efuse *channel) +{ + assert(channel != NULL); + assert(channel->efuse_functions->pgood != NULL); + + return channel->efuse_functions->pgood(channel); +} + +bool io_efuse_ok(const Efuse *efuse) +{ + assert(efuse != NULL); + return IS_EXIT_OK(efuse->efuse_functions->efuse_ok(efuse)); +} + +void io_efuse_setDiagnostics(const Efuse *efuse, bool enabled) +{ + assert(efuse != NULL); + assert(efuse->efuse_functions->set_diagnostics != NULL); + + efuse->efuse_functions->set_diagnostics(efuse, enabled); +} diff --git a/firmware/shared/src/io/io_efuse/io_efuse.h b/firmware/shared/src/io/io_efuse/io_efuse.h new file mode 100644 index 0000000000..e87da0eae7 --- /dev/null +++ b/firmware/shared/src/io/io_efuse/io_efuse.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include "io_efuse_datatypes.h" +// #include "io_efuse_ST/io_efuse_ST.h" +// #include "io_efuse_TI/io_efuse_TI.h" + +typedef struct __Efuse Efuse; + +/** + * Enable or disable the provided efuse channel. + * @param channel Channel to enable/disable + * @param enabled Enable if enabled is true, disable if false + */ +void io_efuse_setChannel(const Efuse *channel, bool enabled); +/** + * Check of provided efuse channel is enabled + * @param channel Channel to enable/disable + * @return If efuse channel is enabled + */ +bool io_efuse_isChannelEnabled(const Efuse *channel); +/** + * Get the current read from the provided channel + * @param channel Channel to enable/disable + * @return The current read from the provided channel, in A + */ +float io_efuse_getChannelCurrent(const Efuse *channel); + +/** + * @param efuse + * @param set + */ +void io_efuse_reset_set(const Efuse *channel, bool set); + +/** + * Reset the hardfault set by the efuse + * @param efuse Reset the hardfault set by efuse + */ +void io_efuse_reset(const Efuse *channel); + +/** + * @brief Check the pgood status + * @param channel: TI Efuse + * @return status of the pgood line + */ +bool io_efuse_pgood(const Efuse *channel); + +/** + * @brief Check the if the efuse status is ok + * @param efuse: efuse to check status on + * @return True if status is ok, False otherwise + */ +bool io_efuse_ok(const Efuse *efuse); + +/** + * Enable or disable the provided efuse diagnostics. + * @param efuse Channel to enable/disable + * @param enabled Enable if enabled is true, disable if false + */ +void io_efuse_setDiagnostics(const Efuse *efuse, bool enabled); \ No newline at end of file diff --git a/firmware/shared/src/io/io_efuse/io_efuse_ST_VND5/io_efuse_ST_VND5.c b/firmware/shared/src/io/io_efuse/io_efuse_ST_VND5/io_efuse_ST_VND5.c new file mode 100644 index 0000000000..8dc580524a --- /dev/null +++ b/firmware/shared/src/io/io_efuse/io_efuse_ST_VND5/io_efuse_ST_VND5.c @@ -0,0 +1,133 @@ +#include +#include + +#include "app_utils.h" + +#include "io_efuse_ST_VND5.h" +#include "io_efuse/io_efuse_datatypes.h" + +#include "hw_gpio.h" + +#include "util_errorCodes.h" + +/** + * ST_VND5 Efuse Datasheet: + * https://octopart.com/datasheet/vnd5t100ajtr-e-stmicroelectronics-21218840 + */ + +// TODO: Characterize this voltage +#define NOMINAL_V 3.3f +// Vsenseh lower threshold +#define V_SENSE_H_L 7.5f +// Vsenseh upper threshold +#define V_SENSE_H_H 9.5f + +#define V_THRES 0.1f + +#define ST_VND5_Efuse_FAULT_FLAGS 0x3F + +// {fault_reset_stby, channel_enabled} +#define L_L 0x00U +#define L_H 0x01U +#define H_L 0x02U +#define H_H 0x03U + +static void io_efuse_ST_setChannel(const Efuse *channel, bool enabled); +static bool io_efuse_ST_isChannelEnabled(const Efuse *channel); +static float io_efuse_ST_getChannelCurrent(const Efuse *channel); +static void io_efuse_ST_reset_set(const Efuse *channel, bool set); +static void io_efuse_ST_reset(const Efuse *channel); +static ExitCode io_efuse_ST_ok(const Efuse *efuse); + +const EfuseFunctions ST_VND5_Efuse_functions = { .set_channel = io_efuse_ST_setChannel, + .is_channel_enabled = io_efuse_ST_isChannelEnabled, + .get_channel_current = io_efuse_ST_getChannelCurrent, + .loadswitch_reset_set = io_efuse_ST_reset_set, + .reset_efuse = io_efuse_ST_reset, + .pgood = NULL, + .efuse_ok = io_efuse_ST_ok, + .set_diagnostics = NULL }; + +static void io_efuse_ST_setChannel(const Efuse *channel, bool enabled) +{ + assert(channel->enable_gpio != NULL); + hw_gpio_writePin(channel->enable_gpio, enabled); +} + +static bool io_efuse_ST_isChannelEnabled(const Efuse *channel) +{ + assert(channel->enable_gpio != NULL); + return hw_gpio_readPin(channel->enable_gpio); +} + +static float io_efuse_ST_getChannelCurrent(const Efuse *channel) +{ + const AdcChannel *current_sense = channel->sns_adc_channel; + assert(current_sense != NULL); + return hw_adc_getVoltage(current_sense) * ADC_VOLTAGE_TO_CURRENT_A; +} + +static void io_efuse_ST_reset_set(const Efuse *channel, bool set) +{ + assert(channel->st_vnd5->stby_reset_gpio != NULL); + hw_gpio_writePin(channel->st_vnd5->stby_reset_gpio, set); +} + +static void io_efuse_ST_reset(const Efuse *channel) +{ + assert(channel->st_vnd5->stby_reset_gpio != NULL); + hw_gpio_writePin(channel->st_vnd5->stby_reset_gpio, false); + hw_gpio_writePin(channel->st_vnd5->stby_reset_gpio, true); + hw_gpio_writePin(channel->st_vnd5->stby_reset_gpio, false); +} + +static ExitCode io_efuse_ST_ok(const Efuse *efuse) +{ + assert(efuse != NULL); + + float voltage = io_efuse_ST_getChannelCurrent(efuse) / ADC_VOLTAGE_TO_CURRENT_A; + bool channelEnabled = io_efuse_ST_isChannelEnabled(efuse); + bool fault_reset_stby = hw_gpio_readPin(efuse->st_vnd5->stby_reset_gpio); + uint8_t fault_table_idx = 0x00U | ((uint8_t)((fault_reset_stby << 1) | (channelEnabled))); + + // Setting faults for st_vnd5 efuse + switch (fault_table_idx) + { + case L_L: + efuse->st_vnd5->faults.flags.overload = APPROX_EQUAL_FLOAT(voltage, 0.0f, V_THRES); + efuse->st_vnd5->faults.flags.ovt_stp = APPROX_EQUAL_FLOAT(voltage, 0.0f, V_THRES); + efuse->st_vnd5->faults.flags.short_to_vbat = APPROX_EQUAL_FLOAT(voltage, 0.0f, V_THRES); + efuse->st_vnd5->faults.flags.open_load_off_stat = APPROX_EQUAL_FLOAT(voltage, 0.0f, V_THRES); + efuse->st_vnd5->faults.flags.negative_output_voltage_clamp = APPROX_EQUAL_FLOAT(voltage, 0.0f, V_THRES); + break; + case L_H: + efuse->st_vnd5->faults.flags.overload = APPROX_EQUAL_FLOAT(voltage, NOMINAL_V, V_THRES); + efuse->st_vnd5->faults.flags.ovt_stp = IS_IN_RANGE(V_SENSE_H_L, V_SENSE_H_H, voltage); + efuse->st_vnd5->faults.flags.short_to_vbat = APPROX_EQUAL_FLOAT(voltage, NOMINAL_V, V_THRES); + efuse->st_vnd5->faults.flags.open_load_off_stat = APPROX_EQUAL_FLOAT(voltage, 0.0f, V_THRES); + // efuse->st_vnd5->faults.flags.negative_output_voltage_clamp = (voltage <= 0.0f); + break; + case H_L: + efuse->st_vnd5->faults.flags.overload = APPROX_EQUAL_FLOAT(voltage, 0.0f, V_THRES); + efuse->st_vnd5->faults.flags.ovt_stp = APPROX_EQUAL_FLOAT(voltage, 0.0f, V_THRES); + efuse->st_vnd5->faults.flags.short_to_vbat = IS_IN_RANGE(V_SENSE_H_L, V_SENSE_H_H, voltage); + efuse->st_vnd5->faults.flags.open_load_off_stat = IS_IN_RANGE(V_SENSE_H_L, V_SENSE_H_H, voltage); + efuse->st_vnd5->faults.flags.negative_output_voltage_clamp = APPROX_EQUAL_FLOAT(voltage, 0.0f, V_THRES); + break; + case H_H: + efuse->st_vnd5->faults.flags.overload = APPROX_EQUAL_FLOAT(voltage, NOMINAL_V, V_THRES); + efuse->st_vnd5->faults.flags.ovt_stp = IS_IN_RANGE(V_SENSE_H_L, V_SENSE_H_H, voltage); + efuse->st_vnd5->faults.flags.short_to_vbat = APPROX_EQUAL_FLOAT(voltage, NOMINAL_V, V_THRES); + efuse->st_vnd5->faults.flags.open_load_off_stat = APPROX_EQUAL_FLOAT(voltage, 0.0f, V_THRES); + // efuse->st_vnd5->faults.flags.negative_output_voltage_clamp = (voltage <= 0.0f); + break; + default: + break; + } + + // This fault is the same for all conditions + efuse->st_vnd5->faults.flags.under_voltage = APPROX_EQUAL_FLOAT(voltage, 0.0f, V_THRES); + + // check if any flag is set, then return the status + return ((efuse->st_vnd5->faults.raw & ST_VND5_Efuse_FAULT_FLAGS) > 0) ? EXIT_CODE_ERROR : EXIT_CODE_OK; +} diff --git a/firmware/shared/src/io/io_efuse/io_efuse_ST_VND5/io_efuse_ST_VND5.h b/firmware/shared/src/io/io_efuse/io_efuse_ST_VND5/io_efuse_ST_VND5.h new file mode 100644 index 0000000000..cb55c4f83a --- /dev/null +++ b/firmware/shared/src/io/io_efuse/io_efuse_ST_VND5/io_efuse_ST_VND5.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include "hw_gpio.h" +#include "hw_adc.h" + +typedef struct __EfuseFunctions EfuseFunctions; + +typedef struct __ST_VND5_Efuse +{ + const Gpio *stby_reset_gpio; + + /* Portable bit-fields: use 'unsigned' and name the subgroup */ + union + { + struct + { + uint8_t overload : 1; + uint8_t ovt_stp : 1; + uint8_t under_voltage : 1; + uint8_t short_to_vbat : 1; + uint8_t open_load_off_stat : 1; + uint8_t negative_output_voltage_clamp : 1; + uint8_t padding : 2; + } flags; + uint8_t raw; + } faults; +} ST_VND5_Efuse; + +extern const EfuseFunctions ST_VND5_Efuse_functions; diff --git a/firmware/shared/src/io/io_efuse/io_efuse_TI_TPS25/io_efuse_TI_TPS25.c b/firmware/shared/src/io/io_efuse/io_efuse_TI_TPS25/io_efuse_TI_TPS25.c new file mode 100644 index 0000000000..625a96bde6 --- /dev/null +++ b/firmware/shared/src/io/io_efuse/io_efuse_TI_TPS25/io_efuse_TI_TPS25.c @@ -0,0 +1,60 @@ +#include "io_efuse_TI_TPS25.h" +#include "io_efuse/io_efuse_datatypes.h" +#include "hw_gpio.h" +#include +#include + +static void io_TI_TPS25_Efuse_setChannel(const Efuse *channel, bool enabled); +static bool io_TI_TPS25_Efuse_isChannelEnabled(const Efuse *channel); +static float io_TI_TPS25_Efuse_getChannelCurrent(const Efuse *channel); +static void io_TI_TPS25_Efuse_reset(const Efuse *efuse); +static bool io_TI_TPS25_Efuse_pgood(const Efuse *efuse); +static ExitCode io_TI_TPS25_Efuse_ok(const Efuse *efuse); + +const EfuseFunctions TI_TPS25_Efuse_functions = { .set_channel = io_TI_TPS25_Efuse_setChannel, + .is_channel_enabled = io_TI_TPS25_Efuse_isChannelEnabled, + .get_channel_current = io_TI_TPS25_Efuse_getChannelCurrent, + .loadswitch_reset_set = NULL, + .reset_efuse = io_TI_TPS25_Efuse_reset, + .pgood = io_TI_TPS25_Efuse_pgood, + .efuse_ok = io_TI_TPS25_Efuse_ok, + .set_diagnostics = NULL }; + +static void io_TI_TPS25_Efuse_setChannel(const Efuse *channel, bool enabled) +{ + assert(channel->enable_gpio != NULL); + hw_gpio_writePin(channel->enable_gpio, enabled); +} + +static bool io_TI_TPS25_Efuse_isChannelEnabled(const Efuse *channel) +{ + assert(channel->enable_gpio != NULL); + return hw_gpio_readPin(channel->enable_gpio); +} + +static float io_TI_TPS25_Efuse_getChannelCurrent(const Efuse *channel) +{ + const AdcChannel *current_sense = channel->sns_adc_channel; + assert(current_sense != NULL); + return hw_adc_getVoltage(current_sense) * ADC_VOLTAGE_TO_CURRENT_A; +} + +static void io_TI_TPS25_Efuse_reset(const Efuse *efuse) +{ + assert(efuse->enable_gpio != NULL); + + hw_gpio_writePin(efuse->enable_gpio, false); + hw_gpio_writePin(efuse->enable_gpio, true); + hw_gpio_writePin(efuse->enable_gpio, false); +} + +static bool io_TI_TPS25_Efuse_pgood(const Efuse *efuse) +{ + assert(efuse->ti_tps25->pgood != NULL); + return hw_gpio_readPin(efuse->ti_tps25->pgood); +} + +static ExitCode io_TI_TPS25_Efuse_ok(const Efuse *efuse) +{ + return io_TI_TPS25_Efuse_pgood(efuse) ? EXIT_CODE_OK : EXIT_CODE_OK; +} diff --git a/firmware/shared/src/io/io_efuse/io_efuse_TI_TPS25/io_efuse_TI_TPS25.h b/firmware/shared/src/io/io_efuse/io_efuse_TI_TPS25/io_efuse_TI_TPS25.h new file mode 100644 index 0000000000..9dd10194d7 --- /dev/null +++ b/firmware/shared/src/io/io_efuse/io_efuse_TI_TPS25/io_efuse_TI_TPS25.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include +#include "hw_gpio.h" +#include "hw_adc.h" + +typedef struct __EfuseFunctions EfuseFunctions; + +typedef struct __TI_TPS25_Efuse +{ + const Gpio *pgood; +} TI_TPS25_Efuse; + +extern const EfuseFunctions TI_TPS25_Efuse_functions; diff --git a/firmware/shared/src/io/io_efuse/io_efuse_TI_TPS28/io_efuse_TI_TPS28.c b/firmware/shared/src/io/io_efuse/io_efuse_TI_TPS28/io_efuse_TI_TPS28.c new file mode 100644 index 0000000000..7cd59df69c --- /dev/null +++ b/firmware/shared/src/io/io_efuse/io_efuse_TI_TPS28/io_efuse_TI_TPS28.c @@ -0,0 +1,133 @@ +#include "app_utils.h" +#include "io_efuse_TI_TPS28.h" +#include "io_efuse/io_efuse_datatypes.h" +#include "hw_gpio.h" +#include +#include + +/** + * TI_TPS28 Efuse Datasheet: + * https://www.ti.com/lit/ds/symlink/tps281c30.pdf?ts=1763641186988&ref_url=https%253A%252F%252Fwww.ti.com%252Fproduct%252FTPS281C30 + */ + +/** + * The Current limit (I_LIM) is set attaching the ILIM pin to a pulldown resistor. + * + * The pulldown resistor must be between 10kOhm - 50kOhm + * The current limit ratio K_CL is: + * 40 A*kOhm - 60 A*kOhm (Rev A or C) + * 80 A*kOhm - 120 A*kOhm (Rev B, D, or C) + */ + +#define R_PD 24.9f // kOhm +// TODO: is this actualy a linear interpolation and which revision are we?? +#define K_CL ((((60.0f - 40.0f) / (50.0f - 10.0f)) * R_PD) + 35.0f) // A*kOhm +#define I_LIM (K_CL / R_PD) + +// There are different V_SNSFH thresholds if DIAG_EN gpio is set to LOw (3.3V) or HIGH (5V) +#define V_SNSFH_MIN 3.5f +#define V_SNSFH 3.95f +#define V_SNSFH_MAX 4.4f + +static void io_TI_TPS28_Efuse_setChannel(const Efuse *channel, bool enabled); +static bool io_TI_TPS28_Efuse_isChannelEnabled(const Efuse *channel); +static float io_TI_TPS28_Efuse_getChannelCurrent(const Efuse *channel); +static void io_TI_TPS28_Efuse_reset(const Efuse *efuse); +static bool io_TI_TPS28_Efuse_pgood(const Efuse *efuse); +static ExitCode io_TI_TPS28_Efuse_ok(const Efuse *efuse); + +const EfuseFunctions TI_TPS28_Efuse_functions = { .set_channel = io_TI_TPS28_Efuse_setChannel, + .is_channel_enabled = io_TI_TPS28_Efuse_isChannelEnabled, + .get_channel_current = io_TI_TPS28_Efuse_getChannelCurrent, + .loadswitch_reset_set = NULL, + .reset_efuse = io_TI_TPS28_Efuse_reset, + .pgood = NULL, + .efuse_ok = io_TI_TPS28_Efuse_ok, + .set_diagnostics = io_TI_TPS28_Efuse_setDiagnostics }; + +static void io_TI_TPS28_Efuse_setChannel(const Efuse *channel, bool enabled) +{ + assert(channel->enable_gpio != NULL); + hw_gpio_writePin(channel->enable_gpio, enabled); +} + +static bool io_TI_TPS28_Efuse_isChannelEnabled(const Efuse *channel) +{ + assert(channel->enable_gpio != NULL); + return hw_gpio_readPin(channel->enable_gpio); +} + +static bool io_TI_TPS28_Efuse_isDiagEnabled(const Efuse *channel) +{ + assert(channel->ti_tps28->diag_en_gpio != NULL); + return hw_gpio_readPin(channel->ti_tps28->diag_en_gpio); +} + +static bool io_TI_TPS28_Efuse_setDiagnostics(const Efuse *efuse, bool enabled) +{ + assert(efuse->ti_tps28->diag_en_gpio != NULL); + hw_gpio_writePin(efuse->ti_tps28->diag_en_gpio, enabled); +} + +static float io_TI_TPS28_Efuse_getChannelCurrent(const Efuse *channel) +{ + const AdcChannel *current_sense = channel->sns_adc_channel; + assert(current_sense != NULL); + return hw_adc_getVoltage(current_sense) * ADC_VOLTAGE_TO_CURRENT_A; +} +// TODO: verify reset function +static void io_TI_TPS28_Efuse_reset(const Efuse *efuse) +{ + assert(efuse->enable_gpio != NULL); + + hw_gpio_writePin(efuse->enable_gpio, false); + hw_gpio_writePin(efuse->enable_gpio, true); + hw_gpio_writePin(efuse->enable_gpio, false); +} + +static ExitCode io_TI_TPS28_Efuse_ok(const Efuse *efuse) +{ + assert(efuse != NULL); + + bool channel_enabled = io_TI_TPS28_Efuse_isChannelEnabled(efuse); + bool diag_enabled = hw_gpio_readPin(efuse->ti_tps28->diag_en_gpio); + float voltage = io_TI_TPS28_Efuse_getChannelCurrent(efuse) / ADC_VOLTAGE_TO_CURRENT_A; + + /** + * Note: Table 8.2 DIAG_EN Logic Table + * + * If DIAG_EN is low, FAULT will continue to indicate + * TSD (thermal_shdn) or ILIM (forgor what this is) + * + * Thus we only dont check for overcurrent if it's disabled + * + * TODO: but Table 8-3 contradicts this + */ + + // if diagnostics are not enabled, only check TSD and ILIM + bool is_faulted = hw_gpio_readPin(efuse->ti_tps28->fault_gpio); + if (diag_enabled) + { + if (is_faulted) + { + // Determine specific faults + efuse->ti_tps28->faults.flags.overcurrent = + (channel_enabled && IS_IN_RANGE(V_SNSFH_MIN, V_SNSFH_MAX, voltage)); + efuse->ti_tps28->faults.flags.thermal_shdn = + (channel_enabled && IS_IN_RANGE(V_SNSFH_MIN, V_SNSFH_MAX, voltage)); + } + else + { + efuse->ti_tps28->faults.raw = 0x00U; + } + } + else + { + // TODO: is there a different way to check diagnostics when diag_enabled is low or is it the same way?? + efuse->ti_tps28->faults.raw = 0x03; + } + + return (efuse->ti_tps28->faults.raw > 0) ? EXIT_CODE_OK : EXIT_CODE_ERROR; +} + +static void io diff --git a/firmware/shared/src/io/io_efuse/io_efuse_TI_TPS28/io_efuse_TI_TPS28.h b/firmware/shared/src/io/io_efuse/io_efuse_TI_TPS28/io_efuse_TI_TPS28.h new file mode 100644 index 0000000000..7fb38b7557 --- /dev/null +++ b/firmware/shared/src/io/io_efuse/io_efuse_TI_TPS28/io_efuse_TI_TPS28.h @@ -0,0 +1,25 @@ +#pragma once + +#include "hw_gpio.h" + +typedef struct __EfuseFunctions EfuseFunctions; + +typedef struct __TI_TPS28_Efuse +{ + const Gpio *fault_gpio; + const Gpio *diag_en_gpio; + + /* Portable bit-fields: use 'unsigned' and name the subgroup */ + union + { + struct + { + uint8_t overcurrent : 1; + uint8_t thermal_shdn : 1; + uint8_t padding : 6; + } flags; + uint8_t raw; + } faults; +} TI_TPS28_Efuse; + +extern const EfuseFunctions TI_TPS28_EfuseFunctions; \ No newline at end of file diff --git a/firmware/shared/src/io/io_efuse/io_efuse_datatypes.h b/firmware/shared/src/io/io_efuse/io_efuse_datatypes.h new file mode 100644 index 0000000000..8c3ef6caae --- /dev/null +++ b/firmware/shared/src/io/io_efuse/io_efuse_datatypes.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include "hw_gpio.h" +#include "hw_adc.h" + +#include "util_errorCodes.h" + +#define ADC_VOLTAGE_TO_CURRENT_A 1.720f + +typedef struct __Efuse Efuse; +typedef struct __ST_VND5_Efuse ST_VND5_Efuse; +typedef struct __TI_TPS25_Efuse TI_TPS25_Efuse; +typedef struct __TI_TPS28_Efuse TI_TPS28_Efuse; +typedef struct __EfuseFunctions EfuseFunctions; + +struct __EfuseFunctions +{ + void (*set_channel)(const Efuse *channel, bool enabled); + bool (*is_channel_enabled)(const Efuse *channel); + float (*get_channel_current)(const Efuse *channel); + void (*loadswitch_reset_set)(const Efuse *channel, bool set); + void (*reset_efuse)(const Efuse *channel); + bool (*pgood)(const Efuse *channel); + ExitCode (*efuse_ok)(const Efuse *efuse); + bool (*set_diagnostics)(const Efuse *efuse, bool enabled); +}; + +struct __Efuse +{ + const Gpio *enable_gpio; + const AdcChannel *sns_adc_channel; + union + { + ST_VND5_Efuse *st_vnd5; + TI_TPS25_Efuse *ti_tps25; + TI_TPS28_Efuse *ti_tps28; + }; + + const EfuseFunctions *efuse_functions; +}; diff --git a/firmware/shared/src/io/io_loadswitch.c b/firmware/shared/src/io/io_loadswitch.c deleted file mode 100644 index 620de3f2ad..0000000000 --- a/firmware/shared/src/io/io_loadswitch.c +++ /dev/null @@ -1,53 +0,0 @@ -#include "io_loadswitch.h" -#include "hw_gpio.h" -#include -#include - -void io_loadswitch_setChannel(const Efuse *channel, const bool enabled) -{ - assert(channel->enable_gpio != NULL); - hw_gpio_writePin(channel->enable_gpio, enabled); -} - -bool io_loadswitch_isChannelEnabled(const Efuse *channel) -{ - assert(channel->enable_gpio != NULL); - return hw_gpio_readPin(channel->enable_gpio); -} - -float io_loadswitch_getChannelCurrent(const Efuse *channel) -{ - const AdcChannel *current_sense = channel->sns_adc_channel; - assert(current_sense != NULL); - return hw_adc_getVoltage(current_sense) * ADC_VOLTAGE_TO_CURRENT_A; -} - -void io_STloadswitch_reset_set(const ST_LoadSwitch *loadswitch, const bool set) -{ - assert(loadswitch->stby_reset_gpio != NULL); - hw_gpio_writePin(loadswitch->stby_reset_gpio, set); -} - -void io_STloadswitch_Reset(const ST_LoadSwitch *loadswitch) -{ - assert(loadswitch->stby_reset_gpio != NULL); - hw_gpio_writePin(loadswitch->stby_reset_gpio, false); - hw_gpio_writePin(loadswitch->stby_reset_gpio, true); - hw_gpio_writePin(loadswitch->stby_reset_gpio, false); -} - -void io_TILoadswitch_Reset(const TI_LoadSwitch *loadSwitch) -{ - assert(loadSwitch->efuse != NULL); - assert(loadSwitch->efuse->enable_gpio != NULL); - - hw_gpio_writePin(loadSwitch->efuse->enable_gpio, false); - hw_gpio_writePin(loadSwitch->efuse->enable_gpio, true); - hw_gpio_writePin(loadSwitch->efuse->enable_gpio, false); -} - -bool io_TILoadswitch_pgood(const TI_LoadSwitch *loadSwitch) -{ - assert(loadSwitch->pgood != NULL); - return hw_gpio_readPin(loadSwitch->pgood); -} diff --git a/firmware/shared/src/io/io_loadswitch.h b/firmware/shared/src/io/io_loadswitch.h deleted file mode 100644 index 2623a67088..0000000000 --- a/firmware/shared/src/io/io_loadswitch.h +++ /dev/null @@ -1,96 +0,0 @@ -#pragma once - -#include "io_loadswitch.h" -#include - -#define ADC_VOLTAGE_TO_CURRENT_A 1.720f - -#ifdef TARGET_EMBEDDED -#include "hw_gpios.h" -#include "hw_adcs.h" - -#define ADC_VOLTAGE_TO_CURRENT_A 1.720f - -typedef struct -{ - const Gpio *enable_gpio; - const AdcChannel *sns_adc_channel; -} Efuse; - -typedef struct -{ - const Efuse *efuse1; - const Efuse *efuse2; - const Gpio *stby_reset_gpio; -} ST_LoadSwitch; - -typedef struct -{ - const Efuse *efuse; - const Gpio *pgood; -} TI_LoadSwitch; -#else -typedef struct -{ - bool enabled; - float current; - bool simulate_fault; -} Efuse; -typedef struct -{ - const Efuse *efuse; - bool pgood; -} TI_LoadSwitch; -typedef struct -{ - const Efuse *efuse1; - const Efuse *efuse2; - - bool pgood; - bool set_stby_reset_gpio; -} ST_LoadSwitch; -#endif - -/** - * Enable or disable the provided loadswitch channel. - * @param channel Channel to enable/disable - * @param enabled Enable if enabled is true, disable if false - */ -void io_loadswitch_setChannel(const Efuse *channel, bool enabled); -/** - * Check of provided loadswitch channel is enabled - * @param channel Channel to enable/disable - * @return If loadswitch channel is enabled - */ -bool io_loadswitch_isChannelEnabled(const Efuse *channel); -/** - * Get the current read from the provided channel - * @param channel Channel to enable/disable - * @return The current read from the provided channel, in A - */ -float io_loadswitch_getChannelCurrent(const Efuse *channel); - -/** - * @param loadswitch - * @param set - */ -void io_STloadswitch_reset_set(const ST_LoadSwitch *loadswitch, bool set); - -/** - * Reset the hardfault set by the loadswitch - * @param loadswitch Reset the hardfault set by loadswitch - */ -void io_STloadswitch_Reset(const ST_LoadSwitch *loadswitch); - -/** - * Check if pgood is ok and if not reset the loadswitch - * @param loadswitch Reset the hardfault set by loadswitch - */ -void io_TILoadswitch_Reset(const TI_LoadSwitch *loadSwitch); - -/** - * TI Loadswitch pgood line status - * @param loadSwitch TI Loadswitch in question - * @return status of the pgood line - */ -bool io_TILoadswitch_pgood(const TI_LoadSwitch *loadSwitch); \ No newline at end of file diff --git a/firmware/shared/srcpp/hw/hw_adc.hpp b/firmware/shared/srcpp/hw/hw_adc.hpp index 2e10916999..8f3db48d60 100644 --- a/firmware/shared/srcpp/hw/hw_adc.hpp +++ b/firmware/shared/srcpp/hw/hw_adc.hpp @@ -1,6 +1,6 @@ #pragma once -#include "app_utils.hpp" +#include "util_utils.hpp" #include #ifdef TARGET_EMBEDDED diff --git a/firmware/shared/srcpp/io/efuse/io_efuse.hpp b/firmware/shared/srcpp/io/efuse/io_efuse.hpp new file mode 100644 index 0000000000..9f840874a5 --- /dev/null +++ b/firmware/shared/srcpp/io/efuse/io_efuse.hpp @@ -0,0 +1,28 @@ +#pragma once +#include + +#include "hw_gpio.hpp" +#include "hw_adc.hpp" + +namespace io +{ +class Efuse +{ + protected: + static constexpr float ADC_VOLTAGE_TO_CURRENT_A = 1.720f; + const hw::Gpio &enable_gpio; + const hw::Adc &sns_adc_channel; + + public: + explicit consteval Efuse(const hw::Gpio &in_enable_gpio, const hw::Adc &in_sns_adc_channel) + : enable_gpio(in_enable_gpio), sns_adc_channel(in_sns_adc_channel) + { + } + virtual ~Efuse() = default; + void setChannel(bool enabled) { enable_gpio.writePin(enabled); } + bool isChannelEnabled() const { return this->enable_gpio.readPin(); } + float getChannelCurrent() { return this->sns_adc_channel.getVoltage() * ADC_VOLTAGE_TO_CURRENT_A; } + virtual void reset() = 0; + virtual bool ok() = 0; +}; +} // namespace io \ No newline at end of file diff --git a/firmware/shared/srcpp/io/efuse/io_efuse_ST_VND5.cpp b/firmware/shared/srcpp/io/efuse/io_efuse_ST_VND5.cpp new file mode 100644 index 0000000000..0e0afb5aaf --- /dev/null +++ b/firmware/shared/srcpp/io/efuse/io_efuse_ST_VND5.cpp @@ -0,0 +1,88 @@ +extern "C" +{ +#include "app_utils.h" +} + +#include "io_efuse_ST_VND5.hpp" +#include "hw_gpio.hpp" + +namespace io +{ +static constexpr float NOMINAL_V = 3.0f; +// Vsenseh upper threshold +static constexpr float V_SENSE_H_H = 9.5f; +// Vsenseh lower threshold +static constexpr float V_SENSE_H_L = 7.5f; +static constexpr float V_THRES = 0.1f; + +static constexpr uint8_t ST_VND5_Efuse_FAULT_FLAGS = 0x3F; + +// {fault_reset_stby, channel_enabled} +static constexpr uint8_t L_L = 0x00U; +static constexpr uint8_t L_H = 0x01U; +static constexpr uint8_t H_L = 0x02U; +static constexpr uint8_t H_H = 0x03U; + +void ST_VND5_Efuse::reset() +{ + this->stby_reset_gpio.writePin(false); + this->stby_reset_gpio.writePin(true); + this->stby_reset_gpio.writePin(false); +} + +void ST_VND5_Efuse::resetSet(const bool set) +{ + this->stby_reset_gpio.writePin(set); +} + +// TODO: Do we want to return a boolean or should we just return the faults union and add additional methods to check +// specific faults +bool ST_VND5_Efuse::ok() +{ + const float voltage = this->getChannelCurrent() / ADC_VOLTAGE_TO_CURRENT_A; + const bool channel_enabled = this->isChannelEnabled(); + const bool fault_reset_stby = this->stby_reset_gpio.readPin(); + const uint8_t fault_table_idx = + static_cast((static_cast(fault_reset_stby) << 1U) | static_cast(channel_enabled)); + + // Setting faults for st_vnd5 efuse + // TODO: verify these + switch (fault_table_idx) + { + case L_L: + this->faults.flags.overload = APPROX_EQUAL_FLOAT(voltage, 0.0f, V_THRES); + this->faults.flags.ovt_stp = APPROX_EQUAL_FLOAT(voltage, 0.0f, V_THRES); + this->faults.flags.short_to_vbat = APPROX_EQUAL_FLOAT(voltage, 0.0f, V_THRES); + this->faults.flags.open_load_off_stat = APPROX_EQUAL_FLOAT(voltage, 0.0f, V_THRES); + this->faults.flags.negative_output_voltage_clamp = APPROX_EQUAL_FLOAT(voltage, 0.0f, V_THRES); + break; + case L_H: + this->faults.flags.overload = APPROX_EQUAL_FLOAT(voltage, NOMINAL_V, V_THRES); + this->faults.flags.ovt_stp = IS_IN_RANGE(V_SENSE_H_L, V_SENSE_H_H, voltage); + this->faults.flags.short_to_vbat = APPROX_EQUAL_FLOAT(voltage, NOMINAL_V, V_THRES); + this->faults.flags.open_load_off_stat = APPROX_EQUAL_FLOAT(voltage, 0.0f, V_THRES); + break; + case H_L: + this->faults.flags.overload = APPROX_EQUAL_FLOAT(voltage, 0.0f, V_THRES); + this->faults.flags.ovt_stp = APPROX_EQUAL_FLOAT(voltage, 0.0f, V_THRES); + this->faults.flags.short_to_vbat = IS_IN_RANGE(V_SENSE_H_L, V_SENSE_H_H, voltage); + this->faults.flags.open_load_off_stat = IS_IN_RANGE(V_SENSE_H_L, V_SENSE_H_H, voltage); + this->faults.flags.negative_output_voltage_clamp = APPROX_EQUAL_FLOAT(voltage, 0.0f, V_THRES); + break; + case H_H: + this->faults.flags.overload = APPROX_EQUAL_FLOAT(voltage, NOMINAL_V, V_THRES); + this->faults.flags.ovt_stp = IS_IN_RANGE(V_SENSE_H_L, V_SENSE_H_H, voltage); + this->faults.flags.short_to_vbat = APPROX_EQUAL_FLOAT(voltage, NOMINAL_V, V_THRES); + this->faults.flags.open_load_off_stat = APPROX_EQUAL_FLOAT(voltage, 0.0f, V_THRES); + break; + default: + break; + } + + this->faults.flags.under_voltage = APPROX_EQUAL_FLOAT(voltage, 0.0f, V_THRES); + + const uint8_t flags = this->faults.raw & ST_VND5_Efuse_FAULT_FLAGS; + + return !(flags > 0U); +} +} // namespace io diff --git a/firmware/shared/srcpp/io/efuse/io_efuse_ST_VND5.hpp b/firmware/shared/srcpp/io/efuse/io_efuse_ST_VND5.hpp new file mode 100644 index 0000000000..22895fe82b --- /dev/null +++ b/firmware/shared/srcpp/io/efuse/io_efuse_ST_VND5.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include +#include "io_efuse.hpp" + +namespace io +{ +class ST_VND5_Efuse final : public Efuse +{ + private: + const hw::Gpio &stby_reset_gpio; + union + { + struct + { + uint8_t overload : 1; + uint8_t ovt_stp : 1; + uint8_t under_voltage : 1; + uint8_t short_to_vbat : 1; + uint8_t open_load_off_stat : 1; + uint8_t negative_output_voltage_clamp : 1; + uint8_t padding : 2; + } flags; + uint8_t raw; + } faults; + + public: + explicit consteval ST_VND5_Efuse( + const hw::Gpio &in_enable_gpio, + const hw::Adc &in_sns_adc_channel, + const hw::Gpio &in_stby_reset_gpio) + : Efuse(in_enable_gpio, in_sns_adc_channel), stby_reset_gpio(in_stby_reset_gpio) + { + } + void reset() override final; + void resetSet(const bool set); + bool ok() override final; +}; +} // namespace io diff --git a/firmware/shared/srcpp/io/efuse/io_efuse_TI_TPS25.cpp b/firmware/shared/srcpp/io/efuse/io_efuse_TI_TPS25.cpp new file mode 100644 index 0000000000..892b5d9bb0 --- /dev/null +++ b/firmware/shared/srcpp/io/efuse/io_efuse_TI_TPS25.cpp @@ -0,0 +1,23 @@ +#include +#include "io_efuse_TI_TPS25.hpp" +#include "hw_gpio.hpp" + +namespace io +{ +void TI_TPS25_Efuse::reset() +{ + this->enable_gpio.writePin(false); + this->enable_gpio.writePin(true); + this->enable_gpio.writePin(false); +} + +bool TI_TPS25_Efuse::pgood() const +{ + return this->pgood_gpio.readPin(); +} + +bool TI_TPS25_Efuse::ok() +{ + return this->pgood(); +} +} // namespace io diff --git a/firmware/shared/srcpp/io/efuse/io_efuse_TI_TPS25.hpp b/firmware/shared/srcpp/io/efuse/io_efuse_TI_TPS25.hpp new file mode 100644 index 0000000000..a5e87db980 --- /dev/null +++ b/firmware/shared/srcpp/io/efuse/io_efuse_TI_TPS25.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include "io_efuse.hpp" + +namespace io +{ +class TI_TPS25_Efuse final : public Efuse +{ + private: + const hw::Gpio &pgood_gpio; + + public: + explicit consteval TI_TPS25_Efuse( + const hw::Gpio &in_enable_gpio, + const hw::Adc &in_sns_adc_channel, + const hw::Gpio &in_pgood) + : Efuse(in_enable_gpio, in_sns_adc_channel), pgood_gpio(in_pgood) + { + } + void reset() override final; + bool pgood() const; + bool ok() override final; +}; +} // namespace io diff --git a/firmware/shared/srcpp/io/efuse/io_efuse_TI_TPS28.cpp b/firmware/shared/srcpp/io/efuse/io_efuse_TI_TPS28.cpp new file mode 100644 index 0000000000..92c9740c97 --- /dev/null +++ b/firmware/shared/srcpp/io/efuse/io_efuse_TI_TPS28.cpp @@ -0,0 +1,81 @@ +extern "C" +{ +#include "app_utils.h" +} + +#include "io_efuse_TI_TPS28.hpp" +#include "hw_gpio.hpp" + +/** + * TI_TPS28 Efuse Datasheet: + * https://www.ti.com/lit/ds/symlink/tps281c30.pdf?ts=1763641186988&ref_url=https%253A%252F%252Fwww.ti.com%252Fproduct%252FTPS281C30 + */ + +/** + * The Current limit (I_LIM) is set attaching the ILIM pinto a pulldown resistor. + * + * The pulldown resistor must be between 10kOhm - 50kOhm + * The current limit ration K_CL is: + * 40 A*kOhm - 60 A*kOhm (Rev A or C) + * 80 A*kOhm - 120 A*kOhm (Rev B, D, or C) + */ + +namespace io +{ +static constexpr float R_PD = 24.9f; // kOhm +// TODO: is this actualy a linear interpolation?? +static constexpr float K_CL = (((60.0f - 40.0f) / (50.0f - 10.0f)) * R_PD); // A*kOhm +static constexpr float I_LIM = (K_CL / R_PD); + +// There are different V_SNSFH thresholds if DIAG_EN gpio is set to LOw (3.3V) or HIGH (5V) +static constexpr float V_SNSFH_MIN = 3.5f; +static constexpr float V_SNSFH = 3.95f; +static constexpr float V_SNSFH_MAX = 4.4f; + +// TODO: verify reset function +void TI_TPS28_Efuse::reset() +{ + this->enable_gpio.writePin(false); + this->enable_gpio.writePin(true); + this->enable_gpio.writePin(false); +} + +bool TI_TPS28_Efuse::ok() +{ + const bool channel_enabled = this->isChannelEnabled(); + const bool diag_enabled = this->diag_en_gpio.readPin(); + const float voltage = this->getChannelCurrent() / ADC_VOLTAGE_TO_CURRENT_A; + + /** + * Note: Table 8.2 DIAG_EN Logic Table + * + * If DIAG_EN is low, FAULT will continue to indicate + * TSD (thermal_shdn) or ILIM (forgor what this is) + * + * Thus we only dont check for overcurrent if it's disabled + * + * TODO: but Table 8-3 contradicts this + */ + + // if diagnostics are not enabled, only check TSD and ILIM + const bool is_faulted = this->fault_gpio.readPin(); + if (diag_enabled) + { + if (is_faulted) + { + this->faults.flags.overcurrent = (channel_enabled && IS_IN_RANGE(V_SNSFH_MIN, V_SNSFH_MAX, voltage)); + this->faults.flags.thermal_shdn = (channel_enabled && IS_IN_RANGE(V_SNSFH_MIN, V_SNSFH_MAX, voltage)); + } + else + { + this->faults.raw = 0x00U; + } + } + else + { + this->faults.raw = 0x03U; + } + + return !(this->faults.raw > 0U); +} +} // namespace io diff --git a/firmware/shared/srcpp/io/efuse/io_efuse_TI_TPS28.hpp b/firmware/shared/srcpp/io/efuse/io_efuse_TI_TPS28.hpp new file mode 100644 index 0000000000..bc9e9fcb18 --- /dev/null +++ b/firmware/shared/srcpp/io/efuse/io_efuse_TI_TPS28.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include "io_efuse.hpp" + +namespace io +{ +class TI_TPS28_Efuse final : public Efuse +{ + private: + const hw::Gpio &fault_gpio; + const hw::Gpio &diag_en_gpio; + + /* Portable bit-fields: use 'unsigned' and name the subgroup */ + union + { + struct + { + uint8_t overcurrent : 1; + uint8_t thermal_shdn : 1; + uint8_t padding : 6; + } flags; + uint8_t raw; + } faults; + + public: + explicit consteval TI_TPS28_Efuse( + const hw::Gpio &in_enable_gpio, + const hw::Adc &in_sns_adc_channel, + const hw::Gpio &in_fault_gpio, + const hw::Gpio &in_diag_en_gpio) + : Efuse(in_enable_gpio, in_sns_adc_channel), fault_gpio(in_fault_gpio), diag_en_gpio(in_diag_en_gpio) + { + } + void reset() override final; + bool ok() override final; +}; +} // namespace io