/************************************************************************** * * 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 Battery.c * * @author (last) Suresh Dharnala * @date (last) 15-May-2026 * * @author (original) Suresh Dharnala * @date (original) 13-May-2026 * ***************************************************************************/ #include "Battery.h" #include "BatteryDriver.h" #include "Messaging.h" #include "PersistentAlarm.h" #include "TestSupport.h" #include "Timers.h" /** * @addtogroup Battery * @{ */ // ********** private definitions ********** #define BATTERY_MONITOR_INTERVAL_MS 247 ///< Battery monitor interval in ms. #define BATTERY_COMM_FAULT_COUNT 5 ///< Battery communication fault persistent count before alarming. #define BATTERY_COMM_FAULT_TIMER ( 10 * SEC_PER_MIN * MS_PER_SECOND ) ///< Battery communication fault persistence timer. #define BATTERY_PACK_ERROR_BITS 0x0F ///< Error codes are in the first byte. #define BATTERY_MIN_CAPACITY_MAH 1950U ///< Minimum battery capacity for starting a treatment. #define BATTERY_CHARGER_ERROR_BITS 0x08 ///< Error codes are in the first byte of third bit. #define BATTERY_CHARGER_FAULTY_ERROR_BITS 0XFF ///< Error codes are in the first and second byte. // ********** private data ********** static const U32 BATT_STATUS_ERROR_PERSIST_MS = ( 5 * MS_PER_SECOND ); ///< Persist time in ms for battery pack status error condition. static U32 lastBatteryMonitorTime; ///< Previous battery monitor time. static OVERRIDE_U32_T batteryRegisterOverrides[ NUM_OF_BATTERY_REGISTERS-1 ]; ///< Battery register data and overrides. static BATTERY_MANAGEMENT_ENUM_T currentBmValue; ///< Index for which battery data to read now. static BATTERY_DATA_PAYLOAD_T batteryData; ///< Record with latest battery data. // ********** private function prototypes ********** static void initBatteryRegisterOverrides( void ); static void setBatteryRegisterData( BATTERY_MANAGEMENT_ENUM_T registerSel, U32 data ); static U32 getBatteryRegisterValue( BATTERY_MANAGEMENT_ENUM_T registerSel ); static void getBatteryManagementData( void ); static void publishBatteryDataPayload( void ); static void checkPersistentAlarmTrigger( ALARM_ID_T alarmID, BATTERY_MANAGEMENT_ENUM_T registerSel, U08 errorBits ); /*********************************************************************//** * @brief * The initBattery function initializes the Battery monitor module. * @details \b Inputs: none * @details \b Outputs: Battery monitor module initialized. * @return none *************************************************************************/ void initBattery( void ) { initSetupI2cDriver(); currentBmValue = BEGINNING_OF_LIST; lastBatteryMonitorTime = 0; initBatteryRegisterOverrides(); memset( &batteryData, 0, sizeof( BATTERY_DATA_PAYLOAD_T ) ); // Initialize persistent alarm for battery pack status error initPersistentAlarm( ALARM_ID_TD_BATTERY_PACK_ERROR_DETECTED, 0, BATT_STATUS_ERROR_PERSIST_MS ); // Initialize bad message CRC time windowed count initTimeWindowedCount( TIME_WINDOWED_COUNT_BATT_COMM_ERROR, BATTERY_COMM_FAULT_COUNT, BATTERY_COMM_FAULT_TIMER ); } /*********************************************************************//** * @brief * The initBatteryRegisterOverrides function initializes the battery register * data/override records. * @details \b Inputs: none * @details \b Outputs: batteryRegisterOverrides[] * @return none *************************************************************************/ static void initBatteryRegisterOverrides( void ) { U32 registerIdx; for ( registerIdx = 0; registerIdx < NUM_OF_BATTERY_REGISTERS-1; registerIdx++ ) { batteryRegisterOverrides[ registerIdx ].data = 0U; batteryRegisterOverrides[ registerIdx ].ovData = 0U; batteryRegisterOverrides[ registerIdx ].ovInitData = 0U; batteryRegisterOverrides[ registerIdx ].override = OVERRIDE_RESET; } } /*********************************************************************//** * @brief * The setBatteryRegisterData function updates the raw battery register data. * @details \b Inputs: batteryRegisterOverrides[] * @details \b Outputs: batteryRegisterOverrides[] * @param registerSel battery register to update * @param data value to store for the given register * @return none *************************************************************************/ static void setBatteryRegisterData( BATTERY_MANAGEMENT_ENUM_T registerSel, U32 data ) { if ( registerSel < NUM_OF_BATTERY_REGISTERS-1 ) { batteryRegisterOverrides[ registerSel ].data = data; } } /*********************************************************************//** * @brief * The getBatteryRegisterValue function returns the current battery register * value after applying any active override. * @details \b Inputs: batteryRegisterOverrides[] * @details \b Outputs: none * @param registerSel battery register to read * @return current battery register value *************************************************************************/ static U32 getBatteryRegisterValue( BATTERY_MANAGEMENT_ENUM_T registerSel ) { U32 result = 0U; if ( registerSel < NUM_OF_BATTERY_REGISTERS-1 ) { result = getU32OverrideValue( &batteryRegisterOverrides[ registerSel ] ); } return result; } /*********************************************************************//** * @brief * The checkPersistentAlarmTrigger function checks whether the specified * battery register contains any error bits and triggers a persistent alarm * when the condition remains active for the configured persistence period. * @details \b Inputs: batteryRegisterOverrides, persistent alarm state * @details \b Outputs: Alarm generated if error condition persists * @param alarmID alarm identifier associated with the error condition * @param registerSel battery register to evaluate * @param errorBits bit mask containing the error bits to monitor * @return none *************************************************************************/ static void checkPersistentAlarmTrigger( ALARM_ID_T alarmID, BATTERY_MANAGEMENT_ENUM_T registerSel, U08 errorBits ) { if ( registerSel < NUM_OF_BATTERY_REGISTERS-1 ) { if ( TRUE == isPersistentAlarmTriggered( alarmID, ( getBatteryRegisterValue( registerSel ) & errorBits ) != 0U ) ) { SET_ALARM_WITH_2_U32_DATA( alarmID, getBatteryRegisterValue( registerSel ), errorBits ); } } } /*********************************************************************//** * @brief * The execBatteryMonitor function monitors the battery status by reading * battery data in a round robin fashion and publishing the results. * @details \b Inputs: lastBatteryMonitorTime * @details \b Outputs: lastBatteryMonitorTime * @return none *************************************************************************/ void execBatteryMonitor( void ) { if ( TRUE == didTimeout( lastBatteryMonitorTime, BATTERY_MONITOR_INTERVAL_MS ) ) { lastBatteryMonitorTime = getMSTimerCount(); getBatteryManagementData(); } } /*********************************************************************//** * @brief * The getBatteryManagementData function reads one battery data item per * call in a round-robin fashion and publishes the collected battery data * when the full list has been read. * @details \b Alarm: ALARM_ID_TD_BATTERY_PACK_ERROR_DETECTED if battery * pack status error bits are set. * @details \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if an invalid battery * management data state is detected. * @details \b Inputs: currentBmValue, batteryRegisterOverrides[] * @details \b Outputs: currentBmValue, batteryRegisterOverrides[] * @return none *************************************************************************/ static void getBatteryManagementData( void ) { U32 responseData; // Increment the position in the enum. Starting value is BEGINNING_OF_LIST (0), so on the first // cycle through this function it will be set to the first valid position (1) currentBmValue += 1; switch( currentBmValue ) { case BATTERY_PACK_REMAINING_CAPACITY: responseData = 0U; if ( TRUE == getBatteryData( BATTERY_PACK_ADDRESS, BATTERY_PACK_REMAINING_CAPACITY, &responseData ) ) { setBatteryRegisterData( BATTERY_PACK_REMAINING_CAPACITY, responseData ); } break; case BATTERY_PACK_BATTERY_STATUS: responseData = 0U; if ( TRUE == getBatteryData( BATTERY_PACK_ADDRESS, BATTERY_PACK_BATTERY_STATUS, &responseData ) ) { setBatteryRegisterData( BATTERY_PACK_BATTERY_STATUS, responseData ); checkPersistentAlarmTrigger( ALARM_ID_TD_BATTERY_PACK_ERROR_DETECTED, BATTERY_PACK_BATTERY_STATUS, BATTERY_PACK_ERROR_BITS ); } break; case BATTERY_PACK_RELATIVE_STATE_OF_CHARGE: responseData = 0U; if ( TRUE == getBatteryData( BATTERY_PACK_ADDRESS, BATTERY_PACK_RELATIVE_STATE_OF_CHARGE, &responseData ) ) { setBatteryRegisterData( BATTERY_PACK_RELATIVE_STATE_OF_CHARGE, responseData ); } break; case BATTERY_PACK_FULL_CHARGE_CAPACITY: responseData = 0U; if ( TRUE == getBatteryData( BATTERY_PACK_ADDRESS, BATTERY_PACK_FULL_CHARGE_CAPACITY, &responseData ) ) { setBatteryRegisterData( BATTERY_PACK_FULL_CHARGE_CAPACITY, responseData ); } break; case BATTERY_CHARGER_STATUS: responseData = 0U; if ( TRUE == getBatteryData( BATTERY_CHARGER_ADDRESS, BATTERY_CHARGER_STATUS, &responseData ) ) { setBatteryRegisterData( BATTERY_CHARGER_STATUS, responseData ); checkPersistentAlarmTrigger( ALARM_ID_TD_BATTERY_CHARGER_ERROR_DETECTED, BATTERY_CHARGER_STATUS, BATTERY_CHARGER_ERROR_BITS ); } break; case BATTERY_CHARGER_TS: responseData = 0U; if ( TRUE == getBatteryData( BATTERY_CHARGER_ADDRESS, BATTERY_CHARGER_TS, &responseData ) ) { setBatteryRegisterData( BATTERY_CHARGER_TS, (U32)( ( responseData * 1024.0F ) / 100.0F ) ); } break; case BATTERY_CHARGER_VBAT: responseData = 0U; if ( TRUE == getBatteryData( BATTERY_CHARGER_ADDRESS, BATTERY_CHARGER_VBAT, &responseData ) ) { setBatteryRegisterData( BATTERY_CHARGER_VBAT, (U32)( responseData * 2U ) ); } break; case BATTERY_CHARGER_VSYS: responseData = 0U; if ( TRUE == getBatteryData( BATTERY_CHARGER_ADDRESS, BATTERY_CHARGER_VSYS, &responseData ) ) { setBatteryRegisterData( BATTERY_CHARGER_VSYS, (U32)( responseData * 2U ) ); } break; case BATTERY_CHARGER_FAULT: responseData = 0U; if ( TRUE == getBatteryData( BATTERY_CHARGER_ADDRESS, BATTERY_CHARGER_FAULT, &responseData ) ) { setBatteryRegisterData( BATTERY_CHARGER_FAULT, responseData ); checkPersistentAlarmTrigger( ALARM_ID_TD_BATTERY_CHARGER_ERROR_DETECTED, BATTERY_CHARGER_FAULT, BATTERY_CHARGER_FAULTY_ERROR_BITS ); } break; case BATTERY_CHARGER_CURRENT: responseData = 0U; if ( TRUE == getBatteryData( BATTERY_CHARGER_ADDRESS, BATTERY_CHARGER_CURRENT, &responseData ) ) { setBatteryRegisterData( BATTERY_CHARGER_CURRENT, (U32)( responseData * 2U ) ); } break; case BATTERY_END_OF_LIST_TIME_TO_PUBLISH: currentBmValue = BEGINNING_OF_LIST; publishBatteryDataPayload(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_BATTERY_MANAGEMENT_DATA_STATE, currentBmValue ); break; } } /*********************************************************************//** * @brief * The isBatteryCharged function checks if the battery has sufficient * charge to allow starting a treatment. * @details \b Inputs: batteryRegisterOverrides[] * @details \b Outputs: none * @return TRUE if battery has sufficient charge, otherwise FALSE *************************************************************************/ BOOL isBatteryCharged( void ) { return ( getBatteryRegisterValue( BATTERY_PACK_REMAINING_CAPACITY ) > BATTERY_MIN_CAPACITY_MAH ? TRUE : FALSE ); } /*********************************************************************//** * @brief * The publishBatteryDataPayload function updates and publishes the battery * data payload over CAN. * @details \b Message \b Sent: MSG_ID_TD_BATTERY_DATA * @details \b Inputs: batteryRegisterOverrides[], batteryData * @details \b Outputs: batteryData * @return none *************************************************************************/ static void publishBatteryDataPayload( void ) { batteryData.remainingCapacity = getBatteryRegisterValue( BATTERY_PACK_REMAINING_CAPACITY ); batteryData.batteryStatus = getBatteryRegisterValue( BATTERY_PACK_BATTERY_STATUS ); batteryData.batteryChargerStatus = getBatteryRegisterValue( BATTERY_CHARGER_STATUS ); batteryData.batteryI2cStatus = getI2cStatusRegister( FALSE ); batteryData.faultStatus = getBatteryRegisterValue( BATTERY_CHARGER_FAULT ); batteryData.ts = getBatteryRegisterValue( BATTERY_CHARGER_TS ); batteryData.vbat = getBatteryRegisterValue( BATTERY_CHARGER_VBAT ); batteryData.vsys = getBatteryRegisterValue( BATTERY_CHARGER_VSYS ); batteryData.ibat = getBatteryRegisterValue( BATTERY_CHARGER_CURRENT ); batteryData.relativeStateOfCharge = getBatteryRegisterValue( BATTERY_PACK_RELATIVE_STATE_OF_CHARGE ); batteryData.fullChargeCapacity = getBatteryRegisterValue( BATTERY_PACK_FULL_CHARGE_CAPACITY ); broadcastData( MSG_ID_TD_BATTERY_DATA, COMM_BUFFER_OUT_CAN_TD_BROADCAST, (U08*)&batteryData, sizeof( BATTERY_DATA_PAYLOAD_T ) ); } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testBatteryRegisterOverride function overrides one battery register * value based on the supplied array index. * @details \b Inputs: none * @details \b Outputs: batteryRegisterOverrides[] * @param message override message from Diamond which includes the register * index and value to override. * @return TRUE if override request is successful, FALSE if not *************************************************************************/ BOOL testBatteryRegisterOverride( MESSAGE_T *message ) { return u32ArrayOverride( message, batteryRegisterOverrides, BATTERY_CHARGER_CURRENT, OFF, HEX_32_BIT_FULL_SCALE ); } /**@}*/