RTOS系统

RTOS系统一 RTOS 操作系统概述

大家好,欢迎来到IT知识分享网。

一 RTOS操作系统概述

RTOS: Real Time OS, 就是实时操作系统 强调实时性 实时操作系统分为软实时和硬实时 实时操作系统最大的特色就是其“实时性”。也就是说,如果有任务需要执行,实时操作系统会立即 (在较短时间内)执行该任务,保证了任务在指定时间内完成。 实时操作系统根据任务执行的实时性,分为“硬实时”操作系统和“软实时”操作系统, “硬实时”操作系统比“软实时”操作系统响应更快、实时性更高,“硬实时”操作系统大多应用于工业领域。 “硬实时”操作系统必须使任务在确定的时间内完成。 “软实时”操作系统能让绝大多数任务在确定时间内完成。 常见的RTOS系统有: FreeRTOS, UCOS, RTX, RT-Tread 等 FreeRTOS 是可剥夺型内核,剥夺其他进程对CPU的使用权,它总是运行在 就绪任务重的优先级最高的任务。 也就是说,高优先级的任务不执行完,低优先级的任务是没有机会执行的 

在这里插入图片描述

任务调度原则: 高优先级的进程可以打断低优先级进程的执行 中断可以打断任务的执行, 但是中断处理程序并没有返回到被打断的点,而是返回到了更高优先级任务的位置 记住:Free-RTOS执行的永远是就绪态的任务队列中,优先级最高的那个任务! FreeRTOS 是一个可剪裁的,可剥夺型的多任务内核,而且没有任务数的限制。 FreeRTOS提供了实时操作系统所需的所有的功能,包括资源管理,同步,任务通信等。 为什么企业中使用FreeRTOS? 免费、开源、支持多种第三方组件! FreeRTOS 是一个免费的实时嵌入式操作系统 免费开源: 商业产品中使用,无潜在的风险,无需担心 可裁剪: FreeRTOS的核心代码9000+行,包含在3个.c文件中 简单:简单易用,可移植性非常好 优先级不限:任务优先级分配没有限制,多任务可以同一优先级 任务不限:可以创建的实时任务数量没有软件限制 支持抢占/协程/任务调度 重点: FreeRTOS的官方: http://www.freertos.org 开源电子网:www.openedv.com FreeRTOS的基础知识 了解任务调度器,以及RTOS的特性, 打好基础 FreeRTOS的内核 任务启动流程,中断管理,任务切换 FreeRTOS各项功能 列表、队列、信号量、内存管理 

二 命名惯例

RTOS 内核和演示应用程序源代码使用以下惯例: 变量 变量名称使用驼峰式大小写,具有明确的描述性,并使用完整的单词(没有缩写,但普遍接受的缩写除外)。 uint32_t 类型变量以 ul 为前缀,其中“u”表示“unsigned” ,“l”表示“long”。 uint16_t 类型变量以 us 为前缀,其中“u”表示“unsigned” , “s”表示“short”。 uint8_t 类型变量以 uc 为前缀,其中“u”表示“unsigned” , “c”表示“char ”。 非 stdint 类型的变量以 x 为前缀。 例如,BaseType_t 和 TickType_t,二者分别是可移植层定义的定义类型,主要架构的自然类型或最有效类型,以及用于保存 RTOS ticks 计数的类型。 非 stdint 类型的未签名变量存在附加前缀 u。 例如,UBaseType_t(未签名 BaseType_t)类型变量以 ux 为前缀。 size_t 类型变量也带有 x 前缀。 枚举变量以 e 为前缀 指针以附加 p 为前缀,例如,指向 uint16_t 的指针将以 pus 为前缀。 根据 MISRA 指南,未限定标准 char 类型仅可包含 ASCII 字符,并以 c 为前缀。 根据 MISRA 指南,char * 类型变量仅可包含指向 ASCII 字符串的指针,并以 pc 为前缀。 函数 函数名称使用驼峰式大小写,具有明确的描述性,并使用完整的单词(无缩写,但普遍接受的缩写除外)。 文件作用域静态(私有)函数以 prv 为前缀。 根据变量定义的相关规定,API 函数以其返回类型为前缀,并为 void 添加前缀 v。 API 函数名称以定义 API 函数文件的名称开头。例如,在 tasks.c 中定义 vTaskDelete,并且具有 void 返回类型。 宏 宏具有明确的描述性,并使用完整的单词(无缩写,但普遍接受的缩写除外)。 宏以定义宏的文件为前缀。前缀为小写。例如,在 FreeRTOSConfig.h 中定义 configUSE_PREEMPTION。 除前缀外,所有宏均使用大写字母书写,并使用下划线来分隔单词。 

