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

[STM32]从零开始的STM32 FreeRTOS移植教程

一、前言

        如果能看到这个教程的话,说明大家已经学习嵌入式有一段时间了。还记得嵌入式在大多数时候指的是什么吗?是的,我们所说的学习嵌入式大部分时候都是在学习嵌入式操作系统。从简单的一些任务状态机再到复杂一些的RTOS,再到最复杂的Linux,这些都属于嵌入式操作系统的一种,只是简单与复杂的区别。在之前,我们开发STM32时采用的都是裸机开发所以对实时系统并没有什么概念,接触RTOS会接触到一种新的编程方式。当然,因为RTOS已经引入了系统的概念了,所以代码看起来与调试起来也更加抽象。但,就现在而言,实时操作系统仍然是许多公司要求嵌入式工程师必会的技能之一。所以,如果你准备好了,就让我们一起来移植RTOS吧!

二、在开始之前

        因为已经涉及到嵌入式系统了,所以还请学习这篇教程的小伙伴有一定的STM32基础,我始终不推荐纯小白直接接触RTOS。在这篇教程中,我会教大家如何下载RTOS的代码固件包,如何将RTOS移植到对应的单片机中,以及如何解决一些常见的问题。当然,你也可以下载我下面给的资料,资料中会包含本次会用到的RTOS源码包和我已经移植好的工程:

RTOS移植资料:https://pan.baidu.com/s/1YbfUm1LUSomslaGWqJ-g4w?pwd=clxm 
提取码:clxm

三、芯片的选择        

        因为RTOS是一个嵌入式操作系统,RTOS强调的是代码的实时性,比起裸机开发,它并不能提高芯片的性能,相反,它是一个消耗芯片性能的软件。在运行了RTOS以后,我们芯片的资源就被占用了一部分,这也会让我们的可编程范围变小了。既然是这样,那为什么我们还需要嵌入式操作系统呢?当然是为了实时性呀,裸机在开发时,代码始终顺序执行,这也导致了我们有的步骤要等很久才会执行到,在某些特定的情况下就可能出现问题。如果使用实时操作系统,它会合理分配每一个任务的执行时间,保证我们每一个任务都执行一段时间,不会有任务长期阻塞程序。当然,我们这次的重点并不是RTOS的原理,如果想要了解RTOS详细原理与系统性的学习的话,还是建议大家去看一些视频教程。这里话又说回来,因为RTOS会消耗一部分的性能,所以有的芯片可能无法运行RTOS,这里我会使用STM32F407ZGT6进行RTOS的移植演示,我并不推荐大家使用STM32F103C6T6单片机,这款单片机在移植RTOS时可能会出现你无法解决的错误。在移植RTOS时尽量选择RAM与ROM都比较大的芯片。这样代码在编译时也不容易报错。

四、RTOS系统固件包的下载

        RTOS的虽说是一个操作系统,但是本质上还是代码构成的,在这里面我们仍然可以看到.c .h文件。所以,移植RTOS简单来说,就是将对应的C语言代码在STM32中运行起来。所以我们现在需要下载RTOS的固件包,这里我们直接在浏览器中搜索“FreeRTOS”:

我们可以看到,这里搜索出来的第一个网站就是FreeRTOS的官网了:

当然,如果你没有找到FreeRTOS的官网的话,也可以点击下方的链接前往:

FreeRTOS官网:FreeRTOS™ - FreeRTOS™

进入FreeRTOS的官网以后,就可以看到以下页面了:

如果你这里不是中文的,可以点击右上角这里切换语言:

接着,我们点击网页中的“下载”:

点击了下载以后,弹出的窗口中会让我们选择要下载的版本:

这里我推荐大家使用2022或者2020的版本,这些版本经过了几年的迭代已经非常稳定了。如果你是小白的话,建议下载和我一样的版本,以保证文件的统一。

我们选择好版本以后,直接点击“Download”:

随后浏览器就会弹出下载了:

我们将其下载到我们能找到的地方:

如果你这里无法打开RTOS的官网或者是无法下载RTOS的固件包,那么你就可以打开我给的资料中的RTOS固件包文件夹,这里有我已经下载好的固件包:

下面,我们再将下载的压缩包解压得到以下文件夹:

至此,我们RTOS系统的固件包就已经下载完成了。

