> 文档中心 > 正点原子FreeRTOS

正点原子FreeRTOS

目录

  • 第一章FreeRTOS 简介
    • 1.1 初识FreeRTOS
      • 1.1.1 什么是FreeRTOS?
      • 1.1.2 为什么选择FreeRTOS?
      • 1.1.3 FreeRTOS 特点
      • 1.1.4 商业许可
    • 1.3 FreeRTOS 源码初探
      • 1.3.1 FreeRTOS 源码下载
      • 1.3.2 FreeRTOS 文件预览
  • 第二章FreeRTOS 移植
    • 2.1 准备工作
      • 2.1.1 准备基础工程
      • 2.1.2 FreeRTOS 系统源码
    • 2.2 FreeRTOS 移植
      • 2.2.1 向工程中添加相应文件
      • 2.2.2 修改SYSTEM 文件
    • 2.3 移植验证实验
      • 2.3.1 实验程序设计
      • 2.3.2 实验程序运行结果分析

第一章FreeRTOS 简介

1.1 初识FreeRTOS

1.1.1 什么是FreeRTOS?

我们看一下FreeRTOS 的名字,可以分为两部分:Free 和RTOS,Free 就是免费的、自由的、不受约束的意思,RTOS 全称是Real Time Operating System,中文名就是实时操作系统。RTOS 不是指某一个确定的系统,而是指一类系统。比如UCOS,FreeRTOS,RTX,RT-Thread 等这些都是RTOS 类操作系统。

操作系统允许多个任务同时运行,这个叫做多任务,实际上,一个处理器核心在某一时刻只能运行一个任务。操作系统中任务调度器的责任就是决定在某一时刻究竟运行哪个任务,任务调度在各个任务之间的切换非常快!这就给人们造成了同一时刻有多个任务同时运行的错觉

操作系统的分类方式可以由任务调度器的工作方式决定,比如有的操作系统给每个任务分配同样的运行时间,时间到了就轮到下一个任务,Unix 操作系统就是这样的。RTOS 的任务调度器被设计为可预测的,而这正是嵌入式实时操作系统所需要的,实时环境中要求操作系统必须对某一个事件做出实时的响应,因此系统任务调度器的行为必须是可预测的。像FreeRTOS 这
种传统的RTOS 类操作系统是由用户给每个任务分配一个任务优先级,任务调度器就可以根据此任务优先级来决定下一刻应该运行哪个任务。

FreeRTOS 是RTOS 系统的一种,FreeRTOS 十分的小巧,可以在资源有限的微控制器中运行,当然了,FreeRTOS 不仅局限于在微控制器中使用。但从文件数量上来看FreeRTOS 要比UCOSII 和UCOSIII 小的多。

1.1.2 为什么选择FreeRTOS?

1、FreeRTOS 免费。
2、许多其他半导体厂商产品的SDK 包就使用FreeRTOS 作为其操作系统,尤其是WIFI、蓝牙这些带协议栈的芯片或模块。
3、许多软件厂商也使用FreeRTOS 做本公司软件的操作系统,比如著名的TouchGFX,其所有的例程都是基于FreeRTOS 操作系统的。ST 公司的所有要使用到RTOS 系统的例程也均采用了FreeRTOS,由此可见免费的力量啊!
3、简单,FreeRTOS 的文件数量很少,和UCOS系统相比要少很多!
4、文档相对齐全,在FreeRTOS 的官网(www.freertos.org)上可以找到所需的文档和源码,但是所有的文档都是英文版本的,而且下载pdf 文档的时候是要收费的。
5、FreeRTOS 被移植到了很多不同的微处理器上,比如我们使用的STM32,F1、F3、F4 和最新的F7 都有移植,这个极大的方便了我们学习和使用。
6、社会占有量很高,EEtimes 统计的2015 年RTOS 系统占有量中FreeRTOS 已经跃升至第一位。

1.1.3 FreeRTOS 特点

FreeRTOS 是一个可裁剪的小型RTOS 系统,其特点包括:

