STM32F407ZGT6 的FreeRTOS 移植

STM32F407ZGT6 的FreeRTOS 移植

一、下载FreeRTOS

到FreeRTOS的官网可以直接免费下载,FreeRTOS官网地址:点击跳转

在这里插入图片描述

除开官网可以下载外,还可以使用正点原子资料中的(因为我之前是使用的正点原子的教程,所以已经下好她的资料包)

在这里插入图片描述

解压后可以看到两个主要的文件夹在这里插入图片描述

二、资料中各文件夹的作用

可能资料不同,里面所包含的文件夹会有稍微的出入,但是这并不影响,可以看到文件夹的作用如下:
在这里插入图片描述

在重要的Free RTOS的文件夹下有下面的文件
在这里插入图片描述

2.1 Demo文件夹

而【Demo】文件夹中的有对应型号的例程
比如我是用的stm32F407ZGT6开发板就有如下:
在这里插入图片描述

还有经常用于开发的stm32f103也有。Free RTOS支持多种芯片架构,支持不同的芯片型号。

2.2 Source文件夹

Source文件夹是Free RTOS源码本尊,

在这里插入图片描述

2.2.1 Source–>portable

【portable】文件夹是移植文件,那么这个文件中的东西就是将FreeRTOS与硬件联系在一起的连接桥梁

由于使用MDK开发,所以选用
在这里插入图片描述

在这里插入图片描述

三、FreeRTOS的移植

3.1 添加FreerOS源码

3.1.1 复制源码内容

之前各文件的内容就提到过,Free RTOS的源码在文件夹【Source】中
在这里插入图片描述

放入到工程准备的工程中,在准备的工程中创建一个新的文件夹【FreeRTOS】,将Free RTOS的内核源码复制进来。
在这里插入图片描述

注意
准备的工程使用正点原子的工程为最佳,因为是根据正点原子的教程进行的移植。(使用启明欣欣的话,文件结构不一样)下面有些图片的工程框架是启明欣欣的,所以我后来又重新来了一遍)

之前有提到过的在【protable】文件夹中只需要使用到三个文件夹,其余用不到的文件,可以自行决定删除与否。

在这里插入图片描述

3.2 在工程中添加文件

打开基础工程,新建两个文件分组,可以分别是FreeRTOS_CORE 和
FreeRTOS_PORT,如下图所示:
在这里插入图片描述

FreeRTOS_CORE 分组用于存放 FreeRTOS 的内核 C 源码文件,要将FreeRTOS 的内核 C 源文件添加到FreeRTOS_CORE 分组中。如下图:在这里插入图片描述

FreeRTOS_PORT 分组用于存放 FreeRTOS 内核的移植文件,需要添加两个文件到这个分组,分别为heap_x.c和port.c

heap_4.c文件选择:
该文件位置在 portable/MemMang 文件夹下(MemMang是内存管理相关的)
在这里插入图片描述


poct.c文件选择:
stm32f407是Cortex-M4内核并且带有FPU。poct.c在 portabl/RVDS 文件夹下,该文件夹下有ARM的不同内核的文件夹,因为是stm32f407顾选择ARM_CM4F的文件夹下的poct.c
在这里插入图片描述

port.c 是 FreeRTOS 这个软件与 MCU 这个硬件连接的桥梁,因此对于STM32 系列不同的开发板,所使用的 port.c 文件是不同的。port.c 文件的路径在FreeRTOS/portable/RVDS 下。进入到 FreeRTOS/portable/RVDS,可以看到 FreeRTOS 针对不同的MCU提供了不同的port.c文件,具体STM32系列开发板与不同port.c的对应关系如下表所示:在这里插入图片描述

3.3 添加头文件路径

添加完FreeRTOS源码中的C文件以后还要添加FreeRTOS源码的头文件路径。
在这里插入图片描述

3.4 添加FreeRTOSConfig.h文件

编译工程后报错缺少FreeRTOSConfig.h文件
在这里插入图片描述

从FreeRTOS的官方移植工程中找个针对STM32F407的移植工程 中,把FreeRTOSConfig.h这个文件复制过来。放FreeRTOS\include文件夹下

FreeRTOSConfig.h 是 FreeRTOS 操作系统的配置文件,FreeRTOS操作系统是可裁剪的,用户可以根据需求对FreeRTOS进行裁剪,裁剪掉不需要用到的FreeRTOS功能,以此来节约MCU中寸土寸金的内存资源。

