FreeRTOS移植STM32超详细(以STM32F103ZE为例)

我刚学FreeROTS时想移植到STM32,找了网上很多资料,但大多都不是很完整,于是我把我自己的移植过程分享出来,供大家参考。

我们以STM32F103ZE,正点原子的跑马灯实验为例,

准备工作:

跑马灯实验工程

FreeRTOS文件源码(可在官方下载)

 

 

第一步  移植文件到工程

首先在工程目录新建一个名为FreeRTOS的文件夹

然后打开从FreeRTOS官方下载的文件中路径为FreeRTOSv202212.01\FreeRTOS中的Source

文件夹

将里面的文件全部复制到工程目录的FreeRTOS文件夹 

 

为了更加简洁,我们新建一个Source文件夹,将外面的.c文件放进去 

 回到官方下载的FreeRTOS文件中,在Demo文件夹中找到对应的内核

 

打开文件夹复制里面的FreeRTOSCongic.h文件放到工程文件FreeRTOS文件夹中的include目录里

 

这是我们的文件移植已经完成了

到目前为止FreeRTOS源码被我们分成三部分

① include目录

② portable目录

③ source目录

①和③包含的是FreeRTOS核心功能源文件及头文件 .c和.h,这两部分的文件是通用的,基本不需要修改,②为需要移植修改的目录,这与编译器和所使用的CPU有关,属于RTOS硬件接口层。

Portable目录是系统和硬件的桥梁,所以我们下一步就要在Portable文件夹中找到自己MCU与编译环境的文件

 

 只需要保留这三个文件夹,其余的可删除

 

第二步 工程文件添加

打开工程,新建一个名为 FreeRTOS_COR 的组,把Source目录的全部文件添加进去

然后再新建一个名为 FreeRTOS_PORTABLE 的组,添加Portable目录中的MemMang文件夹中的heap4.c(这是重要的内存管理文件)

 

 再添加portable->RVDS->AMR_CM3中的port文件

 最终是这样的

 

 然后添加它们的头文件

 编译发现没有错误

③文件的配置

虽然没有错误了,但还有些步骤要做

先把FreeRTOSConfig.h文件添加进工程

 

然后在FreeRTOSConfig.h中添加

#define xPortPendSVHandler   PendSV_Handler

#define vPortSVCHandler   SVC_Handler

编译,发现有重复定义的错误 

 

 解决方法:进入对应的文件stm32f1xx_it.c屏蔽重复的3个函数

 把 SysTick_Handler中断函数也注释了,因为我们等下要在delay文件里建立新的中断函数

再次编译已发现没有错误了

最后还需进行一些配置

将以下代码替换delay.c中的代码

#include "delay.h"
#include "FreeRTOS.h"
#include "task.h"
// 	 
//如果使用ucos,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_UCOS
#include "includes.h"					//ucos 使用	  
#endif

#if SYSTEM_SUPPORT_RTOS
#include "includes.h"

#define OS_TICKS_PER_SEC	configTICK_RATE_HZ	
#define OSRunning			xSchedulerRunning

#endif
//	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//使用SysTick的普通计数模式对延迟进行管理
//包括delay_us,delay_ms
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2012/9/2
//版本:V1.5
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
//All rights reserved
//********************************************************************************
//V1.2修改说明
//修正了中断中调用出现死循环的错误
//防止延时不准确,采用do while结构!

//V1.3修改说明
//增加了对UCOSII延时的支持.
//如果使用ucosII,delay_init会自动设置SYSTICK的值,使之与ucos的TICKS_PER_SEC对应.
//delay_ms和delay_us也进行了针对ucos的改造.
//delay_us可以在ucos下使用,而且准确度很高,更重要的是没有占用额外的定时器.
//delay_ms在ucos下,可以当成OSTimeDly来用,在未启动ucos时,它采用delay_us实现,从而准确延时
//可以用来初始化外设,在启动了ucos之后delay_ms根据延时的长短,选择OSTimeDly实现或者delay_us实现.

