当前位置: 首页 > news >正文

《基于STMF103的FreeRTOS内核移植》

目录

1.FreeRTOS资料下载与出处

  1.1官网下载,网址:www.freertos.org

   1.2在正点原子官网,任意STM32F1的开发板资料A盘里,

2.FreeRTOS移植重要文件讲解

   2.1  FreeRTOS与FreeRTOS-Plus文件夹

   2.2  Demo、Lincence、Source

       ●Demo文件夹

●Lincese文件夹

● Source文件夹

 在Source文件夹下各个文件的含义

3.FreeRTOS移植STM32F103单片机

3.1 准备工作

   3.1.1 准备基础工程

3.1.2 FreeRTOS 系统源码获取

3.2FreeRTOS移植

   3.2.1 向工程中添加相应文件

3.2.2 向工程分组中添加文件

3.2.3 添加相应的头文件路径

3.3 修改 SYSTEM 文件

3.3.1 修改 sys.h 文件

3.3.2 修改 usart.c 文件

3.3.3 修改 delay.c 文件

xTaskGetSchedulerState()函数报错,

taskSCHEDULER_NOT_STARTED没有定义

《FreeRTOS初步移植出现的xTaskGetSchedulerState()函数报错,taskSCHEDULER_NOT_STARTED没有定义的问题》

3.4 移植验证实验

总结:

下面是我所移植成功的代码:


1.FreeRTOS资料下载与出处

  1.1官网下载,网址:www.freertos.org

  

   1.2在正点原子官网,任意STM32F1的开发板资料A盘里,

下载打开文件后,找到软件资料,打开

里面有一个FreeRTOS源码文件夹,点入会看到FreeRTOSv~.~.~(由于文件会更新,所以这里版本用~.~.~替代),如图片所示,即可下载FreeRTOS源码。

注意!FreeRTOS版本更新非常快!从正点原子官方资料下载的FreeRTOS版本低是非常正常的,强烈建议若是初学FreeRTOS,可以从正点原子的官方文档去下载并学习。

2.FreeRTOS移植重要文件讲解

   2.1  FreeRTOS与FreeRTOS-Plus文件夹

           对于打开FreeRTOS文档,我们可以看到FreeRTOS和FreeRTOS-Plus两份文件夹,前者的含义是FreeRTOS源文件,后者的含义是FreeTROS拓展函数,对于初学者而言,只要关注FreeTROS文件夹就够了。

 

   2.2  Demo、Lincence、Source

       ●Demo文件夹

       Demo 文件夹里面就是 FreeRTOS 的相关例程,

 可以看出 FreeRTOS 针对不同的 MCU 提供了非常多的 Demo,其中就有 ST F1F4 F7 的相关例程,这对于我们学习来说是非常友好的,我们在移植的时候就会参考这些例程。

●Lincese文件夹

这个文件夹里面就是相关的许可信息,要用 FreeRTOS 做产品的得仔细看看,尤其是要出口的产品

● Source文件夹

看名字就知道了,这个就是 FreeRTOS 的本尊了。

 在Source文件夹下各个文件的含义

其中红色加粗是必须要添加进去的,其他可用可不用,根据自己的使用需求

FreeRTOS是一个系统,归根结底就是一个纯软件的东西,include文件不必多说,重点是portable文件,他是软件和硬件链接的一个桥梁,不同的编译环境,不同的 MCU,其桥梁是不同的,故而需要我们在移植的时候格外注意。

可以看出 FreeRTOS 针对不同的编译环境和 MCU 都有不同的“桥梁”,我们这里就以 MDK 编译环境下的 STM32F103 为例。MemMang 这个文件夹是跟内存管理相关的,我们移植的时候是必须的,具体内容我们后面会专门有一章来讲解。

Keil 文件夹里面的东西肯定也是必须的,但是我们打开Keil文件夹以后里面只有一个文件:See-also-the-RVDS-directory.txt

这个 txt 文件是什么鬼?别急嘛!看文件名字“See-also-the-RVDS-directory”,意思就是参考RVDS文件夹里面的东西!哎,好吧,再打开 RVDS 文件夹。

RVDS 文件夹针对不同的架构的 MCU 做了详细的分类,STM32F103就参考ARM_CM3,打开 ARM_CM3 文件夹

ARM_CM3 有两个文件,这两个文件就是我们移植的时候所需要的!

以上就是FreeRTOS移植所用到的的,重要的文件讲解。

