Compare commits

...

3 Commits

10 changed files with 459 additions and 459 deletions

View File

@ -2,15 +2,16 @@
#ifndef __CONFIG_ADBMS6830_H #ifndef __CONFIG_ADBMS6830_H
#define __CONFIG_ADBMS6830_H #define __CONFIG_ADBMS6830_H
#include "main.h" #include "main.h"
#define N_BMS 2
[[maybe_unused]] static void mcuAdbmsCSLow() { #define N_BMS 2
#define ADBMS_SPI_TIMEOUT 100 // Timeout in ms
[[maybe_unused]] 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);
} }
[[maybe_unused]] static void mcuAdbmsCSHigh() { [[maybe_unused]] static inline void mcuAdbmsCSHigh() {
HAL_GPIO_WritePin(AMS_CS_GPIO_Port, AMS_CS_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(AMS_CS_GPIO_Port, AMS_CS_Pin, GPIO_PIN_SET);
} }
#endif //__CONFIG_ADBMS6830_H #endif //__CONFIG_ADBMS6830_H

View File

@ -52,7 +52,8 @@ static inline uint32_t __swo_putc(uint32_t c, unsigned int channel) {
return (c); return (c);
} }
#define DEBUG_CHANNEL_ENABLED(channel) ({ \ #define DEBUG_CHANNEL_ENABLED(channel) \
({ \
unsigned int ch = (channel); \ unsigned int ch = (channel); \
(ch < 32) ? __ITM_channel_enabled(ch) : false; \ (ch < 32) ? __ITM_channel_enabled(ch) : false; \
}) })

View File

@ -9,10 +9,11 @@
#define INC_ADBMS_ABSTRACTION_H_ #define INC_ADBMS_ABSTRACTION_H_
#include "ADBMS_CMD_MAKROS.h" #include "ADBMS_CMD_MAKROS.h"
#include "ADBMS_LL_Driver.h"
#include "ADBMS_Driver.h" #include "ADBMS_Driver.h"
#include "ADBMS_LL_Driver.h"
#include "main.h" #include "main.h"
// see table 103 in datasheet (page 71) // see table 103 in datasheet (page 71)
#define DEFAULT_UV 417 // VUV * 16 * 150 uV + 1.5 V Default Setting 2.5V #define DEFAULT_UV 417 // VUV * 16 * 150 uV + 1.5 V Default Setting 2.5V
#define DEFAULT_OV 1125 // VOV * 16 * 150 uV + 1.5 V Default Setting 4.2V #define DEFAULT_OV 1125 // VOV * 16 * 150 uV + 1.5 V Default Setting 4.2V

View File

