本文讲解的是使用移植FreeRTOS到GD32F350RX中(基于KEIL 5编译器)。
系统移植之后创建了3个任务,LED1,LED2,LED3交替闪烁,每个LED的闪烁交替周期不一样。FreeRTOS是一个迷你的实时操作系统内核。作为一个轻量级的操作系统,功能包括:任务管理、时间管理、信号量、消息队列、内存管理、记录功能、软件定时器、协程等,可基本满足较小系统的需要。
一、FreeRTOS源码下载
首先需要下载FreeRTOS的源码:FreeRTOS源码https://www.freertos.org/a00104.html
下载之后解压。
二、GD32F350RX工程
在开始移植之前,我们需要准备一个GD32F350RX的工程文件,需要先确保这个工程能够跑去来。
GD32F350RX工程可以在兆易创新的官网上下载,对于工程创建运行不在本文的讨论范围之内。
本文使用的是一个LED的工程,运行之后可以看到三个LED交替变化闪烁。
创建一个FreeRTOS文件夹用于存放FreeRTOS的内核源码文件.
三、FreeRTOS的移植
打开我们下载解压的FreeRTOS源码文件夹。
1、FreeRTOS目录结构介绍
- 打开文件夹之后有两个文件夹:一个是FreeRTOS,另一个是FreeRTOS-Plus。
我们进行系统移植主要使用的就是FreeRTOS里面的内容,FreeRTOS-Plus中的内容是一个扩展功能,和系统内核是没有关系的,我们在系统的移植的过程中不用管。 - 打开FreeRTOS文件夹:里面有三个文件夹Demo,License,Source。
-
- License中存放的是使用权限,也不用关心,FreeRTOS是开源免费的。
-
- Source就是FreeRTOS的内核源文件,我们移植过程中主要使用到的就是这个文件夹里面的文件。
2、系统移植过程
下面开始将系统的移植过程。
(1)复制需要使用到的文件
首先打开Source文件夹,我们需要使用到的源码如下所示:
这里面的*.c文件我们都需要使用到。
将红色方框中的文件全部复制到上面我们在工程文件夹中创建的文件夹FreeRTOS中。
复制到GDF350RX工程中如下所示,其中的*.c文件以及文件夹include中的头文件都会使用到,所以不能删除:
(2)删除不需要使用的文件以及文件夹
打开portable文件夹,可以看到其中有很多文件夹,我们需要使用到是如下所示几个。
我们只需要保留下面的三个文件夹即可,其他的都可以删除掉
(3)RVDS中GD32需要使用到的内核文件
打开RVDS文件夹,看到里面列举了很多种芯片的文件夹,GD32F350RX需要使用的是ARM_CM3,虽然是一款M4内核的单片机,但是汇编指令还是M3的,所以需要使用这个,其他不使用的可以删除掉。
(4)导入FreeRTOS内核文件到GD32工程中
打开GD32F350RX的工程,然后打开Manage Project Items,添加FreeRTOS内容源码文件到工程中,先添加源文件。
先创建两个文件夹,FreeRTOS以及FreeRTOS_H,FreeROS里面存放*.c文件,FreeRTOS_H里面存放头文件,这个也可以不需要,主要是为了方便打开头文件进行查看。
-
导入内核文件
-
导入内存处理文件
-
导入port文件
FreeRTOS移植需要用到的文件都已经加载到工程中了,如下图所示,请检查是否加载完全。
如法炮制加载*.h文件到FreeRTOS_H文件夹中即可(可以不用添加头文件到工程中,主要是为了后期打开方便)。
(5)添加头文件路径
添加头文件路径,使用过Keil的小伙伴都知道编译工程需要使用到头文件,编译器是不会中需要头文件的,所以需要我们自己添加头文件的路径。
-
打开Options for Target … —> C/C++
-
添加如下两个头文件路径
- 完成添加,点击OK确定
四、工程编译以及系统运行
1、编译工程
现在开始编译工程,但是出现下面的错误。
提示没有找到FreeRTOSConfig.h文件,这个是FreeRTOS内核裁剪的配置文件,一般有用户自己编写配置的,我们可以使用FreeRTOS的Demo文件夹下面现有的配置文件。
找到FreeRTOS源码文件中的FreeRTOSv10.3.1\FreeRTOS\Demo\CORTEX_STM32F103_Keil目录。看到其中有一个FreeRTOSConfig.h文件。
直接将其中的FreeRTOSConfig.h文件拷贝到我们工程文件的FreeRTOS_V10.3.1\include中。
2、解决报错信息再次编译
再次编译出现下述问题
说是这个函数没有定义,我们直接打开FreeRTOSConfig.h文件,修改一下源文件就可以了(注意,向上面将*.h文件导入到工程中一样,我们也需要将后面的FreeRTOSConfig.h导入到工程的FreeRTOS_H文件夹中,方便我们打开修改)。
从错误中可以看出来,在strem_buffer.c中使用到了这个函数,但是并没有声明,我们通过KEIL工具的全局搜索找到这个文件,然后看看是不是宏定义没有关闭或者打开的原因。
3、查找报错原因
通过搜索,可以看到在FreeRTOS.h文件中,有关于该函数是否使用的宏定义。
从中可以看到,如果没有定义INCLUDE_xTaskGetCurrentTaskHandle这个宏就会将INCLUDE_xTaskGetCurrentTaskHandle宏设置为0,所以不会初始化xTaskGetCurrentTaskHandle()这个函数。
我们可以搜索一下INCLUDE_xTaskGetCurrentTaskHandle,发现此时函数的定义是禁止的。如下图所示:
五、FreeRTOSConfig.h文件的配置
上一章还遗留了一个问题,一个error还没有修改。我们需要修改配置文件。
我们在该文件中添加一个INCLUDE_xTaskGetCurrentTaskHandle定义就行了。
现在点击编译就不会报错了,可以进行接下来的操作了。
注意
除了上面需要添加的宏以外,我们还需要修改一下configCPU_CLOCK_HZ这个宏,使用系统时钟,不用我们自己定义的。
如下:
#define configCPU_CLOCK_HZ ( ( unsigned long ) SystemCoreClock )
这个是工程中的关键字,所以需要添加头文件:
#include <stdint.h>
extern uint32_t SystemCoreClock;
六、任务的创建与运行
上面的准备工作都做好了,之后我们就可以开始创建任务运行了。
我们创建3个控制LED的任务,因为该板子上面有三个LED,一个任务控制一个LED闪烁,不同的任务闪烁频率不一样。
1、首先是在main.h函数中添加头文件,添加如下两个即可:
#include "FreeRTOS.h"
#include "task.h"
2、下面的代码是创建了三个函数,并且开始任务:
#include "gd32f3x0.h"
#include "gd32f3x0_libopt.h"
#include "gd32f3x0_eval.h"
#include "systick.h"
#include <stdio.h>
#include "FreeRTOS.h"
#include "task.h"
#define LED1_TASK_PRIO 3 //任务优先级
#define LED1_STK_SIZE 50 //任务堆栈大小
TaskHandle_t LED1Task_Handler; //任务句柄
void led1_task(void *pvParameters); //任务函数
#define LED2_TASK_PRIO 3 //任务优先级
#define LED2_STK_SIZE 50 //任务堆栈大小
TaskHandle_t LED2Task_Handler; //任务句柄
void led2_task(void *pvParameters); //任务函数
#define LED3_TASK_PRIO 2 //任务优先级
#define LED3_STK_SIZE 50 //任务堆栈大小
TaskHandle_t LED3Task_Handler; //任务句柄
void led3_task(void *pvParameters); //任务函数
/@@*!
\brief main function
\param[in] none
\param[out] none
\retval none
*/
int main(void)
{
/@@*Initialization*/
// systick_config();
// nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0); ////设置系统中断优先级分组4
gd_eval_led_init(LED1);
gd_eval_led_init(LED2);
gd_eval_led_init(LED3);
//创建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); //任务句柄
//创建led2任务
xTaskCreate((TaskFunction_t )led2_task, //任务函数
(const char* )"led2_task", //任务名称
(uint16_t )LED2_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )LED2_TASK_PRIO, //任务优先级
(TaskHandle_t* )&LED2Task_Handler); //任务句柄
//创建led3任务
xTaskCreate((TaskFunction_t )led3_task, //任务函数
(const char* )"led3_task", //任务名称
(uint16_t )LED3_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )LED3_TASK_PRIO, //任务优先级
(TaskHandle_t* )&LED3Task_Handler); //任务句柄
vTaskStartScheduler(); //开启任务调度
}
//LED1任务
void led1_task(void *pvParameters)
{
while(1)
{
gd_eval_led_toggle(LED1);
vTaskDelay(200);
}
}
//LED2任务
void led2_task(void *pvParameters)
{
while(1)
{
gd_eval_led_toggle(LED2);
vTaskDelay(600);
}
}
//LED3任务
void led3_task(void *pvParameters)
{
while(1)
{
gd_eval_led_toggle(LED3);
vTaskDelay(1000);
}
}
现在编译工程,编译能够通过,但是我们下载程序之后,发现不能运行。
是因为FreeRTOS所使用的几个中断函数我们没有添加到中断向量表中,这几个函数分别是xPortPendSVHandler(), xPortSysTickHandler(), vPortSVCHandler(), 这几个函数在port.c中。
3、修改中断向量表
打开汇编文件startup_gd32f3x0.s, 找到__Vectors,修改其中的三个中断名称为上述说的那三个,如下图所示:
现在我们重新编译发现会报如下错误
上述error表示找不到这几个符号,因为这几个函数是外部函数,所以需要导入到这个文件中,如下所示:
七、运行结果
通过上面的操作,我们已经成功的移植了FreeRTOS到我们的GD32F350RX中了,编译工程,下载工程,按一下板子上的复位按钮,可以看到运行结果。如下图所示:
和我们在工程中写得任务结果一致,三个LED交替显示。
上述就是整个移植过程,系统已经成功移植,如果有什么问题或者错误,欢迎指正。
来源:oschina
链接:https://my.oschina.net/u/2963604/blog/4479978