/********************************************************************//** * @brief Update WDT timeout value and feed * @param[in] TimeOut TimeOut value to be updated * @return None *********************************************************************/ void WDT_UpdateTimeOut ( uint32_t TimeOut) { uint32_t ClkSrc; ClkSrc = LPC_WDT->WDCLKSEL; ClkSrc &=WDT_WDCLKSEL_MASK; WDT_SetTimeOut(ClkSrc,TimeOut); WDT_Feed(); }
/*********************************************************************//** * @brief Start WDT activity with given timeout value * @param[in] TimeOut WDT reset after timeout if it is not feed * @return None **********************************************************************/ void WDT_Start(uint32_t TimeOut) { uint32_t ClkSrc; ClkSrc = LPC_WDT->WDCLKSEL; ClkSrc &=WDT_WDCLKSEL_MASK; WDT_SetTimeOut(ClkSrc,TimeOut); //enable watchdog LPC_WDT->WDMOD |= WDT_WDMOD_WDEN; WDT_Feed(); }
/** * @brief Update WDT timeout value and feed. * * @param TimeOut: TimeOut value to be updated. * @retval None */ void WDT_UpdateTimeOut ( uint32_t TimeOut) { WDT_SetTimeOut(TimeOut); WDT_Feed(); }
/*! \brief The main function. * * This contains initialization and the main loop as described in the * application note. Interrupt service routines (ISR) supply all data to be * processed in the main loop. * */ int main( void ) { /* Initialisation of peripheral modules: - Watchdog (timeout must be more than longer than the longest conversion period used for the CC-ADC) - Turn of unneeded modules (only SPI atm) - Bandgap - Set Current protection levels/configuration - V-ADC - Get cell voltages (requires that interrupts are enabled) - Get temperature - Estimate capacity ("gas gauging") - Check battery voltage and release the device from DUVR mode - CC-ADC - Get initial current reading (momentary and average). - Communication */ // Initialization. wdt_reset(); WDT_SetTimeOut( WDTO_2Kms ); //Set Watchdog timeout to 2 sec #ifdef DEBUG if( FAILURE == Reset_Initialization() ){ // TODO: The Watchdog has triggered several times - might want to shut down the battery?! // POWMAN_Shutdown(); } #endif // Enable power reduction for SPI PRR_DISABLE_SPI(); // Disable digital input for port a DIDR0 = (1<<PA0DID) | (1<<PA1DID); // Enable pull-ups for port b to avoid floating pins PORTB = (1<<PORTB0) | (1<<PORTB1) | (1<<PORTB2) | (1<<PORTB3) | (1<<PORTB4) | (1<<PORTB5) | (1<<PORTB6) | (1<<PORTB7); DDRB = (1<<PORTB0) | (1<<PORTB1) | (1<<PORTB2) | (1<<PORTB3) | (1<<PORTB4); // Check the CRC checksum of the battery parameters struct errorFlags.checksumFailure = (! BATTPARAM_CheckCRC()) | (! CheckSignatureCRC()); // Initialize Bandgap reference. if( FAILURE == BANDGAP_Initialization() ){ errorFlags.criticalConditionDetected = true; } // Initialize the battery protection module (enabled by default - if default settings are good // this is not required). BATTPROT_SetShortCircuitDetection( battParams.shortcircuitCurrent, battParams.shortcircuitReactionTime ); BATTPROT_SetOverCurrentDetection( battParams.overcurrentDischarge, battParams.overcurrentCharge, battParams.overcurrentReactionTime ); BATTPROT_SetHighCurrentDetection( battParams.highcurrentDischarge, battParams.highcurrentCharge, battParams.highcurrentReactionTime ); BATTPROT_DisableDischargeHighCurrentProtection(); // Don't want a high discharging current to disable the FETs (in this example) BATTPROT_EnableAllInterrupts(); //Enable DUVR from the start, so we are always in DUVR mode. FETCTRL_EnableDeepUnderVoltageMode(); // On reset, the device is in DUVR mode depending on the fuse setting DUVRINIT stateFlags.inDUVR = true; // Enable the voltage regulator monitor interrupt VREGMON_InterruptEnable(); // Initialize V-ADC and configure a full scan VADC_InitializeVadcCoefficients(); VADC_ScanConfig( (vadcIndex_t)HIGHEST_CELL, (vadcIndex_t)HIGHEST_VADC, (vadcIndex_t)VTEMP ); // This can only fail if the VADC is set-up with an invalid ScanConfig if( FAILURE == VADC_StartScan() ){ errorFlags.criticalConditionDetected = true; } else { __enable_interrupt(); while( VADC_RUNNING == VADC_ScanState() ); __disable_interrupt(); VADC_ClearReadyFlags(); disableDUVRIfPossible(); VBSoC_Init(); } // Calculate the coefficient numbers for ccadc_ticks to mA and gas_gauging to mAh functions BATTCUR_CalculateShuntCoeffient(battParams.shuntResistance, CCADC_VOLTREF); uint16_t temp = RCCAL_CalculateUlpRCperiod(coreTemperature/10); CCGASG_CalculateShuntCoefficients(temp, battParams.shuntResistance, CCADC_VOLTREF); // Calculate values for the sram battery parameters struct BATTPARAM_InitSramParameters(); // Copy key from eeprom to sram if using AES #if defined(AUTH_USE_AES) AUTH_CopyKeyToSram(); #endif // If a critical condition occured, (either bandgap didn't work or VADC was configured wrong) // don't do any of the battery related initialization and disable DUVR mode (so charging is impossible) if( errorFlags.criticalConditionDetected ) { FETCTRL_DisableDeepUnderVoltageMode(); FETCTRL_DisableFets(); } else { // Initialize the CC-ADC (sample every second and set regular current level) CCADC_Init( ACCT_1024, RCCI_1024, 10 mA ); CCADC_SetMode( CCADC_ACC ); // Set initial remaining capacity in the CCGASG module, it is marked as in- // accurate and updated the first time the VBSoC have an accurate reading. CCGASG_SetStateofCharge(VBSoC_EstimatedSoC()); stateFlags.remainingCapacityInaccurate = 1; __enable_interrupt(); // Needs to run a conversion before the value is correct while( !CCADC_isAccResultReady() ); wdt_reset(); __disable_interrupt(); // Calculate the CCADC Accumulating conversion offset if it is invalid and // DUVR mode is disabled if(CCADC_GetRawAccOffset() == 32767 && ! stateFlags.inDUVR) { FETCTRL_DisableFets(); // Disable FETs when measuring offset as changes in current not is good __enable_interrupt(); CCADC_CalculateAccOffset(); __disable_interrupt(); } // If two or more cells, init the misbalance corrector #if BATTPARAM_CELLS_IN_SERIES >= 2 BATTVMON_InitializeBalancing(); #endif //Store information about the current (to be able to reply if host asks...) BATTCUR_StoreCurrent( CCADC_GetAccResult() ); BATTCUR_InitializeAverageCurrent( BATTCUR_GetOffsetCalibratedCurrent() ); } //init communication bus T1init(); // Used for SmBus timeout and LED Comm_Init(); // Ready to run - enable interrupts and enter main loop. __enable_interrupt(); /******************************************************************* Main loop: If any of the task flags are set the corresponding task should be run. The following things are done: - Reset watchdog - If a new CCADC result is ready, start a VADC conversion - If the communication interface have a new byte, handle it - and if a whole new command has arrived, handle that - Handle new CCADC result if it's ready - Check new core temperature if ready, and run a fast RC calibration if needed - Check new cell temperature if ready - Check new cell voltage if ready - Set different sleep modes depending on what's running - If in communication or RC calibration (any timer running) can only enter idle mode - If VADC is running, can't go deeper than ADC noise reduction mode - If "nothing" is running, enter power-save - Disable/enable fets depeding on various things - Go to sleep */ while(1) { //Tickle the watchdog every cycle wdt_reset(); if( CCADC_isAccResultReady() ) { // Start a new VADC scan as fast as possible so that all scans hopefully are // ready before the main loop ends, to avoid having to cycle again. #if BATTPARAM_CELLS_IN_SERIES >= 2 // Disable cell balancing if more than one cell BATTVMON_DisableCellBalancing(); #endif // Start a new VADC scan configureVADC(); VADC_StartScan(); taskFlags.newCurrentAvailable = true; } //See if there were any received commands. taskFlags.newSbsCommandAvailable = Comm_Handle( &sbs ); //If complete sbs command has been received: process it if( taskFlags.newSbsCommandAvailable ) { handleSBSCommand(); // If authentication is needed, run. It will use and change the values in sbs, // make sure the communication interrupt does not write directly to it. // Note that this can take long time and communication will not work // during that time. if(taskFlags.doAuthentication) { AUTH_Execute(&sbs); taskFlags.doAuthentication = false; } } /* A new CC-ADC conversion is available. Update momentary current, average current, and calculate new battery capacity. Update discharge_cycle_accumulator (and increment cycle_count if required). */ if( taskFlags.newCurrentAvailable ) { taskFlags.newCurrentAvailable = false; // We use the CCADC interrupt as counter for the RTC runEverySecond(); BATTCUR_StoreCurrent( CCADC_GetAccResult() ); handleNewCCADCResult(); CCGASG_AccumulateCCADCMeasurements( BATTCUR_GetCurrent(), slowRcOscillatorPeriod ); //Set ledstatus SoC_State = GASG_StateOfCharge(BATTCUR_GetAverageCurrent(), battParams_sram.capacityInCCAccumulated); SetLEDs((uint8_t)SoC_State); } /* New core temperature reading is available and should be processed. If temperature has changed set RC_calibration_required flag. */ if( VADC_VTempReady() ) { handleNewCoreTemperature(); } /* RC oscillator requires calibration (new CC_ADC conversion period time should be calculated and stored. */ if( taskFlags.rcCalibrationRequired ) { taskFlags.rcCalibrationRequired = false; RCCAL_StartCalibrateFastRC(); // Calculating slow RC period works on whole kelvins slowRcOscillatorPeriod = RCCAL_CalculateSlowRCperiod( coreTemperature/10 ); } if( RCCAL_GetState() == RCCAL_CALIB_INIT || RCCAL_GetState() == RCCAL_CALIB_WORKING ) { RCCAL_CalibrateFastRCruntime(slowRcOscillatorPeriod); } /* Battery cell temperature is available. Check it and disable FETs if too high/low */ if( VADC_CellTempReady() ) { handleNewCellTemperature(); } /* Check battery voltage cell . Check if critical (high/low voltage) */ if( VADC_Cell1VoltageReady() ) { handleNewCellVoltage(CELL1); } /* Check battery voltage cell . Check if critical (high/low voltage) and misbalance */ #if BATTPARAM_CELLS_IN_SERIES >= 2 if( VADC_Cell2VoltageReady() ) { handleNewCellVoltage(CELL2); } #if BATTPARAM_CELLS_IN_SERIES >= 3 if( VADC_Cell3VoltageReady() ) { handleNewCellVoltage(CELL3); } #if BATTPARAM_CELLS_IN_SERIES >= 4 if( VADC_Cell4VoltageReady() ) { handleNewCellVoltage(CELL4); } #endif #endif #endif // Check what sleep mode we can enter. Needs disable interrupt because otherwise it might // overwrite what interrupts write to SMCR (for example, if external interrupt is triggered and // wants the sleep mode to be no higher than idle, it should be run after all those checks) uint8_t interruptState = __save_interrupt(); __disable_interrupt(); if( Comm_IsIdle() && ! RCCAL_IS_RUNNING() ) { if( VADC_RUNNING == VADC_ScanState() ) { SLEEP_SET_ADC_NR_MODE(); } else { SLEEP_SET_POWER_SAVE_MODE(); } } else { SLEEP_SET_IDLE_MODE(); } __restore_interrupt(interruptState); /* Disable FETs if critical condition have been detected. Enable them if that is okey, depending om various factors like temperature and voltage. */ if( errorFlags.criticalConditionDetected || stateFlags.simulatePowerOff) { FETCTRL_DisableFets(); } else if( ! errorFlags.cellTemperatureTooHigh && ! errorFlags.cellTemperatureTooLow && ! stateFlags.inDUVR) { if( ! errorFlags.voltageTooHigh && ! errorFlags.reoccuringChargeProtection && ! stateFlags.chargingProhibited && ! sbsFlags.forceChargeFETDisabled) { FETCTRL_EnableChargeFet(); } if( ! errorFlags.voltageTooLow && ! stateFlags.dischargingProhibited && ! sbsFlags.forceDischargeFETDisabled) { FETCTRL_EnableDischargeFet(); } } // The reason to do this is that on short-circuit, the cell voltage reading can // be wrong and if that causes both chargingProhibited and dischargingProhibited // to get set, they would never be cleared unless we do this. // If they should be set, they will be set again before the FETs are enabled again // on next cycle. if( stateFlags.chargingProhibited && stateFlags.dischargingProhibited ) { stateFlags.chargingProhibited = false; stateFlags.dischargingProhibited = false; } // Enter sleep if no new byte just have been received on the communication // If a byte is received between the check for Comm_Flag and __sleep is // executed, the sleep function will have no effect as the software communication // interrupt clears sleep mode enable. if( (Comm_Flag() == 0 ) && (ShowLedBusy()==false) ){ __sleep(); } } }