立创天空星GD32移植FreeRTOS过程(GD32F407VET6)

前言

        由于作者本人之前学了一下FreeRTOS,想着移植到板子中玩玩,恰好前阵子嘉立创的天空星搞优惠活动,活动期间我就购入了一块GD版本的F407开发板,想着给移植过程进行一个简单的记录,方便本人回忆并供需要的读者参考学习,本文中的移植适合开发环境为Keil5的读者,由于本人技术有限,如果文章中出现错误,也请读者能够指出并纠正,万分感谢!

 

准备工作

工程模板获取

       以下的移植过程中,工程结构使用立创官方提供的库函数点灯工程模板,以便作为统一参考。

立创官方飞书文档地址:

‬​‬​​​​​⁠‌‌​‍‬​‌​⁠‬​​‬​​​​‍‌​‍⁠‍‌​‬‬​​​⁠⁠​​‬【立创·天空星GD32F407VET6】入门手册 - 飞书云文档 (feishu.cn)

在官方资料中获取工程模板

7e3e53b4df774bb1ad39ad7593c1084e.png

具体文件夹中的目录结构说明可以查看官方文档的描述,这里不过多赘述。

 

解压并打开该工程文件,这里我们先新建一个FreeRTOS文件夹,用于存放裁剪后的FreeRTOS源码

文件结构如下:

76eb2fa03ec242fba378589d4c54d3b0.png

 

FreeRTOS源码获取

        首先我们要移植一个RTOS,我们就得先去获得官方给的源代码,这里我们去到FreeRTOS的官方网站

FreeRTOS官网网址:FreeRTOS - Market leading RTOS (Real Time Operating System) for embedded systems with Internet of Things extensions

5977b896d8a54b0fab17b5679b9086ed.png点击下载FreeRTOS

c789a5e3ee2e43a4914503caa619d905.png

这里有两个版本,不带LTS的版本是官方发布的最新版本,带有示例代码,LTS版本是长期支持版本,没有示例代码,可以按需选择,这里我选择带示例代码的最新版本

 

裁剪FreeRTOS文件

以下是带例程版本的FreeRTOS工程源码的目录结构

18222b21227e4015937075491e5b9fba.png

在FreeRTOS/Source路径下可以看到FreeRTOS要移植的核心文件,我们对非必要的文件进行删除

e2e6cdb5703c435da7636ff2a4f3be72.png

只保留上图中选中的这些文件,其余删除,删除完后的文件结构如下:

d96f7d16721f4e789bf2c980eed6ebf0.png

 

进一步删除portable文件夹中多余的文件,只留下Keil、MemMang、RVDS这三个文件夹

MemMang存放的是内存分配有关的文件,RVDS保存了对应处理器架构的文件

9ed6516f5b794ddc8b600519c442ba9a.png

 

把裁剪好的文件放入先前在工程中创建好的FreeRTOS文件夹中

77cdc7f558d04cf58d0f3de6850881d7.png

 

修改Keil5工程文件

23c57c3a5b9342e7a9c858943de23bfc.png

根据上图文件路径,在官方例程的该目录下打开工程

点击魔术棒-->C/C++-->省略号, 添加以下文件路径到工程中

eeecbdaecca84ae583d811da6cf418fb.png

 

添加工程文件组FreeRTOSCore、FreeRTOSPort

FreeRTOSCore存放核心文件

FreeRTOSPort存放接口文件

(可以根据个人喜好进行创建和命名,此处仅为个人习惯)

18ea4fe09bd244ca98937c71c1a2873a.png

FreeRTOSCore中添加以下文件(FreeRTOS文件夹内):

fa18a53624434557aceb57d2f1c57566.png

FreeRTOSPort中添加以下文件:

dcf82a7220c0490196eeb630ed9ec24c.png

heap_4.cMemMang文件夹中

因为天空星是F4内核的,port.c接口文件在RVDS/ARM_CM4路径下选择

 

最后工程整体为:

db2d42fef07d4048a190e9a5132fefa9.png

 

此时我们的移植尚未完成,FreeRTOS官方还提供了相关的配置文件,我们可以在带例程版本的官方源码中找到配置文件FreeRTOSConfig.h文件

找到M4内核例程代码,路径为:FreeRTOS\Demo\CORTEX_M4F_STM32F407ZG-SK

