MXC-A36_2024.04.17/MXC-Screen_display/components/modules/FreeRTOS/freertos_sleep.c

407 lines
15 KiB
C
Raw Normal View History

2024-02-28 16:45:49 +08:00
#include <stdbool.h>
#include "FreeRTOS.h"
#include "task.h"
#include "co_util.h"
#include "fr30xx.h"
#define FLASH_POWER_DOWN 0
#define DEBUG_TIMING 0
#define DEBUG_UART_BASE UART3_BASE
/* Constants required to manipulate the core. Registers first... */
#define portNVIC_SYSTICK_CTRL_REG ( *( ( volatile uint32_t * ) 0xe000e010 ) )
#define portNVIC_SYSTICK_LOAD_REG ( *( ( volatile uint32_t * ) 0xe000e014 ) )
#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( *( ( volatile uint32_t * ) 0xe000e018 ) )
/* ...then bits in the registers. */
#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL )
#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL )
#define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL )
#define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL )
#define portNVIC_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) )
uint8_t rwip_calc_sleep_time(int32_t *sleep_duration);
/* base: BIT0~27 is valid, unit is 312.5us; fine: 0~624, unit is 0.5us */
void rwip_counter_get(uint32_t *base, uint16_t *fine);
void low_power_save_cpu(void);
void low_power_restore_cpu(void);
void rwip_wakeup_start(void);
bool rwip_wakeup_is_ongoing(void);
/*
* The number of SysTick increments that make up one tick period.
*/
#if ( configUSE_TICKLESS_IDLE == 1 )
extern uint32_t ulTimerCountsForOneTick;
#endif /* configUSE_TICKLESS_IDLE */
/*
* The maximum number of tick periods that can be suppressed is limited by the
* 24 bit resolution of the SysTick timer.
*/
#if ( configUSE_TICKLESS_IDLE == 1 )
extern uint32_t xMaximumPossibleSuppressedTicks;
#endif /* configUSE_TICKLESS_IDLE */
/*
* Compensate for the CPU cycles that pass while the SysTick is stopped (low
* power functionality only.
*/
#if ( configUSE_TICKLESS_IDLE == 1 )
extern uint32_t ulStoppedTimerCompensation;
#endif /* configUSE_TICKLESS_IDLE */
__RAM_CODE uint32_t ms_2_lpcycles(uint32_t ms)
{
uint32_t lpcycles;
uint32_t tmp_low, tmp_high;
mul_64(&tmp_low, &tmp_high, system_get_LPRCCLK(), ms);
lpcycles = simple_div_64(tmp_low, tmp_high, 1000);
return(lpcycles);
}
__RAM_CODE uint32_t us_2_lpcycles(uint32_t us)
{
uint32_t lpcycles;
uint32_t tmp_low, tmp_high;
mul_64(&tmp_low, &tmp_high, system_get_LPRCCLK(), us);
lpcycles = simple_div_64(tmp_low, tmp_high, 1000000);
return(lpcycles);
}
__RAM_CODE uint32_t lpcycles_2_us(uint32_t lp_cyc)
{
uint32_t us;
uint32_t tmp_low, tmp_high;
mul_64(&tmp_low, &tmp_high, 1000000, lp_cyc);
us = simple_div_64(tmp_low, tmp_high, system_get_LPRCCLK());
return(us);
}
__RAM_CODE void low_power_enter_sleep(void)
{
#if DEBUG_TIMING
*(volatile uint32_t *)0x50110000 = 'e';
#endif
ool_write(PMU_REG_SW_OP, 0x01);
}
__RAM_CODE void low_power_enter(void)
{
uint32_t org_value;
org_value = ool_read32(0xfc);
if (org_value & 0x01) {
ool_write32(0xfc/*PMU_REG_CPU_RESET_VECTOR*/, (uint32_t)low_power_restore_cpu | 0x01);
}
else {
ool_write32(0xfc/*PMU_REG_CPU_RESET_VECTOR*/, (uint32_t)low_power_restore_cpu & (~0x01));
}
low_power_save_cpu();
}
__RAM_CODE __WEAK bool user_deep_sleep_check(void)
{
return true;
}
__RAM_CODE __WEAK void user_entry_before_sleep(void)
{
}
__RAM_CODE __WEAK void user_entry_after_sleep(void)
{
}
__RAM_CODE __WEAK void user_entry_after_sleep_user(void)
{
}
#if DEBUG_TIMING
__RAM_CODE __STATIC_INLINE void init_uart_for_debug(void)
{
__SYSTEM_UART2_CLK_ENABLE();
*(volatile uint32_t *)0xE005012c = 0x00009911;
*(volatile uint32_t *)(DEBUG_UART_BASE+0x0c) = 0x03;
*(volatile uint32_t *)(DEBUG_UART_BASE+0x1c) = 0x09;
*(volatile uint32_t *)(DEBUG_UART_BASE+0x08) = 0x09;
/* baudrate */
*(volatile uint32_t *)(DEBUG_UART_BASE+0x0c) = 0x83;
*(volatile uint32_t *)(DEBUG_UART_BASE+0x00) = 0x01;
*(volatile uint32_t *)(DEBUG_UART_BASE+0x04) = 0x00;
*(volatile uint32_t *)(DEBUG_UART_BASE+0xc0) = 0x28;
*(volatile uint32_t *)(DEBUG_UART_BASE+0x0c) = 0x03;
}
#endif
volatile TickType_t latest_idle_time;
__RAM_CODE void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )
{
uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements;
latest_idle_time = xExpectedIdleTime;
/* Make sure the Sleep LP counter does not overflow RTC . */
if( xExpectedIdleTime > (0x01FFFFFF / portTICK_RATE_MS) )
{
xExpectedIdleTime = 0x01FFFFFF / portTICK_RATE_MS;
}
/* Enter a critical section but don't use the taskENTER_CRITICAL()
* method as that will mask interrupts that should exit sleep mode. */
__disable_irq();
__DSB();
__ISB();
/* 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;
/* 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 SysTick. */
portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;
/* Re-enable interrupts - see comments above __disable_irq() call
* above. */
__enable_irq();
}
else
{
int ms = xExpectedIdleTime * portTICK_RATE_MS;
bool deep_sleep = false;
if ((ms < 20)
|| (system_prevent_sleep_get()!=0)
|| (user_deep_sleep_check()==false)) { // enter light sleep mode
/* Make sure the SysTick reload value does not overflow the counter. */
if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks )
{
xExpectedIdleTime = xMaximumPossibleSuppressedTicks;
}
/* 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;
}
/* 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;
__DSB();
__WFI();
__ISB();
/* Disable the SysTick clock without reading the
* portNVIC_SYSTICK_CTRL_REG register to ensure the
* portNVIC_SYSTICK_COUNT_FLAG_BIT is not cleared if it is set. 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*/
portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT );
/* Determine if the SysTick clock has already counted to zero and
* been set back to the current reload value (the reload back being
* correct for the entire expected idle time) or if the SysTick is yet
* to count to zero (in which case an interrupt other than the SysTick
* must have brought the system out of sleep mode). */
if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 )
{
uint32_t ulCalculatedLoadValue;
/* The tick interrupt is already pending, 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 );
portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue;
/* 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;
/* 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;
}
}
else { // enter deep sleep mode
uint32_t sleep_us;
uint32_t escaped_system_tick, tail_system_tick;
uint32_t before_counter, after_counter;
deep_sleep = true;
#if DEBUG_TIMING
*(volatile uint32_t *)DEBUG_UART_BASE = 'a';
#endif
/* remove time consumed during wake up procedure (from PMU IRQ to __enable_irq) */
ms -= 10;
/*
* 1. record start RTC counter, used to calculate actual sleep time
* 2. setup target RTC counter
*/
{
uint32_t target_counter;
#if DEBUG_TIMING
*(volatile uint32_t *)DEBUG_UART_BASE = 'b';
#endif
ool_write(PMU_REG_RTC_CTRL, ool_read(PMU_RTC_SAMPLE_BIT) | PMU_RTC_SAMPLE_BIT);
while (ool_read(PMU_REG_RTC_CTRL) & PMU_RTC_SAMPLE_BIT);
#if DEBUG_TIMING
*(volatile uint32_t *)DEBUG_UART_BASE = 'c';
#endif
before_counter = ool_read32(PMU_REG_RTC_COUNTER_0);
target_counter = before_counter + ms_2_lpcycles(ms);
ool_write32(PMU_REG_ALARM_B_COUNTER_0, target_counter);
ool_write(PMU_REG_RTC_CTRL, ool_read(PMU_RTC_SAMPLE_BIT) | PMU_RTC_ALARM_B_EN_BIT);
}
/* notice APP layer that system will enter deep sleep mode */
user_entry_before_sleep();
/* prepare for deep sleep mode */
uint32_t reset_vector = SCB->VTOR;
uint32_t rtos_priorities = portNVIC_SHPR3_REG;
ool_write(PMU_REG_STATUS, PMU_STATUS_DEEP_SLEEP);
escaped_system_tick = ulTimerCountsForOneTick - portNVIC_SYSTICK_CURRENT_VALUE_REG;
#if DEBUG_TIMING
*(volatile uint32_t *)DEBUG_UART_BASE = 'd';
#endif
low_power_enter();
/* FPU settings */
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
SCB->CPACR |= ((3UL << 10*2) | (3UL << 11*2)); /* set CP10 and CP11 Full Access */
#endif
__SYSTEM_FREE_COUNTER_CLK_ENABLE();
#if DEBUG_TIMING
init_uart_for_debug();
*(volatile uint32_t *)DEBUG_UART_BASE = '1';
#endif
//init internal flash
__SYSTEM_PFC_CLK_ENABLE();
__SYSTEM_QSPI0_CLK_ENABLE();
__SYSTEM_APB_CLK_ENABLE();
__SYSTEM_APB1_CLK_ENABLE();
system_cache_enable(true);
flash_init_controller(QSPI0, FLASH_RD_TYPE_DUAL, FLASH_WR_TYPE_SINGLE);
flash_set_baudrate(QSPI0, QSPI_BAUDRATE_DIV_4);
#if DEBUG_TIMING
*(volatile uint32_t *)DEBUG_UART_BASE = '2';
#endif
ool_write(PMU_REG_STATUS, PMU_STATUS_NORAML);
SCB->VTOR = reset_vector;
portNVIC_SHPR3_REG = rtos_priorities;
#if DEBUG_TIMING
*(volatile uint32_t *)DEBUG_UART_BASE = '3';
#endif
/* notice APP layer that system has recovery from deep sleep mode */
user_entry_after_sleep();
{
uint32_t target;
ool_write(PMU_REG_RTC_CTRL, (ool_read(PMU_RTC_SAMPLE_BIT) & (~PMU_RTC_ALARM_B_EN_BIT)) | PMU_RTC_SAMPLE_BIT | PMU_RTC_ALARM_B_CLR_BIT);
while (ool_read(PMU_REG_RTC_CTRL) & PMU_RTC_SAMPLE_BIT);
after_counter = ool_read32(PMU_REG_RTC_COUNTER_0);
}
#if DEBUG_TIMING
*(volatile uint32_t *)DEBUG_UART_BASE = '4';
#endif
sleep_us = lpcycles_2_us(after_counter - before_counter);
sleep_us += 180;
#if DEBUG_TIMING
*(volatile uint32_t *)DEBUG_UART_BASE = '5';
#endif
/*
* 1. how many ticks have passed during sleep mode, ulCompleteTickPeriods
* 2. how many system ticks left in the following RTOS tick, tail_system_tick
*/
ulCompleteTickPeriods = pdMS_TO_TICKS(sleep_us / 1000);
tail_system_tick = (pdMS_TO_TICKS(sleep_us % 1000) * ulTimerCountsForOneTick) / 1000;
tail_system_tick += escaped_system_tick;
if (tail_system_tick >= ulTimerCountsForOneTick) {
tail_system_tick -= ulTimerCountsForOneTick;
ulCompleteTickPeriods++;
}
portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT );
portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1 - tail_system_tick;
#if DEBUG_TIMING
*(volatile uint32_t *)DEBUG_UART_BASE = '6';
#endif
}
/* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG
* again, then set portNVIC_SYSTICK_LOAD_REG back to its standard
* value. */
portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;
vTaskStepTick( ulCompleteTickPeriods );
portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;
#if DEBUG_TIMING
*(volatile uint32_t *)DEBUG_UART_BASE = '7';
#endif
/* Exit with interrupts enabled. */
__enable_irq();
if (deep_sleep == true) {
user_entry_after_sleep_user();
}
#if DEBUG_TIMING
*(volatile uint32_t *)DEBUG_UART_BASE = '8';
#endif
}
}