●FreeRTOS 的内核支持抢占式,合作式和时间片调度。
●SafeRTOS 衍生自FreeRTOS,SafeRTOS 在代码完整性上相比FreeRTOS 更胜一筹。
●提供了一个用于低功耗的Tickless 模式。
●系统的组件在创建时可以选择动态或者静态的RAM,比如任务、消息队列、信号量、软件定时器等等。
●已经在超过30 种架构的芯片上进行了移植。
●FreeRTOS-MPU 支持Corex-M 系列中的MPU 单元,如STM32F103。
●FreeRTOS 系统简单、小巧、易用,通常情况下内核占用4k-9k 字节的空间。
●高可移植性,代码主要C 语言编写。
●支持实时任务和协程(co-routines 也有称为合作式、协同程序,本教程均成为协程)。
●任务与任务、任务与中断之间可以使用任务通知、消息队列、二值信号量、数值型信号量、递归互斥信号量和互斥信号量进行通信和同步。
●创新的事件组(或者事件标志)。
●具有优先级继承特性的互斥信号量。
●高效的软件定时器。
●强大的跟踪执行功能。
●堆栈溢出检测功能。
●任务数量不限。
●任务优先级不限。

1.1.4 商业许可

FreeRTOS 衍生出来了另外两个系统:OpenRTOS 和SafeTROS,FreeRTOS 开源许可协议允许在商业应用中使用FreeRTOS 系统,并且不需要公开你的私有代码。如果有以下需求的话可以使用OpenRTOS:
1、你不能接受FreeRTOS 的开源许可协议条件,具体参见表1.4.1。
2、你需要技术支持。
3、你想获得开发帮助
4、你需要法律保护或者其他的保护。

使用OpenRTOS 的话需要遵守商业协议,FreeRTOS 的开源许可和OpenRTOS 的商业许可区别如表1.4.1 所示:
正点原子FreeRTOS
OpenRTOS 是FreeRTOS 的商业化版本,OpenRTOS 的商业许可协议不包含任何GPL 条款。

还有另外一个系统:SafeRTOS,SafeRTOS 看名字有个Safe,安全的意思!SafeRTOS 也是FreeRTOS 的衍生版本,只是SafeRTOS 过了一些安全认证,比如IEC61508。

1.3 FreeRTOS 源码初探

1.3.1 FreeRTOS 源码下载

正点原子FreeRTOS
正点原子FreeRTOS

1.3.2 FreeRTOS 文件预览

从图1.3.1.5 可以看出FreeRTOS 源码中有两个文件夹,4 个HTML 格式的网页和一个txt 文档,HTML 网页和txt 文档就不用介绍了,看名字就知道是什么东西了,重点在于上面那两个文件夹:FreeRTOS 和FreeRTOS-Plus,这两个文件夹里面的东西就是FreeRTOS 的源码。我们知道苹果从Iphone6 以后分为了Iphone6 和Iphone6 Plus两个版本,区别就是Plus 比普通的功能多一点,配置强大一点。现在FreeRTOS 也这么分,是不是Plus 版本比FreeRTOS 功能强一点啊,强大到哪里?是不是源码都不同了呀?

1、FreeRTOS 文件夹
打开FreeRTOS 文件夹,如图1.3.2.1 所示:
正点原子FreeRTOS
图1.3.2.1 中有三个文件夹,Demo、License 和Source,从名字上就可以很容易的得出他们都是些什么。

●Demo 文件夹
Demo 文件夹里面就是FreeRTOS 的相关例程,打开以后如图1.3.2.2 所示:
正点原子FreeRTOS
可以看出FreeRTOS 针对不同的MCU 提供了非常多的Demo,其中就有ST 的F1、F4 和F7 的相关例程,这对于我们学习来说是非常友好的,我们在移植的时候就会参考这些例程。

●License 文件夹
这个文件夹里面就是相关的许可信息,要用FreeRTOS 做产品的得仔细看看,尤其是要出口的产品。

●Source 文件夹
看名字就知道了,这个就是FreeRTOS 的本尊了,打开后如图1.3.2.3 所示:
正点原子FreeRTOS
图1.3.2.3 就是FreeRTOS 的源码文件,也是我们以后打交道的,可以看出,相比于UCOS来说FreeRTOS 的文件非常少!include 文件夹是一些头文件,移植的时候是需要的,下面的这些.C 文件就是FreeRTOS 的源码文件了,移植的时候肯定也是需要的。