三 FreeRTOS源码初探

从官网上下载FreeRTOS的源码 FreeRTOS下面有4个目录: 1 Demo文件夹 FreeRTOS的相关例程 移植的时候可以进行参考 2 License文件夹 相关许可 3 Source文件夹 FreeRTOS的相关源码 FreeRTOS: Source --> include 包含了头文件,移植的时候需要 --> 源文件 移植的时候需要 --> protable FreeRTOS与不同硬件直接连接的桥梁 --> Keil 转向RVDS 使用MDK集成开发环境所需要的文件 --> ARM_CM3 --> port.c portmacro.h --> MemMang 内存管理相关文件,移植需要 FreeRTOS-Plus: 源码其实并不是 FreeRTOS 系统的源码,而是在 FreeRTOS 系统上另外增加的一些功能代码,比如 CLI、FAT、Trace 等等 

四 FreeRTOS基础知识

1 任务调度器 调度器就是使用相关的调度算法,来决定当前需要执行哪个任务 FreeRTOS 一共支持三种任务调度方式 抢占式调度 主要是针对优先级不同的任务,每个任务都有一个优先级,优先级高的任务可以抢占 优先级低的任务 

在这里插入图片描述

抢占式总结: 1 高优先级任务,优先执行 2 高优先级任务不停止,低优先级任务无法执行 时间片调度 主要是针对优先级相同的任务,当多个任务的优先级相同时,任务调度器会在每一次系统时钟节拍 到的时候切换任务 同等优先级任务轮流的享有相同的CPU,叫时间片,在FreeRTOS中, 一个时间片就等于SysTick中断周期(一般就是1ms) eg: 运行条件: 1 创建三个任务 task1 task2 task3 2 task1、task2、task3的优先级均为1;即3个任务的优先级一致 

在这里插入图片描述

运行过程如下 1 首先task1运行完一个时钟周期,切换至task2运行 2 task2运行完一个时间片之后,切换至task3运行 3 task3运行过程中(没有运行完一个时间片), task3阻塞了(系统延时或者等待信号量)此时直接切换到task1运行 4 task1运行完一个时间片后,切换至task2运行 注意: 1 同等优先级任务,轮流执行,时间片轮转 2 一个时间片大小,取决为滴答定时器中断周期 3 注意没有完全用完的时间片不会再使用,下次任务task3得到执行还是按照一个时间片 的时钟节拍运行 协程式调度(了解) 当前执行任务将会一直运行,同时高优先级的任务不会抢占低优先级任务 FreeRTOS现在虽然还支持,但是官方已经表示不会再更新协程式调度 2 任务状态 a 运行态 正在运行的任务,该任务就是运行态,注意,在STM32中,同一时间仅一个任务处于运行态 b 就绪态 如果该任务已经能够被执行,但是尚未被执行,那么该任务处于就绪态 c 阻塞态 如果一个任务因为延时或者等待外部事件发生,那么这个任务就处于阻塞态 d 挂起态 类似暂停,调用vTaskSuspend()进入挂起状态,需要调用vTaskResume()才可以进入就绪态 

在这里插入图片描述

