stm32L476 uses low power under freeRTOS

Previously, we talked about how to reduce power consumption at the hardware level, how to reduce power consumption by tickless in stm32, etc. (ma level), but the power consumption is not low enough.In the process of using stm32L476 single-chip computer, more low-power modes and clock selection are found.Because the system needs low power consumption, but at the same time the real-time requirement is also high (fast wake-up), when the power consumption is low, the serial communication should be timely, and the timer should work.Based on this requirement, the stop2 mode of the stm32L series was selected.(stop2+LPUART+LPTIM1+RTC)

In stop2 mode, data in sRAM will not be lost, LSE and LSI clocks will not be turned off, supporting multiple wake-up modes. RTC wake-up, LPTIM wake-up and LPUART are currently used here.

In this way, the most important thing is to consider 1. how to configure the clock, 2. how to configure the wake-up source before entering stop2 mode 3. The wake-up clock will resume 4. The most important thing is that when we enter stop2, the main clock will turn off, so the period of time after sleep will be lost. When waking-up, low power consumption will cause task scheduling abnormalities, how to find this backTime is the key to success.

 

Before we do that, we usually look for some reference programs, and st officially provides the process of RTC wake-up and LPUART wake-up stop2 mode

STM32Cube_FW_L4_V1.14.0\Projects\NUCLEO-L476RG\Examples\PWR\PWR_STOP2_RTC

STM32Cube_FW_L4_V1.14.0\Projects\NUCLEO-L476RG\Examples_LL\LPUART\LPUART_WakeUpFromStop2

But they are all without system, but we usually implement a project, using bare machine front and back task operation for multitask processing is more cumbersome, here added FreeRTOS real-time system.

Porting official code into the system, waking up through RTC alone is OK, and waking up through LPUART alone is OK, but when combined, problems arise. Analyse, in the configuration of RTC,

 tick_sleep = ((uint32_t)prvGetExpectedIdleTime());

HAL_RTCEx_DeactivateWakeUpTimer (&RTCHandle); //Configure RTC wake-up

HAL_RTCEx_SetWakeUpTimer_IT(&RTCHandle,tick_sleep*2,RTC_WAKEUPCLOCK_RTCCLK_DIV16)

 

Through the system's own function, the time to the next task, that is, the time to sleep, is dynamically calculated, and then set to RTC, at which time the sleep time is known, sleep does not affect task scheduling.However, when LPUART wake-up is added, scheduling becomes more complex, because you don't know when your serial port wake-up will come, and the clock will be restored when it comes, so the scheduling will be wrong.It is true that there are two tasks defined, a serial port task and a 1s periodic light-on task. After introducing LPUART wake-up, the lamp flickers abnormally, which affects the system scheduling time.

 

I. Clock allocation

Clock allocation is a critical step in low power consumption.How to set it?

 

1. There are several key points in this area. RTC uses LSI 32Khz clock, but LSE can also be used. As long as RTC is well associated with the corresponding clock and RTC is initially initialized with crossover, otherwise wake-up time will be inaccurate. How RTC time is calculated can refer to this blog

https://blog.csdn.net/qingwufeiyang12346/article/details/80686350

 

Note that our LSI is 32K, and he uses a different model inside. The clocks are different.

 

2. The MSI used by our main clock, up to 48M, can also be used with HSI, but HSI can only reach 16M, there is a certain relationship between the reason of using 48M and the wake-up time.Official data say that at 48MHz MSI, it can wake up at 1us.

Be careful not to use the HSE clock (never used).At the same time, the system needs to manually configure the clock LL_RCC_SetClkAfterWakeFromStop(LL_RCC_STOP_WAKEUPCLOCK_MSI) after waking up;

 

3.LPUART uses LSE instead of PCLK because it will be turned off after low power consumption, but note that LSE only supports a maximum baud rate of 9600, and data loss will occur if the baud rate is large (usually the first byte is lost).

4. After waking up, restore the stop2 sleep time, or it will not work properly.

 

 

2. How do I incorporate stop2 wake-up and sleep into FreeRtos?

