实现STM32与FreeRTOS操作系统的无缝移植”

一、FreeRTOS源码下载

(1)移植钱得准备前菜对吧,我们先来去官网瞄一瞄

网址:https://freertos.org/zh-cn-cmn-s/

第一步:点击下载FreeRTOS

第二步:选择版本下载(我选择稳定版本)

注:我们下载的稳定版本不包含DEMO例程,可以自己下载上面的那个最新版本,有官方移植好的DEMO,那么最新版本下载过程如下图:

但是,这时候下载完成的压缩波其实并不包含FreeRTOS的子模块。所以,在该页面向下翻找。

第三步:回到我们下载稳定版本的路上,选择GitHub,复制网址可

到这里前菜算是准备完毕了。

二、添加内核文件

第一步:准备一个STM32工程,这里工程越简洁越好(可以参考之前写的创建工程理论与实践偏:

https://blog.csdn.net/qq_38295600/article/details/129372829

https://blog.csdn.net/qq_38295600/article/details/129372829

第二步:准备好工程后,创建一个FreeRTOS文件夹

第三步:打开刚才下载好的稳定版本文件,路径:FreeRTOS-LTS\FreeRTOS\FreeRTOS-Kernel将里面内容复制出来

第四步:将内容粘贴到工程中得FreeRTOS文件中

为了简洁好看,我们在创建个source文件夹把这些.c文件放到这里面

外部这些文件有删除或者留着看自己

现在FreeRTOS源码被我们分成了三个目录,分别是①include目录;②portable目录;③source目录

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

我们知道 FreeRTOS 是个系统,归根结底就是个纯软件的东西,它是怎么和硬件

联系在一起的呢?软件到硬件中间必须有一个桥梁,portable 文件夹里面的东西就是 FreeRTOS

系统和具体的硬件之间的连接桥梁!不同的编译环境,不同的 MCU,其桥梁应该是不同的,打

开 portable 文件夹,我们现在用的MDK,所以我们只需要

最终保留如下这三个文件

第五步:文件添加到工程

注: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,具体可以看看原子的讲解内存视频。

然后由于我用的是stm32f1内核是M3,所以选择如下

第六步:编译工程

直接编译(出现9个错误)这是因为缺少 FreeRTOSConfig.h 文件

FreeRTOS的裁剪主要依靠FreeRTOSConfig.h中的宏定义去完成,需要什么功能就打开对应的宏就可以了。

现在我们需要一个FreeRTOSConfig.h文件。但在源码FreeRTOS-Kernel目录下没有提供config文件。

那么哪里找呢?

我们打开最开始下载好的FreeRTOS文件(最开始下载我们下载了三个文件)

打开第一个FreeRTOS->路径:FreeRTOS\FreeRTOS\Demo

在demo目录下找到对应内核目录下的demo(可能是其它MCU,不要紧)拷贝一个FreeRTOSConfig.h文件到本地source\FreeRTOS\include目录内

重新编译没有错

三、修改工程

虽然没有错误了,但是我们的移植没有完成,还有一些小步骤需要完成,的确有些繁琐,好在逻辑性还是比较强的,理解起来相对容易。

第一步:

在FreeRTOSConfig.h中添加#define xPortPendSVHandler PendSV_Handler

在FreeRTOSConfig.h中添加#define vPortSVCHandler SVC_Handler

编译,还是报错,重复定义

第二步:处理错误: 进入对应的文件stm32f1xx_it.c屏蔽重复的3个函数

把SysTick_Handler也屏蔽起来是因为,等下我们要建立一个Bsp_SysTick.c,到时候这个中断函数也会放在这里面,所以我在这里也给他屏蔽了

重新编译

到这里,算移植成功了!!!,后面还需要做配置操作

四、滴答时钟配置

SysTick中断服务函数是一个非常重要的函数,FreeRTOS所有跟时间相关的事情都在里面处理,SysTick就是FreeRTOS的一个心跳时钟,驱动着FreeRTOS的运行,如果FreeRTOS没有了心跳,那么它就会卡死在某个地方,不能进行任务调度,不能运行任何的东西,因此我们需要实现一个FreeRTOS的心跳时钟

所以我们创建一个

BSP_SysTick.h 和一个BSP_SysTick.c

.c直接上代码块:

#include "BSP_SysTick.h"
#include "FreeRTOS.h"
#include "task.h"
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();
    }
}
   
//初始化延迟函数
//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    
}

.h代码块

#ifndef _BSP_SYSTICK_H
#define _BSP_SYSTICK_H
#include "stm32f10x.h"
#include "stdio.h"


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

void BSP_SysTickInit(void);

#endif

然后我们编译下:成功报错,说明还有问题

这里显示“xTaskGetSchedulerState”这个找不到,可是我们明明声明了头文件

#include "FreeRTOS.h"//FreeRTOS使用

#include "task.h"

这两个,还是报错,那么经过查找关键点在于“FreeRTOSCongfig.h”的配置

新增上这一句,编译就通过了

那么说明一个问题,我们的FreeRTOSCongfig.h配置不完善,得改良,早这里先引用原子的配置,我们先以这个配置进行更新,后面在专门写一篇关于FreeRTOSCongfig.h的功能介绍

