目录
前言
传统的裸机开发缺少有效的内存管理和资源分配机制,伴随着系统功能的持续增多,代码的复杂程度也会急剧飙升,这时候,如果再使用裸机开发,将会导致资源浪费,加大系统不稳定性。
Free RTOS 作为一款具有显著优势的开源轻量实时操作系统,其具备极高的可移植性以及异常灵活的任务调度机制。它能够有效地适应不同的硬件架构和开发环境,使得开发人员可以轻松地将其应用于各种嵌入式系统中。同时,其出色的任务调度灵活性,能够根据任务的优先级、截止时间等因素,精准而高效地分配系统资源,确保关键任务能够及时得到处理。
将 STM32 与 Free RTOS 相结合后,STM32 的硬件优势将得到充分发挥,而 Free RTOS 则能为其提供高效的任务管理和资源调度,有效提升系统的实时性表现,使得系统能够更加迅速和准确地响应各种外部事件和任务需求。
一、实时操作系统是什么?
实时操作系统(RTOS):全称为 Real Time OS,是指能够在规定的时间内对外部事件或数据做出响应,并保证系统的正确性和确定性的操作系统。
该系统强调的是:实时性。当外界事件或数据产生时,能够接受并以足够快的速度予以处理,其处理的结果又能在规定的时间之内来控制生产过程或对处理系统做出快速响应,调度一切可利用的资源完成实时任务,并控制所有实时任务协调一致运行的操作系统。提供及时响应和高可靠性
在实时操作系统中,我们可以把要实现的功能划分为多个子程序,每个子程序负责实现其中的一部分,做到高内聚低耦合,从而提高程序模块间的可复用性与可移植性。
二、为什么要用FreeRTOS
- 开源且免费:FreeRTOS 是开源的,这意味着开发者可以免费获取和使用其源代码,降低了开发成本。
- 轻量级:它占用的系统资源少,适用于资源受限的嵌入式设备,如内存较小的微控制器。
- 可移植性强:能够轻松地移植到各种不同的微控制器架构和硬件平台上,使得开发具有通用性。
- 丰富的功能:提供了任务管理、消息队列、信号量、互斥量等多种功能,满足复杂的多任务系统需求。
三、使用步骤
1. 前期准备
- 获取 Free RTOS 源码:链接:https://pan.xunlei.com/s/VOFCLzzu8xnSUie3GJ8VJLf3A1#
提取码:7svr
官网下载: https://www.freertos.org/
托管网站下载:https://sourceforge.net/projects/freertos/files/FreeRTOS/
- STM32裸机工程
2.FreeRTOS 文件夹介绍
- Demo:FreeRTOS 官方为各个单片机移植好的工程代码。
- Source 文件夹:FreeRTOS 内核的源代码。
include文件夹 和 portable ”文件夹包 含的是 FreeRTOS 的通用的头文件和 C 文件。
3.向裸机工程中添加FreeRTOS 源码
- 首先在我们的 STM32 裸机工程模板根目录下新建一个文件夹,命名为 FreeRTOS”
在新建的FreeRTOS 文件夹下再次新建两个空文件夹,分别命名为 “port与“src 文件夹用于
保存 FreeRTOS 中的核心源文件。 - 打开 FreeRTOS 源码,在 “../FreeRTOS/Source” 目录下找到 所有的“.c 文件 ’’,将它们拷贝到我们新建的 src 文件夹中。
- 打开 FreeRTOS源码,在 “../FreeRTOS/Source/portable” 目录下找到“ MemMang” 文件夹与 “ 文件夹,将它们拷贝到我们新建的 port 文件夹中 。
- 、打开 FreeRTOS 源码,在 “../FreeRTOS/Source” 目录下找到"include” 文件 夹,它是我们需要用到 FreeRTOS 的一些头文件,将它直接拷贝 到我们新建的FreeRTOS 文件夹中,完成这一步之后就可以看到我们新建的FreeRTOS 文件夹已经有 3个文件夹,这3 个文件夹就包含 FreeRTOS 的核心文件, 至此, FreeRTOS 的源码就提取完成 。
- 拷贝 FreeRTOS Config.h 文件到FreeRTOS Config.h 文件到 user 文件夹。FreeRTOSConfig.h文件是 FreeRTOS 的工程配置文件。
- Free RTOS 源码到工程组文件夹根据
- 导入文件路径
- 根据实际情况修改 FreeRTOS Config.h 文件
#include "stm32f10x.h" /* 我们可以添加的宏定义 */ #define configUSE_TIME_SLICING 1 //使能时间片调度(默认式使能的) #define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 //硬件计算前导零指令,如果所使用的, MCU 没有这些硬件指令的话此宏应该设置为 0 #define configUSE_TICKLESS_IDLE 0 //置 1:使能低功耗 tickless 模式;置 0:保持系统节拍(tick)中断一直运行 #define configUSE_QUEUE_SETS 1 //启用队列 #define configUSE_TASK_NOTIFICATIONS 1 //开启任务通知功能,默认开启 #define configUSE_MUTEXES 1 //使用互斥信号量 #define configUSE_RECURSIVE_MUTEXES 1 //使用递归互斥信号量 #define configUSE_COUNTING_SEMAPHORES 1 //为 1 时使用计数信号量 #define configQUEUE_REGISTRY_SIZE 10 //设置可以注册的信号量和消息队列个数 #define configUSE_APPLICATION_TASK_TAG 0 #define configSUPPORT_DYNAMIC_ALLOCATION 1 //支持动态内存申请 #define configUSE_MALLOC_FAILED_HOOK 0 //使用内存申请失败钩子函数 #define configCHECK_FOR_STACK_OVERFLOW 1// 大于 0 时启用堆栈溢出检测功能,如果使用此功能用户必须提供一个栈溢出钩子函数如果使用的话此值可以为 1 或者 2,因为有两种栈溢出检测方法 #define configGENERATE_RUN_TIME_STATS 0 //启用运行时间统计功能 #define configUSE_STATS_FORMATTING_FUNCTIONS 1 #define configUSE_TIMERS 1 //启用软件定时器 #define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-1) //软件定时器优先级 #define configTIMER_QUEUE_LENGTH 10 //软件定时器队列长度 #define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE*2) //软件定时器任务堆栈大小 //可选函数配置选项 #define INCLUDE_xTaskGetSchedulerState 1 #define INCLUDE_eTaskGetState 1 #define INCLUDE_xTimerPendFunctionCall 1 //中断服务函数 也可以修改起始文件 #define vPortSVCHandler SVC_Handler #define xPortPendSVHandler PendSV_Handler #define xPortSysTickHandler SysTick_Handler /*源文件自带宏定义 可以修改*/ #define configUSE_PREEMPTION 1 //置 1: RTOS 使用抢占式调度器;置 0:RTOS 使用协作式调度器(时间片) #define configUSE_IDLE_HOOK 0 //置 1:使用空闲钩子(Idle Hook 类似于回调函数);置 0:忽略空闲钩子 #define configUSE_TICK_HOOK 0 //置 1:使用时间片钩子(Tick Hook);置 0:忽略时间片钩子 #define configCPU_CLOCK_HZ ( ( unsigned long ) 72000000 ) // 写 入 实 际 的CPU 内核时钟频率,也就是 CPU 指令执行频率 #define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) //RTOS 系 统 节拍中断的频率。即一秒中断的次数,每次中断 RTOS 都会进行任务调度 #define configMAX_PRIORITIES ( 5 ) //可使用的最大优先级 #define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 ) //空闲任务使用的堆栈大小 #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 17 * 1024 ) ) //系统所有总的堆大小 #define configMAX_TASK_NAME_LEN ( 16 ) //任务名字字符串长度 #define configUSE_TRACE_FACILITY 0 //启用可视化跟踪调试 #define configUSE_16_BIT_TICKS 0 //系统节拍计数器变量数据类型, 1 表示为16 位无符号整形, 0 表示为 32 位无符号整形 #define configIDLE_SHOULD_YIELD 1 //空闲任务放弃 CPU 使用权给其他同优先级的用户任务 /* Co-routine definitions. */ #define configUSE_CO_ROUTINES 0 //启用协程,启用协程以后必须添加文件croutine.c #define configMAX_CO_ROUTINE_PRIORITIES ( 2 ) //协程的有效优先级数目 /* Set the following definitions to 1 to include the API function, or zero to exclude the API function. */ #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 /* This is the raw value as per the Cortex-M3 NVIC. Values can be 255 (lowest) to 0 (1?) (highest). */ #define configKERNEL_INTERRUPT_PRIORITY 255 /* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!! See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */ #define configMAX_SYSCALL_INTERRUPT_PRIORITY 191 /* equivalent to 0xb0, or priority 11. */ /* This is the value being used as per the ST library which permits 16 priority values, 0 to 15. This must correspond to the configKERNEL_INTERRUPT_PRIORITY setting. Here 15 corresponds to the lowest NVIC value of 255. */ #define configLIBRARY_KERNEL_INTERRUPT_PRIORITY 15 //中断最低优先级
- 修改 stm 32f10x_it.c
- 修改main.c
注意:#include "FreeRTOS.h"应该在#include "task.h"上面 -
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "stm32f10x.h" #include "FreeRTOS.h" #include "task.h" #include "led.h" #include "user.h" #include "usart1.h" //任务的句柄,任务相关的信息,后期操作任务都是以句柄进行操作 TaskHandle_t xLED1_Handle = NULL; TaskHandle_t xLED2_Handle = NULL; void vLED1TaskCode( void * pvParameters ); void vLED2TaskCode( void * pvParameters ); int main(void) { NVIC_SetPriorityGrouping(5); Usart1_Config(); ledInit(); BaseType_t xReturned; xReturned = xTaskCreate( vLED1TaskCode, //任务函数的入口地址 每个任务都是不退出的循环 "LED1Task", //任务的描述性名称 无实际意义 128, //任务堆栈空间大小 单位:字=4个字节 128*4=512字节 NULL, //任务的参数 需要传递给任务的参数,如果无参数传递,填空 1, //任务的优先级 优先级的范围:0--configMAX_PRIORITIES之间 &xLED1_Handle ); if( xReturned == pdPASS ) { // printf("任务LED1创建成功\r\n"); } xReturned = xTaskCreate( vLED2TaskCode, //任务函数的入口地址 每个任务都是不退出的循环 "LED2Task", //任务的描述性名称 无实际意义 128, //任务堆栈空间大小 单位:字=4个字节 128*4=512字节 NULL, //任务的参数 需要传递给任务的参数,如果无参数传递,填空 2, //任务的优先级 优先级的范围:0--configMAX_PRIORITIES之间 &xLED2_Handle ); if( xReturned == pdPASS ) { // printf("任务LED2创建成功\r\n"); } vTaskStartScheduler(); while(1) { // printf("------------------\r\n"); } } //任务栈区溢出钩子函数 void vApplicationStackOverflowHook(void) { //printf("任务栈空间溢出了\r\n"); while(1) { ; } } //任务函数 //注意:每个任务函数都是不退出的循环 void vLED1TaskCode( void * pvParameters ) { while(1) { togglePin(GPIOE,GPIO_Pin_2); vTaskDelay(800); } } void vLED2TaskCode( void * pvParameters ) { while(1) { togglePin(GPIOE,GPIO_Pin_3); vTaskDelay(1500); } }
- 编译,看看有没有报错
总结
水平有限,如有错误,万望海涵!
源码:链接:https://pan.xunlei.com/s/VOFCMe0IsLIEgJGCu2hpDgY7A1#
提取码:pirb