总结: 1 仅就绪态可以进入运行态 2 其他状态的任务想要运行,必须先进入就绪态 这四种状态中,除了运行态,其他三种任务状态的任务都有其对应的任务状态列表 就绪列表 pxReadyTasksLists[x], 其中x代表任务优先级数值 //用硬件来表示优先级的话,x的取值为0~31 阻塞列表 pxDelayedTaskList 挂起列表 xSuspendedTasklist 将来从就绪列表中挑选一个优先级最高的任务进行执行! 通过一个32b的变量,当某一位置1时,代表所对应的优先级列表中就有任务。 加入创建了三个任务:Task1 Task2 Task3, 他们分别对应的优先级是 31,30,29 他们三个任务会被分别放到 pxReadyTaskLists[31], pxReadyTaskLists[30],pxReadyTaskLists[29] 结合变量中位置为1的位置,cpu就知道先去执行哪个列表中的任务了 eg: 

在这里插入图片描述

调度器总是在所有就绪列表的任务中,选择具有最高优先级的任务来执行 五 FreeRTOS在STM32F103上的移植 5.0 先将01test拷贝一份, 放到porting目录下面, 添加led.c led.h 编译运行,保证led0, led1, led2 是正常可以点亮的 为了确保在移植的过程中某一步出错,导致移植失败 5.1 向工程中添加相应的文件 a) 添加FreeRTOS源码 在01test中新建一个FreeRTOS的目录,将来FreeRTOS的相关的文件放到这里面来 将从官网下的FreeRTOS的源码中的include目录拷贝到FreeRTOS目录下面来 将从官网下的FreeRTOS的源码中的源文件拷贝到FreeRTOS目录下面来 将从官网下的FreeRTOS的源码中的protable目录拷贝到FreeRTOS目录下面来 将拷贝过来的protable 目录中的 Keil、MemMang、RVDS三个目录保留下来,其余的删除就可以了 打开项目工程,右键Target,点击 add Group ... --> 右键 New Group --> 点击"Manage project items" ---> 将"New Group"的名字改为"FreeRTOS_protable" 双击 FreeRTOS_protable 将 官方源码中的port.c 和 heap_4.c 添加到项目工程中来 打开项目工程,右键Target,点击 add Group ... --> 右键 New Group --> 点击"Manage project items" ---> 将"New Group"的名字改为"FreeRTOS_core" 双击 FreeRTOS_core 将 官方源码中的源文件添加到项目工程中来 5.2 添加相应的头文件路径 分别右键 FreeRTOS_protable 和 FreeRTOS_core 点击"Options for group..." --> 点击Include Paths --> 新增两条路径 --》 ..\FreeRTOS\include; ..\FreeRTOS\portable\RVDS\ARM_CM3 包含完头文件之后,编译会发现报 --》 找不到FreeRTOSConfig.h 头文件 去FreeRTOS官方移植的Demo中找到这个头文件: FreeRTOSv.01\FreeRTOS\Demo\CORTEX_STM32F103_Keil 有该文件 将其放到FreeRTOS源码的include目录下, 但是建议使用模板工程中的FreeRTOSConfig.h 到这里我们再编译一次,没有错误! 5.3 添加相关的文件 a) 添加sys.h (模板工程中的sys.h) 文件拷贝到mylib中 在sys.h中添加: //定义该宏表示支持操作系统 #define SYSTEM_SUPPORT_OS 1 其他位置不需要修改 b) 添加delay.c 和delay.h 到mylib中,并将delay.c 添加到项目工程中 vi delay.h 
 #ifndef __DELAY_H #define __DELAY_H  #include "sys.h"  void delay_init(void); void delay_ms(u32 nms); void delay_us(u32 nus); void delay_xms(u32 nms); #endif 
 vi delay.c 