重点来看一下portable这个文件夹,我们知道FreeRTOS 是个系统,归根结底就是个纯软件的东西,它是怎么和硬件联系在一起的呢?软件到硬件中间必须有一个桥梁,portable 文件夹里面的东西就是FreeRTOS系统和具体的硬件之间的连接桥梁!不同的编译环境,不同的MCU,其桥梁应该是不同的,打开portable 文件夹,如图1.3.2.4 所示:
正点原子FreeRTOS
从图1.3.2.4 中可以看出FreeRTOS 针对不同的编译环境和MCU 都有不同的“桥梁”,我们这里就以MDK 编译环境下的STM32F103 为例。MemMang 这个文件夹是跟内存管理相关的,我们移植的时候是必须的,具体内容我们后面会专门有一章来讲解。Keil 文件夹里面的东西肯定也是必须的,但是我们打开Keil 文件夹以后里面只有一个文件:See-also-the-RVDS-directory.txt。
这个txt 文件是什么鬼?别急嘛!看文件名字“See-also-the-RVDS-directory”,意思就是参考RVDS文件夹里面的东西!哎,好吧,再打开RVDS 文件夹,如图1.3.2.5 所示:
正点原子FreeRTOS
RVDS 文件夹针对不同的架构的MCU 做了详细的分类,STM32F103 就参考ARM_CM3,打开ARM_CM3 文件夹,如图1.3.2.6 所示:
正点原子FreeRTOS
ARM_CM3 有两个文件,这两个文件就是我们移植的时候所需要的!

2、FreeRTOS-Plus 文件夹
上面我们分析完了FreeRTOS 文件夹,接下来看一下FreeRTOS-Plus,打开以后如图1.3.2.7所示:
正点原子FreeRTOS
同样,FreeRTOS-Plus 也有Demo 和Source,Demo 就不看了,肯定是一些例程。我们看一下Source,打开以后如图1.3.2.8 所示:
正点原子FreeRTOS
可以看出,FreeRTOS-Plus 中的源码其实并不是FreeRTOS 系统的源码,而是在FreeRTOS系统上另外增加的一些功能代码,比如CLI、FAT、Trace 等等。就系统本身而言,和FreeRTOS里面的一模一样的,所以我们如果只是学习FreeRTOS 这个系统的话,FreeRTOS-Plus 就没必要看了。

第二章FreeRTOS 移植

以ALIENTEK 的STM32F103 开发板为例。

2.1 准备工作

2.1.1 准备基础工程

要移植FreeRTOS,肯定需要一个基础工程,基础工程越简单越好,这里我们就用基础例程中的跑马灯实验来作为基础工程。

2.1.2 FreeRTOS 系统源码

FreeRTOS 系统源码在上一章已经详细的讲解过如何获取了,这里我们会将FreeRTOS 的系统源码放到开发板光盘中去,路径为:6,软件资料->14,FreeRTOS 学习资料->FreeRTOS 源码。

2.2 FreeRTOS 移植

2.2.1 向工程中添加相应文件

1、添加FreeRTOS 源码
在基础工程中新建一个名为FreeRTOS 的文件夹,如图2.2.1.1 所示:
正点原子FreeRTOS
创建FreeRTOS 文件夹以后就可以将FreeRTOS 的源码添加到这个文件夹中,添加完以后如图2.2.1.2 所示:

正点原子FreeRTOS

在1.3.2 小节中详细的讲解过portable 文件夹,我们只需要留下keil、MemMang 和RVDS这三个文件夹,其他的都可以删除掉,完成以后如图2.2.1.3 所示:

正点原子FreeRTOS

2、向工程分组中添加文件
打开基础工程,新建分组FreeRTOS_CORE 和FreeRTOS_PORTABLE,然后向这两个分组中添加文件,如图2.2.1.4 所示:
正点原子FreeRTOS
分组FreeRTOS_CORE 中的文件在什么地方就不说了,打开FreeRTOS 源码一目了然。重点来说说FreeRTOS_PORTABLE 分组中的port.c 和heap_4.c 是怎么来的,port.c 是RVDS 文件夹下的ARM_CM3 中的文件,因为STM32F103 是Cortex-M3 内核的,因此要选择ARM_CM3中的port.c 文件。heap_4.c 是MemMang 文件夹中的,前面说了MemMang 是跟内存管理相关
的,里面有5 个c 文件:heap_1.c、heap_2.c、heap_3.c、heap_4.c 和heap_5.c。这5 个c 文件是五种不同的内存管理方法,都可以用来作为FreeRTOS 的内存管理文件,只是它们的实现原理不同,各有利弊。这里我们选择heap_4.c,至于原因,后面会有一章节专门来讲解FreeRTOS 的内存管理,到时候大家就知道原因了。这里就先选择heap_4.c,毕竟本章的重点是FreeRTOS 的移植。

