首页 > 其他分享 >FreeRTOS移植到STM32

FreeRTOS移植到STM32

时间:2024-07-03 22:31:39浏览次数:1  
标签:Task FreeRTOS void STM32 任务 文件夹 源码 移植

  本内容主要是讲解关于如果把FreeRTOS移植到STM32中去的操作。明白各部分的作用以及打通思路,具体操作按照下列进行相应的操作。

第一:早一个STM32的裸机程序

  我们这里用的是STM32F103的芯片为例。

 二、去官网上下载FreeRTOS V9.0.0 源码

  在移植之前,我们首先要获取到 FreeRTOS 的官方的源码包。这里我们提供两个下载 链 接 , 一 个 是 官 网 : http://www.freertos.org/ , 另 外 一 个 是 代 码 托 管 网 站 : https://sourceforge.net/projects/freertos/files/FreeRTOS/。虽然不是最新版本的源码包但是因为内核很稳定, 并且网上资料很多所以我们选用V9.0.0 版本

我们打开 FreeRTOS 的代码托管网站,就可以看到 FreeRTOS 的源码及其版本信息了, 具体见图

然后我们点开V9.0.0下载zip这个

然后我们进行解压完成后就会得到一个完整的Freertos源码包

 在FeerTOS这个文件包中间,含有多个文件夹。它们分别是Demo,Source这2个文件比较重要。

Demo

  首先是Demo文件,它是FeerTOS例程和内核源码(这个文件是比较重要的)

FreeRTOS 文件夹下的 Demo 文件夹里面包含了 FreeRTOS 官方为各个单片机移植好的工程代码,FreeRTOS 为了 推广自己,会给各种半导体厂商的评估板写好完整的工程程序,这些程序就放在 Demo 这 个目录下,这部分 Demo 非常有参考价值。我们把 FreeRTOS 到 STM32 的时候, FreeRTOSConfig.h 这个头文件就是从这里拷贝过来的。所以这个文件里面的参考价值还是非常好的,可以帮助更好的使用。

Source

  FreeRTOS 文件夹下的 Source 文件夹里面包含的是 FreeRTOS 内 核的源代码,我们移植 FreeRTOS 的时候就需要这部,其中还有我们需要的很多文件在下面的操作过程中我们也会提到。

 

 

三、往裸机工程添加 FreeRTOS 源码

  首先我们应该在我们stm32的裸 机工 程模 板根 目录 下新 建一 个文 件夹, 命名 为 “FreeRTOS”,并且在 FreeRTOS 文件夹下新建两个空文件夹,分别命名为“src” 与“port”,src 文件夹用于保存 FreeRTOS 中的核心源文件,也就是我们常说的 ‘.c 文件’,port 文件夹用于保存内存管理以及处理器架构相关代码,这些代码 FreeRTOS 官方已经提供给我们的,直接使用即可,在前面已经说了,FreeRTOS 是软件,我们的开发版是硬件,软硬件必须有桥梁来连接,这些与处理器架构相 关的代码,可以称之为 RTOS 硬件接口层,它们位于 FreeRTOS/Source/Portable 文 件夹下。

 

1. 然后我们需要打开 FreeRTOS V9.0.0 源码,在“FreeRTOSv9.0.0\FreeRTOS\Source”目录下找到 所有的‘.c 文件’,将它们拷贝到我们新建的 src 文件夹中,

 

2.然后再打开 FreeRTOS V9.0.0 源码,在“FreeRTOSv9.0.0\FreeRTOS\Source\portable”目 录下找到“MemMang”文件夹与“RVDS”文件夹,将它们拷贝到我们新建的 port 文件夹中,

 

 

 

 

 

 

3. 打开 FreeRTOS V9.0.0 源码,在“FreeRTOSv9.0.0\ FreeRTOS\Source”目录下找到 “include”文件夹,它是我们需要用到 FreeRTOS 的一些头文件,将它直接拷贝 到我们新建的 FreeRTOS 文件夹中,完成这一步之后就可以看到我们新建的 FreeRTOS 文件夹已经有 3 个文件夹,这 3个文件夹就包含 FreeRTOS 的核心文件, 至此,FreeRTOS 的源码就提取完成,

 

 

 

 

4.拷贝 FreeRTOS 到裸机工程根目录

鉴于 FreeRTOS 容量很小,我们直接将刚刚提取的整个 FreeRTOS 文件夹拷贝到我们 的 STM32 裸机工程里面,让整个 FreeRTOS 跟随我们的工程一起发布,使用这种方法打包 的 FreeRTOS 工程,即使是将工程拷贝到一台没有安装 FreeRTOS 支持包(MDK 中有 FreeRTOS 的支持包)的电脑上面都是可以直接使用的,因为工程已经包含了 FreeRTOS 的 源码。

 