五、RTOS的移植

        当我们准备好了RTOS系统的固件包以后,就可以开始移植了,这里我们移植RTOS系统是在原本的工程之上,所以,首先就要保证我们有一个对应芯片的对应工程模板。我这里使用的是STM32F407ZGT6芯片,所以准备的也是STM32F407ZGT6的工程,如图:

我们进入工程,这里需要我们的工程编译没有错误:

下面我们直接写一个LED的代码,证明我们的硬件没有问题,这个地方的点灯代码很简单,就不多说了,我的LED在PA12口上,并且为高电平点亮:

将程序编译下载到芯片后,就可以看到LED已经亮起了,证明我们的硬件方面没有问题:

下面,我们就要在这个原本的工程上移植RTOS。

首先需要在工程的主目录下新建一个RTOS的文件夹用来存放RTOS的相关文件:

我们进入这个RTOS的文件夹,然后新建三个文件夹,分别是“inc”,“src”,“port”:

这里的inc文件夹用来存放RTOS的头文件和配置文件,src用来存放RTOS的源文件,port用来存放RTOS的内存管理文件。

我们现在去RTOS固件包解压出来的文件夹中复制文件,我们进入解压后的文件夹就可以看到以下文件夹和文件:

这里我们再进入“FreeRTOSv202212.01”文件夹下的“FreeRTOS”文件夹,可以看到以下文件和文件夹:

然后我们再进入“FreeRTOS”文件夹下的“Source”文件夹,这里面的文件就是我们要用的了:

这里我们首先将“Source”文件夹下的所有.c的文件复制到刚才工程目录中创建的“RTOS\src”文件夹下:

这里需要注意的是,我们只需要复制.c,多余的文件如果被复制过去了删除就行了。

随后,我们再把“Source”文件夹下的“include”文件夹下的所有.h的文件复制到“RTOS\inc”文件夹下

这里同样的只要.h文件,不能复制别的文件。

随后我们进入“Source”文件夹下的“portable”文件夹,看到以下文件夹:

这里包含了RTOS的内存管理文件和不同编译器的相关文件。这里我们首先来复制内存管理文件,我们进入“portable”文件夹下的“MemMang”文件夹,将“heap_4.c”文件复制到工程目录下的“RTOS\port下”:

这里的内存管理文件我们一般都用4,如果你是小白的话请不要随意修改。

随后我们再打开“portable”文件夹下的“RVDS”文件夹,可以看到以下文件夹:

这里面对应了RVDS集成编译环境不同的ARM内核的编译规则文件。因为我这里使用的是STM32F407ZGT6单片机,所以这里首先肯定要选择M4内核,因为STM32F407ZG系列都不附带MPU所以我们这里直接选择“ARM_CM4F”文件夹,我们将“RVDS\ARM_CM4F”下的文件复制到工程目录下的“RTOS\port”下:

至此,我们RTOS的基本文件就已经复制完成了,这里我们还需要复制一下配置文件。配置文件被放在了“FreeRTOSv202212.01\FreeRTOS\Demo”目录下,进入这个目录,我们就能看到非常多的芯片型号,这些芯片官方都提供的相关的Demo,我们只需要在对应的芯片Demo中寻找配置文件即可:

因为这里我使用的是STM32F407ZGT6的芯片,所以我芯片所对应的配置文件就被存放在了“CORTEX_M4F_STM32F407ZG-SK”文件夹下。大家可以根据自己的芯片型号和对应的IDE寻找相关的Demo:

这里我们进入“CORTEX_M4F_STM32F407ZG-SK”文件夹,文件夹中的“FreeRTOSConfig.h”就是我们需要的配置文件了,我们将其复制到工程目录下的“RTOS\inc”下:

至此,我们RTOS运行所需的所有文件都已经复制完成了,我们直接打开工程:

接着,我们把刚才复制的文件添加的工程中,点击菜单栏中的箱子:

我们这里新建三个文件夹,分别是“RTOS\inc”,“RTOS\src”,“RTOS\port”:

接着将对应文件夹中的文件添加进来,这里一定要对应好,不要添加错了:

这里要注意的是,一定是把对应的所有文件添加进来以后再进行下一步。

我们再在魔术棒中将对应文件夹的路径添加进来,这里路径添加很基础,就不细讲了:

将文件添加进来以后,我们点击编译,这里的编译肯定会报错的,我们慢慢来解决:

这里的错误提示我们“configSYSTICK_CLOCK_HZ”这个变量没有定义。这里我们去到配置文件中,大概在40行左右,可以看到这样一条条件编译:

这里是为了判断是否是使用IAR编译器,我们这里使用的是keil,这条条件编译肯定不会过的。我们将这一条条件编译修改一下,改成下面这样:

#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)

这里修改了条件编译以后就可以兼容keil,IAR, GCC编译器了。

我们再次编译,出现三个重复定义的错误,因为这三个函数在STM32原本的库中已经被定义过了,但是在RTOS的库中又被定义了一次。既然我们要使用RTOS那当然也要用RTOS的处理函数,这里我们直接将原本的函数注释掉:

原本的函数被写在了“stm32f4xx_it.c”中,我们分别将其注释掉,通过报错我们可以得知,这里被重复定义的函数分别是“PendSV_Handler”,“SVC_Handler”,“SysTick_Handler”:

注释后如图:

这里将重复定义的函数注释掉以后,我们再次编译,我们可以看到这几个函数没有被定义:

这里又需要我们修改配置文件了,我们同样的打开配置文件我们将配置文件中的如下几项的值改为0:

#define configUSE_IDLE_HOOK				0
#define configUSE_TICK_HOOK				0
#define configCHECK_FOR_STACK_OVERFLOW	0
#define configUSE_MALLOC_FAILED_HOOK	0

修改完配置文件以后,我们再次编译:

可以看到,现在已经没有报错了,我们现在就算是移植完成了,我们可以写一点代码来试一下,首先回到“main.c”,这里我们先在main.c中引入RTOS相关的头文件:

#include "freertos.h"
#include "task.h"

然后我们创建一个任务函数:

void LED(void* age)
{while(1){GPIO_ResetBits(GPIOA,GPIO_Pin_12);vTaskDelay(500);GPIO_SetBits(GPIOA,GPIO_Pin_12);vTaskDelay(500);}
}

这里就是我们的任务函数了,控制了LED的亮灭,注意,任务函数进入以后一定是在一个死循环中,任务函数不能返回,不然可能引发一些错误。在使用了RTOS以后,我们的所以延时都要使用RTOS中的相对延时。

在主函数中,我们写引脚的初始化和任务的创建:

int main(void)
{TaskHandle_t myTaskHandle_t;GPIO_InitTypeDef  GPIO_InitStructure;delay_init(168);RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_SetBits(GPIOA,GPIO_Pin_12);xTaskCreate(LED,"LED",128,NULL,2,&myTaskHandle_t);vTaskStartScheduler();while(1){}
}

这里主要解释一下“xTaskCreate”函数。xTaskCreate用于创建RTOS中的任务。这里的第一个参数为任务函数的名称,我们这里任务函数名为“LED”所以第一个参数就传入LED,第二个参数是任务的名字,要求传入字符串格式的,我们这里传入一个字符串即可。第三个参数是为任务分配的内存,我们这里的任务只是点灯而已,所以不用分配太大,直接128就已经很充足了。第四个参数是向任务中传入的参数,我们这里没有参数要传入,直接写NULL,第五个参数是任务的优先级,因为只有一个任务,也不存在谁优先的问题,随便写个优先级即可。最后要我们传入一个“TaskHandle_t ”类型的结构体指针,我们直接定义一个结构体,取它的地址传入即可。

随后,我们再使用vTaskStartScheduler函数启动任务调度器。当调度器启动以后,STM32就已经被系统接管了。

我们将代码编译下载到开发板中,可以看到LED正常闪烁:

至此,我们的RTOS已经算是移植成功了。当然RTOS的知识还很多,能够运用只是第一步。

六、结语

        本次我们讲解了如何下载RTOS的固件包,以及如何复制RTOS的相关文件,以及在遇到错误以后如何解决。内容比较多,还是希望大家多吸收一下。那么最后,感谢大家的观看!

相关文章:

[STM32]从零开始的STM32 FreeRTOS移植教程

一、前言 如果能看到这个教程的话,说明大家已经学习嵌入式有一段时间了。还记得嵌入式在大多数时候指的是什么吗?是的,我们所说的学习嵌入式大部分时候都是在学习嵌入式操作系统。从简单的一些任务状态机再到复杂一些的RTOS,再到最…...

