我刚学FreeROTS时想移植到STM32,找了网上很多资料,但大多都不是很完整,于是我把我自己的移植过程分享出来,供大家参考。
我们以STM32F103ZE,正点原子的跑马灯实验为例,
准备工作:
跑马灯实验工程
FreeRTOS文件源码(可在官方下载)
文章来源地址https://www.toymoban.com/news/detail-475211.html
第一步 移植文件到工程
首先在工程目录新建一个名为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);
}
}
成功点亮! 文章来源:https://www.toymoban.com/news/detail-475211.html
到了这里,关于FreeRTOS移植STM32超详细(以STM32F103ZE为例)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!