#include "delay.h" #include "FreeRTOS.h"  #include "task.h" static u8 fac_us=0; //us延时倍乘数  static u16 fac_ms=0; //ms延时倍乘数 extern void xPortSysTickHandler(void); //systick中断服务函数 void SysTick_Handler(void) { 
    if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行 { 
    xPortSysTickHandler(); } } //初始化延迟函数 //SYSTICK的时钟固定为AHB时钟,基础例程里面SYSTICK时钟频率为AHB/8 //这里为了兼容FreeRTOS,所以将SYSTICK的时钟频率改为AHB的频率 及系统的时钟频率为72MHz //SYSCLK:系统时钟频率 void delay_init() { 
    u32 reload; SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);//选择外部时钟 HCLK fac_us = SystemCoreClock / ; //不论是否使用OS,fac_us都需要使用 reload = SystemCoreClock / ; //每秒钟的计数次数 单位为M  reload *=  / configTICK_RATE_HZ; //根据configTICK_RATE_HZ设定溢出时间 //reload为24位寄存器,最大值:,在72M下,约合0.233s左右  fac_ms = 1000 / configTICK_RATE_HZ; //代表OS可以延时的最少单位  SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk; //开启SYSTICK中断 SysTick->LOAD=reload; //每1/configTICK_RATE_HZ秒中断一次  SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK  } //延时nus //nus:要延时的us数.  //nus:0~(最大值即2^32/fac_us@fac_us=168)  void delay_us(u32 nus) { 
    u32 ticks; u32 told,tnow,tcnt = 0; u32 reload = SysTick->LOAD; //LOAD的值  ticks = nus*fac_us; //需要的节拍数  told = SysTick->VAL; //刚进入时的计数器值 while(1) { 
    tnow = SysTick->VAL; if(tnow != told) { 
    if(tnow < told) { 
    tcnt += told - tnow; } //这里注意一下SYSTICK是一个递减的计数器就可以了. else { 
    tcnt += reload - tnow + told; } told = tnow; //时间超过/等于要延迟的时间,则退出. if(tcnt >= ticks) { 
    break; } } }; } //延时nms //nms:要延时的ms数 //nms:0~65535 void delay_ms(u32 nms) { 
    if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行 { 
    if(nms>=fac_ms) //延时的时间大于OS的最少时间周期  { 
    vTaskDelay(nms / fac_ms); //FreeRTOS延时, FreeRTOS的最小的延时是1ms } nms % =fac_ms; //OS已经无法提供这么小的延时了,采用普通方式延时  } delay_us((u32)(nms*1000)); //普通方式延时 } //延时nms,不会引起任务调度 //nms:要延时的ms数 void delay_xms(u32 nms) { 
    u32 i; for(i=0;i<nms;i++) delay_us(1000); } 
c) 增加头文件的索引路径: 

在这里插入图片描述

此时再重新编译, 会报如下的错误: 

在这里插入图片描述

在stm32f10x_it.c 中, 将上面的三个函数屏蔽掉即可 d) 添加misc.c 双击fwliib将misc.c 添加到项目工程中来即可 编译通过! FreeRTOS的基本移植就完成了 
#include "led.h" #include "delay.h" #include "FreeRTOS.h" #include "task.h" //任务优先级 #define START_TASK_PRIO 1 //任务使用的栈的大小  #define START_STK_SIZE 128  //任务句柄 TaskHandle_t StartTask_Handler; //任务函数 void start_task(void *pvParameters); //任务优先级 #define LED0_TASK_PRIO 2 //任务使用的栈的大小  #define LED0_STK_SIZE 256  //任务句柄 TaskHandle_t LED0Task_Handler; //任务函数 void led0_task(void *pvParameters); //任务优先级 #define LED1_TASK_PRIO 3 //任务使用的栈的大小  #define LED1_STK_SIZE 256  //任务句柄 TaskHandle_t LED1Task_Handler; //任务函数 void led1_task(void *pvParameters); //任务优先级 #define LED2_TASK_PRIO 4 //任务使用的栈的大小  #define LED2_STK_SIZE 256  //任务句柄 TaskHandle_t LED2Task_Handler; //任务函数 void led2_task(void *pvParameters); int main(void) { 
    led_init (); delay_init (); //创建开始任务 xTaskCreate(start_task, "start_task", START_STK_SIZE, NULL, START_TASK_PRIO, &StartTask_Handler); //开启任务调度 vTaskStartScheduler(); } //开始任务任务函数 void start_task(void *pvParameters) { 
    //进入临界区 taskENTER_CRITICAL(); //创建led0任务 xTaskCreate(led0_task, "led0_task", LED0_STK_SIZE, NULL, LED0_TASK_PRIO, &LED0Task_Handler); //创建led1任务 xTaskCreate(led1_task, "led1_task", LED1_STK_SIZE, NULL, LED1_TASK_PRIO, &LED1Task_Handler); //创建led2任务 xTaskCreate(led2_task, "led2_task", LED2_STK_SIZE, NULL, LED2_TASK_PRIO, &LED2Task_Handler); //删除开始任务 vTaskDelete(StartTask_Handler); //退出临界区 taskEXIT_CRITICAL(); } //LED0任务函数 void led0_task(void *pvParameters) { 
    while(1) { 
    led_on (0); vTaskDelay(300); led_off (0); vTaskDelay (300); } } //LED1任务函数 void led1_task(void *pvParameters) { 
    while(1) { 
    led_on (1); vTaskDelay(300); led_off (1); vTaskDelay (600); } } //led2任务函数 void led2_task(void *pvParameters) { 
    while(1) { 
    led_on (2); vTaskDelay(300); led_off (2); vTaskDelay(900); } } 