st generally provides bare-metal operations in the process, so how to join in freertos, has said before, the principle of free Rtos low power consumption is to go to sleep when idle tasks, task scheduling when wake up, idle longer, the lower power consumption is sure.We need to refer to the tickless mechanism provided by freeRtos.

 

1.freeRTos Power Consumption Implementation

Open #define configUSE_TICKLESS_IDLE 1 in FreeRTOSConfig.h

The tickless power consumption mode is now started, and the approximate entity function in tickless is vPortSuppressTicksAndSleep(). The general idea is as follows:

When the processor enters an idle task, turn off the system clock (turn off the tick timer interrupt), when other interrupts occur or other tasks need to be processed, the processor wakes up because the system clock is turned off and this time needs to be supplemented (it is implemented using the tick timer), and the execution time in the low-power mode is obtained through some mechanisms, and the time is supplemented after waking upCompensation, to achieve accurate wake-up.

 

At the same time, for lower power consumption, some interfaces have been made for user supplement.

#define configPRE_SLEEP_PROCESSING(x) RTOS_PreSleepProcessing(x)//First function before entering low-power sleep

#define configPOST_SLEEP_PROCESSING(x) RTOS_PostSleepProcessing(x)//First function after low power wake-up

 

This implements the user's own functions, such as frequency down, interrupt off, peripheral off and so on.

 

2. When we sleep with RTC

vPortSuppressTicksAndSleep is not modified, RTOS_PreSleepProcessing(x) sleep operation (configure RTC wake-up, set wake-up time, enter stop2 mode)

void OS_PreSleepProcessing(uint32_t *ulExpectedIdleTime)
{
       *ulExpectedIdleTime = 0;  //Block the default wfi command execution to prevent system tick wake-up
         

        tick_sleep = ((uint32_t)prvGetExpectedIdleTime()); Get Sleep Time

        if(tick_sleep > 2) //Determine whether to enter low power consumption
        {
            vTaskSuspendAll(); //Suspend Scheduler

            HAL_RTCEx_DeactivateWakeUpTimer(&RTCHandle);//Configure RTC Wake Up

            HAL_RTCEx_SetWakeUpTimer_IT(&RTCHandle, tick_sleep*2,             
            RTC_WAKEUPCLOCK_RTCCLK_DIV16);

            EnterSTOP2Mode(); //Enter low power consumption

        }
}

void EnterSTOP2Mode(void)
{
  LL_PWR_SetPowerMode(LL_PWR_MODE_STOP2);
  LL_LPM_EnableDeepSleep();  
  __WFI();
}

RTOS_PostSleepProcessing(x)Wake up (from new clock configuration)
void OS_PostSleepProcessing(uint32_t *ulExpectedIdleTime)
{
   xTaskResumeAll(); //Restore Scheduler
   SYSCLKConfig_STOP();//Recovery Clock
}

void SYSCLKConfig_STOP(void)
{
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  uint32_t pFLatency = 0;

  /* Enable Power Control clock */
  __HAL_RCC_PWR_CLK_ENABLE();

  /* Get the Oscillators configuration according to the internal RCC registers */

  HAL_RCC_GetOscConfig(&RCC_OscInitStruct);

  /* Enable PLL */

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_NONE;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {

  }

  /* Get the Clocks configuration according to the internal RCC registers */
  HAL_RCC_GetClockConfig(&RCC_ClkInitStruct, &pFLatency);

  /* Select PLL as system clock source and keep HCLK, PCLK1 and PCLK2 clocks dividers as before */

  RCC_ClkInitStruct.ClockType     = RCC_CLOCKTYPE_SYSCLK;
  RCC_ClkInitStruct.SYSCLKSource  = RCC_SYSCLKSOURCE_PLLCLK;
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, pFLatency) != HAL_OK)
  {
 
  }

}

 

The phenomenon is normal, because we use freeRTOS to get sleep time, but we do not know the wake-up time of peripherals such as LPUART, and the tick clock in stop mode is stopped, so it must be a problem to run.