//V1.4修改说明 20110929
//修改了使用ucos,但是ucos未启动的时候,delay_ms中中断无法响应的bug.
//V1.5修改说明 20120902
//在delay_us加入ucos上锁,防止由于ucos打断delay_us的执行,可能导致的延时不准。
// 	 

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


   void SysTickInit()
{
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);	//选择外部时钟  HCLK/8,HCLK = 72MHZ
	fac_us=SystemCoreClock/8000000;				//为系统时钟的1/8  ,计算9次,9
	fac_ms=(u16)fac_us*1000;	
}
	void SysTick_Handler(void)
{
    if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
    {
       xPortSysTickHandler();
    }
}	   
//初始化延迟函数
//当使用ucos的时候,此函数会初始化ucos的时钟节拍
//SYSTICK的时钟固定为HCLK时钟的1/8
//SYSCLK:系统时钟
//初始化延迟函数
//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数.		    								   
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的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对72M条件下,nms<=1864 

			 

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.h同理

#ifndef __DELAY_H
#define __DELAY_H 			   
#include "sys.h"  
//	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//使用SysTick的普通计数模式对延迟进行管理
//包括delay_us,delay_ms
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2012/9/2
//版本:V1.5
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
//All rights reserved
//********************************************************************************
//V1.2修改说明
//修正了中断中调用出现死循环的错误
//防止延时不准确,采用do while结构!

//V1.3修改说明
//增加了对UCOSII延时的支持.
//如果使用ucosII,delay_init会自动设置SYSTICK的值,使之与ucos的TICKS_PER_SEC对应.
//delay_ms和delay_us也进行了针对ucos的改造.
//delay_us可以在ucos下使用,而且准确度很高,更重要的是没有占用额外的定时器.
//delay_ms在ucos下,可以当成OSTimeDly来用,在未启动ucos时,它采用delay_us实现,从而准确延时
//可以用来初始化外设,在启动了ucos之后delay_ms根据延时的长短,选择OSTimeDly实现或者delay_us实现.

//V1.4修改说明 20110929
//修改了使用ucos,但是ucos未启动的时候,delay_ms中中断无法响应的bug.
//V1.5修改说明 20120902
//在delay_us加入ucos上锁,防止由于ucos打断delay_us的执行,可能导致的延时不准。
// 	 	 
void delay_init(u8 SYSCLK);
void delay_ms(u32 nms);
void delay_us(u32 nus);
void delay_xms(u32 nms);
void SysTickInit();
#endif






























再次编译提示“xTaskGetSchedulerState”没有定义

 

 

我们只需在FReeRTOS.h中加上这句定义就行了

#define INCLUDE_xTaskGetSchedulerState          1

 再次编译0错误

这样移植工作就完成了

加上主程序,实现点灯实验

#include "stm32f10x.h"
#include "delay.h"    
#include "usart.h"
#include "LED.H"
/* FreeRTOS头文件 */
#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);    //任务函数
 
 
static int TaskRun1,TaskRun2;  //用来观察任务运行

void BoardInit(void)  //设置初始化环境
{
    SysTickInit();   //系统时钟配置
	delay_init(72);  //延迟函数配置
	LED_Init();
    uart_init(115200);
}
 
 
//开始任务任务函数
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);         
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
}
 

int main(void)
{    
//  BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
    BoardInit();   //设置初始化环境
    printf("Welcome to FreeRTOS,CoreClock:%d\r\n",SystemCoreClock);
     
    //创建开始任务
    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();                                                     //开启任务调度   
}
 
//LED0任务函数 
void led0_task(void *pvParameters)
{
    while(1)
    {
		TaskRun1=1;
		TaskRun2=0;
		LED0=~LED0;
		printf("Task1Running\n");
        vTaskDelay(500);
    }
}   
 
//LED1任务函数
void led1_task(void *pvParameters)
{
    while(1)
    {
		TaskRun1=0;
		TaskRun2=1;
		LED1=~LED1;
		printf("Task2Running\n");
        vTaskDelay(800);
    }
}

成功点亮! 

 

物联沃分享整理
物联沃-IOTWORD物联网 » FreeRTOS移植STM32超详细(以STM32F103ZE为例)

发表评论