更新“FreeRTOSCongfig.h”

/*
    FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd.
    All rights reserved

    VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.

    This file is part of the FreeRTOS distribution.

    FreeRTOS is free software; you can redistribute it and/or modify it under
    the terms of the GNU General Public License (version 2) as published by the
    Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception.

    ***************************************************************************
    >>!   NOTE: The modification to the GPL is included to allow you to     !<<
    >>!   distribute a combined work that includes FreeRTOS without being   !<<
    >>!   obliged to provide the source code for proprietary components     !<<
    >>!   outside of the FreeRTOS kernel.                                   !<<
    ***************************************************************************

    FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY
    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    FOR A PARTICULAR PURPOSE.  Full license text is available on the following
    link: http://www.freertos.org/a00114.html

    ***************************************************************************
     *                                                                       *
     *    FreeRTOS provides completely free yet professionally developed,    *
     *    robust, strictly quality controlled, supported, and cross          *
     *    platform software that is more than just the market leader, it     *
     *    is the industry's de facto standard.                               *
     *                                                                       *
     *    Help yourself get started quickly while simultaneously helping     *
     *    to support the FreeRTOS project by purchasing a FreeRTOS           *
     *    tutorial book, reference manual, or both:                          *
     *    http://www.FreeRTOS.org/Documentation                              *
     *                                                                       *
    ***************************************************************************

    http://www.FreeRTOS.org/FAQHelp.html - Having a problem?  Start by reading
    the FAQ page "My application does not run, what could be wrong?".  Have you
    defined configASSERT()?

    http://www.FreeRTOS.org/support - In return for receiving this top quality
    embedded software for free we request you assist our global community by
    participating in the support forum.

    http://www.FreeRTOS.org/training - Investing in training allows your team to
    be as productive as possible as early as possible.  Now you can receive
    FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers
    Ltd, and the world's leading authority on the world's leading RTOS.

    http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
    including FreeRTOS+Trace - an indispensable productivity tool, a DOS
    compatible FAT file system, and our tiny thread aware UDP/IP stack.

    http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate.
    Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS.

    http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High
    Integrity Systems ltd. to sell under the OpenRTOS brand.  Low cost OpenRTOS
    licenses offer ticketed support, indemnification and commercial middleware.

    http://www.SafeRTOS.com - High Integrity Systems also provide a safety
    engineered and independently SIL3 certified version for use in safety and
    mission critical applications that require provable dependability.

    1 tab == 4 spaces!
*/


#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

#include "BSP_Uart.h"      
//针对不同的编译器调用不同的stdint.h文件
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
    #include <stdint.h>
    extern uint32_t SystemCoreClock;
#endif

//断言
#define vAssertCalled(char,int) printf("Error:%s,%d\r\n",char,int)
#define configASSERT(x) if((x)==0) vAssertCalled(__FILE__,__LINE__)

/***************************************************************************************************************/
/*                                        FreeRTOS基础配置配置选项                                              */
/***************************************************************************************************************/
#define configUSE_PREEMPTION                    1                       //1使用抢占式内核,0使用协程
#define configUSE_TIME_SLICING                    1                        //1使能时间片调度(默认式使能的)
#define configUSE_PORT_OPTIMISED_TASK_SELECTION    1                       //1启用特殊方法来选择下一个要运行的任务
                                                                        //一般是硬件计算前导零指令,如果所使用的
                                                                        //MCU没有这些硬件指令的话此宏应该设置为0!
#define configUSE_TICKLESS_IDLE                    0                       //1启用低功耗tickless模式
#define configUSE_QUEUE_SETS                    1                       //为1时启用队列
#define configCPU_CLOCK_HZ                        (SystemCoreClock)       //CPU频率
#define configTICK_RATE_HZ                        (1000)                  //时钟节拍频率,这里设置为1000,周期就是1ms
#define configMAX_PRIORITIES                    (32)                    //可使用的最大优先级
#define configMINIMAL_STACK_SIZE                ((unsigned short)130)   //空闲任务使用的堆栈大小
#define configMAX_TASK_NAME_LEN                    (16)                    //任务名字字符串长度

#define configUSE_16_BIT_TICKS                    0                       //系统节拍计数器变量数据类型,
                                                                        //1表示为16位无符号整形,0表示为32位无符号整形
#define configIDLE_SHOULD_YIELD                    1                       //为1时空闲任务放弃CPU使用权给其他同优先级的用户任务
#define configUSE_TASK_NOTIFICATIONS            1                       //为1时开启任务通知功能,默认开启
#define configUSE_MUTEXES                        1                       //为1时使用互斥信号量
#define configQUEUE_REGISTRY_SIZE                8                       //不为0时表示启用队列记录,具体的值是可以
                                                                        //记录的队列和信号量最大数目。
#define configCHECK_FOR_STACK_OVERFLOW            0                       //大于0时启用堆栈溢出检测功能,如果使用此功能
                                                                        //用户必须提供一个栈溢出钩子函数,如果使用的话
                                                                        //此值可以为1或者2,因为有两种栈溢出检测方法。
