/************************************************************************** * * Copyright (c) 2026 Diality Inc. - All Rights Reserved. * * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * @file BatteryDriver.c * * @author (last) Suresh Dharnala * @date (last) 15-May-2026 * * @author (original) Suresh Dharnala * @date (original) 11-May-2026 * ***************************************************************************/ #include // For memcpy #include "BatteryDriver.h" #include "i2c.h" #include "Messaging.h" #include "Timers.h" /** * @addtogroup BatteryDriver * @{ */ // ********** private definitions ********** #define BATTERY_COMM_TIME_OUT_MS 2 ///< Battery communication time out in ms. #define BATTERY_CHARGER_SLAVE_ADDRESS 0x6B ///< Battery charger controller device address. #define BATTERY_PACK_SLAVE_ADDRESS 0x0B ///< Battery pack device address. // ********** private data ********** static OVERRIDE_U32_T batteryI2CStatusRegister; ///< Battery I2C Interrupt Status register // ********** private function prototypes ********** static void setupI2CDriver( void ); static BOOL waitForTxReady( void ); static BOOL waitForRxReady( void ); static BOOL waitForAccessReady( void ); static void generateStopCondition( void ); static BOOL startCommTx( U32 slaveAddr ); static BOOL getData( U08 command, U16 * dataPtr ); static void checkTooManyI2CCommFaults( void ); /*********************************************************************//** * @brief * The initsetupI2CDriver function initializes the I2C driver for battery * communication. * @details \b Inputs: none * @details \b Outputs: I2C driver initialized. * @return none *************************************************************************/ void initsetupI2CDriver(void) { // batteryI2CStatusRegister.data = 0; // batteryI2CStatusRegister.ovData = 0; // batteryI2CStatusRegister.ovInitData = 0; // batteryI2CStatusRegister.override = 0; memset(&batteryI2CStatusRegister, 0, sizeof(OVERRIDE_U32_T)); setupI2CDriver(); } /*********************************************************************//** * @brief * The setupI2CDriver function sets up the I2C driver in repeat mode to be * compatible with SMBus protocol. * @details \b Inputs: none * @details \b Outputs: I2C driver configured in repeat mode. * @return none *************************************************************************/ static void setupI2CDriver( void ) { i2cREG1->MDR = (U32)I2C_RESET_IN; i2cREG1->MDR = (U32)( I2C_MASTER | I2C_TRANSMITTER | I2C_7BIT_AMODE | I2C_REPEATMODE | I2C_8_BIT ); i2cREG1->MDR |= (U32)I2C_RESET_OUT; } /*********************************************************************//** * @brief * The waitForTxReady function checks for transmit ready status from the * I2C driver with a timeout. * @details \b Inputs: none * @details \b Outputs: none * @return TRUE if I2C driver is ready to transmit, otherwise FALSE *************************************************************************/ static BOOL waitForTxReady( void ) { U32 const startTime = getMSTimerCount(); BOOL timeout = FALSE; while ( ( 0 == i2cIsTxReady( i2cREG1 ) ) && ( FALSE == timeout ) ) { timeout = didTimeout( startTime, BATTERY_COMM_TIME_OUT_MS ); } return ( TRUE == timeout ? FALSE : TRUE ); } /*********************************************************************//** * @brief * The waitForRxReady function checks for receive ready status from the * I2C driver with a timeout. * @details \b Inputs: none * @details \b Outputs: none * @return TRUE if I2C driver is ready to receive, otherwise FALSE *************************************************************************/ static BOOL waitForRxReady( void ) { U32 const startTime = getMSTimerCount(); BOOL timeout = FALSE; while ( ( 0 == i2cIsRxReady( i2cREG1 ) ) && ( FALSE == timeout ) ) { timeout = didTimeout( startTime, BATTERY_COMM_TIME_OUT_MS ); } return ( TRUE == timeout ? FALSE : TRUE ); } /*********************************************************************//** * @brief * The waitForAccessReady function checks if I2C registers are ready * to be accessed. * @details \b Inputs: none * @details \b Outputs: none * @return TRUE if I2C driver registers are ready to be accessed, otherwise FALSE *************************************************************************/ static BOOL waitForAccessReady( void ) { U32 const startTime = getMSTimerCount(); BOOL timeout = FALSE; while ( ( 0 == ( i2cREG1->STR & (U32)I2C_ARDY ) ) && ( FALSE == timeout ) ) { timeout = didTimeout( startTime, BATTERY_COMM_TIME_OUT_MS ); } return ( TRUE == timeout ? FALSE : TRUE ); } /*********************************************************************//** * @brief * The generateStopCondition function generates an I2C stop condition and * waits until the stop condition is detected or timed out. * @details \b Inputs: none * @details \b Outputs: I2C stop condition generated. * @return none *************************************************************************/ static void generateStopCondition( void ) { U32 const startTime = getMSTimerCount(); BOOL timeout = FALSE; i2cSetStop( i2cREG1 ); while ( ( 0 == i2cIsStopDetected( i2cREG1 ) ) && ( FALSE == timeout ) ) { timeout = didTimeout( startTime, BATTERY_COMM_TIME_OUT_MS ); } } /*********************************************************************//** * @brief * The getBatteryData function starts i2c communication with battery device * and get data based on given command. * @details Inputs: none * @details Outputs: get data from battery device * @param slaveAddr battery slave device address * @param command command to send to the slave device * @param dataPtr data pointer to store command response data * @return none *************************************************************************/ BOOL getBatteryData( U32 slaveAddr, U08 command, U32 * dataPtr ) { BOOL result = FALSE; if (slaveAddr) { slaveAddr = BATTERY_PACK_SLAVE_ADDRESS; } else { slaveAddr = BATTERY_CHARGER_SLAVE_ADDRESS; } if ( TRUE == startCommTx( slaveAddr ) ) { U16 data = (U16)( (*dataPtr) & MASK_OFF_MSW ); if ( TRUE == getData( command, &data ) ) { *dataPtr = data; result = TRUE; } } return result; } /*********************************************************************//** * @brief * The startCommTx function starts I2C communication and verifies the * slave device acknowledges. * @details \b Alarm: ALARM_ID_TD_BATTERY_COMM_FAULT if too many * communication faults detected. * @details \b Inputs: i2cREG1 * @details \b Outputs: I2C communication started in master transmit mode. * @param slaveAddr slave device address to communicate with. * @return TRUE if communication started successfully, otherwise FALSE *************************************************************************/ static BOOL startCommTx( U32 slaveAddr ) { BOOL result = FALSE; i2cSetSlaveAdd( i2cREG1, slaveAddr ); if ( FALSE == i2cIsBusBusy( i2cREG1 ) ) { i2cSetDirection( i2cREG1, I2C_TRANSMITTER ); i2cSetMode( i2cREG1, I2C_MASTER ); i2cSetStart( i2cREG1 ); if ( TRUE == waitForAccessReady() ) { if ( 0 == ( getI2CStatusRegister( TRUE ) & ( (U32)I2C_NACK | (U32)I2C_AL ) ) ) { result = TRUE; } else { generateStopCondition(); checkTooManyI2CCommFaults(); } } } if ( FALSE == result ) { // Reset i2c bus if cannot communicate with battery slave devices setupI2CDriver(); } return result; } /*********************************************************************//** * @brief * The getData function sends a command to the battery device and retrieves * the response data. * @details \b Alarm: ALARM_ID_TD_BATTERY_COMM_FAULT if too many * communication faults detected. * @details \b Inputs: none * @details \b Outputs: battery data retrieved from device. * @param command command to send to the device. * @param dataPtr pointer to store the command response data. * @return TRUE if data received successfully, otherwise FALSE *************************************************************************/ static BOOL getData( U08 command, U16 * dataPtr ) { BOOL result = FALSE; U16 batteryData = 0; if ( TRUE == waitForTxReady() ) { i2cSendByte( i2cREG1, command ); } // Wait until command has been transmitted before start receiving command response if ( TRUE == waitForTxReady() ) { i2cSetDirection( i2cREG1, I2C_RECEIVER ); i2cSetStart( i2cREG1 ); if ( TRUE == waitForRxReady() ) { // Due to the double buffer, the master must generate the stop condition after the (message size - 1)th data i2cSetStop( i2cREG1 ); batteryData = i2cReceiveByte( i2cREG1); if ( TRUE == waitForRxReady() ) { batteryData = ( batteryData | ( i2cReceiveByte( i2cREG1) << 8 ) ); *dataPtr = batteryData; result = TRUE; } else { checkTooManyI2CCommFaults(); } } } return result; } /*********************************************************************//** * @brief * The getI2CStatusRegister function returns the current I2C status * register value. * @details \b Inputs: batteryI2CStatusRegister, i2cREG1->STR * @details \b Outputs: none * @param resetOverride flag to reset the override after reading. * @return current I2C interrupt status register value. *************************************************************************/ U32 getI2CStatusRegister( BOOL resetOverride ) { U32 result = i2cREG1->STR; if ( OVERRIDE_KEY == batteryI2CStatusRegister.override ) { result = batteryI2CStatusRegister.ovData; if ( TRUE == resetOverride ) { batteryI2CStatusRegister.override = OVERRIDE_RESET; batteryI2CStatusRegister.ovData = batteryI2CStatusRegister.ovInitData; } } return result; } /*********************************************************************//** * @brief * The checkTooManyI2CCommFaults function checks for too many I2C * communication faults within a set period of time. * @details \b Alarm: ALARM_ID_TD_BATTERY_COMM_FAULT if too many * communication faults are detected within the time window. * @details \b Inputs: none * @details \b Outputs: none * @return none *************************************************************************/ static void checkTooManyI2CCommFaults( void ) { if ( TRUE == incTimeWindowedCount( TIME_WINDOWED_COUNT_BATT_COMM_ERROR ) ) { SET_ALARM_WITH_1_U32_DATA( ALARM_ID_TD_BATTERY_COMM_FAULT, TIME_WINDOWED_COUNT_BATT_COMM_ERROR ); } } /*********************************************************************//** * @brief * The testBatteryI2CStatusOverride function overrides the battery I2C * status register value. * @details \b Inputs: none * @details \b Outputs: batteryI2CStatusRegister * @param message override message from Diamond which includes the * I2C status register value to override to. * @return TRUE if override request is successful, FALSE if not *************************************************************************/ BOOL testBatteryI2CStatusOverride( MESSAGE_T *message ) { BOOL result = f32Override( message, (OVERRIDE_F32_T*)&batteryI2CStatusRegister ); return result; } /**@}*/