STM32-FreeRTOS操作系统-软件定时器

引言

在嵌入式系统开发中,STM32微控制器与FreeRTOS操作系统的结合应用极为广泛。其中,件定时器作为FreeRTOS的重要功能模块,为实现精准任务调度与时间管理提供了有力支持,本文将对其作简要介绍。

软件定时器定义

软件定时器是 FreeRTOS 提供的一种功能模块,它允许用户在指定的时间间隔后执行特定的回调函数。与任务不同,软件定时器不具有独立的执行上下文,也不占用系统资源来维持运行状态。任务是 FreeRTOS 的核心调度单元,具有独立的栈空间和优先级,可以主动运行和被调度。而软件定时器仅在设定的时间点触发回调,依赖任务或其他机制来执行实际操作,更适合用于简单的定时需求,如周期性检查或延迟操作,相比任务更加轻量级。

简单来说软件定时器就是一个简单的定时任务,而且不需要占用大量内存。设置的优先级通常比较低,这样才不会打断正在运行的任务。

软件定时器

首先是定义软件定时器的句柄,并给其赋初值为NULL。这里我创建两个软件定时器。

TimerHandle_t Timer1_Handle =NULL; //软件定时器句柄1

TimerHandle_t Timer2_Handle =NULL; //软件定时器句柄2

xTimerCreate函数

该函数的原型为:

TimerHandle_t xTimerCreate(

const char * const pcTimerName, // 定时器名称(调试用)

const TickType_t xTimerPeriod, // 定时器周期(以 tick 为单位)

const UBaseType_t uxAutoReload, // 是否自动重载(pdTRUE/pdFALSE)

void * const pvTimerID, // 定时器 ID(回调函数中识别用)

TimerCallbackFunction_t pxCallbackFunction // 回调函数

);

可以看见,该函数是一个含参数且有返回值的函数。下面分析一下各个参数的作用:

pcTimerName:顾名思义,定时器名称,前面是const char*,表示常量字符串指针。

xTimerPeriod:定时周期,一般情况下都是以Tick系统节拍为单位。

uxAutoReload:是否自动重载,pdFALSE(0)表示的是单次定时器,到期后自动停止,不再触发,pdTRUE(1)表示的是周期定时器,到期后自动重置,持续触发。

pvTimerID:顾名思义,定时器ID,可自定义,主要是用来区分多个定时器

pxCallbackFunction:定时器到期是调用的函数。必要时应提前声明!

使用xTimerCreate函数

然后在临界区内创建软件定时器,并将创建返回的句柄赋值给刚刚创建的空句柄。

//创建定时器1

Timer1_Handle=xTimerCreate("AutoReloadTimer",1000,pdTRUE,(void*)1,Timer1_Callback);

//创建定时器2

Timer2_Handle=xTimerCreate("OneShotTimer",5000,pdFALSE,(void*)2,Timer2_Callback);

接着开启定时器:

if(Timer1_Handle != NULL)

{

xTimerStart(Timer1_Handle,0); //开启定时器1

}

if(Timer2_Handle != NULL)

{

xTimerStart(Timer2_Handle,0); //开启定时器2

}

实现回调函数

前面也说过了,必要时最好先声明该回调函数。

//定时器1回调函数

//软件定时器不要调用阻塞函数,也不要进行死循环,应快进快出

void Timer1_Callback(void* parameter)

{

LED=!LED;

}

//定时器2回调函数

//软件定时器不要调用阻塞函数,也不要进行死循环,应快进快出

void Timer2_Callback(void* parameter)

{

// LED=!LED;

}

当然,我这个是单独给每个定时器创建的函数,就用不到刚刚的定时器ID标识符了。如果需要更简便一点可以只用一个回调函数,然后在里面根据不同的ID标识符设置相应的逻辑。

void vUnifiedCallback(TimerHandle_t xTimer)

{

uint32_t id = (uint32_t)pvTimerGetTimerID(xTimer);

switch(id)

{

//在switch语句中根据不同的ID标识符做出对应的逻辑

}

}

示例代码

cs 复制代码
#include "myfreertos.h"
#include "FreeRTOS.h"
#include "event_groups.h"
#include "Timers.h"
#include "semphr.h"
#include "Timers.h"
#include "queue.h"
#include "Usart.h"
#include "Task.h"
#include "led.h"
#include "key.h"