3.FreeRTOS移植STM32F103单片机

以上说完了各个文件的含义,就开始移植辣!

这里以 ALIENTEK STM32F103 开发板为

分为如下几部分:

1)准备工作

2)FreeRTOS 移植

3)移植验证实验

3.1 准备工作

   3.1.1 准备基础工程

要移植 FreeRTOS,肯定需要一个基础工程,基础工程越简单越好,这里我们就用基础例程库函数中的跑马灯实验来作为基础工程。

3.1.2 FreeRTOS 系统源码获取

FreeRTOS 的系统源码已经详细的讲解过如何获取了,这里我们会将 FreeRTOS 的系统源码开发板光盘的库函数的跑马灯的实例工程中去。

3.2FreeRTOS移植

   3.2.1 向工程中添加相应文件

添加 FreeRTOS 源码

在基础工程中新建一个名为 FreeRTOS 的文件夹

创建 FreeRTOS 文件夹以后就可以将 FreeRTOS 的源码添加到这个文件夹中,添加完以后

如图:

这里有些.c文件可以不添加,可以不添加的文件前文已经说明,这里因为是实例,所以都添加。

注意!,关于portable文件夹,我们只需要留下keil、MemMang和RVDS这三个文件夹,其他的都可以删除掉,完成后如下图:

 

 注意!portable留下这三个文件夹,如果用的其他编译器或者MCU的话请根据实际情况选择

3.2.2 向工程分组中添加文件

打开基础工程,新建分组 FreeRTOS_CORE 和 FreeRTOS_PORTABLE,然后向这两个分组

中添加文件,如图:

分组 FreeRTOS_CORE 中的文件在什么地方就不说了,打开 FreeRTOS 源码一目了然。

重点来说说 FreeRTOS_PORTABLE 分组中的 port.c 和 heap_4.c 是怎么来的,port.c 是 RVDS 文件 夹下的 ARM_CM3 中的文件,因为 STM32F103 是 Cortex-M3 内核的,因此要选择ARM_CM3 中的 port.c 文件。heap_4.c 是 MemMang 文件夹中的,前面说了 MemMang 是跟内存管理相关 的,里面有 5 个 c 文件:heap_1.c、heap_2.c、heap_3.c、heap_4.c 和 heap_5.c。这 5 个 c 文件是 五种不同的内存管理方法,就像从北京到上海你可以坐火车、坐飞机,如果心情好的话也可以 走路,反正有很多种方法,只要能到上海就行。这里也一样的,这 5 个文件都可以用来作为 FreeRTOS 的内存管理文件,只是它们的实现原理不同,各有利弊。这里我们选择 heap_4.c,至 于原因,会涉及到FreeRTOS 的内存管理的内容,这里就不多赘述。

3.2.3 添加相应的头文件路径

添加完 FreeRTOS 源码中的 C 文件以后还要添加 FreeRTOS 源码的头文件路径,头文件路

径,如图:

头文件路径添加完成以后编译一下,看看有没有什么错误,结果会发现提示打不开 “FreeRTOSConfig.h”这个文件,如图所示:

 这是因为缺少 FreeRTOSConfig.h 文件,这个文件在哪里找呢?你可以自己创建,显然这不是一个明智的做法。

我们可以找找 FreeRTOS 的官方移植工程中会不会有这个文件,打开FreeRTOS 针对STM32F103 的移植工程文件,文件夹是 CORTEX_STM32F103_Keil,打开以后会官方的移植工程中有这个文件,我们可以使用这个文件,但是建议大家使用我们例程中的 FreeRTOSConf.h 文件,这个文件是 FreeRTOS 的系统配置文件,不同的平台其配置不同,但是我们提供的例程中的这个文件肯定是针对 ALIENTEK 开发板配置正确的。这个文件复制到什么地方大家可以自行决定,这里我为了方便放到了 FreeRTOS 源码include文件夹下。

FreeRTOSConfig.h 是何方神圣?看名字就知道,他是 FreeRTOS 的配置文件,一般的操作系统都有裁剪、配置功能,而这些裁剪及配置都是通过一个文件来完成的,也就是FreeRTOSConfig.h

     到这里我们再编译一次,没有错误!