FreeRTOS does also support the refactoring of vPortSuppressTicksAndSleep

#define configUSE_TICKLESS_IDLE 2 allows refactoring.

Then the tick clock uses LPTIM to record the compensation time???vPortSuppressTicksAndSleep under Refactoring??

It should be possible to use LPTIM as the tick clock directly, but don't know how to replace it with the tick clock?

 

3. Conclusion:

1.Stop 2 mode is based on Cortex-M4 deep sleep mode and external clock gating.In top 2 mode, all clocks in the Vcore domain are stopped and PLL, MSI, HSI16 and HSE oscillators are prohibited.Some peripherals with wake-up capabilities (I2C3 and LPUART) can turn on HSI16 to get a frame, or turn off HSI16 after receiving a frame if it is not a wake-up frame.SRAM1, SRAM2, SRAM3, and register contents will be retained, and all I/O pins will be in the same state as in run mode.

2. According to the manual, Stop 2 mode will turn off the system clock, and the current OS Tick is based on the kernel of the Systeick timer.OS Tick will also stop after the system clock stops. For some OS Tick-dependent applications, problems will occur after entering Stop 2 mode and waking up after interruption, so OS Tick needs to be compensated after the system wakes up.In Stop 2 mode, most peripherals stop working. Only when the low power timer 1 (LP_TIM1) chooses LSI as the clock source, it still works properly, so LP_TIM1 is chosen as the time compensation timer in Stop 2 mode.

 

3. Next, sleep time is obtained by LP_TIM1, which is indeed obtained, but how to compensate to the clock source, it is directly assigned to the system clock.However, while sleeping again, freertos's own function to get the next task time made a mistake, indicating that it's not right to restore the system clock.

The overall idea should be right, but stuck here. Later, I thought that if the serial interrupt is triggered frequently and the power consumption will be affected by the continuous recovery from the stop2 mode, I decided to distinguish the two situations. stop2+RTC will wake up if the current period of time can sleep, and other system clocks such as LPRUN low power mode or sleep will not stop if it is necessary to receive the serial port at this stage.Mode to switch between multiple modes.Switch to a different low power mode in different situations.

My stm32l476 board consumes about 3ma in clock sleep mode of 48MHZ system.stop mode is less than 1ma, and tests show that most of the power consumption is in the clock block.(Only one flash task is on in freeRTos, cycle 1s)

 

Note: In low power applications, we usually pull down the pins to prevent leakage of current, but pay attention to the matching of hardware and software.If a pull-down resistance is added to the serial port and you configure it with a pull-up output, some unnecessary power loss will result.

In order to achieve lower power consumption under the condition of satisfying the performance, it seems impossible not to proceed with the whole process. It is OK to get it out and use it, but the stability should be verified, and several core functions should be marked out.

1. System clock configuration (that is, the configuration of the cubmx block diagram above)