5.拷贝 FreeRTOSConfig.h 文件到 user 文件夹

FreeRTOSConfig.h 文件是 FreeRTOS 的工程配置文件,因为 FreeRTOS 是可以裁剪的 实时操作内核,应用于不同的处理器平台,用户可以通过修改这个 FreeRTOS 内核的配置 头文件来裁剪 FreeRTOS 的功能,所以我们把它拷贝一份放在 user 这个文件夹下面。 打开 FreeRTOSv9.0.0 源码,在“FreeRTOSv9.0.0\FreeRTOS\Demo”文件夹下面找到 “ CORTEX_STM32F103_Keil ” 这 个 文 件 夹 , 双 击 打 开 , 在 其 根 目 录 下 找 到 这 个 “FreeRTOSConfig.h”文件,然后拷贝到我们工程的 user 文件夹下即可,

 

 

三.添加 FreeRTOS 源码到工程组文件夹

1.在上一步我们只是将 FreeRTOS 的源码放到了本地工程目录下,还没有添加到开发环 境里面的组文件夹里面,FreeRTOS 也就没有移植到我们的工程中去。新建 FreeRTOS/src 和 FreeRTOS/port 组。

 

 

2.接下来我们在开发环境里面新建 FreeRTOS/src 和 FreeRTOS/port 两个组文件夹中,在 FreeRTOS/src 用于存放 src 文件夹的内容

 

 

3.FreeRTOS/port 用于存放 port\MemMang 文件夹 与 port\RVDS\ARM_CM?文件夹的内容,“?”表示 3、4 或者 7,具体选择哪个得看你 使用的是哪个型号的 STM32 开发板,如果是F3系列就拷贝port\RVDS\ARM_CM3F4系列就拷贝port\RVDS\ARM_CM4,然后我们将工程文件中 FreeRTOS 的内容添加到工程中去,按照已经新建的分组添加 我们的 FreeRTOS 工程源码。 在 FreeRTOS/port 分组中添加 MemMang 文件夹中的文件只需选择其中一个即可,我 们选择“heap_4.c”,这是 FreeRTOS 的一个内存管理源码文件。同时,需要根据自己的开 发板型号在 FreeRTOS\port\RVDS\ARM_CM?中选择,“?”表示 3、4 或者 7,具体选择 哪个得看你使用的是哪个型号的 STM32 开发板。

 

4.然后在 user 分组中添加我们 FreeRTOS 的配置文件“FreeRTOSConfig.h”,

 四.指定 FreeRTOS 头文件的路径

  接下来我们需要指定FeerRTOS中文件的一些头文件,不然会报错

 

 

 

到这里后我们编译代码,还是会报错,我们还需要进行修改

 

  五.FreeRTOSConfig.h 文件修改

  

  FreeRTOSConfig.h 头文件的内容修改的不多,具体是:修改与对应开发板的头文件 , 如果是使用野火 STM32F1 的开发板,则包含 F1 的头文件#include "stm32f10x.h",同理是 使用了其它系列的开发板,则包含与开发板对应的头文件即可,

 

 

然后进行修改 stm32f10x_it.SysTick 中断服务函数是一个非常重要的函数,FreeRTOS 所有跟时间相关的事情都在 里面处理,SysTick 就是 FreeRTOS 的一个心跳时钟,驱动着 FreeRTOS 的运行,就像人的 心跳一样,假如没有心跳,我们就相当于“死了”,同样的,FreeRTOS 没有了心跳,那么 它就会卡死在某个地方,不能进行任务调度,不能运行任何的东西,因此我们需要实现一 个 FreeRTOS 的心跳时钟,FreeRTOS 帮我们实现了 SysTick 的启动的配置:在 port.c 文件 中已经实现 vPortSetupTimerInterrupt()函数,并且 FreeRTOS 通用的 SysTick 中断服务函数 也实现了:在 port.c 文件中已经实现 xPortSysTickHandler()函数,所以移植的时候只需要我 们在 stm32f10x_it.c 文件中实现我们对应(STM32)平台上的 SysTick_Handler()函数即可。 FreeRTOS 为开发者考虑得特别多,PendSV_Handler()与 SVC_Handler()这两个很重要的函 数都帮我们实现了,在 port.c 文件中已经实现 xPortPendSVHandler()与 vPortSVCHandler() 函数,防止我们自己实现不了,那么在 stm32f10x_it.c 中就需要我们注释掉 PendSV_Handler()与 SVC_Handler()这两个函数了,具体看一下代码

 

 

到这里我们就修改的差不多了

 

六.修改main.c里面的代码进行测试

 

  在这里我创建了2个任务,一个是实现点灯的操作。一个是OLED屏幕显示数字的操作。大家可以根据自己的需要进行修改