那么这个文件如何获取呢?

1.自己编写。FreeRTOS官网的在线文档中就详细地对FreeRTOSConfig.h中各个配置项进行了描述,网页

2.FreeRTOS内核的演示工程.Demo文件夹中包含了FreeRTOS官方提供的演示工程,在这些演示工程当中就包含了每个演示工程对应的FreeRTOSConfig.h文件
(但是可能是老版本的,可能不能很好的兼容新版本的FreeRTOS)

3.使用正点原子示例中的文件
在这里插入图片描述

3.5 SystemCoreClock未定义

再次编译后报错
在这里插入图片描述

SystemCoreClock未定义,在FreeRTOSConfig.h 中使用 SystemCoreClock 来标记MCU的频率.
所以在刚刚加入的FreeRTOSConfig.h文件中进行更改:
在这里插入图片描述

/* Ensure stdint is only used by the compiler, and not the assembler. */
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
	#include <stdint.h>
	extern uint32_t SystemCoreClock;
#endif

3.6 报错:重复定义

更改后再次编译会报错:
在这里插入图片描述

这是重复定义了,port.c和stm32f4xx_it.c两个文件中有重复定义的函数:PendSV_Handler(), SVC_Handler(), SysTick_Handler()。屏蔽掉stm32f4xx_it.c中的PendSV_Handler(), SVC_Handler(), SysTick_Handler()三个函数。
在这里插入图片描述

3.7 钩子函数未定义

继续编译一次,还是会有错,这次的错是函数未定义,他们都是Hook结尾的,称为钩子函数

在这里插入图片描述

去FreeRTOSConfig.h中关闭这些钩子函数,他们都是宏定义决定,这里将configUSE_IDLE_HOOK、configUSE_TICK_HOOK、configUSE_MALLOC_FAILED_HOOK和configUSE_FOR_STACK_OVERFLOW定义为0.

在这里插入图片描述

在进行编译就没有报错了。
在这里插入图片描述

3.8 SYSTEM 文件的更改

正点原子的SYSTEM文件夹中的文件一开始是正对µC/OS编写的,因此使用FreeRTOS的话,就需要作相应的修改。

3.8.1 修改sys.h文件

在sys.h文件中使用了宏SYS_SUPPORT_OS来定义是否支持OS,因为要支持FreeRTOS,因此应当将宏SYS_SUPPORT_OS定义为1。
在这里插入图片描述

3.8.2 usart.c

usart.c文件有两部分要修改,一个是添加FreeRTOS.h头文件,默认是添加的UCOS中的includes.h头文件。修改之后内容如下:
**加粗样式
**

话需要修改USART1的中断服务函数,因为FREERTOS在使用中断的时候不需要OSIntEnter()和OSIntExit()这两个函数,但是Free RTOS是不需要的,所以删除就好。
在这里插入图片描述

3.8.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();
    }
}

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

//初始化延迟函数
//SYSTICK的时钟固定为AHB时钟,基础例程里面SYSTICK时钟频率为AHB/8
//这里为了兼容FreeRTOS,所以将SYSTICK的时钟频率改为AHB的频率!
//SYSCLK:系统时钟频率
void delay_init(u8 SYSCLK)
{
	u32 reload;
 	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK); 
	fac_us=SYSCLK;							//不论是否使用OS,fac_us都需要使用
	reload=SYSCLK;							//每秒钟的计数次数 单位为M	   
	reload*=1000000/configTICK_RATE_HZ;		//根据configTICK_RATE_HZ设定溢出时间
											//reload为24位寄存器,最大值:16777216,在168M下,约合0.0998s左右	
	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     
}							    

在这里插入图片描述

延时函数定义如下:

//延时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)
		{	    
			if(tnow<told)tcnt+=told-tnow;	//这里注意一下SYSTICK是一个递减的计数器就可以了.
			else tcnt+=reload-tnow+told;	    
			told=tnow;
			if(tcnt>=ticks)break;			//时间超过/等于要延迟的时间,则退出.
		}  
	};										    
}
//延时nms
//nms:要延时的ms数
//nms:0~65535
void 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文件改的有点多,下面直接贴出整个文件的代码:

#include "delay.h"
#include "sys.h"
// 	 
//如果使用OS,则包括下面的头文件(以ucos为例)即可.
#if SYSTEM_SUPPORT_OS
#include "FreeRTOS.h"					//支持OS时,使用	 
#include "task.h"
#endif
//  
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32F407开发板
//使用SysTick的普通计数模式对延迟进行管理(支持OS)
//包括delay_us,delay_ms
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2014/5/2
//版本:V1.3
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//********************************************************************************
//修改说明
//V1.1 20140803 
//1,delay_us,添加参数等于0判断,如果参数等于0,则直接退出. 
//2,修改ucosii下,delay_ms函数,加入OSLockNesting的判断,在进入中断后,也可以准确延时.
//V1.2 20150411  
//修改OS支持方式,以支持任意OS(不限于UCOSII和UCOSIII,理论上任意OS都可以支持)
//添加:delay_osrunning/delay_ostickspersec/delay_osintnesting三个宏定义
//添加:delay_osschedlock/delay_osschedunlock/delay_ostimedly三个函数
//V1.3 20150521
//修正UCOSIII支持时的2个bug:
//delay_tickspersec改为:delay_ostickspersec
//delay_intnesting改为:delay_osintnesting
// 

static u8  fac_us=0;							//us延时倍乘数			   
static u16 fac_ms=0;							//ms延时倍乘数,在os下,代表每个节拍的ms数
	
extern void xPortSysTickHandler(void);
//systick中断服务函数,使用OS时用到
void SysTick_Handler(void)
{	
	if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
	{
		xPortSysTickHandler();
	}
}
			   
//初始化延迟函数
//当使用OS的时候,此函数会初始化OS的时钟节拍
//SYSTICK的时钟固定为AHB时钟的1/8
//SYSCLK:系统时钟频率
void delay_init(u8 SYSCLK)
{
	u32 reload;
	//SysTick 频率为 HCLK
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);
	fac_us=SYSCLK; //不论是否使用 OS,fac_us 都需要使用
	reload=SYSCLK; //每秒钟的计数次数 单位为 K 
	reload*=1000000/configTICK_RATE_HZ; //根据 configTICK_RATE_HZ 设定溢出时间
	//reload 为 24 位寄存器,最大值:16777216,
	//在 168M 下,约合 0.0998s 左右
	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
}								    

//延时nus
//nus:要延时的us数.	
//nus:0~204522252(最大值即2^32/fac_us@fac_us=21)	    								   
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)
		{	    
			if(tnow<told)tcnt+=told-tnow;	//这里注意一下SYSTICK是一个递减的计数器就可以了.
			else tcnt+=reload-tnow+told;	    
			told=tnow;
			if(tcnt>=ticks)break;			//时间超过/等于要延迟的时间,则退出.
		}  
	}									    
}  
//延时nms
//nms:要延时的ms数
//nms:0~65535
void delay_ms(u16 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)); //普通方式延时
}

void delay_xms(u16 nms)
{	 		  	  
	u32 i;
	for(i=0;i<nms;i++) delay_us(1000);  	    
} 

3.9 Symbol SysTick Handler multiply defined

修改好SYSTEM文件夹之后,编译会有如下报错
在这里插入图片描述

这表示在 port.c 和 delay.c 中有重复定义的函数:SysTick_Handler(),二选一!

在这里插入图片描述

在FreeRTOSConfig.h 文件中找到上面的行并注释掉。

四、验证

再次编译后不会报错了,可以在mian函数中添加一个程序进行验证

#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 *pvParameters);

//任务优先级
#define LED1_TASK_PRIO		3
//任务堆栈大小	
#define LED1_STK_SIZE 		50  
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);

//任务优先级
#define FLOAT_TASK_PRIO		4
//任务堆栈大小	
#define FLOAT_STK_SIZE 		128
//任务句柄
TaskHandle_t FLOATTask_Handler;
//任务函数
void float_task(void *pvParameters);

int main(void)
{ 
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
	delay_init(168);		//初始化延时函数
	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);        
    //浮点测试任务
    xTaskCreate((TaskFunction_t )float_task,     
                (const char*    )"float_task",   
                (uint16_t       )FLOAT_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )FLOAT_TASK_PRIO,
                (TaskHandle_t*  )&FLOATTask_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);
    }
}

//浮点测试任务
void float_task(void *pvParameters)
{
	static float float_num=0.00;
	while(1)
	{
		float_num+=0.01f;
		printf("float_num的值为: %.4f\r\n",float_num);
        vTaskDelay(1000);
	}
}
  • 27
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

写的什么石山代码

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

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

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

打赏作者

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

抵扣说明:

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

余额充值