TaskHandle_t MyTaskHandler;//主任务句柄

TaskHandle_t LED_TASK_Handler;//LED任务句柄

TaskHandle_t KEY_TASK_Handler;       //LED任务句柄

void MyTask(void *pvParameters);     //声明启动函数

void LED_TASK(void *pvParameters);   //声明LED任务函数

void KEY_TASK(void *pvParameters);   //声明KEY任务函数

TimerHandle_t Timer1_Handle =NULL;   //软件定时器句柄1

TimerHandle_t Timer2_Handle =NULL;   //软件定时器句柄2

static void Timer1_Callback(void* parameter);  //软件定时器1回调函数
static void Timer2_Callback(void* parameter);  //软件定时器2回调函数


void Start_Task(void)
{
	xTaskCreate(MyTask,"MyTask",128,NULL,1,&MyTaskHandler);//动态方法创建任务
	vTaskStartScheduler();//启动任务调动		
}

void MyTask(void *pvParameters)           //开始创建任务函数
{	
	taskENTER_CRITICAL();           //进入临界区
	
	//创建定时器1
	Timer1_Handle=xTimerCreate("AutoReloadTimer",1000,pdTRUE,(void*)1,Timer1_Callback);
	
	//创建定时器2
	Timer2_Handle=xTimerCreate("OneShotTimer",5000,pdFALSE,(void*)2,Timer2_Callback);
	
	xTaskCreate(LED_TASK,"LED_TASK",50,NULL,3,&LED_TASK_Handler);//动态方法创建LED任务
	
	xTaskCreate(KEY_TASK,"KEY_TASK",50,NULL,4,&KEY_TASK_Handler);//动态方法创建LED任务

  vTaskDelete(MyTaskHandler);    //删除开始任务
	
	if(Timer1_Handle != NULL) 
	{
		xTimerStart(Timer1_Handle,0);	//开启定时器1
	}
	
	if(Timer2_Handle != NULL) 
	{
		xTimerStart(Timer2_Handle,0);	//开启定时器2
	}
	
	taskEXIT_CRITICAL();           //退出临界区
}

void LED_TASK(void *pvParameters)
{
	while(1)
	{
			vTaskDelay(200);
	}
	
}

void KEY_TASK(void *pvParameters)
{
	while(1)
	{			
		  vTaskDelay(200);
	}
	
}

//定时器1回调函数
//软件定时器不要调用阻塞函数,也不要进行死循环,应快进快出
void Timer1_Callback(void* parameter)
{		
	LED=!LED;
}

//定时器2回调函数
//软件定时器不要调用阻塞函数,也不要进行死循环,应快进快出
void Timer2_Callback(void* parameter)
{		
//	LED=!LED;
}

这个代码就是创建两个软件定时器和两个任务。然后在任务阻塞的时候调用软件定时器,然后进入定时器的回调函数。

总结

以上仅是本人的观点,如有不足,欢迎指出!

相关推荐
2023自学中2 小时前
Linux 内核中的 start_kernel() 函数内部:流程图与总结
linux·嵌入式硬件·uboot
szxinmai主板定制专家2 小时前
RK3588 8个USB工控解决方案,适用于机器视觉,工业互联等
arm开发·人工智能·fpga开发
炸膛坦客2 小时前
FreeRTOS 学习:(二十八)任务调度器 + 启动第一个任务(了解)
stm32·单片机·操作系统·freertos
2301_816997884 小时前
Word版本介绍与选择
c#·word·xhtml
rosir_zhong4 小时前
嵌入式开发中FIFO buffer的使用
单片机·嵌入式硬件
广药门徒4 小时前
PADS同网络相邻引脚怎么走出粗线 FPC 电源布线如何布出粗线
嵌入式硬件
炸膛坦客5 小时前
FreeRTOS 学习:(二十七)死等延时函数会对任务调度产生什么影响
stm32·操作系统·freertos
A星空1236 小时前
二、交叉编译工具链(arm-linux-gnueabihf-gcc)安装与验证,搭建 TFTP+NFS 服务,调试开发板网络连通性;
linux·c++·驱动开发·单片机·嵌入式硬件
z20348315206 小时前
Keil界面优化配置,快捷键格式化配置,警告屏蔽
单片机