diff --git a/libraries/codegen/boards/bms_carrier.yaml b/libraries/codegen/boards/bms_carrier.yaml index b8a18b6b7..64de317d9 100644 --- a/libraries/codegen/boards/bms_carrier.yaml +++ b/libraries/codegen/boards/bms_carrier.yaml @@ -19,20 +19,29 @@ watchdog: 3 critical: true signals: - batt_perc: + fault: length: 16 - status: + aux_batt_v: + length: 32 + + battery_info: + id: 33 + target: + centre_console: + watchdog: 0 + signals: + fan1: length: 8 - fault: + fan2: length: 8 - + max_cell_v: + length: 32 + battery_vt: id: 30 target: centre_console: watchdog: 0 - telemetry: - watchdog: 0 critical: false signals: voltage: @@ -43,41 +52,7 @@ length: 16 batt_perc: length: 16 - - battery_aggregate_vc: - id: 33 - target: - telemetry: - watchdog: 0 - signals: - voltage: - length: 32 - current: - length: 32 - - battery_fan_state: - id: 57 - target: - bms_carrier: - watchdog: 0 - signals: - fan_1: - length: 8 - fan_2: - length: 8 - fan_3: - length: 8 - fan_4: - length: 8 - fan_5: - length: 8 - fan_6: - length: 8 - fan_7: - length: 8 - fan_8: - length: 8 - + battery_relay_info: id: 58 target: diff --git a/libraries/ms-common/inc/arm/pwm_mcu.h b/libraries/ms-common/inc/arm/pwm_mcu.h index 1fa038348..0c1f81ab5 100644 --- a/libraries/ms-common/inc/arm/pwm_mcu.h +++ b/libraries/ms-common/inc/arm/pwm_mcu.h @@ -6,6 +6,7 @@ typedef enum { PWM_TIMER_1 = 0, // PWM_TIMER_2, // Requisitioned to back the soft_timer module. PWM_TIMER_3, + PWM_TIMER_4, // Usually used by i2c PWM_TIMER_14, PWM_TIMER_15, PWM_TIMER_16, diff --git a/libraries/ms-common/inc/exported_enums.h b/libraries/ms-common/inc/exported_enums.h index 022635219..c6231f8a6 100644 --- a/libraries/ms-common/inc/exported_enums.h +++ b/libraries/ms-common/inc/exported_enums.h @@ -19,6 +19,7 @@ typedef enum { EE_RELAY_STATE_OPEN = 0, EE_RELAY_STATE_CLOSE, + EE_RELAY_STATE_FAULT, NUM_EE_RELAY_STATES, } EERelayState; @@ -26,8 +27,8 @@ typedef enum { // Signals for analog inputs received at the steering board typedef enum SteeringInfoAnalog { EE_STEERING_LIGHTS_OFF_STATE = 0, // Turn lights off - EE_STEERING_LIGHTS_RIGHT_STATE, // Right Turn Signal EE_STEERING_LIGHTS_LEFT_STATE, // Left Turn Signal + EE_STEERING_LIGHTS_RIGHT_STATE, // Right Turn Signal NUM_EE_STEERING_LIGHTS, } SteeringInfoAnalog; diff --git a/libraries/ms-common/inc/x86/pwm_mcu.h b/libraries/ms-common/inc/x86/pwm_mcu.h index 2308f7aa1..6bd8db4f4 100644 --- a/libraries/ms-common/inc/x86/pwm_mcu.h +++ b/libraries/ms-common/inc/x86/pwm_mcu.h @@ -8,6 +8,7 @@ typedef enum { PWM_TIMER_1 = 0, // PWM_TIMER_2, // Requisitioned to back the soft_timer module. PWM_TIMER_3, + PWM_TIMER_4, // Usually used by i2c PWM_TIMER_14, PWM_TIMER_15, PWM_TIMER_16, diff --git a/libraries/ms-common/src/arm/pwm.c b/libraries/ms-common/src/arm/pwm.c index f842a2809..cb126bf91 100644 --- a/libraries/ms-common/src/arm/pwm.c +++ b/libraries/ms-common/src/arm/pwm.c @@ -16,6 +16,7 @@ typedef enum APBClk { static uint16_t s_period_us[NUM_PWM_TIMERS] = { [PWM_TIMER_1] = 0, // [PWM_TIMER_3] = 0, // + [PWM_TIMER_4] = 0, // This timer is typically used by I2C [PWM_TIMER_14] = 0, // [PWM_TIMER_15] = 0, // [PWM_TIMER_16] = 0, // @@ -25,6 +26,7 @@ static uint16_t s_period_us[NUM_PWM_TIMERS] = { static TIM_TypeDef *s_timer_def[NUM_PWM_TIMERS] = { [PWM_TIMER_1] = TIM1, // [PWM_TIMER_3] = TIM3, // + [PWM_TIMER_4] = TIM4, // Typically used for I2C [PWM_TIMER_14] = TIM14, // [PWM_TIMER_15] = TIM15, // [PWM_TIMER_16] = TIM16, // @@ -39,6 +41,9 @@ static APBClk prv_enable_periph_clock(PwmTimer timer) { case PWM_TIMER_3: RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); return APB1; + case PWM_TIMER_4: + RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); + return APB1; case PWM_TIMER_14: RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14, ENABLE); return APB1; diff --git a/libraries/ms-drivers/inc/ltc_afe.h b/libraries/ms-drivers/inc/ltc_afe.h index 2c634505a..5047b6d61 100644 --- a/libraries/ms-drivers/inc/ltc_afe.h +++ b/libraries/ms-drivers/inc/ltc_afe.h @@ -72,13 +72,11 @@ typedef struct LtcAfeSettings { } LtcAfeSettings; typedef struct LtcAfeStorage { - Fsm fsm; - // Only used for storage in the FSM so we store data for the correct cells uint16_t aux_index; uint16_t retry_count; uint16_t device_cell; - uint16_t time_elapsed; + uint32_t timer_start; uint16_t cell_voltages[LTC_AFE_MAX_CELLS]; uint16_t aux_voltages[LTC_AFE_MAX_THERMISTORS]; diff --git a/libraries/ms-drivers/inc/max17261_fuel_gauge.h b/libraries/ms-drivers/inc/max17261_fuel_gauge.h index 1913b6c0b..51198ba5d 100644 --- a/libraries/ms-drivers/inc/max17261_fuel_gauge.h +++ b/libraries/ms-drivers/inc/max17261_fuel_gauge.h @@ -27,7 +27,7 @@ typedef struct { uint16_t charge_term_current; // LSB = 1.5625 (micro Volts / R Sense) uint16_t i_thresh_max; - uint16_t i_thresh_min; + int16_t i_thresh_min; uint16_t temp_thresh_max; float r_sense_mohms; // Rsense in micro ohms @@ -77,7 +77,7 @@ StatusCode max17261_time_to_full(Max17261Storage *storage, uint16_t *ttf_ms); * @param soc_pct - current in amps returned in this var * @return STATUS_CODE_OK on success */ -StatusCode max17261_current(Max17261Storage *storage, uint16_t *current_a); +StatusCode max17261_current(Max17261Storage *storage, int16_t *current_a); /* @brief Gets a single cell's voltage in mV * @param storage - a pointer to an already initialized Max17261Storage struct diff --git a/libraries/ms-drivers/src/ltc_afe_impl.c b/libraries/ms-drivers/src/ltc_afe_impl.c index 3023303ac..649595c2a 100644 --- a/libraries/ms-drivers/src/ltc_afe_impl.c +++ b/libraries/ms-drivers/src/ltc_afe_impl.c @@ -75,8 +75,8 @@ static StatusCode prv_read_register(LtcAfeStorage *afe, LtcAfeRegister reg, uint } // read from a voltage register -static StatusCode prv_read_voltage(LtcAfeStorage *afe, LtcAfeVoltageRegister reg, - LtcAfeVoltageRegisterGroup *data) { +static inline StatusCode prv_read_voltage(LtcAfeStorage *afe, LtcAfeVoltageRegister reg, + LtcAfeVoltageRegisterGroup *data) { if (reg > NUM_LTC_AFE_VOLTAGE_REGISTERS) { return status_code(STATUS_CODE_INVALID_ARGS); } @@ -86,7 +86,7 @@ static StatusCode prv_read_voltage(LtcAfeStorage *afe, LtcAfeVoltageRegister reg } // start cell voltage conversion -static StatusCode prv_trigger_adc_conversion(LtcAfeStorage *afe) { +static inline StatusCode prv_trigger_adc_conversion(LtcAfeStorage *afe) { LtcAfeSettings *settings = &afe->settings; uint8_t mode = (uint8_t)((settings->adc_mode + 1) % 3); // ADCV command @@ -264,7 +264,7 @@ StatusCode ltc_afe_impl_trigger_aux_conv(LtcAfeStorage *afe, uint8_t device_cell return prv_trigger_aux_adc_conversion(afe); } -StatusCode ltc_afe_impl_read_cells(LtcAfeStorage *afe) { +inline StatusCode ltc_afe_impl_read_cells(LtcAfeStorage *afe) { // Read all voltage A, then B, ... LtcAfeSettings *settings = &afe->settings; for (uint8_t cell_reg = 0; cell_reg < NUM_LTC_AFE_VOLTAGE_REGISTERS; ++cell_reg) { diff --git a/libraries/ms-drivers/src/max17261_fuel_gauge.c b/libraries/ms-drivers/src/max17261_fuel_gauge.c index 103bd76b7..e4b049f90 100644 --- a/libraries/ms-drivers/src/max17261_fuel_gauge.c +++ b/libraries/ms-drivers/src/max17261_fuel_gauge.c @@ -1,5 +1,7 @@ #include "max17261_fuel_gauge.h" +#include "log.h" + // See Table 3 on pg.18 of the datasheet #define PCT_LSB (1.0f / 256) // LSBit is 1/256% #define CAP_LSB (5.0f / storage->settings->r_sense_mohms) // LSBit is 5 micro Volt hrs / Rsense @@ -67,10 +69,10 @@ StatusCode max17261_time_to_full(Max17261Storage *storage, uint16_t *ttf_ms) { return STATUS_CODE_OK; } -StatusCode max17261_current(Max17261Storage *storage, uint16_t *current_ua) { +StatusCode max17261_current(Max17261Storage *storage, int16_t *current_ua) { uint16_t current_reg_val = 0; status_ok_or_return(max17261_get_reg(storage, MAX17261_CURRENT, ¤t_reg_val)); - *current_ua = current_reg_val * CUR_LSB; + *current_ua = (int16_t)(current_reg_val)*CUR_LSB; return STATUS_CODE_OK; } @@ -84,7 +86,7 @@ StatusCode max17261_voltage(Max17261Storage *storage, uint16_t *vcell_mv) { StatusCode max17261_temp(Max17261Storage *storage, uint16_t *temp_c) { uint16_t temp_reg_val = 0; status_ok_or_return(max17261_get_reg(storage, MAX17261_TEMP, &temp_reg_val)); - *temp_c = temp_reg_val * temp_reg_val; + *temp_c = temp_reg_val * TEMP_LSB; return STATUS_CODE_OK; } @@ -100,6 +102,10 @@ StatusCode max17261_init(Max17261Storage *storage, Max17261Settings *settings) { uint16_t config = 0; status_ok_or_return(max17261_get_reg(storage, MAX17261_CONFIG, &config)); config |= (1 << 2); + // Enable external temp readings + // config |= (1 << 15); + // config |= (1 << 8); + // config |= (1 << 4); status_ok_or_return(max17261_set_reg(storage, MAX17261_CONFIG, config)); // Upper byte is IMAX and lower byte is IMIN uint16_t current_th = (settings->i_thresh_max << 8) & (settings->i_thresh_min & 0x00FF); diff --git a/projects/bms_carrier/config.json b/projects/bms_carrier/config.json index 57bd3ced3..4b551820f 100644 --- a/projects/bms_carrier/config.json +++ b/projects/bms_carrier/config.json @@ -2,7 +2,7 @@ "libs": [ "FreeRTOS", "ms-common", - "master", + "master", "ms-drivers" ], "can": true diff --git a/projects/bms_carrier/inc/aux_sense.h b/projects/bms_carrier/inc/aux_sense.h new file mode 100644 index 000000000..f6ddbadaf --- /dev/null +++ b/projects/bms_carrier/inc/aux_sense.h @@ -0,0 +1,17 @@ +#pragma once + +#include "adc.h" +#include "bms_carrier_getters.h" +#include "bms_carrier_setters.h" +#include "gpio.h" + +#define R2_OHMS 10000 +#define R1_OHMS 47000 + +typedef struct AuxStorage { + uint16_t aux_battery_voltage; +} AuxStorage; + +StatusCode aux_sense_init(AuxStorage *aux_storage); + +StatusCode aux_sense_run(void); diff --git a/projects/bms_carrier/inc/bms.h b/projects/bms_carrier/inc/bms.h index cf50066cc..cad0eb1da 100644 --- a/projects/bms_carrier/inc/bms.h +++ b/projects/bms_carrier/inc/bms.h @@ -2,34 +2,44 @@ #include -#include "cell_sense.h" -#include "current_sense.h" +#include "aux_sense.h" +#include "fault_bps.h" #include "i2c.h" +#include "ltc_afe.h" +#include "max17261_fuel_gauge.h" #include "status.h" #define BMS_PERIPH_I2C_PORT I2C_PORT_2 #define BMS_PERIPH_I2C_SDA_PIN \ - { GPIO_PORT_B, 11 } + { .port = GPIO_PORT_B, .pin = 11 } #define BMS_PERIPH_I2C_SCL_PIN \ - { GPIO_PORT_B, 10 } -#define BMS_FAN_ALERT_PIN \ - { GPIO_PORT_A, 9 } + { .port = GPIO_PORT_B, .pin = 10 } -#define BMS_IO_EXPANDER_I2C_ADDR 0x40 +typedef struct CurrentStorage { + uint16_t soc; + int16_t current; + uint16_t voltage; + uint16_t temperature; + uint32_t fuel_guage_cycle_ms; // Time in ms between conversions (soft timer kicks) +} CurrentStorage; -#define BMS_FAN_CTRL_1_I2C_ADDR 0x5E -#define BMS_FAN_CTRL_2_I2C_ADDR 0x5F -#define NUM_BMS_FAN_CTRLS 2 - -// Not dealing with debouncer here typedef struct BmsStorage { - // RelayStorage relay_storage; + AuxStorage aux_storage; CurrentStorage current_storage; - AfeReadings afe_readings; LtcAfeStorage ltc_afe_storage; - CellSenseStorage cell_storage; - // FanStorage fan_storage_1; - // FanStorage fan_storage_2; - // DebouncerStorage killswitch_storage; - // BpsStorage bps_storage; + BpsStorage bps_storage; + Max17261Settings fuel_guage_settings; + Max17261Storage fuel_guage_storage; } BmsStorage; + +typedef enum { + BMS_FAULT_OVERVOLTAGE, + BMS_FAULT_UNBALANCE, + BMS_FAULT_OVERTEMP_AMBIENT, + BMS_FAULT_COMMS_LOSS_AFE, + BMS_FAULT_COMMS_LOSS_CURR_SENSE, + BMS_FAULT_OVERTEMP_CELL, + BMS_FAULT_OVERCURRENT, + BMS_FAULT_UNDERVOLTAGE, + BMS_FAULT_KILLSWITCH +} BmsFault; diff --git a/projects/bms_carrier/inc/cell_sense.h b/projects/bms_carrier/inc/cell_sense.h index ead8cf1d2..19a8258dd 100644 --- a/projects/bms_carrier/inc/cell_sense.h +++ b/projects/bms_carrier/inc/cell_sense.h @@ -1,4 +1,3 @@ -// Individual AFE FSM module. Initialize it and then it should continuously run by itself #pragma once #include @@ -6,94 +5,49 @@ #include #include -#include "fsm.h" +#include "bms.h" +#include "delay.h" +#include "fault_bps.h" #include "gpio.h" #include "ltc_afe.h" +#include "ltc_afe_impl.h" #include "spi.h" -#include "status.h" -#define LTC_AFE_FSM_CELL_CONV_DELAY_MS 10 -#define LTC_AFE_FSM_AUX_CONV_DELAY_MS 6 +#define CONV_DELAY_MS 10 // Maximum number of retry attempts to read cell/aux data once triggered -#define LTC_AFE_FSM_MAX_RETRY_COUNT 3 - -#define NUM_LTC_AFE_FSM_STATES 6 -#define NUM_LTC_AFE_FSM_TRANSITIONS 14 +#define RETRY_DELAY_MS 3 +#define CELL_SENSE_CONVERSIONS 0 #define NUM_AFES 3 -#define NUM_CELL_MODULES_PER_AFE 6 +#define NUM_CELL_MODULES_PER_AFE 12 #define NUM_TOTAL_CELLS (NUM_AFES * NUM_CELL_MODULES_PER_AFE) #define NUM_THERMISTORS (NUM_TOTAL_CELLS * 2) -#define MAX_AFE_FAULTS 5 -#define AFE_SPI_PORT SPI_PORT_1 -#define AFE_SPI_SS \ - { .port = GPIO_PORT_A, .pin = 4 } +// Fault thresholds +// TODO: Verify these values! +#define CELL_OVERVOLTAGE 42500 +#define CELL_UNDERVOLTAGE 25000 +#define CELL_UNBALANCED 5000 +#define CELL_MAX_TEMPERATURE 60 + +#define AFE_BALANCING_UPPER_THRESHOLD 41500 +#define AFE_BALANCING_LOWER_THRESHOLD 40000 + +#define AFE_SPI_PORT SPI_PORT_2 +#define AFE_SPI_CS \ + { .port = GPIO_PORT_B, .pin = 12 } #define AFE_SPI_SCK \ - { .port = GPIO_PORT_A, .pin = 5 } + { .port = GPIO_PORT_B, .pin = 13 } #define AFE_SPI_MISO \ - { .port = GPIO_PORT_A, .pin = 6 } + { .port = GPIO_PORT_B, .pin = 14 } #define AFE_SPI_MOSI \ - { .port = GPIO_PORT_A, .pin = 7 } - -// Wraps the LTC AFE module and handles all the sequencing. -// Requires LTC AFE, soft timers to be initialized. -// - -DECLARE_FSM(ltc_afe_fsm); - -typedef enum LtcAfeFsmStateId { - LTC_AFE_IDLE = 0, - LTC_AFE_TRIGGER_CELL_CONV, - LTC_AFE_READ_CELLS, - LTC_AFE_TRIGGER_AUX_CONV, - LTC_AFE_READ_AUX, - LTC_AFE_AUX_COMPLETE, - LTC_AFE_FAULT -} LtcAfeFsmStateId; + { .port = GPIO_PORT_B, .pin = 15 } -// We can raise a fault using this when transitioning to LTC_AFE_FAULT to identify where it came -// from -typedef enum { - LTC_AFE_FSM_FAULT_TRIGGER_CELL_CONV = 0, - LTC_AFE_FSM_FAULT_READ_ALL_CELLS, - LTC_AFE_FSM_FAULT_TRIGGER_AUX_CONV, - LTC_AFE_FSM_FAULT_READ_AUX, - NUM_LTC_AFE_FSM_FAULTS -} LtcAfeFsmFault; - -typedef struct CellSenseSettings { - // Units are 100 uV (or DeciMilliVolts) - uint16_t undervoltage_dmv; - uint16_t overvoltage_dmv; - uint16_t charge_overtemp_dmv; - uint16_t discharge_overtemp_dmv; -} CellSenseSettings; - -typedef struct AfeReadings { - // TODO(SOFT-9): total_voltage used to be stored here as well - uint16_t voltages[NUM_TOTAL_CELLS]; - uint16_t temps[NUM_THERMISTORS]; -} AfeReadings; - -typedef struct CellSenseStorage { - LtcAfeStorage *afe; - AfeReadings *readings; - uint16_t num_afe_faults; - CellSenseSettings settings; -} CellSenseStorage; - -// First initialize the cell_sense module. -// Since it is the only module using the LTC6811, we can also initialize that and the corresponding -// FSM. Initialize the LTC6811. |settings.cell_bitset| and |settings.aux_bitset| should be an array -// of bitsets where bits 0 to 11 represent whether we should monitor the cell input for the given -// device. prv_extract_cell_result and prv_extract_aux_result will be called when the -// corresponding conversion is completed. - -StatusCode cell_sense_init(const CellSenseSettings *settings, AfeReadings *readings, - LtcAfeStorage *afe, LtcAfeSettings *ltc_settings); +StatusCode cell_sense_init(LtcAfeStorage *afe_storage); // Mark cell for discharging (takes effect after config is re-written) // |cell| should be [0, settings.num_cells) -StatusCode ltc_afe_toggle_cell_discharge(LtcAfeStorage *afe, uint16_t cell, bool discharge); +StatusCode cell_conversions(void); + +StatusCode cell_sense_run(void); diff --git a/projects/bms_carrier/inc/current_sense.h b/projects/bms_carrier/inc/current_sense.h index b34dca970..64c6a486e 100644 --- a/projects/bms_carrier/inc/current_sense.h +++ b/projects/bms_carrier/inc/current_sense.h @@ -6,35 +6,39 @@ #include #include +#include "bms.h" #include "i2c.h" #include "max17261_fuel_gauge.h" +#include "pwm.h" #include "spi.h" #include "status.h" #include "tasks.h" -#define NUM_STORED_CURRENT_READINGS 20 -#define CURRENT_SENSE_SPI_PORT SPI_PORT_2 +#define MAX17261_I2C_PORT (I2C_PORT_2) +#define MAX17261_I2C_ADDR (0x36) -// slightly larger than conversion time of adc -#define CONVERSION_TIME_MS 18 +// TODO (Adel C): Change these values to their actual values +#define CURRENT_SENSE_R_SENSE_MILLI_OHMS (0.5) +#define MAIN_PACK_DESIGN_CAPACITY \ + (1.0f / CURRENT_SENSE_R_SENSE_MILLI_OHMS) // LSB = 5.0 (micro Volt Hours / R Sense) +#define MAIN_PACK_EMPTY_VOLTAGE (1.0f / 78.125) // Only a 9-bit field, LSB = 78.125 (micro Volts) +#define CHARGE_TERMINATION_CURRENT (1.0f / (1.5625f / CURRENT_SENSE_R_SENSE_MILLI_OHMS)) -// see current sense on confluence for these values (centiamps) -#define DISCHARGE_OVERCURRENT_CA (13000) // 130 Amps -#define CHARGE_OVERCURRENT_CA (-8160) // -81.6 Amp +// Thresholds for ALRT Pin +#define CURRENT_SENSE_MAX_CURRENT_A (58.2f) // 58.2 Amps +#define CURRENT_SENSE_MIN_CURRENT_A (27.0f) // Actually -27 +#define CURRENT_SENSE_MAX_TEMP (60U) +#define CURRENT_SENSE_MAX_VOLTAGE (150U) +#define ALRT_PIN_V_RES_MICRO_V (400) // Enum for GPIO IT alerts (just the one pin) -typedef enum { CURRENT_SENSE_RUN_CYCLE = 0, ALRT_GPIO_IT } CurrentSenseNotification; +typedef enum { CURRENT_SENSE_RUN_CYCLE = 0, ALRT_GPIO_IT, KILLSWITCH_IT } CurrentSenseNotification; -typedef struct CurrentStorage { - int16_t readings_ring[NUM_STORED_CURRENT_READINGS]; - uint16_t ring_idx; - int16_t average; - uint32_t fuel_guage_cycle_ms; // Time in ms between conversions (soft timer kicks) -} CurrentStorage; +StatusCode current_sense_fault_check(); -StatusCode run_current_sense_cycle(); +StatusCode current_sense_run(); bool current_sense_is_charging(); -StatusCode current_sense_init(CurrentStorage *storage, I2CSettings *i2c_settings, +StatusCode current_sense_init(BmsStorage *bms_storage, I2CSettings *i2c_settings, uint32_t fuel_guage_cycle_ms); diff --git a/projects/bms_carrier/inc/fan.h b/projects/bms_carrier/inc/fan.h new file mode 100644 index 000000000..438cb26da --- /dev/null +++ b/projects/bms_carrier/inc/fan.h @@ -0,0 +1,16 @@ +#pragma once + +#include "adc.h" +#include "bms.h" +#include "bms_carrier_getters.h" +#include "bms_carrier_setters.h" +#include "gpio.h" +#include "log.h" +#include "pwm.h" + +#define BMS_FAN_PERIOD 40 // Period in ms. Frequency = 25,000 Hz +#define BMS_FAN_TEMP_UPPER_THRESHOLD 55 // Threshold before fan is full strength +#define BMS_FAN_TEMP_LOWER_THRESHOLD 40 // Threshold before fan is full strength + +void bms_run_fan(void); +void bms_fan_init(BmsStorage *bms_storage); diff --git a/projects/bms_carrier/inc/fault_bps.h b/projects/bms_carrier/inc/fault_bps.h index 2bfd1c690..1dbd2409e 100644 --- a/projects/bms_carrier/inc/fault_bps.h +++ b/projects/bms_carrier/inc/fault_bps.h @@ -1,9 +1,14 @@ #pragma once -#include "bms.h" +#include "bms_carrier_getters.h" +#include "bms_carrier_setters.h" #include "status.h" -StatusCode fault_bps_init(BmsStorage *storage); +typedef struct BpsStorage { + uint16_t fault_bitset; +} BpsStorage; + +StatusCode fault_bps_init(BpsStorage *storage); StatusCode fault_bps_set(uint8_t fault_bitmask); diff --git a/projects/bms_carrier/inc/relays_fsm.h b/projects/bms_carrier/inc/relays_fsm.h new file mode 100644 index 000000000..b59d7b549 --- /dev/null +++ b/projects/bms_carrier/inc/relays_fsm.h @@ -0,0 +1,23 @@ +#pragma once + +#include "bms.h" +#include "bms_carrier_getters.h" +#include "bms_carrier_setters.h" +#include "can.h" +#include "delay.h" +#include "exported_enums.h" +#include "fsm.h" +#include "gpio.h" +#include "log.h" +#include "soft_timer.h" +#include "status.h" +#include "task.h" + +#define NUM_RELAY_STATES 3 +#define BMS_CLOSE_RELAYS_DELAY 200 + +DECLARE_FSM(bms_relays); +typedef enum RelaysStateId { RELAYS_OPEN = 0, RELAYS_CLOSED, RELAYS_FAULT } RelaysStateId; +typedef enum RelayType { NO_RELAYS = 0, POS_RELAY, NEG_RELAY, SOLAR_RELAY } RelayType; + +StatusCode init_bms_relays(BmsStorage *bms_storage); diff --git a/projects/bms_carrier/src/aux_sense.c b/projects/bms_carrier/src/aux_sense.c new file mode 100644 index 000000000..cc8689159 --- /dev/null +++ b/projects/bms_carrier/src/aux_sense.c @@ -0,0 +1,23 @@ +#include "aux_sense.h" + +static const GpioAddress aux_sense_pin = { .port = GPIO_PORT_A, .pin = 5 }; +AuxStorage *s_aux_storage; + +StatusCode aux_sense_init(AuxStorage *aux_storage) { + s_aux_storage = aux_storage; + gpio_init_pin(&aux_sense_pin, GPIO_ANALOG, GPIO_STATE_LOW); + adc_add_channel(aux_sense_pin); + adc_init(); + + return STATUS_CODE_OK; +} + +StatusCode aux_sense_run() { + adc_run(); + adc_read_converted(aux_sense_pin, &s_aux_storage->aux_battery_voltage); + s_aux_storage->aux_battery_voltage = (s_aux_storage->aux_battery_voltage) * (R2_OHMS + R1_OHMS) / + (R2_OHMS * 100); // CONV VOLTAGE; + LOG_DEBUG("AUX READING: %d\n", s_aux_storage->aux_battery_voltage); + set_battery_status_aux_batt_v(s_aux_storage->aux_battery_voltage); + return STATUS_CODE_OK; +} diff --git a/projects/bms_carrier/src/cell_sense.c b/projects/bms_carrier/src/cell_sense.c new file mode 100644 index 000000000..81d7fcb30 --- /dev/null +++ b/projects/bms_carrier/src/cell_sense.c @@ -0,0 +1,172 @@ +#include "cell_sense.h" + +LtcAfeStorage *ltc_afe_storage; + +static const LtcAfeSettings s_afe_settings = { + .mosi = AFE_SPI_MOSI, + .miso = AFE_SPI_MISO, + .sclk = AFE_SPI_SCK, + .cs = AFE_SPI_CS, + + .spi_port = AFE_SPI_PORT, + .spi_baudrate = 750000, + + .adc_mode = LTC_AFE_ADC_MODE_7KHZ, + + .cell_bitset = { 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF }, + .aux_bitset = { 0 }, + + .num_devices = 1, + .num_cells = 12, + .num_thermistors = 12, +}; + +static inline StatusCode prv_cell_sense_conversions() { + StatusCode status = STATUS_CODE_OK; + // TODO: Figure out why cell_conv cannot happen without spi timing out (Most likely RTOS + // implemntation error) Retry Mechanism + if (ltc_afe_impl_trigger_cell_conv(ltc_afe_storage)) { + // If this has failed, try once more after a short delay + delay_ms(RETRY_DELAY_MS); + status |= ltc_afe_impl_trigger_cell_conv(ltc_afe_storage); + } + delay_ms(CONV_DELAY_MS); + + if (status != STATUS_CODE_OK) { + LOG_DEBUG("CELL_SENSE (conv failed): %d\n", status); + fault_bps_set(BMS_FAULT_COMMS_LOSS_AFE); + return status; + } + + status |= ltc_afe_impl_read_cells(ltc_afe_storage); + + if (status != STATUS_CODE_OK) { + LOG_DEBUG("READ FAILED\n"); + fault_bps_set(BMS_FAULT_COMMS_LOSS_AFE); + return status; + } + + // for (size_t i = 0; i < 10; i++) { + // status |= ltc_afe_impl_trigger_aux_conv(ltc_afe_storage, i); + // if (status != STATUS_CODE_OK) { + // fault_bps_set(BMS_FAULT_COMMS_LOSS_AFE); + // return status; + // } + // delay_ms(CONV_DELAY_MS); + // } + return status; +} + +// Task bc delays +TASK(cell_sense_conversions, TASK_STACK_256) { + while (true) { + notify_wait(NULL, BLOCK_INDEFINITELY); + + // run conversions every 10 seconds + // if (xTaskGetTickCount() - ltc_afe_storage->timer_start >= pdMS_TO_TICKS(10000)) { + prv_cell_sense_conversions(); + // } + + send_task_end(); + } +} + +StatusCode cell_conversions() { + StatusCode ret = notify(cell_sense_conversions, CELL_SENSE_CONVERSIONS); + if (ret != STATUS_CODE_OK) { + LOG_DEBUG("NOTIFY FAILED\n"); + fault_bps_set(BMS_FAULT_COMMS_LOSS_AFE); + return STATUS_CODE_INTERNAL_ERROR; + } + return STATUS_CODE_OK; +} + +StatusCode cell_sense_run() { + StatusCode status = STATUS_CODE_OK; + uint16_t max_voltage = 0; + uint16_t min_voltage = 0xffff; + + for (size_t cell = 0; cell < (s_afe_settings.num_devices * s_afe_settings.num_cells); cell++) { + // LOG_DEBUG("CELL %d: %d\n\r", cell, + // ltc_afe_storage->cell_voltages[ltc_afe_storage->cell_result_lookup[cell]]); + max_voltage = + ltc_afe_storage->cell_voltages[ltc_afe_storage->cell_result_lookup[cell]] > max_voltage + ? ltc_afe_storage->cell_voltages[ltc_afe_storage->cell_result_lookup[cell]] + : max_voltage; + min_voltage = + ltc_afe_storage->cell_voltages[ltc_afe_storage->cell_result_lookup[cell]] < min_voltage + ? ltc_afe_storage->cell_voltages[ltc_afe_storage->cell_result_lookup[cell]] + : min_voltage; + delay_ms(1); + } + LOG_DEBUG("MAX VOLTAGE: %d\n", max_voltage); + LOG_DEBUG("MIN VOLTAGE: %d\n", min_voltage); + set_battery_info_max_cell_v(max_voltage); + + if (max_voltage >= CELL_OVERVOLTAGE) { + LOG_DEBUG("OVERVOLTAGE\n"); + fault_bps_set(BMS_FAULT_OVERVOLTAGE); + return STATUS_CODE_INTERNAL_ERROR; + } + if (min_voltage <= CELL_UNDERVOLTAGE) { + LOG_DEBUG("UNDERVOLTAGE\n"); + fault_bps_set(BMS_FAULT_UNDERVOLTAGE); + return STATUS_CODE_INTERNAL_ERROR; + } + if (max_voltage - min_voltage >= CELL_UNBALANCED) { + LOG_DEBUG("UNBALANCED\n"); + fault_bps_set(BMS_FAULT_UNBALANCE); + return STATUS_CODE_INTERNAL_ERROR; + } + + delay_ms(1); + if (min_voltage >= AFE_BALANCING_UPPER_THRESHOLD) { + min_voltage += 20; + } else if (min_voltage < AFE_BALANCING_UPPER_THRESHOLD && + min_voltage >= AFE_BALANCING_LOWER_THRESHOLD) { + min_voltage += 100; + } else { + min_voltage += 250; + } + + // if (xTaskGetTickCount() - ltc_afe_storage->timer_start >= 10000) { + ltc_afe_storage->timer_start = xTaskGetTickCount(); + for (size_t cell = 0; cell < (s_afe_settings.num_devices * s_afe_settings.num_cells); cell++) { + if (ltc_afe_storage->cell_voltages[ltc_afe_storage->cell_result_lookup[cell]] > min_voltage) { + ltc_afe_impl_toggle_cell_discharge(ltc_afe_storage, cell, true); + // LOG_DEBUG("Cell %d unbalanced %d MIN VOLTAGE: %d\n", cell, + // ltc_afe_storage->cell_voltages[ltc_afe_storage->cell_result_lookup[cell]], min_voltage); + } else { + ltc_afe_impl_toggle_cell_discharge(ltc_afe_storage, cell, false); + } + // } + } + + // LOG_DEBUG("Config discharge bitset %d\n", ltc_afe_storage->discharge_bitset[0]); + + // for (size_t thermistor = 0; + // thermistor < (s_afe_settings.num_thermistors * s_afe_settings.num_devices); thermistor++) + // { + // ltc_afe_impl_read_aux(ltc_afe_storage, thermistor); + + // // Log thermistor result + // LOG_DEBUG("Thermistor reading: %d\n", + // ltc_afe_storage->aux_voltages[ltc_afe_storage->aux_result_lookup[thermistor]]); + + // if (ltc_afe_storage->aux_voltages[ltc_afe_storage->aux_result_lookup[thermistor]] >= + // CELL_MAX_TEMPERATURE) { + // fault_bps_set(BMS_FAULT_OVERTEMP_CELL); + // return STATUS_CODE_INTERNAL_ERROR; + // } + // } + + return status; +} + +StatusCode cell_sense_init(LtcAfeStorage *afe_storage) { + ltc_afe_storage = afe_storage; + ltc_afe_init(ltc_afe_storage, &s_afe_settings); + delay_ms(10); + tasks_init_task(cell_sense_conversions, TASK_PRIORITY(2), NULL); + return STATUS_CODE_OK; +} diff --git a/projects/bms_carrier/src/current_sense.c b/projects/bms_carrier/src/current_sense.c index aecc44f1c..5b0310faa 100644 --- a/projects/bms_carrier/src/current_sense.c +++ b/projects/bms_carrier/src/current_sense.c @@ -9,65 +9,58 @@ #include "gpio_it.h" #include "interrupt.h" #include "log.h" -#include "soft_timer.h" +#include "relays_fsm.h" #include "tasks.h" -#define MAX17261_I2C_PORT (I2C_PORT_2) -#define MAX17261_I2C_ADDR (0x36) - -// TODO (Adel C): Change these values to their actual values -#define CURRENT_SENSE_R_SENSE_MILLI_OHMS (0.5) -#define MAIN_PACK_DESIGN_CAPACITY \ - (1.0f / CURRENT_SENSE_R_SENSE_MILLI_OHMS) // LSB = 5.0 (micro Volt Hours / R Sense) -#define MAIN_PACK_EMPTY_VOLTAGE (1.0f / 78.125) // Only a 9-bit field, LSB = 78.125 (micro Volts) -#define CHARGE_TERMINATION_CURRENT (1.0f / (1.5625f / CURRENT_SENSE_R_SENSE_MILLI_OHMS)) - -// Thresholds for ALRT Pin -#define CURRENT_SENSE_MAX_CURRENT_A (58.2f) -#define CURRENT_SENSE_MIN_CURRENT_A (27.0f) // Actually -27 -#define CURRENT_SENSE_MAX_TEMP (60U) -#define ALRT_PIN_V_RES_MICRO_V (400) - static Max17261Storage s_fuel_guage_storage; static Max17261Settings s_fuel_gauge_settings; static CurrentStorage *s_current_storage; -static SoftTimer s_timer; static bool s_is_charging; // Periodically read and update the SoC of the car & update charging bool -static StatusCode prv_fuel_gauge_read() { +StatusCode prv_fuel_gauge_read() { StatusCode status = STATUS_CODE_OK; - uint16_t soc = 0; - uint16_t current = 0; - uint16_t voltage = 0; - uint16_t temperature = 0; + status |= max17261_state_of_charge(&s_fuel_guage_storage, &s_current_storage->soc); + status |= max17261_current(&s_fuel_guage_storage, &s_current_storage->current); + status |= max17261_voltage(&s_fuel_guage_storage, &s_current_storage->voltage); + status |= max17261_temp(&s_fuel_guage_storage, &s_current_storage->temperature); - status |= max17261_state_of_charge(&s_fuel_guage_storage, &soc); - status |= max17261_current(&s_fuel_guage_storage, ¤t); - status |= max17261_voltage(&s_fuel_guage_storage, &voltage); - status |= max17261_temp(&s_fuel_guage_storage, &temperature); + LOG_DEBUG("SOC: %d\n", s_current_storage->soc); + LOG_DEBUG("CURRENT: %d\n", s_current_storage->current); + LOG_DEBUG("VOLTAGE: %d\n", s_current_storage->voltage); + LOG_DEBUG("TEMP: %d\n", s_current_storage->temperature); if (status != STATUS_CODE_OK) { // TODO (Adel): Handle a fuel gauge fault // Open Relays + LOG_DEBUG("Status error: %d\n", status); + fault_bps_set(BMS_FAULT_COMMS_LOSS_CURR_SENSE); return status; } // Set Battery VT message signals - set_battery_vt_batt_perc(soc); - set_battery_vt_current(current); - set_battery_vt_voltage(voltage); - set_battery_vt_temperature(temperature); - - // update s_is_charging - // note that a negative value indicates the battery is charging - s_is_charging = s_current_storage->average < 0; + set_battery_vt_batt_perc(s_current_storage->soc); + set_battery_vt_current((uint16_t)s_current_storage->current); + set_battery_vt_voltage(s_current_storage->voltage); + set_battery_vt_temperature(s_current_storage->temperature); + + // TODO (Aryan): Validate these checks + if (s_current_storage->current >= CURRENT_SENSE_MAX_CURRENT_A * 1000) { + fault_bps_set(BMS_FAULT_OVERCURRENT); + return STATUS_CODE_INTERNAL_ERROR; + } else if (s_current_storage->voltage >= CURRENT_SENSE_MAX_VOLTAGE) { + fault_bps_set(BMS_FAULT_OVERVOLTAGE); + return STATUS_CODE_INTERNAL_ERROR; + } else if (s_current_storage->temperature >= CURRENT_SENSE_MAX_TEMP) { + fault_bps_set(BMS_FAULT_OVERTEMP_AMBIENT); + return STATUS_CODE_INTERNAL_ERROR; + } return status; } -TASK(current_sense, TASK_MIN_STACK_SIZE) { +TASK(current_sense, TASK_STACK_256) { while (true) { uint32_t notification = 0; notify_wait(¬ification, BLOCK_INDEFINITELY); @@ -75,7 +68,9 @@ TASK(current_sense, TASK_MIN_STACK_SIZE) { // Handle alert from fuel gauge if (notification & (1 << ALRT_GPIO_IT)) { - // TODO (Adel): BMS Open Relays + // fault_bps_set(BMS_FAULT_COMMS_LOSS_CURR_SENSE); + } else if (notification & 1 << KILLSWITCH_IT) { + fault_bps_set(BMS_FAULT_KILLSWITCH); } prv_fuel_gauge_read(); @@ -83,39 +78,37 @@ TASK(current_sense, TASK_MIN_STACK_SIZE) { } } -bool current_sense_is_charging() { - return s_is_charging; -} - -StatusCode run_current_sense_cycle() { +StatusCode current_sense_run() { StatusCode ret = notify(current_sense, CURRENT_SENSE_RUN_CYCLE); - if (ret == pdFALSE) { + if (ret != STATUS_CODE_OK) { + fault_bps_set(BMS_FAULT_COMMS_LOSS_CURR_SENSE); return STATUS_CODE_INTERNAL_ERROR; } return STATUS_CODE_OK; } -StatusCode current_sense_init(CurrentStorage *storage, I2CSettings *i2c_settings, +StatusCode current_sense_init(BmsStorage *bms_storage, I2CSettings *i2c_settings, uint32_t fuel_guage_cycle_ms) { - interrupt_init(); - gpio_it_init(); - i2c_init(I2C_PORT_1, i2c_settings); + s_current_storage = &bms_storage->current_storage; + s_fuel_gauge_settings = bms_storage->fuel_guage_settings; + s_fuel_guage_storage = bms_storage->fuel_guage_storage; + i2c_init(I2C_PORT_2, i2c_settings); - GpioAddress alrt_pin = { .port = GPIO_PORT_B, .pin = 1 }; + GpioAddress alrt_pin = { .port = GPIO_PORT_A, .pin = 7 }; + GpioAddress kill_switch_mntr = { .port = GPIO_PORT_A, .pin = 15 }; InterruptSettings it_settings = { .priority = INTERRUPT_PRIORITY_NORMAL, .type = INTERRUPT_TYPE_INTERRUPT, .edge = INTERRUPT_EDGE_RISING, }; - + gpio_init_pin(&kill_switch_mntr, GPIO_INPUT_FLOATING, GPIO_STATE_LOW); gpio_it_register_interrupt(&alrt_pin, &it_settings, ALRT_GPIO_IT, current_sense); + it_settings.edge = INTERRUPT_EDGE_FALLING; + gpio_it_register_interrupt(&kill_switch_mntr, &it_settings, KILLSWITCH_IT, current_sense); - memset(storage, 0, sizeof(CurrentStorage)); - s_current_storage = storage; - - s_fuel_gauge_settings.i2c_port = MAX17261_I2C_PORT; + s_fuel_gauge_settings.i2c_port = I2C_PORT_2; s_fuel_gauge_settings.i2c_address = MAX17261_I2C_ADDR; s_fuel_gauge_settings.charge_term_current = CHARGE_TERMINATION_CURRENT; @@ -137,6 +130,6 @@ StatusCode current_sense_init(CurrentStorage *storage, I2CSettings *i2c_settings s_current_storage->fuel_guage_cycle_ms = fuel_guage_cycle_ms; status_ok_or_return(max17261_init(&s_fuel_guage_storage, &s_fuel_gauge_settings)); - tasks_init_task(current_sense, TASK_PRIORITY(3), NULL); + tasks_init_task(current_sense, TASK_PRIORITY(2), NULL); return STATUS_CODE_OK; } diff --git a/projects/bms_carrier/src/fan.c b/projects/bms_carrier/src/fan.c new file mode 100644 index 000000000..77fcf394d --- /dev/null +++ b/projects/bms_carrier/src/fan.c @@ -0,0 +1,53 @@ +#include "fan.h" + +BmsStorage *s_storage; + +const GpioAddress bms_fan_sense1 = { .port = GPIO_PORT_B, .pin = 1 }; +const GpioAddress bms_fan_sense2 = { .port = GPIO_PORT_B, .pin = 0 }; +const GpioAddress bms_fan_pwm = { .port = GPIO_PORT_A, .pin = 8 }; + +static void prv_bms_fan_sense(void) { + LOG_DEBUG("RUN FAN SENSE\n"); + TickType_t tick = xTaskGetTickCount() + pdMS_TO_TICKS(5); + GpioState state = 1; + + while (xTaskGetTickCount() < tick && state) { + // LOG_DEBUG("STATE FANSENSE_1: %d\n", state); + gpio_get_state(&bms_fan_sense1, &state); + } + set_battery_info_fan1(!state); + + tick = xTaskGetTickCount() + pdMS_TO_TICKS(5); + + while (xTaskGetTickCount() < tick && state) { + // LOG_DEBUG("STATE FANSENSE_2: %d\n", state); + gpio_get_state(&bms_fan_sense2, &state); + } + set_battery_info_fan2(!state); + // LOG_DEBUG("FAN1: %d\n", g_tx_struct.battery_status_fan1); + // LOG_DEBUG("FAN2: %d\n", g_tx_struct.battery_status_fan2); +} + +void bms_run_fan(void) { + if (s_storage->current_storage.temperature >= BMS_FAN_TEMP_UPPER_THRESHOLD) { + pwm_set_dc(PWM_TIMER_1, 100); + } else if (s_storage->current_storage.temperature <= BMS_FAN_TEMP_LOWER_THRESHOLD) { + pwm_set_dc(PWM_TIMER_1, 0); + } else { + pwm_set_dc(PWM_TIMER_1, + (100 * (s_storage->current_storage.temperature - BMS_FAN_TEMP_LOWER_THRESHOLD)) / + (BMS_FAN_TEMP_UPPER_THRESHOLD - BMS_FAN_TEMP_LOWER_THRESHOLD)); + } + prv_bms_fan_sense(); +} + +void bms_fan_init(BmsStorage *bms_storage) { + s_storage = bms_storage; + gpio_init_pin(&bms_fan_pwm, GPIO_ALTFN_PUSH_PULL, GPIO_STATE_LOW); + pwm_init(PWM_TIMER_1, BMS_FAN_PERIOD); + pwm_set_pulse(PWM_TIMER_1, BMS_FAN_PERIOD); + pwm_set_dc(PWM_TIMER_1, 0); + + gpio_init_pin(&bms_fan_sense1, GPIO_INPUT_FLOATING, GPIO_STATE_LOW); + gpio_init_pin(&bms_fan_sense2, GPIO_INPUT_FLOATING, GPIO_STATE_LOW); +} diff --git a/projects/bms_carrier/src/fault_bps.c b/projects/bms_carrier/src/fault_bps.c index 0df39363f..7e9f7f76a 100644 --- a/projects/bms_carrier/src/fault_bps.c +++ b/projects/bms_carrier/src/fault_bps.c @@ -1,27 +1,28 @@ #include "fault_bps.h" -#include "bms.h" -#include "exported_enums.h" +static BpsStorage *s_bps_storage; -static BmsStorage *s_storage; - -StatusCode fault_bps_init(BmsStorage *storage) { - s_storage = storage; +StatusCode fault_bps_init(BpsStorage *storage) { + s_bps_storage = storage; return STATUS_CODE_OK; } // TODO: These faulting mechanism will be changing substantially // Fault BPS and open relays StatusCode fault_bps_set(uint8_t fault_bitmask) { - // s_storage->bps_storage.fault_bitset |= fault_bitmask; - // if (fault_bitmask != EE_BPS_STATE_FAULT_RELAY) { - // relay_fault(&s_storage->relay_storage); - // } + s_bps_storage->fault_bitset |= (1 << fault_bitmask); + LOG_DEBUG("FAULT_BITMASK: %d\n", fault_bitmask); + if (s_bps_storage->fault_bitset & 0x7) { + s_bps_storage->fault_bitset |= (1 << 15); + } else { + s_bps_storage->fault_bitset |= (1 << 14); + } + set_battery_status_fault(fault_bitmask); return STATUS_CODE_OK; } // Clear fault from fault_bitmask StatusCode fault_bps_clear(uint8_t fault_bitmask) { - // s_storage->bps_storage.fault_bitset &= ~(fault_bitmask); + s_bps_storage->fault_bitset &= ~(1 << fault_bitmask); return STATUS_CODE_OK; } diff --git a/projects/bms_carrier/src/main.c b/projects/bms_carrier/src/main.c index 8cd370d7f..9066450e5 100644 --- a/projects/bms_carrier/src/main.c +++ b/projects/bms_carrier/src/main.c @@ -1,40 +1,83 @@ #include #include +#include "bms.h" +#include "can.h" +#include "can_board_ids.h" +#include "cell_sense.h" #include "current_sense.h" +#include "fan.h" +#include "gpio.h" #include "gpio_it.h" +#include "interrupt.h" #include "log.h" #include "master_task.h" -#include "max17261_fuel_gauge.h" +#include "relays_fsm.h" #include "tasks.h" -#define I2C2_SDA \ - { .port = GPIO_PORT_B, .pin = 11 } -#define I2C2_SCL \ - { .port = GPIO_PORT_B, .pin = 10 } - #define FUEL_GAUGE_CYCLE_TIME_MS 100 -static CurrentStorage s_currentsense_storage; +static CanStorage s_can_storage = { 0 }; + +static const CanSettings can_settings = { + .device_id = SYSTEM_CAN_DEVICE_BMS_CARRIER, + .bitrate = CAN_HW_BITRATE_500KBPS, + .tx = { GPIO_PORT_A, 12 }, + .rx = { GPIO_PORT_A, 11 }, + .loopback = false, +}; + +static const I2CSettings i2c_settings = { + .speed = I2C_SPEED_STANDARD, + .sda = BMS_PERIPH_I2C_SDA_PIN, + .scl = BMS_PERIPH_I2C_SCL_PIN, +}; -void pre_loop_init() {} +BmsStorage bms_storage; + +void pre_loop_init() { + LOG_DEBUG("Welcome to BMS \n"); + fault_bps_init(&bms_storage.bps_storage); + current_sense_init(&bms_storage, &i2c_settings, FUEL_GAUGE_CYCLE_TIME_MS); + // cell_sense_init(&bms_storage.ltc_afe_storage); + aux_sense_init(&bms_storage.aux_storage); + init_bms_relays(&bms_storage); + bms_fan_init(&bms_storage); +} void run_fast_cycle() {} void run_medium_cycle() { - run_current_sense_cycle(); + run_can_rx_cycle(); + wait_tasks(1); + + // cell_conversions(); + // wait_tasks(1); + current_sense_run(); + wait_tasks(1); + + // cell_sense_run(); + aux_sense_run(); + + fsm_run_cycle(bms_relays); + wait_tasks(1); + bms_run_fan(); + + run_can_tx_cycle(); + wait_tasks(1); } void run_slow_cycle() {} int main() { - I2CSettings i2c_settings = { .sda = I2C2_SDA, .scl = I2C2_SCL, .speed = I2C_SPEED_STANDARD }; - tasks_init(); log_init(); - LOG_DEBUG("Welcome to BMS!\n"); + gpio_init(); + interrupt_init(); + gpio_it_init(); + can_init(&s_can_storage, &can_settings); - current_sense_init(&s_currentsense_storage, &i2c_settings, FUEL_GAUGE_CYCLE_TIME_MS); + LOG_DEBUG("Welcome to BMS!\n"); init_master_task(); tasks_start(); diff --git a/projects/bms_carrier/src/relays_fsm.c b/projects/bms_carrier/src/relays_fsm.c new file mode 100644 index 000000000..a13959927 --- /dev/null +++ b/projects/bms_carrier/src/relays_fsm.c @@ -0,0 +1,124 @@ +#include "relays_fsm.h" + +FSM(bms_relays, NUM_RELAY_STATES, TASK_STACK_256); +static RelaysStateId fsm_state = RELAYS_OPEN; +static RelayType relay_toggle = NO_RELAYS; + +static BmsStorage *s_storage; +static SoftTimer relays_timer; + +static const GpioAddress pos_relay_en = { .port = GPIO_PORT_B, .pin = 8 }; +static const GpioAddress pos_relay_sense = { .port = GPIO_PORT_B, .pin = 5 }; + +static const GpioAddress neg_relay_en = { .port = GPIO_PORT_B, .pin = 4 }; +static const GpioAddress neg_relay_sense = { .port = GPIO_PORT_B, .pin = 3 }; + +static const GpioAddress solar_relay_en = { .port = GPIO_PORT_C, .pin = 13 }; +static const GpioAddress solar_relay_sense = { .port = GPIO_PORT_B, .pin = 9 }; + +static void prv_close_relays(SoftTimerId id) { + // 150 MS GAP BETWEEN EACH RELAY BC OF CURRENT DRAW + switch (relay_toggle) { + case NO_RELAYS: { + gpio_set_state(&pos_relay_en, GPIO_STATE_HIGH); + relay_toggle = POS_RELAY; + soft_timer_start(&relays_timer); + break; + } + case POS_RELAY: { + gpio_set_state(&neg_relay_en, GPIO_STATE_HIGH); + relay_toggle = NEG_RELAY; + soft_timer_start(&relays_timer); + break; + } + case NEG_RELAY: { + gpio_set_state(&solar_relay_en, GPIO_STATE_HIGH); + relay_toggle = SOLAR_RELAY; + break; + } + default: + // FAULT? SHOULD NEVER BE DEFAULT + break; + } +} + +static void prv_open_relays() { + gpio_set_state(&pos_relay_en, GPIO_STATE_LOW); + gpio_set_state(&neg_relay_en, GPIO_STATE_LOW); + gpio_set_state(&solar_relay_en, GPIO_STATE_LOW); +} + +static void prv_bms_fault_ok_or_transition(Fsm *fsm) { + LOG_DEBUG("FSM RUN CYCLE\n"); + if (s_storage->bps_storage.fault_bitset) { + LOG_DEBUG("Fault %d\n", s_storage->bps_storage.fault_bitset); + fsm_transition(fsm, RELAYS_FAULT); + } +} + +static void prv_relays_open_output(void *context) { + LOG_DEBUG("Transitioned to RELAYS_OPEN\n"); + prv_open_relays(); + set_battery_relay_info_state(EE_RELAY_STATE_OPEN); + fsm_state = RELAYS_OPEN; + relay_toggle = NO_RELAYS; +} + +static void prv_relays_open_input(Fsm *fsm, void *context) { + prv_bms_fault_ok_or_transition(fsm); + RelaysStateId relay_event = get_bms_relays_relays_state(); + if (relay_event == RELAYS_CLOSED) { + fsm_transition(fsm, RELAYS_CLOSED); + } +} + +static void prv_relays_closed_output(void *context) { + LOG_DEBUG("Transitioned to RELAYS_CLOSED\n"); + set_battery_relay_info_state(EE_RELAY_STATE_CLOSE); + fsm_state = RELAYS_CLOSED; + soft_timer_start(&relays_timer); +} + +static void prv_relays_closed_input(Fsm *fsm, void *context) { + prv_bms_fault_ok_or_transition(fsm); + GpioState state; + RelaysStateId relay_event = get_bms_relays_relays_state(); + gpio_get_state(&pos_relay_sense, &state); + if (relay_event == RELAYS_OPEN) { + fsm_transition(fsm, RELAYS_OPEN); + } +} + +static void prv_relays_fault_output(void *context) { + LOG_DEBUG("Transitioned to RELAYS_FAULT\n"); + prv_open_relays(); + set_battery_relay_info_state(EE_RELAY_STATE_FAULT); + fsm_state = RELAYS_FAULT; +} + +static void prv_relays_fault_input(Fsm *fsm, void *context) { + prv_bms_fault_ok_or_transition(fsm); +} + +// Relays FSM declaration for states and transitions +static FsmState s_relays_state_list[NUM_RELAY_STATES] = { + STATE(RELAYS_OPEN, prv_relays_open_input, prv_relays_open_output), + STATE(RELAYS_CLOSED, prv_relays_closed_input, prv_relays_closed_output), + STATE(RELAYS_FAULT, prv_relays_fault_input, prv_relays_fault_output) +}; + +static bool s_relays_transitions[NUM_RELAY_STATES][NUM_RELAY_STATES] = { + TRANSITION(RELAYS_OPEN, RELAYS_CLOSED), TRANSITION(RELAYS_OPEN, RELAYS_FAULT), + TRANSITION(RELAYS_CLOSED, RELAYS_OPEN), TRANSITION(RELAYS_CLOSED, RELAYS_FAULT), + TRANSITION(RELAYS_FAULT, RELAYS_FAULT), +}; + +StatusCode init_bms_relays(BmsStorage *bms_storage) { + s_storage = bms_storage; + gpio_init_pin(&pos_relay_en, GPIO_OUTPUT_PUSH_PULL, GPIO_STATE_LOW); + gpio_init_pin(&neg_relay_en, GPIO_OUTPUT_PUSH_PULL, GPIO_STATE_LOW); + gpio_init_pin(&solar_relay_en, GPIO_OUTPUT_PUSH_PULL, GPIO_STATE_LOW); + soft_timer_init(BMS_CLOSE_RELAYS_DELAY, prv_close_relays, &relays_timer); + fsm_init(bms_relays, s_relays_state_list, s_relays_transitions, RELAYS_OPEN, NULL); + return STATUS_CODE_OK; +} diff --git a/projects/centre_console/inc/update_dashboard.h b/projects/centre_console/inc/update_dashboard.h index 17606ddf4..d5dfb7715 100644 --- a/projects/centre_console/inc/update_dashboard.h +++ b/projects/centre_console/inc/update_dashboard.h @@ -6,6 +6,11 @@ #include "exported_enums.h" #include "status.h" +// TODO (Aryan): validate these numbers +#define MAX_VOLTAGE 42000 +#define MIN_VOLTAGE 40000 +#define MAX_CURRENT 58.2 + typedef enum MainTaskBtnEvents { POWER_BUTTON_EVENT = 0, REGEN_BUTTON_EVENT, diff --git a/projects/centre_console/src/drive_fsm.c b/projects/centre_console/src/drive_fsm.c index 68e061603..d1e8dd0b4 100644 --- a/projects/centre_console/src/drive_fsm.c +++ b/projects/centre_console/src/drive_fsm.c @@ -47,10 +47,11 @@ static void prv_neutral_input(Fsm *fsm, void *context) { StateId power_state = get_power_info_power_state(); uint8_t received_power_state = get_received_power_info(); - if (power_state == EE_POWER_DRIVE_STATE && power_state_main_flag == 0) { + if ((power_state == EE_POWER_DRIVE_STATE || power_state == EE_POWER_ON_STATE) && + power_state_main_flag == 0) { power_state_main_flag = 1; pca9555_gpio_set_state(&s_drive_btn_leds[NEUTRAL_LED], PCA9555_GPIO_STATE_HIGH); - } else if (power_state == EE_POWER_ON_STATE) { + } else if (power_state == EE_POWER_OFF_STATE) { power_state_main_flag = 0; pca9555_gpio_set_state(&s_drive_btn_leds[NEUTRAL_LED], PCA9555_GPIO_STATE_LOW); } @@ -203,7 +204,6 @@ StatusCode init_drive_fsm(void) { status_ok_or_return(pca9555_gpio_init_pin(&s_drive_btn_leds[i], &pca_settings)); } - pca9555_gpio_set_state(&s_drive_btn_leds[NEUTRAL_LED], PCA9555_GPIO_STATE_HIGH); fsm_init(drive, s_drive_state_list, s_drive_transitions, NEUTRAL, NULL); return STATUS_CODE_OK; } diff --git a/projects/centre_console/src/update_dashboard.c b/projects/centre_console/src/update_dashboard.c index 7ffb66b5a..a54774955 100644 --- a/projects/centre_console/src/update_dashboard.c +++ b/projects/centre_console/src/update_dashboard.c @@ -14,7 +14,7 @@ SegDisplay all_displays = ALL_DISPLAYS; // Centre Console State Variables static bool s_cc_enabled; -static bool s_regen_braking; +static uint8_t s_regen_braking; static bool s_hazard_state; static uint32_t s_target_velocity; static uint32_t s_last_power_state = EE_POWER_OFF_STATE; @@ -42,6 +42,11 @@ static Pca9555GpioAddress s_output_leds[NUM_DRIVE_LED] = { [AUX_WARNING_LED] = AUX_WARNING_LED_ADDR, }; +static uint8_t prv_regen_calc(uint16_t batt_current, uint16_t batt_voltage) { + return 100 * (MAX_VOLTAGE - (batt_voltage < MIN_VOLTAGE ? MIN_VOLTAGE : batt_voltage)) / + (MAX_VOLTAGE - MIN_VOLTAGE); +} + void update_indicators(uint32_t notif) { // Update hazard light if (notify_check_event(¬if, HAZARD_BUTTON_EVENT)) { @@ -50,19 +55,21 @@ void update_indicators(uint32_t notif) { pca9555_gpio_set_state(&s_output_leds[HAZARD_LED], PCA9555_GPIO_STATE_HIGH); } } else { - if (s_hazard_state) { - s_hazard_state = false; - pca9555_gpio_set_state(&s_output_leds[HAZARD_LED], PCA9555_GPIO_STATE_LOW); - } + s_hazard_state = false; + pca9555_gpio_set_state(&s_output_leds[HAZARD_LED], PCA9555_GPIO_STATE_LOW); } // Update regen light if (notify_check_event(¬if, REGEN_BUTTON_EVENT)) { - if (s_regen_braking) { - s_regen_braking = false; - pca9555_gpio_set_state(&s_output_leds[REGEN_LED], PCA9555_GPIO_STATE_LOW); - } else { - s_regen_braking = true; + uint16_t batt_voltage = get_battery_info_max_cell_v(); // Gets max voltage out of all cells + uint16_t batt_current = get_battery_vt_current(); + // solar current + regen current <= 27 AMPS + // regen current shouldnt push cell above 4.2 V + if (!s_regen_braking && batt_current < MAX_CURRENT && batt_voltage < MAX_VOLTAGE) { + s_regen_braking = prv_regen_calc(batt_current, batt_voltage); pca9555_gpio_set_state(&s_output_leds[REGEN_LED], PCA9555_GPIO_STATE_HIGH); + } else { + s_regen_braking = 0; + pca9555_gpio_set_state(&s_output_leds[REGEN_LED], PCA9555_GPIO_STATE_LOW); } } @@ -147,11 +154,12 @@ void update_displays(void) { // Read data from CAN structs and update displays with those values float avg_speed = (get_motor_velocity_velocity_l() + get_motor_velocity_velocity_r()) / 2; float speed_kph = avg_speed * CONVERT_VELOCITY_TO_KPH; - uint16_t batt_perc_val = get_battery_status_batt_perc(); + uint16_t batt_perc_val = get_battery_vt_batt_perc(); + uint32_t aux_battery_voltage = get_battery_status_aux_batt_v(); if (speed_kph >= 100) { - seg_displays_set_int(&all_displays, (int)speed_kph, batt_perc_val, s_target_velocity); + seg_displays_set_int(&all_displays, (int)speed_kph, batt_perc_val, aux_battery_voltage); } else { - seg_displays_set_float(&all_displays, speed_kph, batt_perc_val, s_target_velocity); + seg_displays_set_float(&all_displays, speed_kph, batt_perc_val, aux_battery_voltage); } } diff --git a/projects/power_distribution/src/power_seq_fsm.c b/projects/power_distribution/src/power_seq_fsm.c index 0e2423b2c..3a179cdf3 100644 --- a/projects/power_distribution/src/power_seq_fsm.c +++ b/projects/power_distribution/src/power_seq_fsm.c @@ -65,9 +65,11 @@ static void prv_close_relays_state_input(Fsm *fsm, void *context) { static void prv_on_state_output(void *context) { LOG_DEBUG("Transitioned to ON STATE\n"); - pd_set_active_output_group(OUTPUT_GROUP_POWER_ON); - power_context.latest_state = POWER_STATE_ON; - set_power_info_power_state(EE_POWER_ON_STATE); + if (power_context.target_state != POWER_STATE_DRIVE) { + pd_set_active_output_group(OUTPUT_GROUP_POWER_ON); + power_context.latest_state = POWER_STATE_ON; + set_power_info_power_state(EE_POWER_ON_STATE); + } } static void prv_on_state_input(Fsm *fsm, void *context) { @@ -77,7 +79,8 @@ static void prv_on_state_input(Fsm *fsm, void *context) { return; } CentreConsoleCCPwrEvent cc_power_event = get_cc_power_control_power_event(); - if (cc_power_event == EE_CC_PWR_CTL_EVENT_BTN_AND_BRAKE) { + if (cc_power_event == EE_CC_PWR_CTL_EVENT_BTN_AND_BRAKE || + power_context.target_state == POWER_STATE_DRIVE) { power_context.target_state = POWER_STATE_DRIVE; fsm_transition(fsm, TURN_ON_DRIVE_OUTPUTS); } else if (cc_power_event == EE_CC_PWR_CTL_EVENT_BTN) { @@ -95,7 +98,8 @@ static void prv_turn_on_drive_outputs_state_output(void *context) { static void prv_turn_on_drive_outputs_state_input(Fsm *fsm, void *context) { uint8_t mci_relay_state = get_mc_status_precharge_status(); - if (mci_relay_state == EE_RELAY_STATE_CLOSE) { + LOG_DEBUG("mci_relay_state %d\n", mci_relay_state); + if (mci_relay_state) { fsm_transition(fsm, POWER_STATE_DRIVE); } else if ((xTaskGetTickCount() - power_context.timer_start_ticks) > pdMS_TO_TICKS(MCI_RESPONSE_TIMEOUT_MS)) { @@ -115,7 +119,9 @@ static void prv_drive_state_input(Fsm *fsm, void *context) { return; } CentreConsoleCCPwrEvent cc_power_event = get_cc_power_control_power_event(); - if (cc_power_event == EE_CC_PWR_CTL_EVENT_BTN_AND_BRAKE || EE_CC_PWR_CTL_EVENT_BTN) { + LOG_DEBUG("cc_power_event %d\n", cc_power_event); + if (cc_power_event == EE_CC_PWR_CTL_EVENT_BTN_AND_BRAKE || + cc_power_event == EE_CC_PWR_CTL_EVENT_BTN) { power_context.target_state = POWER_STATE_ON; fsm_transition(fsm, POWER_STATE_ON); } diff --git a/smoke/bms_carrier/README.md b/smoke/bms_carrier/README.md new file mode 100644 index 000000000..bba80d0c2 --- /dev/null +++ b/smoke/bms_carrier/README.md @@ -0,0 +1,15 @@ + +# bms \ No newline at end of file diff --git a/smoke/bms_carrier/config.json b/smoke/bms_carrier/config.json new file mode 100644 index 000000000..5a1f1667e --- /dev/null +++ b/smoke/bms_carrier/config.json @@ -0,0 +1,15 @@ +{ + "libs": [ + "FreeRTOS", + "ms-common", + "ms-drivers" + ], + "include": [ + "projects/bms_carrier/inc" + ], + "sources": [ + "projects/bms_carrier/src/relays_fsm.o", + "projects/bms_carrier/src/current_sense.o" + ], + "can": true +} \ No newline at end of file diff --git a/smoke/bms_carrier/src/main.c b/smoke/bms_carrier/src/main.c new file mode 100644 index 000000000..62b93ce44 --- /dev/null +++ b/smoke/bms_carrier/src/main.c @@ -0,0 +1,33 @@ +#include + +#include "delay.h" +#include "gpio.h" +#include "log.h" +#include "relays_fsm.h" +#include "spi.h" +#include "tasks.h" + +TASK(smoke_bms_task, TASK_STACK_512) { + init_bms_relays(); + close_relays(); + while (true) { + LOG_DEBUG("CLOSING RELAYS!!!!\n"); + delay_ms(2000); + // LOG_DEBUG("OPENING!!!! RELAYS!!!!\n"); + // open_relays(); + // delay_ms(2000); + } +} + +int main() { + gpio_init(); + tasks_init(); + log_init(); + LOG_DEBUG("Welcome to BMS!!!!! TEST!\n"); + + tasks_init_task(smoke_bms_task, TASK_PRIORITY(1), NULL); + + tasks_start(); + + return 0; +} diff --git a/smoke/fan/README.md b/smoke/fan/README.md new file mode 100644 index 000000000..cd1d75537 --- /dev/null +++ b/smoke/fan/README.md @@ -0,0 +1,15 @@ + +# fan \ No newline at end of file diff --git a/smoke/fan/config.json b/smoke/fan/config.json new file mode 100644 index 000000000..4019f4748 --- /dev/null +++ b/smoke/fan/config.json @@ -0,0 +1,6 @@ +{ + "libs": [ + "FreeRTOS", + "ms-common" + ] +} \ No newline at end of file diff --git a/smoke/fan/src/main.c b/smoke/fan/src/main.c new file mode 100644 index 000000000..ed5408ce2 --- /dev/null +++ b/smoke/fan/src/main.c @@ -0,0 +1,14 @@ +#include "gpio.h" +#include "log.h" +#include "pwm.h" + +GpioAddress pin = { .port = GPIO_PORT_A, .pin = 8 }; +int main(void) { + gpio_init(); + log_init(); + gpio_init_pin(&pin, GPIO_ALTFN_PUSH_PULL, GPIO_STATE_HIGH); + pwm_init(PWM_TIMER_1, 40); + pwm_set_dc(PWM_TIMER_1, 50); + while (1) { + } +} diff --git a/smoke/ltc6811/README.md b/smoke/ltc6811/README.md new file mode 100644 index 000000000..d068552ae --- /dev/null +++ b/smoke/ltc6811/README.md @@ -0,0 +1,15 @@ + +# ltc6811 \ No newline at end of file diff --git a/smoke/ltc6811/src/main.c b/smoke/ltc6811/src/main.c new file mode 100644 index 000000000..e02f70f42 --- /dev/null +++ b/smoke/ltc6811/src/main.c @@ -0,0 +1,117 @@ +#include + +#include "delay.h" +#include "interrupt.h" +#include "log.h" +#include "ltc_afe.h" +#include "ltc_afe_impl.h" +#include "status.h" +#include "tasks.h" + +LtcAfeStorage s_ltc_store; +StatusCode status = STATUS_CODE_OK; + +LtcAfeSettings s_afe_settings = { + // Settings pending hardware validation + .mosi = { .port = GPIO_PORT_B, .pin = 15 }, + .miso = { .port = GPIO_PORT_B, .pin = 14 }, + .sclk = { .port = GPIO_PORT_B, .pin = 13 }, + .cs = { .port = GPIO_PORT_B, .pin = 12 }, + + .spi_port = SPI_PORT_2, + .spi_baudrate = 750000, + + .adc_mode = LTC_AFE_ADC_MODE_7KHZ, + + .cell_bitset = { 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF }, + .aux_bitset = { 0 }, + + .num_devices = 1, + .num_cells = 12, + .num_thermistors = 12, +}; + +TASK(smoke_ltc, TASK_STACK_1024) { + ltc_afe_init(&s_ltc_store, &s_afe_settings); + delay_ms(10); + while (1) { + // Trigger conversions + status = ltc_afe_impl_trigger_cell_conv(&s_ltc_store); + LOG_DEBUG("Status after cell_conv: %d\n", status); + // Wait for conversion to complete + delay_ms(10); + + // Read converted cell values + status = ltc_afe_impl_read_cells(&s_ltc_store); + LOG_DEBUG("Status after read_cells: %d\n", status); + // Log cell values + for (size_t cell = 0; cell < (s_afe_settings.num_devices * s_afe_settings.num_cells); cell++) { + LOG_DEBUG("CELL %d: %d\n\r", cell, + s_ltc_store.cell_voltages[s_ltc_store.cell_result_lookup[cell]]); + delay_ms(3); + } + + // Get min cell reading + uint16_t min_cell = UINT16_MAX; + for (size_t cell = 0; cell < (s_afe_settings.num_devices * s_afe_settings.num_cells); cell++) { + if (s_ltc_store.cell_voltages[s_ltc_store.cell_result_lookup[cell]] < min_cell) { + min_cell = s_ltc_store.cell_voltages[s_ltc_store.cell_result_lookup[cell]]; + } + } + LOG_DEBUG("Min cell voltage: %d\n", min_cell); + delay_ms(1); + + // Every cell that is 10mV or greater above (unbalanced) the minimum voltage cell should be + // discharged + min_cell += 100; // Add 100 to set the min discharge value (units of 100uV) + for (size_t cell = 0; cell < (s_afe_settings.num_devices * s_afe_settings.num_cells); cell++) { + if (s_ltc_store.cell_voltages[s_ltc_store.cell_result_lookup[cell]] > min_cell) { + ltc_afe_impl_toggle_cell_discharge(&s_ltc_store, cell, true); + LOG_DEBUG("Cell %d unbalanced \n", cell); + delay_ms(1); + } else { + ltc_afe_impl_toggle_cell_discharge(&s_ltc_store, cell, false); + } + } + + // Re-write the config with the new cells to discharge + // ltc_afe_impl_toggle_cell_discharge(&s_ltc_store, 3, true); + LOG_DEBUG("Config discharge bitset %d\n", s_ltc_store.discharge_bitset[0]); + delay_ms(1); + // ltc_afe_impl_write_config(&s_ltc_store); + + // for (size_t i = 0; i < 10; i++) { + // // Thermistor select cell 0 + // ltc_afe_impl_trigger_aux_conv(&s_ltc_store, i); + + // // Wait + // delay_ms(10); + + // // Thermistor read cell 0 + // ltc_afe_impl_read_aux(&s_ltc_store, i); + + // Log thermistor result + LOG_DEBUG("Thermistor reading: %d\n", + s_ltc_store.aux_voltages[s_ltc_store.aux_result_lookup[i]]); + } + + // Delay until next cycle + // Discharging should happen here + LOG_DEBUG("\n\n\r"); + delay_ms(5000); +} +} + +int main() { + gpio_init(); + interrupt_init(); + tasks_init(); + log_init(); + LOG_DEBUG("Welcome to TEST!"); + + tasks_init_task(smoke_ltc, 1, NULL); + tasks_start(); + + LOG_DEBUG("exiting main?"); + return 0; +}