3.3 修改 SYSTEM 文件

     SYSTEM 文件夹里面的文件一开始是针对 UCOS 而编写的,所以如果使用 FreeRTOS 的话就需要做相应的修改。本来打算让 SYSTEM 文件夹也支持 FreeRTOS,但是这样的话会导致SYSTEM 里面的文件太过于复杂,这样非常不利于初学者学习,所以这里就专门针对 FreeRTOS修改了 SYSTEM 里面的文件。

3.3.1 修改 sys.h 文件

 sys.h 文件修改很简单,在 sys.h 文件里面用宏 SYSTEM_SUPPORT_OS 来定义是否使用 OS,我们使用了 FreeRTOS,所以应该将宏 SYSTEM_SUPPORT_OS 改为 1。

3.3.2 修改 usart.c 文件

usart.c 文件修改也很简单,usart.c 文件有两部分要修改,一个是添加 FreeRTOS.h 头文件,

默认是添加的 UCOS 中的 includes.h 头文件,修改以后如下

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

3.3.3 修改 delay.c 文件

delay.c 文件修改的就比较大了,因为涉及到 FreeRTOS 的系统时钟,delay.c 文件里面有 4

个函数需要修改,还要添加应该函数,分别是,SysTick_Handler(),delay_init() delay_us(u32nus) delay_ms(u32 nms),delay_xms(u32 nms) 

delay.c的代码如下:

#include "delay.h"#include "FreeRTOS.h"					//ucos 使用	  
#include "task.h"	
//	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//使用SysTick的普通计数模式对延迟进行管理(适合STM32F10x系列)
//包括delay_us,delay_ms
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2010/1/1
//版本:V1.8
//版权所有,盗版必究。
//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的执行,可能导致的延时不准。
//V1.6修改说明 20150109
//在delay_ms加入OSLockNesting判断。
//V1.7修改说明 20150319
//修改OS支持方式,以支持任意OS(不限于UCOSII和UCOSIII,理论上任意OS都可以支持)
//添加:delay_osrunning/delay_ostickspersec/delay_osintnesting三个宏定义
//添加:delay_osschedlock/delay_osschedunlock/delay_ostimedly三个函数
//V1.8修改说明 20150519
//修正UCOSIII支持时的2个bug:
//delay_tickspersec改为:delay_ostickspersec
//delay_intnesting改为:delay_osintnesting
//  static u8  fac_us=0;							//us延时倍乘数			   
static u16 fac_ms=0;							//ms延时倍乘数,在ucos下,代表每个节拍的ms数extern void xPortSysTickHandler(void);
//systick 中断服务函数,使用 OS 时用到
void SysTick_Handler(void)
{ if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行{xPortSysTickHandler();}
}//初始化延迟函数
//SYSTICK 的时钟固定为 AHB 时钟,基础例程里面 SYSTICK 时钟频率为 AHB/8
//这里为了兼容 FreeRTOS,所以将 SYSTICK 的时钟频率改为 AHB 的频率!
//SYSCLK:系统时钟频率
void delay_init()
{
u32 reload;
SysTick_CLKSourceConfig(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 
}//延时 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)
{
//这里注意一下 SYSTICK 是一个递减的计数器就可以了. 
if(tnow<told)tcnt+=told-tnow;
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_xms()是添加的函数,需要在.h中声明,修改SysTick_Handler()时需要在之前添加extern void xPortSysTickHandler(void);

delay_us()us 级延时函数,delay_ms delay_xms()都是 ms 级的延时函数,delay_us()delay_xms()不会导致任务切换。delay_ms()其实就是对 FreeRTOS 中的延时函数 vTaskDelay()的简单封装,所以在使用 delay_ms()的时候就会导致任务切换。

delay.c 修改完成以后编译一下,会提示如图所示错误:

2.2.2.1 的错误提示表示在 port.cdelay.c stm32f10x_it.c 中三个重复定义的函数:

SysTick_Handler()SVC_Handler()PendSV_Handler(),这三个函数分别为滴答定时器中断服务函数、SVC 中断服务函数和 PendSV 中断服务函数,将 stm32f10x_it.c 中的三个函数屏蔽掉,如图所示:

再次编译代码,应该没有错误了,如果还是错误的话,比如

xTaskGetSchedulerState()函数报错

taskSCHEDULER_NOT_STARTED没有定义

等问题,这里我单独写了一个篇章,方便查看:

《FreeRTOS初步移植出现的xTaskGetSchedulerState()函数报错,taskSCHEDULER_NOT_STARTED没有定义的问题》

3.4 移植验证实验

1. 实验目的

编写简单的 FreeRTOS 应用代码,测试 FreeRTOS 的移植是否成功。鉴于大家还没正式学习FreeRTOS,可以直接将本实验代码复制粘贴到自己的移植工程中。

2. 实验设计

本实验设计四个任务:start_task()、led0_task ()、led1_task ()和 float_task(),这四个任务的

任务功能如下:

start_task():用来创建其他三个任务。

led0_task ():控制 LED0 的闪烁,提示系统正在运行。

led1_task ():控制 LED1 的闪烁。

实验主函数代码如下:

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "FreeRTOS.h"
#include "task.h"
//#include "stm32f10x.h"                  // Device header/************************************************
ALIENTEK 战舰 STM32F103 开发板 FreeRTOS 实验 2-1
FreeRTOS 移植实验-库函数版本
技术支持:www.openedv.com
淘宝店铺:http://eboard.taobao.com 
关注微信公众平台微信号:"正点原子",免费获取 STM32 资料。
广州市星翼电子科技有限公司 
作者:正点原子 @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); //任务函数
int main(void)
{NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组 4delay_init(); //延时函数初始化 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); 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);}
}

测试代码中创建了3个任务:LED0测试任务、LED1测试任务和浮点测试任务,它们的任务函数分别为:led0_task()led1_task()led0_task()led1_task()任务很简单,就是让 LED0

LED1 周期性闪烁。 由于我们只是用测试代码来测试FreeRTOS是否移植成功的,所以关于具体的函数的调用方法这些不要深究,后面会有详细的讲解!

实验程序运行结果分析

编译并下载代码到 STM32F103 开发板中,下载进去以后会看到 LED0 和 LED1 开始闪烁LED0 均匀闪烁,那是因为我们在 LED0 的任务代码中设置好的 LED0 亮 500ms,灭 500ms。

LED1 亮的时间短,灭的时间长,这是因为在 LED1 的任务代码中设置好的亮 200ms,灭 800ms。

总结:

        初学FreeRTOS,基本了解其构成,并进行了初步移植,建议大家还是从官方文档开始看,一步步去解析FreeRTOS,虽然Cubemx可以直接生成FreeRTOS,但从学习者的角度来看,我们更应该深入了解FreeRTOS,发挥其裁剪的功能,这样才能与别人拉开差距!希望这篇文章能帮助到大家!

下面是我所移植成功的代码:

通过百度网盘分享的文件:跑马灯实验--Freertos移植
链接:https://pan.baidu.com/s/1-SY4iWqYZJSkQX5LdAOwJQ 
提取码:j72l

http://www.lryc.cn/news/475997.html

相关文章:

  • 一七二、Vue3性能优化方式
  • 软件测试--BUG篇
  • Scikit-learn和Keras简介
  • python在word的页脚插入页码
  • Java面试题十四
  • yarn : 无法加载文件,未对文件 进行数字签名。无法在当前系统上运行该脚本。
  • Hadoop——HDFS
  • 计算机的一些基础知识
  • 学习RocketMQ(记录了个人艰难学习RocketMQ的笔记)
  • 【设计模式】策略模式定义及其实现代码示例
  • list与iterator的之间的区别,如何用斐波那契数列探索yield
  • 抖音店铺数据也就是抖店,如何使用小店数据集来挖掘价值?
  • KubeVirt 安装和配置 Windows虚拟机
  • CM API方式设置YARN队列资源
  • Mysql常用语法一篇文章速成
  • Intel nuc x15 重装系统步骤和注意事项(LAPKC71F、LAPKC71E、LAPKC51E)
  • Linux之实战命令59:iwlist应用实例(九十三)
  • 数据库_SQLite3
  • .Net Framework里演示怎么样使用StringBuilder、Math.Min和String.Format
  • Oracle创建存储过程,创建定时任务
  • <HarmonyOS第一课>应用/元服务上架的课后习题
  • 【Python】探索函数的奥秘:从基础到高级的深度解析(下)
  • ima.copilot:智慧因你而生
  • Vue-$el属性
  • LLC Power Switches and Resonant Tank 笔记
  • Python 如何在 Web 环境中使用 Matplotlib 进行数据可视化
  • C#-数组:一维数组、二维数组、交错数组
  • 动态规划应该如何学习?
  • 【力扣 + 牛客 | SQL题 | 每日4题】牛客SQL热题210,213,212,219
  • Qt 应用开发之 MVC 架构