/************************************************************************** * * 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 #include // For memcpy #include "i2c.h" #include "Timers.h" /** * @addtogroup Battery * @{ */ // ********** private definitions ********** #define BATTERY_COMM_TIME_OUT_MS 2 ///< Battery communication time out in ms. // ********** private data ********** static OVERRIDE_U32_T batteryI2CStatusRegister = { 0, 0, 0, 0 }; ///< 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. * @details Inputs: none * @details Outputs: I2C driver initialized. * @return none *************************************************************************/ void initsetupI2CDriver(void) { setupI2CDriver(); } /*********************************************************************//** * @brief * The setupI2CDriver function setups i2c driver in repeat mode to be * compatible with SMBus protocol. * @details Inputs: none * @details Outputs: setup i2c driver 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 i2c * driver with a timeout. * @details Inputs: none * @details Outputs: checked i2c transmit ready status * @return TRUE if i2c driver ready to transmit data, 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 i2c * driver with a timeout. * @details Inputs: none * @details Outputs: checked i2c receive ready status * @return TRUE if i2c driver ready to receive data, 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 Inputs: none * @details Outputs: checked i2c registers access ready status * @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 a i2c stop condition and * waits until the stop condition is detected or timed out. * @details Inputs: none * @details Outputs: generated i2c stop condition * @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 ( 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 slave devices ack. * @details Inputs: i2cREG1 * @details Outputs: starts i2c comm in master transmit mode * @param slaveAddr slave device address * @return TRUE if start communication successful, 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 send command to battery interface to get data. * @details Inputs: none * @details Outputs: get the battery data based on given command * @param command command to send to the slave device * @param dataPtr data pointer to store command response data * @return TRUE if received battery data, 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 I2C status register * @details Inputs: batteryI2CStatusRegister, i2cREG1->STR * @details Outputs: none * @param reset override on get * @return I2C Interrupt Status. *************************************************************************/ 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 comm faults * within a set period of time. Assumed function is being called when a new * comm fault is detected so a new comm fault will be added to the list. * @details Inputs: none * @details Outputs: alarm 98 on trigger. * @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 ); } } /**@}*/