第七章:FreeRTOS动态任务创建

第六章:freertosconfig.h核心函数详解(官方最新版,附FreeRTOSconfig.h归类完的完整中文注释)

STM32上创建简单FreeRTOS程序详解

前言

本文将详细讲解如何在STM32平台上创建一个简单的FreeRTOS程序,并通过一个LED闪烁任务来验证程序的正确运行。整个过程将按照步骤一步一步展开,确保即使是初学者也能轻松上手。

源码获取:本教程相关的工程文件已上传至GitHub,您可以通过以下链接获取完整代码:
https://github.com/Despacito0o/FreeRTOS.git

开发环境准备

  • Keil MDK 5.42a 或更高版本
  • STM32F103系列开发板(本教程使用STM32F103C8)
  • ST-Link调试器(如使用实际硬件)

具体步骤

1. 复制工程模板

首先,我们需要复制之前创建好的工程模板,并命名为003。这样做是为了保留原始工程,同时在新的工程上进行FreeRTOS程序的开发,避免对原有工程造成破坏。使用模板也能确保基础的STM32配置已经完成,省去了从零开始的繁琐步骤。

提示:如果您直接从GitHub克隆了项目,可以在Despacito/002目录中找到基础工程模板。

工程模板复制

2. 清理不必要的头文件

打开工程文件,删除不需要的头文件。这一步是为了精简工程,删除与我们当前FreeRTOS程序开发无关的组件,减少可能的冲突和编译错误。同时,一个干净的工程结构也有助于我们更清晰地理解FreeRTOS的组成部分和我们的程序结构。

删除不需要的头文件

3. 修改FreeRTOS配置文件

接下来,我们需要修改freertosconfig.h文件,添加三个关键中断处理函数的定义。这是FreeRTOS程序开发中的重要一步。FreeRTOS需要使用系统的一些中断处理,包括:

  • PendSV_Handler:用于任务切换
  • SVC_Handler:用于系统服务调用
  • SysTick_Handler:用于系统滴答计时,是FreeRTOS进行任务调度的时间基准

通过这样的定义,我们告诉编译器FreeRTOS的中断处理函数将使用系统默认的同名函数。

#define xPortPendSVHandler 	PendSV_Handler
#define vPortSVCHandler 	SVC_Handler
#define xPortSysTickHandler SysTick_Handler

修改FreeRTOS配置

4. 注释原有中断处理函数

由于上一步中我们已经将三个关键中断交给了FreeRTOS处理,所以需要在STM32的中断文件stm32f10x_it.c中注释掉原本的这三个中断处理函数。如果不注释,将会导致函数重定义错误,因为FreeRTOS和STM32标准库都定义了相同名称的函数。

注释中断处理函数1
注释中断处理函数2

5. 编写测试程序

完成基础配置后,我们需要编写一个测试程序来验证FreeRTOS是否正常工作。在嵌入式系统开发中,LED闪烁通常是最简单且直观的测试方式,也被称为"嵌入式世界的Hello World"。

5.1 定义任务句柄

首先定义一个任务句柄,它是FreeRTOS中用于操作和管理任务的标识符。

TaskHandle_t myTaskHandler;
5.2 创建任务

使用xTaskCreate函数创建一个任务,这个函数有六个参数:

  • 任务函数名称:myTask,指定任务的入口函数
  • 任务名称:"myTask",一个字符串,用于调试识别
  • 堆栈大小:128,指定任务所需的堆栈空间大小
  • 传入参数:NULL,可以传递给任务的参数
  • 优先级:2,任务优先级,数字越大优先级越高
  • 任务句柄:&myTaskHandler,用于返回任务的句柄
xTaskCreate(myTask, "myTask", 128, NULL, 2, &myTaskHandler);
5.3 启动任务调度器

创建任务后,需要启动FreeRTOS的任务调度器,使系统开始运行并根据任务优先级进行调度。

vTaskStartScheduler();
5.4 创建任务函数

定义任务函数,这是任务执行的入口。FreeRTOS任务函数通常是一个无限循环,因为任务终止会导致系统出错。

void myTask(void *arg)
{
    while(1)
    {
        // 任务代码将在这里执行
    }
}
5.5 实现LED闪烁功能

在任务函数中添加LED翻转操作,使LED以固定时间间隔闪烁,这样我们可以直观地观察任务是否正常执行。

void myTask(void *arg)
{
    while(1)
    {
        GPIO_ResetBits(GPIOC, GPIO_Pin_13);  // LED亮
        vTaskDelay(500);                     // 延时500个时钟节拍,相当于500ms
        GPIO_SetBits(GPIOC, GPIO_Pin_13);    // LED灭
        vTaskDelay(500);                     // 再延时500ms
    }
}

这里的vTaskDelay函数是FreeRTOS提供的延时函数,它会使当前任务进入阻塞状态,并允许其他任务运行,实现了真正的多任务操作。

代码说明:与传统的延时函数不同,vTaskDelay不会占用CPU资源。当任务调用此函数后,FreeRTOS会将该任务置于阻塞状态,并切换到其他就绪任务执行,直到指定的延时时间到达。

6. 配置调试环境

为了验证我们的程序,需要正确配置调试环境。点击IDE中的"魔法棒"(Debug按钮),选择"use Simulator",并设置适合STM32F103C8芯片的调试参数:

  • Dialog DLL:DARMSTM.DLL
  • Parameter:-PSTM32F103C8

这些设置告诉调试器使用STM32设备模拟器,并指定了正确的芯片型号,确保仿真环境与实际硬件一致。

配置调试环境

7. 开始调试

配置完成后,点击调试按钮开始调试过程。此时,调试器会加载程序到虚拟的STM32芯片中,并准备执行。

启动调试

8. 打开逻辑分析仪

