基于STM32F103标准库移植FreeRTOS教程

前言

本教程只针对移植实操,关于理论部分请另行百度学习。

本教程移植实操基于正点原子源码、教程手册进程移植。

移植实操

1、将正点原子跑马灯程序源码复制一份,添加一个文件夹对FreeRTOS源码进行管理

2、将FreeRTOS源码下的Source文件夹下内的所有文件复制到FreeRTOS文件夹下。

FreeRTOSv202112.00\FreeRTOS\Source

3、对portable文件夹内进行裁剪,只保留keil、MenMang和RVDS 三个文件夹

4、打开工程添加分组,如下:

分组 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 的移植。

5、添加FreeRTOS头文件路径

6、添加“FreeRTOSConfig.h”文件

果然!官方的移植工程中有这个文件,我们可以使用这个文件,但是建议大家使用我们例程中的 FreeRTOSConf.h 文件,这个文件是 FreeRTOS 的系统配置文件,不同的平台其配置不同。这个文件复制到什么地方大家可以自行决定,这里我为了方便放到了 FreeRTOS 源码中的 include 文件夹下。

FreeRTOSConfig.h 是何方神圣?看名字就知道,他是 FreeRTOS 的配置文件,一般的操作

系统都有裁剪、配置功能,而这些裁剪及配置都是通过一个文件来完成的,基本都是通过宏定

义来完成对系统的配置和裁剪的。

这里我是直接复制了正点原子移植裁剪好的 “FreeRTOSConfig.h”文件

7、到这里我们就可以进行编译,如果出现如下错误,则可以将C/C++下的C99勾选上,具体原因可自行百度,这里不赘述。

若编译后又出现如下错误:

解决办法,将stm32f10x_it.c 文件下的这两个函数注释掉

8、修改SYSTEM文件

因为我是基于正点原子的源码进行FreeRTOS的移植,所以需要修改

原子手册原话:

SYSTEM 文件夹里面的文件一开始是针对 UCOS 而编写的,所以如果使用 FreeRTOS 的话

就需要做相应的修改。本来打算让 SYSTEM 文件夹也支持 FreeRTOS,但是这样的话会导致

SYSTEM 里面的文件太过于复杂,这样非常不利于初学者学习,所以这里就专门针对 FreeRTOS

修改了 SYSTEM 里面的文件。

1、修改 sys.h 文件

sys.h 文件修改很简单,在 sys.h 文件里面用宏 SYSTEM_SUPPORT_OS 来定义是否使用 OS,

我们使用了 FreeRTOS,所以应该将宏 SYSTEM_SUPPORT_OS 改为 1。


//0,不支持 os
//1,支持 os
#define SYSTEM_SUPPORT_OS 1 
//定义系统文件夹是否支持 OS

2、修改 usart.c 文件

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

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


//如果使用 os,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
#include "FreeRTOS.h" 
//os 使用 
#endif

另外一个就是 USART1 的中断服务函数,在使用 UCOS 的时候进出中断的时候需要添加

OSIntEnter()和 OSIntExit(),使用 FreeRTOS 的话就不需要了,所以将这两行代码删除掉,修改

以后如下:


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

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

FreeRTOS 的心跳就是由滴答定时器产生的,根据 FreeRTOS 的系统时钟节拍设置好滴答定

时器的周期,这样就会周期触发滴答定时器中断了。在滴答定时器中断服务函数中调用

FreeRTOS 的 API 函数 xPortSysTickHandler()。

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


//初始化延迟函数
//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 
}

前面我们说了 FreeRTOS 的系统时钟是由滴答定时器提供的,那么肯定要根据 FreeRTOS 的

系统时钟节拍来初始化滴答定时器了,delay_init()就是来完成这个功能的。FreeRTOS 的系统时

钟节拍由宏 configTICK_RATE_HZ 来设置,这个值我们可以自由设置,但是一旦设置好以后我

们就要根据这个值来初始化滴答定时器,其实就是设置滴答定时器的中断周期。在基础例程中

滴答定时器的时钟频率设置的是 AHB 的 1/8,这里为了兼容 FreeRTOS 将滴答定时器的时钟频

率改为了 AHB,也就是 72MHz!这一点一定要注意!

接下来的三个函数都是延时的,代码如下:


//延时 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_us()是 us 级延时函数,delay_ms 和 delay_xms()都是 ms 级的延时函数,delay_us()和

delay_xms()不会导致任务切换。delay_ms()其实就是对 FreeRTOS 中的延时函数 vTaskDelay()的

简单封装,所以在使用 delay_ms()的时候就会导致任务切换。

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

该错误上面也有提到,对应修改即可

如图的错误提示表示在 port.c、delay.c 和 stm32f10x_it.c 中三个重复定义的函数:

SysTick_Handler()、SVC_Handler()和 PendSV_Handler(),这三个函数分别为滴答定时器中断服

务函数、SVC 中断服务函数和 PendSV 中断服务函数,将 stm32f10x_it.c 中的三个函数屏蔽掉,

修改完成后整个delay.c文件内容如下:


#include "delay.h"
//      
//如果需要使用OS,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
#include "FreeRTOS.h"                    //FreeRTOS使用          
#include "task.h"
#endif

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


extern void xPortSysTickHandler(void);


 
//systick中断服务函数,使用ucos时用到
void SysTick_Handler(void)
{    
    if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
    {
        xPortSysTickHandler();    
    }
}
               
//初始化延迟函数
//当使用OS的时候,此函数会初始化OS的时钟节拍
//SYSTICK的时钟固定为HCLK时钟的1/8
//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数.                                               
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数
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));                //普通方式延时
}

再次编译代码,应该没有错误了,如果还是错误的话自行根据错误类型修改!至此,SYSTEM

文件夹就修改完成了,至此FreeRTOS的移植实操到此结束了。

移植好的工程文件

链接:https://pan.baidu.com/s/1ai0CDkk4-ejmKLKtLuRulQ 
提取码:czv1

  • 4
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值