【FreeRTOS】在Cortex-M4开发板上移植FreeRTOS并且实现LED灯闪烁(保姆级教程)

news2024/11/23 11:29:33

相信有不少小伙伴手上只有M4的开发板,想要移植FreeRTOS;但是,网上大部分都是M3移植教程。因此,陷入深深的迷茫中,难不成只能使用仿真了???🤔因此,小编特意写了一篇关于M4的移植教程。
本文移植基于以下软硬件平台:

  • 硬件平台为:主板为Cortex-M4芯片的开发板,此处,小编将以蓝桥杯嵌入式开发板——STM32G431RBT6开发板作为示例
  • 软件系统为:FreeRTOSv202212.00

FreeRTOS简介

在嵌入式领域中,嵌入式实时操作系统正得到越来越广泛的应用。采用嵌入式实时操作系统(RTOS)可以更合理、更有效地利用CPU的资源,简化应用软件的设计,缩短系统开发时间,更好地保证系统的实时性和可靠性。
FreeRTOS就是一款嵌入式实时操作系统,并且还是一款“开源免费”的实时操作系统,遵循GPLv2+的许可协议。好好使用该系统,大家将告别裸机开发,迈向基于操作系统的程序开发。
其功能与特点有:

  • 用户可配置内核功能;
  • 多平台的支持;
  • 提供一个高层次的信任代码的完整性;
  • 目标代码小,简单易用;
  • 遵循MISRA-C标准的编程规范;
  • 强大的执行跟踪功能;
  • 堆栈溢出检测;
  • 没有限制的任务数量;
  • 没有限制的任务优先级;
  • 多个任务可以分配相同的优先权;
  • 队列,二进制信号量,计数信号灯和递归通信和同步的任务;
  • 优先级继承;
  • 免费开源的源代码;

项目工程建立

由于STM32G431RBT6无法直接使用Keil建立工程,需要借助CubeMx才能完成此工作,因此,项目工程建立都是基于CubeMx的,当然,如果大家会使用CubeMx建立工程或者开发板可以不使用CubeMx建立工程就可跳过此部分。

模板工程配置

  • 步骤一:打开CubeMX后,初始化时钟RCC,使用无源外部晶振;
    在这里插入图片描述

  • 步骤二:调整系统主频至80MHz

  • 步骤三:配置工程信息;
    在这里插入图片描述
    在这里插入图片描述

LEDCubeMX配置

  • PC8-PC15 配置成 GPIO_OutPut,将默认电平电平设置成高电平,不加上拉下拉;

  • PD2配置成GPIO_OutPut,将默认电平设置成低电平,不加上拉下拉;
    在这里插入图片描述
    每次使用LED时一定要记得将PD2拉高拉低,也就是打开关闭锁存器;否则,本次操作就会变成无效操作。

FreeRTOS下载以及移植步骤

下载?🤔当然还是进入官网下载啦!🤣🤣🤣FreeRTOS官网

在这里插入图片描述
解压后可以得到以下文件,而目标文件则是第一个FreeRTOS。
在这里插入图片描述
移植时需要将source里的大部分文件全部移植,因此可以将整个文件夹都搬过去,而demo中的文件是使用示例,在测试时可以康康里面的示例。在这里插入图片描述
在Source/portable文件夹中,我们需要的就是内存管理文件与处理器文件,其余的文件暂时用不到,可以将除RVDS与MemMang两个文件夹外其余所有的文件夹都删掉。
在这里插入图片描述
Source/portable/RVDS文件夹中,其提供了里面包含了各种处理器相关的文件,如图所示,因为本次所使用开发板的处理器是M4,因此要保留ARM_CM4F文件夹的内容,可以将其他内容都删除。ARM_CM4F中,ARM_CM4表示其处理器为M4,F表示支持该芯片加强了浮点运算。
在这里插入图片描述
Source/portable/MemMang文件夹中,其提供的是一些内存管理文件,共有5个,各个文件解释如下:

  • heap1:最简单的内存管理,管理的其实是一个静态全局变量,只允许分配,不允许释放,设计之初就是用于创建信号量、任务、队列一般不需要释放的数据,不过在FreeRTOS V9.0.0及其以后版本添加 support for static allocation基本就被替代了。
  • heap2.:比1多了内存释放,分配也因为有内存释放原因多了一个最佳适配算法,不过在内存释放后没有空闲块合并的功能,只适合信号量队列任务等大小固定的内存分配,随机大小的内存分配释放会因为内存碎片问题而无法进行。后面heap4是heap2的优化版。
  • heap3:简单封装标准C库mallco和free,提供线程安全。
  • heap4:一般来说FREERTOS五种堆管理里这个是最常见使用的了,首次适配算法,heap2的优化版,多了相邻内存碎片合并功能,内存碎片的风险少很多,不过没有MMU的芯片没有逻辑地址和物理地址的映射概念,内存分配就是一个萝卜一个坑,想做内存紧凑比较费劲。
  • heap5:比heap4多了不连续地址多段内存管理,分配释放代码完全一样。

