目录
1. 定时器的理论讲解
本节视频参考的源码是25_freertos_example_timer
,从05_freertos_example_createtask
复制得到。本节视频尚未修改它。
1.1 定时器三要素
-
超时时间
-
函数
-
单次触发还是周期性触发
1.2 定时器函数执行的上下文
在中断里执行?在任务里执行?
1.3 定时器函数的内部:队列
定时器的超时函数是在守护任务里面执行,这个守护任务可以管理各种定时器,别的任务可以调用定时器函数(去启动/停止/复位/改变周期),这些函数的实质是把命令写入队列中,之后就会唤起守护任务,守护任务就会从队列中取那些命令,让我做什么事情。
如果队列满,不等待的话,这些队列都有可能失败。
2. 定时器的一般使用
视频对应的源码为:25_freertos_example_timer
-
简单示例
-
优先级相关
static TimerHandle_t xMyTimerHandle;
static int flagTimer = 0;
void Task1Function(void * param)
{
volatile int i = 0;
xTimerStart(xMyTimerHandle, 0);
while (1)
{
printf("Task1Function ...\r\n");
}
}
void MyTimerCallbackFunction( TimerHandle_t xTimer )
{
static int cnt = 0;
flagTimer = !flagTimer;
printf("MyTimerCallbackFunction_t cnt = %d\r\n", cnt++);
}
/*-----------------------------------------------------------*/
int main( void )
{
TaskHandle_t xHandleTask1;
#ifdef DEBUG
debug();
#endif
prvSetupHardware();
printf("Hello, world!\r\n");
xMyTimerHandle = xTimerCreate("mytimer", 100, pdTRUE, NULL, MyTimerCallbackFunction);
xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);
//xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);
/* Start the scheduler. */
vTaskStartScheduler();
/* Will only get here if there was not enough heap space to create the
idle task. */
return 0;
}
定时器函数一直没有被执行,因为定时器的优先级没有Task1的高,定时器没有办法抢占Task1,
3. 定时器防抖
视频对应的源码为:26_freertos_example_readkey
-
在中断函数中启动、复位定时器
-
每次抖动都会推迟定时器的超时时间
-
多次抖动只导致定时器超时一次:消除了抖动
main.c
void MyTimerCallbackFunction( TimerHandle_t xTimer )
{
static int cnt = 0;
flagTimer = !flagTimer;
printf("Get GPIO Key cnt = %d\r\n", cnt++);
}
/*-----------------------------------------------------------*/
void KeyInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure;//定义结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);// 使能时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // 选择IO口 PA0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 设置成上拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure); // 使用结构体信息进行初始化IO口
}
void KeyIntInit(void)
{
EXTI_InitTypeDef EXTI_InitStructure;//定义初始化结构体
NVIC_InitTypeDef NVIC_InitStructure;//定义结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); /* 使能AFIO复用时钟 */
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); /* 将GPIO口与中断线映射起来 */
EXTI_InitStructure.EXTI_Line=EXTI_Line0; // 中断线
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; // 双边沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); // 初始化
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQChannel; //使能外部中断所在的通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能外部中断通道
NVIC_Init(&NVIC_InitStructure); // 初始化
}
void EXTI0_IRQHandler(void)
{
static int cnt = 0;
if(EXTI_GetITStatus(EXTI_Line0) != RESET)
{
printf("EXTI0_IRQHandler cnt = %d\r\n", cnt++);
/* 使用定时器消除抖动 */
xTimerResetfromISR(xMyTimerHandle, 0); /* Tcur + 2000 */
EXTI_ClearITPendingBit(EXTI_Line0); //清除中断
}
}
int main( void )
{
TaskHandle_t xHandleTask1;
#ifdef DEBUG
debug();
#endif
prvSetupHardware();
printf("Hello, world!\r\n");
KeyInit();
KeyIntInit();
xMyTimerHandle = xTimerCreate("mytimer", 2000, pdFALSE, NULL, MyTimerCallbackFunction);
xTaskCreate(Task1Function, "Task1", 100, NULL, 1, &xHandleTask1);
//xTaskCreate(Task2Function, "Task2", 100, NULL, 1, NULL);
/* Start the scheduler. */
vTaskStartScheduler();
/* Will only get here if there was not enough heap space to create the
idle task. */
return 0;
}