diff --git a/.github/ISSUE_TEMPLATE/bug-form.yml b/.github/ISSUE_TEMPLATE/bug-form.yml index f3df30db..854803df 100644 --- a/.github/ISSUE_TEMPLATE/bug-form.yml +++ b/.github/ISSUE_TEMPLATE/bug-form.yml @@ -2,33 +2,20 @@ name: Bug description: For any issue related to a bug title: '[Area] - Short Description' labels: [bug] +projects: ["Northeastern-Electric-Racing/20"] body: - type: textarea id: observed-behavior attributes: label: Observed Behavior - description: What happened? - validations: - required: true - - type: textarea - id: expected-behavior - attributes: - label: Expected Behavior - description: What should have happened? + description: What happened? What should of happened? validations: required: true - type: textarea id: steps-to-reproduce attributes: label: Steps to Reproduce - description: How could someone reproduce this bug? + description: How could someone reproduce this bug? Include versions of code or PCBs as applicable. value: "1. \n2. \n3. " validations: required: true - - type: textarea - id: screenshots - attributes: - label: Screenshots (as needed) - description: Add screenshots of the bug if applicable - validations: - required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 0086358d..3ba13e0c 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1 +1 @@ -blank_issues_enabled: true +blank_issues_enabled: false diff --git a/.github/ISSUE_TEMPLATE/epic.yml b/.github/ISSUE_TEMPLATE/epic.yml deleted file mode 100644 index 1827d9e6..00000000 --- a/.github/ISSUE_TEMPLATE/epic.yml +++ /dev/null @@ -1,64 +0,0 @@ -name: Epic -description: A collection of many tasks -title: '[Area] - Short Description' -labels: [epic] -body: - - type: textarea - id: overview - attributes: - label: Overview - description: Provide a brief summary of this epic - validations: - required: true - - type: textarea - id: stakeholders - attributes: - label: Stakeholders - description: Who to contact for this epic? - value: | - Product Stakeholder: - Software Stakeholder: - Reference Users: - - type: textarea - id: metrics - attributes: - label: Success Metrics - description: What are the metrics we will use to determine if this is successful? - validations: - required: true - - type: textarea - id: rollout - attributes: - label: Rollout Plan - description: How will this be released? All at once? In parts? - validations: - required: true - - type: textarea - id: scope - attributes: - label: Out of Scope - description: What is not included in this epic? - validations: - required: true - - type: textarea - id: background-context - attributes: - label: Background / Context - description: What is the context for this epic? What already exists? - validations: - required: true - - type: textarea - id: acceptance - attributes: - label: Acceptance Criteria - description: How are we evaluating the success of this epic? - validations: - required: true - - type: textarea - id: tickets - attributes: - label: Tickets - description: What tickets will be a part of this epic? - value: " - [ ] #\n - [ ] #" - validations: - required: true diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml deleted file mode 100644 index 4b491b02..00000000 --- a/.github/ISSUE_TEMPLATE/feature-request.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Feature Request -description: Suggest a new feature for the project -title: '[Area] - Short Description' -labels: [new feature] -body: - - type: textarea - id: current-features - attributes: - label: Current Features - description: What exists currently? - validations: - required: true - - type: textarea - id: desired-features - attributes: - label: Desired Additional Features - description: What features do you want to add? - validations: - required: true - - type: textarea - id: screenshots - attributes: - label: Screenshots (as needed) - description: Add screenshots of the current or desired state if applicable - validations: - required: false diff --git a/.github/ISSUE_TEMPLATE/other.yml b/.github/ISSUE_TEMPLATE/other.yml deleted file mode 100644 index 7df8945a..00000000 --- a/.github/ISSUE_TEMPLATE/other.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Other -description: For issues that don't fit the other categories -title: '[Area] - Short Description' -body: - - type: textarea - id: desired-changes - attributes: - label: Desired Changes - description: What changes do you want to make? - validations: - required: true - - type: textarea - id: screenshots - attributes: - label: Screenshots (as needed) - description: Add screenshots if applicable - validations: - required: false diff --git a/.github/ISSUE_TEMPLATE/spike.yml b/.github/ISSUE_TEMPLATE/spike.yml deleted file mode 100644 index f5dc1c4d..00000000 --- a/.github/ISSUE_TEMPLATE/spike.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: Spike -description: For any research or investigation into a feature or our current architecture -title: '[Area] - Short Description' -labels: [spike] -body: - - type: markdown - attributes: - value: For a spike ticket, please make sure to remember to add links to the resources you are pulling information from so others who are reviewing and giving feedback can also take a look at the same information you are looking at. - - type: dropdown - id: spike-type - attributes: - label: Spike Type - description: Is this spike about the product (functional) or the implementation of the product (technical)? - multiple: true - options: - - Functional - - Technical - - type: textarea - id: goal - attributes: - label: Goal - description: What is this spike ticket looking into/trying to solve? Is this about enhancing something we currently do or looking into a new feature? - validations: - required: true - - type: textarea - id: purpose - attributes: - label: Reason for Spike - description: Why is this spike ticket necessary? - validations: - required: true - - type: textarea - id: sidenotes - attributes: - label: Additional notes - description: Add any extra comments related to the spike. - validations: - required: false diff --git a/.github/ISSUE_TEMPLATE/task.yml b/.github/ISSUE_TEMPLATE/task.yml index 81bd7915..0328d7d2 100644 --- a/.github/ISSUE_TEMPLATE/task.yml +++ b/.github/ISSUE_TEMPLATE/task.yml @@ -1,19 +1,20 @@ -name: Task -description: Create a task that a developer can complete. +name: Feature +description: Describe a feature that a developer can add. title: '[Area] - Short Description' +projects: ["Northeastern-Electric-Racing/20"] body: - type: textarea id: description attributes: label: Description - description: Provide a brief summary of this issue + description: Provide a brief summary of the current shortcoming or new improvement validations: required: true - type: textarea id: acceptance-criteria attributes: label: Acceptance Criteria - description: What are the conditions that need to be satisified to complete this task? + description: What are the test conditions that need to be satisified to complete this task? validations: required: true - type: textarea diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 5c723e37..0a8fc14a 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -25,7 +25,6 @@ It can be helpful to check the `Checks` and `Files changed` tabs. Please reach out to your Project Lead if anything is unclear. Please request reviewers and ping on slack only after you've gone through this whole checklist. -- [ ] All commits are tagged with the ticket number - [ ] No merge conflicts - [ ] All checks passing - [ ] Remove any non-applicable sections of this template diff --git a/.github/workflows/build-check.yml b/.github/workflows/build-check.yml index 222e8985..de82e429 100644 --- a/.github/workflows/build-check.yml +++ b/.github/workflows/build-check.yml @@ -3,8 +3,6 @@ on: [push] jobs: run-build: runs-on: ubuntu-latest - container: - image: ghcr.io/northeastern-electric-racing/embedded-base:main timeout-minutes: 10 steps: - name: Checkout code @@ -12,9 +10,10 @@ jobs: with: submodules: recursive token: ${{ secrets.PAT_TOKEN }} + fetch-depth: 0 - name: Execute Make run: | - if ! make; then + if ! docker compose run --rm ner-gcc-arm make -j `nproc`; then echo "The application has failed to build." exit 1 # This will cause the workflow to fail - fi \ No newline at end of file + fi diff --git a/Core/Inc/can_handler.h b/Core/Inc/can_handler.h index b5987b2b..d6214258 100644 --- a/Core/Inc/can_handler.h +++ b/Core/Inc/can_handler.h @@ -13,18 +13,33 @@ #define NUM_INBOUND_CAN2_IDS 1 #define CHARGE_CANID 0x176 +#define CHARGE_SIZE 8 #define DISCHARGE_CANID 0x156 +#define DISCHARGE_SIZE 8 #define ACC_STATUS_CANID 0x80 +#define ACC_STATUS_SIZE 8 #define BMS_STATUS_CANID 0x81 +#define BMS_STATUS_SIZE 4 +#define FAULT_STATUS_CANID 0x89 +#define FAULT_STATUS_SIZE 8 #define SHUTDOWN_CTRL_CANID 0x82 +#define SHUTDOWN_CTRL_SIZE 1 #define CELL_DATA_CANID 0x83 +#define CELL_DATA_SIZE 8 #define CELL_VOLTAGE_CANID 0x87 +#define CELL_VOLTAGE_SIZE 8 #define CURRENT_CANID 0x86 +#define CURRENT_SIZE 6 #define CELL_TEMP_CANID 0x84 +#define CELL_TEMP_SIZE 8 #define SEGMENT_TEMP_CANID 0x85 -#define FAULT_CANID 0x703 +#define SEGMENT_TEMP_SIZE 6 +#define FAULT_CANID 0x703 // TODO: cleanup +#define FAULT_SIZE 5 #define NOISE_CANID 0x88 +#define NOISE_SIZE 6 #define DEBUG_CANID 0x702 + #define ALPHA_CELL_CANID 0x6FA #define BETA_CELL_CANID 0x6FB #define CELL_MSG_SIZE 7 @@ -59,6 +74,10 @@ typedef enum { RL_MSG_COUNT } rate_lim_t; +#define DEBUG_SIZE 8 +#define FAULT_TIMER_CANID 0x6FF +#define FAULT_TIMER_SIZE 4 + void can_receive_callback(CAN_HandleTypeDef *hcan); /** diff --git a/Core/Inc/compute.h b/Core/Inc/compute.h index 7bcfb0c8..9620af90 100644 --- a/Core/Inc/compute.h +++ b/Core/Inc/compute.h @@ -109,6 +109,14 @@ void compute_set_fault(int fault_state); */ void compute_send_acc_status_message(acc_data_t *bmsdata); +/** + * @brief sends fault status message + * + * @param bms_state + * + */ +void compute_send_fault_status_message(acc_data_t *bmsdata); + /** * @brief sends BMS status message * @@ -170,6 +178,9 @@ void compute_send_segment_temp_message(acc_data_t *bmsdata); void compute_send_fault_message(uint8_t status, int16_t curr, int16_t in_dcl); +void compute_send_fault_timer_message(uint8_t start_stop, uint32_t fault_code, + uint16_t data_1); + /** * @brief Send CAN message for debugging the car on the fly. * diff --git a/Core/Inc/datastructs.h b/Core/Inc/datastructs.h index d923ed4c..fe7545ac 100644 --- a/Core/Inc/datastructs.h +++ b/Core/Inc/datastructs.h @@ -40,10 +40,10 @@ typedef struct { * to set or get the error codes */ // clang-format off -typedef enum { +enum { FAULTS_CLEAR = 0x0, - /* Orion BMS faults */ + /* Shepherd BMS faults */ CELLS_NOT_BALANCING = 0x1, CELL_VOLTAGE_TOO_HIGH = 0x4, CELL_VOLTAGE_TOO_LOW = 0x2, @@ -64,7 +64,8 @@ typedef enum { CHARGE_LIMIT_ENFORCEMENT_FAULT = 0x20000, MAX_FAULTS = 0x80000000 /* Maximum allowable fault code */ -} bms_fault_t; +}; + // clang-format on /** @@ -93,6 +94,7 @@ typedef struct { cell_asic chips[NUM_CHIPS]; int fault_status; + int fault_status; // FIXME: this field is unused int16_t pack_current; /* this value is multiplied by 10 to account for decimal precision */ uint16_t pack_voltage; @@ -111,7 +113,9 @@ typedef struct { /** * @brief Note that this is a 32 bit integer, so there are 32 max possible fault codes */ - uint32_t fault_code; + // uint32_t fault_code; + uint32_t fault_code_crit; + uint32_t fault_code_noncrit; /* Max, min, and avg thermistor readings */ crit_cellval_t max_temp; @@ -186,7 +190,8 @@ typedef struct { int data_2; int lim_2; - bool is_faulted; + bool is_critical; + // bool is_faulted; /* note: unused field */ } fault_eval_t; #endif \ No newline at end of file diff --git a/Core/Inc/stateMachine.h b/Core/Inc/stateMachine.h index 9e20cda6..04d55fd5 100644 --- a/Core/Inc/stateMachine.h +++ b/Core/Inc/stateMachine.h @@ -38,18 +38,18 @@ bool sm_charging_check(acc_data_t *bmsdata); * @note Should be bitwise OR'ed with the current fault status * * @param accData - * @return uint32_t + * @return uint64_t to be cast to a bms_fault_t */ -uint32_t sm_fault_return(acc_data_t *accData); +uint64_t sm_fault_return(acc_data_t *accData); /** * @brief Used in parellel to faultReturn(), calculates each fault to append the * fault status * - * @param index - * @return fault_code + * @param fault_item + * @return fault_status */ -uint32_t sm_fault_eval(fault_eval_t *index); +bool sm_fault_eval(fault_eval_t *fault_item); /** * @brief handles the state machine, calls the appropriate handler function and diff --git a/Core/Inc/stm32f4xx_it.h b/Core/Inc/stm32f4xx_it.h index 78f33294..4f9096b1 100644 --- a/Core/Inc/stm32f4xx_it.h +++ b/Core/Inc/stm32f4xx_it.h @@ -57,6 +57,7 @@ void CAN1_RX0_IRQHandler(void); void TIM3_IRQHandler(void); void UART4_IRQHandler(void); void DMA2_Stream0_IRQHandler(void); +void DMA2_Stream2_IRQHandler(void); void CAN2_RX0_IRQHandler(void); void CAN2_RX1_IRQHandler(void); /* USER CODE BEGIN EFP */ diff --git a/Core/Src/can_handler.c b/Core/Src/can_handler.c index 0725ac01..c2931558 100644 --- a/Core/Src/can_handler.c +++ b/Core/Src/can_handler.c @@ -2,6 +2,7 @@ #include #include #include +#include #define CAN_MSG_QUEUE_SIZE 50 /* messages */ @@ -12,12 +13,26 @@ static osMessageQueueId_t can_outbound_queue; static osMessageQueueId_t can_inbound_queue; +/** + * @brief Datastructure for keeping track of the last time a CAN message was transmitted. + * + */ +typedef struct { + uint32_t id; + uint32_t prev_tick; + uint32_t msg_rate; /* in milliseconds */ +} rl_can_msg_t; + +struct node_t { + rl_can_msg_t val; + struct node_t *next; +}; + +struct node_t *rl_bms_msgs = NULL; + can_t *can1; can_t *can2; -can_msg_t bms_can_msgs[RL_MSG_COUNT]; -rl_data_t rl_data[RL_MSG_COUNT]; - static uint32_t can1_id_list[] = { //CANID_X, 0x002 @@ -40,130 +55,46 @@ osStatus_t queue_and_set_flag(osMessageQueueId_t queue, const void *msg_ptr, return status; } -void init_can_msg_config() +/** + * @brief Add a CAN message to the list of rate limited CAN messages. + * + * @param id ID of the CAN message to rate limit. + * @param msg_rate The amount of time that must pass before this CAN message can be ttansmitted again. + */ +void init_rl_can_msg(uint32_t id, uint32_t msg_rate) { - can_msg_t discharge_msg = { 0 }; - discharge_msg.id = - DISCHARGE_CANID; // 0x0A is the dcl id, 0x22 is the device id set by us - discharge_msg.len = 8; - - can_msg_t charge_msg = { 0 }; - charge_msg.id = - CHARGE_CANID; // 0x0A is the dcl id, 0x157 is the device id set by us - charge_msg.len = 8; - - can_msg_t acc_status_msg; - acc_status_msg.id = ACC_STATUS_CANID; - acc_status_msg.len = 8; - - can_msg_t bms_status_msg; - bms_status_msg.id = BMS_STATUS_CANID; - bms_status_msg.len = 8; - - can_msg_t shutdown_ctrl_msg; - shutdown_ctrl_msg.id = SHUTDOWN_CTRL_CANID; - shutdown_ctrl_msg.len = 1; - - can_msg_t cell_data_msg; - cell_data_msg.id = CELL_DATA_CANID; - cell_data_msg.len = 8; - - can_msg_t cell_voltage_msg; - cell_voltage_msg.id = CELL_VOLTAGE_CANID; - cell_voltage_msg.len = 8; - - can_msg_t current_msg; - current_msg.id = CURRENT_CANID; - current_msg.len = 6; - - can_msg_t cell_temp_msg; - cell_temp_msg.id = CELL_TEMP_CANID; - cell_temp_msg.len = 8; - - can_msg_t segment_temp_msg; - segment_temp_msg.id = SEGMENT_TEMP_CANID; - segment_temp_msg.len = 6; - - can_msg_t fault_msg; - fault_msg.id = FAULT_CANID; - fault_msg.len = 5; - - can_msg_t noise_msg; - noise_msg.id = NOISE_CANID; - noise_msg.len = 6; - - can_msg_t debug_msg; - debug_msg.id = DEBUG_CANID; - debug_msg.len = 8; // yaml decodes this to 8 bytes - - // rl_data_t rl_discharge_data = { .msg_rate = 5000 }; - // rl_data_t rl_charge_data = { .msg_rate = 0 }; - - bms_can_msgs[DISCHARGE] = discharge_msg; - bms_can_msgs[CHARGE] = charge_msg; - bms_can_msgs[ACC_STATUS] = acc_status_msg; - bms_can_msgs[BMS_STATUS] = bms_status_msg; - bms_can_msgs[SHUTDOWN_CTRL] = shutdown_ctrl_msg; - bms_can_msgs[CELL_DATA] = cell_data_msg; - bms_can_msgs[CELL_VOLTAGE] = cell_voltage_msg; - bms_can_msgs[CURRENT] = current_msg; - bms_can_msgs[CELL_TEMP] = cell_temp_msg; - bms_can_msgs[SEGMENT_TEMP] = segment_temp_msg; - bms_can_msgs[FAULT] = fault_msg; - bms_can_msgs[NOISE] = noise_msg; - bms_can_msgs[DEBUG] = debug_msg; - - // rl_data[DISCHARGE] = rl_discharge_data; - // rl_data[CHARGE] = rl_charge_data; -} + if (rl_bms_msgs == NULL) { + rl_bms_msgs = malloc(sizeof(struct node_t)); + rl_bms_msgs->val.id = id; + rl_bms_msgs->val.msg_rate = msg_rate; + rl_bms_msgs->val.prev_tick = HAL_GetTick(); + rl_bms_msgs->next = NULL; + return; + } -rl_data_t *get_rl_msg(uint32_t can_id) -{ - switch (can_id) { - case CHARGE_CANID: - return &rl_data[CHARGE]; - break; - case DISCHARGE_CANID: - return &rl_data[DISCHARGE]; - break; - case ACC_STATUS_CANID: - return &rl_data[ACC_STATUS]; - break; - case BMS_STATUS_CANID: - return &rl_data[BMS_STATUS]; - break; - case SHUTDOWN_CTRL_CANID: - return &rl_data[SHUTDOWN_CTRL]; - break; - case CELL_DATA_CANID: - return &rl_data[CELL_DATA]; - break; - case CELL_VOLTAGE_CANID: - return &rl_data[CELL_VOLTAGE]; - break; - case CURRENT_CANID: - return &rl_data[CURRENT]; - break; - case CELL_TEMP_CANID: - return &rl_data[CELL_TEMP]; - break; - case SEGMENT_TEMP_CANID: - return &rl_data[SEGMENT_TEMP]; - break; - case FAULT_CANID: - return &rl_data[FAULT]; - break; - case NOISE_CANID: - return &rl_data[NOISE]; - break; - case DEBUG_CANID: - return &rl_data[DEBUG]; - break; - default: - break; + struct node_t *curr = rl_bms_msgs; + + while (curr->next != NULL) { + curr = curr->next; } - return NULL; + struct node_t *next = malloc(sizeof(struct node_t)); + next->val.id = id; + next->val.msg_rate = msg_rate; + next->val.prev_tick = HAL_GetTick(); + next->next = NULL; + + curr->next = next; +} + +/** + * @brief Initialize any per message configurations. + * + */ +void init_can_msg_config() +{ + // EXAMPLE + // init_rl_can_msg(DISCHARGE_CANID, 4000); } void init_both_can(CAN_HandleTypeDef *hcan1, CAN_HandleTypeDef *hcan2) @@ -227,16 +158,23 @@ int8_t queue_can_msg(can_msg_t msg) if (!can_outbound_queue) return -1; - rl_data_t *rl_data = get_rl_msg(msg.id); - - if (rl_data != NULL && rl_data->msg_rate != 0) { - if (HAL_GetTick() <= - pdMS_TO_TICKS(rl_data->prev_tick) + rl_data->msg_rate) { - // block message - return 0; - } else { - rl_data->prev_tick = HAL_GetTick(); + struct node_t *curr = rl_bms_msgs; + + while (curr != NULL) { + if (curr->val.id == msg.id) { + if (HAL_GetTick() <= + curr->val.prev_tick + + pdMS_TO_TICKS(curr->val.msg_rate)) { + // block message + // printf("Blocked 0x%lX\t", msg.id); + return 0; + } else { + // printf("Sent 0x%lX\n", msg.id); + curr->val.prev_tick = HAL_GetTick(); + break; + } } + curr = curr->next; } return queue_and_set_flag(can_outbound_queue, &msg, can_dispatch_handle, @@ -263,7 +201,7 @@ void vCanDispatch(void *pv_params) for (;;) { osThreadFlagsWait(CAN_DISPATCH_FLAG, osFlagsWaitAny, - osFlagsWaitAny); + osWaitForever); /* Send all CAN messages in the queue */ while (osOK == osMessageQueueGet(can_outbound_queue, diff --git a/Core/Src/compute.c b/Core/Src/compute.c index 067ebd46..633a5b64 100644 --- a/Core/Src/compute.c +++ b/Core/Src/compute.c @@ -8,6 +8,7 @@ #include #include "bmsConfig.h" #include "can_handler.h" +#include #define REF_CHANNEL 0 #define VOUT_CHANNEL 1 @@ -17,6 +18,8 @@ uint8_t fan_speed; bool is_charging_enabled; enum { CHARGE_ENABLED, CHARGE_DISABLED }; +uint32_t channel_1_buf[2]; +uint32_t raw_high_current_buf; extern TIM_HandleTypeDef htim1; extern TIM_HandleTypeDef htim8; @@ -24,8 +27,6 @@ extern TIM_HandleTypeDef htim8; extern ADC_HandleTypeDef hadc1; extern ADC_HandleTypeDef hadc2; -extern can_msg_t bms_can_msgs[RL_MSG_COUNT]; - TIM_OC_InitTypeDef pwm_config; ADC_ChannelConfTypeDef adc_config; @@ -61,7 +62,14 @@ uint8_t compute_init(acc_data_t *bmsdata) // HAL_TIM_PWM_Start(&htim8, fan_channels[FAN6]); bmsdata->is_charger_connected = false; - HAL_ADC_Start(&hadc2); + //DMA for first ADC channel -- raw_low_current and ref_5V + assert(!HAL_ADC_Start_DMA(&hadc1, channel_1_buf, + sizeof(channel_1_buf) / sizeof(uint32_t))); + + //DMA for second ADC channel -- raw_high_current + assert(!HAL_ADC_Start_DMA(&hadc2, &raw_high_current_buf, + sizeof(raw_high_current_buf) / + sizeof(uint32_t))); return 0; } @@ -195,20 +203,19 @@ int16_t compute_get_pack_current() 1 / 0.0041; // Calibrated with current = 5A, 10A, 20A static const float LOWCHANNEL_GAIN = 1 / 0.0267; */ - // Change ADC channel to read the high current sensor - change_adc1_channel(VOUT_CHANNEL); - HAL_ADC_Start(&hadc1); - HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY); - int raw_low_current = HAL_ADC_GetValue(&hadc1); - HAL_ADC_Start(&hadc2); - HAL_ADC_PollForConversion(&hadc2, HAL_MAX_DELAY); - int raw_high_current = HAL_ADC_GetValue(&hadc2); + uint32_t raw_high_current; + uint32_t raw_low_current; + uint32_t ref_5V; + + memcpy(&raw_high_current, &raw_high_current_buf, + sizeof(raw_high_current_buf)); - change_adc1_channel(REF_CHANNEL); - HAL_ADC_Start(&hadc1); - HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY); - int ref_5V = HAL_ADC_GetValue(&hadc1); + memcpy(&raw_low_current, &channel_1_buf[0], + sizeof(channel_1_buf[0])); //From the rank of ADC_CHANNEL_15 + + memcpy(&ref_5V, &channel_1_buf[1], + sizeof(channel_1_buf[1])); //From the rank of ADC_CHANNEL_9 int16_t ref_voltage_raw = (int16_t)(1000.0f * ((float)ref_5V * CURRENT_ADC_RESOLUTION)); @@ -244,7 +251,6 @@ int16_t compute_get_pack_current() return -low_current; } - // printf("\rHigh Current: %d\n", -high_current); return -high_current; } @@ -261,10 +267,13 @@ void compute_send_mc_discharge_message(acc_data_t *bmsdata) endian_swap(&discharge_data.max_discharge, sizeof(discharge_data.max_discharge)); - memcpy(bms_can_msgs[DISCHARGE].data, &discharge_data, - sizeof(discharge_data)); + can_msg_t msg; + msg.id = DISCHARGE_CANID; + msg.len = DISCHARGE_SIZE; - queue_can_msg(bms_can_msgs[DISCHARGE]); + memcpy(msg.data, &discharge_data, sizeof(discharge_data)); + + queue_can_msg(msg); } void compute_send_mc_charge_message(acc_data_t *bmsdata) @@ -279,9 +288,13 @@ void compute_send_mc_charge_message(acc_data_t *bmsdata) /* convert to big endian */ endian_swap(&charge_data.max_charge, sizeof(charge_data.max_charge)); - memcpy(bms_can_msgs[CHARGE].data, &charge_data, sizeof(charge_data)); + can_msg_t msg; + msg.id = CHARGE_CANID; + msg.len = CHARGE_SIZE; + + memcpy(msg.data, &charge_data, sizeof(charge_data)); - queue_can_msg(bms_can_msgs[CHARGE]); + queue_can_msg(msg); } void compute_send_acc_status_message(acc_data_t *bmsdata) @@ -309,10 +322,39 @@ void compute_send_acc_status_message(acc_data_t *bmsdata) endian_swap(&acc_status_msg_data.pack_ah, sizeof(acc_status_msg_data.pack_ah)); - memcpy(bms_can_msgs[ACC_STATUS].data, &acc_status_msg_data, - sizeof(acc_status_msg_data)); + can_msg_t msg; + msg.id = ACC_STATUS_CANID; + msg.len = ACC_STATUS_SIZE; + + memcpy(msg.data, &acc_status_msg_data, sizeof(acc_status_msg_data)); + + queue_can_msg(msg); +} + +void compute_send_fault_status_message(acc_data_t *bmsdata) +{ + struct __attribute__((__packed__)) { + uint32_t fault_crit; + uint32_t fault_noncrit; + } fault_status_msg_data; + + /* convert to big endian */ + endian_swap(&fault_status_msg_data.fault_crit, + sizeof(fault_status_msg_data.fault_crit)); + endian_swap(&fault_status_msg_data.fault_noncrit, + sizeof(fault_status_msg_data.fault_noncrit)); + + fault_status_msg_data.fault_crit = bmsdata->fault_code_crit; + fault_status_msg_data.fault_noncrit = bmsdata->fault_code_noncrit; + + can_msg_t fault_msg; + fault_msg.id = FAULT_STATUS_CANID; + fault_msg.len = FAULT_STATUS_SIZE; + + memcpy(fault_msg.data, &fault_status_msg_data, + sizeof(fault_status_msg_data)); - queue_can_msg(bms_can_msgs[ACC_STATUS]); + queue_can_msg(fault_msg); } void compute_send_bms_status_message(acc_data_t *bmsdata, int bms_state, @@ -320,7 +362,6 @@ void compute_send_bms_status_message(acc_data_t *bmsdata, int bms_state, { struct __attribute__((__packed__)) { uint8_t state; - uint32_t fault; int8_t temp_avg; uint8_t temp_internal; uint8_t balance; @@ -328,18 +369,16 @@ void compute_send_bms_status_message(acc_data_t *bmsdata, int bms_state, bms_status_msg_data.temp_avg = (int8_t)(bmsdata->avg_temp); bms_status_msg_data.state = (uint8_t)(bms_state); - bms_status_msg_data.fault = bmsdata->fault_code; bms_status_msg_data.temp_internal = (uint8_t)(0); bms_status_msg_data.balance = (uint8_t)(balance); - /* convert to big endian */ - endian_swap(&bms_status_msg_data.fault, - sizeof(bms_status_msg_data.fault)); + can_msg_t msg; + msg.id = BMS_STATUS_CANID; + msg.len = BMS_STATUS_SIZE; - memcpy(bms_can_msgs[BMS_STATUS].data, &bms_status_msg_data, - sizeof(bms_status_msg_data)); + memcpy(msg.data, &bms_status_msg_data, sizeof(bms_status_msg_data)); - queue_can_msg(bms_can_msgs[BMS_STATUS]); + queue_can_msg(msg); } void compute_send_shutdown_ctrl_message(uint8_t mpe_state) @@ -350,10 +389,14 @@ void compute_send_shutdown_ctrl_message(uint8_t mpe_state) shutdown_control_msg_data.mpeState = mpe_state; - memcpy(bms_can_msgs[SHUTDOWN_CTRL].data, &shutdown_control_msg_data, + can_msg_t msg; + msg.id = SHUTDOWN_CTRL_CANID; + msg.len = SHUTDOWN_CTRL_SIZE; + + memcpy(msg.data, &shutdown_control_msg_data, sizeof(shutdown_control_msg_data)); - queue_can_msg(bms_can_msgs[SHUTDOWN_CTRL]); + queue_can_msg(msg); } void compute_send_cell_voltage_message(acc_data_t *bmsdata) @@ -383,10 +426,49 @@ void compute_send_cell_voltage_message(acc_data_t *bmsdata) endian_swap(&cell_data_msg_data.volt_avg, sizeof(cell_data_msg_data.volt_avg)); - memcpy(bms_can_msgs[CELL_DATA].data, &cell_data_msg_data, - sizeof(cell_data_msg_data)); + can_msg_t msg; + msg.id = CELL_DATA_CANID; + msg.len = CELL_DATA_SIZE; + + memcpy(msg.data, &cell_data_msg_data, sizeof(cell_data_msg_data)); + + queue_can_msg(msg); +} + +void compute_send_cell_voltage_message(uint8_t cell_id, + uint16_t instant_voltage, + uint16_t internal_Res, uint8_t shunted, + uint16_t open_voltage) +{ + struct __attribute__((__packed__)) { + uint8_t cellID; + uint16_t instantVoltage; + uint16_t internalResistance; + uint8_t shunted; + uint16_t openVoltage; + } cell_voltage_msg_data; + + cell_voltage_msg_data.cellID = cell_id; + cell_voltage_msg_data.instantVoltage = instant_voltage; + cell_voltage_msg_data.internalResistance = internal_Res; + cell_voltage_msg_data.shunted = shunted; + cell_voltage_msg_data.openVoltage = open_voltage; + + /* convert to big endian */ + endian_swap(&cell_voltage_msg_data.instantVoltage, + sizeof(cell_voltage_msg_data.instantVoltage)); + endian_swap(&cell_voltage_msg_data.internalResistance, + sizeof(cell_voltage_msg_data.internalResistance)); + endian_swap(&cell_voltage_msg_data.openVoltage, + sizeof(cell_voltage_msg_data.openVoltage)); + + can_msg_t msg; + msg.id = CELL_VOLTAGE_CANID; + msg.len = CELL_VOLTAGE_SIZE; - queue_can_msg(bms_can_msgs[CELL_DATA]); + memcpy(msg.data, &cell_voltage_msg_data, sizeof(cell_voltage_msg_data)); + + queue_can_msg(msg); } void compute_send_current_message(acc_data_t *bmsdata) @@ -409,10 +491,14 @@ void compute_send_current_message(acc_data_t *bmsdata) endian_swap(¤t_status_msg_data.pack_curr, sizeof(current_status_msg_data.pack_curr)); - memcpy(bms_can_msgs[CURRENT].data, ¤t_status_msg_data, + can_msg_t msg; + msg.id = CURRENT_CANID; + msg.len = CURRENT_SIZE; + + memcpy(msg.data, ¤t_status_msg_data, sizeof(current_status_msg_data)); - queue_can_msg(bms_can_msgs[CURRENT]); + queue_can_msg(msg); } void compute_send_cell_temp_message(acc_data_t *bmsdata) @@ -441,10 +527,13 @@ void compute_send_cell_temp_message(acc_data_t *bmsdata) endian_swap(&cell_temp_msg_data.average_temp, sizeof(cell_temp_msg_data.average_temp)); - memcpy(bms_can_msgs[CELL_TEMP].data, &cell_temp_msg_data, - sizeof(cell_temp_msg_data)); + can_msg_t msg; + msg.id = CELL_TEMP_CANID; + msg.len = CELL_TEMP_SIZE; + + memcpy(msg.data, &cell_temp_msg_data, sizeof(cell_temp_msg_data)); - queue_can_msg(bms_can_msgs[CELL_TEMP]); + queue_can_msg(msg); } void compute_send_segment_temp_message(acc_data_t *bmsdata) @@ -472,10 +561,13 @@ void compute_send_segment_temp_message(acc_data_t *bmsdata) segment_temp_msg_data.segment6_average_temp = bmsdata->segment_average_temps[5]; - memcpy(bms_can_msgs[SEGMENT_TEMP].data, &segment_temp_msg_data, - sizeof(segment_temp_msg_data)); + can_msg_t msg; + msg.id = SEGMENT_TEMP_CANID; + msg.len = SEGMENT_TEMP_SIZE; - queue_can_msg(bms_can_msgs[SEGMENT_TEMP]); + memcpy(msg.data, &segment_temp_msg_data, sizeof(segment_temp_msg_data)); + + queue_can_msg(msg); } void compute_send_fault_message(uint8_t status, int16_t curr, int16_t in_dcl) @@ -494,10 +586,40 @@ void compute_send_fault_message(uint8_t status, int16_t curr, int16_t in_dcl) sizeof(fault_msg_data.pack_curr)); endian_swap(&fault_msg_data.dcl, sizeof(fault_msg_data.dcl)); - memcpy(bms_can_msgs[FAULT].data, &fault_msg_data, - sizeof(fault_msg_data)); + can_msg_t msg; + msg.id = FAULT_CANID; + msg.len = FAULT_SIZE; + + memcpy(msg.data, &fault_msg_data, sizeof(fault_msg_data)); - queue_can_msg(bms_can_msgs[FAULT]); + queue_can_msg(msg); +} + +void compute_send_fault_timer_message(uint8_t start_stop, uint32_t fault_code, + uint16_t data_1) +{ + struct __attribute__((__packed__)) { + uint8_t start_stop; + uint8_t fault_code; + int16_t data_1; + } fault_timer_msg_data; + + fault_timer_msg_data.start_stop = start_stop; + fault_timer_msg_data.fault_code = log2(fault_code); + fault_timer_msg_data.data_1 = data_1; + + endian_swap(&fault_timer_msg_data.fault_code, + sizeof(fault_timer_msg_data.fault_code)); + endian_swap(&fault_timer_msg_data.data_1, + sizeof(fault_timer_msg_data.data_1)); + + can_msg_t msg; + msg.id = FAULT_TIMER_CANID; + msg.len = FAULT_TIMER_SIZE; + + memcpy(msg.data, &fault_timer_msg_data, sizeof(fault_timer_msg_data)); + + queue_can_msg(msg); } void compute_send_voltage_noise_message(acc_data_t *bmsdata) @@ -524,10 +646,14 @@ void compute_send_voltage_noise_message(acc_data_t *bmsdata) voltage_noise_msg_data.seg6_noise = bmsdata->segment_noise_percentage[5]; - memcpy(bms_can_msgs[NOISE].data, &voltage_noise_msg_data, + can_msg_t msg; + msg.id = NOISE_CANID; + msg.len = NOISE_SIZE; + + memcpy(msg.data, &voltage_noise_msg_data, sizeof(voltage_noise_msg_data)); - queue_can_msg(bms_can_msgs[NOISE]); + queue_can_msg(msg); } void compute_send_debug_message(uint8_t debug0, uint8_t debug1, uint16_t debug2, @@ -548,9 +674,13 @@ void compute_send_debug_message(uint8_t debug0, uint8_t debug1, uint16_t debug2, endian_swap(&debug_msg_data.debug2, sizeof(debug_msg_data.debug2)); endian_swap(&debug_msg_data.debug3, sizeof(debug_msg_data.debug3)); - memcpy(bms_can_msgs[DEBUG].data, &debug_msg_data, 8); + can_msg_t msg; + msg.id = DEBUG_CANID; + msg.len = DEBUG_SIZE; + + memcpy(msg.data, &debug_msg_data, 8); - queue_can_msg(bms_can_msgs[DEBUG]); + queue_can_msg(msg); } void compute_send_cell_data_message(bool alpha, uint16_t temperature, diff --git a/Core/Src/main.c b/Core/Src/main.c index f3230a68..28df1f08 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -25,6 +25,7 @@ #include "shep_tasks.h" #include "assert.h" +#include "string.h" #include "serialPrintResult.h" @@ -60,6 +61,7 @@ ADC_HandleTypeDef hadc1; ADC_HandleTypeDef hadc2; DMA_HandleTypeDef hdma_adc1; +DMA_HandleTypeDef hdma_adc2; CAN_HandleTypeDef hcan1; CAN_HandleTypeDef hcan2; @@ -320,7 +322,8 @@ int main(void) acc_data_t *acc_data = malloc(sizeof(acc_data_t)); acc_data->is_charger_connected = false; - acc_data->fault_code = FAULTS_CLEAR; + acc_data->fault_code_crit = FAULTS_CLEAR; + acc_data->fault_code_noncrit = FAULTS_CLEAR; /* USER CODE END Init */ @@ -513,13 +516,13 @@ static void MX_ADC1_Init(void) hadc1.Instance = ADC1; hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2; hadc1.Init.Resolution = ADC_RESOLUTION_12B; - hadc1.Init.ScanConvMode = DISABLE; + hadc1.Init.ScanConvMode = ENABLE; hadc1.Init.ContinuousConvMode = DISABLE; hadc1.Init.DiscontinuousConvMode = DISABLE; hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; - hadc1.Init.NbrOfConversion = 1; + hadc1.Init.NbrOfConversion = 2; hadc1.Init.DMAContinuousRequests = ENABLE; hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV; if (HAL_ADC_Init(&hadc1) != HAL_OK) @@ -536,6 +539,15 @@ static void MX_ADC1_Init(void) { Error_Handler(); } + + /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. + */ + sConfig.Channel = ADC_CHANNEL_9; + sConfig.Rank = 2; + if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) + { + Error_Handler(); + } /* USER CODE BEGIN ADC1_Init 2 */ /* USER CODE END ADC1_Init 2 */ @@ -1185,6 +1197,9 @@ static void MX_DMA_Init(void) /* DMA2_Stream0_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 5, 0); HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn); + /* DMA2_Stream2_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, 5, 0); + HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn); } @@ -1291,6 +1306,37 @@ void watchdog_pet(void) } +struct __attribute__((__packed__)) git_version_data { + uint8_t git_major_version; + uint8_t git_minor_version; + uint8_t git_patch_version; + bool git_is_upstream_clean; + bool git_is_local_clean; + } git_version_data; + + struct __attribute__((__packed__)) git_hash_data { + uint32_t git_shorthash; + uint32_t git_authorhash; + } git_hash_data; + +/** + * @brief Sends git version infomation as a can message + */ +void send_git_version_message() { + //const struct git_hash_data git_hash_data2 = {GIT_SHORTHASH , GIT_AUTHORHASH}; + const struct git_version_data git_version_data2 = {GIT_MAJOR_VERSION , GIT_MINOR_VERSION, GIT_PATCH_VERSION, GIT_IS_UPSTREAM_CLEAN, GIT_IS_LOCAL_CLEAN}; + + can_msg_t msg1 = { .id = 0x698, .len = sizeof(git_version_data2)}; + //can_msg_t msg2 = { .id = 0x699, .len = sizeof(git_hash_data2)}; + + memcpy(&msg1.data, &git_version_data2, sizeof(git_version_data2)); + //memcpy(&msg2.data, &git_hash_data2, sizeof(git_hash_data2)); + + queue_can_msg(msg1); + //queue_can_msg(msg2); + +} + /* USER CODE END 4 */ /* USER CODE BEGIN Header_StartDefaultTask */ @@ -1324,7 +1370,10 @@ void StartDefaultTask(void *argument) compute_send_bms_status_message(bmsdata, current_state, segment_is_balancing(bmsdata->chips)); + compute_send_fault_status_message(bmsdata); + send_git_version_message(); + HAL_IWDG_Refresh(&hiwdg); osDelay(1000); diff --git a/Core/Src/stateMachine.c b/Core/Src/stateMachine.c index a77906b1..6c719ad0 100644 --- a/Core/Src/stateMachine.c +++ b/Core/Src/stateMachine.c @@ -35,6 +35,14 @@ const bool valid_transition_from_to[NUM_STATES][NUM_STATES] = { { true, false, false, true } /* FAULTED */ }; +typedef union _bms_fault_t { + uint64_t all; + struct { + uint32_t fault_code_crit; + uint32_t fault_code_noncrit; + } fields; +} bms_fault_t; + /* private function prototypes */ void init_boot(acc_data_t *bmsdata); void init_ready(acc_data_t *bmsdata); @@ -148,11 +156,14 @@ void init_faulted(acc_data_t *bmsdata) void handle_faulted(acc_data_t *bmsdata) { if (entered_faulted) { + previousFault = bmsdata->fault_code_crit; entered_faulted = false; - previousFault = sm_fault_return(bmsdata); + + // uint32_t fault_crit = 0, fault_noncrit = 0; + // previousFault = sm_fault_return(bmsdata, &fault_crit, &fault_noncrit); } - if (bmsdata->fault_code == FAULTS_CLEAR) { + if (bmsdata->fault_code_crit == FAULTS_CLEAR) { compute_set_fault(1); request_transition(bmsdata, BOOT_STATE); return; @@ -166,11 +177,15 @@ void handle_faulted(acc_data_t *bmsdata) void sm_handle_state(acc_data_t *bmsdata) { - bmsdata->fault_code = sm_fault_return(bmsdata); + bms_fault_t faults = { .all = 0 }; + faults.all = sm_fault_return(bmsdata); + + bmsdata->fault_code_crit = faults.fields.fault_code_crit; + bmsdata->fault_code_noncrit = faults.fields.fault_code_noncrit; // calculate_pwm(bmsdata); - if (bmsdata->fault_code != FAULTS_CLEAR) { + if (bmsdata->fault_code_crit != FAULTS_CLEAR) { bmsdata->discharge_limit = 0; request_transition(bmsdata, FAULTED_STATE); } @@ -193,7 +208,7 @@ void request_transition(acc_data_t *bmsdata, BMSState_t next_state) current_state = next_state; } -uint32_t sm_fault_return(acc_data_t *accData) +uint64_t sm_fault_return(acc_data_t *bmsdata) { /* FAULT CHECK (Check for fuckies) */ @@ -207,22 +222,26 @@ uint32_t sm_fault_return(acc_data_t *accData) static fault_eval_t *fault_table = NULL; static acc_data_t *fault_data = NULL; - fault_data = accData; + static uint32_t fault_status_crit = 0; + static uint32_t fault_status_noncrit = 0; + + if (!fault_data) + fault_data = bmsdata; if (!fault_table) { /* Note that we are only allocating this table once at runtime, so there is - * no need to free it */ + * no need to free it */ fault_table = (fault_eval_t *)malloc(NUM_FAULTS * sizeof(fault_eval_t)); // clang-format off - // ___________FAULT ID____________ __________TIMER___________ _____________DATA________________ __OPERATOR__ __________________________THRESHOLD____________________________ _______TIMER LENGTH_________ _____________FAULT CODE_________________ ___OPERATOR 2__ _______________DATA 2______________ __THRESHOLD 2__ - fault_table[0] = (fault_eval_t) {.id = "Discharge Current Limit", .timer = ovr_curr_timer, .data_1 = fault_data->pack_current, .optype_1 = GT, .lim_1 = (fault_data->discharge_limit + DCDC_CURRENT_DRAW)*10 * CURR_ERR_MARG, .timeout = OVER_CURR_TIME, .code = DISCHARGE_LIMIT_ENFORCEMENT_FAULT, .optype_2 = NOP/* ---------------------------UNUSED------------------- */ }; - fault_table[1] = (fault_eval_t) {.id = "Charge Current Limit", .timer = ovr_chgcurr_timer, .data_1 = fault_data->pack_current, .optype_1 = GT, .lim_1 = (fault_data->charge_limit)*10, .timeout = OVER_CHG_CURR_TIME, .code = CHARGE_LIMIT_ENFORCEMENT_FAULT, .optype_2 = LT, .data_2 = fault_data->pack_current, .lim_2 = 0 }; - fault_table[2] = (fault_eval_t) {.id = "Low Cell Voltage", .timer = undr_volt_timer, .data_1 = fault_data->min_voltage.val, .optype_1 = LT, .lim_1 = MIN_VOLT * 10000, .timeout = UNDER_VOLT_TIME, .code = CELL_VOLTAGE_TOO_LOW, .optype_2 = NOP/* ---------------------------UNUSED-------------------*/ }; - fault_table[3] = (fault_eval_t) {.id = "High Cell Voltage", .timer = ovr_chgvolt_timer, .data_1 = fault_data->max_voltage.val, .optype_1 = GT, .lim_1 = MAX_CHARGE_VOLT * 10000, .timeout = OVER_VOLT_TIME, .code = CELL_VOLTAGE_TOO_HIGH, .optype_2 = NOP/* ---------------------------UNUSED-------------------*/ }; - fault_table[4] = (fault_eval_t) {.id = "High Cell Voltage", .timer = ovr_volt_timer, .data_1 = fault_data->max_voltage.val, .optype_1 = GT, .lim_1 = MAX_VOLT * 10000, .timeout = OVER_VOLT_TIME, .code = CELL_VOLTAGE_TOO_HIGH, .optype_2 = EQ, .data_2 = fault_data->is_charger_connected, .lim_2 = false }; - fault_table[5] = (fault_eval_t) {.id = "High Temp", .timer = high_temp_timer, .data_1 = fault_data->max_temp.val, .optype_1 = GT, .lim_1 = MAX_CELL_TEMP, .timeout = HIGH_TEMP_TIME, .code = PACK_TOO_HOT, .optype_2 = NOP/* ----------------------------------------------------*/ }; - fault_table[6] = (fault_eval_t) {.id = "Extremely Low Voltage", .timer = low_cell_timer, .data_1 = fault_data->min_voltage.val, .optype_1 = LT, .lim_1 = 900, .timeout = LOW_CELL_TIME, .code = LOW_CELL_VOLTAGE, .optype_2 = NOP/* --------------------------UNUSED--------------------*/ }; + // ___________FAULT ID____________ __________TIMER___________ _____________DATA________________ __OPERATOR__ ____________________________________THRESHOLD____________________________ _______TIMER LENGTH_________ _____________FAULT CODE_________________ ___OPERATOR 2__ ________________________DATA 2______________ __THRESHOLD 2_____ ______CRITICAL________ + fault_table[0] = (fault_eval_t) {.id = "Discharge Current Limit", .timer = ovr_curr_timer, .data_1 = fault_data->pack_current, .optype_1 = GT, .lim_1 = (fault_data->discharge_limit + DCDC_CURRENT_DRAW)*10 * CURR_ERR_MARG, .timeout = OVER_CURR_TIME, .code = DISCHARGE_LIMIT_ENFORCEMENT_FAULT, .optype_2 = NOP/* ------------------------------UNUSED-------------------------*/, .is_critical = true }; + fault_table[1] = (fault_eval_t) {.id = "Charge Current Limit", .timer = ovr_chgcurr_timer, .data_1 = fault_data->pack_current, .optype_1 = GT, .lim_1 = (fault_data->charge_limit)*10, .timeout = OVER_CHG_CURR_TIME, .code = CHARGE_LIMIT_ENFORCEMENT_FAULT, .optype_2 = LT, .data_2 = fault_data->pack_current, .lim_2 = 0, .is_critical = true }; + fault_table[2] = (fault_eval_t) {.id = "Low Cell Voltage", .timer = undr_volt_timer, .data_1 = fault_data->min_voltage.val, .optype_1 = LT, .lim_1 = MIN_VOLT * 10000, .timeout = UNDER_VOLT_TIME, .code = CELL_VOLTAGE_TOO_LOW, .optype_2 = NOP/* ------------------------------UNUSED-------------------------*/, .is_critical = true }; + fault_table[3] = (fault_eval_t) {.id = "High Cell Voltage", .timer = ovr_chgvolt_timer, .data_1 = fault_data->max_voltage.val, .optype_1 = GT, .lim_1 = MAX_CHARGE_VOLT * 10000, .timeout = OVER_VOLT_TIME, .code = CELL_VOLTAGE_TOO_HIGH, .optype_2 = NOP/* ------------------------------UNUSED-------------------------*/, .is_critical = true }; + fault_table[4] = (fault_eval_t) {.id = "High Cell Voltage", .timer = ovr_volt_timer, .data_1 = fault_data->max_voltage.val, .optype_1 = GT, .lim_1 = MAX_VOLT * 10000, .timeout = OVER_VOLT_TIME, .code = CELL_VOLTAGE_TOO_HIGH, .optype_2 = EQ, .data_2 = fault_data->is_charger_connected, .lim_2 = false, .is_critical = true }; + fault_table[5] = (fault_eval_t) {.id = "High Temp", .timer = high_temp_timer, .data_1 = fault_data->max_temp.val, .optype_1 = GT, .lim_1 = MAX_CELL_TEMP, .timeout = HIGH_TEMP_TIME, .code = PACK_TOO_HOT, .optype_2 = NOP/* ------------------------------UNUSED-------------------------*/, .is_critical = true }; + fault_table[6] = (fault_eval_t) {.id = "Extremely Low Voltage", .timer = low_cell_timer, .data_1 = fault_data->min_voltage.val, .optype_1 = LT, .lim_1 = 900, .timeout = LOW_CELL_TIME, .code = LOW_CELL_VOLTAGE, .optype_2 = NOP/* ------------------------------UNUSED-------------------------*/, .is_critical = true }; fault_table[7] = (fault_eval_t) {.id = NULL}; cancel_timer(&ovr_curr_timer); @@ -233,9 +252,7 @@ uint32_t sm_fault_return(acc_data_t *accData) cancel_timer(&low_cell_timer); cancel_timer(&high_temp_timer); // clang-format on - } - - else { + } else { fault_table[0].data_1 = fault_data->pack_current; fault_table[0].lim_1 = (fault_data->discharge_limit + DCDC_CURRENT_DRAW) * 10 * @@ -250,66 +267,90 @@ uint32_t sm_fault_return(acc_data_t *accData) fault_table[6].data_1 = fault_data->min_voltage.val; } - static uint32_t fault_status = 0; int incr = 0; while (fault_table[incr].id != NULL) { - fault_status |= sm_fault_eval(&fault_table[incr]); + uint32_t item_code = fault_table[incr].code; + if (sm_fault_eval(&fault_table[incr])) { + if (fault_table[incr].is_critical) { + fault_status_crit |= item_code; + } else { + fault_status_noncrit |= item_code; + } + } else { + // Clear bit for non-critical faults + if (!fault_table[incr].is_critical) { + fault_status_noncrit &= ~item_code; + } + } incr++; } - // TODO: Remove This !!!! - fault_status &= ~DISCHARGE_LIMIT_ENFORCEMENT_FAULT; - return fault_status; + + // TODO: Remove This !!!! (because this is actually a non-critical fault?) + // fault_status &= ~DISCHARGE_LIMIT_ENFORCEMENT_FAULT; + + bms_fault_t return_faults = { .all = 0 }; + return_faults.fields.fault_code_crit = fault_status_crit; + return_faults.fields.fault_code_noncrit = fault_status_noncrit; + + return return_faults.all; } -uint32_t sm_fault_eval(fault_eval_t *index) +bool sm_fault_eval(fault_eval_t *item) { + enum { + FAULT_STAT_TIMER_START = 1, + FAULT_STAT_FAULTED = 2, + }; + bool condition1; bool condition2; // clang-format off - switch (index->optype_1) + switch (item->optype_1) { - case GT: condition1 = index->data_1 > index->lim_1; break; - case LT: condition1 = index->data_1 < index->lim_1; break; - case GE: condition1 = index->data_1 >= index->lim_1; break; - case LE: condition1 = index->data_1 <= index->lim_1; break; - case EQ: condition1 = index->data_1 == index->lim_1; break; - case NEQ: condition1 = index->data_1 != index->lim_1; break; + case GT: condition1 = item->data_1 > item->lim_1; break; + case LT: condition1 = item->data_1 < item->lim_1; break; + case GE: condition1 = item->data_1 >= item->lim_1; break; + case LE: condition1 = item->data_1 <= item->lim_1; break; + case EQ: condition1 = item->data_1 == item->lim_1; break; + case NEQ: condition1 = item->data_1 != item->lim_1; break; case NOP: condition1 = false; default: condition1 = false; } - switch (index->optype_2) + switch (item->optype_2) { - case GT: condition2 = index->data_2 > index->lim_2; break; - case LT: condition2 = index->data_2 < index->lim_2; break; - case GE: condition2 = index->data_2 >= index->lim_2; break; - case LE: condition2 = index->data_2 <= index->lim_2; break; - case EQ: condition2 = index->data_2 == index->lim_2; break; - case NEQ: condition2 = index->data_2 != index->lim_2; break; + case GT: condition2 = item->data_2 > item->lim_2; break; + case LT: condition2 = item->data_2 < item->lim_2; break; + case GE: condition2 = item->data_2 >= item->lim_2; break; + case LE: condition2 = item->data_2 <= item->lim_2; break; + case EQ: condition2 = item->data_2 == item->lim_2; break; + case NEQ: condition2 = item->data_2 != item->lim_2; break; case NOP: condition2 = false; default: condition2 = false; } // clang-format on bool fault_present = ((condition1 && condition2) || - (condition1 && (index->optype_2 == NOP))); - if ((!(is_timer_active(&index->timer))) && !fault_present) { + (condition1 && (item->optype_2 == NOP))); + if ((!(is_timer_active(&item->timer))) && !fault_present) { return 0; } - if (is_timer_active(&index->timer)) { + if (is_timer_active(&item->timer)) { if (!fault_present) { - printf("\t\t\t*******Fault cleared: %s\r\n", index->id); - cancel_timer(&index->timer); + printf("\t\t\t*******Fault cleared: %s\r\n", item->id); + cancel_timer(&item->timer); + compute_send_fault_timer_message(0, item->code, + item->data_1); return 0; } - if (is_timer_expired(&index->timer) && fault_present) { - printf("\t\t\t*******Faulted: %s\r\n", index->id); - compute_send_fault_message(2, index->data_1, - index->lim_1); - return index->code; + if (is_timer_expired(&item->timer) && fault_present) { + printf("\t\t\t*******Faulted: %s\r\n", item->id); + compute_send_fault_timer_message(2, item->code, + item->data_1); + return item->code; } else @@ -317,20 +358,17 @@ uint32_t sm_fault_eval(fault_eval_t *index) } - else if (!is_timer_active(&index->timer) && fault_present) { - printf("\t\t\t*******Starting fault timer: %s\r\n", index->id); - start_timer(&index->timer, index->timeout); - if (index->code == DISCHARGE_LIMIT_ENFORCEMENT_FAULT) { - compute_send_fault_message(1, index->data_1, - index->lim_1); - } + else if (!is_timer_active(&item->timer) && fault_present) { + printf("\t\t\t*******Starting fault timer: %s\r\n", item->id); + start_timer(&item->timer, item->timeout); + compute_send_fault_timer_message(1, item->code, item->data_1); return 0; } - /* if (index->code == CELL_VOLTAGE_TOO_LOW) { + /* if (item->code == CELL_VOLTAGE_TOO_LOW) { printf("\t\t\t*******Not fautled!!!!!\t%d\r\n", - !is_timer_active(&index->timer) && condition1 && condition2); printf("More - stats...\t:%d\t%d\r\n", is_timer_expired(&index->timer), index->timer.active); + !is_timer_active(&item->timer) && condition1 && condition2); printf("More + stats...\t:%d\t%d\r\n", is_timer_expired(&item->timer), item->timer.active); } */ printf("err should not get here"); return 0; diff --git a/Core/Src/stm32f4xx_hal_msp.c b/Core/Src/stm32f4xx_hal_msp.c index 116021cd..eed59f5f 100644 --- a/Core/Src/stm32f4xx_hal_msp.c +++ b/Core/Src/stm32f4xx_hal_msp.c @@ -25,6 +25,8 @@ /* USER CODE END Includes */ extern DMA_HandleTypeDef hdma_adc1; +extern DMA_HandleTypeDef hdma_adc2; + extern DMA_HandleTypeDef hdma_uart4_tx; /* Private typedef -----------------------------------------------------------*/ @@ -126,7 +128,7 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc) hdma_adc1.Init.MemInc = DMA_MINC_ENABLE; hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; - hdma_adc1.Init.Mode = DMA_NORMAL; + hdma_adc1.Init.Mode = DMA_CIRCULAR; hdma_adc1.Init.Priority = DMA_PRIORITY_LOW; hdma_adc1.Init.FIFOMode = DMA_FIFOMODE_DISABLE; if (HAL_DMA_Init(&hdma_adc1) != HAL_OK) @@ -157,6 +159,25 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc) GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(I_SenseB0_GPIO_Port, &GPIO_InitStruct); + /* ADC2 DMA Init */ + /* ADC2 Init */ + hdma_adc2.Instance = DMA2_Stream2; + hdma_adc2.Init.Channel = DMA_CHANNEL_1; + hdma_adc2.Init.Direction = DMA_PERIPH_TO_MEMORY; + hdma_adc2.Init.PeriphInc = DMA_PINC_DISABLE; + hdma_adc2.Init.MemInc = DMA_MINC_ENABLE; + hdma_adc2.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; + hdma_adc2.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; + hdma_adc2.Init.Mode = DMA_CIRCULAR; + hdma_adc2.Init.Priority = DMA_PRIORITY_LOW; + hdma_adc2.Init.FIFOMode = DMA_FIFOMODE_DISABLE; + if (HAL_DMA_Init(&hdma_adc2) != HAL_OK) + { + Error_Handler(); + } + + __HAL_LINKDMA(hadc,DMA_Handle,hdma_adc2); + /* USER CODE BEGIN ADC2_MspInit 1 */ /* USER CODE END ADC2_MspInit 1 */ @@ -207,6 +228,8 @@ void HAL_ADC_MspDeInit(ADC_HandleTypeDef* hadc) */ HAL_GPIO_DeInit(I_SenseB0_GPIO_Port, I_SenseB0_Pin); + /* ADC2 DMA DeInit */ + HAL_DMA_DeInit(hadc->DMA_Handle); /* USER CODE BEGIN ADC2_MspDeInit 1 */ /* USER CODE END ADC2_MspDeInit 1 */ diff --git a/Core/Src/stm32f4xx_it.c b/Core/Src/stm32f4xx_it.c index eea515e8..af740ab4 100644 --- a/Core/Src/stm32f4xx_it.c +++ b/Core/Src/stm32f4xx_it.c @@ -57,6 +57,7 @@ /* External variables --------------------------------------------------------*/ extern DMA_HandleTypeDef hdma_adc1; +extern DMA_HandleTypeDef hdma_adc2; extern CAN_HandleTypeDef hcan1; extern CAN_HandleTypeDef hcan2; extern DMA_HandleTypeDef hdma_uart4_tx; @@ -235,6 +236,20 @@ void DMA2_Stream0_IRQHandler(void) /* USER CODE END DMA2_Stream0_IRQn 1 */ } +/** + * @brief This function handles DMA2 stream2 global interrupt. + */ +void DMA2_Stream2_IRQHandler(void) +{ + /* USER CODE BEGIN DMA2_Stream2_IRQn 0 */ + + /* USER CODE END DMA2_Stream2_IRQn 0 */ + HAL_DMA_IRQHandler(&hdma_adc2); + /* USER CODE BEGIN DMA2_Stream2_IRQn 1 */ + + /* USER CODE END DMA2_Stream2_IRQn 1 */ +} + /** * @brief This function handles CAN2 RX0 interrupts. */ diff --git a/Makefile b/Makefile index 91cb92ff..c7878836 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,32 @@ ###################################### TARGET = shepherd2 +###################################### +# git +###################################### +GIT_MAJOR_VERSION := $(shell git describe --tags --abbrev=0 | cut -c2- | cut -d'.' -f1) +GIT_MINOR_VERSION := $(shell git describe --tags --abbrev=0 | cut -c2- | cut -d'.' -f2) +GIT_PATCH_VERSION := $(shell git describe --tags --abbrev=0 | cut -c2- | cut -d'.' -f3) +GIT_IS_UPSTREAM_CLEAN := $(shell bash -c 'git merge-base --is-ancestor HEAD @{u} && echo 0 || echo 1') +GIT_IS_LOCAL_CLEAN := $(shell bash -c 'test -z "$$(git status --porcelain)" && echo 0 || echo 1') +GIT_SHORTHASH := $(shell git rev-parse --short=8 HEAD) +GIT_AUTHORHASH := $(shell bash -c 'git log -n 1 --format=%h --author=$(git config --get user.name)') + + +all: + @echo "VERSION: $(GIT_MAJOR_VERSION).$(GIT_MINOR_VERSION).$(GIT_PATCH_VERSION)" + @echo "UNPUSHED CHANGES: $(GIT_IS_UPSTREAM_CLEAN)" + @echo "UNCOMMITTED CHANGES: $(GIT_IS_LOCAL_CLEAN)" + @echo "COMMIT HASH: $(GIT_SHORTHASH)" + @echo "AUTHOR HASH: $(GIT_AUTHORHASH)" + +CFLAGS += -DGIT_MAJOR_VERSION=$(GIT_MAJOR_VERSION) \ + -DGIT_MINOR_VERSION=$(GIT_MINOR_VERSION) \ + -DGIT_PATCH_VERSION=$(GIT_PATCH_VERSION) \ + -DGIT_IS_UPSTREAM_CLEAN=$(GIT_IS_UPSTREAM_CLEAN) \ + -DGIT_IS_LOCAL_CLEAN=$(GIT_IS_LOCAL_CLEAN) \ + -DGIT_SHORTHASH=$(GIT_SHORTHASH) \ + -DGIT_AUTHORHASH=$(GIT_AUTHORHASH) ###################################### # building variables diff --git a/shepherd2.ioc b/shepherd2.ioc index 028e1f8f..f4626d26 100644 --- a/shepherd2.ioc +++ b/shepherd2.ioc @@ -1,10 +1,14 @@ #MicroXplorer Configuration settings - do not modify ADC1.Channel-0\#ChannelRegularConversion=ADC_CHANNEL_15 +ADC1.Channel-1\#ChannelRegularConversion=ADC_CHANNEL_9 ADC1.DMAContinuousRequests=ENABLE -ADC1.IPParameters=Rank-0\#ChannelRegularConversion,Channel-0\#ChannelRegularConversion,SamplingTime-0\#ChannelRegularConversion,NbrOfConversionFlag,master,DMAContinuousRequests +ADC1.IPParameters=Rank-0\#ChannelRegularConversion,Channel-0\#ChannelRegularConversion,SamplingTime-0\#ChannelRegularConversion,NbrOfConversionFlag,master,DMAContinuousRequests,Rank-1\#ChannelRegularConversion,Channel-1\#ChannelRegularConversion,SamplingTime-1\#ChannelRegularConversion,NbrOfConversion +ADC1.NbrOfConversion=2 ADC1.NbrOfConversionFlag=1 ADC1.Rank-0\#ChannelRegularConversion=1 +ADC1.Rank-1\#ChannelRegularConversion=2 ADC1.SamplingTime-0\#ChannelRegularConversion=ADC_SAMPLETIME_3CYCLES +ADC1.SamplingTime-1\#ChannelRegularConversion=ADC_SAMPLETIME_3CYCLES ADC1.master=1 ADC2.Channel-1\#ChannelRegularConversion=ADC_CHANNEL_8 ADC2.IPParameters=Rank-1\#ChannelRegularConversion,Channel-1\#ChannelRegularConversion,SamplingTime-1\#ChannelRegularConversion,NbrOfConversionFlag @@ -49,14 +53,25 @@ Dma.ADC1.0.FIFOMode=DMA_FIFOMODE_DISABLE Dma.ADC1.0.Instance=DMA2_Stream0 Dma.ADC1.0.MemDataAlignment=DMA_MDATAALIGN_WORD Dma.ADC1.0.MemInc=DMA_MINC_ENABLE -Dma.ADC1.0.Mode=DMA_NORMAL +Dma.ADC1.0.Mode=DMA_CIRCULAR Dma.ADC1.0.PeriphDataAlignment=DMA_PDATAALIGN_WORD Dma.ADC1.0.PeriphInc=DMA_PINC_DISABLE Dma.ADC1.0.Priority=DMA_PRIORITY_LOW Dma.ADC1.0.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority,FIFOMode +Dma.ADC2.2.Direction=DMA_PERIPH_TO_MEMORY +Dma.ADC2.2.FIFOMode=DMA_FIFOMODE_DISABLE +Dma.ADC2.2.Instance=DMA2_Stream2 +Dma.ADC2.2.MemDataAlignment=DMA_MDATAALIGN_WORD +Dma.ADC2.2.MemInc=DMA_MINC_ENABLE +Dma.ADC2.2.Mode=DMA_CIRCULAR +Dma.ADC2.2.PeriphDataAlignment=DMA_PDATAALIGN_WORD +Dma.ADC2.2.PeriphInc=DMA_PINC_DISABLE +Dma.ADC2.2.Priority=DMA_PRIORITY_LOW +Dma.ADC2.2.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority,FIFOMode Dma.Request0=ADC1 Dma.Request1=UART4_TX -Dma.RequestsNb=2 +Dma.Request2=ADC2 +Dma.RequestsNb=3 Dma.UART4_TX.1.Direction=DMA_MEMORY_TO_PERIPH Dma.UART4_TX.1.FIFOMode=DMA_FIFOMODE_DISABLE Dma.UART4_TX.1.Instance=DMA1_Stream4 @@ -173,6 +188,7 @@ NVIC.CAN2_RX0_IRQn=true\:5\:0\:false\:false\:true\:true\:true\:true\:true NVIC.CAN2_RX1_IRQn=true\:5\:0\:false\:false\:true\:true\:true\:true\:true NVIC.DMA1_Stream4_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true\:true NVIC.DMA2_Stream0_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true\:true +NVIC.DMA2_Stream2_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true\:true NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false NVIC.ForceEnableDMAVector=true NVIC.HardFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false