RTOS系统

一 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中找到这个头文件:
	FreeRTOSv202212.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 / 1000000;				//不论是否使用OS,fac_us都需要使用
	reload = SystemCoreClock / 1000000;				//每秒钟的计数次数 单位为M  
	reload *= 1000000 / configTICK_RATE_HZ;			//根据configTICK_RATE_HZ设定溢出时间
													//reload为24位寄存器,最大值:16777216,在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~204522252(最大值即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的基本移植就完成了

六 移植验证实验
vi main.c

#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);
    }
}

七 移植总结:
FreeRTOSConfig.h 文件

“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位才是真正的优先级 。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘德华海淀分华

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值