文章目录
- 一、定时器理论
- 1.1定时器创建和使用
- 二、定时器实践
- 2.1周期触发定时器
- 2.2按键消抖
一、定时器理论
定时器是一种允许在特定时间间隔后或在将来的某个时间点调用回调函数的机制。对于需要周期性任务或延迟执行任务的嵌入式应用程序特别有用。
软件定时器: FreeRTOS 提供的用于实现定时操作的功能。与硬件定时器不同,软件定时器在 FreeRTOS 的任务调度机制之上运行。
定时器回调函数: 当定时器到期时,FreeRTOS 将调用用户定义的回调函数。
定时器类型分为以下两种:
一次性定时器(One-shot Timer):定时器在到期后自动停止,仅调用一次回调函数。
周期性定时器(Auto-reload Timer):定时器在到期后自动重新启动,周期性地调用回调函数。
1.1定时器创建和使用
回调函数
pxCallbackFunction
可以使用pvTimerID
分辨是哪个定时器
返回值: 成功则返回TimerHandle_t
, 否则返回NULL
TimerHandle_t xTimerCreate( const char * const pcTimerName,
const TickType_t xTimerPeriodInTicks,
const UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction );
在RTOS中,每隔一个固定的时间产生中断,中断函数里面可以去判断定时器时间有没有超时,超时后就唤醒守护任务去执行回调函数
其他任务要配置和使用定时器时,是通过定时器命令队列(timer command queue)和守护任务交互,所以守护任务优先级要尽可能高
所以在启动定时器要有一个
xTicksToWait
,当队列满了要设置等待时间
BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait );
定时器状态:冬眠(Dormant)和运行(Running)、
二、定时器实践
2.1周期触发定时器
创建定时器
static TimerHandle_t xMyTimerHandle;
xMyTimerHandle = xTimerCreate("mytimer", 100, pdTRUE, NULL, MyTimerCallbackFunction);
回调函数
void MyTimerCallbackFunction( TimerHandle_t xTimer )
{
static int cnt = 0;
flagTimer = !flagTimer;
printf("MyTimerCallbackFunction_t cnt = %d\r\n", cnt++);
}
启动定时器:把命令通过 定时器队列 发给守护任务,由守护任务来启动定时器
void Task1Function(void * param)
{
volatile int i = 0;
xTimerStart(xMyTimerHandle, 0);
while (1)
{
printf("Task1Function ...\r\n");
}
}
结果:每隔100ms进入回调函数
2.2按键消抖
在嵌入式开发中,我们使用机械开关时经常碰到抖动问题:引脚电平在短时间内反复变化。针对这个问题,我们在中断函数中添加定时器,在产生中断后定时器延时20ms,假如由于抖动再次进入中断,继续延时20ms直到按键趋于稳定。
创建定时器, 设置一次触发,延时20ms
static TimerHandle_t xMyTimerHandle;
xMyTimerHandle = xTimerCreate("mytimer", 2000, pdFALSE, NULL, MyTimerCallbackFunction);
在回调函数中记录定时器中断次数
cnt
void MyTimerCallbackFunction( TimerHandle_t xTimer )
{
static int cnt = 0;
flagTimer = !flagTimer;
printf("Get GPIO Key cnt = %d\r\n", cnt++);
}
按键中断函数中使用定时器消除抖动,通过复位函数
xTimerReset
往定时器队列写入数据,由守护任务根据队列命令来复位定时器。在这里多次产生中断会多次调用复位函数xTimerReset
。时间到达后进入回调函数
void EXTI0_IRQHandler(void)
{
static int cnt = 0;
if(EXTI_GetITStatus(EXTI_Line0) != RESET)
{
printf("EXTI0_IRQHandler cnt = %d\r\n", cnt++);
/* 使用定时器消除抖动 */
xTimerReset(xMyTimerHandle, 0); /* Tcur + 2000 */
EXTI_ClearITPendingBit(EXTI_Line0); //清除中断
}
}