/* Including necessary module. Cpu.h contains other modules needed for compiling.*/ #include "Cpu.h" #include #include uint8_t RX_data [2] = {0x00, 0x00}; // used in several functions to store data read from BQ769x2 uint8_t RX_32Byte [32] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t FET_Status; // FET Status register contents - Shows states of FETs uint8_t DSG = 0; // discharge FET state uint8_t CHG = 0; // charge FET state uint8_t PCHG = 0; // pre-charge FET state uint8_t PDSG = 0; // pre-discharge FET state uint8_t value_SafetyStatusA; // Safety Status Register A uint8_t value_SafetyStatusB; // Safety Status Register B uint8_t value_SafetyStatusC; // Safety Status Register C uint8_t value_PFStatusA; // Permanent Fail Status Register A uint8_t value_PFStatusB; // Permanent Fail Status Register B uint8_t value_PFStatusC; // Permanent Fail Status Register C uint8_t UV_Fault = 0; // under-voltage fault state uint8_t OV_Fault = 0; // over-voltage fault state uint8_t SCD_Fault = 0; // short-circuit fault state uint8_t OCD_Fault = 0; // over-current fault state uint8_t ProtectionsTriggered = 0; // Set to 1 if any protection triggers uint16_t CellVoltage [16] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; uint16_t Stack_Voltage = 0x00; uint16_t Pack_Voltage = 0x00; uint16_t LD_Voltage = 0x00; uint16_t Current = 0x00; uint16_t TS1_Temp = 0x00; uint16_t TS2_Temp = 0x00; uint16_t TS3_Temp = 0x00; uint16_t HDQT_Temp = 0x00; uint16_t u16_CFETOFFTemperature = 0x00; uint16_t u16_DFETOFFTemperature = 0x00; uint16_t U16_FETstatus = 0x00; uint32_t AccumulatedCharge_Int; // in BQ769x2_READPASSQ func uint32_t AccumulatedCharge_Frac;// in BQ769x2_READPASSQ func uint32_t AccumulatedCharge_Time;// in BQ769x2_READPASSQ func /*FUNCTION********************************************************************** * * Function Name : BCC_WaitMs * Description : Waits for specified amount of milliseconds. * *END**************************************************************************/ void BCC_WaitMs(uint16_t delay) { // uint32_t cycles = (uint32_t) BCC_GET_CYCLES_FOR_MS(1U, BCC_GetSystemClockFreq()); uint32_t cycles = 48*1000; //System clock frequency = 48MHz /* Advance to multiple of 4. */ cycles = (cycles & 0xFFFFFFFCU) | 0x04U; for (; delay > 0U; delay--) { BCC_WAIT_FOR_MUL4_CYCLES(cycles); } } /*FUNCTION********************************************************************** * * Function Name : BCC_WaitUs * Description : Waits for specified amount of microseconds. * *END**************************************************************************/ void BCC_WaitUs(uint16_t delay) { // uint32_t cycles = (uint32_t) BCC_GET_CYCLES_FOR_US(delay, BCC_GetSystemClockFreq()); uint32_t cycles = delay*48; //System clock frequency = 48MHz /* Advance to next multiple of 4. Value 0x04U ensures that the number * is not zero. */ cycles = (cycles & 0xFFFFFFFCU) | 0x04U; BCC_WAIT_FOR_MUL4_CYCLES(cycles); } void Delayus(uint16_t uscnt) { BCC_WaitUs(uscnt); } void Delayms(uint16_t mscnt) { BCC_WaitMs(mscnt); } void CopyArray(uint8_t *source, uint8_t *dest, uint8_t count) { uint8_t copyIndex = 0; for (copyIndex = 0; copyIndex < count; copyIndex++) { dest[copyIndex] = source[copyIndex]; } } unsigned char Checksum(unsigned char *ptr, unsigned char len) // Calculates the checksum when writing to a RAM register. The checksum is the inverse of the sum of the bytes. { unsigned char i; unsigned char checksum = 0; for(i=0; i> 8) & 0xff; LPI2C_DRV_MasterSendData(INST_LPI2C1, ®_addrs, 1, true); LPI2C_DRV_MasterSendData(INST_LPI2C1, reg_data, count, true); #endif } int I2C_ReadReg(uint8_t reg_addr, uint8_t *reg_data, uint8_t count) { unsigned int RX_CRC_Fail = 0; // reset to 0. If in CRC Mode and CRC fails, this will be incremented. uint8_t RX_Buffer [MAX_BUFFER_SIZE] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; // uint8_t reg_addrs = reg_addr; #if CRC_Mode { uint8_t crc_count = 0; uint8_t ReceiveBuffer [10] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; crc_count = count * 2; unsigned int j; unsigned int i; unsigned char CRCc = 0; uint8_t temp_crc_buffer [3]; // HAL_I2C_Mem_Read(&hi2c1, DEV_ADDR, reg_addr, 1, ReceiveBuffer, crc_count, 1000); // LPI2C_DRV_MasterSendData(INST_LPI2C1, ®_addr, 1, true); // LPI2C_DRV_MasterReceiveData(INST_LPI2C1, ReceiveBuffer, crc_count, true); LPI2C_DRV_MasterSendDataBlocking(INST_LPI2C1, ®_addr, 1, true, 1000); LPI2C_DRV_MasterReceiveDataBlocking(INST_LPI2C1, ReceiveBuffer, crc_count, true, 1000); uint8_t crc1stByteBuffer [4] = {0x10, reg_addr, 0x11, ReceiveBuffer[0]}; CRCc = CRC8(crc1stByteBuffer,4); if (CRCc != ReceiveBuffer[1]) { RX_CRC_Fail += 1; } RX_Buffer[0] = ReceiveBuffer[0]; j = 2; for (i=1; i> 8) & 0xff; LPI2C_DRV_MasterSendData(INST_LPI2C1, ®_addrs, 1, true); LPI2C_DRV_MasterReceiveData(INST_LPI2C1, reg_data, count, true); #endif return 0; } void BQ769x2_SetRegister(uint16_t reg_addr, uint32_t reg_data, uint8_t datalen) { uint8_t TX_Buffer[2] = {0x00, 0x00}; uint8_t TX_RegData[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; //TX_RegData in little endian format TX_RegData[0] = reg_addr & 0xff; TX_RegData[1] = (reg_addr >> 8) & 0xff; TX_RegData[2] = reg_data & 0xff; //1st byte of data switch(datalen) { case 1: //1 byte datalength I2C_WriteReg(0x3E, TX_RegData, 3); Delayus(2000); TX_Buffer[0] = Checksum(TX_RegData, 3); TX_Buffer[1] = 0x05; //combined length of register address and data I2C_WriteReg(0x60, TX_Buffer, 2); // Write the checksum and length Delayus(2000); break; case 2: //2 byte datalength TX_RegData[3] = (reg_data >> 8) & 0xff; I2C_WriteReg(0x3E, TX_RegData, 4); Delayus(2000); TX_Buffer[0] = Checksum(TX_RegData, 4); TX_Buffer[1] = 0x06; //combined length of register address and data I2C_WriteReg(0x60, TX_Buffer, 2); // Write the checksum and length Delayus(2000); break; case 4: //4 byte datalength, Only used for CCGain and Capacity Gain TX_RegData[3] = (reg_data >> 8) & 0xff; TX_RegData[4] = (reg_data >> 16) & 0xff; TX_RegData[5] = (reg_data >> 24) & 0xff; I2C_WriteReg(0x3E, TX_RegData, 6); Delayus(2000); TX_Buffer[0] = Checksum(TX_RegData, 6); TX_Buffer[1] = 0x08; //combined length of register address and data I2C_WriteReg(0x60, TX_Buffer, 2); // Write the checksum and length Delayus(2000); break; } } void CommandSubcommands(uint16_t command) //For Command only Subcommands // See the TRM or the BQ76952 header file for a full list of Command-only subcommands { //For DEEPSLEEP/SHUTDOWN subcommand you will need to call this function twice consecutively uint8_t TX_Reg[2] = {0x00, 0x00}; //TX_Reg in little endian format TX_Reg[0] = command & 0xff; TX_Reg[1] = (command >> 8) & 0xff; I2C_WriteReg(0x3E,TX_Reg,2); Delayus(2000); } void Subcommands(uint16_t command, uint16_t data, uint8_t type) // See the TRM or the BQ76952 header file for a full list of Subcommands { //security keys and Manu_data writes dont work with this function (reading these commands works) //max readback size is 32 bytes i.e. DASTATUS, CUV/COV snapshot uint8_t TX_Reg[4] = {0x00, 0x00, 0x00, 0x00}; uint8_t TX_Buffer[2] = {0x00, 0x00}; //TX_Reg in little endian format TX_Reg[0] = command & 0xff; TX_Reg[1] = (command >> 8) & 0xff; if (type == R) {//read I2C_WriteReg(0x3E,TX_Reg,2); Delayus(2000); I2C_ReadReg(0x40, RX_32Byte, 4); //RX_32Byte is a global variable } else if (type == W) { //FET_Control, REG12_Control TX_Reg[2] = data & 0xff; I2C_WriteReg(0x3E,TX_Reg,3); Delayus(1000); TX_Buffer[0] = Checksum(TX_Reg, 3); TX_Buffer[1] = 0x05; //combined length of registers address and data I2C_WriteReg(0x60, TX_Buffer, 2); Delayus(1000); } else if (type == W2){ //write data with 2 bytes //CB_Active_Cells, CB_SET_LVL TX_Reg[2] = data & 0xff; TX_Reg[3] = (data >> 8) & 0xff; I2C_WriteReg(0x3E,TX_Reg,4); Delayus(1000); TX_Buffer[0] = Checksum(TX_Reg, 4); TX_Buffer[1] = 0x06; //combined length of registers address and data I2C_WriteReg(0x60, TX_Buffer, 2); Delayus(1000); } } void DirectCommands(uint8_t command, uint16_t data, uint8_t type) // See the TRM or the BQ76952 header file for a full list of Direct Commands { //type: R = read, W = write uint8_t TX_data[2] = {0x00, 0x00}; //little endian format TX_data[0] = data & 0xff; TX_data[1] = (data >> 8) & 0xff; if (type == R) {//Read I2C_ReadReg(command, RX_data, 2); //RX_data is a global variable Delayus(2000); } if (type == W) {//write //Control_status, alarm_status, alarm_enable all 2 bytes long I2C_WriteReg(command,TX_data,2); Delayus(2000); } } uint16_t BQ769x2_ReadVoltage(uint8_t command) // This function can be used to read a specific cell voltage or stack / pack / LD voltage { //RX_data is global var DirectCommands(command, 0x00, R); if(command >= Cell1Voltage && command <= Cell16Voltage) {//Cells 1 through 16 (0x14 to 0x32) return (RX_data[1]*256 + RX_data[0]); //voltage is reported in mV } else {//stack, Pack, LD return 10 * (RX_data[1]*256 + RX_data[0]); //voltage is reported in 0.01V units } } float BQ769x2_ReadTemperature(uint8_t command) { DirectCommands(command, 0x00, R); // This should fill RX_data[0] and RX_data[1] int16_t raw_temp = ((int16_t)RX_data[1] << 8) | RX_data[0]; return raw_temp / 10.0f; // Now gives temperature in °C } void BQ769x2_ReadAllVoltages() // Reads all cell voltages, Stack voltage, PACK pin voltage, and LD pin voltage { int cellvoltageholder = Cell1Voltage; //Cell1Voltage is 0x14 for (int x = 0; x < 16; x++) {//Reads all cell voltages CellVoltage[x] = BQ769x2_ReadVoltage(cellvoltageholder); cellvoltageholder = cellvoltageholder + 2; } Stack_Voltage = BQ769x2_ReadVoltage(StackVoltage); Pack_Voltage = BQ769x2_ReadVoltage(PACKPinVoltage); LD_Voltage = BQ769x2_ReadVoltage(LDPinVoltage); } void BQ769x2_ReadAllTemperature() { TS1_Temp = BQ769x2_ReadTemperature(TS1Temperature); TS2_Temp = BQ769x2_ReadTemperature(TS2Temperature); TS3_Temp = BQ769x2_ReadTemperature(TS3Temperature); u16_CFETOFFTemperature = BQ769x2_ReadTemperature(HDQTemperature); u16_DFETOFFTemperature = BQ769x2_ReadTemperature(DFETOFFTemperature); } void BQ769x2_Init() { // Configures all parameters in device RAM // Enter CONFIGUPDATE mode (Subcommand 0x0090) - It is required to be in CONFIG_UPDATE mode to program the device RAM settings // See TRM for full description of CONFIG_UPDATE mode CommandSubcommands(SET_CFGUPDATE); // After entering CONFIG_UPDATE mode, RAM registers can be programmed. When programming RAM, checksum and length must also be // programmed for the change to take effect. All of the RAM registers are described in detail in the BQ769x2 TRM. // An easier way to find the descriptions is in the BQStudio Data Memory screen. When you move the mouse over the register name, // a full description of the register and the bits will pop up on the screen. // 'Power Config' - 0x9234 = 0x2D80 // Setting the DSLP_LDO bit allows the LDOs to remain active when the device goes into Deep Sleep mode // Set wake speed bits to 00 for best performance BQ769x2_SetRegister(PowerConfig, 0x2D80, 2); // 'REG0 Config' - set REG0_EN bit to enable pre-regulator BQ769x2_SetRegister(REG0Config, 0x01, 1); // 'REG12 Config' - Enable REG1 with 3.3V output (0x0D for 3.3V, 0x0F for 5V) BQ769x2_SetRegister(REG12Config, 0x0D, 1); // BQ769x2_SetRegister(CFETOFFPinConfig, 0x00, 1); // Set DFETOFF pin to control BOTH CHG and DSG FET - 0x92FB = 0x42 (set to 0x00 to disable) BQ769x2_SetRegister(DFETOFFPinConfig, 0x00, 1); // Set up ALERT Pin - 0x92FC = 0x2A // This configures the ALERT pin to drive high (REG1 voltage) when enabled. // The ALERT pin can be used as an interrupt to the MCU when a protection has triggered or new measurements are available BQ769x2_SetRegister(ALERTPinConfig, 0x2A, 1); // Set TS1 to measure Cell Temperature - 0x92FD = 0x07 BQ769x2_SetRegister(TS1Config, 0x07, 1); // Set TS3 to measure FET Temperature - 0x92FF = 0x0F BQ769x2_SetRegister(TS3Config, 0x0F, 1); // Set HDQ to measure Cell Temperature - 0x9300 = 0x07 BQ769x2_SetRegister(HDQPinConfig, 0x00, 1); // No thermistor installed on EVM HDQ pin, so set to 0x00 // 'VCell Mode' - Enable 16 cells - 0x9304 = 0x0000; Writing 0x0000 sets the default of 16 cells BQ769x2_SetRegister(VCellMode, 0x0000, 2); // Enable protections in 'Enabled Protections A' 0x9261 = 0xBC // Enables SCD (short-circuit), OCD1 (over-current in discharge), OCC (over-current in charge), // COV (over-voltage), CUV (under-voltage) BQ769x2_SetRegister(EnabledProtectionsA, 0xBC, 1); // Enable all protections in 'Enabled Protections B' 0x9262 = 0xF7 // Enables OTF (over-temperature FET), OTINT (internal over-temperature), OTD (over-temperature in discharge), // OTC (over-temperature in charge), UTINT (internal under-temperature), UTD (under-temperature in discharge), UTC (under-temperature in charge) BQ769x2_SetRegister(EnabledProtectionsB, 0xF7, 1); // 'Default Alarm Mask' - 0x..82 Enables the FullScan and ADScan bits, default value = 0xF800 BQ769x2_SetRegister(DefaultAlarmMask, 0xF882, 2); // Set up Cell Balancing Configuration - 0x9335 = 0x03 - Automated balancing while in Relax or Charge modes // Also see "Cell Balancing with BQ769x2 Battery Monitors" document on ti.com BQ769x2_SetRegister(BalancingConfiguration, 0x03, 1); // Set up CUV (under-voltage) Threshold - 0x9275 = 0x31 (2479 mV) // CUV Threshold is this value multiplied by 50.6mV //writing 0x31 decimal 49 threshold voltage = 49 × 50.6 mV = 2479.4 mV BQ769x2_SetRegister(CUVThreshold, 0x31, 1); // Set up COV (over-voltage) Threshold - 0x9278 = 0x55 (4301 mV) // COV Threshold is this value multiplied by 50.6mV BQ769x2_SetRegister(COVThreshold, 0x55, 1); // Set up OCC (over-current in charge) Threshold - 0x9280 = 0x05 (10 mV = 10A across 1mOhm sense resistor) Units in 2mV BQ769x2_SetRegister(OCCThreshold, 0x05, 1); // Set up OCD1 Threshold - 0x9282 = 0x0A (20 mV = 20A across 1mOhm sense resistor) units of 2mV BQ769x2_SetRegister(OCD1Threshold, 0x0A, 1); // Set up SCD Threshold - 0x9286 = 0x05 (100 mV = 100A across 1mOhm sense resistor) 0x05=100mV BQ769x2_SetRegister(SCDThreshold, 0x05, 1); // Set up SCD Delay - 0x9287 = 0x03 (30 us) Enabled with a delay of (value - 1) * 15 µs; min value of 1 BQ769x2_SetRegister(SCDDelay, 0x03, 1); // Set up SCDL Latch Limit to 1 to set SCD recovery only with load removal 0x9295 = 0x01 // If this is not set, then SCD will recover based on time (SCD Recovery Time parameter). BQ769x2_SetRegister(SCDLLatchLimit, 0x01, 1); // Exit CONFIGUPDATE mode - Subcommand 0x0092 CommandSubcommands(EXIT_CFGUPDATE); } /*! \brief The main function for the project. \details The startup initialization sequence is the following: * - startup asm routine * - main() */ int main(void) { /* Write your local variable definition here */ /*** Processor Expert internal initialization. DON'T REMOVE THIS CODE!!! ***/ #ifdef PEX_RTOS_INIT PEX_RTOS_INIT(); /* Initialization of the selected RTOS. Macro is defined by the RTOS component. */ #endif /*** End of Processor Expert internal initialization. ***/ /* Write your code here */ /* For example: for(;;) { } */ CLOCK_SYS_Init(g_clockManConfigsArr, CLOCK_MANAGER_CONFIG_CNT,g_clockManCallbacksArr, CLOCK_MANAGER_CALLBACK_CNT); CLOCK_SYS_UpdateConfiguration(0U,CLOCK_MANAGER_POLICY_FORCIBLE); PINS_DRV_Init(NUM_OF_CONFIGURED_PINS, g_pin_mux_InitConfigArr); LPI2C_DRV_MasterInit(INST_LPI2C1, &lpi2c1_MasterConfig0, &lpi2c1MasterState); CommandSubcommands(BQ769x2_RESET); // Resets the BQ769x2 registers Delayus(60000); BQ769x2_Init(); // Configure all of the BQ769x2 register settings Delayus(10000); CommandSubcommands(FET_ENABLE); // Enable the CHG and DSG FETs Delayus(10000); CommandSubcommands(SLEEP_DISABLE); // Sleep mode is enabled by default. For this example, Sleep is disabled to // demonstrate full-speed measurements in Normal mode. Delayus(60000); Delayus(60000); Delayus(60000); Delayus(60000); //wait to start measurements after FETs close while (1) { //Reads Cell, Stack, Pack, LD Voltages, Pack Current and TS1/TS3 Temperatures in a loop //This basic example polls the Alarm Status register to see if protections have triggered or new measurements are ready //The ALERT pin can also be used as an interrupt to the microcontroller for fastest response time instead of polling //In this example the LED on the microcontroller board will be turned on to indicate a protection has triggered and will //be turned off if the protection condition has cleared. BQ769x2_ReadAllVoltages(); BQ769x2_ReadAllTemperature(); Delayus(20000); // repeat loop every 20 ms } /*** Don't write any code pass this line, or it will be deleted during code generation. ***/ /*** RTOS startup code. Macro PEX_RTOS_START is defined by the RTOS component. DON'T MODIFY THIS CODE!!! ***/ #ifdef PEX_RTOS_START PEX_RTOS_START(); /* Startup of the selected RTOS. Macro is defined by the RTOS component. */ #endif /*** End of RTOS startup code. ***/ /*** Processor Expert end of main routine. DON'T MODIFY THIS CODE!!! ***/ for(;;) { if(exit_code != 0) { break; } } return exit_code; }