refactor: more work on error handling and logging, as well as general cleanup

This commit is contained in:
Kilian Bracher 2025-02-23 16:12:30 +01:00
parent 522ef539be
commit f3aa252b0e
Signed by: k.bracher
SSH Key Fingerprint: SHA256:mXpyZkK7RDiJ7qeHCKJX108woM0cl5TrCvNBJASu6lM
8 changed files with 360 additions and 332 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,10 +52,11 @@ 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); \ ({ \
(ch < 32) ? __ITM_channel_enabled(ch) : false; \ unsigned int ch = (channel); \
}) (ch < 32) ? __ITM_channel_enabled(ch) : false; \
})
[[gnu::nonnull(2), gnu::null_terminated_string_arg(2)]] [[gnu::nonnull(2), gnu::null_terminated_string_arg(2)]]
static inline void __swo_print(unsigned int channel, const char *str) { static inline void __swo_print(unsigned int channel, const char *str) {
@ -67,31 +68,31 @@ static inline void __swo_print(unsigned int channel, const char *str) {
} }
} }
#define debug_log(level, msg, ...) \ #define debug_log(level, msg, ...) \
do { \ do { \
if (DEBUG_CHANNEL_ENABLED(level)) { \ if (DEBUG_CHANNEL_ENABLED(level)) { \
char buffer[MAX_MESSAGE_LENGTH]; \ char buffer[MAX_MESSAGE_LENGTH]; \
size_t len = snprintf(buffer, sizeof(buffer), msg, ##__VA_ARGS__); \ size_t len = snprintf(buffer, sizeof(buffer), msg, ##__VA_ARGS__); \
__swo_putc('\n', level); \ __swo_putc('\n', level); \
__swo_print(level, log_level_names[level]); \ __swo_print(level, log_level_names[level]); \
__swo_print(level, buffer); \ __swo_print(level, buffer); \
if (len >= sizeof(buffer)) { \ if (len >= sizeof(buffer)) { \
__swo_print(level, " [message length exceeded] "); \ __swo_print(level, " [message length exceeded] "); \
} \ } \
} \ } \
} while (0) } while (0)
#define debug_log_cont(level, msg, ...) \ #define debug_log_cont(level, msg, ...) \
do { \ do { \
if (DEBUG_CHANNEL_ENABLED(level)) { \ if (DEBUG_CHANNEL_ENABLED(level)) { \
char buffer[MAX_MESSAGE_LENGTH]; \ char buffer[MAX_MESSAGE_LENGTH]; \
size_t len = snprintf(buffer, sizeof(buffer), msg, ##__VA_ARGS__); \ size_t len = snprintf(buffer, sizeof(buffer), msg, ##__VA_ARGS__); \
__swo_print(level, buffer); \ __swo_print(level, buffer); \
if (len >= sizeof(buffer)) { \ if (len >= sizeof(buffer)) { \
__swo_print(level, " [message length exceeded] "); \ __swo_print(level, " [message length exceeded] "); \
} \ } \
__swo_putc('\n', level); \ __swo_putc('\n', level); \
} \ } \
} while (0) } while (0)
#endif /* __SWO_LOG_H */ #endif /* __SWO_LOG_H */

View File

@ -9,15 +9,16 @@
#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)
#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(); HAL_StatusTypeDef amsReset();

View File

@ -1,7 +1,7 @@
#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
@ -13,8 +13,8 @@ typedef enum : uint16_t {
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
@ -76,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

@ -14,38 +14,58 @@
extern uint8_t numberofCells; extern uint8_t numberofCells;
#define CHECK_RETURN(x) \ static const char* const HAL_Statuses[] = {"HAL_OK", "HAL_ERROR", "HAL_BUSY", "HAL_TIMEOUT"};
do { \
HAL_StatusTypeDef status = x; \ #define CHECK_RETURN(x) \
if (status != 0) \ do { \
return status; \ HAL_StatusTypeDef status = x; \
} while (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() { HAL_StatusTypeDef amsReset() {
amsWakeUp(); amsWakeUp();
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;
debug_log(LOG_LEVEL_INFO, "BMS reset complete"); for (size_t i = 0; i < N_BMS; i++) {
if (sidbuffer[BUFFER_BMS_OFFSET(i, SID_GROUP_SIZE)] != 0) {
if (DEBUG_CHANNEL_ENABLED(LOG_LEVEL_INFO)) { nonzero = true;
debug_log(LOG_LEVEL_INFO, "Found IDs: "); }
for (size_t i = 0; i < N_BMS; i++) { }
uint64_t id = 0; if (nonzero) {
for (size_t j = 0; j < SID_GROUP_SIZE; j++) { debug_log(LOG_LEVEL_ERROR,
id |= sidbuffer[BUFFER_BMS_OFFSET(i, SID_GROUP_SIZE) + j] << (j * 8); "CRC failure when reading BMS IDs, but some IDs are nonzero. --- Intermittent connection?");
} } else {
debug_log_cont(LOG_LEVEL_INFO, "0x%llx ", id); debug_log(LOG_LEVEL_ERROR, "CRC failure when reading BMS IDs, all IDs are zero. --- No connection?");
}
return HAL_ERROR;
} }
debug_log_cont(LOG_LEVEL_INFO, "\n");
}
mcuDelay(10); debug_log(LOG_LEVEL_INFO, "BMS reset complete");
amsWakeUp();
amsStopBalancing(); if (DEBUG_CHANNEL_ENABLED(LOG_LEVEL_INFO)) {
amsConfigOverUnderVoltage(DEFAULT_OV, DEFAULT_UV); debug_log(LOG_LEVEL_INFO, "Found IDs: ");
for (size_t i = 0; i < N_BMS; i++) {
uint64_t id = 0;
for (size_t j = 0; j < SID_GROUP_SIZE; j++) {
id |= sidbuffer[BUFFER_BMS_OFFSET(i, SID_GROUP_SIZE) + j] << (j * 8);
}
debug_log_cont(LOG_LEVEL_INFO, "0x%llx ", id);
}
debug_log_cont(LOG_LEVEL_INFO, "\n");
}
mcuDelay(10);
amsWakeUp();
amsStopBalancing();
amsConfigOverUnderVoltage(DEFAULT_OV, DEFAULT_UV);
uint8_t flagsbuffer[CMD_BUFFER_SIZE(6)] = {[0 ... CMD_BUFFER_SIZE(6) - 1] = 0xFF}; //technically not all 6 bytes are used, but it's easier to just set all to 0xFF uint8_t flagsbuffer[CMD_BUFFER_SIZE(6)] = {[0 ... CMD_BUFFER_SIZE(6) - 1] = 0xFF}; //technically not all 6 bytes are used, but it's easier to just set all to 0xFF
//see page 27 of the datasheet for the memory map //see page 27 of the datasheet for the memory map
@ -55,31 +75,31 @@ HAL_StatusTypeDef amsReset() {
CHECK_RETURN(writeCMD(ADCV | ADCV_CONT | ADCV_RD, flagsbuffer, 0)); //start continuous cell voltage measurement with redundancy CHECK_RETURN(writeCMD(ADCV | ADCV_CONT | ADCV_RD, flagsbuffer, 0)); //start continuous cell voltage measurement with redundancy
CHECK_RETURN(writeCMD(ADAX | ADAX_CONV_ALL, flagsbuffer, 0)); //start aux measurement CHECK_RETURN(writeCMD(ADAX | ADAX_CONV_ALL, flagsbuffer, 0)); //start aux measurement
return 0; return 0;
} }
HAL_StatusTypeDef initAMS(SPI_HandleTypeDef* hspi) { HAL_StatusTypeDef initAMS(SPI_HandleTypeDef* hspi) {
adbmsDriverInit(hspi); adbmsDriverInit(hspi);
return amsReset(); return amsReset();
} }
HAL_StatusTypeDef amsWakeUp() { HAL_StatusTypeDef amsWakeUp() {
uint8_t buffer[CMD_BUFFER_SIZE(CFG_GROUP_A_SIZE)] = {0}; uint8_t buffer[CMD_BUFFER_SIZE(CFG_GROUP_A_SIZE)] = {0};
return readCMD(RDCFGA, buffer, CFG_GROUP_A_SIZE); return readCMD(RDCFGA, buffer, CFG_GROUP_A_SIZE);
} }
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;
@ -98,105 +118,105 @@ HAL_StatusTypeDef amsAuxAndStatusMeasurement(Cell_Module (*module)[N_BMS]) {
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
return HAL_OK; return HAL_OK;
} }
HAL_StatusTypeDef amsConfigBalancing(const uint32_t channels[static N_BMS], uint8_t dutyCycle) { HAL_StatusTypeDef amsConfigBalancing(const uint32_t channels[static N_BMS], uint8_t dutyCycle) {
if (dutyCycle > 0x0F) { if (dutyCycle > 0x0F) {
return HAL_ERROR; // only 4 bits are allowed for dutyCycle return HAL_ERROR; // only 4 bits are allowed for dutyCycle
} }
uint8_t rxbufA[CMD_BUFFER_SIZE(PWM_GROUP_A_SIZE)] = {}; uint8_t rxbufA[CMD_BUFFER_SIZE(PWM_GROUP_A_SIZE)] = {};
uint8_t rxbufB[CMD_BUFFER_SIZE(PWM_GROUP_B_SIZE)] = {}; uint8_t rxbufB[CMD_BUFFER_SIZE(PWM_GROUP_B_SIZE)] = {};
CHECK_RETURN(readCMD(RDPWMA, rxbufA, PWM_GROUP_A_SIZE)); CHECK_RETURN(readCMD(RDPWMA, rxbufA, PWM_GROUP_A_SIZE));
CHECK_RETURN(readCMD(RDPWMB, rxbufB, PWM_GROUP_B_SIZE)); CHECK_RETURN(readCMD(RDPWMB, rxbufB, PWM_GROUP_B_SIZE));
for (size_t i = 0; i < N_BMS; i++) { for (size_t i = 0; i < N_BMS; i++) {
size_t offsetA = BUFFER_BMS_OFFSET(i, PWM_GROUP_A_SIZE); size_t offsetA = BUFFER_BMS_OFFSET(i, PWM_GROUP_A_SIZE);
size_t offsetB = BUFFER_BMS_OFFSET(i, PWM_GROUP_B_SIZE); size_t offsetB = BUFFER_BMS_OFFSET(i, PWM_GROUP_B_SIZE);
for (size_t c = 0; c < 16; c += 2) { for (size_t c = 0; c < 16; c += 2) {
uint8_t high = (channels[i] & (1 << (c + 1))) ? (dutyCycle << 4) : 0; uint8_t high = (channels[i] & (1 << (c + 1))) ? (dutyCycle << 4) : 0;
uint8_t low = (channels[i] & (1 << c)) ? dutyCycle : 0; uint8_t low = (channels[i] & (1 << c)) ? dutyCycle : 0;
if (c < 12) { if (c < 12) {
rxbufA[offsetA + (c / 2)] = high | low; rxbufA[offsetA + (c / 2)] = high | low;
} else { } else {
rxbufB[offsetB + ((c - 12) / 2)] = high | low; rxbufB[offsetB + ((c - 12) / 2)] = high | low;
} }
} }
//log the new PWM settings //log the new PWM settings
//output is: PWM - [BMS_ID]: [PWM0] ... [PWM16] //output is: PWM - [BMS_ID]: [PWM0] ... [PWM16]
if (DEBUG_CHANNEL_ENABLED(LOG_LEVEL_DEBUG)) { if (DEBUG_CHANNEL_ENABLED(LOG_LEVEL_DEBUG)) {
debug_log(LOG_LEVEL_DEBUG, "PWM - %d: ", i); debug_log(LOG_LEVEL_DEBUG, "PWM - %d: ", i);
for (size_t j = 0; j < 6; j++) { for (size_t j = 0; j < 6; j++) {
debug_log_cont(LOG_LEVEL_DEBUG, "%x %x ", rxbufA[offsetA + j] & 0x0F, rxbufA[offsetA + j] >> 4); debug_log_cont(LOG_LEVEL_DEBUG, "%x %x ", rxbufA[offsetA + j] & 0x0F, rxbufA[offsetA + j] >> 4);
} }
for (size_t j = 0; j < 2; j++) { for (size_t j = 0; j < 2; j++) {
debug_log_cont(LOG_LEVEL_DEBUG, "%x %x ", rxbufB[offsetB + j] & 0x0F, rxbufB[offsetB + j] >> 4); debug_log_cont(LOG_LEVEL_DEBUG, "%x %x ", rxbufB[offsetB + j] & 0x0F, rxbufB[offsetB + j] >> 4);
} }
debug_log_cont(LOG_LEVEL_DEBUG, "\n"); debug_log_cont(LOG_LEVEL_DEBUG, "\n");
}
} }
}
CHECK_RETURN(writeCMD(WRPWMA, rxbufA, PWM_GROUP_A_SIZE)); CHECK_RETURN(writeCMD(WRPWMA, rxbufA, PWM_GROUP_A_SIZE));
CHECK_RETURN(writeCMD(WRPWMB, rxbufB, PWM_GROUP_B_SIZE)); CHECK_RETURN(writeCMD(WRPWMB, rxbufB, PWM_GROUP_B_SIZE));
return HAL_OK; return HAL_OK;
} }
HAL_StatusTypeDef amsStartBalancing(uint8_t dutyCycle) { return writeCMD(UNMUTE, CMD_EMPTY_BUFFER, 0); } HAL_StatusTypeDef amsStartBalancing(uint8_t dutyCycle) { return writeCMD(UNMUTE, CMD_EMPTY_BUFFER, 0); }
@ -206,59 +226,59 @@ HAL_StatusTypeDef amsStopBalancing() { return writeCMD(MUTE, CMD_EMPTY_BUFFER, 0
HAL_StatusTypeDef amsSelfTest() { return 0; } HAL_StatusTypeDef amsSelfTest() { return 0; }
HAL_StatusTypeDef amsConfigOverUnderVoltage(uint16_t overVoltage, uint16_t underVoltage) { HAL_StatusTypeDef amsConfigOverUnderVoltage(uint16_t overVoltage, uint16_t underVoltage) {
uint8_t buffer[CMD_BUFFER_SIZE(CFG_GROUP_B_SIZE)] = {}; uint8_t buffer[CMD_BUFFER_SIZE(CFG_GROUP_B_SIZE)] = {};
if (underVoltage & 0xF000 || overVoltage & 0xF000) { // only 12 bits allowed if (underVoltage & 0xF000 || overVoltage & 0xF000) { // only 12 bits allowed
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));
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, CFG_GROUP_B_SIZE); size_t offset = BUFFER_BMS_OFFSET(i, CFG_GROUP_B_SIZE);
//UV //UV
buffer[offset + 0] = (uint8_t)(underVoltage & 0xFF); buffer[offset + 0] = (uint8_t)(underVoltage & 0xFF);
buffer[offset + 1] &= 0xF0; buffer[offset + 1] &= 0xF0;
buffer[offset + 1] |= (uint8_t)((underVoltage >> 8) & 0x0F); buffer[offset + 1] |= (uint8_t)((underVoltage >> 8) & 0x0F);
//OV //OV
buffer[offset + 1] &= 0x0F; buffer[offset + 1] &= 0x0F;
buffer[offset + 1] |= (uint8_t)(overVoltage << 4); buffer[offset + 1] |= (uint8_t)(overVoltage << 4);
buffer[offset + 2] = (uint8_t)(overVoltage >> 4); buffer[offset + 2] = (uint8_t)(overVoltage >> 4);
} }
return writeCMD(WRCFGB, buffer, CFG_GROUP_B_SIZE); return writeCMD(WRCFGB, buffer, CFG_GROUP_B_SIZE);
} }
HAL_StatusTypeDef amsCheckUnderOverVoltage(Cell_Module (*module)[N_BMS]) { HAL_StatusTypeDef amsCheckUnderOverVoltage(Cell_Module (*module)[N_BMS]) {
uint8_t regbuffer[CMD_BUFFER_SIZE(STATUS_GROUP_D_SIZE)] = {}; uint8_t regbuffer[CMD_BUFFER_SIZE(STATUS_GROUP_D_SIZE)] = {};
CHECK_RETURN(readCMD(RDSTATD, regbuffer, STATUS_GROUP_D_SIZE));
for (size_t i = 0; i < N_BMS; i++) { CHECK_RETURN(readCMD(RDSTATD, regbuffer, STATUS_GROUP_D_SIZE));
size_t offset = BUFFER_BMS_OFFSET(i, STATUS_GROUP_D_SIZE);
uint32_t ov_uv_data = 0; 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) | ov_uv_data = (regbuffer[offset + 0] << 0) | (regbuffer[offset + 1] << 8) |
(regbuffer[offset + 2] << 16) | (regbuffer[offset + 3] << 24); (regbuffer[offset + 2] << 16) | (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; 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]->underVoltage |= (ov_uv_data >> (j * 2)) & 0x01;
module[i]->overVoltage |= (ov_uv_data >> (j * 2 + 1)) & 0x01; module[i]->overVoltage |= (ov_uv_data >> (j * 2 + 1)) & 0x01;
}
} }
}
return HAL_OK; return HAL_OK;
} }
HAL_StatusTypeDef amsClearFlag() { HAL_StatusTypeDef amsClearFlag() {
uint8_t buffer[CMD_BUFFER_SIZE(6)] = {[0 ... CMD_BUFFER_SIZE(6) - 1] = 0xFF}; uint8_t buffer[CMD_BUFFER_SIZE(6)] = {[0 ... CMD_BUFFER_SIZE(6) - 1] = 0xFF};
return writeCMD(CLRFLAG, buffer, 6); return writeCMD(CLRFLAG, buffer, 6);
} }
HAL_StatusTypeDef amsClearAux() { HAL_StatusTypeDef amsClearAux() {
@ -266,233 +286,238 @@ HAL_StatusTypeDef amsClearAux() {
} }
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)] = {};
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
uint8_t buffer[CMD_BUFFER_SIZE(COMM_GROUP_SIZE)] = {}; 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; uint8_t max_datalen = 0;
for (size_t i = 0; i < N_BMS; i++) { for (size_t i = 0; i < N_BMS; i++) {
if (datalens[i] > max_datalen) { if (datalens[i] > max_datalen) {
max_datalen = datalens[i]; max_datalen = datalens[i];
}
} }
}
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, COMM_GROUP_SIZE); size_t offset = BUFFER_BMS_OFFSET(i, COMM_GROUP_SIZE);
if (!(bms_mask & (1 << i))) { if (!(bms_mask & (1 << i))) {
buffer[offset + 0] = I2C_SEND_NOTRANSFER; buffer[offset + 0] = I2C_SEND_NOTRANSFER;
buffer[offset + 2] = I2C_SEND_NOTRANSFER; buffer[offset + 2] = I2C_SEND_NOTRANSFER;
buffer[offset + 4] = I2C_SEND_NOTRANSFER; buffer[offset + 4] = I2C_SEND_NOTRANSFER;
continue; continue;
} }
} }
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++) {
size_t offset = BUFFER_BMS_OFFSET(j, COMM_GROUP_SIZE); size_t offset = BUFFER_BMS_OFFSET(j, COMM_GROUP_SIZE);
if (!(bms_mask & (1 << j))) 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
continue;
}
//add data to the buffer 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
continue;
}
//case 1: the last group of 3 bytes contained the last data byte, so we need to send a stop // add data to the buffer
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 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) { if (datalens[j] - (int)i == -1) {
buffer[offset + ((i % 3) * 2)] = I2C_SEND_NOTRANSFER; buffer[offset + ((i % 3) * 2)] = I2C_SEND_STOP;
continue; continue;
} }
//case 3: we are still sending data // case 2: we are more than one byte past the end, so we don't need to send any data
buffer[offset + ((i % 3) * 2)] = I2C_SEND; if (datalens[j] - (int)i < -1) {
buffer[offset + ((i % 3) * 2) + 1] = data[j][i - 1]; buffer[offset + ((i % 3) * 2)] = I2C_SEND_NOTRANSFER;
continue;
}
// 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
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
}
} }
//send the data // send the last packet
if (i % 3 == 0 && i != 0) { CHECK_RETURN(writeCMD(WRCOMM, buffer, COMM_GROUP_SIZE));
CHECK_RETURN(writeCMD(WRCOMM, buffer, COMM_GROUP_SIZE)); __pollCMD(STCOMM, 72);
__pollCMD(STCOMM, 72); //wait 72 cycles for the I2C transfer to complete (datasheet page 43) return HAL_OK;
//TODO: not sure if this is correct for a daisy chain
}
}
//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 // 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
uint8_t buffer[CMD_BUFFER_SIZE(COMM_GROUP_SIZE)] = {}; 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; uint8_t max_datalen = 0;
for (size_t i = 0; i < N_BMS; i++) { for (size_t i = 0; i < N_BMS; i++) {
if (datalens[i] > max_datalen) { if (datalens[i] > max_datalen) {
max_datalen = datalens[i]; max_datalen = datalens[i];
}
} }
}
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, COMM_GROUP_SIZE); size_t offset = BUFFER_BMS_OFFSET(i, COMM_GROUP_SIZE);
if (!(bms_mask & (1 << i))) { if (!(bms_mask & (1 << i))) {
buffer[offset + 0] = I2C_SEND_NOTRANSFER; buffer[offset + 0] = I2C_SEND_NOTRANSFER;
buffer[offset + 2] = I2C_SEND_NOTRANSFER; buffer[offset + 2] = I2C_SEND_NOTRANSFER;
buffer[offset + 4] = I2C_SEND_NOTRANSFER; buffer[offset + 4] = I2C_SEND_NOTRANSFER;
continue; continue;
} }
} }
size_t packet_count = ((max_datalen + 1) / 3) + ((max_datalen + 1) % 3 != 0); size_t packet_count = ((max_datalen + 1) / 3) + ((max_datalen + 1) % 3 != 0);
for (size_t i = 0; i < (packet_count * 3); 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) 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++) {
size_t offset = BUFFER_BMS_OFFSET(j, COMM_GROUP_SIZE); size_t offset = BUFFER_BMS_OFFSET(j, COMM_GROUP_SIZE);
if (!(bms_mask & (1 << j))) if (!(bms_mask & (1 << j)))
continue; // skip BMS that are not selected continue; // skip BMS that are not selected
if (i == 0) { if (i == 0) {
buffer[offset + 0] = I2C_SEND_START; buffer[offset + 0] = I2C_SEND_START;
buffer[offset + 1] = addresses[j] << 1 | 0x01; // shift the address left by 1 to make space for the R/W bit buffer[offset + 1] = addresses[j] << 1 | 0x01; // shift the address left by 1 to make space for the R/W bit
continue; 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) { if (datalens[j] - (int)i == -1) {
buffer[offset + ((i % 3) * 2)] = I2C_SEND_STOP; buffer[offset + ((i % 3) * 2)] = I2C_SEND_STOP;
continue; continue;
} }
// case 2: we are more than one byte past the end, so we don't need to receive any data // case 2: we are more than one byte past the end, so we don't need to receive any data
if (datalens[j] - (int)i < -1) { if (datalens[j] - (int)i < -1) {
buffer[offset + ((i % 3) * 2)] = I2C_SEND_NOTRANSFER; buffer[offset + ((i % 3) * 2)] = I2C_SEND_NOTRANSFER;
continue; continue;
} }
// case 3: we are still receiving data // case 3: we are still receiving data
buffer[offset + ((i % 3) * 2)] = I2C_SEND_ACK; buffer[offset + ((i % 3) * 2)] = I2C_SEND_ACK;
} }
// send the command data // send the command data
if (i % 3 == 0 && i != 0) { if (i % 3 == 0 && i != 0) {
CHECK_RETURN(writeCMD(WRCOMM, buffer, COMM_GROUP_SIZE)); CHECK_RETURN(writeCMD(WRCOMM, buffer, COMM_GROUP_SIZE));
__pollCMD(STCOMM, 72); // wait 72 cycles for the I2C transfer to complete (datasheet page 43) __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 // TODO: not sure if this is correct for a daisy chain
// read the data // read the data
CHECK_RETURN(readCMD(RDCOMM, buffer, COMM_GROUP_SIZE)); CHECK_RETURN(readCMD(RDCOMM, buffer, COMM_GROUP_SIZE));
for (size_t j = 0; j < N_BMS; j++) { for (size_t j = 0; j < N_BMS; j++) {
size_t offset = BUFFER_BMS_OFFSET(j, COMM_GROUP_SIZE); size_t offset = BUFFER_BMS_OFFSET(j, COMM_GROUP_SIZE);
if (!(bms_mask & (1 << j))) if (!(bms_mask & (1 << j)))
continue; // skip BMS that are not selected continue; // skip BMS that are not selected
for (size_t k = 0; k < 3; k++) { for (size_t k = 0; k < 3; k++) {
if (datalens[j] - (int)i < -1) { if (datalens[j] - (int)i < -1) {
continue; continue;
} }
data[j][i - 1 + k] = buffer[offset + (k * 2) + 1]; data[j][i - 1 + k] = buffer[offset + (k * 2) + 1];
} }
} }
} }
} }
// send the last packet // send the last packet
CHECK_RETURN(writeCMD(WRCOMM, buffer, COMM_GROUP_SIZE)); CHECK_RETURN(writeCMD(WRCOMM, buffer, COMM_GROUP_SIZE));
__pollCMD(STCOMM, 72); __pollCMD(STCOMM, 72);
// read the data // read the data
CHECK_RETURN(readCMD(RDCOMM, buffer, COMM_GROUP_SIZE)); CHECK_RETURN(readCMD(RDCOMM, buffer, COMM_GROUP_SIZE));
for (size_t j = 0; j < N_BMS; j++) { for (size_t j = 0; j < N_BMS; j++) {
size_t offset = BUFFER_BMS_OFFSET(j, COMM_GROUP_SIZE); size_t offset = BUFFER_BMS_OFFSET(j, COMM_GROUP_SIZE);
if (!(bms_mask & (1 << j))) if (!(bms_mask & (1 << j)))
continue; // skip BMS that are not selected continue; // skip BMS that are not selected
for (size_t k = 0; k < 3; k++) { for (size_t k = 0; k < 3; k++) {
if (datalens[j] - (int)max_datalen < -1) { if (datalens[j] - (int)max_datalen < -1) {
continue; continue;
} }
data[j][max_datalen - 1 + k] = buffer[offset + (k * 2) + 1]; data[j][max_datalen - 1 + k] = buffer[offset + (k * 2) + 1];
} }
} }
return HAL_OK; return HAL_OK;
} }

View File

@ -54,7 +54,7 @@ ADBMS_DetailedStatus AMS_Init(SPI_HandleTypeDef* hspi) {
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++) { \
Cell_Module module = modules[__any_intern_i]; \ Cell_Module module = modules[__any_intern_i]; \
if (x) { \ if ((x)) { \
first_match = __any_intern_i; \ first_match = __any_intern_i; \
break; \ break; \
} \ } \
@ -68,7 +68,7 @@ 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);
static int match = 0; static int match = 0;
if ((match = any(module.status.SLEEP))) { if ((match = any(module.status.SLEEP))) {
@ -94,8 +94,8 @@ ADBMS_DetailedStatus AMS_Idle_Loop() {
return (ADBMS_DetailedStatus){ADBMS_INTERNAL_BMS_FAULT, match}; return (ADBMS_DetailedStatus){ADBMS_INTERNAL_BMS_FAULT, match};
} }
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) {
return (ADBMS_DetailedStatus){ADBMS_INTERNAL_BMS_CHECKSUM_FAIL, -1}; return (ADBMS_DetailedStatus){ADBMS_INTERNAL_BMS_CHECKSUM_FAIL, -1};

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