删除部分文件后完整的移植所需的内核文件如下:
在这里插入图片描述

移植后验证

目标:
首次实验的目标是,使用FreeRTOS创建两个任务,即:
任务一:LED1以合适的时间间隔闪烁;
任务二:LED2以合适的时间间隔闪烁;
由于FreeRTOS是一个可配置的实时操作系统,因此在使用前还需要配置其功能,也就是设置宏定义的值,否则编译时会报错。

程序

首先,给大家展示小编理想代码

其思路是:

  • 1.创建两个任务,并且保存创建时的返回值;
  • 2.判断两个任务是否都创建成功,如果失败,就点亮LED6,;否则就进入裸机的while(1)中,执行裸机程序,将不再执行下面步骤;
  • 3.开启FreeRTOS的任务调度;

可以看出来实现步骤非常简单,而且下面的任务函数也非常简单;但是在实际使用时,一旦所有任务经vTaskDelay函数进入阻塞状态,这些任务将无法恢复,整个系统就会处于空闲状态(也就是没有任何正在执行的任务)。🤔🤔🤔 这个问题小编暂时还未解决方法,如有必要,后续会出一篇文件讲解这个问题。


//任务控制权柄
TaskHandle_t xHandleTsak[2];
/*****************************************
* 函数功能:freertos工作函数
* 函数参数:无
* 函数返回值:无
*****************************************/
void freertosWork(void)
{
	//存储创建任务的返回值
	BaseType_t xReturn[2] ;
	
	//动态创建任务1
	xReturn[0] = xTaskCreate(
				(TaskFunction_t )task1,//任务入口函数
				(const char *)"task1",//任务名字
				(uint16_t)100,//任务栈大小
				(void*)NULL,//任务入口参数
				1,//任务优先级  优先级越高,任务优先选越高
				&xHandleTsak[0]//任务控制块
				);
	
	//动态创建任务2
	xReturn[1] = xTaskCreate(
				(TaskFunction_t )task2,//任务入口函数
				(const char *)"task2",//任务名字
				(uint16_t)100,//任务栈大小
				(void*)NULL,//任务入口参数
				1,//任务优先级  优先级越高,任务优先选越高
				&xHandleTsak[1]//任务控制块
				);	
	//创建成功 
	if (pdPASS == xReturn[0] == xReturn[1])
		//启动任务,开启调度 
		vTaskStartScheduler(); 
	//创建失败
	else
		//点亮LED6
		changeLedStateByLocation(LED6,ON);
}

//任务1
void task1(void)
{
	const volatile TickType_t xDelay1000ms = pdMS_TO_TICKS( 1000UL );
	while(1)
	{
		//LED1反转状态
		rollbackLedByLocation(LED1);
		vTaskDelay( xDelay1000ms );
	}
}

//任务2
void task2(void)
{
	const volatile TickType_t xDelay1000ms = pdMS_TO_TICKS( 900UL );
	while(1)
	{
		//LED2状态反转
		rollbackLedByLocation(LED2);
		vTaskDelay( xDelay1000ms );
	}
}

这是小编实际使用的代码
其实,实际使用的代码跟上述代码大部分一样,并且逻辑也大致相似。
不同的是,实现LED灯闪烁的方式,也就是任务的阻塞态与运行态转换的方式不同。本来是打算借助于vTaskDelay函数实现的,但是一旦出现该函数,程序就会死机;因此,小编打算使用空闲任务来定时激活两个任务,这里的前提条件是两个任务一旦执行完一次就必须进入挂起状态(阻塞状态),因为只有这样,该空闲任务才能重新激活已经挂起的任务。


//任务控制权柄
TaskHandle_t xHandleTsak[2];
//存储计数值
volatile uint32_t count[3] = {0,0,0};