java——Tomcat连接池配置NIO、BIO、APR

Tomcat连接池的配置涉及不同的IO模型,包括NIO(Non-blocking IO,非阻塞IO)、APR(Apache Portable Runtime,Apache可移植运行库)和BIO(Blocking IO,阻塞IO)。以…...

跨域相关的一些问题 ✅

当网页从一个源(https://baidu.com)请求另一个源(如 https://taobao/api)的资源时,就发生了跨域。由于安全原因(防止恶意网站通过脚本访问用户在其他网站上的数据),浏览器对跨域请求…...

RPC学习

一、什么是 RPC RPC(Remote Procedure Call),即远程过程调用,是一种计算机通信协议,它允许运行在一台计算机上的程序调用另一台计算机上的子程序或函数,就好像调用本地程序中的函数一样,无需程序…...

coe文件转mif(c语言)

1 mif文件格式 DEPTH=1024; --The size of data in bits WIDTH=16; --The size of memory in words ADDRESS_RADIX = DEC; --The radix for address values DATA_RADIX = UNS...

【leetcode】动态规划

31. 873. 最长的斐波那契子序列的长度 题目&#xff1a; 如果序列 X_1, X_2, ..., X_n 满足下列条件&#xff0c;就说它是 斐波那契式 的&#xff1a; n > 3对于所有 i 2 < n&#xff0c;都有 X_i X_{i1} X_{i2} 给定一个严格递增的正整数数组形成序列 arr &#xff0…...

介绍一下atoi(arr);(c基础)

hi , I am 36 适合对象c语言初学者 atoi(arr)&#xff1b;是返回整数(int型)&#xff0c;整数是arr数组中字符中数字 格式 #include<stdio.h> atoi(arr); 返回值arr数组中的数字 未改变arr数组 #include<stdlib.h>//atoi(arr); 返 <stdlib> int main(…...

docker入门学习笔记

docker的定义 docker是一个用于构建、运行、传送 应用程序的平台。 为什么要使用docker &#xff1f; 在开发测试库环境中测试成功后&#xff0c;打包成集装箱&#xff0c;到生产环境也是能够成功的。而传统的安装方式不仅繁琐&#xff0c;并且在测试环境安装后&#xff0c;到…...

使用Python和Pybind11调用C++程序(CMake编译)

目录 一、前言二、安装 pybind11三、编写C示例代码四、结合Pybind11和CMake编译C工程五、Python调用动态库六、参考 一、前言 跨语言调用能对不同计算机语言进行互补&#xff0c;本博客主要介绍如何实现Python调用C语言编写的函数。 实验环境&#xff1a; Linux gnuPython3.10…...

tableau-制作30个图表

制作条形图 步骤: 1、横轴是数值,对应了某一个度量值,纵轴是一个标签 战区的成交额,条形图横轴是战区,纵轴是成交额 下钻条形图 1、增加业务架构-战区右键点击,分层结构,增加分层结构 调整业务架构,将战区,城市,小组移动到业务架构下方 此时的条形图上方有➕号展开后…...

2024APMCM亚太杯数学建模C题【宠物行业】原创论文分享

大家好呀&#xff0c;从发布赛题一直到现在&#xff0c;总算完成了2024 年APMCM亚太地区大学生数学建模竞赛C题的成品论文。 给大家看一下目录吧&#xff1a; 目录 摘 要&#xff1a; 10 一、问题重述 14 二&#xff0e;问题分析 15 2.1问题一 15 2.2问题二 15 2.3问题三…...

C语言解析命令行参数

原文地址&#xff1a;C语言解析命令行参数 – 无敌牛 欢迎参观我的个人博客&#xff1a;无敌牛 – 技术/著作/典籍/分享等 C语言有一个 getopt 函数&#xff0c;可以对命令行进行解析&#xff0c;下面给出一个示例&#xff0c;用的时候可以直接copy过去修改&#xff0c;很方便…...

推荐一款龙迅HDMI2.0转LVDS芯片 LT6211UX LT6211UXC

龙迅的HDMI2.0转LVDS芯片LT6211UX和LT6211UXC是两款高性能的转换器芯片&#xff0c;它们在功能和应用上有所差异&#xff0c;同时也存在一些共同点。以下是对这两款芯片的详细比较和分析&#xff1a; 一、LT6211UX 主要特性&#xff1a; HDMI2.0至LVDS和MIPI转换器。HDMI2.0输…...

libmodbus 源码学习笔记

1.核心函数_框架_数据结构 整个通信的过程 就是上面这个框架 下面就是具体过程 <1> 主设备 我们首先要初始化 我们要使用的串口 然后 设置我们要访问的哪一个设备 最后打开串口 <2>从机设备 也是我们要初始化我们的串口 然后随后立即设置我们的串口设备地址 最后…...

通用网络安全设备之【防火墙】

概念&#xff1a; 防火墙&#xff08;Firewall&#xff09;&#xff0c;也称防护墙&#xff0c;它是一种位于内部网络与外部网络之间的网络安全防护系统&#xff0c;是一种隔离技术&#xff0c;允许或是限制传输的数据通过。 基于 TCP/IP 协议&#xff0c;主要分为主机型防火…...

Vue.js基础——贼简单易懂!!(响应式 ref 和 reactive、v-on、v-show 和 v-if、v-for、v-bind)

Vue.js是一个渐进式JavaScript框架&#xff0c;用于构建用户界面。它专门设计用于Web应用程序&#xff0c;并专注于视图层。Vue允许开发人员创建可重用的组件&#xff0c;并轻松管理状态和数据绑定。它还提供了一个虚拟DOM系统&#xff0c;用于高效地渲染和重新渲染组件。Vue以…...

Mybatis 执行存储过程,获取输出参数的值

数据库环境&#xff1a;SQL Server 2008 R2 存储过程 alter procedure proc_generateOuterApplyId acceptType varchar(4),acceptGroupId int,outerApplyId varchar(20) output as begin set nocount onset outerApplyId 24GD6688--select outerApplyId as …...

RAG架构类型

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…...

Oracle 数据库 IDENTITY 列的性能选项

在上一篇文章Oracle 数据库 IDENTITY 列中&#xff0c;我们介绍了Oracle IDENTITY列的基础知识。本文将介绍IDENTITY列的几个性能选项。由于IDENTITY列内部使用sequence机制&#xff0c;因此也等同于是sequence的性能选项。 由于sequence是递增的&#xff0c;在高并发时&#…...

计算(a+b)/c的值

计算&#xff08;ab&#xff09;/c的值 C语言代码C语言代码Java语言代码Python语言代码 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 给定3个整数a、b、c&#xff0c;计算表达式(ab)/c的值&#xff0c;/是整除运算。 输入 输入仅一行&…...

微信小程序之bind和catch

这两个呢&#xff0c;都是绑定事件用的&#xff0c;具体使用有些小区别。 官方文档&#xff1a; 事件冒泡处理不同 bind&#xff1a;绑定的事件会向上冒泡&#xff0c;即触发当前组件的事件后&#xff0c;还会继续触发父组件的相同事件。例如&#xff0c;有一个子视图绑定了b…...

【配置 YOLOX 用于按目录分类的图片数据集】

现在的图标点选越来越多&#xff0c;如何一步解决&#xff0c;采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集&#xff08;每个目录代表一个类别&#xff0c;目录下是该类别的所有图片&#xff09;&#xff0c;你需要进行以下配置步骤&#x…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

蓝桥杯3498 01串的熵

问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798&#xff0c; 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

Selenium常用函数介绍

目录 一&#xff0c;元素定位 1.1 cssSeector 1.2 xpath 二&#xff0c;操作测试对象 三&#xff0c;窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四&#xff0c;弹窗 五&#xff0c;等待 六&#xff0c;导航 七&#xff0c;文件上传 …...

GitHub 趋势日报 (2025年06月06日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 590 cognee 551 onlook 399 project-based-learning 348 build-your-own-x 320 ne…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能

1. 开发环境准备 ​​安装DevEco Studio 3.1​​&#xff1a; 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK ​​项目配置​​&#xff1a; // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...

DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态

前言 在人工智能技术飞速发展的今天&#xff0c;深度学习与大模型技术已成为推动行业变革的核心驱动力&#xff0c;而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心&#xff0c;系统性地呈现了两部深度技术著作的精华&#xff1a;…...