STM32F407ZGT6移植FreeRTOS全过程


因为刚开始学写博客,如果哪里写的你看不明白请评论区告诉我,我加以改进,谢谢大家

资料准备

在开始之前我们需要准备几样东西
1.正点原子STM32F4 FreeRTOS开发手册_V1.1
2.正点原子标准库函数中内存管理实验的代码
3.正点原子移植好的FreeRTOS代码
4.FreeRTOS官方提供的资料
这里我汇总了一下,可以一站式解决,资料链接

开始移植

1.打开内存管理项目并新建文件夹FreeRTOS

在这里插入图片描述
这里为了防止出现中文路径出现奇奇怪怪的问题,我把项目文件夹名改了一下。

2.打开FreeRTOS官方提供的资料

复制Source文件夹下的所有文件并粘贴到刚刚创建的FreeRTOS文件夹下
在这里插入图片描述

图1 复制Source下所有文件

在这里插入图片描述

图1 粘贴到刚刚创建的FreeRTOS文件夹中

3.删除刚刚复制过去的部分文件

这一步,我们看正点原子提供的教程就好。
在这里插入图片描述

图1 正点原子教程截图

在这里插入图片描述

图1 留下这三个文件夹

4.向工程分组中添加文件

打开基础工程,新建分组FreeRTOS_CORE和FreeRTOS_PORT,然后向这两个分组中添加文件。添加后两个分组应该是这样的。
在这里插入图片描述

  1. FreeRTOS_CORE中的添加的文件打开项目中FreeRTOS文件夹一目了然。

  2. FreeRTOS_PORT中port.c和heap_4.c分别在项目文件夹下FreeRTOS/portable中MemMang和RVDS文件夹中。

根据正点教程,MemMang中我选择heap_4.h。又因为我的单片机型号是STM32F407ZGT6,所以RVDS我选择ARM_CM4F中的port.h。

在这里我遇到一个小插曲,这是Keil5的一个Bug,如果你们也遇到了,具体解决方法我给你们贴出来,自行跳转过去看吧
Warning #440: Requested device 'STM32F407ZG' is substituted with variant 'STM32F407ZGTx for target 'xxxx''
在这里插入图片描述

5.添加头文件路径

在这里插入图片描述
在这里插入图片描述

添加完后编译一下
..\FreeRTOS\include\FreeRTOS.h(59): error: #5: cannot open source input file "FreeRTOSConfig.h": No such file or directory
结果可以看到找不到FreeRTOSconfig.h文件。那这个文件去哪找?

  1. 官方例程
  2. 正点原子移植好的FreeRTOS源码
  3. 自己写(显然这不是初学者该做的事)

这里我就从FreeRTOS官方提供的资料中复制过来。复制到./FreeRTOS/include/文件夹下
在这里插入图片描述
好了,我们再编译一下,发现还是有错。
..\FreeRTOS\portable\RVDS\ARM_CM4F\port.c(788): error: #20: identifier "SystemCoreClock" is undefined
在这里插入图片描述
这个问题和FreeRTOSconfig.h的一个条件编译有关,跟着正点原子的教程改就好了。

#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
// 用这个替换
#ifdef __ICCARM__

在这里插入图片描述
在这里插入图片描述
改为后,还是有报错,继续改吧!
..\OBJ\MALLOC.axf: Error: L6200E: Symbol SVC_Handler multiply defined (by port.o and stm32f4xx_it.o).
..\OBJ\MALLOC.axf: Error: L6200E: Symbol PendSV_Handler multiply defined (by port.o and stm32f4xx_it.o).
..\OBJ\MALLOC.axf: Error: L6200E: Symbol SysTick_Handler multiply defined (by port.o and stm32f4xx_it.o).

这个报错是因为在port.c 和stm32f4xx_it.c文件中出现重复定义的函数SVC_Handler、SysTick_Handler、 PendSV_Handler。我们将stm32f4xx_it.c中这三个函数直接注释掉,再编译。

到这里我出现了和正点原子教程中不一样的错误
..\OBJ\MALLOC.axf: Error: L6406E: No space in execution regions with .ANY selector matching heap_4.o(.bss).
在这里插入图片描述
看了一下报错信息,我怀疑是芯片内存不足,所以我我删掉了项目中的MALLOC组,并把主函数清空,然后就好了。
在这里插入图片描述

在这里插入图片描述
再编译,接下来出现的错误和正点的接上了。
..\OBJ\MALLOC.axf: Error: L6218E: Undefined symbol vApplicationIdleHook (referred from tasks.o).
..\OBJ\MALLOC.axf: Error: L6218E: Undefined symbol vApplicationStackOverflowHook (referred from tasks.o).
..\OBJ\MALLOC.axf: Error: L6218E: Undefined symbol vApplicationTickHook (referred from tasks.o).
..\OBJ\MALLOC.axf: Error: L6218E: Undefined symbol vApplicationMallocFailedHook (referred from heap_4.o).
在这里插入图片描述
解决方法按教程就好了。
在这里插入图片描述

6.修改sys.h、usart.c、delay.c三个文件

6.1 sys.h

修改宏定义,将0改成1,支持OS
在这里插入图片描述

6.2 usart.c
  1. 修改头文件
    在这里插入图片描述
  2. 删除与UCOS相关代码

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

void USART1_IRQHandler(void) //串口1中断服务程序
{
	u8 Res;
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) 
	{
		Res =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
		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;
				}
			}
		} 
	} 
}
6.3 delay.c

到这里我懒得写了,大家看具体步骤的话看教程,这边我直接贴出来我按教程修改的结果。

#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);  	    
} 

修改完这三个文件后编译,发现还有错误:
..\OBJ\MALLOC.axf: Error: L6200E: Symbol SysTick_Handler multiply defined (by port.o and delay.o).
这表示在 port.c 和 delay.c 中有重复定义的函数:SysTick_Handler(),二选一!
很明显 delay.c 中的 SysTick_Handler()得留下来,打开 FreeRTOSConfig.h 文件,找到如下一个宏
定义:

#define xPortSysTickHandler SysTick_Handler

注释掉就好了,注释之后再编译

按照我发的这个版本中教程和正点给的源代码中存在函数定义形参和头文件函数声明形参类型不同步的问题,所以大家在复制的过程中需要根据函数定义去修改头文件中函数形参类型。

7.在main.c文件中添加代码验证

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "FreeRTOS.h"
#include "task.h" 

//ALIENTEK 探索者STM32F407开发板 实验37
//内存管理实验-库函数版本 
//技术支持:www.openedv.com
//淘宝店铺:http://eboard.taobao.com  
//广州市星翼电子科技有限公司  
//作者:正点原子 @ALIENTEK
 
 
#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); //任务函数

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

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 *p_arg)
{
	static float float_num=0.00;
	while(1)
	{
		float_num+=0.01f;
		printf("float_num 的值为: %.4f\r\n",float_num);
		vTaskDelay(1000);
	}
}


超级重点,一定要看!!!!!!!!!!!

大家一定要勾选上Reset and Run,不然你烧进去程序不能运行,必须手动复位才能运行,我被这个给坑惨了,我代码烧进去一直不运行我以为我自己代码写错了,后面试着按一下复位键就正常运行了,再后面才想起来要改这个,所以大家不要再踩这个坑了。
在这里插入图片描述

在这里插入图片描述

取消勾选Enable,如果看不懂我的请跳转到此链接

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值