/*****************************************
* 函数功能:freertos工作函数
* 函数参数:无
* 函数返回值:无
*****************************************/
void freertosWork(void)
{
	//存储创建任务的返回值
	BaseType_t xReturn[2] ;
	
	//动态创建任务1
	xReturn[0] = xTaskCreate(
				(TaskFunction_t )task1,//任务入口函数
				(const char *)"task1",//任务名字
				(uint16_t)100,//任务栈大小
				(void*)NULL,//任务入口参数
				1,//任务优先级  优先级越高,任务优先选越高
				&xHandleTsak[0]//任务控制块
				);
	
	//动态创建任务2
	xReturn[1] = xTaskCreate(
				(TaskFunction_t )task2,//任务入口函数
				(const char *)"task2",//任务名字
				(uint16_t)100,//任务栈大小
				(void*)NULL,//任务入口参数
				1,//任务优先级  优先级越高,任务优先选越高
				&xHandleTsak[1]//任务控制块
				);	
	//创建成功 
	if (pdPASS == xReturn[0] == xReturn[1])
		//启动任务,开启调度 
		vTaskStartScheduler(); 
	//创建失败
	else
		//点亮LED6
		changeLedStateByLocation(LED6,ON);
}

//任务1
void task1(void)
{
	const volatile TickType_t xDelay1000ms = pdMS_TO_TICKS( 1000UL );
	while(1)
	{
		//LED1反转状态
		rollbackLedByLocation(LED1);
		//挂起任务
		vTaskSuspend(xHandleTsak[0]);
	}
}

//任务2
void task2(void)
{
	const volatile TickType_t xDelay1000ms = pdMS_TO_TICKS( 900UL );
	while(1)
	{
		//LED2状态反转
		rollbackLedByLocation(LED2);
		//挂起任务
		vTaskSuspend(xHandleTsak[1]);
	}
}

//空闲任务
void vApplicationIdleHook (void)
{
	while(1)
	{
		if(count[0]++ % 900000 == 0)
			//恢复任务1
			vTaskResume(xHandleTsak[1]);
		else if(count[0] % 1000000 == 0)
			//恢复任务0
			vTaskResume(xHandleTsak[0]);
	}										
}

结果

本来在此处放置一个视频的,但是放置后发现好丑啊🤣🤣🤣。因此,在此处就放置三张图片,注意,这三张图片是连续的哈🤔🤔🤔。


结果1-点亮LED1

结果2-点亮LED2

结果3-点亮LED1与LED2

移植时遇到的典型问题

问题一:中断服务函数重复

问题描述:该报错是因为FreeRTOS中断服务函数与Cortex-M4中断服务函数重复;
在这里插入图片描述
问题解决:

  • 1.使用弱定义__weak修饰其中一个函数;
  • 2.删除其中一个函数;

问题二:汇编代码报错

问题描述:该报错是因为汇编代码操作数据时数据类型有误;
在这里插入图片描述
在这里插入图片描述

问题解决:将stm32g431xx.h文件(指的是芯片头文件)中__NVIC_PRIO_BITS 声明的值由4U改成4即可,如下图;
在这里插入图片描述

问题三:芯片空间不足

问题描述:由于芯片空间不足就会出现类似 freertosTest\freertosTest.axf: Error: L6406E: No space in execution regions with .ANY selector matching heap_4.o(.bss).的报错
在这里插入图片描述
解决方法:该问题小编暂时还未解决,不过网上有很多博客都在解释这个报错,有兴趣的朋友可以去看看。
看到这,相信大部分朋友可能会问:小编都没有解决这个问题,那这个工程时咋跑起来的呢?这不框我嘛😢😢😢。别急,小编这就告诉你原因,正如小编前面所说,小编一开始是移植一位大佬线程的FreeRTOS操作系统的,但是呢,在多方面尝试后,发现咋都不行;因此,小编就去官网下载了官方的FreeRTOS,再参考大佬的移植步骤,最后移植出一个可使用的带FreeRTOS操作系统的G431开发板😁😁😁。

小结

虽然移植过程非常磨人,也花了很多时间去写bug与修bug,但是好在最终还是弄出来了。总的来说移植并不难,只要大家找对芯片信号,再仔细对照教程移植,相信大家总会拥有一个带操作系统的开发板🎉🎉🎉。

福利