#define configUSE_RECURSIVE_MUTEXES                1                       //为1时使用递归互斥信号量
#define configUSE_MALLOC_FAILED_HOOK            0                       //1使用内存申请失败钩子函数
#define configUSE_APPLICATION_TASK_TAG            0                       
#define configUSE_COUNTING_SEMAPHORES            1                       //为1时使用计数信号量

/***************************************************************************************************************/
/*                                FreeRTOS与内存申请有关配置选项                                                */
/***************************************************************************************************************/
#define configSUPPORT_DYNAMIC_ALLOCATION        1                       //支持动态内存申请
#define configTOTAL_HEAP_SIZE                    ((size_t)(20*1024))     //系统所有总的堆大小

/***************************************************************************************************************/
/*                                FreeRTOS与钩子函数有关的配置选项                                              */
/***************************************************************************************************************/
#define configUSE_IDLE_HOOK                        0                       //1,使用空闲钩子;0,不使用
#define configUSE_TICK_HOOK                        0                       //1,使用时间片钩子;0,不使用

/***************************************************************************************************************/
/*                                FreeRTOS与运行时间和任务状态收集有关的配置选项                                 */
/***************************************************************************************************************/
#define configGENERATE_RUN_TIME_STATS            0                       //为1时启用运行时间统计功能
#define configUSE_TRACE_FACILITY                1                       //为1启用可视化跟踪调试
#define configUSE_STATS_FORMATTING_FUNCTIONS    1                       //与宏configUSE_TRACE_FACILITY同时为1时会编译下面3个函数
                                                                        //prvWriteNameToBuffer(),vTaskList(),
                                                                        //vTaskGetRunTimeStats()
                                                                        
/***************************************************************************************************************/
/*                                FreeRTOS与协程有关的配置选项                                                  */
/***************************************************************************************************************/
#define configUSE_CO_ROUTINES                     0                       //为1时启用协程,启用协程以后必须添加文件croutine.c
#define configMAX_CO_ROUTINE_PRIORITIES         ( 2 )                   //协程的有效优先级数目

/***************************************************************************************************************/
/*                                FreeRTOS与软件定时器有关的配置选项                                            */
/***************************************************************************************************************/
#define configUSE_TIMERS                        1                               //为1时启用软件定时器
#define configTIMER_TASK_PRIORITY                (configMAX_PRIORITIES-1)        //软件定时器优先级
#define configTIMER_QUEUE_LENGTH                5                               //软件定时器队列长度
#define configTIMER_TASK_STACK_DEPTH            (configMINIMAL_STACK_SIZE*2)    //软件定时器任务堆栈大小

/***************************************************************************************************************/
/*                                FreeRTOS可选函数配置选项                                                      */
/***************************************************************************************************************/
#define INCLUDE_xTaskGetSchedulerState          1                       
#define INCLUDE_vTaskPrioritySet                1
#define INCLUDE_uxTaskPriorityGet                1
#define INCLUDE_vTaskDelete                        1
#define INCLUDE_vTaskCleanUpResources            1
#define INCLUDE_vTaskSuspend                    1
#define INCLUDE_vTaskDelayUntil                    1
#define INCLUDE_vTaskDelay                        1
#define INCLUDE_eTaskGetState                    1
#define INCLUDE_xTimerPendFunctionCall            1

/***************************************************************************************************************/
/*                                FreeRTOS与中断有关的配置选项                                                  */
/***************************************************************************************************************/
#ifdef __NVIC_PRIO_BITS
    #define configPRIO_BITS               __NVIC_PRIO_BITS
#else
    #define configPRIO_BITS               4                  
#endif

#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY            15                      //中断最低优先级
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    5                       //系统可管理的最高中断优先级
#define configKERNEL_INTERRUPT_PRIORITY         ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY     ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )

/***************************************************************************************************************/
/*                            移植添加的,FreeRTOS与中断服务函数有关的配置选项                                  */
/***************************************************************************************************************/
#define xPortPendSVHandler     PendSV_Handler
#define vPortSVCHandler     SVC_Handler

#endif /* FREERTOS_CONFIG_H */



到这里我们总体的移植算是完成了!

五、创建任务,点灯实验启动

上代码块:

#include "main.h"
#include "BSP_SysTick.h"    
#include "BSP_Gpio.h"
#include "BSP_Uart.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);    //任务函数



void BoardInit(void)
{
    BSP_SysTickInit();
    BSP_GpioInit();
    BSP_UartInit(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)
    {
        BSP_SetLed0(2);
        vTaskDelay(500);
    }
}   

//LED1任务函数
void led1_task(void *pvParameters)
{
    while(1)
    {
        BSP_SetLed1(2);
        vTaskDelay(800);
    }
}




编译完成

可以看到输出信息正常,同时开发板指示灯在循环闪烁

到此整个FreeRTOS移植完成!!!

六、附录:完整的工程代码

链接:https://pan.baidu.com/s/1DDstmG_wbpAjl8WUP2SxiQ

提取码:c518

物联沃分享整理
物联沃-IOTWORD物联网 » 实现STM32与FreeRTOS操作系统的无缝移植”

发表评论