Files
CuboBmsFirmware/firmware/Core/Src/driverSWLTC6804.c
2023-03-29 20:05:42 +03:00

790 lines
35 KiB
C

/*
Copyright 2017 - 2018 Danny Bokma danny@diebie.nl
Copyright 2019 - 2020 Kevin Dionne kevin.dionne@ennoid.me
This file is part of the DieBieMS/ENNOID-BMS firmware.
The DieBieMS/ENNOID-BMS firmware is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The DieBieMS/ENNOID-BMS firmware is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "driverSWLTC6804.h"
#define LTC6804_CS_Pin GPIO_PIN_3
#define LTC6804_CS_GPIO_Port GPIOC
uint8_t driverSWLTC6804TotalNumberOfICs = 0;
uint8_t driverSWLTC6804MaxNoOfCellPerModule = 0;
uint8_t driverSWLTC6804MaxNoOfTempSensorPerModule = 0;
driverLTC6804ConfigStructTypedef driverSWLTC6804ConfigStruct;
void driverSWLTC6804DelayMS(uint32_t delayMS) {
uint32_t currentTick = HAL_GetTick();
while(!modDelayTick1ms(&currentTick,delayMS)){};
}
void driverSWLTC6804Init(driverLTC6804ConfigStructTypedef configStruct, uint8_t totalNumberOfLTCs, uint8_t noOfCellPerModule, uint8_t noOfTempSensorPerModule, uint8_t cellMonitorType) {
driverSWLTC6804ConfigStruct = configStruct;
driverSWLTC6804TotalNumberOfICs = totalNumberOfLTCs;
driverSWLTC6804MaxNoOfCellPerModule = noOfCellPerModule;
driverSWLTC6804MaxNoOfTempSensorPerModule = noOfTempSensorPerModule;
uint8_t rxConfig [driverSWLTC6804TotalNumberOfICs][8];
uint8_t LTCScanCount = 0;
int8_t returnPEC = -1;
driverHWSPI1Init(LTC6804_CS_GPIO_Port,LTC6804_CS_Pin);
driverSWLTC6804WakeIC();
while((LTCScanCount < 5) && (returnPEC == -1)){
returnPEC = driverSWLTC6804ReadConfigRegister(driverSWLTC6804TotalNumberOfICs,rxConfig);
driverSWLTC6804WakeIC();
driverSWLTC6804WriteConfigRegister(driverSWLTC6804TotalNumberOfICs,0,false);
if(cellMonitorType==CELL_MON_LTC6812_1 || cellMonitorType == CELL_MON_LTC6813_1){
driverSWLTC6804WriteConfigRegisterB(driverSWLTC6804TotalNumberOfICs,0,false);
}
driverSWLTC6804WakeIC();
LTCScanCount++;
}
}
void driverSWLTC6804ResetCellVoltageRegisters(void) {
uint8_t cmd[4];
uint16_t cmd_pec;
cmd[0] = 0x07;
cmd[1] = 0x11;
cmd_pec = driverSWLTC6804CalcPEC15(2, cmd);
cmd[2] = (uint8_t)(cmd_pec >> 8);
cmd[3] = (uint8_t)(cmd_pec );
driverSWLTC6804WakeIC();
driverSWLTC6804Write(cmd,4);
}
void driverSWLTC6804ResetAuxRegisters(void) {
uint8_t cmd[4];
uint16_t cmd_pec;
cmd[0] = 0x07;
cmd[1] = 0x12;
cmd_pec = driverSWLTC6804CalcPEC15(2, cmd);
cmd[2] = (uint8_t)(cmd_pec >> 8);
cmd[3] = (uint8_t)(cmd_pec );
driverSWLTC6804WakeIC();
driverSWLTC6804Write(cmd,4);
}
void driverSWLTC6804ResetStatusRegisters(void) {
uint8_t cmd[4];
uint16_t cmd_pec;
cmd[0] = 0x07;
cmd[1] = 0x13;
cmd_pec = driverSWLTC6804CalcPEC15(2, cmd);
cmd[2] = (uint8_t)(cmd_pec >> 8);
cmd[3] = (uint8_t)(cmd_pec );
driverSWLTC6804WakeIC();
driverSWLTC6804Write(cmd,4);
}
void driverSWLTC6804StartCellAndAuxVoltageConversion(uint8_t MD,uint8_t DCP) {
uint8_t cmd[4];
uint16_t cmd_pec;
uint8_t ADCVAX[2]; //!< Cell Voltage conversion command.
ADCVAX[0] = ((MD & 0x02) >> 1) + 0x04;
ADCVAX[1] = ((MD & 0x01) << 7) + 0x6F + (DCP<<4);
cmd[0] = ADCVAX[0];
cmd[1] = ADCVAX[1];
cmd_pec = driverSWLTC6804CalcPEC15(2, ADCVAX);
cmd[2] = (uint8_t)(cmd_pec >> 8);
cmd[3] = (uint8_t)(cmd_pec);
driverSWLTC6804WakeIC();
driverSWLTC6804Write(cmd,4);
}
void driverSWLTC6804StartCellVoltageConversion(uint8_t MD,uint8_t DCP, uint8_t CH) {
uint8_t cmd[4];
uint16_t cmd_pec;
uint8_t ADCV[2]; //!< Cell Voltage conversion command.
ADCV[0] = ((MD & 0x02) >> 1) + 0x02;
ADCV[1] = ((MD & 0x01) << 7) + 0x60 + (DCP<<4) + CH;
cmd[0] = ADCV[0];
cmd[1] = ADCV[1];
cmd_pec = driverSWLTC6804CalcPEC15(2, ADCV);
cmd[2] = (uint8_t)(cmd_pec >> 8);
cmd[3] = (uint8_t)(cmd_pec);
driverSWLTC6804WakeIC();
driverSWLTC6804Write(cmd,4);
}
void driverSWLTC6804StartLoadedCellVoltageConversion(uint8_t MD,uint8_t DCP, uint8_t CH,uint8_t PUP) {
uint8_t cmd[4];
uint16_t cmd_pec;
uint8_t ADOW[2]; //!< Cell Voltage conversion command.
ADOW[0] = ((MD & 0x02) >> 1) + 0x02;
ADOW[1] = ((MD & 0x01) << 7) + 0x28 + (DCP<<4) + CH + (PUP<<6);
cmd[0] = ADOW[0];
cmd[1] = ADOW[1];
cmd_pec = driverSWLTC6804CalcPEC15(2, ADOW);
cmd[2] = (uint8_t)(cmd_pec >> 8);
cmd[3] = (uint8_t)(cmd_pec);
driverSWLTC6804WakeIC();
driverSWLTC6804Write(cmd,4);
}
void driverSWLTC6804StartAuxVoltageConversion(uint8_t MD, uint8_t CHG) {
uint8_t cmd[4];
uint16_t cmd_pec;
uint8_t ADAX[2]; //!< GPIO conversion command.
ADAX[0] = ((MD & 0x02) >> 1) + 0x04;
ADAX[1] = ((MD & 0x01) << 7) + 0x60 + CHG ;
cmd[0] = ADAX[0];
cmd[1] = ADAX[1];
cmd_pec = driverSWLTC6804CalcPEC15(2, ADAX);
cmd[2] = (uint8_t)(cmd_pec >> 8);
cmd[3] = (uint8_t)(cmd_pec);
driverSWLTC6804WakeIC();
driverSWLTC6804Write(cmd,4);
}
//bool driverSWLTC6804ReadCellVoltagesArray(float cellVoltagesArray[][driverSWLTC6804MaxNoOfCellPerModule]) {
bool driverSWLTC6804ReadCellVoltagesArray(float cellVoltagesArray[][18]) {
bool dataValid = true;
// uint8_t driverSWLTC6804MaxNoOfCellPerModule1 = 12;
//uint8_t i = 0;
uint16_t cellVoltageArrayCodes[driverSWLTC6804TotalNumberOfICs][driverSWLTC6804MaxNoOfCellPerModule];
driverSWLTC6804ReadCellVoltageRegisters(CELL_CH_ALL,driverSWLTC6804TotalNumberOfICs,cellVoltageArrayCodes);
for(uint8_t modulePointer = 0; modulePointer < driverSWLTC6804TotalNumberOfICs; modulePointer++) {
for(uint8_t cellPointer = 0; cellPointer < driverSWLTC6804MaxNoOfCellPerModule; cellPointer++){
if (cellVoltageArrayCodes[modulePointer][cellPointer]*0.0001f < 10.0f) {
cellVoltagesArray[modulePointer][cellPointer] = cellVoltageArrayCodes[modulePointer][cellPointer]*0.0001f;
// cellVoltagesArray[modulePointer][cellPointer] = i++;
} else {
dataValid = false;
}
}
}
// cellVoltagesArray[0][0] = 1;
// cellVoltagesArray[0][1] = 2;
// cellVoltagesArray[0][2] = 3;
// cellVoltagesArray[0][3] = 4;
// cellVoltagesArray[0][4] = 5;
// cellVoltagesArray[0][5] = 6;
// cellVoltagesArray[0][6] = 7;
// cellVoltagesArray[0][7] = 8;
// cellVoltagesArray[0][8] = 9;
// cellVoltagesArray[0][9] = 10;
// cellVoltagesArray[0][10] = 11;
// cellVoltagesArray[0][11] = 12;
//
// cellVoltagesArray[1][0] = 13;
// cellVoltagesArray[1][1] = 14;
// cellVoltagesArray[1][2] = 15;
// cellVoltagesArray[1][3] = 16;
// cellVoltagesArray[1][4] = 17;
// cellVoltagesArray[1][5] = 18;
// cellVoltagesArray[1][6] = 13;
// cellVoltagesArray[1][7] = 14;
// cellVoltagesArray[1][8] = 15;
// cellVoltagesArray[1][9] = 16;
// cellVoltagesArray[1][10] = 17;
// cellVoltagesArray[1][11] = 18;
// cellVoltagesArray[0][4] = cellVoltagesArray[0][6]; // 5=7
// cellVoltagesArray[0][5] = cellVoltagesArray[0][7]; // 6=8
// cellVoltagesArray[0][6] = cellVoltagesArray[0][8]; // 7=9
// cellVoltagesArray[0][7] = cellVoltagesArray[0][9]; // 8=10
// cellVoltagesArray[0][8] = 0;
// cellVoltagesArray[0][9] = 0;
// cellVoltagesArray[0][10] = 0;
// cellVoltagesArray[0][11] = 0;
return dataValid;
}
uint8_t driverSWLTC6804ReadCellVoltageRegisters(uint8_t reg, uint8_t total_ic, uint16_t cell_codes[][driverSWLTC6804MaxNoOfCellPerModule]) {
const uint8_t NUM_RX_BYT = 8;
const uint8_t BYT_IN_REG = 6;
const uint8_t CELL_IN_REG = 3;
uint8_t *cell_data;
int8_t pec_error = 0;
uint16_t parsed_cell;
uint16_t received_pec;
uint16_t data_pec;
uint8_t data_counter=0; //data counter
cell_data = (uint8_t *) malloc((NUM_RX_BYT*total_ic)*sizeof(uint8_t));
if (reg == 0) {
for(uint8_t cell_reg = 1; cell_reg<((driverSWLTC6804MaxNoOfCellPerModule/3)+1); cell_reg++) { //executes once for each of the LTC6804 cell voltage registers
data_counter = 0;
driverSWLTC6804ReadCellVoltageGroups(cell_reg, total_ic,cell_data ); //Reads a single Cell voltage register
for (uint8_t current_ic = 0 ; current_ic < total_ic; current_ic++) { // executes for every LTC6804 in the daisy chain current_ic is used as the IC counter
for(uint8_t current_cell = 0; current_cell<CELL_IN_REG; current_cell++) { // This loop parses the read back data into cell voltages, it loops once for each of the 3 cell voltage codes in the register
parsed_cell = cell_data[data_counter] + (cell_data[data_counter + 1] << 8); //Each cell code is received as two bytes and is combined to create the parsed cell voltage code
cell_codes[current_ic][current_cell + ((cell_reg - 1) * CELL_IN_REG)] = parsed_cell;
data_counter = data_counter + 2; //Because cell voltage codes are two bytes the data counter must increment by two for each parsed cell code
}
received_pec = (cell_data[data_counter] << 8) + cell_data[data_counter+1]; //The received PEC for the current_ic is transmitted as the 7th and 8th after the 6 cell voltage data bytes
data_pec = driverSWLTC6804CalcPEC15(BYT_IN_REG, &cell_data[current_ic * NUM_RX_BYT]);
if(received_pec != data_pec) {
pec_error = -1; //The pec_error variable is simply set negative if any PEC errors are detected in the serial data
}
data_counter=data_counter+2; //Because the transmitted PEC code is 2 bytes long the data_counter must be incremented by 2 bytes to point to the next ICs cell voltage data
}
}
}else{
driverSWLTC6804ReadCellVoltageGroups(reg, total_ic,cell_data);
for (uint8_t current_ic = 0 ; current_ic < total_ic; current_ic++) { // executes for every LTC6804 in the daisy chain current_ic is used as the IC counter
for(uint8_t current_cell = 0; current_cell < CELL_IN_REG; current_cell++) { // This loop parses the read back data into cell voltages, it loops once for each of the 3 cell voltage codes in the register
parsed_cell = cell_data[data_counter] + (cell_data[data_counter+1]<<8); //Each cell code is received as two bytes and is combined to create the parsed cell voltage code
cell_codes[current_ic][current_cell + ((reg - 1) * CELL_IN_REG)] = 0x0000FFFF & parsed_cell;
data_counter= data_counter + 2; //Because cell voltage codes are two bytes the data counter must increment by two for each parsed cell code
}
received_pec = (cell_data[data_counter] << 8 )+ cell_data[data_counter + 1]; //The received PEC for the current_ic is transmitted as the 7th and 8th after the 6 cell voltage data bytes
data_pec = driverSWLTC6804CalcPEC15(BYT_IN_REG, &cell_data[current_ic * NUM_RX_BYT]);
if(received_pec != data_pec) {
pec_error = -1; //The pec_error variable is simply set negative if any PEC errors are detected in the serial data
}
data_counter= data_counter + 2; //Because the transmitted PEC code is 2 bytes long the data_counter must be incremented by 2 bytes to point to the next ICs cell voltage data
}
}
free(cell_data);
return(pec_error);
}
void driverSWLTC6804ReadCellVoltageGroups(uint8_t reg, uint8_t total_ic, uint8_t *data ) {
const uint8_t REG_LEN = 8; //number of bytes in each ICs register + 2 bytes for the PEC
uint8_t cmd[4];
uint16_t cmd_pec;
//static uint8_t clean_flag = 0;
// uint8_t unlimbuf[1024];
//if(clean_flag==0){
// memset(unlimbuf,0,1023);
//clean_flag=1;
// }
if (reg == 1) { //1: RDCVA
cmd[1] = 0x04;
cmd[0] = 0x00;
}else if(reg == 2) { //2: RDCVB
cmd[1] = 0x06;
cmd[0] = 0x00;
}else if(reg == 3) { //3: RDCVC
cmd[1] = 0x08;
cmd[0] = 0x00;
}else if(reg == 4) { //4: RDCVD
cmd[1] = 0x0A;
cmd[0] = 0x00;
}else if(reg == 5) { //5: RDCVE - LTC6812 & LTC6813 only
cmd[1] = 0x09;
cmd[0] = 0x00;
}else if(reg == 6) { //6: RDCVF - LTC6813 only
cmd[1] = 0x0B;
cmd[0] = 0x00;
}
cmd_pec = driverSWLTC6804CalcPEC15(2, cmd);
cmd[2] = (uint8_t)(cmd_pec >> 8);
cmd[3] = (uint8_t)(cmd_pec);
driverSWLTC6804WakeIC(); //This will guarantee that the LTC6804 isoSPI port is awake. This command can be removed.
driverSWLTC6804WriteRead(cmd,4,data,(REG_LEN*total_ic));
// driverSWLTC6804WriteRead(cmd,4,unlimbuf,(REG_LEN*total_ic));
}
bool driverSWLTC6804ReadVoltageFlags(uint32_t *underVoltageFlags, uint32_t *overVoltageFlags, uint32_t lastICMask, uint8_t noOfParallelModules, uint32_t dieTemperature[]) {
// Variables
uint32_t newVoltageUnder = 0;
uint32_t newVoltageOver = 0;
driverSWLTC6804StatusStructTypedef driverSWLTC6804StatusStruct[driverSWLTC6804TotalNumberOfICs];
// Get the data from the modules
driverSWLTC6804ReadStatusValues(driverSWLTC6804TotalNumberOfICs,driverSWLTC6804StatusStruct);
// Combine it
for(uint8_t modulePointer = 0; modulePointer < driverSWLTC6804TotalNumberOfICs; modulePointer++) {
dieTemperature[modulePointer] = driverSWLTC6804StatusStruct[modulePointer].dieTemperature;
//if we have a different number of cells monitored in the last IC, disable error bits with mask
if((modulePointer+1) % (driverSWLTC6804TotalNumberOfICs/noOfParallelModules) == 0 && modulePointer != 0){
driverSWLTC6804StatusStruct[modulePointer].underVoltage &= lastICMask;
driverSWLTC6804StatusStruct[modulePointer].overVoltage &= lastICMask;
newVoltageUnder |= driverSWLTC6804StatusStruct[modulePointer].underVoltage;
newVoltageOver |= driverSWLTC6804StatusStruct[modulePointer].overVoltage;
}else{
newVoltageUnder |= driverSWLTC6804StatusStruct[modulePointer].underVoltage;
newVoltageOver |= driverSWLTC6804StatusStruct[modulePointer].overVoltage;
}
}
// Transfer the data to the output
*underVoltageFlags = newVoltageUnder;
*overVoltageFlags = newVoltageOver;
return false;
}
uint8_t driverSWLTC6804ReadStatusValues(uint8_t total_ic, driverSWLTC6804StatusStructTypedef statusArray[]) {
const uint8_t NUM_RX_BYT = 8;
const uint8_t BYT_IN_REG = 6;
uint32_t registersCombined;
uint32_t registersCombinedTemp;
int8_t pec_error = 0;
uint16_t received_pec;
uint16_t data_pec;
uint8_t data_counter = 0; //data counter
uint8_t *status_data = (uint8_t *) malloc((NUM_RX_BYT*total_ic)*sizeof(uint8_t));
driverSWLTC6804ReadStatusGroups(1,total_ic,status_data);
data_counter = 0;
for (uint8_t current_ic = 0 ; current_ic < total_ic; current_ic++) {
// Extract DATA
statusArray[current_ic].sumOfCells = ((status_data[data_counter+1] << 8) | status_data[data_counter]) * 0.0001f;
statusArray[current_ic].dieTemperature = (((status_data[data_counter+3] << 8) | status_data[data_counter+2]) * 0.0001f)/0.0075f - 273.0f;
statusArray[current_ic].voltageAnalogSupply = ((status_data[data_counter+5] << 8) | status_data[data_counter+4]) * 0.0001f;
data_counter += 6;
// Calculate PEC
received_pec = (status_data[data_counter] << 8) + status_data[data_counter+1]; //The received PEC for the current_ic is transmitted as the 7th and 8th after the 6 cell voltage data bytes
data_counter += 2;
data_pec = driverSWLTC6804CalcPEC15(BYT_IN_REG, &status_data[current_ic * NUM_RX_BYT]);
if(received_pec != data_pec) {
pec_error = -1; //The pec_error variable is simply set negative if any PEC errors are detected in the serial data
}
}
driverSWLTC6804ReadStatusGroups(2,total_ic,status_data);
data_counter = 0;
for (uint8_t current_ic = 0 ; current_ic < total_ic; current_ic++) {
// Reset vlag registers
statusArray[current_ic].underVoltage = 0;
statusArray[current_ic].overVoltage = 0;
// Extract DATA
statusArray[current_ic].voltageDigitalSupply = ((status_data[data_counter+1] << 8) | status_data[data_counter]) * 0.0001f;
registersCombined = (status_data[data_counter+4] << 16) | (status_data[data_counter+3] << 8) | status_data[data_counter+2]; // Combine all registers in one variable
registersCombinedTemp = registersCombined & 0x00555555; // Filter out only the undervoltage bits
for(int bitPointer = 0; bitPointer < driverSWLTC6804ConfigStruct.noOfCells; bitPointer++)
statusArray[current_ic].underVoltage |= (registersCombinedTemp & (1 << bitPointer*2)) ? (1 << bitPointer) : 0; // Shift undervoltage bits closer together and store them in *underVoltageFlags
registersCombinedTemp = registersCombined & 0x00AAAAAA; // Filter out only the overvoltage bits
registersCombinedTemp = registersCombinedTemp >> 1; // Move everything one bit to the right
for(int bitPointer = 0; bitPointer < driverSWLTC6804ConfigStruct.noOfCells; bitPointer++)
statusArray[current_ic].overVoltage |= (registersCombinedTemp & (1 << bitPointer*2)) ? (1 << bitPointer) : 0; // And do the same for the overvoltage bits
statusArray[current_ic].muxFail = 0;
data_counter += 6;
// Calculate PEC
received_pec = (status_data[data_counter] << 8) + status_data[data_counter+1]; //The received PEC for the current_ic is transmitted as the 7th and 8th after the 6 cell voltage data bytes
data_counter += 2;
data_pec = driverSWLTC6804CalcPEC15(BYT_IN_REG, &status_data[current_ic * NUM_RX_BYT]);
if(received_pec != data_pec) {
pec_error = -1; //The pec_error variable is simply set negative if any PEC errors are detected in the serial data
}
}
// Execute for higher cell count with auxiliary group D OV & UV flags
if(driverSWLTC6804MaxNoOfCellPerModule > 12){
driverSWLTC6804ReadStatusGroups(3,total_ic,status_data);
data_counter = 0;
for (uint8_t current_ic = 0 ; current_ic < total_ic; current_ic++) {
//Extract DATA for under & over voltage flags STBR4, STBR3, & STBR2
registersCombined = (status_data[data_counter+5] << 8) | (status_data[data_counter+4]); // Combine all registers in one variable
registersCombinedTemp = registersCombined & 0x00000555; // Filter out only the undervoltage bits
for(int bitPointer = 0; bitPointer < (driverSWLTC6804ConfigStruct.noOfCells-12); bitPointer++){
statusArray[current_ic].underVoltage |= (((registersCombinedTemp & (1 << bitPointer*2)) ? (1 << bitPointer) : 0) << 12) ; // Shift undervoltage bits closer together and store them in *underVoltageFlags + shift 12 bits
registersCombinedTemp = registersCombined & 0x00000AAA; // Filter out only the overvoltage bits
registersCombinedTemp = registersCombinedTemp >> 1; // Move everything one bit to the right
for(int bitPointer = 0; bitPointer < driverSWLTC6804ConfigStruct.noOfCells-12; bitPointer++)
statusArray[current_ic].overVoltage |= (((registersCombinedTemp & (1 << bitPointer*2)) ? (1 << bitPointer) : 0) << 12); // And do the same for the overvoltage bits
}
}
}
free(status_data);
return pec_error;
}
void driverSWLTC6804ReadStatusGroups(uint8_t reg, uint8_t total_ic, uint8_t *data ) {
const uint8_t REG_LEN = 8; //number of bytes in each ICs register + 2 bytes for the PEC
uint8_t cmd[4];
uint16_t cmd_pec;
if (reg == 1) { //1: RDSTATA
cmd[1] = 0x10;
cmd[0] = 0x00;
}else if(reg == 2) { //2: RDSTATB
cmd[1] = 0x12;
cmd[0] = 0x00;
}else if(reg == 3) { //Read auxiliary group D LTC6812 & LTC6813 only for AVDR4 & AVDR5 OV & UV flags
cmd[1] = 0x0F;
cmd[0] = 0x00;
}
cmd_pec = driverSWLTC6804CalcPEC15(2, cmd);
cmd[2] = (uint8_t)(cmd_pec >> 8);
cmd[3] = (uint8_t)(cmd_pec);
driverSWLTC6804WriteRead(cmd,4,data,(REG_LEN*total_ic));
}
bool driverSWLTC6804ReadAuxVoltagesArray(float auxVoltagesArray[][12],uint32_t ntcNominal,uint32_t ntcSeriesResistance, uint16_t ntcBetaFactor,float ntcNominalTemp) {
bool dataValid = true;
uint16_t auxVoltageArrayCodes[driverSWLTC6804TotalNumberOfICs][driverSWLTC6804MaxNoOfTempSensorPerModule];
driverSWLTC6804ReadAuxVoltageRegisters(AUX_CH_ALL,driverSWLTC6804TotalNumberOfICs,auxVoltageArrayCodes);
for(uint8_t modulePointer = 0; modulePointer < driverSWLTC6804TotalNumberOfICs; modulePointer++) {
for(uint8_t auxPointer = 0; auxPointer < driverSWLTC6804MaxNoOfTempSensorPerModule; auxPointer++){
if(auxVoltageArrayCodes[modulePointer][auxPointer]*0.0001f < 10.0f)
auxVoltagesArray[modulePointer][auxPointer] = driverSWLTC6804ConvertTemperatureExt(auxVoltageArrayCodes[modulePointer][auxPointer], ntcNominal, ntcSeriesResistance, ntcBetaFactor, ntcNominalTemp);
else
dataValid = false;
}
}
return dataValid;
}
int8_t driverSWLTC6804ReadAuxVoltageRegisters(uint8_t reg, uint8_t total_ic, uint16_t aux_codes[][driverSWLTC6804MaxNoOfTempSensorPerModule]) {
const uint8_t NUM_RX_BYT = 8;
const uint8_t BYT_IN_REG = 6;
const uint8_t GPIO_IN_REG = 3;
uint8_t *data;
uint8_t data_counter = 0;
int8_t pec_error = 0;
uint16_t parsed_aux;
uint16_t received_pec;
uint16_t data_pec;
data = (uint8_t *) malloc((NUM_RX_BYT*total_ic)*sizeof(uint8_t));
//1.a
if (reg == 0) {
for(uint8_t gpio_reg = 1; gpio_reg<((driverSWLTC6804MaxNoOfTempSensorPerModule/3)+1); gpio_reg++) { //executes once for each of the LTC6804 aux voltage registers
data_counter = 0;
driverSWLTC6804ReadAuxGroups(gpio_reg, total_ic,data); //Reads the raw auxiliary register data into the data[] array
for (uint8_t current_ic = 0 ; current_ic < total_ic; current_ic++) { // executes for every LTC6804 in the daisy chain current_ic is used as the IC counter
for(uint8_t current_gpio = 0; current_gpio< GPIO_IN_REG; current_gpio++) { // This loop parses the read back data into GPIO voltages, it loops once for each of the 3 gpio voltage codes in the register
parsed_aux = data[data_counter] + (data[data_counter+1]<<8); //Each gpio codes is received as two bytes and is combined to create the parsed gpio voltage code
aux_codes[current_ic][current_gpio +((gpio_reg-1)*GPIO_IN_REG)] = parsed_aux;
data_counter=data_counter+2; //Because gpio voltage codes are two bytes the data counter must increment by two for each parsed gpio voltage code
}
received_pec = (data[data_counter]<<8)+ data[data_counter+1]; //The received PEC for the current_ic is transmitted as the 7th and 8th after the 6 gpio voltage data bytes
data_pec = driverSWLTC6804CalcPEC15(BYT_IN_REG, &data[current_ic*NUM_RX_BYT]);
if(received_pec != data_pec) {
pec_error = -1; //The pec_error variable is simply set negative if any PEC errors are detected in the received serial data
}
data_counter=data_counter+2; //Because the transmitted PEC code is 2 bytes long the data_counter must be incremented by 2 bytes to point to the next ICs gpio voltage data
}
}
}else{
driverSWLTC6804ReadAuxGroups(reg, total_ic, data);
for (int current_ic = 0 ; current_ic < total_ic; current_ic++) { // executes for every LTC6804 in the daisy chain current_ic is used as an IC counter
for(int current_gpio = 0; current_gpio<GPIO_IN_REG; current_gpio++) { // This loop parses the read back data. Loops once for each aux voltage in the register
parsed_aux = (data[data_counter] + (data[data_counter+1]<<8)); //Each gpio codes is received as two bytes and is combined to create the parsed gpio voltage code
aux_codes[current_ic][current_gpio +((reg-1)*GPIO_IN_REG)] = parsed_aux;
data_counter=data_counter+2; //Because gpio voltage codes are two bytes the data counter must increment by two for each parsed gpio voltage code
}
received_pec = (data[data_counter]<<8) + data[data_counter+1]; //The received PEC for the current_ic is transmitted as the 7th and 8th after the 6 gpio voltage data bytes
data_pec = driverSWLTC6804CalcPEC15(BYT_IN_REG, &data[current_ic*NUM_RX_BYT]);
if(received_pec != data_pec) {
pec_error = -1; //The pec_error variable is simply set negative if any PEC errors are detected in the received serial data
}
data_counter=data_counter+2; //Because the transmitted PEC code is 2 bytes long the data_counter must be incremented by 2 bytes to point to the next ICs gpio voltage data
}
}
free(data);
return (pec_error);
}
void driverSWLTC6804ReadAuxGroups(uint8_t reg, uint8_t total_ic, uint8_t *data) {
const uint8_t REG_LEN = 8; // number of bytes in the register + 2 bytes for the PEC
uint8_t cmd[4];
uint16_t cmd_pec;
//1
if (reg == 1) { //Read back auxiliary group A
cmd[1] = 0x0C;
cmd[0] = 0x00;
}else if(reg == 2) { //Read back auxiliary group B
cmd[1] = 0x0E;
cmd[0] = 0x00;
}else if(reg == 3) { //Read auxiliary group C LTC6812 & LTC6813 only
cmd[1] = 0x0D;
cmd[0] = 0x00;
}else if(reg == 4) { //Read auxiliary group D LTC6812 & LTC6813 only
cmd[1] = 0x0F;
cmd[0] = 0x00;
}else{ //Read back auxiliary group A
cmd[1] = 0x0C;
cmd[0] = 0x00;
}
cmd_pec = driverSWLTC6804CalcPEC15(2, cmd);
cmd[2] = (uint8_t)(cmd_pec >> 8);
cmd[3] = (uint8_t)(cmd_pec);
driverSWLTC6804WakeIC(); //This will guarantee that the LTC6804 isoSPI port is awake, this command can be removed.
driverSWLTC6804WriteRead(cmd,4,data,(REG_LEN*total_ic));
}
__IO double VuV_dbl;
void driverSWLTC6804WriteConfigRegister(uint8_t totalNumberOfLTCs, uint32_t *balanceEnableMaskArray, bool useArray) {
const uint8_t BYTES_IN_REG = 6;
const uint8_t CMD_LEN = 4+(8*totalNumberOfLTCs);
uint8_t *cmd;
uint16_t cfg_pec;
uint8_t cmd_index; //command counter
uint8_t tx_cfg[totalNumberOfLTCs][6];
//VuV_buf = driverSWLTC6804ConfigStruct.CellUnderVoltageLimit;
//VuV_buf = 2.29999995 * 625;
//VuV_buf = ggg * VuV_buf_del;
//driverSWLTC6804ConfigStruct.CellUnderVoltageLimit
VuV_dbl = driverSWLTC6804ConfigStruct.CellUnderVoltageLimit/(16*0.0001);
//uint16_t VuV = driverSWLTC6804ConfigStruct.CellUnderVoltageLimit/(16*0.0001);
uint16_t VuV = VuV_dbl;
//VuV = 2;
uint16_t VoV = driverSWLTC6804ConfigStruct.CellOverVoltageLimit/(16*0.0001);
//uint16_t VoV = 4.19 * 625;
uint32_t activeBalanceMask=0;
for(int i = 0; i<totalNumberOfLTCs;i++) {
if(useArray)
activeBalanceMask = balanceEnableMaskArray[i];
else
activeBalanceMask = driverSWLTC6804ConfigStruct.DisChargeEnableMask;
tx_cfg[i][0] = (driverSWLTC6804ConfigStruct.GPIO5 << 7) | (driverSWLTC6804ConfigStruct.GPIO4 << 6) | (driverSWLTC6804ConfigStruct.GPIO3 << 5) | (driverSWLTC6804ConfigStruct.GPIO2 << 4) | (driverSWLTC6804ConfigStruct.GPIO1 << 3) | (driverSWLTC6804ConfigStruct.ReferenceON << 2) | (driverSWLTC6804ConfigStruct.ADCOption);
tx_cfg[i][1] = (VuV & 0xFF) ;
tx_cfg[i][2] = ((VoV & 0x0F) << 4) | (VuV >> 8) ;
tx_cfg[i][3] = (VoV >> 4) ;
tx_cfg[i][4] = (activeBalanceMask & 0xFF) ;
tx_cfg[i][5] = ((driverSWLTC6804ConfigStruct.DischargeTimout & 0x0F) << 4) | (activeBalanceMask >> 8) ;
}
cmd = (uint8_t *)malloc(CMD_LEN*sizeof(uint8_t));
cmd[0] = 0x00; // config register command
cmd[1] = 0x01; // config register command
cmd[2] = 0x3d; // PEC
cmd[3] = 0x6e; // PEC
cmd_index = 4;
for (uint8_t current_ic = totalNumberOfLTCs; current_ic > 0; current_ic--) { // executes for each LTC6804 in daisy chain, this loops starts with the last IC on the stack. The first configuration written is received by the last IC in the daisy chain
for (uint8_t current_byte = 0; current_byte < BYTES_IN_REG; current_byte++) { // executes for each of the 6 bytes in the CFGR register current_byte is the byte counter
cmd[cmd_index] = tx_cfg[current_ic-1][current_byte]; //adding the config data to the array to be sent
cmd_index = cmd_index + 1;
}
cfg_pec = (uint16_t)driverSWLTC6804CalcPEC15(BYTES_IN_REG, &tx_cfg[current_ic-1][0]); // calculating the PEC for each ICs configuration register data
cmd[cmd_index] = (uint8_t)(cfg_pec >> 8);
cmd[cmd_index + 1] = (uint8_t)cfg_pec;
cmd_index = cmd_index + 2;
}
driverSWLTC6804WakeIC();
driverSWLTC6804Write(cmd,CMD_LEN);
free(cmd);
}
void driverSWLTC6804WriteConfigRegisterB(uint8_t totalNumberOfLTCs, uint32_t *balanceEnableMaskArray, bool useArray) {
const uint8_t BYTES_IN_REG = 6;
const uint8_t CMD_LEN = 4+(8*totalNumberOfLTCs);
uint8_t *cmd;
uint16_t cfg_pec;
uint8_t cmd_index; //command counter
uint8_t tx_cfg[totalNumberOfLTCs][6];
uint32_t activeBalanceMask=0;
uint16_t cmd_pec;
for(int i = 0; i<totalNumberOfLTCs;i++) {
if(useArray)
activeBalanceMask = balanceEnableMaskArray[i];
else
activeBalanceMask = driverSWLTC6804ConfigStruct.DisChargeEnableMask;
tx_cfg[i][0] = ((activeBalanceMask >> 8 ) & 0x000000F0) | (driverSWLTC6804ConfigStruct.GPIO9 << 3) | (driverSWLTC6804ConfigStruct.GPIO8 << 2) | (driverSWLTC6804ConfigStruct.GPIO7 << 1) | (driverSWLTC6804ConfigStruct.GPIO6) ;
tx_cfg[i][1] = ((activeBalanceMask >> 16 ) & 0x00000003) ;
tx_cfg[i][2] = 0;
tx_cfg[i][3] = 0;
tx_cfg[i][4] = 0;
tx_cfg[i][5] = 0;
}
cmd = (uint8_t *)malloc(CMD_LEN*sizeof(uint8_t));
cmd[0] = 0x00; // config register B command
cmd[1] = 0x24; // config register B command
cmd_pec = driverSWLTC6804CalcPEC15(2, cmd);
cmd[2] = (uint8_t)(cmd_pec >> 8);
cmd[3] = (uint8_t)(cmd_pec);
cmd_index = 4;
for (uint8_t current_ic = totalNumberOfLTCs; current_ic > 0; current_ic--) { // executes for each LTC6804 in daisy chain, this loops starts with the last IC on the stack. The first configuration written is received by the last IC in the daisy chain
for (uint8_t current_byte = 0; current_byte < BYTES_IN_REG; current_byte++) { // executes for each of the 6 bytes in the CFGR register current_byte is the byte counter
cmd[cmd_index] = tx_cfg[current_ic-1][current_byte]; //adding the config data to the array to be sent
cmd_index = cmd_index + 1;
}
cfg_pec = (uint16_t)driverSWLTC6804CalcPEC15(BYTES_IN_REG, &tx_cfg[current_ic-1][0]); // calculating the PEC for each ICs configuration register data
cmd[cmd_index] = (uint8_t)(cfg_pec >> 8);
cmd[cmd_index + 1] = (uint8_t)cfg_pec;
cmd_index = cmd_index + 2;
}
driverSWLTC6804WakeIC();
driverSWLTC6804Write(cmd,CMD_LEN);
free(cmd);
}
void driverSWLTC6804EnableBalanceResistors(uint32_t enableMask,uint8_t cellMonitorType) {
driverSWLTC6804ConfigStruct.DisChargeEnableMask = enableMask;
driverSWLTC6804WriteConfigRegister(driverSWLTC6804TotalNumberOfICs,0,false);
if(cellMonitorType==CELL_MON_LTC6812_1 || cellMonitorType == CELL_MON_LTC6813_1){
driverSWLTC6804WriteConfigRegisterB(driverSWLTC6804TotalNumberOfICs,0,false);
}
}
void driverSWLTC6804EnableBalanceResistorsArray(uint32_t *enableMask, uint8_t cellMonitorType) {
driverSWLTC6804WriteConfigRegister(driverSWLTC6804TotalNumberOfICs,enableMask,true);
if(cellMonitorType==CELL_MON_LTC6812_1 || cellMonitorType == CELL_MON_LTC6813_1){
driverSWLTC6804WriteConfigRegisterB(driverSWLTC6804TotalNumberOfICs,enableMask,true);
}
}
uint16_t driverSWLTC6804CalcPEC15(uint8_t len, uint8_t *data) {
uint16_t remainder,addr;
remainder = 16;//initialize the PEC
for(uint8_t i = 0; i<len;i++) // loops for each byte in data array
{
addr = ((remainder>>7)^data[i])&0xff;//calculate PEC table address
remainder = (remainder<<8)^crc15Table[addr];
}
return(remainder*2);//The CRC15 has a 0 in the LSB so the remainder must be multiplied by 2
}
int8_t driverSWLTC6804ReadConfigRegister(uint8_t total_ic, uint8_t r_config[][8]) {
const uint8_t BYTES_IN_REG = 8;
uint8_t cmd[4];
uint8_t *rx_data;
int8_t pec_error = 0;
uint16_t data_pec;
uint16_t received_pec;
rx_data = (uint8_t *) malloc((8*total_ic)*sizeof(uint8_t));
cmd[0] = 0x00;
cmd[1] = 0x02;
cmd[2] = 0x2b;
cmd[3] = 0x0A;
driverSWLTC6804WriteRead(cmd, 4, rx_data, (BYTES_IN_REG*total_ic));
for (uint8_t current_ic = 0; current_ic < total_ic; current_ic++) { //executes for each LTC6804 in the daisy chain and packs the data into the r_config array as well as check the received Config data for any bit errors
for (uint8_t current_byte = 0; current_byte < BYTES_IN_REG; current_byte++) {
r_config[current_ic][current_byte] = rx_data[current_byte + (current_ic*BYTES_IN_REG)];
}
received_pec = (r_config[current_ic][6]<<8) + r_config[current_ic][7];
data_pec = driverSWLTC6804CalcPEC15(6, &r_config[current_ic][0]);
if(received_pec != data_pec) {
pec_error = -1;
}
}
free(rx_data);
return(pec_error);
}
// Coupling of drivers
void driverSWLTC6804Write(uint8_t *writeBytes, uint8_t writeLength) {
driverHWSPI1Write(writeBytes,writeLength,LTC6804_CS_GPIO_Port,LTC6804_CS_Pin);
};
// Coupling of drivers
void driverSWLTC6804WriteRead(uint8_t *writeBytes, uint8_t writeLength, uint8_t *readBytes, uint8_t readLength) {
uint8_t buf[100];
uint8_t buf_out[100];
memset(buf_out,0x00,99);
memcpy(buf_out, writeBytes, writeLength);
memset(buf,0x00,99);
driverHWSPI1WriteRead(writeBytes,writeLength,readBytes,readLength,LTC6804_CS_GPIO_Port,LTC6804_CS_Pin);
// driverHWSPI1WriteRead(writeBytes,writeLength,buf,readLength,LTC6804_CS_GPIO_Port,LTC6804_CS_Pin);
};
void driverSWLTC6804WakeIC(void){
driverSWLTC6804DelayMS(1);
HAL_GPIO_WritePin(LTC6804_CS_GPIO_Port,LTC6804_CS_Pin,GPIO_PIN_RESET);
driverSWLTC6804DelayMS(1);
HAL_GPIO_WritePin(LTC6804_CS_GPIO_Port,LTC6804_CS_Pin,GPIO_PIN_SET);
driverSWLTC6804DelayMS(1);
}
float driverSWLTC6804ConvertTemperatureExt(uint16_t inputValue,uint32_t ntcNominal,uint32_t ntcSeriesResistance,uint16_t ntcBetaFactor, float ntcNominalTemp) {
static float scalar;
static float steinhart;
scalar = 30000.0f / (float)inputValue - 1.0f;
scalar = (float)ntcSeriesResistance / scalar;
steinhart = scalar / (float)ntcNominal; // (R/Ro)
steinhart = log(steinhart); // ln(R/Ro)
steinhart /= (float)ntcBetaFactor; // 1/B * ln(R/Ro)
steinhart += 1.0f / ((float)ntcNominalTemp + 273.15f); // + (1/To)
steinhart = 1.0f / steinhart; // Invert
steinhart -= 273.15f; // convert to degree
if(steinhart < -50.0f || (float)inputValue >= 30000.0f)
steinhart = 0.0f;
return steinhart;
}