@ -1,35 +1,28 @@
#ifndef ADBMS_DRIVER_H #ifndef ADBMS_DRIVER_H
#define ADBMS_DRIVER_H #define ADBMS_DRIVER_H
#include "stm32h7xx_hal.h" #include "config_ADBMS6830.h"
#include <stdint.h> #include <stdint.h>
#define ERROR_TIME_THRESH 150 // ms #define ERROR_TIME_THRESH 150 // ms
typedef enum : uint16_t { typedef enum : uint16_t {
ADBMS_OK = 0x00, // same as STM32 HAL status ADBMS_NO_ERROR = 0,
ADBMS_ERROR = 0x01, //
ADBMS_BUSY = 0x02, //
ADBMS_TIMEOUT = 0x03 //
} ADBMS_StatusTypeDef;
typedef enum : uint16_t {
ADBMS_NONE = 0,
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, // BMS went to sleep too many times
ADBMS_INTERNAL_BMS_CHECKSUM_FAIL, ADBMS_INTERNAL_BMS_CHECKSUM_FAIL, // BMS CRC failed too many times
ADBMS_INTERNAL_BMS_OVERTEMP, ADBMS_INTERNAL_BMS_OVERTEMP,
ADBMS_INTERNAL_BMS_FAULT, ADBMS_INTERNAL_BMS_FAULT,
NUM_ERROR_KINDS NUM_ERROR_KINDS
} ADBMS_ErrorKind; } ADBMS_Status;
typedef struct { typedef struct {
ADBMS_StatusTypeDef status : 16; ADBMS_Status status : 16;
ADBMS_ErrorKind error : 16; // zero if status is ADBMS_OK int16_t bms_id : 16; // the BMS that caused the error, zero if no error, -1 if unknown BMS
} ADBMS_DetailedStatus; } ADBMS_DetailedStatus;
typedef struct { typedef struct {
@ -83,6 +76,7 @@ typedef struct {
extern uint32_t error_sources; // Bitfield of error sources extern uint32_t error_sources; // Bitfield of error sources
extern SlaveErrorData error_data[NUM_ERROR_KINDS]; extern SlaveErrorData error_data[NUM_ERROR_KINDS];
extern Cell_Module modules[N_BMS];
[[gnu::nonnull]] ADBMS_DetailedStatus AMS_Init(SPI_HandleTypeDef* hspi); [[gnu::nonnull]] ADBMS_DetailedStatus AMS_Init(SPI_HandleTypeDef* hspi);

View File

@ -11,8 +11,8 @@
static_assert(sizeof(error_sources) * CHAR_BIT >= NUM_ERROR_KINDS, static_assert(sizeof(error_sources) * CHAR_BIT >= NUM_ERROR_KINDS,
"error_sources is too small to hold all error sources"); "error_sources is too small to hold all error sources");
void set_error_source(ADBMS_ErrorKind source); void set_error_source(ADBMS_Status source);
void clear_error_source(ADBMS_ErrorKind source); void clear_error_source(ADBMS_Status source);
#endif //AMS_MASTER_CODE_ADBMS_ERROR_H #endif //AMS_MASTER_CODE_ADBMS_ERROR_H

View File

@ -14,11 +14,17 @@
extern uint8_t numberofCells; extern uint8_t numberofCells;
static const char* const HAL_Statuses[] = {"HAL_OK", "HAL_ERROR", "HAL_BUSY", "HAL_TIMEOUT"};
#define CHECK_RETURN(x) \ #define CHECK_RETURN(x) \
do { \ do { \
HAL_StatusTypeDef status = x; \ HAL_StatusTypeDef status = x; \
if (status != 0) \ if (status != 0) { \
debug_log(LOG_LEVEL_ERROR, "@%s:%s:%d: %s failed with status %d (%s)", __FILE_NAME__, __func__, __LINE__, \
#x, status, \
(status < (sizeof(HAL_Statuses) / sizeof(HAL_Statuses[0]))) ? HAL_Statuses[status] : "Unknown"); \
return status; \ return status; \
} \
} while (0) } while (0)
HAL_StatusTypeDef amsReset() { HAL_StatusTypeDef amsReset() {
@ -26,7 +32,21 @@ HAL_StatusTypeDef amsReset() {
readCMD(SRST, CMD_EMPTY_BUFFER, 0); readCMD(SRST, CMD_EMPTY_BUFFER, 0);
uint8_t sidbuffer[CMD_BUFFER_SIZE(SID_GROUP_SIZE)] = {}; uint8_t sidbuffer[CMD_BUFFER_SIZE(SID_GROUP_SIZE)] = {};
CHECK_RETURN(readCMD(RDSID, sidbuffer, SID_GROUP_SIZE)); if (readCMD(RDSID, sidbuffer, SID_GROUP_SIZE) != HAL_OK) {
bool nonzero = false;
for (size_t i = 0; i < N_BMS; i++) {
if (sidbuffer[BUFFER_BMS_OFFSET(i, SID_GROUP_SIZE)] != 0) {
nonzero = true;
}
}
if (nonzero) {
debug_log(LOG_LEVEL_ERROR,
"CRC failure when reading BMS IDs, but some IDs are nonzero. --- Intermittent connection?");
} else {
debug_log(LOG_LEVEL_ERROR, "CRC failure when reading BMS IDs, all IDs are zero. --- No connection?");
}
return HAL_ERROR;
}
debug_log(LOG_LEVEL_INFO, "BMS reset complete"); debug_log(LOG_LEVEL_INFO, "BMS reset complete");
@ -69,83 +89,84 @@ HAL_StatusTypeDef amsWakeUp() {
} }
HAL_StatusTypeDef amsCellMeasurement(Cell_Module (*module)[N_BMS]) { HAL_StatusTypeDef amsCellMeasurement(Cell_Module (*module)[N_BMS]) {
#warning check conversion counter to ensure that continous conversion has not been stopped #warning check conversion counter to ensure that continuous conversion has not been stopped
#warning check for OW conditions: ADSV | ADSV_OW_0 / ADSV_OW_1 #warning check for OW conditions: ADSV | ADSV_OW_0 / ADSV_OW_1
return amsReadCellVoltages(module); return amsReadCellVoltages(module);
} }
HAL_StatusTypeDef amsAuxAndStatusMeasurement(Cell_Module (*module)[N_BMS]) { HAL_StatusTypeDef amsAuxAndStatusMeasurement(Cell_Module (*module)[N_BMS]) {
uint8_t rxbuf[CMD_BUFFER_SIZE(STATUS_GROUP_C_SIZE)] = {}; uint8_t rxbuf[CMD_BUFFER_SIZE(STATUS_GROUP_C_SIZE)] = {};
CHECK_RETURN(readCMD(RDSTATC, rxbuf, STATUS_GROUP_C_SIZE)); CHECK_RETURN(readCMD(RDSTATC, rxbuf, STATUS_GROUP_C_SIZE));
for (size_t i = 0; i < N_BMS; i++) { for (size_t i = 0; i < N_BMS; i++) {
size_t offset = BUFFER_BMS_OFFSET(i, STATUS_GROUP_C_SIZE); size_t offset = BUFFER_BMS_OFFSET(i, STATUS_GROUP_C_SIZE);
module[i]->status.CS_FLT = rxbuf[offset + 0] | (rxbuf[offset + 1] << 8); (*module)[i].status.CS_FLT = rxbuf[offset + 0] | (rxbuf[offset + 1] << 8);
module[i]->status.CCTS = rxbuf[offset + 2] | (rxbuf[offset + 3] << 8); (*module)[i].status.CCTS = rxbuf[offset + 2] | (rxbuf[offset + 3] << 8);
module[i]->status.VA_OV = (rxbuf[offset + 4] >> 7) & 0x01; (*module)[i].status.VA_OV = (rxbuf[offset + 4] >> 7) & 0x01;
module[i]->status.VA_UV = (rxbuf[offset + 4] >> 6) & 0x01; (*module)[i].status.VA_UV = (rxbuf[offset + 4] >> 6) & 0x01;
module[i]->status.VD_OV = (rxbuf[offset + 4] >> 5) & 0x01; (*module)[i].status.VD_OV = (rxbuf[offset + 4] >> 5) & 0x01;
module[i]->status.VD_UV = (rxbuf[offset + 4] >> 4) & 0x01; (*module)[i].status.VD_UV = (rxbuf[offset + 4] >> 4) & 0x01;
module[i]->status.CED = (rxbuf[offset + 4] >> 3) & 0x01; (*module)[i].status.CED = (rxbuf[offset + 4] >> 3) & 0x01;
module[i]->status.CMED = (rxbuf[offset + 4] >> 2) & 0x01; (*module)[i].status.CMED = (rxbuf[offset + 4] >> 2) & 0x01;
module[i]->status.SED = (rxbuf[offset + 4] >> 1) & 0x01; (*module)[i].status.SED = (rxbuf[offset + 4] >> 1) & 0x01;
module[i]->status.SMED = (rxbuf[offset + 4] >> 0) & 0x01; (*module)[i].status.SMED = (rxbuf[offset + 4] >> 0) & 0x01;
module[i]->status.VDEL = (rxbuf[offset + 5] >> 7) & 0x01; (*module)[i].status.VDEL = (rxbuf[offset + 5] >> 7) & 0x01;
module[i]->status.VDE = (rxbuf[offset + 5] >> 6) & 0x01; (*module)[i].status.VDE = (rxbuf[offset + 5] >> 6) & 0x01;
module[i]->status.COMPARE= (rxbuf[offset + 5] >> 5) & 0x01; (*module)[i].status.COMPARE = (rxbuf[offset + 5] >> 5) & 0x01;
module[i]->status.SPIFLT = (rxbuf[offset + 5] >> 4) & 0x01; (*module)[i].status.SPIFLT = (rxbuf[offset + 5] >> 4) & 0x01;
module[i]->status.SLEEP = (rxbuf[offset + 5] >> 3) & 0x01; (*module)[i].status.SLEEP = (rxbuf[offset + 5] >> 3) & 0x01;
module[i]->status.THSD = (rxbuf[offset + 5] >> 2) & 0x01; (*module)[i].status.THSD = (rxbuf[offset + 5] >> 2) & 0x01;
module[i]->status.TMODCHK= (rxbuf[offset + 5] >> 1) & 0x01; (*module)[i].status.TMODCHK = (rxbuf[offset + 5] >> 1) & 0x01;
module[i]->status.OSCCHK = (rxbuf[offset + 5] >> 0) & 0x01; (*module)[i].status.OSCCHK = (rxbuf[offset + 5] >> 0) & 0x01;
} }
if (pollCMD(PLAUX) == HAL_BUSY) { if (pollCMD(PLAUX) == HAL_BUSY) {
return HAL_BUSY; return HAL_BUSY;
} }
CHECK_RETURN(readCMD(RDAUXA, rxbuf, AUX_GROUP_A_SIZE)); //STATUS_GROUP_C_SIZE is the same as AUX_GROUP_A_SIZE, so we can reuse the buffer CHECK_RETURN(
readCMD(RDAUXA, rxbuf,
AUX_GROUP_A_SIZE)); // STATUS_GROUP_C_SIZE is the same as AUX_GROUP_A_SIZE, so we can reuse the buffer
for (size_t i = 0; i < N_BMS; i++) { for (size_t i = 0; i < N_BMS; i++) {
size_t offset = BUFFER_BMS_OFFSET(i, AUX_GROUP_A_SIZE); size_t offset = BUFFER_BMS_OFFSET(i, AUX_GROUP_A_SIZE);
module[i]->auxVoltages[0] = mV_from_ADBMS6830(rxbuf[offset + 0] | (rxbuf[offset + 1] << 8)); (*module)[i].auxVoltages[0] = mV_from_ADBMS6830(rxbuf[offset + 0] | (rxbuf[offset + 1] << 8));
module[i]->auxVoltages[1] = mV_from_ADBMS6830(rxbuf[offset + 2] | (rxbuf[offset + 3] << 8)); (*module)[i].auxVoltages[1] = mV_from_ADBMS6830(rxbuf[offset + 2] | (rxbuf[offset + 3] << 8));
module[i]->auxVoltages[2] = mV_from_ADBMS6830(rxbuf[offset + 4] | (rxbuf[offset + 5] << 8)); (*module)[i].auxVoltages[2] = mV_from_ADBMS6830(rxbuf[offset + 4] | (rxbuf[offset + 5] << 8));
} }
CHECK_RETURN(readCMD(RDAUXB, rxbuf, AUX_GROUP_B_SIZE)); CHECK_RETURN(readCMD(RDAUXB, rxbuf, AUX_GROUP_B_SIZE));
for (size_t i = 0; i < N_BMS; i++) { for (size_t i = 0; i < N_BMS; i++) {
size_t offset = BUFFER_BMS_OFFSET(i, AUX_GROUP_B_SIZE); size_t offset = BUFFER_BMS_OFFSET(i, AUX_GROUP_B_SIZE);
module[i]->auxVoltages[3] = mV_from_ADBMS6830(rxbuf[offset + 0] | (rxbuf[offset + 1] << 8)); (*module)[i].auxVoltages[3] = mV_from_ADBMS6830(rxbuf[offset + 0] | (rxbuf[offset + 1] << 8));
module[i]->auxVoltages[4] = mV_from_ADBMS6830(rxbuf[offset + 2] | (rxbuf[offset + 3] << 8)); (*module)[i].auxVoltages[4] = mV_from_ADBMS6830(rxbuf[offset + 2] | (rxbuf[offset + 3] << 8));
module[i]->auxVoltages[5] = mV_from_ADBMS6830(rxbuf[offset + 4] | (rxbuf[offset + 5] << 8)); (*module)[i].auxVoltages[5] = mV_from_ADBMS6830(rxbuf[offset + 4] | (rxbuf[offset + 5] << 8));
} }
CHECK_RETURN(readCMD(RDAUXC, rxbuf, AUX_GROUP_C_SIZE)); CHECK_RETURN(readCMD(RDAUXC, rxbuf, AUX_GROUP_C_SIZE));
for (size_t i = 0; i < N_BMS; i++) { for (size_t i = 0; i < N_BMS; i++) {
size_t offset = BUFFER_BMS_OFFSET(i, AUX_GROUP_C_SIZE); size_t offset = BUFFER_BMS_OFFSET(i, AUX_GROUP_C_SIZE);
module[i]->auxVoltages[6] = mV_from_ADBMS6830(rxbuf[offset + 0] | (rxbuf[offset + 1] << 8)); (*module)[i].auxVoltages[6] = mV_from_ADBMS6830(rxbuf[offset + 0] | (rxbuf[offset + 1] << 8));
module[i]->auxVoltages[7] = mV_from_ADBMS6830(rxbuf[offset + 2] | (rxbuf[offset + 3] << 8)); (*module)[i].auxVoltages[7] = mV_from_ADBMS6830(rxbuf[offset + 2] | (rxbuf[offset + 3] << 8));
module[i]->auxVoltages[8] = mV_from_ADBMS6830(rxbuf[offset + 4] | (rxbuf[offset + 5] << 8)); (*module)[i].auxVoltages[8] = mV_from_ADBMS6830(rxbuf[offset + 4] | (rxbuf[offset + 5] << 8));
} }
CHECK_RETURN(readCMD(RDAUXD, rxbuf, AUX_GROUP_D_SIZE)); CHECK_RETURN(readCMD(RDAUXD, rxbuf, AUX_GROUP_D_SIZE));
for (size_t i = 0; i < N_BMS; i++) { for (size_t i = 0; i < N_BMS; i++) {
size_t offset = BUFFER_BMS_OFFSET(i, AUX_GROUP_D_SIZE); size_t offset = BUFFER_BMS_OFFSET(i, AUX_GROUP_D_SIZE);
module[i]->auxVoltages[9] = mV_from_ADBMS6830(rxbuf[offset + 0] | (rxbuf[offset + 1] << 8)); (*module)[i].auxVoltages[9] = mV_from_ADBMS6830(rxbuf[offset + 0] | (rxbuf[offset + 1] << 8));
} }
CHECK_RETURN(readCMD(RDSTATA, rxbuf, STATUS_GROUP_A_SIZE)); CHECK_RETURN(readCMD(RDSTATA, rxbuf, STATUS_GROUP_A_SIZE));
for (size_t i = 0; i < N_BMS; i++) { for (size_t i = 0; i < N_BMS; i++) {
size_t offset = BUFFER_BMS_OFFSET(i, STATUS_GROUP_A_SIZE); size_t offset = BUFFER_BMS_OFFSET(i, STATUS_GROUP_A_SIZE);
module[i]->internalDieTemp = rxbuf[offset + 2] | (rxbuf[offset + 3] << 8); (*module)[i].internalDieTemp = rxbuf[offset + 2] | (rxbuf[offset + 3] << 8);
} }
CHECK_RETURN(readCMD(RDSTATB, rxbuf, STATUS_GROUP_B_SIZE)); CHECK_RETURN(readCMD(RDSTATB, rxbuf, STATUS_GROUP_B_SIZE));
for (size_t i = 0; i < N_BMS; i++) { for (size_t i = 0; i < N_BMS; i++) {
size_t offset = BUFFER_BMS_OFFSET(i, STATUS_GROUP_B_SIZE); size_t offset = BUFFER_BMS_OFFSET(i, STATUS_GROUP_B_SIZE);
module[i]->digitalSupplyVoltage = mV_from_ADBMS6830(rxbuf[offset + 0] | (rxbuf[offset + 1] << 8)); (*module)[i].digitalSupplyVoltage = mV_from_ADBMS6830(rxbuf[offset + 0] | (rxbuf[offset + 1] << 8));
module[i]->analogSupplyVoltage = mV_from_ADBMS6830(rxbuf[offset + 2] | (rxbuf[offset + 3] << 8)); (*module)[i].analogSupplyVoltage = mV_from_ADBMS6830(rxbuf[offset + 2] | (rxbuf[offset + 3] << 8));
module[i]->refVoltage = mV_from_ADBMS6830(rxbuf[offset + 4] | (rxbuf[offset + 5] << 8)); (*module)[i].refVoltage = mV_from_ADBMS6830(rxbuf[offset + 4] | (rxbuf[offset + 5] << 8));
} }
CHECK_RETURN(writeCMD(ADAX | ADAX_CONV_ALL, rxbuf, 0)); // start aux conversion for next iteration CHECK_RETURN(writeCMD(ADAX | ADAX_CONV_ALL, rxbuf, 0)); // start aux conversion for next iteration
@ -212,7 +233,8 @@ HAL_StatusTypeDef amsConfigOverUnderVoltage(uint16_t overVoltage, uint16_t under
return HAL_ERROR; return HAL_ERROR;
} }
debug_log(LOG_LEVEL_INFO, "Configuring OV/UV thresholds to %f/%f", mV_from_ADBMS6830(overVoltage), mV_from_ADBMS6830(underVoltage)); debug_log(LOG_LEVEL_INFO, "Configuring OV/UV thresholds to %f/%f", mV_from_ADBMS6830(overVoltage),
mV_from_ADBMS6830(underVoltage));
CHECK_RETURN(readCMD(RDCFGB, buffer, CFG_GROUP_B_SIZE)); CHECK_RETURN(readCMD(RDCFGB, buffer, CFG_GROUP_B_SIZE));
@ -241,15 +263,16 @@ HAL_StatusTypeDef amsCheckUnderOverVoltage(Cell_Module (*module)[N_BMS]) {
for (size_t i = 0; i < N_BMS; i++) { for (size_t i = 0; i < N_BMS; i++) {
size_t offset = BUFFER_BMS_OFFSET(i, STATUS_GROUP_D_SIZE); size_t offset = BUFFER_BMS_OFFSET(i, STATUS_GROUP_D_SIZE);
uint32_t ov_uv_data = 0; uint32_t ov_uv_data = 0;
ov_uv_data = (regbuffer[offset + 0] << 0) | (regbuffer[offset + 1] << 8) | ov_uv_data = (regbuffer[offset + 0] << 0) | (regbuffer[offset + 1] << 8) | (regbuffer[offset + 2] << 16) |
(regbuffer[offset + 2] << 16) | (regbuffer[offset + 3] << 24); (regbuffer[offset + 3] << 24);
module[i]->overVoltage = 0; (*module)[i].overVoltage = 0;
module[i]->underVoltage = 0; (*module)[i].underVoltage = 0;
for (size_t j = 0; j < numberofCells; j++) { // ov/uv flags are 1-bit flags for each cell C0UV, C0OV, C1UV, C1OV, ... for (size_t j = 0; j < numberofCells;
module[i]->underVoltage |= (ov_uv_data >> (j * 2)) & 0x01; j++) { // ov/uv flags are 1-bit flags for each cell C0UV, C0OV, C1UV, C1OV, ...
module[i]->overVoltage |= (ov_uv_data >> (j * 2 + 1)) & 0x01; (*module)[i].underVoltage |= (ov_uv_data >> (j * 2)) & 0x01;
(*module)[i].overVoltage |= (ov_uv_data >> (j * 2 + 1)) & 0x01;
} }
} }
@ -261,9 +284,7 @@ HAL_StatusTypeDef amsClearFlag() {
return writeCMD(CLRFLAG, buffer, 6); return writeCMD(CLRFLAG, buffer, 6);
} }
HAL_StatusTypeDef amsClearAux() { HAL_StatusTypeDef amsClearAux() { return writeCMD(CLRAUX, CMD_EMPTY_BUFFER, 0); }
return writeCMD(CLRAUX, CMD_EMPTY_BUFFER, 0);
}
HAL_StatusTypeDef amsReadCellVoltages(Cell_Module (*module)[N_BMS]) { HAL_StatusTypeDef amsReadCellVoltages(Cell_Module (*module)[N_BMS]) {
uint8_t rxbuffer[CMD_BUFFER_SIZE(CV_GROUP_A_SIZE)] = {}; uint8_t rxbuffer[CMD_BUFFER_SIZE(CV_GROUP_A_SIZE)] = {};
@ -271,54 +292,56 @@ HAL_StatusTypeDef amsReadCellVoltages(Cell_Module (*module)[N_BMS]) {
CHECK_RETURN(readCMD(RDCVA, rxbuffer, CV_GROUP_A_SIZE)); CHECK_RETURN(readCMD(RDCVA, rxbuffer, CV_GROUP_A_SIZE));
for (size_t i = 0; i < N_BMS; i++) { for (size_t i = 0; i < N_BMS; i++) {
size_t offset = BUFFER_BMS_OFFSET(i, CV_GROUP_A_SIZE); size_t offset = BUFFER_BMS_OFFSET(i, CV_GROUP_A_SIZE);
module[i]->cellVoltages[0] = mV_from_ADBMS6830(rxbuffer[offset + 0] | (rxbuffer[offset + 1] << 8)); (*module)[i].cellVoltages[0] = mV_from_ADBMS6830(rxbuffer[offset + 0] | (rxbuffer[offset + 1] << 8));
module[i]->cellVoltages[1] = mV_from_ADBMS6830(rxbuffer[offset + 2] | (rxbuffer[offset + 3] << 8)); (*module)[i].cellVoltages[1] = mV_from_ADBMS6830(rxbuffer[offset + 2] | (rxbuffer[offset + 3] << 8));
module[i]->cellVoltages[2] = mV_from_ADBMS6830(rxbuffer[offset + 4] | (rxbuffer[offset + 5] << 8)); (*module)[i].cellVoltages[2] = mV_from_ADBMS6830(rxbuffer[offset + 4] | (rxbuffer[offset + 5] << 8));
} }
CHECK_RETURN(readCMD(RDCVB, rxbuffer, CV_GROUP_A_SIZE)); CHECK_RETURN(readCMD(RDCVB, rxbuffer, CV_GROUP_A_SIZE));
for (size_t i = 0; i < N_BMS; i++) { for (size_t i = 0; i < N_BMS; i++) {
size_t offset = BUFFER_BMS_OFFSET(i, CV_GROUP_A_SIZE); size_t offset = BUFFER_BMS_OFFSET(i, CV_GROUP_A_SIZE);
module[i]->cellVoltages[3] = mV_from_ADBMS6830(rxbuffer[offset + 0] | (rxbuffer[offset + 1] << 8)); (*module)[i].cellVoltages[3] = mV_from_ADBMS6830(rxbuffer[offset + 0] | (rxbuffer[offset + 1] << 8));
module[i]->cellVoltages[4] = mV_from_ADBMS6830(rxbuffer[offset + 2] | (rxbuffer[offset + 3] << 8)); (*module)[i].cellVoltages[4] = mV_from_ADBMS6830(rxbuffer[offset + 2] | (rxbuffer[offset + 3] << 8));
module[i]->cellVoltages[5] = mV_from_ADBMS6830(rxbuffer[offset + 4] | (rxbuffer[offset + 5] << 8)); (*module)[i].cellVoltages[5] = mV_from_ADBMS6830(rxbuffer[offset + 4] | (rxbuffer[offset + 5] << 8));
} }
CHECK_RETURN(readCMD(RDCVC, rxbuffer, CV_GROUP_A_SIZE)); CHECK_RETURN(readCMD(RDCVC, rxbuffer, CV_GROUP_A_SIZE));
for (size_t i = 0; i < N_BMS; i++) { for (size_t i = 0; i < N_BMS; i++) {
size_t offset = BUFFER_BMS_OFFSET(i, CV_GROUP_A_SIZE); size_t offset = BUFFER_BMS_OFFSET(i, CV_GROUP_A_SIZE);
module[i]->cellVoltages[6] = mV_from_ADBMS6830(rxbuffer[offset + 0] | (rxbuffer[offset + 1] << 8)); (*module)[i].cellVoltages[6] = mV_from_ADBMS6830(rxbuffer[offset + 0] | (rxbuffer[offset + 1] << 8));
module[i]->cellVoltages[7] = mV_from_ADBMS6830(rxbuffer[offset + 2] | (rxbuffer[offset + 3] << 8)); (*module)[i].cellVoltages[7] = mV_from_ADBMS6830(rxbuffer[offset + 2] | (rxbuffer[offset + 3] << 8));
module[i]->cellVoltages[8] = mV_from_ADBMS6830(rxbuffer[offset + 4] | (rxbuffer[offset + 5] << 8)); (*module)[i].cellVoltages[8] = mV_from_ADBMS6830(rxbuffer[offset + 4] | (rxbuffer[offset + 5] << 8));
} }
CHECK_RETURN(readCMD(RDCVD, rxbuffer, CV_GROUP_A_SIZE)); CHECK_RETURN(readCMD(RDCVD, rxbuffer, CV_GROUP_A_SIZE));
for (size_t i = 0; i < N_BMS; i++) { for (size_t i = 0; i < N_BMS; i++) {
size_t offset = BUFFER_BMS_OFFSET(i, CV_GROUP_A_SIZE); size_t offset = BUFFER_BMS_OFFSET(i, CV_GROUP_A_SIZE);
module[i]->cellVoltages[9] = mV_from_ADBMS6830(rxbuffer[offset + 0] | (rxbuffer[offset + 1] << 8)); (*module)[i].cellVoltages[9] = mV_from_ADBMS6830(rxbuffer[offset + 0] | (rxbuffer[offset + 1] << 8));
module[i]->cellVoltages[10] = mV_from_ADBMS6830(rxbuffer[offset + 2] | (rxbuffer[offset + 3] << 8)); (*module)[i].cellVoltages[10] = mV_from_ADBMS6830(rxbuffer[offset + 2] | (rxbuffer[offset + 3] << 8));
module[i]->cellVoltages[11] = mV_from_ADBMS6830(rxbuffer[offset + 4] | (rxbuffer[offset + 5] << 8)); (*module)[i].cellVoltages[11] = mV_from_ADBMS6830(rxbuffer[offset + 4] | (rxbuffer[offset + 5] << 8));
} }
CHECK_RETURN(readCMD(RDCVE, rxbuffer, CV_GROUP_A_SIZE)); CHECK_RETURN(readCMD(RDCVE, rxbuffer, CV_GROUP_A_SIZE));
for (size_t i = 0; i < N_BMS; i++) { for (size_t i = 0; i < N_BMS; i++) {
size_t offset = BUFFER_BMS_OFFSET(i, CV_GROUP_A_SIZE); size_t offset = BUFFER_BMS_OFFSET(i, CV_GROUP_A_SIZE);
module[i]->cellVoltages[12] = mV_from_ADBMS6830(rxbuffer[offset + 0] | (rxbuffer[offset + 1] << 8)); (*module)[i].cellVoltages[12] = mV_from_ADBMS6830(rxbuffer[offset + 0] | (rxbuffer[offset + 1] << 8));
module[i]->cellVoltages[13] = mV_from_ADBMS6830(rxbuffer[offset + 2] | (rxbuffer[offset + 3] << 8)); (*module)[i].cellVoltages[13] = mV_from_ADBMS6830(rxbuffer[offset + 2] | (rxbuffer[offset + 3] << 8));
module[i]->cellVoltages[14] = mV_from_ADBMS6830(rxbuffer[offset + 4] | (rxbuffer[offset + 5] << 8)); (*module)[i].cellVoltages[14] = mV_from_ADBMS6830(rxbuffer[offset + 4] | (rxbuffer[offset + 5] << 8));
} }
CHECK_RETURN(readCMD(RDCVF, rxbuffer, CV_GROUP_A_SIZE)); CHECK_RETURN(readCMD(RDCVF, rxbuffer, CV_GROUP_A_SIZE));
for (size_t i = 0; i < N_BMS; i++) { for (size_t i = 0; i < N_BMS; i++) {
size_t offset = BUFFER_BMS_OFFSET(i, CV_GROUP_A_SIZE); size_t offset = BUFFER_BMS_OFFSET(i, CV_GROUP_A_SIZE);
module[i]->cellVoltages[15] = mV_from_ADBMS6830(rxbuffer[offset + 0] | (rxbuffer[offset + 1] << 8)); (*module)[i].cellVoltages[15] = mV_from_ADBMS6830(rxbuffer[offset + 0] | (rxbuffer[offset + 1] << 8));
} }
return HAL_OK; return HAL_OK;
} }
//Each selected BMS must have a corresponding address, and the data array for that BMS must be at least datalens[i] bytes long // Each selected BMS must have a corresponding address, and the data array for that BMS must be at least datalens[i]
HAL_StatusTypeDef 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) { // bytes long
HAL_StatusTypeDef 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) {
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
@ -341,7 +364,8 @@ HAL_StatusTypeDef amsSendI2C(const uint8_t addresses[static N_BMS], uint8_t * da
} }
} }
size_t packet_count = ((max_datalen + 1) / 3) + ((max_datalen + 1) % 3 != 0); //number of 3 byte packets needed to send all data size_t packet_count =
((max_datalen + 1) / 3) + ((max_datalen + 1) % 3 != 0); // number of 3 byte packets needed to send all data
for (size_t i = 0; i < (packet_count * 3); i++) { //i - 1 is the number of data bytes sent so far (1 byte for the address) for (size_t i = 0; i < (packet_count * 3); i++) { //i - 1 is the number of data bytes sent so far (1 byte for the address)
for (size_t j = 0; j < N_BMS; j++) { for (size_t j = 0; j < N_BMS; j++) {
@ -390,8 +414,10 @@ HAL_StatusTypeDef amsSendI2C(const uint8_t addresses[static N_BMS], uint8_t * da
return HAL_OK; return HAL_OK;
} }
//Each selected BMS must have a corresponding address, and the data array for that BMS must be at least datalens[i] bytes long // Each selected BMS must have a corresponding address, and the data array for that BMS must be at least datalens[i]
HAL_StatusTypeDef 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) { // bytes long
HAL_StatusTypeDef 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) {
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;

View File

@ -4,14 +4,14 @@
SlaveErrorData error_data[NUM_ERROR_KINDS] = {}; SlaveErrorData error_data[NUM_ERROR_KINDS] = {};
uint32_t error_sources = 0; uint32_t error_sources = 0;
void set_error_source(ADBMS_ErrorKind source) { void set_error_source(ADBMS_Status source) {
if (!(error_sources & (1 << source))) { if (!(error_sources & (1 << source))) {
error_data[source].errors_since = HAL_GetTick(); error_data[source].errors_since = HAL_GetTick();
} }
error_sources |= (1 << source); error_sources |= (1 << source);
} }
void clear_error_source(ADBMS_ErrorKind source) { void clear_error_source(ADBMS_Status source) {
error_data[source].errors_since = 0; error_data[source].errors_since = 0;
error_sources &= ~(1 << source); error_sources &= ~(1 << source);
} }

View File

@ -35,14 +35,14 @@ struct pollingTimes {
struct pollingTimes pollingTimes = {0, 0}; struct pollingTimes pollingTimes = {0, 0};
static constexpr ADBMS_DetailedStatus NO_ERROR = {ADBMS_OK, ADBMS_NONE}; static constexpr ADBMS_DetailedStatus NO_ERROR = {ADBMS_NO_ERROR};
ADBMS_DetailedStatus AMS_Init(SPI_HandleTypeDef* hspi) { ADBMS_DetailedStatus AMS_Init(SPI_HandleTypeDef* hspi) {
debug_log(LOG_LEVEL_INFO, "ADBMS6830B HAL - configured for %d controllers and %d cells per controller...", N_BMS, debug_log(LOG_LEVEL_INFO, "ADBMS6830B HAL - configured for %d controllers and %d cells per controller...", N_BMS,
numberofCells); numberofCells);
if (initAMS(hspi) != HAL_OK) { if (initAMS(hspi) != HAL_OK) {
debug_log(LOG_LEVEL_ERROR, "ADBMS6830B HAL - initialization failed"); debug_log(LOG_LEVEL_ERROR, "ADBMS6830B HAL - initialization failed");
return (ADBMS_DetailedStatus){ADBMS_ERROR, ADBMS_INTERNAL_BMS_FAULT}; return (ADBMS_DetailedStatus){ADBMS_INTERNAL_BMS_FAULT};
} }
pollingTimes = (struct pollingTimes){HAL_GetTick(), HAL_GetTick()}; pollingTimes = (struct pollingTimes){HAL_GetTick(), HAL_GetTick()};
@ -51,13 +51,15 @@ ADBMS_DetailedStatus AMS_Init(SPI_HandleTypeDef* hspi) {
#define any(x) \ #define any(x) \
({ \ ({ \
uint32_t any = false; \ int first_match = -1; \
static_assert(sizeof(any) * CHAR_BIT >= N_BMS, "any datatype needs to be larger!"); \
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++) { \
Cell_Module module = modules[__any_intern_i]; \ Cell_Module module = modules[__any_intern_i]; \
any |= ((x) ? 1 : 0) << __any_intern_i; \ if ((x)) { \
first_match = __any_intern_i; \
break; \
} \ } \
any; \ } \
first_match; \
}) })
ADBMS_DetailedStatus AMS_Idle_Loop() { ADBMS_DetailedStatus AMS_Idle_Loop() {
@ -66,12 +68,13 @@ ADBMS_DetailedStatus AMS_Idle_Loop() {
// set_error_source(ERROR_SOURCE_INTERNAL); //so we can't tell if we timed out // set_error_source(ERROR_SOURCE_INTERNAL); //so we can't tell if we timed out
} }
packetChecksumFails += amsAuxAndStatusMeasurement(&modules); packetChecksumFails += (amsAuxAndStatusMeasurement(&modules) == HAL_ERROR);
if (any(module.status.SLEEP)) { static int match = 0;
if ((match = any(module.status.SLEEP))) {
deviceSleeps++; deviceSleeps++;
if (deviceSleeps > MAX_DEVICE_SLEEP) { if (deviceSleeps > MAX_DEVICE_SLEEP) {
return (ADBMS_DetailedStatus){ADBMS_ERROR, ADBMS_INTERNAL_BMS_TIMEOUT}; return (ADBMS_DetailedStatus){ADBMS_INTERNAL_BMS_TIMEOUT, match};
} else { } else {
amsReset(); amsReset();
} }
@ -88,54 +91,29 @@ ADBMS_DetailedStatus AMS_Idle_Loop() {
// iteration. // iteration.
amsClearFlag(); amsClearFlag();
return (ADBMS_DetailedStatus){ADBMS_ERROR, ADBMS_INTERNAL_BMS_FAULT}; return (ADBMS_DetailedStatus){ADBMS_INTERNAL_BMS_FAULT, match};
} else {
// clear_error_source(SEK_INTERNAL_BMS_FAULT);
} }
packetChecksumFails += amsCellMeasurement(&modules); packetChecksumFails += (amsCellMeasurement(&modules) == HAL_ERROR);
packetChecksumFails += amsCheckUnderOverVoltage(&modules); packetChecksumFails += (amsCheckUnderOverVoltage(&modules) == HAL_ERROR);
if (packetChecksumFails > MAX_PACKET_CHECKSUM_FAILS) { if (packetChecksumFails > MAX_PACKET_CHECKSUM_FAILS) {
set_error_source(ADBMS_INTERNAL_BMS_CHECKSUM_FAIL); return (ADBMS_DetailedStatus){ADBMS_INTERNAL_BMS_CHECKSUM_FAIL, -1};
} }
// TODO: temperature measurement
bool overvolt = false;
bool undervolt = false;
for (size_t i = 0; i < numberofCells; i++) { for (size_t i = 0; i < numberofCells; i++) {
if (any(module.cellVoltages[i] < 2500)) { if ((match = any(module.cellVoltages[i] < 2500))) {
undervolt = true;
error_data[ADBMS_UNDERVOLT].data[0] = i; error_data[ADBMS_UNDERVOLT].data[0] = i;
uint8_t* ptr = &error_data[ADBMS_UNDERVOLT].data[1]; return (ADBMS_DetailedStatus){ADBMS_UNDERVOLT, match};
// ftcan_marshal_unsigned(ptr, module.cellVoltages[i], 2); } else if ((match = any(module.cellVoltages[i] > 4200))) {
} else if (any(module.cellVoltages[i] > 4200)) {
overvolt = true;
error_data[ADBMS_OVERVOLT].data[0] = i; error_data[ADBMS_OVERVOLT].data[0] = i;
uint8_t* ptr = &error_data[ADBMS_OVERVOLT].data[1]; return (ADBMS_DetailedStatus){ADBMS_OVERVOLT, match};
// ftcan_marshal_unsigned(ptr, module.cellVoltages[i], 2);
} }
} }
if (any(module.internalDieTemp > 28000 || module.status.THSD)) { // TODO: change to correct value // TODO: replace with the correct threshold for the internal die temperature
// ftcan_marshal_unsigned(&error_data[SEK_INTERNAL_BMS_OVERTEMP].data[0], module.internalDieTemp, 2); if ((match = any(module.internalDieTemp > 28000 || module.status.THSD))) {
return (ADBMS_DetailedStatus){ADBMS_INTERNAL_BMS_OVERTEMP, match};
set_error_source(ADBMS_INTERNAL_BMS_OVERTEMP);
} else {
clear_error_source(ADBMS_INTERNAL_BMS_OVERTEMP);
}
if (overvolt) {
set_error_source(ADBMS_OVERVOLT);
} else {
clear_error_source(ADBMS_OVERVOLT);
}
if (undervolt) {
set_error_source(ADBMS_UNDERVOLT);
} else {
clear_error_source(ADBMS_UNDERVOLT);
} }
mcuDelay(10); mcuDelay(10);

View File

@ -15,8 +15,6 @@
#define INITIAL_COMMAND_PEC 0x0010 #define INITIAL_COMMAND_PEC 0x0010
#define INITIAL_DATA_PEC 0x0010 #define INITIAL_DATA_PEC 0x0010
#define ADBMS_SPI_TIMEOUT 100 // Timeout in ms
#warning ask about the timeout value
SPI_HandleTypeDef* adbmsspi; SPI_HandleTypeDef* adbmsspi;

View File

@ -10,7 +10,7 @@ target: AMS Master Nucleo
# Can be C or C++ # Can be C or C++
language: C language: C
optimization: Og optimization: O2
# MCU settings # MCU settings
targetMCU: stm32h7x targetMCU: stm32h7x
@ -43,6 +43,7 @@ cFlags:
- -ffunction-sections - -ffunction-sections
- -std=gnu23 - -std=gnu23
- -flto - -flto
# - -fanalyzer
assemblyFlags: assemblyFlags:
- -Wall - -Wall