引言
在当今嵌入式系统开发中,STM32与FreeRTOS的结合应用极为广泛。本文将深入探讨如何在STM32上创建FreeRTOS任务,助力开发者高效构建多任务系统。
搭建Free-RTOS操作系统原因
在嵌入式领域开发中,随着应用复杂度的提升,单线程的程序已难满足需求。STM32作为高性能的微处理器,搭配上FreeRTOS操作系统,能够实现多任务并行处理,有效提升系统的效率及响应速度。我们都知道,我们一般写的程序都是一行一行执行下来,这样程序的利用率不高,而且至始至终只能干一件事,不能同时执行多个程序。我认为这就是裸机开发的最致命的缺点。
任务创建
在任务创建之前,要确保含有该Free-RTOS操作系统的相关文件并且移植到自己的编译器中。
第一步,创建任务句柄
TaskHandle_t MyTaskHandler;//任务句柄
TaskHandle_t是一个类型定义,用于储存任务的句柄,它是一个指向任务控制块(Task Control Block,TCB)的指针。MyTaskHandler是任务句柄,可自定义。
第二步,创建起始任务函数
void Start_Task(void)
{
xTaskCreate(MyTask,"MyTask",128,NULL,1,&MyTaskHandler);//动态方法创建任务
vTaskStartScheduler();//启动任务调动
}
xTaskCreate是FreeRTOS中用于创建任务的函数,它允许用户在运行时动态创建任务,并将任务的控制信息储存在任务控制块(TCB)中,其函数原型如下:
BaseType_t xTaskCreate(
TaskFunction_t pxTaskCode, // 任务的入口函数,必须是一个无限循环
const char * const pcName, // 任务的名称,用于调试目的
const uint16_t usStackDepth, // 任务堆栈的大小,以字为单位
void * const pvParameters, // 传递给任务函数的参数
UBaseType_t uxPriority, // 任务的优先级
TaskHandle_t * const pxCreatedTask // 任务句柄的指针,用于存储创建的任务的句柄
);
BaseType_t 是FreeRTOS中的一个基本数据类型,在FreeRTOS的相关文件中被定义为typedef long BaseType_t,显然一个32位有符号整数,有些不同版本可能会有不同,感兴趣的可自行查看。它通常被用在函数的返回值中,pdPASS表示成功,pdFALL表示失败。
第三步,启动任务调度器
vTaskStartScheduler();//启动任务调动
在调用vTaskStartScheduler()之前,虽然可以创建任务,但该任务不会执行,只有调用该函数后,任务调度器才开始工作。一旦调用该函数,程序的控制权就会交给调度器,调度器会根据任务的优先级和状态来调度任务。(注:vTaskStartScheduler()函数只能被调用一次,如果多次调用,可能会导致错误)。
第四步,主函数调用起始任务函数
int main(void)
{
KEY_Init();//初始化按键
LED_Init();//初始化LED
OLED_Init(); //初始化oled屏幕
SysTick_Init(72); //初始化定时器
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
Usart1_Init(115200);//串口初始化
Start_Task();//调用起始任务
while(1);
}
第五步,在起始任务函数中创建任务函数
void MyTask(void *arg) //开始创建任务函数
{
taskENTER_CRITICAL(); //进入临界区
xTaskCreate(MyTask1,"MyTask1",50,NULL,2,&MyTask1Handler);//动态方法创建任务1vTaskDelete(MyTaskHandler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
首先,创建任务1也是要先创建任务句柄。其次,使用taskENTER_CRITICAL()进入临界区函数和 taskEXIT_CRITICAL()退出临界区函数,是为了防止在任务创建的过程中被其他任务打断,,确保任务创建成功。
第六步,任务的具体实现
void MyTask1(void *arg) //任务2函数体
{
while(1)
{
OLED_ShowString(1,1,"Runing Task2");//oled屏幕显示
GPIO_ResetBits(GPIOC,GPIO_Pin_15);//点灯
delay_ms(1000); //程序延迟1s
GPIO_SetBits(GPIOC,GPIO_Pin_15);//灭灯
vTaskDelay(300); //程序阻塞300ms
}
}
重点来说一下delay_ms(1000)函数与vTaskDelay(300)函数,首先要明白一点,STM32单片机使用的FreeRTOS操作系统的时钟通常都是由systick滴答定时器提供的。所以delay_ms()函数它是一个阻塞式延时函数,什么是阻塞式?就是程序在执行期间无法运行其它函数,直到阻塞结束才继续往下运行。而vTaskDelay(),可以说是非阻塞式函数,也可以说是阻塞式函数,当执行到该函数时,该任务会被挂起,转而去执行次优先级的任务函数。它只是阻塞当前的任务,而不会阻塞整个系统。
七,完整示例代码
首先在main.c中调用起始任务函数
#include "myfreertos.h"
#include "stm32f10x.h" // Device header
#include "SysTick.h"
#include "Usart.h"
#include "oled.h"
#include "led.h"
#include "key.h"
int main(void)
{
KEY_Init();//初始化按键
LED_Init();//初始化LED
OLED_Init(); //初始化oled屏幕
SysTick_Init(72); //初始化定时器
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
Usart1_Init(115200);
Start_Task();//调用任务
while(1);
}
在myfreetros.c中实现具体任务
#include "myfreertos.h"
#include "FreeRTOS.h"
#include "SysTick.h"
#include "Usart.h"
#include "oled.h"
#include "Task.h"
#include "led.h"
#include "key.h"
TaskHandle_t MyTaskHandler;//任务句柄
TaskHandle_t MyTask1Handler;//任务1句柄
void MyTask(void *pvParameters); //声明启动函数
void Start_Task(void)
{
xTaskCreate(MyTask,"MyTask",128,NULL,1,&MyTaskHandler);//动态方法创建任务
vTaskStartScheduler();//启动任务调动
}
void MyTask(void *arg) //开始创建任务函数
{
taskENTER_CRITICAL(); //进入临界区
xTaskCreate(MyTask1,"MyTask1",50,NULL,2,&MyTask1Handler);//动态方法创建任务1
vTaskDelete(MyTaskHandler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
void MyTask1(void *arg) //任务1函数体
{
while(1)
{
OLED_ShowString(1,1,"Runing Task1");
GPIO_ResetBits(GPIOC,GPIO_Pin_13);
vTaskDelay(300);
GPIO_SetBits(GPIOC,GPIO_Pin_13);
vTaskDelay(900);
}
}
总结
本文简单介绍了在STM32微控制器上使用FreeRTOS操作系统创建任务的方法,包括任务函数定义、任务创建函数调用及任务优先级设置等内容,帮助开发者快速掌握STM32下FreeRTOS任务创建的关键步骤。