文章目录
- 打印重定向
- FreeRTOSConfig.h 配置
- portmacro.h 实现
- 低功耗流程
- vPortSuppressTicksAndSleep 实现
- 测试
- 效果
- 注意事项
打印重定向
为了观察睡眠时间,重定向打印函数,打印的时候将时间戳打印出来,实现如下
#define printf(fmt, ...) \do \{ \char time_buf[20]; \time_t now = time(NULL); \struct tm *tm_info = localtime(&now); \strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", tm_info); \fprintf(stdout, "[%s] " fmt, time_buf, ##__VA_ARGS__); \} while (0)
FreeRTOSConfig.h 配置
#define configTICK_RATE_HZ ( 1000 ) /* In this non-real time simulated environment the tick frequency has to be at least a multiple of the Win32 tick frequency, and therefore very slow. */#define configUSE_TICKLESS_IDLE 1
#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 10
portmacro.h 实现
#ifndef portSUPPRESS_TICKS_AND_SLEEPextern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime );#define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime )
#endif
低功耗流程
task.c 文件中
if (xExpectedIdleTime >= (TickType_t)configEXPECTED_IDLE_TIME_BEFORE_SLEEP)
{vTaskSuspendAll();{/* Now the scheduler is suspended, the expected idle* time can be sampled again, and this time its value can* be used. */configASSERT(xNextTaskUnblockTime >= xTickCount);xExpectedIdleTime = prvGetExpectedIdleTime();/* Define the following macro to set xExpectedIdleTime to 0* if the application does not want* portSUPPRESS_TICKS_AND_SLEEP() to be called. */configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING(xExpectedIdleTime);if (xExpectedIdleTime >= (TickType_t)configEXPECTED_IDLE_TIME_BEFORE_SLEEP){traceLOW_POWER_IDLE_BEGIN();portSUPPRESS_TICKS_AND_SLEEP(xExpectedIdleTime);traceLOW_POWER_IDLE_END();}else{mtCOVERAGE_TEST_MARKER();}}(void)xTaskResumeAll();
}
else
{mtCOVERAGE_TEST_MARKER();
}
低功耗函数主要在 vPortSuppressTicksAndSleep
函数,睡眠时间阈值通过宏 configEXPECTED_IDLE_TIME_BEFORE_SLEEP
,如果睡眠时间小于改值,就不进入睡眠。
vPortSuppressTicksAndSleep 实现
void vPortSuppressTicksAndSleep(TickType_t xExpectedIdleTime)
{struct timespec xSleepTime;TickType_t xModifiableIdleTime;uint64_t xStartTime, xEndTime, xElapsedTime;/* Stop the timer interrupt here (emulated by blocking SIGALRM). */pthread_sigmask(SIG_BLOCK, &xAllSignals, NULL);/* Record the start time. */xStartTime = prvGetTimeNs();/* Prepare the sleep time in nanoseconds. */xSleepTime.tv_sec = xExpectedIdleTime / configTICK_RATE_HZ;xSleepTime.tv_nsec = (xExpectedIdleTime % configTICK_RATE_HZ) * (1000000000 / configTICK_RATE_HZ);/* Enter low-power state. */nanosleep(&xSleepTime, NULL);/* Record the end time. */xEndTime = prvGetTimeNs();/* Calculate elapsed time. */xElapsedTime = (xEndTime - xStartTime) / (1000000000 / configTICK_RATE_HZ);printf("sleep time: %d\r\n", xExpectedIdleTime);/* Adjust system ticks. */vTaskStepTick((TickType_t)xElapsedTime);/* Restart the timer interrupt. */pthread_sigmask(SIG_UNBLOCK, &xAllSignals, NULL);
}
测试
新建一个 task,1000ms 打印一次
static void printf_task( void * pvParameters )
{( void ) pvParameters;for( ; ; ){vTaskDelay(1000);console_print( "task1\n" );}
}
xTaskCreate( printf_task, "Rx", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 2,NULL );
效果
[2024-11-29 12:36:14] sleep time: 1000
[2024-11-29 12:36:14] task1
[2024-11-29 12:36:15] sleep time: 1000
[2024-11-29 12:36:15] task1
[2024-11-29 12:36:16] sleep time: 1000
[2024-11-29 12:36:16] task1
[2024-11-29 12:36:17] sleep time: 1000
[2024-11-29 12:36:17] task1
[2024-11-29 12:36:18] sleep time: 1000
[2024-11-29 12:36:18] task1
[2024-11-29 12:36:19] sleep time: 1000
[2024-11-29 12:36:19] task1
[2024-11-29 12:36:20] sleep time: 1000
[2024-11-29 12:36:20] task1
从结果中可以看到,每次睡眠 1000ms,任务还是 1000ms 打印一次。
注意事项
移植 vPortSuppressTicksAndSleep 到 MCU 上时,最重要的是利用 MCU 的硬件支持来管理低功耗模式,合理配置系统的时钟源和中断源,以确保系统能在需要时唤醒并继续精确计时。需要考虑的核心问题包括:
- 低功耗模式选择和外设管理。
- 定时器和中断的正确配置。
- 实际休眠时间的计算和系统时钟同步。
- 调试和验证。
通过以上方法,能确保移植后的系统能在保证精度的同时有效降低功耗。