/************************************************************************** * * Copyright (c) 2019-2021 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 Battery.c * * @author (last) Quang Nguyen * @date (last) 19-Aug-2021 * * @author (original) Quang Nguyen * @date (original) 24-Feb-2021 * ***************************************************************************/ #include "i2c.h" #include "Battery.h" #include "SystemCommMessages.h" #include "Timers.h" /** * @addtogroup Battery * @{ */ // ********** private definitions ********** #define BATTERY_CHARGER_SLAVE_ADDRESS 0x09 ///< Battery charger controller device address. #define BATTERY_PACK_SLAVE_ADDRESS 0x0B ///< Battery pack device address. #define BATTERY_CHARGER_STATUS_CMD 0x13 ///< Command to get battery charger status. #define BATTERY_CHARGER_STATUS_AC_PRESENT_MASK 0x8000 ///< Battery charger status AC present bit mask. #define BATTERY_PACK_REL_STATE_OF_CHARGE_CMD 0x0D ///< Command to get battery pack relative state of charge. #define BATTERY_PACK_STATUS_CMD 0x16 ///< Command to get battery pack status. #define BATTERY_PACK_ERROR_BITS 0x0F ///< Error codes are in the first byte. #define BATTERY_PACK_MIN_CHARGE_PCT 50 ///< Minimum battery pack state of charge in percentage. #define BATTERY_COMM_TIME_OUT_MS 1 ///< Battery communication time out in ms. #define BATTERY_MONITOR_INTERVAL_MS 750 ///< Battery monitor interval in ms. #define AC_POWER_LOST_PERSISTENT_COUNT 3 ///< AC power lost persistent count before alarming. #define BATTERY_COMM_FAULT_PERSISTENTCE_COUNT 5 ///< Battery communication fault persistent count before alarming. // ********** private data ********** static U16 batteryStatus = 0; ///< Battery current status. static OVERRIDE_U32_T batteryRelStateOfCharge_pct = { 0, 0, 0, 0 }; ///< Battery pack relative state of charge. static U16 batteryPackStatus = 0; ///< Battery pack current status. static U32 lastBatteryMonitorTime = 0; ///< Previous battery monitor time. static U32 lostACPowerPersistentCount = 0; ///< Persistent count for AC power lost alarm. static BOOL hasBatteryChargerStatus = FALSE; ///< Flag indicates if battery charger status has been obtained. static U32 commFaultPersistentCount = 0; ///< Persistence count for battery comm fault. // ********** private function prototypes ********** static U32 getBatteryRemainingPercent( void ); static void setupI2CDriver( void ); static BOOL waitForTxReady( void ); static BOOL waitForRxReady( void ); static BOOL waitForAccessReady( void ); static void generateStopCondition( void ); static BOOL getBatteryData( U32 slaveAddr, U08 command, U16 * dataPtr ); static BOOL startCommTx( U32 slaveAddr ); static BOOL getData( U08 command, U16 * dataPtr ); /*********************************************************************//** * @brief * The initBattery function initializes the Battery module. * @details Inputs: none * @details Outputs: Battery module is initialized. * @return none *************************************************************************/ void initBattery( void ) { batteryStatus = 0; batteryRelStateOfCharge_pct.data = 0; batteryPackStatus = 0; lastBatteryMonitorTime = 0; lostACPowerPersistentCount = 0; hasBatteryChargerStatus = FALSE; commFaultPersistentCount = 0; setupI2CDriver(); } /*********************************************************************//** * @brief * The execBatteryMonitor function monitors the battery status. * @details Inputs: lastBatteryMonitorTime, hasBatteryChargerStatus, batteryStatus, * batteryRelStateOfCharge_pct, batteryPackStatus * @details Outputs: monitor battery status * @return none *************************************************************************/ void execBatteryMonitor( void ) { U16 relStateOfCharge_pct; if ( TRUE == didTimeout( lastBatteryMonitorTime, BATTERY_MONITOR_INTERVAL_MS ) ) { lastBatteryMonitorTime = getMSTimerCount(); if ( FALSE == hasBatteryChargerStatus ) { hasBatteryChargerStatus = TRUE; if ( TRUE == getBatteryData( BATTERY_CHARGER_SLAVE_ADDRESS, BATTERY_CHARGER_STATUS_CMD, &batteryStatus ) ) { if ( 0 == ( batteryStatus & BATTERY_CHARGER_STATUS_AC_PRESENT_MASK ) ) { if ( ++lostACPowerPersistentCount > AC_POWER_LOST_PERSISTENT_COUNT ) { #ifndef DISABLE_BATT_COMM SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_AC_POWER_LOST, batteryStatus ); #endif } } else { clearAlarmCondition( ALARM_ID_HD_AC_POWER_LOST ); lostACPowerPersistentCount = 0; } } } else { hasBatteryChargerStatus = FALSE; if ( TRUE == getBatteryData( BATTERY_PACK_SLAVE_ADDRESS, BATTERY_PACK_REL_STATE_OF_CHARGE_CMD, &relStateOfCharge_pct ) ) { batteryRelStateOfCharge_pct.data = (U32)relStateOfCharge_pct; if ( getBatteryRemainingPercent() < BATTERY_PACK_MIN_CHARGE_PCT ) { SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_BATTERY_PACK_CHARGE_TOO_LOW, getBatteryRemainingPercent() ); } else { clearAlarmCondition( ALARM_ID_HD_BATTERY_PACK_CHARGE_TOO_LOW ); } } if ( TRUE == getBatteryData( BATTERY_PACK_SLAVE_ADDRESS, BATTERY_PACK_STATUS_CMD, &batteryPackStatus ) ) { if ( 0 != ( batteryPackStatus & BATTERY_PACK_ERROR_BITS ) ) { SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_BATTERY_PACK_ERROR_DETECTED, batteryPackStatus ); } } } } } /*********************************************************************//** * @brief * The isBatteryCharged function checks if the battery is charged. * @details Inputs: batteryRelStateOfCharge_pct * @details Outputs: none * @return TRUE if battery is charged, otherwise FALSE *************************************************************************/ BOOL isBatteryCharged( void ) { return ( getBatteryRemainingPercent() > BATTERY_PACK_MIN_CHARGE_PCT ? TRUE : FALSE ); } /*********************************************************************//** * @brief * The getBatteryRemainingPercent function returns the latest battery relative * state of charge percentage. * @details Inputs: batteryRelStateOfCharge_pct * @details Outputs: none * @return battery relative state of charge percentage *************************************************************************/ static U32 getBatteryRemainingPercent( void ) { U32 result = batteryRelStateOfCharge_pct.data; if ( OVERRIDE_KEY == batteryRelStateOfCharge_pct.override ) { result = batteryRelStateOfCharge_pct.ovData; } return result; } /*********************************************************************//** * @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 *************************************************************************/ static BOOL getBatteryData( U32 slaveAddr, U08 command, U16 * dataPtr ) { BOOL result = FALSE; if ( TRUE == startCommTx( slaveAddr ) ) { if ( TRUE == getData( command, dataPtr ) ) { 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 == ( i2cREG1->STR & ( (U32)I2C_NACK | (U32)I2C_AL ) ) ) { commFaultPersistentCount = 0; result = TRUE; } else { generateStopCondition(); if ( commFaultPersistentCount++ > BATTERY_COMM_FAULT_PERSISTENTCE_COUNT ) { #ifndef DISABLE_BATT_COMM SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_BATTERY_COMM_FAULT, slaveAddr ); #endif } } } } 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; } } } return result; } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testSetBatteryRemainingPercentOverride function overrides the battery * remaining percent value. * @details Inputs: none * @details Outputs: batteryRelStateOfCharge_pct * @param value override battery remaining percent * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetBatteryRemainingPercentOverride( U32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; batteryRelStateOfCharge_pct.ovData = value; batteryRelStateOfCharge_pct.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetSetBatteryRemainingPercentOverride function resets the * override of the battery remaining percent value. * @details Inputs: none * @details Outputs: batteryRelStateOfCharge_pct * @param value override battery remaining percent * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetSetBatteryRemainingPercentOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; batteryRelStateOfCharge_pct.override = OVERRIDE_RESET; batteryRelStateOfCharge_pct.ovData = batteryRelStateOfCharge_pct.ovInitData; } return result; } /**@}*/