其他
基于STM32、FreeRTOS低功耗设计思路和原理
关注+星标公众号,不错过精彩内容
作者 | strongerHuang
微信公众号 | 嵌入式专栏
嵌入式专栏
1
嵌入式专栏
2
1. 情景分析
FreeRTOS各任务情况:
#define configUSE_TICKLESS_IDLE 1
/* Idle 任务 */
void prvIdleTask( void *pvParameters )
{
for( ; ; )
{
//...
#if(configUSE_TICKLESS_IDLE != 0)
{
TickType_t xExpectedIdleTime;
/* 用户策略以决定是否需要进入 Tickless Mode */
xExpectedIdleTime = prvGetExpectedIdleTime();
if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
{
vTaskSuspendAll(); // 挂起调度器
{
configASSERT( xNextTaskUnblockTime >= xTickCount );
xExpectedIdleTime = prvGetExpectedIdleTime();
if( xExpectedIdleTime >=
configEXPECTED_IDLE_TIME_BEFORE_SLEEP )
{
/* 用户函数接口 */
/* 1. 进入低功耗模式和如何退出低功耗模式 */
/* 2. 系统时间补偿 */
portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime );
}
}
(void) xTaskResumeAll(); // 恢复调度器
}
}
#endif /* configUSE_TICKLESS_IDLE */
//...
}
}
void vPortSuppressTicksAndSleep( portTickType xExpectedIdleTime )
{
unsigned long ulReloadValue, ulCompleteTickPeriods,
ulCompletedSysTickDecrements;
portTickType xModifiableIdleTime;
/* 最长睡眠时间不可以超过定时器的最大定时值 */
/* 通过调整定时器的时间基准可以获得更理想的最大定时值 */
if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks )
{
xExpectedIdleTime = xMaximumPossibleSuppressedTicks;
}
/* 停止 SysTick */
portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT;
/* 计算唤醒时的系统时间,用于唤醒后的系统时间补偿 */
ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE_REG + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) );
if( ulReloadValue > ulStoppedTimerCompensation )
{
ulReloadValue -= ulStoppedTimerCompensation;
}
__disable_interrupt();
/* 确认下是否可以进入低功耗模式 */
if( eTaskConfirmSleepModeStatus() == eAbortSleep )
{
/* 不可以,重新启动系统定时器 */
portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG;
portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT |
portNVIC_SYSTICK_INT_BIT |
portNVIC_SYSTICK_ENABLE_BIT;
portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;
__enable_interrupt();
}
else
{
/* 可以进入低功耗模式 */
/* 保存时间补偿,重启系统定时器 */
portNVIC_SYSTICK_LOAD_REG = ulReloadValue;
portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT |
portNVIC_SYSTICK_INT_BIT |
portNVIC_SYSTICK_ENABLE_BIT;
/* 进入低功耗模式,可以通过 configPRE_SLEEP_PROCESSING 函数进行低功耗模式下
时钟及外设的配置*/
xModifiableIdleTime = xExpectedIdleTime;
configPRE_SLEEP_PROCESSING( xModifiableIdleTime );
if( xModifiableIdleTime > 0 )
{
__DSB();
__WFI();
__ISB();
}
/* 退出低功耗模式 */
configPOST_SLEEP_PROCESSING( xExpectedIdleTime );
portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT |
portNVIC_SYSTICK_INT_BIT;
__disable_interrupt()
__enable_interrupt();
/*唤醒有两种情况:系统定时器或者外部事件(中断) */
if((portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT) != 0)
{
/* 系统定时器唤醒,时间补偿 */
unsigned long ulCalculatedLoadValue;
ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) –
( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG );
if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) ||
( ulCalculatedLoadValue > ulTimerCountsForOneTick ) )
{
ulCalculatedLoadValue = (ulTimerCountsForOneTick - 1UL);
}
portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue;
ulCompleteTickPeriods = xExpectedIdleTime - 1UL;
}
else
{
/* 外部事件(中断)唤醒 */
ulCompletedSysTickDecrements = ( xExpectedIdleTime *
ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG;
ulCompleteTickPeriods = ulCompletedSysTickDecrements /
ulTimerCountsForOneTick;portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1 ) *
ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements;
}
/* 重启 Systick,调整系统定时器中断为正常值 */
portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
portENTER_CRITICAL();
{
portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT |
portNVIC_SYSTICK_INT_BIT |
portNVIC_SYSTICK_ENABLE_BIT;
vTaskStepTick( ulCompleteTickPeriods );
portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;
}
portEXIT_CRITICAL();
}
}
嵌入式专栏
3
低功耗的设计存在很多影响功耗的因素,比如电路设计、IO引脚配置等。
MCU实现低功耗的方法和种类有很多,设计时需要注意一些低功耗细节问题。
最后,以上方法仅供学习参考,具体请按照实际项目选择合理的低功耗设计方案。
后台回复『STM32』『FreeRTOS』『嵌入式软件设计与开发』阅读更多相关文章。
点击“阅读原文”查看更多分享,欢迎点分享、收藏、点赞、在看。