6334b4bdfa904ea78bf488aec5a9004c.png

复制该文件到工程的include文件夹中,如下图:

8af501577bdd44568baea80b240e6b58.png

编译Keil工程

此时出现以下报错:

8e1ef4da035b4b04b654874c47f63245.png

这是因为配置文件的宏定义存在问题

在编译完成后的文件中找到配置文件,修改图示部分的代码:

1d58039b10204ebca17e45ecc1360fd1.png

修改为:

//#ifdef __ICCARM__
#if defined(__ICCARM__)||defined(__CC_ARM)||defined(__GNUC__)
    #include <stdint.h>
    extern uint32_t SystemCoreClock;
#endif

接着我们下滑找到SysTick的宏定义,将其注释

1679defd897045b48b529a5a5c580aec.png

往上滑将FreeRTOS使用的钩子函数相关的宏定义改为0

88ed7624d3f048b587e93741ed380945.png

此时再编译,已经是0e0w,非常令人愉悦

46b8d4c2cf04437699bdb4b4951bb12f.png

 

修改系统中断函数

        systick定时器是FreeRTOS的“心脏”,我们需要对SysTick_Handler函数进行修改,使其能够提供一个系统的Tick。

        在Borad文件夹下的board.c中添加头文件FreeRTOS.h和task.h,并修改SysTick_Handler:

        将其修改如下:

    ae1ff06f37b1426eb3e1f2ca8a2750fb.png

代码如下:

// 头文件
#include "FreeRTOS.h"
#include "task.h"


// 修改的代码
extern void xPortSysTickHandler(void);

void SysTick_Handler(void){
    if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//假如系统已经运行
    {
        xPortSysTickHandler();
    }
}

此时编译,无报错即为修改正确。

 

使用串口打印调试信息

        天空星上引出了串口0的TX和RX引脚,分别为U0T和U0R,正好可以用来作为打印调试信息的串口。如下图:

c6800a1d91b4467e80b281cf8f7a6d25.png

立创官方提供的库函数点灯例程中已经帮我们写好了串口初始化函数,波特率为115200,如下图:

9f5827bd319446e888dfa3692b44e52a.png

串口打印需要使用微库,点击魔术棒,点击use MicroLIB勾选

6343b375b86e4986870f0fca3fd68e63.png

 

实现LED灯闪烁

修改board.c中的延迟函数

在头文件申明的下方加上以下全局变量:

static uint8_t  fac_us=0;			//us延时倍乘数			   
static uint16_t fac_ms=0;			//ms延时倍乘数,在rtos下,代表每个节拍的ms数

删除board.c原本的systick_config函数,更改为以下代码:

//初始化延迟函数
//当使用OS的时候,此函数会初始化OS的时钟节拍
void systick_config(void)
{
	uint32_t reload;
	systick_clksource_set(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   	
}	

删除原本的delay_us和delay_ms函数,添加以下延迟函数: 


//延时nus
//nus为要延时的us数.		    								   
void delay_us(uint32_t nus)
{		
	uint32_t ticks;
	uint32_t told,tnow,tcnt=0;
	uint32_t reload=SysTick->LOAD;	//LOAD的值	    	 
	ticks=nus*fac_us; 				//需要的节拍数 
	told=SysTick->VAL;        		//刚进入时的计数器值
	while(1)
	{
		tnow=SysTick->VAL;	
		if(tnow!=told)
		{	    
			if(tnow<told)tcnt+=told-tnow;	//这里注意一下SYSTICK是一个递减的计数器就可以了
			else tcnt+=reload-tnow+told;	    
			told=tnow;
			if(tcnt>=ticks)break;		//时间超过/等于要延迟的时间,则退出.
		}  
	};									    
}


//延时nms
//nms:要延时的ms数,会引起任务调度
void delay_ms(uint16_t nms)
{	
	if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
	{		
		if(nms>=fac_ms)						//延时的时间大于OS的最少时间周期 
		{ 
   			vTaskDelay(nms/fac_ms);	 		//FreeRTOS延时
		}
		nms%=fac_ms;						//OS已经无法提供这么小的延时了,采用普通方式延时    
	}
	delay_us((uint32_t)(nms*1000));				//普通方式延时
}


//延时nms,不会引起任务调度
//nms:要延时的ms数
void delay_xms(uint32_t nms)
{
	uint32_t i;
	for(i=0;i<nms;i++) delay_us(1000);
}

修改完.c文件记得修改board.h文件

ab99933ea1bf4705891f3d31492e98e1.png

 

        添加FreeRTOS的头文件,FreeRTOS.h和task.h

        定义任务优先级和堆栈大小等,此处LED任务1为点亮LED,任务2为熄灭LED

         任务优先级的值越大,优先级越大

#include "board.h"
#include "bsp_led.h"
#include "bsp_uart.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 LED1_TASK_PRIO		3
//任务堆栈大小	
#define LED1_STK_SIZE 		50  
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void LED_Thread1(void *pvParameters);

//任务优先级
#define LED2_TASK_PRIO		4
//任务堆栈大小	
#define LED2_STK_SIZE 		50  
//任务句柄
TaskHandle_t LED2Task_Handler;
//任务函数
void LED_Thread2(void *pvParameters);

注意:定义优先级时不能使用0和2,因为在FreeRTOSConfig.h文件中,为软件定时器分配了优先级2,而优先级0为空闲中断,这两个优先级都被占用.

91fd621d5ef94de08092979a6a0a692f.png

 

创建任务

  //创建开始任务
		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();           //进入临界区
    //创建LED1任务
    xTaskCreate((TaskFunction_t )LED_Thread1,     	
                (const char*    )"led1_task",   	
                (uint16_t       )LED1_STK_SIZE, 
                (void*          )NULL,				
                (UBaseType_t    )LED1_TASK_PRIO,	
                (TaskHandle_t*  )&LED1Task_Handler);   


	    xTaskCreate((TaskFunction_t )LED_Thread2,     	
                (const char*    )"led2_task",   	
                (uint16_t       )LED2_STK_SIZE, 
                (void*          )NULL,				
                (UBaseType_t    )LED2_TASK_PRIO,	
                (TaskHandle_t*  )&LED2Task_Handler);   
													
							
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区

}

LED任务1及LED任务2

//LED1任务函数 
void LED_Thread1(void *pvParameters)
{
    while(1)
    {
      bsp_led_on(LED1);
      delay_ms(100);
    }
} 

//LED2任务函数 
void LED_Thread2(void *pvParameters)
{
    
    while(1)
    {
	  bsp_led_off(LED1);
      delay_ms(250);
     
    }
} 

     

主函数总体代码如下:

#include "board.h"
#include "bsp_led.h"
#include "bsp_uart.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 LED1_TASK_PRIO		3
//任务堆栈大小	
#define LED1_STK_SIZE 		50  
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void LED_Thread1(void *pvParameters);

//任务优先级
#define LED2_TASK_PRIO		4
//任务堆栈大小	
#define LED2_STK_SIZE 		50  
//任务句柄
TaskHandle_t LED2Task_Handler;
//任务函数
void LED_Thread2(void *pvParameters);

int main(void)
{
    board_init();
	bsp_led_init(); // 初始化板上LED
	bsp_uart_init(); // 初始化uart
	
  //创建开始任务
	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();           //进入临界区
    //创建LED1任务
    xTaskCreate((TaskFunction_t )LED_Thread1,     	
                (const char*    )"led1_task",   	
                (uint16_t       )LED1_STK_SIZE, 
                (void*          )NULL,				
                (UBaseType_t    )LED1_TASK_PRIO,	
                (TaskHandle_t*  )&LED1Task_Handler);   


	    xTaskCreate((TaskFunction_t )LED_Thread2,     	
                (const char*    )"led2_task",   	
                (uint16_t       )LED2_STK_SIZE, 
                (void*          )NULL,				
                (UBaseType_t    )LED2_TASK_PRIO,	
                (TaskHandle_t*  )&LED2Task_Handler);   
													
							
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区

}

//LED1任务函数 
void LED_Thread1(void *pvParameters)
{
    while(1)
    {
      bsp_led_on(LED1);
      delay_ms(100);
    }
} 

//LED2任务函数 
void LED_Thread2(void *pvParameters)
{
    
    while(1)
    {
	  bsp_led_off(LED1);
      delay_ms(250);
     
    }
}  


编译烧录,此时LED灯闪烁。至此,FreeRTOS的简单移植也已经完成。

 

 

 

 

  • 20
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值