-
-
相信有不少小伙伴手上只有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
函数进入阻塞状态,这些任务将无法恢复,整个系统就会处于空闲状态(也就是没有任何正在执行的任务)。🤔🤔🤔 这个问题小编暂时还未解决方法,如有必要,后续会出一篇文件讲解这个问题。c//任务控制权柄 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
函数实现的,但是一旦出现该函数,程序就会死机;因此,小编打算使用空闲任务来定时激活两个任务,这里的前提条件是两个任务一旦执行完一次就必须进入挂起状态(阻塞状态),因为只有这样,该空闲任务才能重新激活已经挂起的任务。c//任务控制权柄 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) { if(count[0]++ % 900000 == 0) //恢复任务1 vTaskResume(xHandleTsak[1]); else if(count[0] % 1000000 == 0) //恢复任务0 vTaskResume(xHandleTsak[0]); }
移植时遇到的典型问题
问题一:中断服务函数重复
问题描述:该报错是因为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,但是好在最终还是弄出来了。总的来说移植并不难,只要大家找对芯片信号,再仔细对照教程移植,相信大家总会拥有一个带操作系统的开发板🎉🎉🎉。
福利
下边是小编个人整理出来免费的、保姆级的 蓝桥杯嵌入式福利,有需要的童鞋可以自取哟!🤤🤤🤤 【蓝桥杯嵌入式】第十一届蓝桥杯嵌入式省赛(第二场)程序设计试题及其题解 【蓝桥杯嵌入式】第十二届蓝桥杯嵌入式省赛程序设计试题以及详细题解 【蓝桥杯嵌入式】第十三届蓝桥杯嵌入式省赛程序设计试题及其详细题解 【蓝桥杯嵌入式】第十三届蓝桥杯嵌入式省赛(第二场)程序设计试题及其题解
最后,欢迎大家留言或私信交流,共同进步哟!😉😉😉
追更1:解决理想代码无法使用
经过小编不懈努力,终于确定使用上述理想代码报错的原因啦!!!
问题在现及说明
对,没错,理想代码没有实现就是因为主板捕捉到了硬件错误(HardFault_Handler) ,网络上也有很多关于该报错的解决方案,常见的报错分类有:
- 内存溢出,访问越界;
- 堆栈溢出 ,程序跑飞;
- 数组越界;
- 中断处理错误;
首先说明,小编在初始化硬件资源时,有用到LED与LCD,本来是打算利用FreeRTOS创建两个点灯任务,并且显示在LCD屏上当前任务名称。 这就是导致出现的HardFault_Handler的原因,也就是说LCD初始化时出现了问题,就这个无意间的任务让小编找了两三天错误🤣🤣🤣。 这里LCD初始化、显示使用硬件IIC实现,也有可能是因为LCD是由硬件实现的所以导致小编整个FreeRTOS任务执行失败😢😢😢。 至于,上述代码是怎么实现LCD与LED同时工作的嘛,这里小编可以讲解一下:
- 首先,不给FreeRTOS设置基础时钟,也就是注释
#define xPortSysTickHandler SysTick_Handler
; - 其次,使用任务挂起与二次唤醒机制来实现任务切换。当然了,这里需要借助空闲任务来实现唤醒;
解决方法
因为小编刚开始学FreeRTOS,因此这里的解决方法比较简单粗暴------将关于LCD部分的代码完全注释。后续小编还会尝试使用软件IIC,尝试解决该错误🤣。
第一次在蓝桥杯嵌入式STM32G431RBT6开发板上使用FreeRTOS操作系统点亮LED灯 完整视屏
追更2:解决FreeRTOS无法与LCD一起使用的问题以及使用CubeMX建立FreeRTOS项目
针对上述代码LCD无法与FreeRTOS同时使用的问题,其主要原因是FreeRTOS的时钟配置问题。
解决方法1: 小编提出的第一个解决方法,是配置FreeRTOS时钟与STM32时钟,即解决FreeRTOS时钟配置问题。
- 步骤1:在freeRTOSConfig.h文件中添加以下FreeRTOS与STM32时钟相关的配置;
c/* freertos与中断服务函数相关的配置 */ #define xPortPendSVHandler PendSV_Handler #define vPortSVCHandler SVC_Handler #define INCLUDE_xTaskGetSchedulerState 1
- 步骤2:在stm32g4xx_it.c文件中将下FreeRTOS的滴答时钟与STM32的滴答时钟想关联,依次保证STM32滴答时钟与FreeRTOS滴答时钟正常工作;这里注意,下图中蓝色框中的内容一定要添加,否则FreeRTOS的时钟还是无法使用。🙂
解决方法2: 于是乎小编提出一个第二个解决方法,即使用CubeMX来建立一个FreeRTOS项目 。 项目建立时,硬件初始化步骤跟 第二部分-项目工程建立 是一样的,可以参考上文即可。 下面就是freeRTOS项目建立的关键步骤,即使能FreeRTOS与创建新的任务,这里需要注意,第一个任务defaultTask是一个默认任务。
工程创建成功后,大家可以直接在默认任务或者新建任务中写下自己想要实现的功能。 如,小编想要实现 移植后验证的目标功能 ,那么freeRTOS的三个任务分别为
c/* USER CODE BEGIN Header_StartDefaultTask */ /** * @brief Function implementing the defaultTask thread. * @param argument: Not used * @retval None */ /* USER CODE END Header_StartDefaultTask */ void StartDefaultTask(void *argument) { /* USER CODE BEGIN StartDefaultTask */ /* Infinite loop */ for(;;) { //LED2状态反转 rollbackLedByLocation(LED1); osDelay(500); } /* USER CODE END StartDefaultTask */ } /* USER CODE BEGIN Header_StartTask02 */ /** * @brief Function implementing the myTask02 thread. * @param argument: Not used * @retval None */ /* USER CODE END Header_StartTask02 */ void StartTask02(void *argument) { /* USER CODE BEGIN StartTask02 */ /* Infinite loop */ for(;;) { //LED2状态反转 rollbackLedByLocation(LED2); osDelay(1000); } /* USER CODE END StartTask02 */ } /* USER CODE BEGIN Header_StartTask03 */ /** * @brief Function implementing the myTask03 thread. * @param argument: Not used * @retval None */ /* USER CODE END Header_StartTask03 */ void StartTask03(void *argument) { /* USER CODE BEGIN StartTask03 */ /* Infinite loop */ for(;;) { //LED2状态反转 rollbackLedByLocation(LED3); osDelay(1000); } /* USER CODE END StartTask03 */ }
-
【FreeRTOS】在Cortex-M4开发板上移植FreeRTOS并且实现LED灯闪烁(保姆级教程)
黑心萝卜三条杠2023-09-02 15:34
相关推荐
WZF-Sang1 天前
Linux—进程学习-01Goboy2 天前
0帧起步:3分钟打造个人博客,让技术成长与职业发展齐头并进结衣结衣.2 天前
【Linux】Linux管道揭秘:匿名管道如何连接进程世界OpenAnolis小助手2 天前
龙蜥副理事长张东:加速推进 AI+OS 深度融合,打造最 AI 的服务器操作系统小蜗的房子3 天前
SQL Server 2022安装要求(硬件、软件、操作系统等)邂逅岁月5 天前
【多线程奇妙屋】 Java 的 Thread类必会小技巧,教你如何用多种方式快速创建线程,学并发编程必备(实践篇)CXDNW6 天前
【系统面试篇】进程和线程类(1)(笔记)——区别、通讯方式、同步、互斥、死锁Anemone_6 天前
MIT 6.S081 Lab3掘了7 天前
持久化内存 | Persistent Memory结衣结衣.8 天前
【Linux】掌握库的艺术:我的动静态库封装之旅