void SystemClock_Config(void)
{
	LL_FLASH_SetLatency(LL_FLASH_LATENCY_2);

	if(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_2)
	{
		Error_Handler();
	}
	LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1);

	LL_RCC_LSI_Enable();

	/* Wait till LSI is ready */
	while(LL_RCC_LSI_IsReady() != 1)
	{

	}
	LL_RCC_MSI_Enable();

	/* Wait till MSI is ready */
	while(LL_RCC_MSI_IsReady() != 1)
	{

	}
	LL_RCC_MSI_EnablePLLMode();
	LL_RCC_MSI_EnableRangeSelection();
	LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_6);
	LL_RCC_MSI_SetCalibTrimming(0);
	LL_PWR_EnableBkUpAccess();
	LL_RCC_ForceBackupDomainReset();
	LL_RCC_ReleaseBackupDomainReset();
	LL_RCC_LSE_SetDriveCapability(LL_RCC_LSEDRIVE_LOW);
	LL_RCC_LSE_Enable();

	/* Wait till LSE is ready */
	while(LL_RCC_LSE_IsReady() != 1)
	{

	}
	LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSI);
	LL_RCC_EnableRTC();
	LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_MSI, LL_RCC_PLLM_DIV_1, 24, 
    LL_RCC_PLLR_DIV_2);
	LL_RCC_PLL_EnableDomain_SYS();
	LL_RCC_PLL_Enable();

	/* Wait till PLL is ready */
	while(LL_RCC_PLL_IsReady() != 1)
	{

	}
	LL_RCC_PLLSAI1_ConfigDomain_48M(LL_RCC_PLLSOURCE_MSI, LL_RCC_PLLM_DIV_1, 24,     
    LL_RCC_PLLSAI1Q_DIV_2);
	LL_RCC_PLLSAI1_EnableDomain_48M();
	LL_RCC_PLLSAI1_Enable();

	/* Wait till PLLSAI1 is ready */
	while(LL_RCC_PLLSAI1_IsReady() != 1)
	{

	}
	LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);

	/* Wait till System clock is ready */
	while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL)
	{

	}
	LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
	LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
	LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1);
	LL_SetSystemCoreClock(48000000);
	LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_LSE);
	LL_RCC_SetI2CClockSource(LL_RCC_I2C2_CLKSOURCE_PCLK1);
	LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM1_CLKSOURCE_LSI);
	LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM2_CLKSOURCE_LSI);
	LL_RCC_SetUSBClockSource(LL_RCC_USB_CLKSOURCE_PLLSAI1);
	LL_RCC_SetADCClockSource(LL_RCC_ADC_CLKSOURCE_SYSCLK);
}

2. Configuration of RTC for user wake-up

void SystemPower_Config(void)
{
  /* Configure RTC */
  RTCHandle.Instance = RTC;

  RTCHandle.Init.HourFormat = RTC_HOURFORMAT_24;
  RTCHandle.Init.AsynchPrediv = RTC_ASYNCH_PREDIV;
  RTCHandle.Init.SynchPrediv = RTC_SYNCH_PREDIV;
  RTCHandle.Init.OutPut = RTC_OUTPUT_DISABLE;
  RTCHandle.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
  RTCHandle.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;

  if( HAL_RTC_Init(&RTCHandle) != HAL_OK)
  {
    /* Initialization Error */

  }
}

stm32l4xx_hal_msp.c Add Code