3、添加相应的头文件路径
添加完FreeRTOS 源码中的C 文件以后还要添加FreeRTOS 源码的头文件路径,头文件路径如图2.2.1.5 所示:
正点原子FreeRTOS头文件路径添加完成以后编译一下,看看有没有什么错误,结果会发现提示打不开“FreeRTOSConfig.h”这个文件,如图2.2.1.6 所示:正点原子FreeRTOS
这是因为缺少FreeRTOSConfig.h 文件,这个文件在哪里找呢?你可以自己创建,显然这不是一个明智的做法。我们可以找找FreeRTOS 的官方移植工程中会不会有这个文件,打开FreeRTOS 针对STM32F103 的移植工程文件,文件夹是CORTEX_STM32F103_Keil,打开以后如图2.2.1.7 所示
正点原子FreeRTOS
果然!官方的移植工程中有这个文件,我们可以使用这个文件,但是建议大家使用我们例程中的FreeRTOSConf.h 文件,这个文件是FreeRTOS 的系统配置文件,不同的平台其配置不同,但是我们提供的例程中的这个文件肯定是针对ALIENTEK 开发板配置正确的。这个文件复制到什么地方大家可以自行决定,这里我为了方便放到了FreeRTOS 源码中的include 文件夹下。

FreeRTOSConfig.h 是何方神圣?看名字就知道,他是FreeRTOS 的配置文件,一般的操作系统都有裁剪、配置功能,而这些裁剪及配置都是通过一个文件来完成的,基本都是通过宏定义来完成对系统的配置和裁剪的,关于FreeRTOS 的配置文件FreeRTOSConfig.h 后面也会有一章节来详细的讲解。

到这里我们再编译一次,没有错误!如图2.2.1.8 所示:

正点原子FreeRTOS
如果还有错误的话大家自行根据错误类型查找和修改错误!

2.2.2 修改SYSTEM 文件

SYSTEM 文件夹里面的文件一开始是针对UCOS 而编写的,所以如果使用FreeRTOS 的话就需要做相应的修改。本来打算让SYSTEM 文件夹也支持FreeRTOS,但是这样的话会导致SYSTEM 里面的文件太过于复杂,这样非常不利于初学者学习,所以这里就专门针对FreeRTOS修改了SYSTEM 里面的文件。

1、修改sys.h 文件
sys.h 文件修改很简单,在sys.h 文件里面用宏SYSTEM_SUPPORT_OS 来定义是否使用OS,我们使用了FreeRTOS,所以应该将宏SYSTEM_SUPPORT_OS 改为1。

//0,不支持os//1,支持os#define SYSTEM_SUPPORT_OS 1 //定义系统文件夹是否支持OS

2、修改usart.c 文件
usart.c 文件修改也很简单,usart.c 文件有两部分要修改,一个是添加FreeRTOS.h 头文件,默认是添加的UCOS 中的includes.h 头文件,修改以后如下:

//如果使用os,则包括下面的头文件即可.#if SYSTEM_SUPPORT_OS#include "FreeRTOS.h" //os 使用#endif

另外一个就是USART1 的中断服务函数,在使用UCOS 的时候进出中断的时候需要添加OSIntEnter()和OSIntExit(),使用FreeRTOS 的话就不需要了,所以将这两行代码删除掉,修改以后如下:

void USART1_IRQHandler(void) //串口1 中断服务程序{    u8 Res;     if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)     {     Res =USART_ReceiveData(USART1); //读取接收到的数据     if((USART_RX_STA&0x8000)==0) //接收未完成     {      if(USART_RX_STA&0x4000) //接收到了0x0d      {if(Res!=0x0a)USART_RX_STA=0; //接收错误,重新开始else USART_RX_STA|=0x8000; //接收完成了      }      else //还没收到0X0D      {if(Res==0x0d)USART_RX_STA|=0x4000;else{ USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ; USART_RX_STA++; if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;}      }     }     }}