“INCLUDE_”开头的宏用来表示使能或禁止 FreeRTOS 中相应的 API 函数,作用就是用来配置 FreeRTOS 中的可选 API 函数的。 比如当宏 INCLUDE_vTaskPrioritySet 设置为 0 的时候 表示不能 使用函数 vTaskPrioritySet() , 当设置 为 1 的时 候就表示可 以使用函 数vTaskPrioritySet()。这个功能其实就是条件编译 “config”开始的宏, “config”开始的宏和“INCLUDE_”开始的宏一样, 都是用来完成 FreeRTOS 的配置和裁剪的 

八 中断管理

Cortex-M 处理器有多个用于管理中断和异常的可编程寄存器,这些寄存器大多数都在 NVIC 和系统控制块(SCB)中,CMSIS 将这些寄存器定义为结构体 core_cm3.h 中: NVIC的内存映射结构: typedef struct { __IO uint32_t ISER[8]; /*!< Offset: 0x000 Interrupt Set Enable Register */ uint32_t RESERVED0[24]; __IO uint32_t ICER[8]; /*!< Offset: 0x080 Interrupt Clear Enable Register */ uint32_t RSERVED1[24]; __IO uint32_t ISPR[8]; /*!< Offset: 0x100 Interrupt Set Pending Register */ uint32_t RESERVED2[24]; __IO uint32_t ICPR[8]; /*!< Offset: 0x180 Interrupt Clear Pending Register */ uint32_t RESERVED3[24]; __IO uint32_t IABR[8]; /*!< Offset: 0x200 Interrupt Active bit Register */ uint32_t RESERVED4[56]; __IO uint8_t IP[240]; /*!< Offset: 0x300 Interrupt Priority Register (8Bit wide) */ uint32_t RESERVED5[644]; __O uint32_t STIR; /*!< Offset: 0xE00 Software Trigger Interrupt Register */ } NVIC_Type; 系统控制块: typedef struct { __I uint32_t CPUID; /*!< Offset: 0x00 CPU ID Base Register */ __IO uint32_t ICSR; /*!< Offset: 0x04 Interrupt Control State Register */ __IO uint32_t VTOR; /*!< Offset: 0x08 Vector Table Offset Register */ __IO uint32_t AIRCR; /*!< Offset: 0x0C Application Interrupt / Reset Control Register */ __IO uint32_t SCR; /*!< Offset: 0x10 System Control Register */ __IO uint32_t CCR; /*!< Offset: 0x14 Configuration Control Register */ __IO uint8_t SHP[12]; /*!< Offset: 0x18 System Handlers Priority Registers (4-7, 8-11, 12-15) */ __IO uint32_t SHCSR; /*!< Offset: 0x24 System Handler Control and State Register */ __IO uint32_t CFSR; /*!< Offset: 0x28 Configurable Fault Status Register */ __IO uint32_t HFSR; /*!< Offset: 0x2C Hard Fault Status Register */ __IO uint32_t DFSR; /*!< Offset: 0x30 Debug Fault Status Register */ __IO uint32_t MMFAR; /*!< Offset: 0x34 Mem Manage Address Register */ __IO uint32_t BFAR; /*!< Offset: 0x38 Bus Fault Address Register */ __IO uint32_t AFSR; /*!< Offset: 0x3C Auxiliary Fault Status Register */ __I uint32_t PFR[2]; /*!< Offset: 0x40 Processor Feature Register */ __I uint32_t DFR; /*!< Offset: 0x48 Debug Feature Register */ __I uint32_t ADR; /*!< Offset: 0x4C Auxiliary Feature Register */ __I uint32_t MMFR[4]; /*!< Offset: 0x50 Memory Model Feature Register */ __I uint32_t ISAR[5]; /*!< Offset: 0x60 ISA Feature Register */ } SCB_Type; NVIC 和 SCB 都位于系统控制空间(SCS)内,SCS 的地址从 0XE000E000 开始 #define SCS_BASE (0xE000E000) #define NVIC_BASE (SCS_BASE + 0x0100) #define SCB_BASE (SCS_BASE + 0x0D00) 将来需要重点关心primask, basepri, Faultmask 这三个寄存器即可 primask 寄存器是中断使能寄存器 priimask 用于禁止除 NMI 和 HardFalut 外的所有异常和中断 可以使用: CPSIE I; //清除 PRIMASK(使能中断) CPSID I; //设置 PRIMASK(禁止中断) 还可以通过 MRS 和 MSR 指令访问: MOVS R0, #1 MSR PRIMASK, R0 ;//将 1 写入 PRIMASK 禁止所有中断 MOVS R0, #0 MSR PRIMASK, R0 ;//将 0 写入 PRIMASK 以使能中断 faultmask 可以连 HardFault 都屏蔽掉, 使用方法与primask类似 CPSIE F ;清除 FAULTMASK CPSID F ;设置 FAULTMASK 还可以利用 MRS 和 MSR 指令访问 FAULTMASK 寄存器 MOVS R0, #1 MSR FAULTMASK, R0 ;将 1 写入 FAULTMASK 禁止所有中断 MOVS R0, #0 MSR FAULTMASK, R0 ;将 0 写入 FAULTMASK 使能中断 在 BASEPRI 寄存器中,不过如果向 BASEPRI 写 0 的话就会停止屏蔽中断。 比如,我们要屏蔽优先级不高于 0X60 的中断,则可以使用如下汇编编程: MOV R0, #0X60 MSR basepri, R0 如果需要取消 BASEPRI 对中断的屏蔽, 可以使用如下代码: MOV R0, #0 MSR BASEPRI, R0 STM32 中使用了寄存器中的4位表示优先级, 最多能表示16个优先级 #define NVIC_PriorityGroup_0 ((uint32_t)0x700) /*!< 0 bits for pre-emption priority 4 bits for subpriority */ #define NVIC_PriorityGroup_1 ((uint32_t)0x600) /*!< 1 bits for pre-emption priority 3 bits for subpriority */ #define NVIC_PriorityGroup_2 ((uint32_t)0x500) /*!< 2 bits for pre-emption priority 2 bits for subpriority */ #define NVIC_PriorityGroup_3 ((uint32_t)0x400) /*!< 3 bits for pre-emption priority 1 bits for subpriority */ #define NVIC_PriorityGroup_4 ((uint32_t)0x300) /*!< 4 bits for pre-emption priority 0 bits for subpriority */ FreeRTOS 中断配置宏: configPRIO_BITS 此宏用来设置 MCU 使用几位优先级,STM32 使用的是 4 位,因此此宏为 4 configLIBRARY_LOWEST_INTERRUPT_PRIORITY 优先级数就是 16 个,最低优先级那就是 15 configKERNEL_INTERRUPT_PRIORIT 内核中断优先级 #define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) 宏 configKERNEL_INTERRUPT_PRIORITY 为, 宏configLIBRARY_LOWEST_INTERRUPT_PRIORITY 左移 8-configPRIO_BITS 位,也就是左移 4位。 为什么要左移 4 位呢?前面我们说了,STM32 使用了 4 位作为优先级,而这 4 位是高 4 位, 因此要左移4位才是真正的优先级 。 

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/122715.html

(0)
上一篇 2025-10-14 16:26
下一篇 2025-10-14 16:45

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信