|
|
|
@ -14,38 +14,58 @@
|
|
|
|
|
|
|
|
|
|
extern uint8_t numberofCells;
|
|
|
|
|
|
|
|
|
|
#define CHECK_RETURN(x) \
|
|
|
|
|
do { \
|
|
|
|
|
HAL_StatusTypeDef status = x; \
|
|
|
|
|
if (status != 0) \
|
|
|
|
|
return status; \
|
|
|
|
|
} while (0)
|
|
|
|
|
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) { \
|
|
|
|
|
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() {
|
|
|
|
|
amsWakeUp();
|
|
|
|
|
readCMD(SRST, CMD_EMPTY_BUFFER, 0);
|
|
|
|
|
amsWakeUp();
|
|
|
|
|
readCMD(SRST, CMD_EMPTY_BUFFER, 0);
|
|
|
|
|
|
|
|
|
|
uint8_t sidbuffer[CMD_BUFFER_SIZE(SID_GROUP_SIZE)] = {};
|
|
|
|
|
CHECK_RETURN(readCMD(RDSID, sidbuffer, SID_GROUP_SIZE));
|
|
|
|
|
|
|
|
|
|
debug_log(LOG_LEVEL_INFO, "BMS reset complete");
|
|
|
|
|
|
|
|
|
|
if (DEBUG_CHANNEL_ENABLED(LOG_LEVEL_INFO)) {
|
|
|
|
|
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);
|
|
|
|
|
uint8_t sidbuffer[CMD_BUFFER_SIZE(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_cont(LOG_LEVEL_INFO, "\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mcuDelay(10);
|
|
|
|
|
amsWakeUp();
|
|
|
|
|
amsStopBalancing();
|
|
|
|
|
amsConfigOverUnderVoltage(DEFAULT_OV, DEFAULT_UV);
|
|
|
|
|
debug_log(LOG_LEVEL_INFO, "BMS reset complete");
|
|
|
|
|
|
|
|
|
|
if (DEBUG_CHANNEL_ENABLED(LOG_LEVEL_INFO)) {
|
|
|
|
|
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
|
|
|
|
|
//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(ADAX | ADAX_CONV_ALL, flagsbuffer, 0)); //start aux measurement
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HAL_StatusTypeDef initAMS(SPI_HandleTypeDef* hspi) {
|
|
|
|
|
adbmsDriverInit(hspi);
|
|
|
|
|
return amsReset();
|
|
|
|
|
adbmsDriverInit(hspi);
|
|
|
|
|
return amsReset();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HAL_StatusTypeDef amsWakeUp() {
|
|
|
|
|
uint8_t buffer[CMD_BUFFER_SIZE(CFG_GROUP_A_SIZE)] = {0};
|
|
|
|
|
return readCMD(RDCFGA, buffer, CFG_GROUP_A_SIZE);
|
|
|
|
|
uint8_t buffer[CMD_BUFFER_SIZE(CFG_GROUP_A_SIZE)] = {0};
|
|
|
|
|
return readCMD(RDCFGA, buffer, CFG_GROUP_A_SIZE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HAL_StatusTypeDef amsCellMeasurement(Cell_Module (*module)[N_BMS]) {
|
|
|
|
|
#warning check conversion counter to ensure that continous conversion has not been stopped
|
|
|
|
|
#warning check for OW conditions: ADSV | ADSV_OW_0 / ADSV_OW_1
|
|
|
|
|
return amsReadCellVoltages(module);
|
|
|
|
|
#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)] = {};
|
|
|
|
|
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);
|
|
|
|
|
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;
|
|
|
|
@ -98,105 +118,105 @@ HAL_StatusTypeDef amsAuxAndStatusMeasurement(Cell_Module (*module)[N_BMS]) {
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
for (size_t i = 0; i < N_BMS; i++) {
|
|
|
|
|
size_t offset = BUFFER_BMS_OFFSET(i, AUX_GROUP_A_SIZE);
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
if (dutyCycle > 0x0F) {
|
|
|
|
|
return HAL_ERROR; // only 4 bits are allowed for dutyCycle
|
|
|
|
|
}
|
|
|
|
|
if (dutyCycle > 0x0F) {
|
|
|
|
|
return HAL_ERROR; // only 4 bits are allowed for dutyCycle
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t rxbufA[CMD_BUFFER_SIZE(PWM_GROUP_A_SIZE)] = {};
|
|
|
|
|
uint8_t rxbufB[CMD_BUFFER_SIZE(PWM_GROUP_B_SIZE)] = {};
|
|
|
|
|
uint8_t rxbufA[CMD_BUFFER_SIZE(PWM_GROUP_A_SIZE)] = {};
|
|
|
|
|
uint8_t rxbufB[CMD_BUFFER_SIZE(PWM_GROUP_B_SIZE)] = {};
|
|
|
|
|
|
|
|
|
|
CHECK_RETURN(readCMD(RDPWMA, rxbufA, PWM_GROUP_A_SIZE));
|
|
|
|
|
CHECK_RETURN(readCMD(RDPWMB, rxbufB, PWM_GROUP_B_SIZE));
|
|
|
|
|
CHECK_RETURN(readCMD(RDPWMA, rxbufA, PWM_GROUP_A_SIZE));
|
|
|
|
|
CHECK_RETURN(readCMD(RDPWMB, rxbufB, PWM_GROUP_B_SIZE));
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < N_BMS; i++) {
|
|
|
|
|
size_t offsetA = BUFFER_BMS_OFFSET(i, PWM_GROUP_A_SIZE);
|
|
|
|
|
size_t offsetB = BUFFER_BMS_OFFSET(i, PWM_GROUP_B_SIZE);
|
|
|
|
|
for (size_t i = 0; i < N_BMS; i++) {
|
|
|
|
|
size_t offsetA = BUFFER_BMS_OFFSET(i, PWM_GROUP_A_SIZE);
|
|
|
|
|
size_t offsetB = BUFFER_BMS_OFFSET(i, PWM_GROUP_B_SIZE);
|
|
|
|
|
|
|
|
|
|
for (size_t c = 0; c < 16; c += 2) {
|
|
|
|
|
uint8_t high = (channels[i] & (1 << (c + 1))) ? (dutyCycle << 4) : 0;
|
|
|
|
|
for (size_t c = 0; c < 16; c += 2) {
|
|
|
|
|
uint8_t high = (channels[i] & (1 << (c + 1))) ? (dutyCycle << 4) : 0;
|
|
|
|
|
uint8_t low = (channels[i] & (1 << c)) ? dutyCycle : 0;
|
|
|
|
|
|
|
|
|
|
if (c < 12) {
|
|
|
|
|
rxbufA[offsetA + (c / 2)] = high | low;
|
|
|
|
|
} else {
|
|
|
|
|
rxbufB[offsetB + ((c - 12) / 2)] = high | low;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (c < 12) {
|
|
|
|
|
rxbufA[offsetA + (c / 2)] = high | low;
|
|
|
|
|
} else {
|
|
|
|
|
rxbufB[offsetB + ((c - 12) / 2)] = high | low;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//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++) {
|
|
|
|
|
debug_log_cont(LOG_LEVEL_DEBUG, "%x %x ", rxbufA[offsetA + j] & 0x0F, rxbufA[offsetA + j] >> 4);
|
|
|
|
|
}
|
|
|
|
|
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, "\n");
|
|
|
|
|
if (DEBUG_CHANNEL_ENABLED(LOG_LEVEL_DEBUG)) {
|
|
|
|
|
debug_log(LOG_LEVEL_DEBUG, "PWM - %d: ", i);
|
|
|
|
|
for (size_t j = 0; j < 6; j++) {
|
|
|
|
|
debug_log_cont(LOG_LEVEL_DEBUG, "%x %x ", rxbufA[offsetA + j] & 0x0F, rxbufA[offsetA + j] >> 4);
|
|
|
|
|
}
|
|
|
|
|
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, "\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CHECK_RETURN(writeCMD(WRPWMA, rxbufA, PWM_GROUP_A_SIZE));
|
|
|
|
|
CHECK_RETURN(writeCMD(WRPWMB, rxbufB, PWM_GROUP_B_SIZE));
|
|
|
|
|
CHECK_RETURN(writeCMD(WRPWMA, rxbufA, PWM_GROUP_A_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); }
|
|
|
|
@ -206,41 +226,41 @@ HAL_StatusTypeDef amsStopBalancing() { return writeCMD(MUTE, CMD_EMPTY_BUFFER, 0
|
|
|
|
|
HAL_StatusTypeDef amsSelfTest() { return 0; }
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
return HAL_ERROR;
|
|
|
|
|
}
|
|
|
|
|
if (underVoltage & 0xF000 || overVoltage & 0xF000) { // only 12 bits allowed
|
|
|
|
|
return HAL_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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++) {
|
|
|
|
|
size_t offset = BUFFER_BMS_OFFSET(i, 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
|
|
|
|
|
buffer[offset + 0] = (uint8_t)(underVoltage & 0xFF);
|
|
|
|
|
buffer[offset + 1] &= 0xF0;
|
|
|
|
|
buffer[offset + 1] |= (uint8_t)((underVoltage >> 8) & 0x0F);
|
|
|
|
|
buffer[offset + 0] = (uint8_t)(underVoltage & 0xFF);
|
|
|
|
|
buffer[offset + 1] &= 0xF0;
|
|
|
|
|
buffer[offset + 1] |= (uint8_t)((underVoltage >> 8) & 0x0F);
|
|
|
|
|
|
|
|
|
|
//OV
|
|
|
|
|
buffer[offset + 1] &= 0x0F;
|
|
|
|
|
buffer[offset + 1] |= (uint8_t)(overVoltage << 4);
|
|
|
|
|
buffer[offset + 2] = (uint8_t)(overVoltage >> 4);
|
|
|
|
|
}
|
|
|
|
|
buffer[offset + 1] &= 0x0F;
|
|
|
|
|
buffer[offset + 1] |= (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]) {
|
|
|
|
|
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));
|
|
|
|
|
CHECK_RETURN(readCMD(RDSTATD, regbuffer, STATUS_GROUP_D_SIZE));
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
@ -250,15 +270,15 @@ HAL_StatusTypeDef amsCheckUnderOverVoltage(Cell_Module (*module)[N_BMS]) {
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return HAL_OK;
|
|
|
|
|
return HAL_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HAL_StatusTypeDef amsClearFlag() {
|
|
|
|
|
uint8_t buffer[CMD_BUFFER_SIZE(6)] = {[0 ... CMD_BUFFER_SIZE(6) - 1] = 0xFF};
|
|
|
|
|
return writeCMD(CLRFLAG, buffer, 6);
|
|
|
|
|
uint8_t buffer[CMD_BUFFER_SIZE(6)] = {[0 ... CMD_BUFFER_SIZE(6) - 1] = 0xFF};
|
|
|
|
|
return writeCMD(CLRFLAG, buffer, 6);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HAL_StatusTypeDef amsClearAux() {
|
|
|
|
@ -266,233 +286,238 @@ HAL_StatusTypeDef amsClearAux() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
for (size_t i = 0; i < N_BMS; i++) {
|
|
|
|
|
size_t offset = BUFFER_BMS_OFFSET(i, CV_GROUP_A_SIZE);
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
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)] = {};
|
|
|
|
|
// 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++) {
|
|
|
|
|
if (datalens[i] > max_datalen) {
|
|
|
|
|
max_datalen = datalens[i];
|
|
|
|
|
uint8_t max_datalen = 0;
|
|
|
|
|
for (size_t i = 0; i < N_BMS; i++) {
|
|
|
|
|
if (datalens[i] > max_datalen) {
|
|
|
|
|
max_datalen = datalens[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < N_BMS; i++) {
|
|
|
|
|
size_t offset = BUFFER_BMS_OFFSET(i, COMM_GROUP_SIZE);
|
|
|
|
|
for (size_t i = 0; i < N_BMS; i++) {
|
|
|
|
|
size_t offset = BUFFER_BMS_OFFSET(i, COMM_GROUP_SIZE);
|
|
|
|
|
|
|
|
|
|
if (!(bms_mask & (1 << i))) {
|
|
|
|
|
buffer[offset + 0] = I2C_SEND_NOTRANSFER;
|
|
|
|
|
buffer[offset + 2] = I2C_SEND_NOTRANSFER;
|
|
|
|
|
buffer[offset + 4] = I2C_SEND_NOTRANSFER;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!(bms_mask & (1 << i))) {
|
|
|
|
|
buffer[offset + 0] = I2C_SEND_NOTRANSFER;
|
|
|
|
|
buffer[offset + 2] = I2C_SEND_NOTRANSFER;
|
|
|
|
|
buffer[offset + 4] = I2C_SEND_NOTRANSFER;
|
|
|
|
|
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 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
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
// 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
|
|
|
|
|
if (datalens[j] - (int)i == -1) {
|
|
|
|
|
buffer[offset + ((i % 3) * 2)] = I2C_SEND_STOP;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// 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
|
|
|
|
|
if (datalens[j] - (int)i < -1) {
|
|
|
|
|
buffer[offset + ((i % 3) * 2)] = I2C_SEND_NOTRANSFER;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// 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
|
|
|
|
|
buffer[offset + ((i % 3) * 2)] = I2C_SEND;
|
|
|
|
|
buffer[offset + ((i % 3) * 2) + 1] = data[j][i - 1];
|
|
|
|
|
// 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
|
|
|
|
|
if (i % 3 == 0 && i != 0) {
|
|
|
|
|
CHECK_RETURN(writeCMD(WRCOMM, buffer, COMM_GROUP_SIZE));
|
|
|
|
|
// send the last packet
|
|
|
|
|
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)
|
|
|
|
|
//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;
|
|
|
|
|
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) {
|
|
|
|
|
uint8_t buffer[CMD_BUFFER_SIZE(COMM_GROUP_SIZE)] = {};
|
|
|
|
|
// 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;
|
|
|
|
|
for (size_t i = 0; i < N_BMS; i++) {
|
|
|
|
|
if (datalens[i] > max_datalen) {
|
|
|
|
|
max_datalen = datalens[i];
|
|
|
|
|
uint8_t max_datalen = 0;
|
|
|
|
|
for (size_t i = 0; i < N_BMS; i++) {
|
|
|
|
|
if (datalens[i] > max_datalen) {
|
|
|
|
|
max_datalen = datalens[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < N_BMS; i++) {
|
|
|
|
|
size_t offset = BUFFER_BMS_OFFSET(i, COMM_GROUP_SIZE);
|
|
|
|
|
for (size_t i = 0; i < N_BMS; i++) {
|
|
|
|
|
size_t offset = BUFFER_BMS_OFFSET(i, COMM_GROUP_SIZE);
|
|
|
|
|
|
|
|
|
|
if (!(bms_mask & (1 << i))) {
|
|
|
|
|
buffer[offset + 0] = I2C_SEND_NOTRANSFER;
|
|
|
|
|
buffer[offset + 2] = I2C_SEND_NOTRANSFER;
|
|
|
|
|
buffer[offset + 4] = I2C_SEND_NOTRANSFER;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!(bms_mask & (1 << i))) {
|
|
|
|
|
buffer[offset + 0] = I2C_SEND_NOTRANSFER;
|
|
|
|
|
buffer[offset + 2] = I2C_SEND_NOTRANSFER;
|
|
|
|
|
buffer[offset + 4] = I2C_SEND_NOTRANSFER;
|
|
|
|
|
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);
|
|
|
|
|
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
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
if (i == 0) {
|
|
|
|
|
buffer[offset + 0] = I2C_SEND_START;
|
|
|
|
|
if (i == 0) {
|
|
|
|
|
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
|
|
|
|
|
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
|
|
|
|
|
if (datalens[j] - (int)i == -1) {
|
|
|
|
|
buffer[offset + ((i % 3) * 2)] = I2C_SEND_STOP;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// 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 receive any data
|
|
|
|
|
if (datalens[j] - (int)i < -1) {
|
|
|
|
|
buffer[offset + ((i % 3) * 2)] = I2C_SEND_NOTRANSFER;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// 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) {
|
|
|
|
|
buffer[offset + ((i % 3) * 2)] = I2C_SEND_NOTRANSFER;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// case 3: we are still receiving data
|
|
|
|
|
buffer[offset + ((i % 3) * 2)] = I2C_SEND_ACK;
|
|
|
|
|
}
|
|
|
|
|
// case 3: we are still receiving data
|
|
|
|
|
buffer[offset + ((i % 3) * 2)] = I2C_SEND_ACK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// send the command data
|
|
|
|
|
if (i % 3 == 0 && i != 0) {
|
|
|
|
|
CHECK_RETURN(writeCMD(WRCOMM, buffer, COMM_GROUP_SIZE));
|
|
|
|
|
// send the command 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
|
|
|
|
|
|
|
|
|
|
// read the data
|
|
|
|
|
CHECK_RETURN(readCMD(RDCOMM, buffer, COMM_GROUP_SIZE));
|
|
|
|
|
// read the data
|
|
|
|
|
CHECK_RETURN(readCMD(RDCOMM, buffer, COMM_GROUP_SIZE));
|
|
|
|
|
|
|
|
|
|
for (size_t j = 0; j < N_BMS; j++) {
|
|
|
|
|
size_t offset = BUFFER_BMS_OFFSET(j, COMM_GROUP_SIZE);
|
|
|
|
|
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
|
|
|
|
|
if (!(bms_mask & (1 << j)))
|
|
|
|
|
continue; // skip BMS that are not selected
|
|
|
|
|
|
|
|
|
|
for (size_t k = 0; k < 3; k++) {
|
|
|
|
|
if (datalens[j] - (int)i < -1) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
for (size_t k = 0; k < 3; k++) {
|
|
|
|
|
if (datalens[j] - (int)i < -1) {
|
|
|
|
|
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
|
|
|
|
|
CHECK_RETURN(writeCMD(WRCOMM, buffer, COMM_GROUP_SIZE));
|
|
|
|
|
__pollCMD(STCOMM, 72);
|
|
|
|
|
// send the last packet
|
|
|
|
|
CHECK_RETURN(writeCMD(WRCOMM, buffer, COMM_GROUP_SIZE));
|
|
|
|
|
__pollCMD(STCOMM, 72);
|
|
|
|
|
|
|
|
|
|
// read the data
|
|
|
|
|
CHECK_RETURN(readCMD(RDCOMM, buffer, COMM_GROUP_SIZE));
|
|
|
|
|
// read the data
|
|
|
|
|
CHECK_RETURN(readCMD(RDCOMM, buffer, COMM_GROUP_SIZE));
|
|
|
|
|
|
|
|
|
|
for (size_t j = 0; j < N_BMS; j++) {
|
|
|
|
|
size_t offset = BUFFER_BMS_OFFSET(j, COMM_GROUP_SIZE);
|
|
|
|
|
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
|
|
|
|
|
if (!(bms_mask & (1 << j)))
|
|
|
|
|
continue; // skip BMS that are not selected
|
|
|
|
|
|
|
|
|
|
for (size_t k = 0; k < 3; k++) {
|
|
|
|
|
if (datalens[j] - (int)max_datalen < -1) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
for (size_t k = 0; k < 3; k++) {
|
|
|
|
|
if (datalens[j] - (int)max_datalen < -1) {
|
|
|
|
|
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;
|
|
|
|
|
}
|