add I2C over BMS GPIO

send only and untested, for now
This commit is contained in:
Kilian Bracher 2025-02-03 17:54:16 +01:00
parent 009a4d1cf5
commit fd1027523b
Signed by: k.bracher
SSH Key Fingerprint: SHA256:mXpyZkK7RDiJ7qeHCKJX108woM0cl5TrCvNBJASu6lM
4 changed files with 94 additions and 7 deletions

View File

@ -77,9 +77,23 @@
#define SRST 0x0027 //Soft reset #define SRST 0x0027 //Soft reset
#define DIAGN 0x0715 // Diagnos MUX and Poll Status #define DIAGN 0x0715 // Diagnos MUX and Poll Status
#define WRCOMM 0x0721 // Write COMM Register Group #define WRCOMM 0x0721 // Write COMM Register Group
#define RDCOMM 0x0722 // Read COMM Register Group #define RDCOMM 0x0722 // Read COMM Register Group
#define STCOMM 0x0723 // Start I2C/SPI Communication #define STCOMM 0x0723 // Start I2C/SPI Communication
#define I2C_SEND_START 0b0110 << 4
#define I2C_SEND_STOP 0b0001 << 4
#define I2C_SEND 0b0000 << 4
#define I2C_SEND_NOTRANSFER 0b0111 << 4
#define I2C_SEND_ACK 0b0000
#define I2C_SEND_NACK 0b1000
#define I2C_SEND_NACK_STOP 0b1001
#define I2C_RECV_ACK 0b0111
#define I2C_RECV_NACK 0b1111
#define I2C_RECV_ACK_STOP 0b0001
#define I2C_RECV_NACK_STOP 0b1001
#define MUTE 0x0028 // Mute Discharge #define MUTE 0x0028 // Mute Discharge
#define UNMUTE 0x0029 // Unmute Discharge #define UNMUTE 0x0029 // Unmute Discharge

View File

@ -57,7 +57,10 @@ static inline HAL_StatusTypeDef __readCMD(uint16_t command, uint8_t * buffer, si
#define readCMD(command, buffer, buflen) \ #define readCMD(command, buffer, buflen) \
__readCMD(command, buffer, buflen, CMD_BUFFER_SIZE(buflen)) __readCMD(command, buffer, buflen, CMD_BUFFER_SIZE(buflen))
HAL_StatusTypeDef pollCMD(uint16_t command); HAL_StatusTypeDef __pollCMD(uint16_t command, uint8_t waitTime);
#define pollCMD(command) \
__pollCMD(command, (N_BMS * 2) + 1) //poll is only valid after 2 * N_BMS clock cycles, +1 for safety, see datasheet page 55
void mcuAdbmsCSLow(); void mcuAdbmsCSLow();

View File

@ -316,3 +316,75 @@ HAL_StatusTypeDef amsReadCellVoltages(Cell_Module (*module)[N_BMS]) {
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(uint8_t addresses[static N_BMS], uint8_t * data[static N_BMS], 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
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);
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
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;
buffer[offset + 1] = addresses[j];
continue;
}
//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 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];
}
//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 last packet
CHECK_RETURN(writeCMD(WRCOMM, buffer, COMM_GROUP_SIZE));
__pollCMD(STCOMM, 72);
return HAL_OK;
}

View File

@ -212,17 +212,15 @@ HAL_StatusTypeDef ___readCMD(uint16_t command, uint8_t * buffer, size_t arglen)
return HAL_OK; return HAL_OK;
} }
//check poll command - no data PEC sent back //check poll command - no data PEC sent back, waitTime is in SPI clock cycles
HAL_StatusTypeDef pollCMD(uint16_t command) { HAL_StatusTypeDef __pollCMD(uint16_t command, uint8_t waitTime) {
uint8_t buffer[4 + (N_BMS * 2) + 1] = {}; //poll is only valid after 2 * N_BMS clock cycles (datasheet page 55) uint8_t buffer[4 + (waitTime / 8) + 1] = {}; //8 cycles per byte, +1 as we round up
//being conservative and adding 1 byte for the poll response
buffer[0] = (command >> 8) & 0xFF; buffer[0] = (command >> 8) & 0xFF;
buffer[1] = (command) & 0xFF; buffer[1] = (command) & 0xFF;
calculateCommandPEC(buffer, 4); calculateCommandPEC(buffer, 4);
mcuAdbmsCSLow(); mcuAdbmsCSLow();
HAL_StatusTypeDef status = mcuSPITransmitReceive(buffer, buffer, 4 + (N_BMS * 2) + 1); HAL_StatusTypeDef status = mcuSPITransmitReceive(buffer, buffer, sizeof buffer);
mcuAdbmsCSHigh(); mcuAdbmsCSHigh();
if (status != HAL_OK) return status; if (status != HAL_OK) return status;