我们战队使用的是自己组的板子,在这个新赛季我们想尝试一下DJI官方的C板。在使用C板的过程中,我们发现DJI的任务控制使用的是FreeRTOS这个单片机操作系统。而我们使用的是裸机开发。仅是Freertos这个形式上的不同。让我很难将FreeRTOS上的代码,移植到裸机开发上。随着越来越多的RoboMaster战队,使用DJI C板,使用FreeRTOS进行开发。如果我们战队很难融入开源这个大环境的话,将会错失很多交流的机会。
什么是FreeRTOS?
FreeRTOS 是单片机的一种操作系统。操作系统这个东西我们很熟悉,就像是手机的操作系统有 安卓,ios。电脑的操作系统有Windows,Mac。
FreerRTOS是单片机的一种操作系统。FreeRTOS提供了许多方法用以实现多线程(threads)、多作业(task)、互斥锁(mutex)、信号量(semaphore)、和软件计时器(software timer)。
是什么其实不重要,重要的是怎么使用。
我们所使用的FreeRTOS—— CMSIS-RTOS
我们使用的是由STM32CubeMx 自动生成的 FreeRTOS。
ST公司,在原来的FreeRTOS上又进行了一层封装,这层就是CMSIS-RTOS 。CMSIS-RTOS是keil公司对不同RTOS的一种封装结构,可以使不同的RTOS具有相同的接口,以方便今后程序的移植。
如何使用FreeRTOS(使用STM32CubeMX 生成)
利用STM32CubeMX生成
这里我假定你已经对STM32CubeMx具有一定的了解(比如会移植时钟树,会用CubeMX生成代码)。
现在在你已经把前面所有常规的配置设置好之后,下面开始配置FreeRTOS。
首先打开Middleware标签,选中其中的FREERTOS选项,进入FREERTOS的配置页面。
在Mode页面下,选择Interface 的版本,这里选择 CMSIS_V1。CMSIS是由 keil 提供的一套特殊的函数接口,他对freeRTOS的功能函数进行了封装,使其变得更加易用,在使用freeRTOS是,不需要再去直接调用 freeRTOS 的函数,只需要调用 CMSIS 为我们提供的函数即可。
通过以上方式就可以完成freeRTOS的开启,在生成代码之后,freeRTOS会自动被移植到工程中。接着在Configuration页面下进行freeRTOS的配置。
在页面中,可以配置 freeRTOS 的一些重要的属性,包括是否支持抢占机制,freeRTOS 的 系统时钟速率,最大优先级数量,最小任务栈尺寸,最大任务名称长度等。
名称 | 功能 |
USE_PREEMPTION | 是否支持抢占机制,支持则设为 Enable |
TICK_RATE_HZ | 系统时钟速率,时钟按照该速率为freeRTOS中各个任务执行计时,设置为1000Hz,则每个任务的最小调度时间为ms |
MAX_PRIORITIES | 最大优先级数量,默认为7 |
MINMAL_STACK_SIZE | 最小任务堆栈大小,每当创建一个任务时,都需要为该任务分配一定大小的栈空间,任务需要使用的变量等都存储在该栈空间中。默认的最小值为128个字。 |
MAX_TASK_NAME_LEN | 最大任务名称长度,在创建任务时,需要给每个任务起名作为标识,这个名称可以用一个字符串标识,本参数规定了字符串长度的上限值,默认为16 |
CubeMX 中创建任务
经过上面的步骤,我们已经开启了FreeRTOS,现在我们要在FreeRTOS里创建任务。
首先在FreeRTOS的配置页面中的configuration 下 选中 Tasks and Queues 标签页,存在一个已经创建的默认任务为“default Task”,点击进入配置选项修改为如下图所示。
名称 | 功能 |
Task Name | 任务名称 |
Priority | 任务创建时的优先级 |
Stack Size(Words) | 任务栈的大小,默认单位为字 |
Entry Function | 任务函数的入口 |
Code Generation Option | 任务函数代码生成方式,设置为Default 则会产生一个普通的任务函数, As weak:产生一个用__weak 修饰符修饰的任务函数(弱函数) As external: 产生一个外部引用的任务函数,用户需要自己实现该函数; Default: 产生一个默认格式的任务函数,用户需要在该函数实现功能。 |
(Parameter Allocation 这两行我们这里不做修改)
如果需要增加一个任务,点击Add,就可以创建一个新的任务。(下图我已经新建好了)
设置完毕之后点击OK,就可以看到列表中多出了自己创建的任务。
之后点击生成代码,打开keil。我们可以在freertos.c 中找到修改的默认任务函数。
由于选择通过__weak修饰符创建一个弱函数,可以再在别处实现该任务函数。程序执行时会自动寻找到这个另外实现的任务函数。
由于新建了任务“LED_GREEN”,并且设置为 As external,故而需要在别处实现任务函数,程序执行时会自动寻找到实现的任务函数。
在程序中创建任务(在 keil 中创建任务)
事实上,我们大多数情况下都是在程序中创建任务的。因为每次利用CubeMX创建任务都是在重新生成一次代码,很是麻烦。所以我们可以直接在keil中创建任务。
打开 freertos.c 文件,在MX_FREERTOS_Init 中,找到创建两个进程的代码,分别是 LED_RED 和 LED_GREEN。可以看到要创建任务,需要调用 osThreadDef 和 osThreadCreate 。
首先要介绍osThreadDef,实际上这不是一个函数,而是一个由CMSIS提供的宏定义,用于对要创建的任务进行设置。
名称 | osThreadDef |
功能 | 对要创建的任务进行设置 |
参数1 | name ,要创建任务的名称 |
参数2 | thread,要创建的任务代码的入口名称 |
参数3 | Priority,要创建的任务的优先级 |
参数4 | Instances,任务下可以创建的线程的数量 |
参数5 | stacksz,任务栈大小 |
接着通过CMSIS 提供的 osThreadCreate 函数来创建任务
函数名称 | osThreadCreate |
函数功能 | 创建一个任务 |
返回值 | osThreadId,任务 ID,ID 是一个任务的重要标识,当在创建完任务后需要执 行修改这个任务的优先级,或者销毁该任务时,就需要调用任务 ID,需要提 前声明一个类型为 osThreadId 的变量在此处存储返回值。 |
参数1 | const osThreadDef_t *thread_def,我们通过 osThreadDef 所设置的任务参 数,采用强制转换+任务名的方式进行输入,比如在 osThreadDef 中设置任 务名为 LED_RED,则在此处输入 osThread(LED_RED) |
参数2 | void *argument,任务需要的初始化参数,一般填为NULL |
在创建任务之前,我还需要做两件事情,
1. 先声明任务的入口函数(希望你可以分清声明 和 定义 二者之间的区别)
2.定义一个任务ID。
之后再在MX_FREERTOS_Init 中利用 osThreadDef 和 osThreadCreate 创建任务。
在程序中创建的任务,我们入口函数一般都是
As external: 产生一个外部引用的任务函数,用户需要自己实现该函数;
参考文档:
RoboMaster开发板C型嵌入式软件教程文档