/*
*************************************************************************
* 包含的头文件
*************************************************************************
*/
/* FreeRTOS头文件 */
#include "stm32f10x.h" // Device header
#include "FreeRTOS.h"
#include "task.h"
/* 开发板硬件bsp头文件 */
#include "LED.h"
#include "OLED.h"
/**************************** 任务句柄 ********************************/
/*
* 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
* 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
* 这个句柄可以为NULL。
*/
/* 创建任务句柄 */
static TaskHandle_t AppTaskCreate_Handle = NULL;
/* LED任务句柄 */
static TaskHandle_t LED_Task_Handle = NULL;
/* OLED任务句柄 */
static TaskHandle_t OLED_Task_Handle = NULL;

/********************************** 内核对象句柄 *********************************/
/*
* 信号量,消息队列,事件标志组,软件定时器这些都属于内核的对象,要想使用这些内核
* 对象,必须先创建,创建成功之后会返回一个相应的句柄。实际上就是一个指针,后续我
* 们就可以通过这个句柄操作这些内核对象。
*
* 内核对象说白了就是一种全局的数据结构,通过这些数据结构我们可以实现任务间的通信,
* 任务间的事件同步等各种功能。至于这些功能的实现我们是通过调用这些内核对象的函数
* 来完成的
*
*/


/******************************* 全局变量声明 ************************************/
/*
* 当我们在写应用程序的时候,可能需要用到一些全局变量。
*/


/*
*************************************************************************
* 函数声明
*************************************************************************
*/
static void AppTaskCreate(void);/* 用于创建任务 */

static void LED_Task(void* pvParameters);/* LED_Task任务实现 */

static void OLED_Task(void* pvParameters);/* LED_Task任务实现 */

static void BSP_Init(void);/* 用于初始化板载相关资源 */

/*****************************************************************
* @brief 主函数
* @param 无
* @retval 无
* @note 第一步:开发板硬件初始化
第二步:创建APP应用任务
第三步:启动FreeRTOS,开始多任务调度
****************************************************************/
int main(void)
{
/* 开发板硬件初始化 */
BSP_Init();

/* 创建AppTaskCreate任务 */
xTaskCreate((TaskFunction_t )AppTaskCreate, /* 任务入口函数 */
(const char* )"AppTaskCreate",/* 任务名字 */
(uint16_t )512, /* 任务栈大小 */
(void* )NULL,/* 任务入口函数参数 */
(UBaseType_t )1, /* 任务的优先级 */
(TaskHandle_t* )&AppTaskCreate_Handle);/* 任务控制块指针 */

vTaskStartScheduler(); /* 启动任务,开启调度 */

}


/***********************************************************************
* @ 函数名 : AppTaskCreate
* @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面
* @ 参数 : 无
* @ 返回值 : 无
**********************************************************************/
static void AppTaskCreate(void)
{
taskENTER_CRITICAL(); //进入临界区

/* 创建LED_Task任务 */
xTaskCreate((TaskFunction_t )LED_Task, /* 任务入口函数 */
(const char* )"LED_Task",/* 任务名字 */
(uint16_t )512, /* 任务栈大小 */
(void* )NULL, /* 任务入口函数参数 */
(UBaseType_t )2, /* 任务的优先级 */
(TaskHandle_t* )&LED_Task_Handle);/* 任务控制块指针 */

/* 创建OLED_Task任务 */
xTaskCreate((TaskFunction_t )OLED_Task, /* 任务入口函数 */
(const char* )"OLED_Task",/* 任务名字 */
(uint16_t )512, /* 任务栈大小 */
(void* )NULL, /* 任务入口函数参数 */
(UBaseType_t )3, /* 任务的优先级 */
(TaskHandle_t* )&OLED_Task_Handle);/* 任务控制块指针 */

vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务

taskEXIT_CRITICAL(); //退出临界区
}

 

/**********************************************************************
* @ 函数名 : LED_Task
* @ 功能说明: LED_Task任务主体
* @ 参数 :
* @ 返回值 : 无
********************************************************************/
static void LED_Task(void* parameter)
{
while (1)
{
LED1_ON();
vTaskDelay(500); /* 延时500个tick */
LED1_OFF();
vTaskDelay(500); /* 延时500个tick */
}
}

/**********************************************************************
* @ 函数名 : LED_Task
* @ 功能说明: LED_Task任务主体
* @ 参数 :
* @ 返回值 : 无
********************************************************************/
static void OLED_Task(void* parameter)
{
while (1)
{ static uint8_t A=0;
OLED_ShowNum(2, 1, A,2);
A=A++;
vTaskDelay(98); /* 延时500个tick */
}
}

/***********************************************************************
* @ 函数名 : BSP_Init
* @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面
* @ 参数 :
* @ 返回值 : 无
*********************************************************************/
static void BSP_Init(void)
{
/*
* STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15
* 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断,
* 都统一用这个优先级分组,千万不要再分组,切忌。
*/
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );

/* LED 初始化 */
LED_Init();
/* OLED 初始化 */
OLED_Init();

}

/********************************END OF FILE****************************/

 

注意这里我使用的是标准库,代码逻辑是这样大家可以根据自己的需求进行修改

 

 

声明:本文引用博主的一部分解释,加上自己的理解整理出来的,为了自己对这方面知识的加深。
原文链接:https://blog.csdn.net/qq_61672347/article/details/125529482

 

标签:Task,FreeRTOS,void,STM32,任务,文件夹,源码,移植
From: https://www.cnblogs.com/zfcm-0117-12/p/18281654

相关文章

  • STM32F407如何点亮一个呼吸灯(库函数)
    /*********************************************************************************@filemain.c*@author*@version*@date2024/07/03*@brief实现利用基本定时器TIM14实现定时10ms,每隔10ms从灭到亮逐渐变亮,再隔10ms从 亮到......
  • STM32基本定时器、通用定时器、高级定时器区别
    一.STM32基本定时器、通用定时器、高级定时器区别STM32系列微控制器中的定时器资源分为基本定时器(BasicTimer)、通用定时器(GeneralPurposeTimer)和高级定时器(AdvancedTimer)三类,它们在功能和复杂性上有所不同。以下是这三类定时器的详细区别:1.基本定时器(BasicTimer)功能特......
  • stm32学习笔记---USART串口外设(代码部分)串口发送/串口发送+接收
    目录第一个代码:串口发送初始化串口的步骤USART的库函数三个初始化函数USART_ClockInit和USART_ClockStructInitUSART_Cmd和USART_ITConfigUSART_DMACmdUSART_SendData和USART_ReceiveData四个标志位相关的函数代码实现Serial.c第一步,开启时钟第二步,GPIO初始化第......
  • stm32学习笔记---USART串口数据包(理论部分)
    目录Hex数据包第一种是固定包长,含包头包尾第二种是可变包长,含包头包尾收发过程中的问题第一个问题就是包头包尾和数据载荷重复的问题第一种方法,限制载荷数据的范围第二种方法,如果无法避免载荷数据和包头包尾重复,就尽量使用固定长度的数据包第三种方法,就是增加包头包尾......
  • STM32远程烧录程序
    目录简介该篇将会从零基础开始像读者讲解怎么使用远程的方法来烧录STM32程序。我这里用的是ESP8266和STM32F407ZGT6,当然,使用其他32的芯片也是可以的,核心都是一样的。不同的程序下载方式目前,单片机的程序烧录方式可以分为三种:ICP,ISP,IAP。ICP:In-CircuitProgramming在电......
  • 【STM32F1例程10】UCOSII系统实验
      那么这个实验,从项目的工程结构来看,其实稍微稍微有一丢丢,有一丢丢比之前几个实验复杂,但是还是老话,既然能读到这篇文章,证明能力还是得到认可的。实验简介  那么在STM32上进行uC/OS-II系统实验是一种常见的实践,可以帮助大家了解和应用实时操作系统(RTOS)在嵌入式系统开......
  • esp32-s3+GC9A01基于vs code+PlatformIO+ardunio框架+Squareline UI 移植
    文章目录前言一、SquarelineStudio是什么?二、使用步骤1.创建测试的UI2.工程导出3.工程移植总结前言本节描述了使用SquarelineStudio创建简单UI,导出文件后如何与LVGL集成并在PlatformIO环境中进行编译和下载过程。环境如下:platform=espressif32board=esp32-s......
  • 040【GD32F470】 灰度传感器移植成功示例
    【1TB嵌入式学习资料免费领取:https://link3.cc/sgzy】2.40灰度传感器2.40.1模块来源2.40.2规格参数工作电压:3.3V-5V工作电流:<20mA输出格式:模拟信号输出控制接口:ADC管脚数量:3Pin(2.54mm间距排针)2.40.3移植过程我们的目标是在梁山派GD32F470上能够判断当前环境......
  • FreeRTOS静态创建任务分析
    #defineconfigSUPPORT_STATIC_ALLOCATION        1  设置了静态创建任务需要重新定义这2个函数,由程序员进行分配任务空间(空闲任务)(定时任务)在调度器里被使用这2个函数空闲任务定时任务定义空闲分配空间函数和定时分配空间函数静态创建任务内部......
  • STM32串口如何输出中文
    当你想在串口调试助手实现换行功能时却不行时,试一试将\n改为\r\n因为我用的是XCOM串口调试助手,就遇到了这样的问题而当你加入intfputc(intch,FILE*f)函数却实现不了printf,putchar调用时需要加入#include<stdio.h>并勾选魔术棒中的UseMicroLIBintfputc(intch,FILE*f)......