/************************************************************************** * * 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) 24-Feb-2021 * * @author (original) Quang Nguyen * @date (original) 24-Feb-2021 * ***************************************************************************/ #include "i2c.h" #include "Battery.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_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 U16 batteryRelStateOfCharge_pct = 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. static SELF_TEST_STATUS_T batteryTestStatus; ///< Current battery SOC test status. // ********** 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 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 = 0; batteryPackStatus = 0; lastBatteryMonitorTime = 0; lostACPowerPersistentCount = 0; hasBatteryChargerStatus = FALSE; commFaultPersistentCount = 0; batteryTestStatus = SELF_TEST_STATUS_IN_PROGRESS; setupI2CDriver(); } /*********************************************************************//** * @brief * The execBatteryMonitor function monitors the battery status. * @details Inputs: batteryMonitorTimerCounter * @details Outputs: monitor battery status * @return none *************************************************************************/ void execBatteryMonitor( void ) { 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 ( ( batteryStatus & BATTERY_CHARGER_STATUS_AC_PRESENT_MASK ) == 0 ) { 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 { lostACPowerPersistentCount = 0; } } } else { hasBatteryChargerStatus = FALSE; if ( TRUE == getBatteryData( BATTERY_PACK_SLAVE_ADDRESS, BATTERY_PACK_REL_STATE_OF_CHARGE_CMD, &batteryRelStateOfCharge_pct ) ) { if ( ( SELF_TEST_STATUS_IN_PROGRESS == batteryTestStatus ) && ( batteryRelStateOfCharge_pct < BATTERY_PACK_MIN_CHARGE_PCT ) ) { SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_BATTERY_PACK_CHARGE_TOO_LOW, batteryRelStateOfCharge_pct ); } } if ( TRUE == getBatteryData( BATTERY_PACK_SLAVE_ADDRESS, BATTERY_PACK_STATUS_CMD, &batteryPackStatus ) ) { if ( 0 != batteryPackStatus ) { SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_BATTERY_PACK_ERROR_DETECTED, batteryPackStatus ); } } } } } /*********************************************************************//** * @brief * The execBatteryTest function executes the battery SOC check. * @details Inputs: battery state of charge * @details Outputs: alerts when battery pack charge is low * @return battery SOC test status *************************************************************************/ SELF_TEST_STATUS_T execBatteryTest( void ) { #ifndef DISABLE_BATT_COMM if ( batteryRelStateOfCharge_pct > BATTERY_PACK_MIN_CHARGE_PCT ) { clearAlarmCondition( ALARM_ID_HD_BATTERY_PACK_CHARGE_TOO_LOW ); batteryTestStatus = SELF_TEST_STATUS_PASSED; } #else batteryTestStatus = SELF_TEST_STATUS_PASSED; #endif return batteryTestStatus; } /*********************************************************************//** * @brief * The getBatteryRemainingPercent function returns the latest battery relative * state of charge percentage. * @details Inputs: none * @details Outputs: none * @return battery relative state of charge percentage *************************************************************************/ U16 getBatteryRemainingPercent( void ) { return batteryRelStateOfCharge_pct; } /*********************************************************************//** * @brief * The setupI2CDriver function setups i2c driver in repeat mode to be * compatiable 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 startComm function starts i2c communication and verifies slave devices ack. * @details Inputs: none * @details Outputs: starts i2c comm in master transmit mode * @param slaveAddr slave device address * @return none *************************************************************************/ 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; } /**@}*/