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;
}

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

总结

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

相关推荐
@晓凡6 小时前
keil5找不到下载器
stm32
Devlab6 小时前
LVGL设计大师——网页版anyui来了!!
嵌入式硬件·物联网·低代码·ui·iot
波波0077 小时前
C# 15 重磅新特性: 带标签 break 与 continue:重新定义嵌套循环控制流
服务器·前端·c#
Dr.kangder9 小时前
嵌入式面试总结(十五)——异常处理
单片机·面试·职场和发展·系统架构·嵌入式
wujian831110 小时前
怎么用千问生成word文档:从「格式崩」到「一键过」,AI导出鸭打通最后半厘米
人工智能·ai·c#·word·豆包·deepseek·ai导出鸭
zlinear数据采集卡11 小时前
D223上位机C#开发实战:从帧解析到波形显示的完整实现
开发语言·arm开发·嵌入式硬件·fpga开发·开源·c#
ACP广源盛1392462567311 小时前
蚂蚁百灵 Ling‑3.0‑flash 开源 + 昇腾 0‑Day 原生适配@ACP#GSV9001E 在国产算力矩阵中的机会与落地场景
大数据·人工智能·分布式·单片机·嵌入式硬件
唐青枫13 小时前
别把 ImmutableArray 当成只读 List:C#.NET 不可变数组深入详解
c#·.net
Rocktech_ruixun13 小时前
机器人端侧大模型部署对主板有哪些要求?瑞迅主控板分级方案对比
人工智能·嵌入式硬件·机器人