3、修改delay.c 文件
delay.c 文件修改的就比较大了,因为涉及到FreeRTOS 的系统时钟,delay.c 文件里面有4个函数,先来看一下函数SysTick_Handler(),此函数是滴答定时器的中断服务函数,代码如下:

extern void xPortSysTickHandler(void);//systick 中断服务函数,使用OS 时用到void SysTick_Handler(void){if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行{xPortSysTickHandler();}}

FreeRTOS 的心跳就是由滴答定时器产生的,根据FreeRTOS 的系统时钟节拍设置好滴答定时器的周期,这样就会周期触发滴答定时器中断了。在滴答定时器中断服务函数中调用FreeRTOS 的API 函数xPortSysTickHandler()。

delay_init()是用来初始化滴答定时器和延时函数,代码如下:

//初始化延迟函数//SYSTICK 的时钟固定为AHB 时钟,基础例程里面SYSTICK 时钟频率为AHB/8//这里为了兼容FreeRTOS,所以将SYSTICK 的时钟频率改为AHB 的频率!//SYSCLK:系统时钟频率void delay_init(){ u32 reload; SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);//选择外部时钟HCLK fac_us=SystemCoreClock/1000000; //不论是否使用OS,fac_us 都需要使用 reload=SystemCoreClock/1000000; //每秒钟的计数次数单位为M reload*=1000000/configTICK_RATE_HZ; //根据configTICK_RATE_HZ 设定溢出 //时间reload 为24 位寄存器,最大值: //16777216,在72M 下,约合0.233s 左右 fac_ms=1000/configTICK_RATE_HZ; //代表OS 可以延时的最少单位 SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk; //开启SYSTICK 中断 SysTick->LOAD=reload; //每1/configTICK_RATE_HZ 秒中断 //一次 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK}

前面我们说了FreeRTOS 的系统时钟是由滴答定时器提供的,那么肯定要根据FreeRTOS 的系统时钟节拍来初始化滴答定时器了,delay_init()就是来完成这个功能的。FreeRTOS 的系统时钟节拍由宏configTICK_RATE_HZ 来设置,这个值我们可以自由设置,但是一旦设置好以后我们就要根据这个值来初始化滴答定时器,其实就是设置滴答定时器的中断周期。在基础例程中
滴答定时器的时钟频率设置的是AHB 的1/8,这里为了兼容FreeRTOS 将滴答定时器的时钟频率改为了AHB,也就是72MHz!这一点一定要注意!

接下来的三个函数都是延时的,代码如下:

/延时nus//nus:要延时的us 数.//nus:0~204522252(最大值即2^32/fac_us@fac_us=168)void delay_us(u32 nus){ u32 ticks; u32 told,tnow,tcnt=0; u32 reload=SysTick->LOAD; //LOAD 的值 ticks=nus*fac_us; //需要的节拍数 told=SysTick->VAL; //刚进入时的计数器值 while(1) {  tnow=SysTick->VAL;  if(tnow!=told)  {   //这里注意一下SYSTICK 是一个递减的计数器就可以了.   if(tnow<told)tcnt+=told-tnow;   else tcnt+=reload-tnow+told;   told=tnow;   if(tcnt>=ticks)break; //时间超过/等于要延迟的时间,则退出.  } };}//延时nms,会引起任务调度//nms:要延时的ms 数//nms:0~65535void delay_ms(u32 nms){ if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行 {  if(nms>=fac_ms) //延时的时间大于OS 的最少时间周期  {   vTaskDelay(nms/fac_ms); //FreeRTOS 延时  }  nms%=fac_ms; //OS 已经无法提供这么小的延时了,  //采用普通方式延时 } delay_us((u32)(nms*1000)); //普通方式延时}//延时nms,不会引起任务调度//nms:要延时的ms 数void delay_xms(u32 nms){u32 i;for(i=0;i<nms;i++) delay_us(1000); }

delay_us()是us 级延时函数,delay_ms 和delay_xms()都是ms 级的延时函数,delay_us()和delay_xms()不会导致任务切换。delay_ms()其实就是对FreeRTOS 中的延时函数vTaskDelay()的简单封装,所以在使用delay_ms()的时候就会导致任务切换。