void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
{
  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_PeriphCLKInitTypeDef  PeriphClkInitStruct;

  /*##-1- Configure the RTC clock source ######################################*/
  /* -a- Enable LSI Oscillator */
  RCC_OscInitStruct.OscillatorType =  RCC_OSCILLATORTYPE_LSI;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  RCC_OscInitStruct.LSIState = RCC_LSI_ON;
  if(HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    while(1);
  }

  /* -b- Select LSI as RTC clock source */
  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
  PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
  if(HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
  { 
    while(1);
  }

  /*##-2- Enable the RTC peripheral Clock ####################################*/
  /* Enable RTC Clock */
  __HAL_RCC_RTC_ENABLE();
  
  /*##-3- Configure the NVIC for RTC Alarm ###################################*/
  HAL_NVIC_SetPriority(RTC_WKUP_IRQn, 0x0, 0);
  HAL_NVIC_EnableIRQ(RTC_WKUP_IRQn);
}

/**
  * @brief RTC MSP De-Initialization 
  *        This function freeze the hardware resources used in this example:
  *          - Disable the Peripheral's clock
  * @param hrtc: RTC handle pointer
  * @retval None
  */
void HAL_RTC_MspDeInit(RTC_HandleTypeDef *hrtc)
{
  /*##-1- Reset peripherals ##################################################*/
  __HAL_RCC_RTC_DISABLE();
}
void HAL_MspInit(void)
{
  /* USER CODE BEGIN MspInit 0 */

  /* USER CODE END MspInit 0 */

  __HAL_RCC_SYSCFG_CLK_ENABLE();
  __HAL_RCC_PWR_CLK_ENABLE();

  /* System interrupt init*/

  /* USER CODE BEGIN MspInit 1 */

  /* USER CODE END MspInit 1 */
}

3. Core power operation and clock recovery

void OS_PreSleepProcessing(uint32_t *ulExpectedIdleTime)
{

   *ulExpectedIdleTime = 0;  //Block default wfi command execution
   if(g_stopmode_flag)
   {
      tick_sleep = ((uint32_t)prvGetExpectedIdleTime());
      if(tick_sleep > 4) //Determine whether to enter low power consumption
      {
          LPUART_PrepareToStopMode();//Configure Serial Wake-up
          HAL_RTCEx_DeactivateWakeUpTimer(&RTCHandle);//Configure RTC Wake Up
          HAL_RTCEx_SetWakeUpTimer_IT(&RTCHandle, (tick_sleep)*2,     
          RTC_WAKEUPCLOCK_RTCCLK_DIV16);// Wakeup Time Base = 16 /(~32.000KHz) = ~0.5 ms
          count_start(); //Reset Count
          tick_sys_old = xTaskGetTickCount();
         HAL_PWREx_EnterSTOP1Mode(PWR_STOPENTRY_WFI);//Enter low power consumption
         g_stopmode_start_flag = 1;
      }
    }

 }
void OS_PostSleepProcessing(uint32_t *ulExpectedIdleTime)
{
  time_count_sys =  LL_LPTIM_GetCounter(LPTIM2)+4;
  count_stop();
  if(g_stopmode_flag && g_stopmode_start_flag)
  {
    g_stopmode_start_flag = 0;
    if(time_count_sys == tick_sleep)
    {
    //Expressed as RTC wake-up
    rtc_wakeup_flag =1;

    }
    SYSCLKConfig_STOP();//Recovery Clock
  }

}
//Clock recovery, which corresponds to configuration, how to configure and how to restore
void SYSCLKConfig_STOP(void)
{
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  uint32_t pFLatency = 0;

  /* Enable Power Control clock */
  __HAL_RCC_PWR_CLK_ENABLE();

  /* Get the Oscillators configuration according to the internal RCC registers */
  HAL_RCC_GetOscConfig(&RCC_OscInitStruct);

  /* Enable PLL */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_NONE;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {

  }

  /* Get the Clocks configuration according to the internal RCC registers */
  HAL_RCC_GetClockConfig(&RCC_ClkInitStruct, &pFLatency);

  /* Select PLL as system clock source and keep HCLK, PCLK1 and PCLK2 clocks dividers as         
  before */
  RCC_ClkInitStruct.ClockType     = RCC_CLOCKTYPE_SYSCLK;
  RCC_ClkInitStruct.SYSCLKSource  = RCC_SYSCLKSOURCE_PLLCLK;
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, pFLatency) != HAL_OK)
  {

  }
}

4. The operations in the tickless function can also be generated directly from a new function, which I change directly based on tickless.Only two were added