下边是小编个人整理出来免费的、保姆级的蓝桥杯嵌入式福利,有需要的童鞋可以自取哟!🤤🤤🤤
【蓝桥杯嵌入式】第十一届蓝桥杯嵌入式省赛(第二场)程序设计试题及其题解
【蓝桥杯嵌入式】第十二届蓝桥杯嵌入式省赛程序设计试题以及详细题解
【蓝桥杯嵌入式】第十三届蓝桥杯嵌入式省赛程序设计试题及其详细题解
【蓝桥杯嵌入式】第十三届蓝桥杯嵌入式省赛(第二场)程序设计试题及其题解

最后,欢迎大家留言或私信交流,共同进步哟!😉😉😉

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/169953.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux】静态库和动态库

Linux为什么不允许普通用户给目录建立硬链接呢? 系统层面上有.和…硬链接指向目录。假设我们是超级用户,允许给目录建立硬链接,给根目录建立硬链接,从根目录开始查找,当查找硬链接的时候就是根目录,这时候递…

面试官让我聊聊 MQ 的数据丢失问题,没想到水这么深。。。

目录 一、背景引入二、Kafka分布式存储架构三、Kafka高可用架构四、画图复现Kafka的写入数据丢失问题五、Kafka的ISR机制是什么?六、Kafka写入的数据如何保证不丢失?七、总结 一、背景引入 这篇文章,给大家聊一下写入Kafka的数据该如何保证…

Git常用命令(全局设置获取仓库)

新建仓库: 填写名称等信息,根据需要选择私有,开源等选项。 创建完成。 邀请成员: Git常用命令 Git全局设置 首先要做的是设置用户名和email地址。这是非常重要的,每次Git提交都会使用该用户信息。 设置用户信息: …

岁末年初捷报频传 HashData斩获多项行业殊荣

凯歌高奏辞旧岁,数据赋智谱新篇。 刚刚过去的2022年,面对充满变数的外部环境,HashData始终坚持以技术为本,持续全面创新,适应数字经济发展趋势,笃行致远,砥砺前行,积极推动企业“上…

VPS融合怪测评脚本(主体已完善,历史遗留问题解决时间未知)(VPS fusion monster evaluation script)

ecs 原仓库链接&#xff1a;https://github.com/spiritLHLS/ecs 支持系统&#xff1a;Ubuntu 18&#xff0c;Debian 8&#xff0c;centos 7&#xff0c;Fedora&#xff0c;Almalinux 8.5, Arch 融合怪测评脚本 bash <(wget -qO- --no-check-certificate https://gitlab.…

Sealer 0.9 :帮助集群和分布式应用实现 Build、 Share、Run

作者&#xff1a;sealer 社区 新春之际&#xff0c;很高兴在此时宣布 Sealer [ 1] 0.9 版本的正式发布。Sealer 是一款致力于探索面向分布式应用的快速打包、交付和运行的解决方案。2021 年5月 Sealer 项目正式开源&#xff1b;短短一年时间&#xff0c;Sealer 在 2022 年 4 月…

人工智能图像识别四大算子

文章目录背景引入图像识别发展简介边缘检测算法*Prewitt算子**Sobel算子**Laplace算子**Conny算子** 文末寄语*背景引入 图像识别是当今计算机科学最热门的研究方向之一。随着科学技术的发展和人类社会的不断进步&#xff0c;图像识别技术在很多行业得到了广泛的应用。本章除了…

【单链表】数据结构,详解单链表,java实现代码

前言&#xff1a; 大家好&#xff0c;我是良辰丫&#x1f353;&#x1f353;&#x1f353;&#xff0c;今天我和大家一起了解一下数据结构中的链表&#xff0c;链表&#xff0c;顾名思义是用链子把一个个数据串连起了的&#xff0c;那么链表和顺序表又有什么不同呢&#xff1f;…

mysql存储过程基本语法

本文来说下mysql存储过程基本语法 文章目录基本语法使用实例变量的使用变量定义declare语句变量赋值用户变量存储过程的参数in 输入参数out 输出参数inout输入输出参数本文小结基本语法 存储过程就是具有名字的一段代码&#xff0c;用来完成一个特定的功能。创建的存储过程保存…

2022年全球白帽常用工具排行榜TOP 10

虽然此时还未到2022年年底&#xff0c;但并不妨碍我们整理一份2022年全球白帽常用的工具榜单&#xff0c;希望能给白帽们和企业安全人员们带来一定的借鉴和参考。 一方面&#xff0c;工具的重要性不言而喻&#xff0c;各大SRC的白帽们也有深刻的切身体会。一个好用且靠谱的工具…