为了直观地观察LED的状态变化,我们需要打开IDE中的逻辑分析仪工具。逻辑分析仪可以实时显示GPIO引脚状态的变化,帮助我们确认任务是否按预期执行。

打开逻辑分析仪

9. 配置逻辑分析仪

点击逻辑分析仪左上角的"Setup…"按钮,开始配置要监视的引脚。

配置逻辑分析仪

10. 添加监视变量

在弹出的配置窗口中,添加要监视的GPIO引脚。在本例中,我们使用的是PC13引脚(GPIOC的第13号引脚),这通常是STM32F103C8板载LED的控制引脚。输入"portc.13"后点击空白处确认添加。

添加监视变量

12. 设置显示类型

继续点击刚刚添加的变量(PC13),将显示类型(Display Type)改为"bit",这样可以更清晰地看到引脚的高低电平变化。完成设置后点击"Close"关闭配置窗口。

设置显示类型

13. 运行程序

配置完成后,点击"Run"按钮开始运行程序。此时,FreeRTOS调度器会启动,并开始执行我们创建的LED闪烁任务。

运行程序

14. 观察结果

如果一切正常,我们应该能在逻辑分析仪中观察到PC13引脚的电平状态按照500ms的间隔周期性变化,这表明我们的FreeRTOS任务已经成功创建并正常执行。这也证明了我们的简单FreeRTOS程序已经成功运行。

观察结果

常见问题与解决方案

1. 编译报错:函数重定义

问题:编译时出现PendSV_Handler、SVC_Handler或SysTick_Handler函数重定义错误。
解决方案:检查是否正确注释了stm32f10x_it.c中的对应函数。

2. LED不闪烁

问题:程序运行后,LED没有按预期闪烁。
解决方案

  • 检查GPIO配置是否正确
  • 确认延时时间是否合适
  • 检查任务是否成功创建(可以使用任务状态查看函数)

3. 堆栈溢出

问题:运行时出现堆栈溢出错误。
解决方案:增加任务堆栈大小,修改xTaskCreate中的堆栈参数(第三个参数)。

本节课main.c代码

#include "stm32f10x.h"                  // Device header
#include "FreeRTOS.h"
#include "task.h"

TaskHandle_t myTaskHandler;

void myTask(void *arg)
{
		while(1)
		{
		  GPIO_ResetBits(GPIOC, GPIO_Pin_13);
			vTaskDelay(500);
			GPIO_SetBits(GPIOC, GPIO_Pin_13);
			vTaskDelay(500);
		}
}

int main(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);	//开启GPIOC的时钟
															//使用各个外设前必须开启时钟,否则对外设的操作无效
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;					//定义结构体变量
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;		//GPIO模式,赋值为推挽输出模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;				//GPIO引脚,赋值为第13号引脚
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		//GPIO速度,赋值为50MHz
	
	GPIO_Init(GPIOC, &GPIO_InitStructure);					//将赋值后的构体变量传递给GPIO_Init函数
															//函数内部会自动根据结构体的参数配置相应寄存器
															//实现GPIOC的初始化
	
	/*设置GPIO引脚的高低电平*/
	/*若不设置GPIO引脚的电平,则在GPIO初始化为推挽输出后,指定引脚默认输出低电平*/
//	GPIO_SetBits(GPIOC, GPIO_Pin_13);						//将PC13引脚设置为高电平
	GPIO_ResetBits(GPIOC, GPIO_Pin_13);						//将PC13引脚设置为低电平
	
	xTaskCreate(myTask,"myTask",128,NULL,2,&myTaskHandler);
	vTaskStartScheduler();
	while (1)
	{
		
	}
}

进阶拓展

多任务示例

以下是一个创建两个任务的示例,一个控制LED闪烁,另一个在串口输出信息:

TaskHandle_t ledTaskHandler;
TaskHandle_t uartTaskHandler;

void ledTask(void *arg)
{
    while(1)
    {
        GPIO_ResetBits(GPIOC, GPIO_Pin_13);
        vTaskDelay(500);
        GPIO_SetBits(GPIOC, GPIO_Pin_13);
        vTaskDelay(500);
    }
}

void uartTask(void *arg)
{
    while(1)
    {
        printf("FreeRTOS running...\r\n");
        vTaskDelay(1000);
    }
}

int main(void)
{
    // 初始化外设...
    
    // 创建任务
    xTaskCreate(ledTask, "LED", 128, NULL, 2, &ledTaskHandler);
    xTaskCreate(uartTask, "UART", 128, NULL, 1, &uartTaskHandler);
    
    // 启动调度器
    vTaskStartScheduler();
    
    // 正常情况下不会执行到这里
    while(1);
}

总结

通过以上步骤,我们成功地在STM32平台上创建了一个简单的FreeRTOS程序,并通过一个LED闪烁任务验证了程序的正确运行。FreeRTOS是一个功能强大的实时操作系统,它为嵌入式开发提供了多任务处理、任务间通信、资源管理等重要功能,能够极大地简化复杂嵌入式应用的开发。

在接下来的学习中,我们将进一步探索FreeRTOS的高级特性,如信号量、消息队列、任务通知等,以便开发更复杂、更强大的嵌入式应用。

更多资源

要深入学习FreeRTOS,可以参考以下资源:

  1. GitHub项目完整代码
  2. FreeRTOS官方文档
  3. STM32参考手册

参考文献

  1. FreeRTOS官方文档:https://www.freertos.org/Documentation/RTOS_book.html
  2. STM32标准外设库用户手册
  3. FreeRTOS源码解析

如有疑问,欢迎在评论区留言交流!您也可以在GitHub上提交Issue或Pull Request,共同改进这个项目。
第八章:FreeRTOS静态任务创建(2025.4.9巨详细)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Despacito0o

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值