Compare commits
3 Commits
53a9597fe8
...
d6de68c358
Author | SHA1 | Date | |
---|---|---|---|
d6de68c358 | |||
f3aa252b0e | |||
522ef539be |
@ -2,15 +2,16 @@
|
||||
#ifndef __CONFIG_ADBMS6830_H
|
||||
#define __CONFIG_ADBMS6830_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);
|
||||
}
|
||||
|
||||
[[maybe_unused]] static void mcuAdbmsCSHigh() {
|
||||
[[maybe_unused]] static inline void mcuAdbmsCSHigh() {
|
||||
HAL_GPIO_WritePin(AMS_CS_GPIO_Port, AMS_CS_Pin, GPIO_PIN_SET);
|
||||
}
|
||||
|
||||
#endif //__CONFIG_ADBMS6830_H
|
||||
|
@ -52,10 +52,11 @@ static inline uint32_t __swo_putc(uint32_t c, unsigned int channel) {
|
||||
return (c);
|
||||
}
|
||||
|
||||
#define DEBUG_CHANNEL_ENABLED(channel) ({ \
|
||||
#define DEBUG_CHANNEL_ENABLED(channel) \
|
||||
({ \
|
||||
unsigned int ch = (channel); \
|
||||
(ch < 32) ? __ITM_channel_enabled(ch) : false; \
|
||||
})
|
||||
})
|
||||
|
||||
[[gnu::nonnull(2), gnu::null_terminated_string_arg(2)]]
|
||||
static inline void __swo_print(unsigned int channel, const char *str) {
|
||||
|
@ -9,15 +9,16 @@
|
||||
#define INC_ADBMS_ABSTRACTION_H_
|
||||
|
||||
#include "ADBMS_CMD_MAKROS.h"
|
||||
#include "ADBMS_LL_Driver.h"
|
||||
#include "ADBMS_Driver.h"
|
||||
#include "ADBMS_LL_Driver.h"
|
||||
#include "main.h"
|
||||
|
||||
//see table 103 in datasheet (page 71)
|
||||
#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 mV_from_ADBMS6830(x) (((((int16_t) (x))) * 0.150) + 1500)
|
||||
// see table 103 in datasheet (page 71)
|
||||
#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 mV_from_ADBMS6830(x) (((((int16_t)(x))) * 0.150) + 1500)
|
||||
|
||||
HAL_StatusTypeDef amsReset();
|
||||
|
||||
|
@ -1,35 +1,28 @@
|
||||
#ifndef ADBMS_DRIVER_H
|
||||
#define ADBMS_DRIVER_H
|
||||
|
||||
#include "stm32h7xx_hal.h"
|
||||
#include "config_ADBMS6830.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#define ERROR_TIME_THRESH 150 // ms
|
||||
|
||||
typedef enum : uint16_t {
|
||||
ADBMS_OK = 0x00, // same as STM32 HAL status
|
||||
ADBMS_ERROR = 0x01, //
|
||||
ADBMS_BUSY = 0x02, //
|
||||
ADBMS_TIMEOUT = 0x03 //
|
||||
} ADBMS_StatusTypeDef;
|
||||
|
||||
typedef enum : uint16_t {
|
||||
ADBMS_NONE = 0,
|
||||
ADBMS_NO_ERROR = 0,
|
||||
ADBMS_OVERTEMP,
|
||||
ADBMS_UNDERTEMP,
|
||||
ADBMS_OVERVOLT,
|
||||
ADBMS_UNDERVOLT,
|
||||
ADBMS_OPENWIRE,
|
||||
ADBMS_INTERNAL_BMS_TIMEOUT,
|
||||
ADBMS_INTERNAL_BMS_CHECKSUM_FAIL,
|
||||
ADBMS_INTERNAL_BMS_TIMEOUT, // BMS went to sleep too many times
|
||||
ADBMS_INTERNAL_BMS_CHECKSUM_FAIL, // BMS CRC failed too many times
|
||||
ADBMS_INTERNAL_BMS_OVERTEMP,
|
||||
ADBMS_INTERNAL_BMS_FAULT,
|
||||
NUM_ERROR_KINDS
|
||||
} ADBMS_ErrorKind;
|
||||
} ADBMS_Status;
|
||||
|
||||
typedef struct {
|
||||
ADBMS_StatusTypeDef status : 16;
|
||||
ADBMS_ErrorKind error : 16; // zero if status is ADBMS_OK
|
||||
ADBMS_Status status : 16;
|
||||
int16_t bms_id : 16; // the BMS that caused the error, zero if no error, -1 if unknown BMS
|
||||
} ADBMS_DetailedStatus;
|
||||
|
||||
typedef struct {
|
||||
@ -83,6 +76,7 @@ typedef struct {
|
||||
|
||||
extern uint32_t error_sources; // Bitfield of error sources
|
||||
extern SlaveErrorData error_data[NUM_ERROR_KINDS];
|
||||
extern Cell_Module modules[N_BMS];
|
||||
|
||||
[[gnu::nonnull]] ADBMS_DetailedStatus AMS_Init(SPI_HandleTypeDef* hspi);
|
||||
|
||||
|
@ -11,8 +11,8 @@
|
||||
static_assert(sizeof(error_sources) * CHAR_BIT >= NUM_ERROR_KINDS,
|
||||
"error_sources is too small to hold all error sources");
|
||||
|
||||
void set_error_source(ADBMS_ErrorKind source);
|
||||
void clear_error_source(ADBMS_ErrorKind source);
|
||||
void set_error_source(ADBMS_Status source);
|
||||
void clear_error_source(ADBMS_Status source);
|
||||
|
||||
|
||||
#endif //AMS_MASTER_CODE_ADBMS_ERROR_H
|
||||
|
@ -14,11 +14,17 @@
|
||||
|
||||
extern uint8_t numberofCells;
|
||||
|
||||
static const char* const HAL_Statuses[] = {"HAL_OK", "HAL_ERROR", "HAL_BUSY", "HAL_TIMEOUT"};
|
||||
|
||||
#define CHECK_RETURN(x) \
|
||||
do { \
|
||||
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; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
HAL_StatusTypeDef amsReset() {
|
||||
@ -26,7 +32,21 @@ HAL_StatusTypeDef amsReset() {
|
||||
readCMD(SRST, CMD_EMPTY_BUFFER, 0);
|
||||
|
||||
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");
|
||||
|
||||
@ -69,86 +89,87 @@ HAL_StatusTypeDef amsWakeUp() {
|
||||
}
|
||||
|
||||
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
|
||||
return amsReadCellVoltages(module);
|
||||
}
|
||||
|
||||
HAL_StatusTypeDef amsAuxAndStatusMeasurement(Cell_Module (*module)[N_BMS]) {
|
||||
uint8_t rxbuf[CMD_BUFFER_SIZE(STATUS_GROUP_C_SIZE)] = {};
|
||||
|
||||
CHECK_RETURN(readCMD(RDSTATC, rxbuf, STATUS_GROUP_C_SIZE));
|
||||
for (size_t i = 0; i < N_BMS; i++) {
|
||||
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.CCTS = rxbuf[offset + 2] | (rxbuf[offset + 3] << 8);
|
||||
module[i]->status.VA_OV = (rxbuf[offset + 4] >> 7) & 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_UV = (rxbuf[offset + 4] >> 4) & 0x01;
|
||||
module[i]->status.CED = (rxbuf[offset + 4] >> 3) & 0x01;
|
||||
module[i]->status.CMED = (rxbuf[offset + 4] >> 2) & 0x01;
|
||||
module[i]->status.SED = (rxbuf[offset + 4] >> 1) & 0x01;
|
||||
module[i]->status.SMED = (rxbuf[offset + 4] >> 0) & 0x01;
|
||||
module[i]->status.VDEL = (rxbuf[offset + 5] >> 7) & 0x01;
|
||||
module[i]->status.VDE = (rxbuf[offset + 5] >> 6) & 0x01;
|
||||
module[i]->status.COMPARE= (rxbuf[offset + 5] >> 5) & 0x01;
|
||||
module[i]->status.SPIFLT = (rxbuf[offset + 5] >> 4) & 0x01;
|
||||
module[i]->status.SLEEP = (rxbuf[offset + 5] >> 3) & 0x01;
|
||||
module[i]->status.THSD = (rxbuf[offset + 5] >> 2) & 0x01;
|
||||
module[i]->status.TMODCHK= (rxbuf[offset + 5] >> 1) & 0x01;
|
||||
module[i]->status.OSCCHK = (rxbuf[offset + 5] >> 0) & 0x01;
|
||||
(*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.VA_OV = (rxbuf[offset + 4] >> 7) & 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_UV = (rxbuf[offset + 4] >> 4) & 0x01;
|
||||
(*module)[i].status.CED = (rxbuf[offset + 4] >> 3) & 0x01;
|
||||
(*module)[i].status.CMED = (rxbuf[offset + 4] >> 2) & 0x01;
|
||||
(*module)[i].status.SED = (rxbuf[offset + 4] >> 1) & 0x01;
|
||||
(*module)[i].status.SMED = (rxbuf[offset + 4] >> 0) & 0x01;
|
||||
(*module)[i].status.VDEL = (rxbuf[offset + 5] >> 7) & 0x01;
|
||||
(*module)[i].status.VDE = (rxbuf[offset + 5] >> 6) & 0x01;
|
||||
(*module)[i].status.COMPARE = (rxbuf[offset + 5] >> 5) & 0x01;
|
||||
(*module)[i].status.SPIFLT = (rxbuf[offset + 5] >> 4) & 0x01;
|
||||
(*module)[i].status.SLEEP = (rxbuf[offset + 5] >> 3) & 0x01;
|
||||
(*module)[i].status.THSD = (rxbuf[offset + 5] >> 2) & 0x01;
|
||||
(*module)[i].status.TMODCHK = (rxbuf[offset + 5] >> 1) & 0x01;
|
||||
(*module)[i].status.OSCCHK = (rxbuf[offset + 5] >> 0) & 0x01;
|
||||
}
|
||||
|
||||
if (pollCMD(PLAUX) == 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++) {
|
||||
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[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[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[2] = mV_from_ADBMS6830(rxbuf[offset + 4] | (rxbuf[offset + 5] << 8));
|
||||
}
|
||||
|
||||
CHECK_RETURN(readCMD(RDAUXB, rxbuf, AUX_GROUP_B_SIZE));
|
||||
for (size_t i = 0; i < N_BMS; i++) {
|
||||
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[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[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[5] = mV_from_ADBMS6830(rxbuf[offset + 4] | (rxbuf[offset + 5] << 8));
|
||||
}
|
||||
|
||||
CHECK_RETURN(readCMD(RDAUXC, rxbuf, AUX_GROUP_C_SIZE));
|
||||
for (size_t i = 0; i < N_BMS; i++) {
|
||||
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[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[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[8] = mV_from_ADBMS6830(rxbuf[offset + 4] | (rxbuf[offset + 5] << 8));
|
||||
}
|
||||
|
||||
CHECK_RETURN(readCMD(RDAUXD, rxbuf, AUX_GROUP_D_SIZE));
|
||||
for (size_t i = 0; i < N_BMS; i++) {
|
||||
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));
|
||||
for (size_t i = 0; i < N_BMS; i++) {
|
||||
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));
|
||||
for (size_t i = 0; i < N_BMS; i++) {
|
||||
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]->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].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].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
|
||||
|
||||
return HAL_OK;
|
||||
}
|
||||
@ -179,8 +200,8 @@ HAL_StatusTypeDef amsConfigBalancing(const uint32_t channels[static N_BMS], uint
|
||||
}
|
||||
}
|
||||
|
||||
//log the new PWM settings
|
||||
//output is: PWM - [BMS_ID]: [PWM0] ... [PWM16]
|
||||
// log the new PWM settings
|
||||
// output is: PWM - [BMS_ID]: [PWM0] ... [PWM16]
|
||||
if (DEBUG_CHANNEL_ENABLED(LOG_LEVEL_DEBUG)) {
|
||||
debug_log(LOG_LEVEL_DEBUG, "PWM - %d: ", i);
|
||||
for (size_t j = 0; j < 6; j++) {
|
||||
@ -212,19 +233,20 @@ HAL_StatusTypeDef amsConfigOverUnderVoltage(uint16_t overVoltage, uint16_t under
|
||||
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));
|
||||
|
||||
for (size_t i = 0; i < N_BMS; i++) {
|
||||
size_t offset = BUFFER_BMS_OFFSET(i, CFG_GROUP_B_SIZE);
|
||||
|
||||
//UV
|
||||
// UV
|
||||
buffer[offset + 0] = (uint8_t)(underVoltage & 0xFF);
|
||||
buffer[offset + 1] &= 0xF0;
|
||||
buffer[offset + 1] |= (uint8_t)((underVoltage >> 8) & 0x0F);
|
||||
|
||||
//OV
|
||||
// OV
|
||||
buffer[offset + 1] &= 0x0F;
|
||||
buffer[offset + 1] |= (uint8_t)(overVoltage << 4);
|
||||
buffer[offset + 2] = (uint8_t)(overVoltage >> 4);
|
||||
@ -241,15 +263,16 @@ HAL_StatusTypeDef amsCheckUnderOverVoltage(Cell_Module (*module)[N_BMS]) {
|
||||
for (size_t i = 0; i < N_BMS; i++) {
|
||||
size_t offset = BUFFER_BMS_OFFSET(i, STATUS_GROUP_D_SIZE);
|
||||
uint32_t ov_uv_data = 0;
|
||||
ov_uv_data = (regbuffer[offset + 0] << 0) | (regbuffer[offset + 1] << 8) |
|
||||
(regbuffer[offset + 2] << 16) | (regbuffer[offset + 3] << 24);
|
||||
ov_uv_data = (regbuffer[offset + 0] << 0) | (regbuffer[offset + 1] << 8) | (regbuffer[offset + 2] << 16) |
|
||||
(regbuffer[offset + 3] << 24);
|
||||
|
||||
module[i]->overVoltage = 0;
|
||||
module[i]->underVoltage = 0;
|
||||
(*module)[i].overVoltage = 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, ...
|
||||
module[i]->underVoltage |= (ov_uv_data >> (j * 2)) & 0x01;
|
||||
module[i]->overVoltage |= (ov_uv_data >> (j * 2 + 1)) & 0x01;
|
||||
for (size_t j = 0; j < numberofCells;
|
||||
j++) { // ov/uv flags are 1-bit flags for each cell C0UV, C0OV, C1UV, C1OV, ...
|
||||
(*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);
|
||||
}
|
||||
|
||||
HAL_StatusTypeDef amsClearAux() {
|
||||
return writeCMD(CLRAUX, CMD_EMPTY_BUFFER, 0);
|
||||
}
|
||||
HAL_StatusTypeDef amsClearAux() { return writeCMD(CLRAUX, CMD_EMPTY_BUFFER, 0); }
|
||||
|
||||
HAL_StatusTypeDef amsReadCellVoltages(Cell_Module (*module)[N_BMS]) {
|
||||
uint8_t rxbuffer[CMD_BUFFER_SIZE(CV_GROUP_A_SIZE)] = {};
|
||||
@ -271,57 +292,59 @@ HAL_StatusTypeDef amsReadCellVoltages(Cell_Module (*module)[N_BMS]) {
|
||||
CHECK_RETURN(readCMD(RDCVA, rxbuffer, CV_GROUP_A_SIZE));
|
||||
for (size_t i = 0; i < N_BMS; i++) {
|
||||
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[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[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[2] = mV_from_ADBMS6830(rxbuffer[offset + 4] | (rxbuffer[offset + 5] << 8));
|
||||
}
|
||||
|
||||
CHECK_RETURN(readCMD(RDCVB, rxbuffer, CV_GROUP_A_SIZE));
|
||||
for (size_t i = 0; i < N_BMS; i++) {
|
||||
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[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[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[5] = mV_from_ADBMS6830(rxbuffer[offset + 4] | (rxbuffer[offset + 5] << 8));
|
||||
}
|
||||
|
||||
CHECK_RETURN(readCMD(RDCVC, rxbuffer, CV_GROUP_A_SIZE));
|
||||
for (size_t i = 0; i < N_BMS; i++) {
|
||||
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[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[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[8] = mV_from_ADBMS6830(rxbuffer[offset + 4] | (rxbuffer[offset + 5] << 8));
|
||||
}
|
||||
|
||||
CHECK_RETURN(readCMD(RDCVD, rxbuffer, CV_GROUP_A_SIZE));
|
||||
for (size_t i = 0; i < N_BMS; i++) {
|
||||
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[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[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[11] = mV_from_ADBMS6830(rxbuffer[offset + 4] | (rxbuffer[offset + 5] << 8));
|
||||
}
|
||||
|
||||
CHECK_RETURN(readCMD(RDCVE, rxbuffer, CV_GROUP_A_SIZE));
|
||||
for (size_t i = 0; i < N_BMS; i++) {
|
||||
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[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[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[14] = mV_from_ADBMS6830(rxbuffer[offset + 4] | (rxbuffer[offset + 5] << 8));
|
||||
}
|
||||
|
||||
CHECK_RETURN(readCMD(RDCVF, rxbuffer, CV_GROUP_A_SIZE));
|
||||
for (size_t i = 0; i < N_BMS; i++) {
|
||||
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;
|
||||
}
|
||||
|
||||
//Each selected BMS must have a corresponding address, and the data array for that BMS must be at least datalens[i] 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) {
|
||||
// Each selected BMS must have a corresponding address, and the data array for that BMS must be at least datalens[i]
|
||||
// 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)] = {};
|
||||
|
||||
//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
|
||||
|
||||
uint8_t max_datalen = 0;
|
||||
for (size_t i = 0; i < N_BMS; i++) {
|
||||
@ -341,57 +364,60 @@ 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 j = 0; j < N_BMS; j++) {
|
||||
size_t offset = BUFFER_BMS_OFFSET(j, COMM_GROUP_SIZE);
|
||||
if (!(bms_mask & (1 << j)))
|
||||
continue; //skip BMS that are not selected
|
||||
continue; // skip BMS that are not selected
|
||||
|
||||
if (i == 0) {
|
||||
buffer[offset + 0] = I2C_SEND_START;
|
||||
buffer[offset + 1] = addresses[j] << 1; //shift the address left by 1 to make space for the R/W bit
|
||||
buffer[offset + 1] = addresses[j] << 1; // shift the address left by 1 to make space for the R/W bit
|
||||
continue;
|
||||
}
|
||||
|
||||
//add data to the buffer
|
||||
// add data to the buffer
|
||||
|
||||
//case 1: the last group of 3 bytes contained the last data byte, so we need to send a stop
|
||||
// case 1: the last group of 3 bytes contained the last data byte, so we need to send a stop
|
||||
if (datalens[j] - (int)i == -1) {
|
||||
buffer[offset + ((i % 3) * 2)] = I2C_SEND_STOP;
|
||||
continue;
|
||||
}
|
||||
|
||||
//case 2: we are more than one byte past the end, so we don't need to send any data
|
||||
// case 2: we are more than one byte past the end, so we don't need to send any data
|
||||
if (datalens[j] - (int)i < -1) {
|
||||
buffer[offset + ((i % 3) * 2)] = I2C_SEND_NOTRANSFER;
|
||||
continue;
|
||||
}
|
||||
|
||||
//case 3: we are still sending data
|
||||
// case 3: we are still sending data
|
||||
buffer[offset + ((i % 3) * 2)] = I2C_SEND;
|
||||
buffer[offset + ((i % 3) * 2) + 1] = data[j][i - 1];
|
||||
}
|
||||
|
||||
//send the data
|
||||
// send the data
|
||||
if (i % 3 == 0 && i != 0) {
|
||||
CHECK_RETURN(writeCMD(WRCOMM, buffer, COMM_GROUP_SIZE));
|
||||
|
||||
__pollCMD(STCOMM, 72); //wait 72 cycles for the I2C transfer to complete (datasheet page 43)
|
||||
//TODO: not sure if this is correct for a daisy chain
|
||||
__pollCMD(STCOMM, 72); // wait 72 cycles for the I2C transfer to complete (datasheet page 43)
|
||||
// TODO: not sure if this is correct for a daisy chain
|
||||
}
|
||||
}
|
||||
|
||||
//send the last packet
|
||||
// send the last packet
|
||||
CHECK_RETURN(writeCMD(WRCOMM, buffer, COMM_GROUP_SIZE));
|
||||
__pollCMD(STCOMM, 72);
|
||||
|
||||
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
|
||||
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) {
|
||||
// Each selected BMS must have a corresponding address, and the data array for that BMS must be at least datalens[i]
|
||||
// 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 max_datalen = 0;
|
||||
|
@ -4,14 +4,14 @@
|
||||
SlaveErrorData error_data[NUM_ERROR_KINDS] = {};
|
||||
uint32_t error_sources = 0;
|
||||
|
||||
void set_error_source(ADBMS_ErrorKind source) {
|
||||
void set_error_source(ADBMS_Status source) {
|
||||
if (!(error_sources & (1 << source))) {
|
||||
error_data[source].errors_since = HAL_GetTick();
|
||||
}
|
||||
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_sources &= ~(1 << source);
|
||||
}
|
||||
|
@ -35,14 +35,14 @@ struct pollingTimes {
|
||||
|
||||
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) {
|
||||
debug_log(LOG_LEVEL_INFO, "ADBMS6830B HAL - configured for %d controllers and %d cells per controller...", N_BMS,
|
||||
numberofCells);
|
||||
if (initAMS(hspi) != HAL_OK) {
|
||||
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()};
|
||||
@ -51,13 +51,15 @@ ADBMS_DetailedStatus AMS_Init(SPI_HandleTypeDef* hspi) {
|
||||
|
||||
#define any(x) \
|
||||
({ \
|
||||
uint32_t any = false; \
|
||||
static_assert(sizeof(any) * CHAR_BIT >= N_BMS, "any datatype needs to be larger!"); \
|
||||
int first_match = -1; \
|
||||
for (size_t __any_intern_i = 0; __any_intern_i < N_BMS; __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() {
|
||||
@ -66,12 +68,13 @@ ADBMS_DetailedStatus AMS_Idle_Loop() {
|
||||
// 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++;
|
||||
if (deviceSleeps > MAX_DEVICE_SLEEP) {
|
||||
return (ADBMS_DetailedStatus){ADBMS_ERROR, ADBMS_INTERNAL_BMS_TIMEOUT};
|
||||
return (ADBMS_DetailedStatus){ADBMS_INTERNAL_BMS_TIMEOUT, match};
|
||||
} else {
|
||||
amsReset();
|
||||
}
|
||||
@ -88,54 +91,29 @@ ADBMS_DetailedStatus AMS_Idle_Loop() {
|
||||
// iteration.
|
||||
amsClearFlag();
|
||||
|
||||
return (ADBMS_DetailedStatus){ADBMS_ERROR, ADBMS_INTERNAL_BMS_FAULT};
|
||||
} else {
|
||||
// clear_error_source(SEK_INTERNAL_BMS_FAULT);
|
||||
return (ADBMS_DetailedStatus){ADBMS_INTERNAL_BMS_FAULT, match};
|
||||
}
|
||||
|
||||
packetChecksumFails += amsCellMeasurement(&modules);
|
||||
packetChecksumFails += amsCheckUnderOverVoltage(&modules);
|
||||
packetChecksumFails += (amsCellMeasurement(&modules) == HAL_ERROR);
|
||||
packetChecksumFails += (amsCheckUnderOverVoltage(&modules) == HAL_ERROR);
|
||||
|
||||
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++) {
|
||||
if (any(module.cellVoltages[i] < 2500)) {
|
||||
undervolt = true;
|
||||
if ((match = any(module.cellVoltages[i] < 2500))) {
|
||||
error_data[ADBMS_UNDERVOLT].data[0] = i;
|
||||
uint8_t* ptr = &error_data[ADBMS_UNDERVOLT].data[1];
|
||||
// ftcan_marshal_unsigned(ptr, module.cellVoltages[i], 2);
|
||||
} else if (any(module.cellVoltages[i] > 4200)) {
|
||||
overvolt = true;
|
||||
return (ADBMS_DetailedStatus){ADBMS_UNDERVOLT, match};
|
||||
} else if ((match = any(module.cellVoltages[i] > 4200))) {
|
||||
error_data[ADBMS_OVERVOLT].data[0] = i;
|
||||
uint8_t* ptr = &error_data[ADBMS_OVERVOLT].data[1];
|
||||
// ftcan_marshal_unsigned(ptr, module.cellVoltages[i], 2);
|
||||
return (ADBMS_DetailedStatus){ADBMS_OVERVOLT, match};
|
||||
}
|
||||
}
|
||||
|
||||
if (any(module.internalDieTemp > 28000 || module.status.THSD)) { // TODO: change to correct value
|
||||
// ftcan_marshal_unsigned(&error_data[SEK_INTERNAL_BMS_OVERTEMP].data[0], module.internalDieTemp, 2);
|
||||
|
||||
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);
|
||||
// TODO: replace with the correct threshold for the internal die temperature
|
||||
if ((match = any(module.internalDieTemp > 28000 || module.status.THSD))) {
|
||||
return (ADBMS_DetailedStatus){ADBMS_INTERNAL_BMS_OVERTEMP, match};
|
||||
}
|
||||
|
||||
mcuDelay(10);
|
||||
|
@ -15,8 +15,6 @@
|
||||
|
||||
#define INITIAL_COMMAND_PEC 0x0010
|
||||
#define INITIAL_DATA_PEC 0x0010
|
||||
#define ADBMS_SPI_TIMEOUT 100 // Timeout in ms
|
||||
#warning ask about the timeout value
|
||||
|
||||
SPI_HandleTypeDef* adbmsspi;
|
||||
|
||||
|
@ -10,7 +10,7 @@ target: AMS Master Nucleo
|
||||
# Can be C or C++
|
||||
language: C
|
||||
|
||||
optimization: Og
|
||||
optimization: O2
|
||||
|
||||
# MCU settings
|
||||
targetMCU: stm32h7x
|
||||
@ -43,6 +43,7 @@ cFlags:
|
||||
- -ffunction-sections
|
||||
- -std=gnu23
|
||||
- -flto
|
||||
# - -fanalyzer
|
||||
|
||||
assemblyFlags:
|
||||
- -Wall
|
||||
|
Loading…
x
Reference in New Issue
Block a user