delay.c 修改完成以后编译一下,会提示如图2.2.2.1 所示错误:
正点原子FreeRTOS
图2.2.2.1 的错误提示表示在port.c、delay.c 和stm32f10x_it.c 中三个重复定义的函数:SysTick_Handler()、SVC_Handler()和PendSV_Handler(),这三个函数分别为滴答定时器中断服务函数、SVC 中断服务函数和PendSV 中断服务函数,将stm32f10x_it.c 中的三个函数屏蔽掉,如图2.2.2.2 所示:
正点原子FreeRTOS
再次编译代码,应该没有错误了,如果还是错误的话自行根据错误类型修改!至此,SYSTEM文件夹就修改完成了。

2.3 移植验证实验

2.3.1 实验程序设计

1、实验目的
编写简单的FreeRTOS 应用代码,测试FreeRTOS 的移植是否成功。鉴于大家还没正式学习FreeRTOS,可以直接将本实验代码复制粘贴到自己的移植工程中。

2、实验设计
本实验设计四个任务:start_task()、led0_task ()、led1_task ()和float_task(),这四个任务的任务功能如下:

  • start_task():用来创建其他三个任务。
  • led0_task ():控制LED0 的闪烁。
  • led1_task ():控制LED1 的闪烁。
  • float_task():简单的浮点测试任务,用于测试STM32F4 的FPU 是否工作正常。

3、实验工程
FreeRTOS 实验2-1 FreeRTOS 移植实验。

4、实验程序与分析

●任务设置

#include "sys.h"#include "delay.h"#include "usart.h"#include "led.h"#include "FreeRTOS.h"#include "task.h"#define START_TASK_PRIO 1 //任务优先级#define START_STK_SIZE 128 //任务堆栈大小TaskHandle_t StartTask_Handler; //任务句柄void start_task(void *pvParameters); //任务函数#define LED0_TASK_PRIO 2 //任务优先级#define LED0_STK_SIZE 50 //任务堆栈大小TaskHandle_t LED0Task_Handler; //任务句柄void led0_task(void *p_arg); //任务函数#define LED1_TASK_PRIO 3 //任务优先级#define LED1_STK_SIZE 50 //任务堆栈大小TaskHandle_t LED1Task_Handler; //任务句柄void led1_task(void *p_arg); //任务函数

●main()函数

int main(void){NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4delay_init(); //延时函数初始化uart_init(115200); //初始化串口LED_Init(); //初始化LED//创建开始任务xTaskCreate((TaskFunction_t )start_task, //任务函数(const char* )"start_task", //任务名称(uint16_t )START_STK_SIZE, //任务堆栈大小(void* )NULL, //传递给任务函数的参数(UBaseType_t )START_TASK_PRIO, //任务优先级(TaskHandle_t* )&StartTask_Handler); //任务句柄vTaskStartScheduler(); //开启任务调度}

●任务函数

//开始任务任务函数void start_task(void *pvParameters){taskENTER_CRITICAL(); //进入临界区//创建LED0 任务xTaskCreate((TaskFunction_t )led0_task,(const char* )"led0_task",(uint16_t )LED0_STK_SIZE,(void* )NULL,(UBaseType_t )LED0_TASK_PRIO,(TaskHandle_t* )&LED0Task_Handler);//创建LED1 任务xTaskCreate((TaskFunction_t )led1_task,(const char* )"led1_task",(uint16_t )LED1_STK_SIZE,(void* )NULL,(UBaseType_t )LED1_TASK_PRIO,(TaskHandle_t* )&LED1Task_Handler);vTaskDelete(StartTask_Handler); //删除开始任务taskEXIT_CRITICAL(); //退出临界区}//LED0 任务函数void led0_task(void *pvParameters){while(1){LED0=~LED0;vTaskDelay(500);}}//LED1 任务函数void led1_task(void *pvParameters){while(1){LED1=0;vTaskDelay(200);LED1=1;vTaskDelay(800);}}

led0_task()和led1_task()任务很简单,就是让LED0 和LED1 周期性闪烁。

关于具体的函数的调用方法这些不要深究,后面会有详细的讲解!

2.3.2 实验程序运行结果分析

编译并下载代码到STM32F103 开发板中,下载进去以后会看到LED0 和LED1 开始闪烁。

LED0 均匀闪烁,那是因为我们在LED0 的任务代码中设置好的LED0 亮500ms,灭500ms。
LED1 亮的时间短,灭的时间长,这是因为在LED1 的任务代码中设置好的亮200ms,灭800ms。