带模糊加工时间的柔性作业车间调度理论和GA复现(python)

文章目录1.模糊作业车间1.1 模糊数1.2 三角模糊数操作1.3 模糊甘特图2.FJSP模糊加工时间GA2.1 GA算法设置2.2 python代码2.3 测试结果1.模糊作业车间 1.1 模糊数 论域X上的模糊集合A由隶属度函数u(x)表示&#xff0c;u(x)取值[0,1]。如果A为三角模糊数&#xff0c;A可以表示为…

软件测试复习07:软件测试过程

作者&#xff1a;非妃是公主 专栏&#xff1a;《软件测试》 个性签&#xff1a;顺境不惰&#xff0c;逆境不馁&#xff0c;以心制境&#xff0c;万事可成。——曾国藩 文章目录测试计划测试设计测试执行测试监控测试结束软件测试过程主要有5个阶段&#xff1a;测试计划、测试设…

《深入浅出计算机组成原理》学习笔记 Day3

ELF和静态链接1. 程序执行&#xff1a;编译、链接和装载2. ELF 格式和链接3. 总结延伸参考1. 程序执行&#xff1a;编译、链接和装载 有这么两个C文件&#xff1a; \\ add.c int add(int a, int b) {return a b; }\\ test.c #include <stdio.h> int main() {int a 1;…

JVM学习(三):聊聊内存泄漏(memory leak)

一、什么是内存泄漏&#xff08;memory leak&#xff09;可达性分析算法来判断对象是否是不再使用的对象&#xff0c;本质都是判断一个对象是否还被引用。那么对于这种情况下&#xff0c;由于代码的实现不同就会出现很多种内存泄漏问题&#xff08;让JVM误以为此对象还在引用中…

最新光速配置VScode运行C/C++教程(W11适用)

文章目录1.下载MingW编译器&#xff1a;2.解压7z压缩文件方法3.VScode配置4.下面看看成果&#xff1a;1.下载MingW编译器&#xff1a; 下载地址 往下面拖找到上面这个东西-下载最新版本 2.解压7z压缩文件方法 7z解压程序官网 下它&#xff01;相信这个时候在装的一般都是6…

【ubuntu | cuda】ubuntu22.04 安装cuda11.3方法

every blog every motto: You can do more than you think. 0. 前言 ubuntu22.04 安装cuda11.3方法记录 1. 正文 1.1 方法一&#xff1a; https://developer.nvidia.com/cuda-11.3.0-download-archive?target_osLinux&target_archx86_64&DistributionUbuntu&…

服务网格领域的百花齐放,是否存在一个更优解?

作者lingsamuel&#xff0c;API7.ai 云原生技术专家&#xff0c;Apache APISIX Committer。 作者林志煌&#xff0c;API7.ai 技术工程师&#xff0c;Apache APISIX contributor。 服务网格是一种技术架构&#xff0c;它用于管理微服务系统中各个服务之间的通信&#xff0c;旨在…

关于volatile和gcc 优化的的思考

volatile是一个特征修饰符&#xff08;type specifier&#xff09; volatile的作用是作为指令关键字&#xff0c;确保本条指令不会因编译器的优化而省略&#xff0c;且要求每次直接读值。这是百度百科的介绍&#xff0c;那编译器是具体是怎么优化的呢。我们知道gcc 是有O0 O1 O…

torch.nn.MSELoss扒开看看它

目录官网介绍Toy默认参数定制参数预测问题-线性回归官网介绍 Toy 设X,Y∈Rnd\mathbf{X} , \mathbf{Y} \in \mathbf{R}^{n\times d}X,Y∈Rnd&#xff0c;假设其中X\mathbf{X}X是模型的输入&#xff0c;Y\mathbf{Y}Y是真实标签 默认参数 torch.nn.MSELoss(size_averageNone, r…

【Java IO流】字节流详解

文章目录1. IO 流概述2. IO 流分类3. 字节输出流4. 字节输入流5. 文件拷贝6. IO 流中的异常处理7. 总结Java编程基础教程系列1. IO 流概述 什么是 IO 流&#xff1f; IO 流是存取数据的解决方案&#xff0c;在计算机中数据存放在硬盘的文件中&#xff0c;如果程序需要使用这些…