__weak void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )
	{
	uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickCTRL;
	TickType_t xModifiableIdleTime;

		/* Make sure the SysTick reload value does not overflow the counter. */
		if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks )
		{
			xExpectedIdleTime = xMaximumPossibleSuppressedTicks;
		}

		/* Stop the SysTick momentarily.  The time the SysTick is stopped for
		is accounted for as best it can be, but using the tickless mode will
		inevitably result in some tiny drift of the time maintained by the
		kernel with respect to calendar time. */
		portNVIC_SYSTICK_CTRL_REG &= ~portNVIC_SYSTICK_ENABLE_BIT;

		/* Calculate the reload value required to wait xExpectedIdleTime
		tick periods.  -1 is used because this code will execute part way
		through one of the tick periods. */
		ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE_REG + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) );
		if( ulReloadValue > ulStoppedTimerCompensation )
		{
			ulReloadValue -= ulStoppedTimerCompensation;
		}

		/* Enter a critical section but don't use the taskENTER_CRITICAL()
		method as that will mask interrupts that should exit sleep mode. */
		__disable_interrupt();
		__DSB();
		__ISB();


		/* If a context switch is pending or a task is waiting for the scheduler
		to be unsuspended then abandon the low power entry. */
		if( eTaskConfirmSleepModeStatus() == eAbortSleep )
		{
			/* Restart from whatever is left in the count register to complete
			this tick period. */
			portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG;

			/* Restart SysTick. */
			portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;

			/* Reset the reload register to the value required for normal tick
			periods. */
			portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;

			/* Re-enable interrupts - see comments above __disable_interrupt()
			call above. */
			__enable_interrupt();
		}
		else
		{
			/* Set the new reload value. */
			portNVIC_SYSTICK_LOAD_REG = ulReloadValue;

			/* Clear the SysTick count flag and set the count value back to
			zero. */
			portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;

			/* Restart SysTick. */
			portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;

			/* Sleep until something happens.  configPRE_SLEEP_PROCESSING() can
			set its parameter to 0 to indicate that its implementation contains
			its own wait for interrupt or wait for event instruction, and so wfi
			should not be executed again.  However, the original expected idle
			time variable must remain unmodified, so a copy is taken. */
			xModifiableIdleTime = xExpectedIdleTime;
			configPRE_SLEEP_PROCESSING( xModifiableIdleTime );
			if( xModifiableIdleTime > 0 )
			{
				__DSB();
				__WFI();
				__ISB();
			}
			configPOST_SLEEP_PROCESSING( xExpectedIdleTime );

			/* Stop SysTick.  Again, the time the SysTick is stopped for is
			accounted for as best it can be, but using the tickless mode will
			inevitably result in some tiny drift of the time maintained by the
			kernel with respect to calendar time. */
			ulSysTickCTRL = portNVIC_SYSTICK_CTRL_REG;
			portNVIC_SYSTICK_CTRL_REG = ( ulSysTickCTRL & ~portNVIC_SYSTICK_ENABLE_BIT );

			/* Re-enable interrupts - see comments above __disable_interrupt()
			call above. */
			__enable_interrupt();

			if( ( ulSysTickCTRL & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 || (rtc_wakeup_flag ==1))
			{
				uint32_t ulCalculatedLoadValue;
                                rtc_wakeup_flag = 0;
                                if(g_stopmode_flag)  //1. This is a new addition
                                {
                                 ulCompleteTickPeriods = time_count_sys;
                                }
                                else
                                {
                                  /* The tick interrupt has already executed, and the SysTick
                                  count reloaded with ulReloadValue.  Reset the
                                  portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick
                                  period. */
                                  ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG );

                                  /* Don't allow a tiny value, or values that have somehow
                                  underflowed because the post sleep hook did something
                                  that took too long. */
                                  if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) )
                                  {
                                          ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL );
                                  }

                                  portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue;

                                  /* The tick interrupt handler will already have pended the tick
                                  processing in the kernel.  As the pending tick will be
                                  processed as soon as this function exits, the tick value
                                  maintained by the tick is stepped forward by one less than the
                                  time spent waiting. */
                                  ulCompleteTickPeriods = xExpectedIdleTime - 1UL;
                                }
                               
			}
			else
			{
				/* Something other than the tick interrupt ended the sleep.
				Work out how long the sleep lasted rounded to complete tick
				periods (not the ulReload value which accounted for part
				ticks). */
				//ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG;
                                if(g_stopmode_flag) //2. New here
                                {
                                 ulCompletedSysTickDecrements = time_count_sys;
                                }
                                else
                                {
                                  ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG;
                                }
				/* How many complete tick periods passed while the processor
				was waiting? */
				ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick;

				/* The reload value is set to whatever fraction of a single tick
				period remains. */
				portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1UL ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements;
			}

			/* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG
			again, then set portNVIC_SYSTICK_LOAD_REG back to its standard
			value.  The critical section is used to ensure the tick interrupt
			can only execute once in the case that the reload register is near
			zero. */
			portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
			portENTER_CRITICAL();
			{
				portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;
				vTaskStepTick( ulCompleteTickPeriods );
				portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;
			}
			portEXIT_CRITICAL();
		}
	}

 

 

 

 

 

 

 

 

 

101 original articles have been published. 27. 20,000 visits+
Private letter follow

Keywords: less

Added by ranman on Thu, 12 Mar 2020 04:23:01 +0200