407 lines
15 KiB
C
407 lines
15 KiB
C
|
#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
|
||
|
}
|
||
|
}
|