Compare commits
2 Commits
3f8e1290fa
...
468ba1d7e7
Author | SHA1 | Date | |
---|---|---|---|
468ba1d7e7 | |||
f910f899a4 |
@ -10,11 +10,11 @@
|
|||||||
#define NTC_B1 0.000300131f
|
#define NTC_B1 0.000300131f
|
||||||
#define NTC_C1 5.08516E-06f
|
#define NTC_C1 5.08516E-06f
|
||||||
#define NTC_D1 2.18765E-07f
|
#define NTC_D1 2.18765E-07f
|
||||||
|
|
||||||
#define VREF 3000.0f // 3V
|
#define VREF 3000.0f // 3V
|
||||||
#define TEMP_CONV 100.0f // 655.35°C max
|
#define TEMP_CONV 100.0f // 655.35°C max
|
||||||
#define CELSIUS_TO_KELVIN 273.15f
|
#define CELSIUS_TO_KELVIN 273.15f
|
||||||
#define CELSIUS_TO_KELVIN_SCALED CELSIUS_TO_KELVIN * TEMP_CONV
|
#define CELSIUS_TO_KELVIN_SCALED (CELSIUS_TO_KELVIN * TEMP_CONV)
|
||||||
|
|
||||||
// More efficient (?) calc using:
|
// More efficient (?) calc using:
|
||||||
// R_T = R_Pullup / (V_REF / ADC - 1)
|
// R_T = R_Pullup / (V_REF / ADC - 1)
|
||||||
@ -23,8 +23,9 @@
|
|||||||
|
|
||||||
[[gnu::optimize("fast-math")]]
|
[[gnu::optimize("fast-math")]]
|
||||||
static inline uint16_t ntc_mv_to_celsius(int16_t adc) {
|
static inline uint16_t ntc_mv_to_celsius(int16_t adc) {
|
||||||
const float log_ohms = logf(1 / ((VREF / adc) - 1));
|
const float log_ohms = logf(1 / ((VREF / adc) - 1));
|
||||||
return (uint16_t) (TEMP_CONV / (NTC_A1 + NTC_B1 * log_ohms + NTC_C1 * log_ohms * log_ohms + NTC_D1 * log_ohms * log_ohms * log_ohms) - CELSIUS_TO_KELVIN_SCALED);
|
return (uint16_t)(TEMP_CONV / (NTC_A1 + NTC_B1 * log_ohms + NTC_C1 * log_ohms * log_ohms +
|
||||||
|
NTC_D1 * log_ohms * log_ohms * log_ohms) - CELSIUS_TO_KELVIN_SCALED);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
// Lookup Table coming soon; not really needed but fun?
|
// Lookup Table coming soon; not really needed but fun?
|
||||||
|
@ -12,8 +12,8 @@
|
|||||||
#define CAN_ID_SLAVE_STATUS_BASE 0x080
|
#define CAN_ID_SLAVE_STATUS_BASE 0x080
|
||||||
#define CAN_ID_AMS_SIGNALS 0x090
|
#define CAN_ID_AMS_SIGNALS 0x090
|
||||||
|
|
||||||
//TEMPORARY!!
|
// TEMPORARY!!
|
||||||
#define CAN_ID_AMS_DETAILS 0x091
|
#define CAN_ID_AMS_DETAILS 0x091
|
||||||
#define CAN_ID_AMS_DETAILS_FC 0x092
|
#define CAN_ID_AMS_DETAILS_FC 0x092
|
||||||
|
|
||||||
#define CAN_ID_SLAVE_LOG 0x4F4
|
#define CAN_ID_SLAVE_LOG 0x4F4
|
||||||
|
@ -5,9 +5,9 @@
|
|||||||
#define N_BMS 1
|
#define N_BMS 1
|
||||||
#define N_CELLS 16
|
#define N_CELLS 16
|
||||||
#define ADBMS_MAX_CHIP_TEMP 1100 // max temperature of ADBMS6830B (not battery) in C
|
#define ADBMS_MAX_CHIP_TEMP 1100 // max temperature of ADBMS6830B (not battery) in C
|
||||||
#define ADBMS_SPI_TIMEOUT 50 // Timeout in ms
|
#define ADBMS_SPI_TIMEOUT 50 // Timeout in ms
|
||||||
#define DEFAULT_UV 3000 // mV
|
#define DEFAULT_UV 3000 // mV
|
||||||
#define DEFAULT_OV 4200 // mV
|
#define DEFAULT_OV 4200 // mV
|
||||||
#define ERROR_TIME_THRESH 150 // ms
|
#define ERROR_TIME_THRESH 150 // ms
|
||||||
|
|
||||||
#endif //__CONFIG_ADBMS6830_H
|
#endif //__CONFIG_ADBMS6830_H
|
||||||
|
@ -6,48 +6,48 @@
|
|||||||
#include "stm32h7xx_hal.h"
|
#include "stm32h7xx_hal.h"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
IMD_STATE_UNKNOWN,
|
IMD_STATE_UNKNOWN,
|
||||||
IMD_STATE_SHORTCIRCUIT_SUPPLY,
|
IMD_STATE_SHORTCIRCUIT_SUPPLY,
|
||||||
IMD_STATE_SHORTCIRCUIT_GND,
|
IMD_STATE_SHORTCIRCUIT_GND,
|
||||||
IMD_STATE_NORMAL,
|
IMD_STATE_NORMAL,
|
||||||
IMD_STATE_UNDERVOLTAGE,
|
IMD_STATE_UNDERVOLTAGE,
|
||||||
IMD_STATE_SST,
|
IMD_STATE_SST,
|
||||||
IMD_STATE_DEV_ERROR,
|
IMD_STATE_DEV_ERROR,
|
||||||
IMD_STATE_GND_FAULT,
|
IMD_STATE_GND_FAULT,
|
||||||
} IMDState;
|
} IMDState;
|
||||||
|
|
||||||
static inline const char *IMDStateToString(IMDState state) {
|
static inline const char *IMDStateToString(IMDState state) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case IMD_STATE_UNKNOWN:
|
case IMD_STATE_UNKNOWN:
|
||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
case IMD_STATE_SHORTCIRCUIT_SUPPLY:
|
case IMD_STATE_SHORTCIRCUIT_SUPPLY:
|
||||||
return "SHORTCIRCUIT_SUPPLY";
|
return "SHORTCIRCUIT_SUPPLY";
|
||||||
case IMD_STATE_SHORTCIRCUIT_GND:
|
case IMD_STATE_SHORTCIRCUIT_GND:
|
||||||
return "SHORTCIRCUIT_GND";
|
return "SHORTCIRCUIT_GND";
|
||||||
case IMD_STATE_NORMAL:
|
case IMD_STATE_NORMAL:
|
||||||
return "NORMAL";
|
return "NORMAL";
|
||||||
case IMD_STATE_UNDERVOLTAGE:
|
case IMD_STATE_UNDERVOLTAGE:
|
||||||
return "UNDERVOLTAGE";
|
return "UNDERVOLTAGE";
|
||||||
case IMD_STATE_SST:
|
case IMD_STATE_SST:
|
||||||
return "SST";
|
return "SST";
|
||||||
case IMD_STATE_DEV_ERROR:
|
case IMD_STATE_DEV_ERROR:
|
||||||
return "DEV_ERROR";
|
return "DEV_ERROR";
|
||||||
case IMD_STATE_GND_FAULT:
|
case IMD_STATE_GND_FAULT:
|
||||||
return "GND_FAULT";
|
return "GND_FAULT";
|
||||||
default:
|
default:
|
||||||
return "INVALID STATE";
|
return "INVALID STATE";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int ok;
|
int ok;
|
||||||
|
|
||||||
IMDState state;
|
IMDState state;
|
||||||
uint32_t r_iso;
|
uint32_t r_iso;
|
||||||
|
|
||||||
uint32_t freq;
|
uint32_t freq;
|
||||||
uint32_t duty_cycle;
|
uint32_t duty_cycle;
|
||||||
uint32_t last_high;
|
uint32_t last_high;
|
||||||
} IMDData;
|
} IMDData;
|
||||||
|
|
||||||
extern IMDData imd_data;
|
extern IMDData imd_data;
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
/* USER CODE BEGIN Header */
|
/* USER CODE BEGIN Header */
|
||||||
/**
|
/**
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* @file : main.h
|
* @file : main.h
|
||||||
* @brief : Header for main.c file.
|
* @brief : Header for main.c file.
|
||||||
* This file contains the common defines of the application.
|
* This file contains the common defines of the application.
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* @attention
|
* @attention
|
||||||
*
|
*
|
||||||
* Copyright (c) 2025 STMicroelectronics.
|
* Copyright (c) 2025 STMicroelectronics.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* This software is licensed under terms that can be found in the LICENSE file
|
* This software is licensed under terms that can be found in the LICENSE file
|
||||||
* in the root directory of this software component.
|
* in the root directory of this software component.
|
||||||
* If no LICENSE file comes with this software, it is provided AS-IS.
|
* If no LICENSE file comes with this software, it is provided AS-IS.
|
||||||
*
|
*
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
/* USER CODE END Header */
|
/* USER CODE END Header */
|
||||||
|
|
||||||
/* Define to prevent recursive inclusion -------------------------------------*/
|
/* Define to prevent recursive inclusion -------------------------------------*/
|
||||||
|
@ -10,17 +10,17 @@
|
|||||||
#define SHUNT_THRESH_OVERTEMP 1000 // 1/10 °C
|
#define SHUNT_THRESH_OVERTEMP 1000 // 1/10 °C
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int32_t current; // mA
|
int32_t current; // mA
|
||||||
int32_t voltage_bat; // mV
|
int32_t voltage_bat; // mV
|
||||||
int32_t voltage_veh; // mV
|
int32_t voltage_veh; // mV
|
||||||
int32_t voltage3; // mV
|
int32_t voltage3; // mV
|
||||||
int32_t busbartemp;
|
int32_t busbartemp;
|
||||||
int32_t power;
|
int32_t power;
|
||||||
int32_t energy;
|
int32_t energy;
|
||||||
float current_counter; // mAs
|
float current_counter; // mAs
|
||||||
|
|
||||||
uint32_t last_message;
|
uint32_t last_message;
|
||||||
uint32_t last_current_message;
|
uint32_t last_current_message;
|
||||||
} ShuntData;
|
} ShuntData;
|
||||||
extern ShuntData shunt_data;
|
extern ShuntData shunt_data;
|
||||||
|
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
/* USER CODE BEGIN Header */
|
/* USER CODE BEGIN Header */
|
||||||
/**
|
/**
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* @file stm32h7xx_it.h
|
* @file stm32h7xx_it.h
|
||||||
* @brief This file contains the headers of the interrupt handlers.
|
* @brief This file contains the headers of the interrupt handlers.
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* @attention
|
* @attention
|
||||||
*
|
*
|
||||||
* Copyright (c) 2025 STMicroelectronics.
|
* Copyright (c) 2025 STMicroelectronics.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* This software is licensed under terms that can be found in the LICENSE file
|
* This software is licensed under terms that can be found in the LICENSE file
|
||||||
* in the root directory of this software component.
|
* in the root directory of this software component.
|
||||||
* If no LICENSE file comes with this software, it is provided AS-IS.
|
* If no LICENSE file comes with this software, it is provided AS-IS.
|
||||||
*
|
*
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
/* USER CODE END Header */
|
/* USER CODE END Header */
|
||||||
|
|
||||||
/* Define to prevent recursive inclusion -------------------------------------*/
|
/* Define to prevent recursive inclusion -------------------------------------*/
|
||||||
@ -22,7 +22,7 @@
|
|||||||
#define __STM32H7xx_IT_H
|
#define __STM32H7xx_IT_H
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Private includes ----------------------------------------------------------*/
|
/* Private includes ----------------------------------------------------------*/
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
#include "stm32h7xx_hal.h"
|
#include "stm32h7xx_hal.h"
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
// Minimum vehicle side voltage to exit precharge
|
// Minimum vehicle side voltage to exit precharge
|
||||||
#define MIN_VEHICLE_SIDE_VOLTAGE 150000 // mV
|
#define MIN_VEHICLE_SIDE_VOLTAGE 150000 // mV
|
||||||
@ -22,56 +22,45 @@
|
|||||||
// Time to wait between closing relays
|
// Time to wait between closing relays
|
||||||
#define RELAY_CLOSE_WAIT 10 // ms
|
#define RELAY_CLOSE_WAIT 10 // ms
|
||||||
|
|
||||||
typedef enum {
|
typedef enum { TS_INACTIVE, TS_ACTIVE, TS_PRECHARGE, TS_DISCHARGE, TS_ERROR, TS_CHARGING_CHECK, TS_CHARGING } TSState;
|
||||||
TS_INACTIVE,
|
|
||||||
TS_ACTIVE,
|
|
||||||
TS_PRECHARGE,
|
|
||||||
TS_DISCHARGE,
|
|
||||||
TS_ERROR,
|
|
||||||
TS_CHARGING_CHECK,
|
|
||||||
TS_CHARGING
|
|
||||||
} TSState;
|
|
||||||
|
|
||||||
static inline const char *TSStateToString(TSState state) {
|
static inline const char *TSStateToString(TSState state) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case TS_INACTIVE:
|
case TS_INACTIVE:
|
||||||
return "INACTIVE";
|
return "INACTIVE";
|
||||||
case TS_ACTIVE:
|
case TS_ACTIVE:
|
||||||
return "ACTIVE";
|
return "ACTIVE";
|
||||||
case TS_PRECHARGE:
|
case TS_PRECHARGE:
|
||||||
return "PRECHARGE";
|
return "PRECHARGE";
|
||||||
case TS_DISCHARGE:
|
case TS_DISCHARGE:
|
||||||
return "DISCHARGE";
|
return "DISCHARGE";
|
||||||
case TS_ERROR:
|
case TS_ERROR:
|
||||||
return "ERROR";
|
return "ERROR";
|
||||||
case TS_CHARGING_CHECK:
|
case TS_CHARGING_CHECK:
|
||||||
return "CHARGING_CHECK";
|
return "CHARGING_CHECK";
|
||||||
case TS_CHARGING:
|
case TS_CHARGING:
|
||||||
return "CHARGING";
|
return "CHARGING";
|
||||||
default:
|
default:
|
||||||
return "INVALID STATE";
|
return "INVALID STATE";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
TS_ERRORKIND_NONE = 0x00,
|
TS_ERRORKIND_NONE = 0x00,
|
||||||
TS_ERRORKIND_CELL_OVERTEMP = 0x01,
|
TS_ERRORKIND_CELL_OVERTEMP = 0x01,
|
||||||
TS_ERRORKIND_SLAVE_PANIC = 0x02,
|
TS_ERRORKIND_SLAVE_PANIC = 0x02,
|
||||||
TS_ERRORKIND_SHUNT_TIMEOUT = 0x03,
|
TS_ERRORKIND_SHUNT_TIMEOUT = 0x03,
|
||||||
TS_ERRORKIND_SHUNT_OVERCURRENT = 0x04,
|
TS_ERRORKIND_SHUNT_OVERCURRENT = 0x04,
|
||||||
TS_ERRORKIND_SHUNT_OVERTEMP = 0x05
|
TS_ERRORKIND_SHUNT_OVERTEMP = 0x05
|
||||||
} TSErrorKind;
|
} TSErrorKind;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum { TS_ERROR_SOURCE_SHUNT = (1 << 0), TS_ERROR_SOURCE_SLAVES = (1 << 1) } TSErrorSource;
|
||||||
TS_ERROR_SOURCE_SHUNT = (1 << 0),
|
|
||||||
TS_ERROR_SOURCE_SLAVES = (1 << 1)
|
|
||||||
} TSErrorSource;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
TSState current_state;
|
TSState current_state;
|
||||||
TSState target_state;
|
TSState target_state;
|
||||||
uint16_t error_source; // TSErrorSource (bitmask)
|
uint16_t error_source; // TSErrorSource (bitmask)
|
||||||
uint16_t error_type; // TSErrorKind
|
uint16_t error_type; // TSErrorKind
|
||||||
} TSStateHandle;
|
} TSStateHandle;
|
||||||
|
|
||||||
extern TSStateHandle ts_state;
|
extern TSStateHandle ts_state;
|
||||||
|
@ -27,7 +27,7 @@ ADBMS_Internal_Status amsStopBalancing();
|
|||||||
|
|
||||||
ADBMS_Internal_Status amsSelfTest();
|
ADBMS_Internal_Status amsSelfTest();
|
||||||
|
|
||||||
ADBMS_Internal_Status amsConfigOverUnderVoltage(uint16_t overVoltage, uint16_t underVoltage); //arguments in mV
|
ADBMS_Internal_Status amsConfigOverUnderVoltage(uint16_t overVoltage, uint16_t underVoltage); // arguments in mV
|
||||||
|
|
||||||
ADBMS_Internal_Status amsCheckUnderOverVoltage(BMS_Chip (*module)[N_BMS]);
|
ADBMS_Internal_Status amsCheckUnderOverVoltage(BMS_Chip (*module)[N_BMS]);
|
||||||
|
|
||||||
|
@ -11,20 +11,20 @@
|
|||||||
#define WRPWMB 0x0021 // Write PWM Register Group B
|
#define WRPWMB 0x0021 // Write PWM Register Group B
|
||||||
#define RDPWMB 0x0023 // Read PWM Register Group B
|
#define RDPWMB 0x0023 // Read PWM Register Group B
|
||||||
|
|
||||||
#define RDCVA 0x0004 // Read Cell Voltage Register Group A
|
#define RDCVA 0x0004 // Read Cell Voltage Register Group A
|
||||||
#define RDCVB 0x0006 // Read Cell Voltage Register Group B
|
#define RDCVB 0x0006 // Read Cell Voltage Register Group B
|
||||||
#define RDCVC 0x0008 // Read Cell Voltage Register Group C
|
#define RDCVC 0x0008 // Read Cell Voltage Register Group C
|
||||||
#define RDCVD 0x000A // Read Cell Voltage Register Group D
|
#define RDCVD 0x000A // Read Cell Voltage Register Group D
|
||||||
#define RDCVE 0x0009 // Read Cell Voltage Register Group E
|
#define RDCVE 0x0009 // Read Cell Voltage Register Group E
|
||||||
#define RDCVF 0x000B // Read Cell Voltage Register Group F
|
#define RDCVF 0x000B // Read Cell Voltage Register Group F
|
||||||
#define RDCVALL 0x000C // Read All Cell Voltage Register Groups
|
#define RDCVALL 0x000C // Read All Cell Voltage Register Groups
|
||||||
|
|
||||||
#define RDACA 0x0044 // Read averaged Cell Voltage Register Group A
|
#define RDACA 0x0044 // Read averaged Cell Voltage Register Group A
|
||||||
#define RDACB 0x0046 // Read averaged Cell Voltage Register Group B
|
#define RDACB 0x0046 // Read averaged Cell Voltage Register Group B
|
||||||
#define RDACC 0x0048 // Read averaged Cell Voltage Register Group C
|
#define RDACC 0x0048 // Read averaged Cell Voltage Register Group C
|
||||||
#define RDACD 0x004A // Read averaged Cell Voltage Register Group D
|
#define RDACD 0x004A // Read averaged Cell Voltage Register Group D
|
||||||
#define RDACE 0x0049 // Read averaged Cell Voltage Register Group E
|
#define RDACE 0x0049 // Read averaged Cell Voltage Register Group E
|
||||||
#define RDACF 0x004B // Read averaged Cell Voltage Register Group F
|
#define RDACF 0x004B // Read averaged Cell Voltage Register Group F
|
||||||
#define RDACALL 0x004C // Read averaged All Cell Voltage Register Groups
|
#define RDACALL 0x004C // Read averaged All Cell Voltage Register Groups
|
||||||
|
|
||||||
#define RDAUXA 0x0019 // Read Auxilliary Register Group A
|
#define RDAUXA 0x0019 // Read Auxilliary Register Group A
|
||||||
@ -44,9 +44,9 @@
|
|||||||
#define ADCV_OW_0 (1u << 0)
|
#define ADCV_OW_0 (1u << 0)
|
||||||
#define ADCV_OW_1 (1u << 1)
|
#define ADCV_OW_1 (1u << 1)
|
||||||
#define ADCV_RSTF (1u << 2)
|
#define ADCV_RSTF (1u << 2)
|
||||||
#define ADCV_DCP (1u << 4)
|
#define ADCV_DCP (1u << 4)
|
||||||
#define ADCV_CONT (1u << 7) // Continuous Mode
|
#define ADCV_CONT (1u << 7) // Continuous Mode
|
||||||
#define ADCV_RD (1u << 8) // Redundancy Mode
|
#define ADCV_RD (1u << 8) // Redundancy Mode
|
||||||
|
|
||||||
#define ADSV 0x0168 // Start Cell Voltage Conversion with S-ADC
|
#define ADSV 0x0168 // Start Cell Voltage Conversion with S-ADC
|
||||||
#define ADSV_OW_0 (1u << 0)
|
#define ADSV_OW_0 (1u << 0)
|
||||||
@ -54,45 +54,42 @@
|
|||||||
#define ADSV_DCP (1u << 4)
|
#define ADSV_DCP (1u << 4)
|
||||||
#define ADSV_CONT (1u << 7) // Continuous Mode
|
#define ADSV_CONT (1u << 7) // Continuous Mode
|
||||||
|
|
||||||
#define ADAX 0x0410 // Start GPIOs and Vref2 Conversion
|
#define ADAX 0x0410 // Start GPIOs and Vref2 Conversion
|
||||||
#define ADAX_CONV_ALL 0x0000 // Convert all GPIOs, VREF2, VD, VA, ITEMP
|
#define ADAX_CONV_ALL 0x0000 // Convert all GPIOs, VREF2, VD, VA, ITEMP
|
||||||
#define ADAX_OW (1u << 8)
|
#define ADAX_OW (1u << 8)
|
||||||
|
|
||||||
#define CLRCELL 0x0711 // Clear Cell Voltage Register Groups
|
#define CLRCELL 0x0711 // Clear Cell Voltage Register Groups
|
||||||
#define CLRAUX 0x0712 // Clear Auxiliary Register Groups
|
#define CLRAUX 0x0712 // Clear Auxiliary Register Groups
|
||||||
#define CLOVUV 0x0715 // Clear Overvoltage and Undervoltage Flags
|
#define CLOVUV 0x0715 // Clear Overvoltage and Undervoltage Flags
|
||||||
#define CLRFLAG 0x0717 // Clear all Flags
|
#define CLRFLAG 0x0717 // Clear all Flags
|
||||||
|
|
||||||
#define PLADC 0x0718 // Poll ADC Conversion Status
|
#define PLADC 0x0718 // Poll ADC Conversion Status
|
||||||
#define PLAUX 0x071E // Poll AUX Conversion Status
|
#define PLAUX 0x071E // Poll AUX Conversion Status
|
||||||
|
|
||||||
#define SRST 0x0027 //Soft reset
|
#define SRST 0x0027 // Soft reset
|
||||||
|
|
||||||
#define DIAGN 0x0715 // Diagnos MUX and Poll Status
|
#define DIAGN 0x0715 // Diagnos MUX and Poll Status
|
||||||
|
|
||||||
|
#define WRCOMM 0x0721 // Write COMM Register Group
|
||||||
#define WRCOMM 0x0721 // Write COMM Register Group
|
#define RDCOMM 0x0722 // Read COMM Register Group
|
||||||
#define RDCOMM 0x0722 // Read COMM Register Group
|
#define STCOMM 0x0723 // Start I2C/SPI Communication
|
||||||
#define STCOMM 0x0723 // Start I2C/SPI Communication
|
#define I2C_SEND_START 0b0110 << 4
|
||||||
#define I2C_SEND_START 0b0110 << 4
|
#define I2C_SEND_STOP 0b0001 << 4
|
||||||
#define I2C_SEND_STOP 0b0001 << 4
|
#define I2C_SEND 0b0000 << 4
|
||||||
#define I2C_SEND 0b0000 << 4
|
|
||||||
#define I2C_SEND_NOTRANSFER 0b0111 << 4
|
#define I2C_SEND_NOTRANSFER 0b0111 << 4
|
||||||
#define I2C_SEND_ACK 0b0000
|
#define I2C_SEND_ACK 0b0000
|
||||||
#define I2C_SEND_NACK 0b1000
|
#define I2C_SEND_NACK 0b1000
|
||||||
#define I2C_SEND_NACK_STOP 0b1001
|
#define I2C_SEND_NACK_STOP 0b1001
|
||||||
#define I2C_RECV_ACK 0b0111
|
#define I2C_RECV_ACK 0b0111
|
||||||
#define I2C_RECV_NACK 0b1111
|
#define I2C_RECV_NACK 0b1111
|
||||||
#define I2C_RECV_ACK_STOP 0b0001
|
#define I2C_RECV_ACK_STOP 0b0001
|
||||||
#define I2C_RECV_NACK_STOP 0b1001
|
#define I2C_RECV_NACK_STOP 0b1001
|
||||||
|
|
||||||
#define MUTE 0x0028 // Mute Discharge
|
#define MUTE 0x0028 // Mute Discharge
|
||||||
#define UNMUTE 0x0029 // Unmute Discharge
|
#define UNMUTE 0x0029 // Unmute Discharge
|
||||||
|
|
||||||
#define RDSID 0x002C // Read Serial ID
|
#define RDSID 0x002C // Read Serial ID
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* GPIO Selection for ADC Converion
|
/* GPIO Selection for ADC Converion
|
||||||
* 000: GPIO1 to 5, 2nd Reference, GPIO 6 to 9
|
* 000: GPIO1 to 5, 2nd Reference, GPIO 6 to 9
|
||||||
* 001: GPIO1 and GPIO6
|
* 001: GPIO1 and GPIO6
|
||||||
@ -133,47 +130,47 @@
|
|||||||
#define PWM_GROUP_A_SIZE 6
|
#define PWM_GROUP_A_SIZE 6
|
||||||
#define PWM_GROUP_B_SIZE 2
|
#define PWM_GROUP_B_SIZE 2
|
||||||
|
|
||||||
#define CV_GROUP_A_SIZE 6
|
#define CV_GROUP_A_SIZE 6
|
||||||
#define CV_GROUP_B_SIZE 6
|
#define CV_GROUP_B_SIZE 6
|
||||||
#define CV_GROUP_C_SIZE 6
|
#define CV_GROUP_C_SIZE 6
|
||||||
#define CV_GROUP_D_SIZE 6
|
#define CV_GROUP_D_SIZE 6
|
||||||
#define CV_GROUP_E_SIZE 6
|
#define CV_GROUP_E_SIZE 6
|
||||||
#define CV_GROUP_F_SIZE 6
|
#define CV_GROUP_F_SIZE 6
|
||||||
|
|
||||||
#define AUX_GROUP_A_SIZE 6
|
#define AUX_GROUP_A_SIZE 6
|
||||||
#define AUX_GROUP_B_SIZE 6
|
#define AUX_GROUP_B_SIZE 6
|
||||||
#define AUX_GROUP_C_SIZE 6
|
#define AUX_GROUP_C_SIZE 6
|
||||||
#define AUX_GROUP_D_SIZE 6
|
#define AUX_GROUP_D_SIZE 6
|
||||||
|
|
||||||
#define STATUS_GROUP_A_SIZE 6
|
#define STATUS_GROUP_A_SIZE 6
|
||||||
#define STATUS_GROUP_B_SIZE 6
|
#define STATUS_GROUP_B_SIZE 6
|
||||||
#define STATUS_GROUP_C_SIZE 6
|
#define STATUS_GROUP_C_SIZE 6
|
||||||
#define STATUS_GROUP_D_SIZE 6
|
#define STATUS_GROUP_D_SIZE 6
|
||||||
#define STATUS_GROUP_E_SIZE 6
|
#define STATUS_GROUP_E_SIZE 6
|
||||||
#define COMM_GROUP_SIZE 6
|
#define COMM_GROUP_SIZE 6
|
||||||
#define S_CONTROL_GROUP_SIZE 6
|
#define S_CONTROL_GROUP_SIZE 6
|
||||||
#define PWM_GROUP_SIZE 6
|
#define PWM_GROUP_SIZE 6
|
||||||
#define PWM_S_CONTROL_GROUP_B_SIZE 6
|
#define PWM_S_CONTROL_GROUP_B_SIZE 6
|
||||||
|
|
||||||
#define CFG_GROUP_A_ID 1
|
#define CFG_GROUP_A_ID 1
|
||||||
#define CFG_GROUP_B_ID 2
|
#define CFG_GROUP_B_ID 2
|
||||||
#define CV_GROUP_A_ID 3
|
#define CV_GROUP_A_ID 3
|
||||||
#define CV_GROUP_B_ID 4
|
#define CV_GROUP_B_ID 4
|
||||||
#define CV_GROUP_C_ID 5
|
#define CV_GROUP_C_ID 5
|
||||||
#define CV_GROUP_D_ID 6
|
#define CV_GROUP_D_ID 6
|
||||||
#define CV_GROUP_E_ID 7
|
#define CV_GROUP_E_ID 7
|
||||||
#define CV_GROUP_F_ID 8
|
#define CV_GROUP_F_ID 8
|
||||||
|
|
||||||
#define AUX_GROUP_A_ID 9
|
#define AUX_GROUP_A_ID 9
|
||||||
#define AUX_GROUP_B_ID 10
|
#define AUX_GROUP_B_ID 10
|
||||||
#define AUX_GROUP_C_ID 11
|
#define AUX_GROUP_C_ID 11
|
||||||
#define AUX_GROUP_D_ID 12
|
#define AUX_GROUP_D_ID 12
|
||||||
|
|
||||||
#define STATUS_GROUP_A_ID 13
|
#define STATUS_GROUP_A_ID 13
|
||||||
#define STATUS_GROUP_B_ID 14
|
#define STATUS_GROUP_B_ID 14
|
||||||
#define COMM_GROUP_ID 15
|
#define COMM_GROUP_ID 15
|
||||||
#define S_CONTROL_GROUP_ID 16
|
#define S_CONTROL_GROUP_ID 16
|
||||||
#define PWM_GROUP_ID 17
|
#define PWM_GROUP_ID 17
|
||||||
#define PWM_S_CONTROL_GROUP_B_ID 18
|
#define PWM_S_CONTROL_GROUP_B_ID 18
|
||||||
|
|
||||||
#define SID_GROUP_SIZE 6
|
#define SID_GROUP_SIZE 6
|
||||||
|
@ -19,18 +19,16 @@ typedef enum : uint16_t {
|
|||||||
NUM_ERROR_KINDS
|
NUM_ERROR_KINDS
|
||||||
} ADBMS_Status;
|
} ADBMS_Status;
|
||||||
|
|
||||||
static const char* ADBMS_Status_Names[NUM_ERROR_KINDS] = {
|
static const char *ADBMS_Status_Names[NUM_ERROR_KINDS] = {"ADBMS_NO_ERROR",
|
||||||
"ADBMS_NO_ERROR",
|
"ADBMS_OVERTEMP",
|
||||||
"ADBMS_OVERTEMP",
|
"ADBMS_UNDERTEMP",
|
||||||
"ADBMS_UNDERTEMP",
|
"ADBMS_OVERVOLT",
|
||||||
"ADBMS_OVERVOLT",
|
"ADBMS_UNDERVOLT",
|
||||||
"ADBMS_UNDERVOLT",
|
"ADBMS_OPENWIRE",
|
||||||
"ADBMS_OPENWIRE",
|
"ADBMS_INTERNAL_BMS_TIMEOUT",
|
||||||
"ADBMS_INTERNAL_BMS_TIMEOUT",
|
"ADBMS_INTERNAL_BMS_CHECKSUM_FAIL",
|
||||||
"ADBMS_INTERNAL_BMS_CHECKSUM_FAIL",
|
"ADBMS_INTERNAL_BMS_OVERTEMP",
|
||||||
"ADBMS_INTERNAL_BMS_OVERTEMP",
|
"ADBMS_INTERNAL_BMS_FAULT"};
|
||||||
"ADBMS_INTERNAL_BMS_FAULT"
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline const char* ADBMS_Status_ToString(ADBMS_Status status) {
|
static inline const char* ADBMS_Status_ToString(ADBMS_Status status) {
|
||||||
return (status < NUM_ERROR_KINDS) ? ADBMS_Status_Names[status] : "UNKNOWN";
|
return (status < NUM_ERROR_KINDS) ? ADBMS_Status_Names[status] : "UNKNOWN";
|
||||||
|
@ -13,6 +13,16 @@ typedef enum {
|
|||||||
ADBMS_TIMEOUT,
|
ADBMS_TIMEOUT,
|
||||||
} ADBMS_Internal_Status;
|
} ADBMS_Internal_Status;
|
||||||
|
|
||||||
|
#define CRITICAL_SECTION_VAR(counter) bool primask_##counter = (__get_PRIMASK() == 0)
|
||||||
|
#define CRITICAL_SECTION_ENTER(counter) do { __disable_irq(); } while(0)
|
||||||
|
#define CRITICAL_SECTION_EXIT(counter) ({do { if (primask_##counter) __enable_irq(); } while(0);})
|
||||||
|
|
||||||
|
#define CRITICAL_SECTION() \
|
||||||
|
CRITICAL_SECTION_VAR(__COUNTER__); \
|
||||||
|
asm volatile ("dmb"); \
|
||||||
|
CRITICAL_SECTION_ENTER(__COUNTER__); \
|
||||||
|
for(int _cs_flag_##__COUNTER__ = 1; _cs_flag_##__COUNTER__; _cs_flag_##__COUNTER__ = 0, CRITICAL_SECTION_EXIT(__COUNTER__))
|
||||||
|
|
||||||
[[maybe_unused, gnu::always_inline]]
|
[[maybe_unused, gnu::always_inline]]
|
||||||
static inline void mcuAdbmsCSLow() {
|
static inline void mcuAdbmsCSLow() {
|
||||||
HAL_GPIO_WritePin(AMS_CS_GPIO_Port, AMS_CS_Pin, GPIO_PIN_RESET);
|
HAL_GPIO_WritePin(AMS_CS_GPIO_Port, AMS_CS_Pin, GPIO_PIN_RESET);
|
||||||
@ -29,20 +39,32 @@ static inline void mcuAdbmsCSHigh() {
|
|||||||
|
|
||||||
[[maybe_unused, gnu::always_inline, gnu::access(read_only, 2, 3), gnu::nonnull(1)]]
|
[[maybe_unused, gnu::always_inline, gnu::access(read_only, 2, 3), gnu::nonnull(1)]]
|
||||||
static inline ADBMS_Internal_Status mcuSPITransmit(USER_PTR_TYPE user_ptr, const uint8_t* buffer, uint8_t buffersize, uint32_t timeout) {
|
static inline ADBMS_Internal_Status mcuSPITransmit(USER_PTR_TYPE user_ptr, const uint8_t* buffer, uint8_t buffersize, uint32_t timeout) {
|
||||||
const HAL_StatusTypeDef status = HAL_SPI_Transmit(user_ptr, buffer, buffersize, timeout);
|
ADBMS_Internal_Status adbms_status;
|
||||||
return (status == HAL_OK) ? ADBMS_OK : ADBMS_ERROR;
|
CRITICAL_SECTION() {
|
||||||
|
const HAL_StatusTypeDef status = HAL_SPI_Transmit(user_ptr, buffer, buffersize, timeout);
|
||||||
|
adbms_status = (status == HAL_OK) ? ADBMS_OK : ADBMS_ERROR;
|
||||||
|
}
|
||||||
|
return adbms_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[maybe_unused, gnu::always_inline, gnu::access(read_write, 2, 3), gnu::nonnull(1)]]
|
[[maybe_unused, gnu::always_inline, gnu::access(read_write, 2, 3), gnu::nonnull(1)]]
|
||||||
static inline ADBMS_Internal_Status mcuSPIReceive(USER_PTR_TYPE user_ptr, uint8_t* buffer, uint8_t buffersize, uint32_t timeout) {
|
static inline ADBMS_Internal_Status mcuSPIReceive(USER_PTR_TYPE user_ptr, uint8_t* buffer, uint8_t buffersize, uint32_t timeout) {
|
||||||
const HAL_StatusTypeDef status = HAL_SPI_Receive(user_ptr, buffer, buffersize, timeout);
|
ADBMS_Internal_Status adbms_status;
|
||||||
return (status == HAL_OK) ? ADBMS_OK : ADBMS_ERROR;
|
CRITICAL_SECTION() {
|
||||||
|
const HAL_StatusTypeDef status = HAL_SPI_Receive(user_ptr, buffer, buffersize, timeout);
|
||||||
|
adbms_status = (status == HAL_OK) ? ADBMS_OK : ADBMS_ERROR;
|
||||||
|
}
|
||||||
|
return adbms_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[maybe_unused, gnu::always_inline, gnu::access(read_write, 2, 4), gnu::access(read_only, 3, 4), gnu::nonnull(1, 2)]]
|
[[maybe_unused, gnu::always_inline, gnu::access(read_write, 2, 4), gnu::access(read_only, 3, 4), gnu::nonnull(1, 2)]]
|
||||||
static inline ADBMS_Internal_Status mcuSPITransmitReceive(USER_PTR_TYPE user_ptr, uint8_t* rxbuffer, const uint8_t* txbuffer, uint8_t buffersize, uint32_t timeout) {
|
static inline ADBMS_Internal_Status mcuSPITransmitReceive(USER_PTR_TYPE user_ptr, uint8_t* rxbuffer, const uint8_t* txbuffer, uint8_t buffersize, uint32_t timeout) {
|
||||||
const HAL_StatusTypeDef status = HAL_SPI_TransmitReceive(user_ptr, txbuffer, rxbuffer, buffersize, timeout);
|
ADBMS_Internal_Status adbms_status;
|
||||||
return (status == HAL_OK) ? ADBMS_OK : ADBMS_ERROR;
|
CRITICAL_SECTION() {
|
||||||
|
const HAL_StatusTypeDef status = HAL_SPI_TransmitReceive(user_ptr, txbuffer, rxbuffer, buffersize, timeout);
|
||||||
|
adbms_status = (status == HAL_OK) ? ADBMS_OK : ADBMS_ERROR;
|
||||||
|
}
|
||||||
|
return adbms_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delay by `delay` milliseconds
|
// Delay by `delay` milliseconds
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
#ifndef ADBMS_LL_DRIVER_H_
|
#ifndef ADBMS_LL_DRIVER_H_
|
||||||
#define ADBMS_LL_DRIVER_H_
|
#define ADBMS_LL_DRIVER_H_
|
||||||
|
|
||||||
#include "config_ADBMS6830.h"
|
|
||||||
#include "ADBMS_Intern.h"
|
#include "ADBMS_Intern.h"
|
||||||
|
#include "config_ADBMS6830.h"
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
//2 command + 2 PEC + (data + 2 DPEC) per BMS
|
// 2 command + 2 PEC + (data + 2 DPEC) per BMS
|
||||||
#define CMD_BUFFER_SIZE(datalen) (4 + (N_BMS * ((datalen) + 2)))
|
#define CMD_BUFFER_SIZE(datalen) (4 + (N_BMS * ((datalen) + 2)))
|
||||||
|
|
||||||
#define BUFFER_BMS_OFFSET(bms, datalen) (4 + ((bms) * ((datalen) + 2)))
|
#define BUFFER_BMS_OFFSET(bms, datalen) (4 + ((bms) * ((datalen) + 2)))
|
||||||
@ -14,30 +14,28 @@
|
|||||||
#define CMD_EMPTY_BUFFER ((uint8_t[CMD_BUFFER_SIZE(0)]){0})
|
#define CMD_EMPTY_BUFFER ((uint8_t[CMD_BUFFER_SIZE(0)]){0})
|
||||||
#define CMD_EMPTY_BUFFER_SIZE CMD_BUFFER_SIZE(0)
|
#define CMD_EMPTY_BUFFER_SIZE CMD_BUFFER_SIZE(0)
|
||||||
|
|
||||||
ADBMS_Internal_Status ___writeCMD(uint16_t command, uint8_t * args, size_t arglen);
|
ADBMS_Internal_Status ___writeCMD(uint16_t command, uint8_t *args, size_t arglen);
|
||||||
|
|
||||||
[[gnu::access(read_write, 2, 4), gnu::nonnull(2), gnu::always_inline]] //add dummy size variable for bounds checking, should be optimized out
|
[[gnu::access(read_write, 2, 4), gnu::nonnull(2), gnu::always_inline]] // add dummy size variable for bounds checking, should be optimized out
|
||||||
static inline ADBMS_Internal_Status __writeCMD(uint16_t command, uint8_t * args, size_t arglen, size_t) {
|
static inline ADBMS_Internal_Status __writeCMD(uint16_t command, uint8_t *args, size_t arglen, size_t) {
|
||||||
return ___writeCMD(command, args, arglen);
|
return ___writeCMD(command, args, arglen);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define writeCMD(command, args, arglen) \
|
#define writeCMD(command, args, arglen) __writeCMD(command, args, arglen, CMD_BUFFER_SIZE(arglen))
|
||||||
__writeCMD(command, args, arglen, CMD_BUFFER_SIZE(arglen))
|
|
||||||
|
|
||||||
ADBMS_Internal_Status ___readCMD(uint16_t command, uint8_t * buffer, size_t arglen);
|
ADBMS_Internal_Status ___readCMD(uint16_t command, uint8_t *buffer, size_t arglen);
|
||||||
|
|
||||||
[[gnu::access(read_write, 2, 4), gnu::nonnull(2), gnu::always_inline]] //add dummy size variable for bounds checking, should be optimized out
|
[[gnu::access(read_write, 2, 4), gnu::nonnull(2), gnu::always_inline]] // add dummy size variable for bounds checking, should be optimized out
|
||||||
static inline ADBMS_Internal_Status __readCMD(uint16_t command, uint8_t * buffer, size_t arglen, size_t) {
|
static inline ADBMS_Internal_Status __readCMD(uint16_t command, uint8_t *buffer, size_t arglen, size_t) {
|
||||||
return ___readCMD(command, buffer, arglen);
|
return ___readCMD(command, buffer, arglen);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define readCMD(command, buffer, buflen) \
|
#define readCMD(command, buffer, buflen) __readCMD(command, buffer, buflen, CMD_BUFFER_SIZE(buflen))
|
||||||
__readCMD(command, buffer, buflen, CMD_BUFFER_SIZE(buflen))
|
|
||||||
|
|
||||||
ADBMS_Internal_Status __pollCMD(uint16_t command, uint8_t waitTime);
|
ADBMS_Internal_Status __pollCMD(uint16_t command, uint8_t waitTime);
|
||||||
|
|
||||||
#define pollCMD(command) \
|
// poll is only valid after 2 * N_BMS clock cycles, +1 for safety, see datasheet page 55
|
||||||
__pollCMD(command, (N_BMS * 2) + 1) //poll is only valid after 2 * N_BMS clock cycles, +1 for safety, see datasheet page 55
|
#define pollCMD(command) __pollCMD(command, (N_BMS * 2) + 1)
|
||||||
|
|
||||||
void initSPI(USER_PTR_TYPE ptr);
|
void initSPI(USER_PTR_TYPE ptr);
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
#include "ADBMS_Abstraction.h"
|
#include "ADBMS_Abstraction.h"
|
||||||
#include "ADBMS_CMD_Defines.h"
|
#include "ADBMS_CMD_Defines.h"
|
||||||
|
#include "ADBMS_Intern.h"
|
||||||
#include "ADBMS_LL_Driver.h"
|
#include "ADBMS_LL_Driver.h"
|
||||||
#include "config_ADBMS6830.h"
|
#include "config_ADBMS6830.h"
|
||||||
#include "ADBMS_Intern.h"
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
|
||||||
@ -10,11 +10,12 @@ static const char* const ADBMS_Statuses[] = {"ADBMS_OK", "ADBMS_ERROR", "ADBMS_B
|
|||||||
|
|
||||||
#define CHECK_RETURN(x) \
|
#define CHECK_RETURN(x) \
|
||||||
do { \
|
do { \
|
||||||
ADBMS_Internal_Status status = x; \
|
ADBMS_Internal_Status status = x; \
|
||||||
if (status != 0) { \
|
if (status != 0) { \
|
||||||
debug_log(ADBMS_LOG_LEVEL_ERROR, "in %s:%d@%s: %s failed with status %d (%s)", __FILE_NAME__, __LINE__, __func__,\
|
debug_log(ADBMS_LOG_LEVEL_ERROR, "in %s:%d@%s: %s failed with status %d (%s)", __FILE_NAME__, __LINE__, \
|
||||||
#x, status, \
|
__func__, #x, status, \
|
||||||
(status < (sizeof(ADBMS_Statuses) / sizeof(ADBMS_Statuses[0]))) ? ADBMS_Statuses[status] : "Unknown"); \
|
(status < (sizeof(ADBMS_Statuses) / sizeof(ADBMS_Statuses[0]))) ? ADBMS_Statuses[status] \
|
||||||
|
: "Unknown"); \
|
||||||
return status; \
|
return status; \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
@ -328,8 +329,8 @@ ADBMS_Internal_Status amsReadCellVoltages(BMS_Chip (*module)[N_BMS]) {
|
|||||||
|
|
||||||
// Each selected BMS must have a corresponding address, and the data array for that BMS must be at least datalens[i]
|
// Each selected BMS must have a corresponding address, and the data array for that BMS must be at least datalens[i]
|
||||||
// bytes long
|
// bytes long
|
||||||
ADBMS_Internal_Status amsSendI2C(const uint8_t addresses[static N_BMS], uint8_t* data[static N_BMS],
|
ADBMS_Internal_Status amsSendI2C(const uint8_t addresses[static N_BMS], uint8_t *data[static N_BMS],
|
||||||
const uint8_t datalens[static N_BMS], uint32_t bms_mask) {
|
const uint8_t datalens[static N_BMS], uint32_t bms_mask) {
|
||||||
uint8_t buffer[CMD_BUFFER_SIZE(COMM_GROUP_SIZE)] = {};
|
uint8_t buffer[CMD_BUFFER_SIZE(COMM_GROUP_SIZE)] = {};
|
||||||
|
|
||||||
// COMM register works in 3 bytes max per go, interleaved with control information
|
// COMM register works in 3 bytes max per go, interleaved with control information
|
||||||
@ -404,8 +405,8 @@ ADBMS_Internal_Status amsSendI2C(const uint8_t addresses[static N_BMS], uint8_t*
|
|||||||
|
|
||||||
// Each selected BMS must have a corresponding address, and the data array for that BMS must be at least datalens[i]
|
// Each selected BMS must have a corresponding address, and the data array for that BMS must be at least datalens[i]
|
||||||
// bytes long
|
// bytes long
|
||||||
ADBMS_Internal_Status amsReadI2C(const uint8_t addresses[static N_BMS], uint8_t* data[static N_BMS],
|
ADBMS_Internal_Status amsReadI2C(const uint8_t addresses[static N_BMS], uint8_t *data[static N_BMS],
|
||||||
const uint8_t datalens[static N_BMS], uint32_t bms_mask) {
|
const uint8_t datalens[static N_BMS], uint32_t bms_mask) {
|
||||||
uint8_t buffer[CMD_BUFFER_SIZE(COMM_GROUP_SIZE)] = {};
|
uint8_t buffer[CMD_BUFFER_SIZE(COMM_GROUP_SIZE)] = {};
|
||||||
|
|
||||||
uint8_t max_datalen = 0;
|
uint8_t max_datalen = 0;
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
#include "ADBMS_Abstraction.h"
|
#include "ADBMS_Abstraction.h"
|
||||||
#include "ADBMS_Driver.h"
|
#include "ADBMS_Driver.h"
|
||||||
#include "config_ADBMS6830.h"
|
|
||||||
#include "ADBMS_Intern.h"
|
#include "ADBMS_Intern.h"
|
||||||
|
#include "config_ADBMS6830.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
BMS_Chip bms_data[N_BMS] = {};
|
BMS_Chip bms_data[N_BMS] = {};
|
||||||
|
|
||||||
uint8_t packetChecksumFails = 0;
|
uint8_t packetChecksumFails = 0;
|
||||||
@ -24,8 +23,8 @@ struct pollingTimes pollingTimes = {0, 0};
|
|||||||
static constexpr ADBMS_DetailedStatus NO_ERROR = {ADBMS_NO_ERROR};
|
static constexpr ADBMS_DetailedStatus NO_ERROR = {ADBMS_NO_ERROR};
|
||||||
|
|
||||||
ADBMS_DetailedStatus AMS_Init(USER_PTR_TYPE ptr) {
|
ADBMS_DetailedStatus AMS_Init(USER_PTR_TYPE ptr) {
|
||||||
debug_log(ADBMS_LOG_LEVEL_INFO, "ADBMS6830B HAL - configured for %d controllers and %d cells per controller...", N_BMS,
|
debug_log(ADBMS_LOG_LEVEL_INFO, "ADBMS6830B HAL - configured for %d controllers and %d cells per controller...",
|
||||||
N_CELLS);
|
N_BMS, N_CELLS);
|
||||||
if (initAMS(ptr) != ADBMS_OK) {
|
if (initAMS(ptr) != ADBMS_OK) {
|
||||||
debug_log(ADBMS_LOG_LEVEL_ERROR, "ADBMS6830B HAL - initialization failed");
|
debug_log(ADBMS_LOG_LEVEL_ERROR, "ADBMS6830B HAL - initialization failed");
|
||||||
return (ADBMS_DetailedStatus){ADBMS_INTERNAL_BMS_FAULT, -1};
|
return (ADBMS_DetailedStatus){ADBMS_INTERNAL_BMS_FAULT, -1};
|
||||||
@ -40,7 +39,7 @@ ADBMS_DetailedStatus AMS_Init(USER_PTR_TYPE ptr) {
|
|||||||
({ \
|
({ \
|
||||||
int first_match = -1; \
|
int first_match = -1; \
|
||||||
for (size_t __any_intern_i = 0; __any_intern_i < N_BMS; __any_intern_i++) { \
|
for (size_t __any_intern_i = 0; __any_intern_i < N_BMS; __any_intern_i++) { \
|
||||||
BMS_Chip module = bms_data[__any_intern_i]; \
|
BMS_Chip module = bms_data[__any_intern_i]; \
|
||||||
if ((x)) { \
|
if ((x)) { \
|
||||||
first_match = __any_intern_i; \
|
first_match = __any_intern_i; \
|
||||||
break; \
|
break; \
|
||||||
@ -69,7 +68,7 @@ ADBMS_DetailedStatus AMS_Idle_Loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((match = any(module.status.CS_FLT || module.status.SPIFLT || module.status.CMED || module.status.SMED ||
|
if ((match = any(module.status.CS_FLT || module.status.SPIFLT || module.status.CMED || module.status.SMED ||
|
||||||
module.status.VDE || module.status.VDEL || module.status.OSCCHK || module.status.TMODCHK))) {
|
module.status.VDE || module.status.VDEL || module.status.OSCCHK || module.status.TMODCHK))) {
|
||||||
// Fault bits are latched -- clear them so we can check again next
|
// Fault bits are latched -- clear them so we can check again next
|
||||||
// iteration.
|
// iteration.
|
||||||
amsClearFlag();
|
amsClearFlag();
|
||||||
@ -105,7 +104,7 @@ ADBMS_DetailedStatus AMS_Idle_Loop() {
|
|||||||
return (ADBMS_DetailedStatus){ADBMS_UNDERVOLT, match - 1};
|
return (ADBMS_DetailedStatus){ADBMS_UNDERVOLT, match - 1};
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((match = any(module.internalDieTemp > ADBMS_MAX_CHIP_TEMP || module.status.THSD))) {
|
if ((match = any(module.internalDieTemp > ADBMS_MAX_CHIP_TEMP || module.status.THSD))) {
|
||||||
return (ADBMS_DetailedStatus){ADBMS_INTERNAL_BMS_OVERTEMP, match - 1};
|
return (ADBMS_DetailedStatus){ADBMS_INTERNAL_BMS_OVERTEMP, match - 1};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include "ADBMS_LL_Driver.h"
|
#include "ADBMS_LL_Driver.h"
|
||||||
#include "config_ADBMS6830.h"
|
|
||||||
#include "ADBMS_Intern.h"
|
#include "ADBMS_Intern.h"
|
||||||
|
#include "config_ADBMS6830.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
|
|
||||||
@ -25,9 +25,9 @@ void initSPI(USER_PTR_TYPE ptr) {
|
|||||||
#define CRC10_REMAINDER_MASK 0x200
|
#define CRC10_REMAINDER_MASK 0x200
|
||||||
#define CRC10_RESULT_MASK 0x3FF
|
#define CRC10_RESULT_MASK 0x3FF
|
||||||
|
|
||||||
//command PEC calculation
|
// command PEC calculation
|
||||||
//CRC-15
|
// CRC-15
|
||||||
//x^15 + x^14 + x^10 + x^8 + x^7 + x^4 + x^3 + 1
|
// x^15 + x^14 + x^10 + x^8 + x^7 + x^4 + x^3 + 1
|
||||||
|
|
||||||
static uint16_t computeCRC15(const uint8_t* data, size_t length) {
|
static uint16_t computeCRC15(const uint8_t* data, size_t length) {
|
||||||
uint16_t remainder = INITIAL_COMMAND_PEC;
|
uint16_t remainder = INITIAL_COMMAND_PEC;
|
||||||
@ -61,36 +61,36 @@ static uint8_t checkCommandPEC(const uint8_t* data, uint8_t datalen) {
|
|||||||
return ((pech == data[datalen - 2]) && (pecl == data[datalen - 1])) ? 0 : 1;
|
return ((pech == data[datalen - 2]) && (pecl == data[datalen - 1])) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
//data PEC calculation
|
// data PEC calculation
|
||||||
//CRC-10
|
// CRC-10
|
||||||
//x^10 + x^7 + x^3 + x^2 + x + 1
|
// x^10 + x^7 + x^3 + x^2 + x + 1
|
||||||
|
|
||||||
static uint16_t computeCRC10(const uint8_t* data, size_t length, bool rx_cmd) {
|
static uint16_t computeCRC10(const uint8_t *data, size_t length, bool rx_cmd) {
|
||||||
uint16_t remainder = INITIAL_DATA_PEC;
|
uint16_t remainder = INITIAL_DATA_PEC;
|
||||||
|
|
||||||
for (size_t i = 0; i < length; i++) {
|
for (size_t i = 0; i < length; i++) {
|
||||||
remainder ^= (uint16_t)(data[i] << 2);
|
remainder ^= (uint16_t)(data[i] << 2);
|
||||||
for (int b = 0; b < 8; b++) {
|
for (int b = 0; b < 8; b++) {
|
||||||
if (remainder & CRC10_REMAINDER_MASK) {
|
if (remainder & CRC10_REMAINDER_MASK) {
|
||||||
remainder = (uint16_t)((remainder << 1) ^ CRC10_POLY);
|
remainder = (uint16_t)((remainder << 1) ^ CRC10_POLY);
|
||||||
} else {
|
} else {
|
||||||
remainder <<= 1;
|
remainder <<= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle last bits of the last byte if rx_cmd is true
|
// Handle last bits of the last byte if rx_cmd is true
|
||||||
if (rx_cmd) {
|
if (rx_cmd) {
|
||||||
remainder ^= (uint16_t)((data[length] & 0xFC) << 2);
|
remainder ^= (uint16_t)((data[length] & 0xFC) << 2);
|
||||||
for (int b = 0; b < 6; b++) {
|
for (int b = 0; b < 6; b++) {
|
||||||
if (remainder & CRC10_REMAINDER_MASK) {
|
if (remainder & CRC10_REMAINDER_MASK) {
|
||||||
remainder = (uint16_t)((remainder << 1) ^ CRC10_POLY);
|
remainder = (uint16_t)((remainder << 1) ^ CRC10_POLY);
|
||||||
} else {
|
} else {
|
||||||
remainder <<= 1;
|
remainder <<= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (uint16_t)(remainder & CRC10_RESULT_MASK);
|
return (uint16_t)(remainder & CRC10_RESULT_MASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t calculateDataPEC(uint8_t* data, uint8_t datalen) {
|
static uint8_t calculateDataPEC(uint8_t* data, uint8_t datalen) {
|
||||||
@ -111,174 +111,173 @@ static const char* const ADBMS_Statuses[] = {"ADBMS_OK", "ADBMS_ERROR", "ADBMS_B
|
|||||||
|
|
||||||
#ifdef STM32_HAL // feel free to replace this with your own SPI error handling
|
#ifdef STM32_HAL // feel free to replace this with your own SPI error handling
|
||||||
static void print_spi_details() {
|
static void print_spi_details() {
|
||||||
extern SPI_HandleTypeDef * usr_ptr;
|
extern SPI_HandleTypeDef * usr_ptr;
|
||||||
const uint32_t spi_error = HAL_SPI_GetError(usr_ptr);
|
const uint32_t spi_error = HAL_SPI_GetError(usr_ptr);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t mask;
|
uint32_t mask;
|
||||||
const char *label;
|
const char *label;
|
||||||
} SPIError;
|
} SPIError;
|
||||||
|
|
||||||
const SPIError errors[] = {
|
const SPIError errors[] = {
|
||||||
{HAL_SPI_ERROR_NONE, "NONE"},
|
{HAL_SPI_ERROR_NONE, "NONE"},
|
||||||
{HAL_SPI_ERROR_MODF, "MODF"},
|
{HAL_SPI_ERROR_MODF, "MODF"},
|
||||||
{HAL_SPI_ERROR_CRC, "CRC"},
|
{HAL_SPI_ERROR_CRC, "CRC"},
|
||||||
{HAL_SPI_ERROR_OVR, "OVR"},
|
{HAL_SPI_ERROR_OVR, "OVR"},
|
||||||
{HAL_SPI_ERROR_FRE, "FRE"},
|
{HAL_SPI_ERROR_FRE, "FRE"},
|
||||||
{HAL_SPI_ERROR_DMA, "DMA"},
|
{HAL_SPI_ERROR_DMA, "DMA"},
|
||||||
{HAL_SPI_ERROR_FLAG, "FLAG"},
|
{HAL_SPI_ERROR_FLAG, "FLAG"},
|
||||||
{HAL_SPI_ERROR_ABORT, "ABORT"},
|
{HAL_SPI_ERROR_ABORT, "ABORT"},
|
||||||
{HAL_SPI_ERROR_UDR, "UDR"},
|
{HAL_SPI_ERROR_UDR, "UDR"},
|
||||||
{HAL_SPI_ERROR_TIMEOUT, "TIMEOUT"},
|
{HAL_SPI_ERROR_TIMEOUT, "TIMEOUT"},
|
||||||
{HAL_SPI_ERROR_UNKNOW, "UNKNOWN"},
|
{HAL_SPI_ERROR_UNKNOW, "UNKNOWN"},
|
||||||
{HAL_SPI_ERROR_NOT_SUPPORTED, "NOT_SUPPORTED"},
|
{HAL_SPI_ERROR_NOT_SUPPORTED, "NOT_SUPPORTED"},
|
||||||
{HAL_SPI_ERROR_RELOAD, "RELOAD"},
|
{HAL_SPI_ERROR_RELOAD, "RELOAD"},
|
||||||
#ifdef HAL_SPI_ERROR_INVALID_CALLBACK
|
#ifdef HAL_SPI_ERROR_INVALID_CALLBACK
|
||||||
{HAL_SPI_ERROR_INVALID_CALLBACK,"INVALID_CALLBACK"},
|
{HAL_SPI_ERROR_INVALID_CALLBACK,"INVALID_CALLBACK"},
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr size_t numErrors = sizeof(errors) / sizeof(errors[0]);
|
constexpr size_t numErrors = sizeof(errors) / sizeof(errors[0]);
|
||||||
for (size_t i = 0; i < numErrors; i++) {
|
for (size_t i = 0; i < numErrors; i++) {
|
||||||
if (spi_error & errors[i].mask) {
|
if (spi_error & errors[i].mask) {
|
||||||
debug_log_cont(ADBMS_LOG_LEVEL_ERROR, "%s ", errors[i].label);
|
debug_log_cont(ADBMS_LOG_LEVEL_ERROR, "%s ", errors[i].label);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
ADBMS_Internal_Status ___writeCMD(uint16_t command, uint8_t * args, size_t arglen) {
|
ADBMS_Internal_Status ___writeCMD(uint16_t command, uint8_t * args, size_t arglen) {
|
||||||
ADBMS_Internal_Status ret;
|
ADBMS_Internal_Status ret;
|
||||||
if (arglen > 0) {
|
if (arglen > 0) {
|
||||||
args[0] = (command >> 8) & 0xFF;
|
args[0] = (command >> 8) & 0xFF;
|
||||||
args[1] = (command) & 0xFF;
|
args[1] = (command) & 0xFF;
|
||||||
|
|
||||||
if (DEBUG_CHANNEL_ENABLED(ADBMS_LOG_LEVEL_NOISY)) {
|
if (DEBUG_CHANNEL_ENABLED(ADBMS_LOG_LEVEL_NOISY)) {
|
||||||
debug_log(ADBMS_LOG_LEVEL_NOISY, "%lu W | %02X %02X ", mcuGetTime(), args[0], args[1]);
|
debug_log(ADBMS_LOG_LEVEL_NOISY, "%lu W | %02X %02X ", mcuGetTime(), args[0], args[1]);
|
||||||
|
|
||||||
for (size_t i = 0; i < N_BMS; i++) {
|
for (size_t i = 0; i < N_BMS; i++) {
|
||||||
debug_log_cont(ADBMS_LOG_LEVEL_NOISY, "%d: ", i);
|
debug_log_cont(ADBMS_LOG_LEVEL_NOISY, "%d: ", i);
|
||||||
for (size_t j = 0; j < arglen; j++) {
|
for (size_t j = 0; j < arglen; j++) {
|
||||||
debug_log_cont(ADBMS_LOG_LEVEL_NOISY, "%02X ", args[BUFFER_BMS_OFFSET(i, arglen) + j]);
|
debug_log_cont(ADBMS_LOG_LEVEL_NOISY, "%02X ", args[BUFFER_BMS_OFFSET(i, arglen) + j]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
calculateCommandPEC(args, 4);
|
||||||
calculateCommandPEC(args, 4);
|
|
||||||
|
for (size_t i = 0; i < N_BMS; i++) {
|
||||||
for (size_t i = 0; i < N_BMS; i++) {
|
args[BUFFER_BMS_OFFSET(i, arglen) + arglen] = 0; // zero out the PEC
|
||||||
args[BUFFER_BMS_OFFSET(i, arglen) + arglen] = 0; // zero out the PEC
|
args[BUFFER_BMS_OFFSET(i, arglen) + arglen + 1] = 0;
|
||||||
args[BUFFER_BMS_OFFSET(i, arglen) + arglen + 1] = 0;
|
calculateDataPEC(&args[4 + (i * (arglen + 2))], arglen + 2); //DPEC is calculated over the data, not the command, and placed at the end of the data
|
||||||
calculateDataPEC(&args[4 + (i * (arglen + 2))], arglen + 2); //DPEC is calculated over the data, not the command, and placed at the end of the data
|
}
|
||||||
|
|
||||||
|
mcuAdbmsCSLow();
|
||||||
|
ret = mcuSPITransmit(usr_ptr, args, CMD_BUFFER_SIZE(arglen), ADBMS_SPI_TIMEOUT);
|
||||||
|
mcuAdbmsCSHigh();
|
||||||
|
} else {
|
||||||
|
args[0] = (command >> 8) & 0xFF;
|
||||||
|
args[1] = (command) & 0xFF;
|
||||||
|
calculateCommandPEC(args, 4);
|
||||||
|
|
||||||
|
mcuAdbmsCSLow();
|
||||||
|
ret = mcuSPITransmit(usr_ptr, args, 4, ADBMS_SPI_TIMEOUT);
|
||||||
|
mcuAdbmsCSHigh();
|
||||||
}
|
}
|
||||||
|
|
||||||
mcuAdbmsCSLow();
|
if (ret != ADBMS_OK) {
|
||||||
ret = mcuSPITransmit(usr_ptr, args, CMD_BUFFER_SIZE(arglen), ADBMS_SPI_TIMEOUT);
|
debug_log(ADBMS_LOG_LEVEL_ERROR, "SPI HAL returned error %s", ADBMS_Statuses[ret]);
|
||||||
mcuAdbmsCSHigh();
|
#ifdef STM32_HAL
|
||||||
} else {
|
debug_log(ADBMS_LOG_LEVEL_ERROR, "SPI error bits: ");
|
||||||
args[0] = (command >> 8) & 0xFF;
|
print_spi_details();
|
||||||
args[1] = (command) & 0xFF;
|
#endif
|
||||||
calculateCommandPEC(args, 4);
|
}
|
||||||
|
|
||||||
mcuAdbmsCSLow();
|
return ret;
|
||||||
ret = mcuSPITransmit(usr_ptr, args, 4, ADBMS_SPI_TIMEOUT);
|
|
||||||
mcuAdbmsCSHigh();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret != ADBMS_OK) {
|
|
||||||
debug_log(ADBMS_LOG_LEVEL_ERROR, "SPI HAL returned error %s", ADBMS_Statuses[ret]);
|
|
||||||
#ifdef STM32_HAL
|
|
||||||
debug_log(ADBMS_LOG_LEVEL_ERROR, "SPI error bits: ");
|
|
||||||
print_spi_details();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ADBMS_Internal_Status ___readCMD(uint16_t command, uint8_t * buffer, size_t arglen) {
|
ADBMS_Internal_Status ___readCMD(uint16_t command, uint8_t * buffer, size_t arglen) {
|
||||||
buffer[0] = (command >> 8) & 0xFF;
|
buffer[0] = (command >> 8) & 0xFF;
|
||||||
buffer[1] = (command)&0xFF;
|
buffer[1] = (command) & 0xFF;
|
||||||
calculateCommandPEC(buffer, 4);
|
calculateCommandPEC(buffer, 4);
|
||||||
|
|
||||||
mcuAdbmsCSLow();
|
mcuAdbmsCSLow();
|
||||||
const ADBMS_Internal_Status status = mcuSPITransmitReceive(usr_ptr, buffer, buffer, CMD_BUFFER_SIZE(arglen), ADBMS_SPI_TIMEOUT);
|
const ADBMS_Internal_Status status = mcuSPITransmitReceive(usr_ptr, buffer, buffer, CMD_BUFFER_SIZE(arglen), ADBMS_SPI_TIMEOUT);
|
||||||
mcuAdbmsCSHigh();
|
mcuAdbmsCSHigh();
|
||||||
|
|
||||||
if (status != ADBMS_OK) {
|
if (status != ADBMS_OK) {
|
||||||
debug_log(ADBMS_LOG_LEVEL_ERROR, "SPI HAL returned error %s", ADBMS_Statuses[status]);
|
debug_log(ADBMS_LOG_LEVEL_ERROR, "SPI HAL returned error %s", ADBMS_Statuses[status]);
|
||||||
#ifdef STM32_HAL
|
#ifdef STM32_HAL
|
||||||
debug_log(ADBMS_LOG_LEVEL_ERROR, "SPI error bits: ");
|
debug_log(ADBMS_LOG_LEVEL_ERROR, "SPI error bits: ");
|
||||||
print_spi_details();
|
print_spi_details();
|
||||||
#endif
|
#endif
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
//[[maybe_unused]] uint8_t commandCounter = buffer[sizeof(buffer) - 2] & 0xFC; //command counter is bits 7-2
|
//[[maybe_unused]] uint8_t commandCounter = buffer[sizeof(buffer) - 2] & 0xFC; //command counter is bits 7-2
|
||||||
//TODO: check command counter?
|
//TODO: check command counter?
|
||||||
|
|
||||||
if (DEBUG_CHANNEL_ENABLED(ADBMS_LOG_LEVEL_NOISY)) {
|
if (DEBUG_CHANNEL_ENABLED(ADBMS_LOG_LEVEL_NOISY)) {
|
||||||
debug_log(ADBMS_LOG_LEVEL_NOISY, "%lu R | %02X %02X ", mcuGetTime(), command >> 8, command & 0xFF);
|
debug_log(ADBMS_LOG_LEVEL_NOISY, "%lu R | %02X %02X ", mcuGetTime(), command >> 8, command & 0xFF);
|
||||||
|
|
||||||
//print out data bytes
|
//print out data bytes
|
||||||
if (arglen > 0) {
|
if (arglen > 0) {
|
||||||
for (size_t i = 0; i < N_BMS; i++) {
|
for (size_t i = 0; i < N_BMS; i++) {
|
||||||
debug_log_cont(ADBMS_LOG_LEVEL_NOISY, "%d: ", i);
|
debug_log_cont(ADBMS_LOG_LEVEL_NOISY, "%d: ", i);
|
||||||
for (size_t j = 0; j < arglen; j++) {
|
for (size_t j = 0; j < arglen; j++) {
|
||||||
debug_log_cont(ADBMS_LOG_LEVEL_NOISY, "%02X ", buffer[BUFFER_BMS_OFFSET(i, arglen) + j]);
|
debug_log_cont(ADBMS_LOG_LEVEL_NOISY, "%02X ", buffer[BUFFER_BMS_OFFSET(i, arglen) + j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (arglen == 0) {
|
||||||
if (arglen == 0) {
|
return ADBMS_OK; //no data to check
|
||||||
return ADBMS_OK; //no data to check
|
|
||||||
}
|
|
||||||
|
|
||||||
//check data PEC
|
|
||||||
for (size_t i = 0; i < N_BMS; i++) {
|
|
||||||
const size_t offset = BUFFER_BMS_OFFSET(i, arglen);
|
|
||||||
if (checkDataPEC(&buffer[offset], arglen + 2) != 0) {
|
|
||||||
debug_log(ADBMS_LOG_LEVEL_ERROR, "Invalid data PEC when reading BMS %d", i);
|
|
||||||
debug_log(ADBMS_LOG_LEVEL_ERROR, "Received: ");
|
|
||||||
for (size_t j = 0; j < arglen + 2; j++) {
|
|
||||||
debug_log_cont(ADBMS_LOG_LEVEL_ERROR, "%02X ", buffer[offset + j]);
|
|
||||||
}
|
|
||||||
debug_log_cont(ADBMS_LOG_LEVEL_ERROR, "| %02X %02X ", buffer[offset + arglen], buffer[offset + arglen + 1]); //print out the DPEC
|
|
||||||
debug_log(ADBMS_LOG_LEVEL_ERROR, " DATA ^");
|
|
||||||
//print out spaces until start of DPEC
|
|
||||||
for (size_t j = 0; j < arglen - 1; j++) {
|
|
||||||
debug_log_cont(ADBMS_LOG_LEVEL_ERROR, (arglen < 2) ? "" : "^^^");
|
|
||||||
}
|
|
||||||
debug_log_cont(ADBMS_LOG_LEVEL_ERROR, "^^ ");
|
|
||||||
debug_log_cont(ADBMS_LOG_LEVEL_ERROR, " PEC ^");
|
|
||||||
return ADBMS_ERROR;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
//check data PEC
|
||||||
return ADBMS_OK;
|
for (size_t i = 0; i < N_BMS; i++) {
|
||||||
|
const size_t offset = BUFFER_BMS_OFFSET(i, arglen);
|
||||||
|
if (checkDataPEC(&buffer[offset], arglen + 2) != 0) {
|
||||||
|
debug_log(ADBMS_LOG_LEVEL_ERROR, "Invalid data PEC when reading BMS %d", i);
|
||||||
|
debug_log(ADBMS_LOG_LEVEL_ERROR, "Received: ");
|
||||||
|
for (size_t j = 0; j < arglen + 2; j++) {
|
||||||
|
debug_log_cont(ADBMS_LOG_LEVEL_ERROR, "%02X ", buffer[offset + j]);
|
||||||
|
}
|
||||||
|
debug_log_cont(ADBMS_LOG_LEVEL_ERROR, "| %02X %02X ", buffer[offset + arglen], buffer[offset + arglen + 1]); //print out the DPEC
|
||||||
|
debug_log(ADBMS_LOG_LEVEL_ERROR, " DATA ^");
|
||||||
|
//print out spaces until start of DPEC
|
||||||
|
for (size_t j = 0; j < arglen - 1; j++) {
|
||||||
|
debug_log_cont(ADBMS_LOG_LEVEL_ERROR, (arglen < 2) ? "" : "^^^");
|
||||||
|
}
|
||||||
|
debug_log_cont(ADBMS_LOG_LEVEL_ERROR, "^^ ");
|
||||||
|
debug_log_cont(ADBMS_LOG_LEVEL_ERROR, " PEC ^");
|
||||||
|
return ADBMS_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ADBMS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
//check poll command - no data PEC sent back, waitTime is in SPI clock cycles
|
//check poll command - no data PEC sent back, waitTime is in SPI clock cycles
|
||||||
ADBMS_Internal_Status __pollCMD(uint16_t command, uint8_t waitTime) {
|
ADBMS_Internal_Status __pollCMD(uint16_t command, uint8_t waitTime) {
|
||||||
uint8_t buffer[4 + (waitTime / 8) + 1] = {}; //8 cycles per byte, +1 as we round up
|
uint8_t buffer[4 + (waitTime / 8) + 1] = {}; //8 cycles per byte, +1 as we round up
|
||||||
buffer[0] = (command >> 8) & 0xFF;
|
buffer[0] = (command >> 8) & 0xFF;
|
||||||
buffer[1] = (command) & 0xFF;
|
buffer[1] = (command) & 0xFF;
|
||||||
calculateCommandPEC(buffer, 4);
|
calculateCommandPEC(buffer, 4);
|
||||||
|
|
||||||
mcuAdbmsCSLow();
|
mcuAdbmsCSLow();
|
||||||
const ADBMS_Internal_Status status = mcuSPITransmitReceive(usr_ptr, buffer, buffer, sizeof buffer, ADBMS_SPI_TIMEOUT);
|
const ADBMS_Internal_Status status = mcuSPITransmitReceive(usr_ptr, buffer, buffer, sizeof buffer, ADBMS_SPI_TIMEOUT);
|
||||||
mcuAdbmsCSHigh();
|
mcuAdbmsCSHigh();
|
||||||
|
|
||||||
if (status != ADBMS_OK) {
|
if (status != ADBMS_OK) {
|
||||||
debug_log(ADBMS_LOG_LEVEL_ERROR, "SPI HAL returned error %s", ADBMS_Statuses[status]);
|
debug_log(ADBMS_LOG_LEVEL_ERROR, "SPI HAL returned error %s", ADBMS_Statuses[status]);
|
||||||
#ifdef STM32_HAL
|
#ifdef STM32_HAL
|
||||||
debug_log(ADBMS_LOG_LEVEL_ERROR, "SPI error bits: ");
|
debug_log(ADBMS_LOG_LEVEL_ERROR, "SPI error bits: ");
|
||||||
print_spi_details();
|
print_spi_details();
|
||||||
#endif
|
#endif
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ((buffer[sizeof buffer] & 0x0F) == 0x0) ? ADBMS_BUSY : ADBMS_OK; //SDO goes high when data is ready
|
return ((buffer[sizeof buffer] & 0x0F) == 0x0) ? ADBMS_BUSY : ADBMS_OK; //SDO goes high when data is ready
|
||||||
}
|
}
|
||||||
|
@ -27,19 +27,19 @@
|
|||||||
static_assert(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__, "Endianness is not little endian, this code is not portable!");
|
static_assert(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__, "Endianness is not little endian, this code is not portable!");
|
||||||
|
|
||||||
typedef union [[gnu::packed]] {
|
typedef union [[gnu::packed]] {
|
||||||
struct {
|
struct {
|
||||||
uint8_t header;
|
uint8_t header;
|
||||||
uint8_t data[MAX_ISOTP_DATA_SINGLE];
|
uint8_t data[MAX_ISOTP_DATA_SINGLE];
|
||||||
} single;
|
} single;
|
||||||
struct {
|
struct {
|
||||||
uint8_t header_1;
|
uint8_t header_1;
|
||||||
uint8_t header_2;
|
uint8_t header_2;
|
||||||
uint8_t data[MAX_ISOTP_DATA_FIRST];
|
uint8_t data[MAX_ISOTP_DATA_FIRST];
|
||||||
} first;
|
} first;
|
||||||
struct {
|
struct {
|
||||||
uint8_t header;
|
uint8_t header;
|
||||||
uint8_t data[MAX_ISOTP_DATA_CONSECUTIVE];
|
uint8_t data[MAX_ISOTP_DATA_CONSECUTIVE];
|
||||||
} consecutive;
|
} consecutive;
|
||||||
} isotp_message;
|
} isotp_message;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include "isotp_log_backend.h"
|
#include "isotp_log_backend.h"
|
||||||
#include "log.h"
|
|
||||||
#include "isotp.h"
|
#include "isotp.h"
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
@ -9,8 +9,8 @@
|
|||||||
|
|
||||||
|
|
||||||
/* ISO-TP CAN ID configuration */
|
/* ISO-TP CAN ID configuration */
|
||||||
#ifndef ISOTP_LOG_CAN_ID //TEMPORARY!!!!!
|
#ifndef ISOTP_LOG_CAN_ID // TEMPORARY!!!!!
|
||||||
#define ISOTP_LOG_CAN_ID 0x123 // CAN ID to use for ISO-TP log messages
|
#define ISOTP_LOG_CAN_ID 0x123 // CAN ID to use for ISO-TP log messages
|
||||||
#endif
|
#endif
|
||||||
#ifndef ISOTP_LOG_FC_CAN_ID
|
#ifndef ISOTP_LOG_FC_CAN_ID
|
||||||
#define ISOTP_LOG_FC_CAN_ID 0x132 // CAN ID for flow control messages
|
#define ISOTP_LOG_FC_CAN_ID 0x132 // CAN ID for flow control messages
|
||||||
|
@ -53,15 +53,15 @@ bool log_is_level_enabled(log_level_t level) {
|
|||||||
/* Main logging function */
|
/* Main logging function */
|
||||||
void log_message(log_level_t level, const char *msg, ...) {
|
void log_message(log_level_t level, const char *msg, ...) {
|
||||||
log_message_t log_msg = {level, HAL_GetTick(), "\n", 0};
|
log_message_t log_msg = {level, HAL_GetTick(), "\n", 0};
|
||||||
|
|
||||||
/* Format the message prefix (timestamp, log level) */
|
/* Format the message prefix (timestamp, log level) */
|
||||||
size_t prefix_len = 1;
|
size_t prefix_len = 1;
|
||||||
|
|
||||||
/* Add timestamp if configured */
|
/* Add timestamp if configured */
|
||||||
if (PRINT_TIMESTAMP) {
|
if (PRINT_TIMESTAMP) {
|
||||||
prefix_len += snprintf(log_msg.message + prefix_len, MAX_MESSAGE_LENGTH, "[%lu] ", log_msg.timestamp);
|
prefix_len += snprintf(log_msg.message + prefix_len, MAX_MESSAGE_LENGTH, "[%lu] ", log_msg.timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add log level name */
|
/* Add log level name */
|
||||||
if (level <= LOG_LEVEL_NOISY) {
|
if (level <= LOG_LEVEL_NOISY) {
|
||||||
prefix_len += snprintf(log_msg.message + prefix_len, MAX_MESSAGE_LENGTH - prefix_len,
|
prefix_len += snprintf(log_msg.message + prefix_len, MAX_MESSAGE_LENGTH - prefix_len,
|
||||||
@ -75,7 +75,7 @@ void log_message(log_level_t level, const char *msg, ...) {
|
|||||||
MAX_MESSAGE_LENGTH - prefix_len,
|
MAX_MESSAGE_LENGTH - prefix_len,
|
||||||
msg, args);
|
msg, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
/* Send to all enabled backends */
|
/* Send to all enabled backends */
|
||||||
for (uint8_t i = 0; i < backend_count; i++) {
|
for (uint8_t i = 0; i < backend_count; i++) {
|
||||||
if (registered_backends[i]->is_enabled(level)) {
|
if (registered_backends[i]->is_enabled(level)) {
|
||||||
@ -89,13 +89,13 @@ void log_message_cont(log_level_t level, const char *msg, ...) {
|
|||||||
log_message_t log_msg;
|
log_message_t log_msg;
|
||||||
log_msg.level = level;
|
log_msg.level = level;
|
||||||
log_msg.timestamp = HAL_GetTick();
|
log_msg.timestamp = HAL_GetTick();
|
||||||
|
|
||||||
/* Format the log message with variable arguments */
|
/* Format the log message with variable arguments */
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, msg);
|
va_start(args, msg);
|
||||||
log_msg.message_length = vsnprintf(log_msg.message, MAX_MESSAGE_LENGTH, msg, args);
|
log_msg.message_length = vsnprintf(log_msg.message, MAX_MESSAGE_LENGTH, msg, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
/* Send to all enabled backends */
|
/* Send to all enabled backends */
|
||||||
for (uint8_t i = 0; i < backend_count; i++) {
|
for (uint8_t i = 0; i < backend_count; i++) {
|
||||||
if (registered_backends[i]->is_enabled(level)) {
|
if (registered_backends[i]->is_enabled(level)) {
|
||||||
|
@ -10,10 +10,10 @@
|
|||||||
#ifndef __LOG_H
|
#ifndef __LOG_H
|
||||||
#define __LOG_H
|
#define __LOG_H
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
/* Configuration */
|
/* Configuration */
|
||||||
#define MAX_MESSAGE_LENGTH 384
|
#define MAX_MESSAGE_LENGTH 384
|
||||||
@ -25,30 +25,30 @@
|
|||||||
|
|
||||||
/* Log level definitions */
|
/* Log level definitions */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
LOG_LEVEL_FATAL,
|
LOG_LEVEL_FATAL,
|
||||||
LOG_LEVEL_ERROR,
|
LOG_LEVEL_ERROR,
|
||||||
LOG_LEVEL_WARNING,
|
LOG_LEVEL_WARNING,
|
||||||
LOG_LEVEL_INFO,
|
LOG_LEVEL_INFO,
|
||||||
LOG_LEVEL_DEBUG,
|
LOG_LEVEL_DEBUG,
|
||||||
LOG_LEVEL_NOISY
|
LOG_LEVEL_NOISY
|
||||||
} log_level_t;
|
} log_level_t;
|
||||||
|
|
||||||
/* Log message structure */
|
/* Log message structure */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
log_level_t level;
|
log_level_t level;
|
||||||
uint32_t timestamp;
|
uint32_t timestamp;
|
||||||
char message[MAX_MESSAGE_LENGTH];
|
char message[MAX_MESSAGE_LENGTH];
|
||||||
size_t message_length;
|
size_t message_length;
|
||||||
} log_message_t;
|
} log_message_t;
|
||||||
|
|
||||||
/* Log backend interface */
|
/* Log backend interface */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char* name;
|
const char *name;
|
||||||
void (*init)(void);
|
void (*init)(void);
|
||||||
bool (*is_enabled)(log_level_t level);
|
bool (*is_enabled)(log_level_t level);
|
||||||
void (*write)(const log_message_t* message);
|
void (*write)(const log_message_t *message);
|
||||||
void (*flush)(void);
|
void (*flush)(void);
|
||||||
void (*clear)(void);
|
void (*clear)(void);
|
||||||
} log_backend_t;
|
} log_backend_t;
|
||||||
|
|
||||||
/* Log level names with formatting for display */
|
/* Log level names with formatting for display */
|
||||||
|
@ -27,12 +27,12 @@ static void swo_backend_clear(void);
|
|||||||
|
|
||||||
/* SWO backend definition */
|
/* SWO backend definition */
|
||||||
static const log_backend_t swo_backend = {
|
static const log_backend_t swo_backend = {
|
||||||
.name = "SWO",
|
.name = "SWO",
|
||||||
.init = swo_backend_init,
|
.init = swo_backend_init,
|
||||||
.is_enabled = swo_backend_is_enabled,
|
.is_enabled = swo_backend_is_enabled,
|
||||||
.write = swo_backend_write,
|
.write = swo_backend_write,
|
||||||
.flush = swo_backend_flush,
|
.flush = swo_backend_flush,
|
||||||
.clear = swo_backend_clear
|
.clear = swo_backend_clear
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Return the SWO backend definition */
|
/* Return the SWO backend definition */
|
||||||
@ -85,7 +85,8 @@ static void swo_backend_write(const log_message_t* message) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Assuming message->message is ensured to be non-NULL by the logger core if message itself is not NULL.
|
// Assuming message->message is ensured to be non-NULL by the logger core if message itself is not NULL.
|
||||||
// Or, if message->message can be NULL, the caller of swo_util_puts should handle it or swo_util_puts should be robust to it.
|
// Or, if message->message can be NULL, the caller of swo_util_puts should handle it or swo_util_puts should be
|
||||||
|
// robust to it.
|
||||||
|
|
||||||
#if USE_MULTIPLE_CHANNELS
|
#if USE_MULTIPLE_CHANNELS
|
||||||
swo_util_puts(message->message, message->level);
|
swo_util_puts(message->message, message->level);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#include "swo_util.h"
|
#include "swo_util.h"
|
||||||
#include "stm32h7xx.h" // For ITM registers, __NOP() and ITM_TCR_ITMENA_Msk, ITM_TER_TER_Msk
|
#include "stm32h7xx.h" // For ITM registers, __NOP() and ITM_TCR_ITMENA_Msk, ITM_TER_TER_Msk
|
||||||
#include <stdint.h> // For uint32_t
|
#include <stddef.h> // For NULL
|
||||||
#include <stddef.h> // For NULL
|
#include <stdint.h> // For uint32_t
|
||||||
|
|
||||||
// Core function to send a character to a specific SWO ITM port
|
// Core function to send a character to a specific SWO ITM port
|
||||||
// Adapted from ITM_SendChar() in the CMSIS-Core
|
// Adapted from ITM_SendChar() in the CMSIS-Core
|
||||||
|
@ -4,10 +4,10 @@
|
|||||||
#include "NTC.h"
|
#include "NTC.h"
|
||||||
#include "can.h"
|
#include "can.h"
|
||||||
#include "config_ADBMS6830.h"
|
#include "config_ADBMS6830.h"
|
||||||
#include "ts_state_machine.h"
|
|
||||||
#include <string.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
#include "ts_state_machine.h"
|
||||||
|
#include <math.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#define SWO_LOG_PREFIX "[BATTERY] "
|
#define SWO_LOG_PREFIX "[BATTERY] "
|
||||||
#include "swo_log.h"
|
#include "swo_log.h"
|
||||||
@ -71,103 +71,99 @@ HAL_StatusTypeDef battery_init(SPI_HandleTypeDef *hspi) {
|
|||||||
|
|
||||||
[[gnu::optimize("no-math-errno")]]
|
[[gnu::optimize("no-math-errno")]]
|
||||||
HAL_StatusTypeDef battery_update() {
|
HAL_StatusTypeDef battery_update() {
|
||||||
auto const ret = AMS_Idle_Loop();
|
auto const ret = AMS_Idle_Loop();
|
||||||
if (ret.status != ADBMS_NO_ERROR) {
|
if (ret.status != ADBMS_NO_ERROR) {
|
||||||
debug_log(LOG_LEVEL_ERROR, "Error while updating battery data: %s",
|
debug_log(LOG_LEVEL_ERROR, "Error while updating battery data: %s", ADBMS_Status_ToString(ret.status));
|
||||||
ADBMS_Status_ToString(ret.status));
|
if (ret.bms_id != -1) {
|
||||||
if (ret.bms_id != -1) {
|
debug_log_cont(LOG_LEVEL_ERROR, " (on BMS ID: %hd)", ret.bms_id);
|
||||||
debug_log_cont(LOG_LEVEL_ERROR, " (on BMS ID: %hd)", ret.bms_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret.status == ADBMS_OVERVOLT || ret.status == ADBMS_UNDERVOLT) {
|
|
||||||
if (ret.bms_id != -1 && ret.bms_id < N_BMS) {
|
|
||||||
const char* error_type = (ret.status == ADBMS_OVERVOLT) ? "overvoltage" : "undervoltage";
|
|
||||||
const uint32_t voltage_flags = (ret.status == ADBMS_OVERVOLT) ?
|
|
||||||
bms_data[ret.bms_id].overVoltage :
|
|
||||||
bms_data[ret.bms_id].underVoltage;
|
|
||||||
|
|
||||||
debug_log(LOG_LEVEL_ERROR, "Cell %s detected on module %d, affected cells: ",
|
|
||||||
error_type, ret.bms_id);
|
|
||||||
|
|
||||||
for (size_t cell = 0; cell < N_CELLS; cell++) {
|
|
||||||
if (voltage_flags & (1UL << cell)) {
|
|
||||||
debug_log_cont(LOG_LEVEL_ERROR, "%u (%d mV) ", cell, bms_data[ret.bms_id].cellVoltages[cell]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!voltage_flags) {
|
if (ret.status == ADBMS_OVERVOLT || ret.status == ADBMS_UNDERVOLT) {
|
||||||
debug_log_cont(LOG_LEVEL_ERROR, "none (something went wrong?)");
|
if (ret.bms_id != -1 && ret.bms_id < N_BMS) {
|
||||||
|
const char *error_type = (ret.status == ADBMS_OVERVOLT) ? "overvoltage" : "undervoltage";
|
||||||
|
const uint32_t voltage_flags = (ret.status == ADBMS_OVERVOLT) ? bms_data[ret.bms_id].overVoltage
|
||||||
|
: bms_data[ret.bms_id].underVoltage;
|
||||||
|
|
||||||
|
debug_log(LOG_LEVEL_ERROR, "Cell %s detected on module %d, affected cells: ", error_type, ret.bms_id);
|
||||||
|
|
||||||
|
for (size_t cell = 0; cell < N_CELLS; cell++) {
|
||||||
|
if (voltage_flags & (1UL << cell)) {
|
||||||
|
debug_log_cont(LOG_LEVEL_ERROR, "%u (%d mV) ", cell, bms_data[ret.bms_id].cellVoltages[cell]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!voltage_flags) {
|
||||||
|
debug_log_cont(LOG_LEVEL_ERROR, "none (something went wrong?)");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
update_error_window(true, ret.bms_id);
|
|
||||||
return HAL_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
update_error_window(false, ret.bms_id);
|
update_error_window(true, ret.bms_id);
|
||||||
|
return HAL_ERROR;
|
||||||
battery.pack.min_voltage = 0xFFFF;
|
|
||||||
battery.pack.max_voltage = 0;
|
|
||||||
battery.pack.min_temp = INT16_MAX;
|
|
||||||
battery.pack.max_temp = INT16_MIN;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < N_BMS; i++) {
|
|
||||||
// Initialize min/max indices for this module
|
|
||||||
battery.module[i].min_v_idx = 0;
|
|
||||||
battery.module[i].max_v_idx = 0;
|
|
||||||
battery.module[i].min_t_idx = 0;
|
|
||||||
battery.module[i].max_t_idx = 0;
|
|
||||||
|
|
||||||
// Track min/max voltages
|
|
||||||
for (size_t j = 0; j < N_CELLS; j++) {
|
|
||||||
if (bms_data[i].cellVoltages[j] < battery.pack.min_voltage) {
|
|
||||||
battery.pack.min_voltage = bms_data[i].cellVoltages[j];
|
|
||||||
}
|
|
||||||
if (bms_data[i].cellVoltages[j] > battery.pack.max_voltage) {
|
|
||||||
battery.pack.max_voltage = bms_data[i].cellVoltages[j];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update min/max voltage indices for this module
|
|
||||||
if (bms_data[i].cellVoltages[j] < bms_data[i].cellVoltages[battery.module[i].min_v_idx]) {
|
|
||||||
battery.module[i].min_v_idx = j;
|
|
||||||
}
|
|
||||||
if (bms_data[i].cellVoltages[j] > bms_data[i].cellVoltages[battery.module[i].max_v_idx]) {
|
|
||||||
battery.module[i].max_v_idx = j;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process temperature values
|
update_error_window(false, ret.bms_id);
|
||||||
for (size_t j = 0; j < 10; j++) { //10 GPIOs
|
|
||||||
battery.module[i].cellTemps[j] = ntc_mv_to_celsius(bms_data[i].auxVoltages[j]);
|
|
||||||
|
|
||||||
|
battery.pack.min_voltage = 0xFFFF;
|
||||||
// For new battery struct
|
battery.pack.max_voltage = 0;
|
||||||
if (battery.module[i].cellTemps[j] > battery.pack.max_temp) {
|
battery.pack.min_temp = INT16_MAX;
|
||||||
battery.pack.max_temp = battery.module[i].cellTemps[j];
|
battery.pack.max_temp = INT16_MIN;
|
||||||
}
|
|
||||||
if (battery.module[i].cellTemps[j] < battery.pack.min_temp) {
|
|
||||||
battery.pack.min_temp = battery.module[i].cellTemps[j];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update min/max temperature indices for this module
|
|
||||||
if (battery.module[i].cellTemps[j] < battery.module[i].cellTemps[battery.module[i].min_t_idx]) {
|
|
||||||
battery.module[i].min_t_idx = j;
|
|
||||||
}
|
|
||||||
if (battery.module[i].cellTemps[j] > battery.module[i].cellTemps[battery.module[i].max_t_idx]) {
|
|
||||||
battery.module[i].max_t_idx = j;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for overtemperature condition
|
for (size_t i = 0; i < N_BMS; i++) {
|
||||||
if (battery.module[i].cellTemps[j] > (MAX_TEMP * (uint16_t)(TEMP_CONV))) {
|
// Initialize min/max indices for this module
|
||||||
debug_log(LOG_LEVEL_ERROR, "Cell %u on BMS %u overtemp: %d0 mC", j, i, battery.module[i].cellTemps[j]);
|
battery.module[i].min_v_idx = 0;
|
||||||
can_send_error(TS_ERRORKIND_CELL_OVERTEMP, i);
|
battery.module[i].max_v_idx = 0;
|
||||||
ts_sm_set_error_source(TS_ERROR_SOURCE_SLAVES, TS_ERRORKIND_CELL_OVERTEMP, true);
|
battery.module[i].min_t_idx = 0;
|
||||||
} else {
|
battery.module[i].max_t_idx = 0;
|
||||||
ts_sm_set_error_source(TS_ERROR_SOURCE_SLAVES, TS_ERRORKIND_CELL_OVERTEMP, false);
|
|
||||||
}
|
// Track min/max voltages
|
||||||
|
for (size_t j = 0; j < N_CELLS; j++) {
|
||||||
|
if (bms_data[i].cellVoltages[j] < battery.pack.min_voltage) {
|
||||||
|
battery.pack.min_voltage = bms_data[i].cellVoltages[j];
|
||||||
|
}
|
||||||
|
if (bms_data[i].cellVoltages[j] > battery.pack.max_voltage) {
|
||||||
|
battery.pack.max_voltage = bms_data[i].cellVoltages[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update min/max voltage indices for this module
|
||||||
|
if (bms_data[i].cellVoltages[j] < bms_data[i].cellVoltages[battery.module[i].min_v_idx]) {
|
||||||
|
battery.module[i].min_v_idx = j;
|
||||||
|
}
|
||||||
|
if (bms_data[i].cellVoltages[j] > bms_data[i].cellVoltages[battery.module[i].max_v_idx]) {
|
||||||
|
battery.module[i].max_v_idx = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process temperature values
|
||||||
|
for (size_t j = 0; j < 10; j++) { // 10 GPIOs
|
||||||
|
battery.module[i].cellTemps[j] = ntc_mv_to_celsius(bms_data[i].auxVoltages[j]);
|
||||||
|
|
||||||
|
// For new battery struct
|
||||||
|
if (battery.module[i].cellTemps[j] > battery.pack.max_temp) {
|
||||||
|
battery.pack.max_temp = battery.module[i].cellTemps[j];
|
||||||
|
}
|
||||||
|
if (battery.module[i].cellTemps[j] < battery.pack.min_temp) {
|
||||||
|
battery.pack.min_temp = battery.module[i].cellTemps[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update min/max temperature indices for this module
|
||||||
|
if (battery.module[i].cellTemps[j] < battery.module[i].cellTemps[battery.module[i].min_t_idx]) {
|
||||||
|
battery.module[i].min_t_idx = j;
|
||||||
|
}
|
||||||
|
if (battery.module[i].cellTemps[j] > battery.module[i].cellTemps[battery.module[i].max_t_idx]) {
|
||||||
|
battery.module[i].max_t_idx = j;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for overtemperature condition
|
||||||
|
if (battery.module[i].cellTemps[j] > (MAX_TEMP * (uint16_t)(TEMP_CONV))) {
|
||||||
|
debug_log(LOG_LEVEL_ERROR, "Cell %u on BMS %u overtemp: %d0 mC", j, i, battery.module[i].cellTemps[j]);
|
||||||
|
can_send_error(TS_ERRORKIND_CELL_OVERTEMP, i);
|
||||||
|
ts_sm_set_error_source(TS_ERROR_SOURCE_SLAVES, TS_ERRORKIND_CELL_OVERTEMP, true);
|
||||||
|
} else {
|
||||||
|
ts_sm_set_error_source(TS_ERROR_SOURCE_SLAVES, TS_ERRORKIND_CELL_OVERTEMP, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return HAL_OK;
|
return HAL_OK;
|
||||||
}
|
}
|
@ -1,14 +1,14 @@
|
|||||||
#include "can.h"
|
#include "can.h"
|
||||||
|
|
||||||
#include "ADBMS_Driver.h"
|
#include "ADBMS_Driver.h"
|
||||||
|
#include "battery.h"
|
||||||
#include "imd_monitoring.h"
|
#include "imd_monitoring.h"
|
||||||
#include "isotp.h"
|
#include "isotp.h"
|
||||||
#include "isotp_user_defs.h"
|
|
||||||
#include "isotp_log_backend.h"
|
#include "isotp_log_backend.h"
|
||||||
|
#include "isotp_user_defs.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "shunt_monitoring.h"
|
#include "shunt_monitoring.h"
|
||||||
#include "battery.h"
|
|
||||||
#include "soc_estimation.h"
|
#include "soc_estimation.h"
|
||||||
#include "ts_state_machine.h"
|
#include "ts_state_machine.h"
|
||||||
|
|
||||||
@ -21,158 +21,155 @@
|
|||||||
static isotp_conn_id_t isotp_connection_id = 0;
|
static isotp_conn_id_t isotp_connection_id = 0;
|
||||||
|
|
||||||
bool isotp_transmit(uint16_t id, const uint8_t *data, size_t datalen) {
|
bool isotp_transmit(uint16_t id, const uint8_t *data, size_t datalen) {
|
||||||
return ftcan_transmit(id, data, datalen) == HAL_OK;
|
return ftcan_transmit(id, data, datalen) == HAL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t isotp_get_time() {
|
uint32_t isotp_get_time() { return HAL_GetTick(); }
|
||||||
return HAL_GetTick();
|
|
||||||
}
|
|
||||||
|
|
||||||
void can_init(FDCAN_HandleTypeDef *handle) {
|
void can_init(FDCAN_HandleTypeDef *handle) {
|
||||||
ftcan_init(handle);
|
ftcan_init(handle);
|
||||||
ftcan_add_filter(CAN_ID_SHUNT_BASE, 0xFF0);
|
ftcan_add_filter(CAN_ID_SHUNT_BASE, 0xFF0);
|
||||||
ftcan_add_filter(CAN_ID_AMS_IN, 0xFFF);
|
ftcan_add_filter(CAN_ID_AMS_IN, 0xFFF);
|
||||||
ftcan_add_filter(CAN_ID_AMS_DETAILS_FC, 0xFFF);
|
ftcan_add_filter(CAN_ID_AMS_DETAILS_FC, 0xFFF);
|
||||||
ftcan_add_filter(ISOTP_LOG_FC_CAN_ID, 0xFFF);
|
ftcan_add_filter(ISOTP_LOG_FC_CAN_ID, 0xFFF);
|
||||||
|
|
||||||
auto const status = isotp_add_connection(CAN_ID_AMS_DETAILS_FC, CAN_ID_AMS_DETAILS, ISOTP_FLAGS_NONE);
|
auto const status = isotp_add_connection(CAN_ID_AMS_DETAILS_FC, CAN_ID_AMS_DETAILS, ISOTP_FLAGS_NONE);
|
||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
log_warning("Failed to add ISO-TP connection: %s", isotp_status_to_string((isotp_status_t)status));
|
log_warning("Failed to add ISO-TP connection: %s", isotp_status_to_string((isotp_status_t)status));
|
||||||
}
|
}
|
||||||
isotp_connection_id = (isotp_conn_id_t)status;
|
isotp_connection_id = (isotp_conn_id_t)status;
|
||||||
|
|
||||||
// ftcan_add_filter(CAN_ID_SLAVE_PANIC, 0xFFF);
|
// ftcan_add_filter(CAN_ID_SLAVE_PANIC, 0xFFF);
|
||||||
// ftcan_add_filter(CAN_ID_SLAVE_STATUS_BASE, 0xFF0);
|
// ftcan_add_filter(CAN_ID_SLAVE_STATUS_BASE, 0xFF0);
|
||||||
// ftcan_add_filter(CAN_ID_SLAVE_LOG, 0xFFF);
|
// ftcan_add_filter(CAN_ID_SLAVE_LOG, 0xFFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
HAL_StatusTypeDef can_send_status() {
|
HAL_StatusTypeDef can_send_status() {
|
||||||
uint8_t data[8];
|
uint8_t data[8];
|
||||||
data[0] = ts_state.current_state | (sdc_closed << 7);
|
data[0] = ts_state.current_state | (sdc_closed << 7);
|
||||||
data[1] = roundf(battery.pack.soc); // Use the battery struct now
|
data[1] = roundf(battery.pack.soc); // Use the battery struct now
|
||||||
ftcan_marshal_unsigned(&data[2], battery.pack.min_voltage, 2); // Use the battery struct now
|
ftcan_marshal_unsigned(&data[2], battery.pack.min_voltage, 2); // Use the battery struct now
|
||||||
ftcan_marshal_signed(&data[4], battery.pack.max_temp, 2); // Use the battery struct now
|
ftcan_marshal_signed(&data[4], battery.pack.max_temp, 2); // Use the battery struct now
|
||||||
data[6] = imd_data.state | (imd_data.ok << 7);
|
data[6] = imd_data.state | (imd_data.ok << 7);
|
||||||
if (imd_data.r_iso < 0xFFF) {
|
if (imd_data.r_iso < 0xFFF) {
|
||||||
data[7] = imd_data.r_iso >> 4;
|
data[7] = imd_data.r_iso >> 4;
|
||||||
} else {
|
} else {
|
||||||
data[7] = 0xFF;
|
data[7] = 0xFF;
|
||||||
}
|
}
|
||||||
const HAL_StatusTypeDef ret = ftcan_transmit(CAN_ID_AMS_STATUS, data, sizeof(data));
|
const HAL_StatusTypeDef ret = ftcan_transmit(CAN_ID_AMS_STATUS, data, sizeof(data));
|
||||||
if (ret != HAL_OK) {
|
if (ret != HAL_OK) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
data[0] = (sdc_closed_nodelay << 0) | (ts_error << 1) | (hv_active << 2) |
|
data[0] = (sdc_closed_nodelay << 0) | (ts_error << 1) | (hv_active << 2) | (neg_air_closed << 3) |
|
||||||
(neg_air_closed << 3) | (pos_air_closed << 4) |
|
(pos_air_closed << 4) | (precharge_closed << 5) | (pre_and_air_open << 6);
|
||||||
(precharge_closed << 5) | (pre_and_air_open << 6);
|
return ftcan_transmit(CAN_ID_AMS_SIGNALS, data, 1);
|
||||||
return ftcan_transmit(CAN_ID_AMS_SIGNALS, data, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HAL_StatusTypeDef can_send_details() {
|
HAL_StatusTypeDef can_send_details() {
|
||||||
static uint8_t module_index = 0;
|
static uint8_t module_index = 0;
|
||||||
static uint8_t data[103] = {}; //sizeof(BMS_Chip) + 10 + 1
|
static uint8_t data[103] = {}; // sizeof(BMS_Chip) + 10 + 1
|
||||||
auto const module = &bms_data[module_index];
|
auto const module = &bms_data[module_index];
|
||||||
auto data_ptr = &data[1];
|
auto data_ptr = &data[1];
|
||||||
|
|
||||||
isotp_status_t status = isotp_try_add_message(isotp_connection_id, data, sizeof(data));
|
isotp_status_t status = isotp_try_add_message(isotp_connection_id, data, sizeof(data));
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case ISOTP_OK:
|
case ISOTP_OK:
|
||||||
break;
|
break;
|
||||||
case ISOTP_MESSAGE_ALREADY_IN_FLIGHT:
|
case ISOTP_MESSAGE_ALREADY_IN_FLIGHT:
|
||||||
return HAL_BUSY;
|
return HAL_BUSY;
|
||||||
default:
|
default:
|
||||||
log_warning("isotp_try_add_message failed: %s", isotp_status_to_string(status));
|
log_warning("isotp_try_add_message failed: %s", isotp_status_to_string(status));
|
||||||
return HAL_ERROR;
|
return HAL_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
data[0] = module_index;
|
|
||||||
data_ptr = ftcan_marshal_unsigned(data_ptr, module->bmsID, 8);
|
|
||||||
data_ptr = ftcan_marshal_unsigned(data_ptr, module->status.CS_FLT, 2);
|
|
||||||
data_ptr = ftcan_marshal_unsigned(data_ptr, module->status.CCTS, 2);
|
|
||||||
|
|
||||||
// Marshal status bits into a single byte
|
|
||||||
uint8_t status_bits = 0;
|
|
||||||
status_bits |= module->status.SMED << 0;
|
|
||||||
status_bits |= module->status.SED << 1;
|
|
||||||
status_bits |= module->status.CMED << 2;
|
|
||||||
status_bits |= module->status.CED << 3;
|
|
||||||
status_bits |= module->status.VD_UV << 4;
|
|
||||||
status_bits |= module->status.VD_OV << 5;
|
|
||||||
status_bits |= module->status.VA_UV << 6;
|
|
||||||
status_bits |= module->status.VA_OV << 7;
|
|
||||||
*(data_ptr++) = status_bits;
|
|
||||||
|
|
||||||
// Marshal the rest of the status bits
|
|
||||||
status_bits = 0;
|
|
||||||
status_bits |= module->status.OSCCHK << 0;
|
|
||||||
status_bits |= module->status.TMODCHK << 1;
|
|
||||||
status_bits |= module->status.THSD << 2;
|
|
||||||
status_bits |= module->status.SLEEP << 3;
|
|
||||||
status_bits |= module->status.SPIFLT << 4;
|
|
||||||
status_bits |= module->status.COMPARE << 5;
|
|
||||||
status_bits |= module->status.VDE << 6;
|
|
||||||
status_bits |= module->status.VDEL << 7;
|
|
||||||
*(data_ptr++) = status_bits;
|
|
||||||
|
|
||||||
// Marshal voltage flags
|
|
||||||
data_ptr = ftcan_marshal_unsigned(data_ptr, module->overVoltage, 4);
|
|
||||||
data_ptr = ftcan_marshal_unsigned(data_ptr, module->underVoltage, 4);
|
|
||||||
|
|
||||||
// Marshal temperature and voltages
|
|
||||||
data_ptr = ftcan_marshal_signed(data_ptr, module->internalDieTemp, 2);
|
|
||||||
data_ptr = ftcan_marshal_unsigned(data_ptr, module->analogSupplyVoltage, 2);
|
|
||||||
data_ptr = ftcan_marshal_unsigned(data_ptr, module->digitalSupplyVoltage, 2);
|
|
||||||
data_ptr = ftcan_marshal_unsigned(data_ptr, module->refVoltage, 2);
|
|
||||||
|
|
||||||
// Marshal cell voltages
|
|
||||||
for (int i = 0; i < 16; i++) {
|
|
||||||
data_ptr = ftcan_marshal_signed(data_ptr, module->cellVoltages[i], 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Marshal auxiliary voltages
|
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
data_ptr = ftcan_marshal_signed(data_ptr, module->auxVoltages[i], 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Marshal temperature data
|
data[0] = module_index;
|
||||||
for (int i = 0; i < 10; i++) {
|
data_ptr = ftcan_marshal_unsigned(data_ptr, module->bmsID, 8);
|
||||||
data_ptr = ftcan_marshal_signed(data_ptr, battery.module[module_index].cellTemps[i], 2);
|
data_ptr = ftcan_marshal_unsigned(data_ptr, module->status.CS_FLT, 2);
|
||||||
}
|
data_ptr = ftcan_marshal_unsigned(data_ptr, module->status.CCTS, 2);
|
||||||
|
|
||||||
if ((status = isotp_add_message(isotp_connection_id, data, sizeof(data))) != ISOTP_OK) {
|
// Marshal status bits into a single byte
|
||||||
log_warning("isotp_add_message failed unexpectedly: %s", isotp_status_to_string(status));
|
uint8_t status_bits = 0;
|
||||||
return HAL_ERROR;
|
status_bits |= module->status.SMED << 0;
|
||||||
}
|
status_bits |= module->status.SED << 1;
|
||||||
|
status_bits |= module->status.CMED << 2;
|
||||||
|
status_bits |= module->status.CED << 3;
|
||||||
|
status_bits |= module->status.VD_UV << 4;
|
||||||
|
status_bits |= module->status.VD_OV << 5;
|
||||||
|
status_bits |= module->status.VA_UV << 6;
|
||||||
|
status_bits |= module->status.VA_OV << 7;
|
||||||
|
*(data_ptr++) = status_bits;
|
||||||
|
|
||||||
module_index = (module_index + 1) % N_BMS;
|
// Marshal the rest of the status bits
|
||||||
return HAL_OK;
|
status_bits = 0;
|
||||||
|
status_bits |= module->status.OSCCHK << 0;
|
||||||
|
status_bits |= module->status.TMODCHK << 1;
|
||||||
|
status_bits |= module->status.THSD << 2;
|
||||||
|
status_bits |= module->status.SLEEP << 3;
|
||||||
|
status_bits |= module->status.SPIFLT << 4;
|
||||||
|
status_bits |= module->status.COMPARE << 5;
|
||||||
|
status_bits |= module->status.VDE << 6;
|
||||||
|
status_bits |= module->status.VDEL << 7;
|
||||||
|
*(data_ptr++) = status_bits;
|
||||||
|
|
||||||
|
// Marshal voltage flags
|
||||||
|
data_ptr = ftcan_marshal_unsigned(data_ptr, module->overVoltage, 4);
|
||||||
|
data_ptr = ftcan_marshal_unsigned(data_ptr, module->underVoltage, 4);
|
||||||
|
|
||||||
|
// Marshal temperature and voltages
|
||||||
|
data_ptr = ftcan_marshal_signed(data_ptr, module->internalDieTemp, 2);
|
||||||
|
data_ptr = ftcan_marshal_unsigned(data_ptr, module->analogSupplyVoltage, 2);
|
||||||
|
data_ptr = ftcan_marshal_unsigned(data_ptr, module->digitalSupplyVoltage, 2);
|
||||||
|
data_ptr = ftcan_marshal_unsigned(data_ptr, module->refVoltage, 2);
|
||||||
|
|
||||||
|
// Marshal cell voltages
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
data_ptr = ftcan_marshal_signed(data_ptr, module->cellVoltages[i], 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal auxiliary voltages
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
data_ptr = ftcan_marshal_signed(data_ptr, module->auxVoltages[i], 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal temperature data
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
data_ptr = ftcan_marshal_signed(data_ptr, battery.module[module_index].cellTemps[i], 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((status = isotp_add_message(isotp_connection_id, data, sizeof(data))) != ISOTP_OK) {
|
||||||
|
log_warning("isotp_add_message failed unexpectedly: %s", isotp_status_to_string(status));
|
||||||
|
return HAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
module_index = (module_index + 1) % N_BMS;
|
||||||
|
return HAL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
HAL_StatusTypeDef can_send_error(TSErrorKind kind, uint8_t arg) {
|
HAL_StatusTypeDef can_send_error(TSErrorKind kind, uint8_t arg) {
|
||||||
uint8_t data[2];
|
uint8_t data[2];
|
||||||
data[0] = kind;
|
data[0] = kind;
|
||||||
data[1] = arg;
|
data[1] = arg;
|
||||||
return ftcan_transmit(CAN_ID_AMS_ERROR, data, sizeof(data));
|
return ftcan_transmit(CAN_ID_AMS_ERROR, data, sizeof(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ftcan_msg_received_cb(uint16_t id, size_t len, const uint8_t *data) {
|
void ftcan_msg_received_cb(uint16_t id, size_t len, const uint8_t *data) {
|
||||||
if ((id & 0xFF0) == CAN_ID_SHUNT_BASE) {
|
if ((id & 0xFF0) == CAN_ID_SHUNT_BASE) {
|
||||||
shunt_handle_can_msg(id, data);
|
shunt_handle_can_msg(id, data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case ISOTP_LOG_FC_CAN_ID:
|
case ISOTP_LOG_FC_CAN_ID:
|
||||||
case CAN_ID_AMS_DETAILS_FC:
|
case CAN_ID_AMS_DETAILS_FC:
|
||||||
auto const status = isotp_handle_incoming(id, data, len);
|
auto const status = isotp_handle_incoming(id, data, len);
|
||||||
if (status != ISOTP_OK) {
|
if (status != ISOTP_OK) {
|
||||||
log_debug("Error when handling flow control: %s", isotp_status_to_string(status));
|
log_debug("Error when handling flow control: %s", isotp_status_to_string(status));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CAN_ID_AMS_IN:
|
case CAN_ID_AMS_IN:
|
||||||
ts_sm_handle_ams_in(data);
|
ts_sm_handle_ams_in(data);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,66 +21,61 @@ IMDData imd_data;
|
|||||||
static TIM_HandleTypeDef *htim;
|
static TIM_HandleTypeDef *htim;
|
||||||
|
|
||||||
void imd_init(TIM_HandleTypeDef *handle) {
|
void imd_init(TIM_HandleTypeDef *handle) {
|
||||||
htim = handle;
|
htim = handle;
|
||||||
HAL_TIM_IC_Start_IT(htim, TIM_CHANNEL_1);
|
HAL_TIM_IC_Start_IT(htim, TIM_CHANNEL_1);
|
||||||
HAL_TIM_IC_Start(htim, TIM_CHANNEL_2);
|
HAL_TIM_IC_Start(htim, TIM_CHANNEL_2);
|
||||||
|
|
||||||
imd_data.state = IMD_STATE_UNKNOWN;
|
imd_data.state = IMD_STATE_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *handle) {
|
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *handle) {
|
||||||
if (handle != htim || htim->Channel != HAL_TIM_ACTIVE_CHANNEL_1) {
|
if (handle != htim || htim->Channel != HAL_TIM_ACTIVE_CHANNEL_1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const uint32_t period = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
|
const uint32_t period = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
|
||||||
if (period == 0) {
|
if (period == 0) {
|
||||||
// First edge, ignore
|
// First edge, ignore
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
imd_data.last_high = HAL_GetTick();
|
imd_data.last_high = HAL_GetTick();
|
||||||
|
|
||||||
imd_data.freq = FREQ_TIMER / period;
|
imd_data.freq = FREQ_TIMER / period;
|
||||||
const uint32_t high_time = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2);
|
const uint32_t high_time = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2);
|
||||||
imd_data.duty_cycle = (100 * high_time) / period;
|
imd_data.duty_cycle = (100 * high_time) / period;
|
||||||
|
|
||||||
// Check PWM frequency for state determination
|
// Check PWM frequency for state determination
|
||||||
if (imd_data.freq > FREQ_NORMAL - FREQ_TOLERANCE &&
|
if (imd_data.freq > FREQ_NORMAL - FREQ_TOLERANCE && imd_data.freq < FREQ_NORMAL + FREQ_TOLERANCE) {
|
||||||
imd_data.freq < FREQ_NORMAL + FREQ_TOLERANCE) {
|
imd_data.state = IMD_STATE_NORMAL;
|
||||||
imd_data.state = IMD_STATE_NORMAL;
|
} else if (imd_data.freq > FREQ_UNDERVOLTAGE - FREQ_TOLERANCE &&
|
||||||
} else if (imd_data.freq > FREQ_UNDERVOLTAGE - FREQ_TOLERANCE &&
|
imd_data.freq < FREQ_UNDERVOLTAGE + FREQ_TOLERANCE) {
|
||||||
imd_data.freq < FREQ_UNDERVOLTAGE + FREQ_TOLERANCE) {
|
imd_data.state = IMD_STATE_UNDERVOLTAGE;
|
||||||
imd_data.state = IMD_STATE_UNDERVOLTAGE;
|
} else if (imd_data.freq > FREQ_SST - FREQ_TOLERANCE && imd_data.freq < FREQ_SST + FREQ_TOLERANCE) {
|
||||||
} else if (imd_data.freq > FREQ_SST - FREQ_TOLERANCE &&
|
imd_data.state = IMD_STATE_SST;
|
||||||
imd_data.freq < FREQ_SST + FREQ_TOLERANCE) {
|
} else if (imd_data.freq > FREQ_DEV_ERROR - FREQ_TOLERANCE && imd_data.freq < FREQ_DEV_ERROR + FREQ_TOLERANCE) {
|
||||||
imd_data.state = IMD_STATE_SST;
|
imd_data.state = IMD_STATE_DEV_ERROR;
|
||||||
} else if (imd_data.freq > FREQ_DEV_ERROR - FREQ_TOLERANCE &&
|
} else if (imd_data.freq > FREQ_GND_FAULT - FREQ_TOLERANCE && imd_data.freq < FREQ_GND_FAULT + FREQ_TOLERANCE) {
|
||||||
imd_data.freq < FREQ_DEV_ERROR + FREQ_TOLERANCE) {
|
imd_data.state = IMD_STATE_GND_FAULT;
|
||||||
imd_data.state = IMD_STATE_DEV_ERROR;
|
} else {
|
||||||
} else if (imd_data.freq > FREQ_GND_FAULT - FREQ_TOLERANCE &&
|
imd_data.state = IMD_STATE_UNKNOWN;
|
||||||
imd_data.freq < FREQ_GND_FAULT + FREQ_TOLERANCE) {
|
}
|
||||||
imd_data.state = IMD_STATE_GND_FAULT;
|
|
||||||
} else {
|
// Calculate R_iso
|
||||||
imd_data.state = IMD_STATE_UNKNOWN;
|
if (imd_data.state == IMD_STATE_NORMAL || imd_data.state == IMD_STATE_UNDERVOLTAGE) {
|
||||||
}
|
if (imd_data.duty_cycle < RISO_MIN_DUTY_CYCLE) {
|
||||||
|
imd_data.r_iso = RISO_MAX;
|
||||||
// Calculate R_iso
|
} else {
|
||||||
if (imd_data.state == IMD_STATE_NORMAL ||
|
imd_data.r_iso = (90 * 1200) / (imd_data.duty_cycle - 5) - 1200;
|
||||||
imd_data.state == IMD_STATE_UNDERVOLTAGE) {
|
}
|
||||||
if (imd_data.duty_cycle < RISO_MIN_DUTY_CYCLE) {
|
|
||||||
imd_data.r_iso = RISO_MAX;
|
|
||||||
} else {
|
|
||||||
imd_data.r_iso = (90 * 1200) / (imd_data.duty_cycle - 5) - 1200;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void imd_update() {
|
void imd_update() {
|
||||||
imd_data.ok = HAL_GPIO_ReadPin(IMD_OK_GPIO_Port, IMD_OK_Pin);
|
imd_data.ok = HAL_GPIO_ReadPin(IMD_OK_GPIO_Port, IMD_OK_Pin);
|
||||||
if (HAL_GetTick() - imd_data.last_high > PWM_TIMEOUT) {
|
if (HAL_GetTick() - imd_data.last_high > PWM_TIMEOUT) {
|
||||||
if (HAL_GPIO_ReadPin(IMD_M_GPIO_Port, IMD_M_Pin) == GPIO_PIN_SET) {
|
if (HAL_GPIO_ReadPin(IMD_M_GPIO_Port, IMD_M_Pin) == GPIO_PIN_SET) {
|
||||||
imd_data.state = IMD_STATE_SHORTCIRCUIT_SUPPLY;
|
imd_data.state = IMD_STATE_SHORTCIRCUIT_SUPPLY;
|
||||||
} else {
|
} else {
|
||||||
imd_data.state = IMD_STATE_SHORTCIRCUIT_GND;
|
imd_data.state = IMD_STATE_SHORTCIRCUIT_GND;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -67,27 +67,26 @@ static float soc_for_ocv(uint16_t ocv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void soc_update() {
|
void soc_update() {
|
||||||
const uint32_t now = HAL_GetTick();
|
const uint32_t now = HAL_GetTick();
|
||||||
if (abs(shunt_data.current) >= SOC_ESTIMATION_NO_CURRENT_THRESH) {
|
if (abs(shunt_data.current) >= SOC_ESTIMATION_NO_CURRENT_THRESH) {
|
||||||
last_current_time = now;
|
last_current_time = now;
|
||||||
if (!current_was_flowing) {
|
if (!current_was_flowing) {
|
||||||
soc_before_current = battery.pack.soc;
|
soc_before_current = battery.pack.soc;
|
||||||
mAs_before_current = shunt_data.current_counter;
|
mAs_before_current = shunt_data.current_counter;
|
||||||
|
}
|
||||||
|
current_was_flowing = 1;
|
||||||
|
} else {
|
||||||
|
current_was_flowing = 0;
|
||||||
}
|
}
|
||||||
current_was_flowing = 1;
|
|
||||||
} else {
|
|
||||||
current_was_flowing = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (now - last_current_time >= SOC_ESTIMATION_NO_CURRENT_TIME ||
|
if (now - last_current_time >= SOC_ESTIMATION_NO_CURRENT_TIME || last_current_time == 0) {
|
||||||
last_current_time == 0) {
|
// Assume we're measuring OCV if there's been no current for a while (or
|
||||||
// Assume we're measuring OCV if there's been no current for a while (or
|
// we've just turned on the battery).
|
||||||
// we've just turned on the battery).
|
battery.pack.soc = soc_for_ocv(battery.pack.min_voltage);
|
||||||
battery.pack.soc = soc_for_ocv(battery.pack.min_voltage);
|
} else {
|
||||||
} else {
|
// Otherwise, use the current counter to update SoC
|
||||||
// Otherwise, use the current counter to update SoC
|
const float as_delta = shunt_data.current_counter - mAs_before_current;
|
||||||
const float as_delta = shunt_data.current_counter - mAs_before_current;
|
const float soc_delta = as_delta / SOC_ESTIMATION_BATTERY_CAPACITY * 100;
|
||||||
const float soc_delta = as_delta / SOC_ESTIMATION_BATTERY_CAPACITY * 100;
|
battery.pack.soc = soc_before_current - soc_delta;
|
||||||
battery.pack.soc = soc_before_current - soc_delta;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user