3 FreeRTOS移植

1 获取FreeRTOS源码

1.1FreeRTOS源码内容

1.2FreeRTOS内核文件

1)Demo文件夹

Demo文件夹里面是FreeRTOS的演示例程:

2)Source文件夹

3)protable文件夹

proble文件夹是将FreeRTOS的操作系统跟硬件联系在一起,由于使用MDK开发,因此只重点介绍其中的部分移植文件

2 FreeRTOS移植

2.1 准备文件

1)正点原子标准例程-HAL库版本的内存管理实验

2)正点原子标准例程-HAL库版本的定时器实验

3)从FreeRTOS的官网获取FreeRTOS的源码

2.2 添加FreeRTOS源码

1)在基础工程的 Middlewares 文件夹中新建一个 FreeRTOS 子文件夹:

2)将官网下载的FreeRTOS源码中的Source文件夹下的所有文件添加到工程中新建的FreeRTOS文件夹中。

对于portable中的文件可以只需要保留Keil、MemMang、RVDS三个文件夹,其余用不到的文件夹,可以自行决定删除与否。

2.3 添加FreeRTOSConfig.h配置文件

1)打开内存管理实验工程,创建两个文件夹分组:

  • Middlewares/FreeRTOS_CORE

        用于存放FreeRTOS 的内核 C 源码文件,即

  • Middlewares/FreeRTOS_PORT

        用于存放 FreeRTOS 内核的移植文件,需要添加两类文件,分别为heap.c和port文件。

        heap.c路径:FreeRTOS/portable/MemMang

        port路径:FreeRTOS/portable/RVDS

        STM32系列开发板不同与不同的port 文件的对用关系如下:

        

        将所有 FreeRTOS 相关的所需文件添加到工程后,如下图所示:
        

2)添加头文件路径

由于在Keil中新增了FreeRTOS文件内容,因此需要添加.h文件路径:

3)添加FreeRTOSConfig.h文件

  • 方法一   用户自行编写,用户可以根据自己的需求编写 FreeRTOSConfig.h 对FreeRTOS 操作系统进行裁剪。
  • 方法二   FreeRTOS 内核的演示工程,Demo 文件夹中包含了 FreeRTOS 官方提供的演示工程,在这些演示工程当中就包含了每个演示工程对应的 FreeRTOSConfig.h 文件,需要注意的是,有些演示工程使用的是老版本的 FreeRTOS,因此部分演示工程的 FreeRTOSConfig.h 文件并不能够很好的适用于新版本的 FreeRTOS。
  • 方法三   正点原子配套例程“FreeRTOS 移植实验” 的 User 子文件夹下找到FreeRTOSConfig.h文件

        通过以上三种途径得到的FreeRTOSConfig.h 文件添加到基础工程的 User 子目录下即可。

2.4 修改SYSTEM文件

SYSTEM 文件夹中的文件一开始是针对 µC/OS 编写的,因此使用 FreeRTOS 的话,就需要作相应的修改。 SYSTEM 文件夹中一共需要修改三个文件,分别是 sys.h、 usart.c、 delay.c。

2.4.1 sys.h文件

在sys.h文件中使用了宏SYS_SUPPORT_OS来定义是否支持OS,因为要支持FreeRTOS,因此应当将宏SYS_SUPPORT_OS定义为1。

2.4.2 usart.c文件

  • 修改处一  :  串口的中断服务函数,原本在使用uC/OS的时候,进入和退出中断需要添加OSIntEnter()和 OSIntExit()两个函数,这是uC/OS对于中断的相关处理机制,而FreeRTOS中并没有这种机制,因此将这两行代码删除(针对F4系列),修改后如图所示:
void USART_UX_IRQHandler(void)
{ 
    uint32_t timeout = 0;
	
		uint32_t maxDelay = 0x1FFFF;
	
		HAL_UART_IRQHandler(&g_uart1_handle); /* 调用 HAL 库中断处理公用函数 */
	
		timeout = 0;
	
		while (HAL_UART_GetState(&g_uart1_handle)!= HAL_UART_STATE_READY) /* 等待就绪 */
	{
		timeout++; /* 超时处理 */
		if(timeout > maxDelay)
		{
			break;
		}
}
		timeout=0;
		/* 一次处理完成之后,重新开启中断并设置 RxXferCount 为 1 */
		while (HAL_UART_Receive_IT( &g_uart1_handle,(uint8_t *)g_rx_buffer,RXBUFFERSIZE) != HAL_OK)
		{
			timeout++;/* 超时处理 */
			if (timeout > maxDelay)
			{	
				break;
			}
		}
}
  • 修改处二: 导入的头文件,因为在串口的中断服务函数当中已经删除了 µC/OS 的相关代码,并且也没有使用到 FreeRTOS 的相关代码,因此将 usart.c 中包含的关于 OS 的头文件删除,删除内容如下:
/* 如果使用 os,则包括下面的头文件即可. */
#if SYS_SUPPORT_OS
#include "includes.h" /* os 使用 */
#endif

2.4.3 delay.c文件

由于delay.c 文件需要改动的地方比较多,大致可分为三个步骤:

(1)删除适用于 µC/OS 但不适用于 FreeRTOS 的相关代码,删除如下程序:

/* 定义 g_fac_ms 变量, 表示 ms 延时的倍乘数,
* 代表每个节拍的 ms 数, (仅在使能 os 的时候,需要用到)
*/
static uint16_t g_fac_ms = 0;
/*
* 当 delay_us/delay_ms 需要支持 OS 的时候需要三个与 OS 相关的宏定义和函数来支持
* 首先是 3 个宏定义:
* delay_osrunning :用于表示 OS 当前是否正在运行,以决定是否可以使用相关函数
* delay_ostickspersec :用于表示 OS 设定的时钟节拍,
* delay_init 将根据这个参数来初始化 systick
* delay_osintnesting :用于表示 OS 中断嵌套级别,因为中断里面不可以调度,
* delay_ms 使用该参数来决定如何运行
* 然后是 3 个函数:
* delay_osschedlock :用于锁定 OS 任务调度,禁止调度
* delay_osschedunlock :用于解锁 OS 任务调度,重新开启调度
* delay_ostimedly :用于 OS 延时,可以引起任务调度.
*
* 本例程仅作 UCOSII 和 UCOSIII 的支持,其他 OS,请自行参考着移植
*/
/* 支持 UCOSII */
#ifdef OS_CRITICAL_METHOD /* OS_CRITICAL_METHOD 定义了
* 说明要支持 UCOSII
*/
#define delay_osrunning OSRunning /* OS 是否运行标记,0,不运行;1,在运行 */
#define delay_ostickspersec OS_TICKS_PER_SEC /* OS 时钟节拍,即每秒调度次数 */
#define delay_osintnesting OSIntNesting /* 中断嵌套级别,即中断嵌套次数 */
#endif
/* 支持 UCOSIII */
#ifdef CPU_CFG_CRITICAL_METHOD /* CPU_CFG_CRITICAL_METHOD 定义了
* 说明要支持 UCOSIII
*/
#define delay_osrunning OSRunning /* OS 是否运行标记,0,不运行;1,在运行 */
#define delay_ostickspersec OSCfg_TickRate_Hz /* OS 时钟节拍,即每秒调度次数 */
#define delay_osintnesting OSIntNestingCtr /* 中断嵌套级别,即中断嵌套次数 */
#endif
/**
* @brief us 级延时时,关闭任务调度(防止打断 us 级延迟)
* @param 无
* @retval 无
*/
static void delay_osschedlock(void)
{
#ifdef CPU_CFG_CRITICAL_METHOD /* 使用 UCOSIII */
OS_ERR err;
OSSchedLock(&err); /* UCOSIII 的方式,禁止调度,防止打断 us 延时 */
#else /* 否则 UCOSII */
OSSchedLock(); /* UCOSII 的方式,禁止调度,防止打断 us 延时 */
#endif
}

(2)添加 FreeRTOS 的相关代码

在使用ARMClang(AC6)的情况下可跳过这个步骤,而在使用ARMCC(AC5)的情况下只需要在delay.c文件中使用extern 关键字导入一个FreeRTOS函数——xPortSysTickHandler()即可,该函数是用于处理FreeRTOS系统时钟节拍的,本教程是使用SysTick作为FreeRTOS操作系统的心跳,因此需要在SysTick的中断服务函数中调用这个函数,因此需要将代码添加到Systick中断服务函数之前,代码修改如下:

extern void xPortSysTickHandler(void);
/**
* @brief systick 中断服务函数,使用 OS 时用到
* @param ticks: 延时的节拍数
* @retval 无
*/
void SysTick_Handler(void)
{
/* 代码省略 */
}

(3)修改部分内容

  最后要修改的内容包括两部分,分别包含头文件和4个函数。

  • SysTick_Handler()

SysTick 的中断服务函数,由于 FreeRTOS 在针对 ARMClang(AC6)编译器的port 文件中实现了该函数,而在针对 ARMCC(AC5)编译器的 port 文件中没有实现该函数,因此该函数的修改针对实际使用的编译器类型(ARMClang 或 ARMCC)分为两种情况。

ARMClang 是基于 LLVM 的编译器,是 ARM 公司推出的较新的编译器工具链,版本号为 6.x 或更高,通常被称为 AC6(ARM Compiler 6)。ARMCC 是较老版本的 ARM 编译器,版本号为 5.x,通常被称为 AC5(ARM Compiler 5)。

由于使用的是 ARMCC( AC5)的情况, 只需在这个函数中不断调用上个步骤中导入的函数xPortSysTickHandler(),代码修改后如下所示:

/**
 * @brief     systick中断服务函数,使用OS时用到
 * @param     ticks : 延时的节拍数  
 * @retval    无
 */  
void SysTick_Handler(void)
{
    HAL_IncTick();
		/* OS 开始跑了,才执行正常的调度处理 */
		if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
		{
			xPortSysTickHandler();
		}
}

对于使用 ARMClang(AC6)的情况,由于 port 文件中定义并实现了 SysTick_Handler 函数,因此首先需要屏蔽图中GCC目录下对应的port文件中定义和实现的Systick_Handler函数,该函数在port文件中定义和实现SysTick_Handler函数。

  • delay_init()

函 数 delay_init() 主 要 用 于 初 始 化 SysTick 。FreeRTOS会按照FreeRTOSConfig.h文件的配置对SysTick进行初始化,因此SysTick 主要使用在 FreeRTOS 开始任务调度之前。程序修改如下:


/**
 * @brief     初始化延迟函数
 * @param     sysclk: 系统时钟频率, 即CPU频率(rcc_c_ck), 168MHz
 * @retval    无
 */  
void delay_init(uint16_t sysclk)
{
	#if SYS_SUPPORT_OS
		uint32_t reload;
	#endif
		HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
		g_fac_us = sysclk;
	#if SYS_SUPPORT_OS
		reload = sysclk;
		/* 使用 configTICK_RATE_HZ 计算重装载值
		* configTICK_RATE_HZ 在 FreeRTOSConfig.h 中定义
		*/
		reload *= 1000000 / configTICK_RATE_HZ;
		/* 删除不用的 g_fac_ms 相关代码 */
		SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;
		SysTick->LOAD = reload;
		SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
	#endif
}

STM32F1系列的函数delay_init()将SysTick的时钟频率设置为CPU时钟频率的1/8,而STM32F4/G4/F7/H7/H5系列的函数delay_init()则将SysTick的时钟频率设置为与CPU相同的时钟频率,由于FreeRTOS在配置SysTick时,并不会配置 SysTick 的时钟源,因此这将导致正点原子 STM32F1 系列与正点原子 STM32F4/G4/F7/H7/H5 系列的 FreeRTOSConfig.h 文件有所差异。

  • delay_us()

函数 delay_us()用于微秒级的 CPU 延时,原本的函数 delay_us()延时的前后加入了自定义函数 delay_osschedlock()和 delay_osschedunlock()用于锁定和解锁 µC/OS 的任务调度器,以此来让延时更加准确。在FreeRTOS中可以不用加这两个函数,但是,这会让函数delay_us()的微妙级延时的精度有所下降,函数 delay_us()修改后的代码如下所示:

void delay_us(uint32_t nus)
{
uint32_t ticks;
uint32_t told, tnow, tcnt = 0;
uint32_t reload = SysTick->LOAD;
/* 删除适用于 µC/OS 用于锁定任务调度器的自定义函数 */
ticks = nus * g_fac_us;
told = SysTick->VAL;
while (1)
{
tnow = SysTick->VAL;
if (tnow != told)
{
if (tnow < told)
{
tcnt += told - tnow;
}
else
{
tcnt += reload - tnow + told;
}
told = tnow;
if (tcnt >= ticks)
{
break;
}
}
}
/* 删除适用于 µC/OS 用于解锁任务调度器的自定义函数 */
}
  • delay_ms()

        函数delay_ms()用于毫秒的CPU延时,原本的函数delay_ms()会判断µC/OS 是否运行,如果µC/OS 正在运行的话,就使用 µC/OS 的 OS 延时进行毫秒级的延时,否则就调用函数delay_us()进行毫秒级的 CPU 忙延时。在 FreeRTOS 中,可以将函数 delay_ms()定义为只进行CPU 忙延时,当需要 OS 延时的时候,调用 FreeRTOS 提供的 OS 延时函数 vTaskDelay()进行系统节拍级延时,函数 delay_ms()修改后的代码如下所示:

void delay_ms(uint16_t nms)
{
uint32_t i;
for (i=0; i<nms; i++)
{
delay_us(1000);
}
}
  • 包含头文件

        根据上述步骤的修改,delay.c文件中使用了FreeRTOS的相关函数,因此就需要在delay.c文件中包含 FreeRTOS 的相关头文件,并且移除掉原本存在的 µC/OS 相关头文件
修改前 delay.c 文件中包含的 µC/OS 相关的头文件:
 

/* 添加公共头文件 ( ucos 需要用到) */
#include "includes.h"

修改后:

/* 添加公共头文件 (FreeRTOS 需要用到) */
#include "FreeRTOS.h"
#include "task.h"

2.5 修改中断相关文件

在FReeRTOS的移植过程中会有三个重要的中断:FreeRTOS系统基时定时器中断(SysTick中断)、SVC中断、PendSV中断,对于正点原子不同的STM32开发板,对应了不同的文件,具体对应关系如下图所示:

其中, SysTick 的中断服务函数在 delay.c 文件中已经定义了,并且 FreeRTOS 也提供了 SVC和 PendSV 的中断服务函数,因此需要将 HAL 库提供的这三个中断服务函数注释掉,这里采用宏开关的方式让 HAL 库中的这三个中断服务函数不加入编译,使用的宏在 sys.h 中定义,因此还需要导入 sys.h 头文件,根据上表中找到对应的文件进行修改,修改后的程序如下:

/* 仅展示修改部分,其余代码未修改、不展示 */
/* Includes --------------------------------------*/
/* 导入 sys.h 头文件 */
#include "./SYSTEM/SYS/sys.h"
/**
* @brief This function handles SVCall exception.
* @param None
* @retval None
*/
/* 加入宏开关 */
#if (!SYS_SUPPORT_OS)
void SVC_Handler(void)
{ }
#endif
/**
* @brief This function handles PendSVC exception.
* @param None
* @retval None
*/
/* 加入宏开关 */
#if (!SYS_SUPPORT_OS)
void PendSV_Handler(void)
{ }
#endif
/**
* @brief This function handles SysTick Handler.
* @param None
* @retval None
*/
/* 加入宏开关 */
#if (!SYS_SUPPORT_OS)
void SysTick_Handler(void)
{
HAL_IncTick();
}
#endif

最后移植FreeRTOS要修改的最后一个地方,FreeRTOSConfig.h文件中有如下定义:
 

#define configPRIO_BITS __NVIC_PRIO_BITS

对于正点原子不同的STM32开发板,_NVIC_PRIO_BITS定义在不同的文件中:

虽然不同类型的开发板对应的文件不同,但是_NVIC_PRIO_BITS都被定义成了相同的值,:

#define __NVIC_PRIO_BITS 4U

虽然这个值是正确的,但是如果将_NVIC_PRIO_BITS定义成4U的话,在编译FreeRTOS工程的时候Keil会报错,具体方法是将4U改成4.

#define __NVIC_PRIO_BITS 4

2.6 可选步骤

2.6.1 修改工程目标名称

由于内存管理实验工程的工程目标名为“MALLOC”,因此可以规范工程,将工程名改为“FreeRTOS”或者根据实际场景修改。

2.6.2 移除USMART调试组件

由于正点原子并未使用USMART调试组件,因此可以将USMART调试组件从工程中移除,或者可以选择不移除。

2.6.3 添加定时器驱动

在后续的实验中需要使用到STM32的基本定时器外设,因此需要向工程中添加定时器的相关驱动文件。将定时器的相关驱动文件(本实验使用的是实验8 基本定时器)添加到工程的 Drivers/BSP 文件分组中。

3 FreeRTOS系统配置

3.1 FreeRTOSConfig.h文件

FreeRTOS使用FreeRTOSConfig.h文件进行剪裁和配置。FreeRTOSConfig.h文件中有几十个配置项,这使得用户能够很好的配置和剪裁FreeRTOS。

FreeRTOSConfig.h文件中的配置项可以分为三大类:“config”配置项、“INCLUDE”配置项和其他配置项。

3.2 “config”配置项

“config”配置项按照配置的功能分类:

/* 基础配置项 */
#define configUSE_PREEMPTION                            1                       
/*1: 抢占式调度器, 0: 协程式调度器。 协程式调度是正在运行的任务主动释放CPU后才能切换到下一个任务,任务切换的时完全取决于正在运行的任务。 协程式的优点在于可以节省开销,但是功能比较有限,现在的 MCU 性能都比较强大,建议使用抢占式调度机*/
#define configUSE_PORT_OPTIMISED_TASK_SELECTION         1                       
/* 1: 使用硬件计算下一个要运行的任务, 0: 使用软件算法计算下一个要运行的任务, 默认: 0。使用通用方法是完全使用 C 实现的软件算法,因此支持所用硬件,并且不限制任务优先级的最大值,但效率相较于特殊方法低。
使用特殊方法的效率相较于通用方法高,但是特殊方法依赖于一个或多个特定架构的汇编指令,因此特殊方法并不支持所有硬件,并且对任务优先级的最大值一般也有限制,通常为 32。 */
#define configUSE_TICKLESS_IDLE                         0                       
/* 1: 使能tickless低功耗模式, 默认: 0 ,tickless 低功耗模式并不适用于所有硬件*/
#define configCPU_CLOCK_HZ                              SystemCoreClock        
/* 定义CPU主频, 单位: Hz, 无默认需定义 */
//#define configSYSTICK_CLOCK_HZ                          (configCPU_CLOCK_HZ / 8)
/* 定义SysTick时钟频率,当SysTick时钟频率与内核时钟频率不同时才可以定义, 单位: Hz, 默认: 不定义 */
#define configTICK_RATE_HZ                              1000                    
/* 定义系统时钟节拍频率, 单位: Hz, 无默认需定义 */
#define configMAX_PRIORITIES                            32                      
/* 定义最大优先级数, 最大优先级=configMAX_PRIORITIES-1, 无默认需定义 */
#define configMINIMAL_STACK_SIZE                        128                     
/* 定义空闲任务的栈空间大小, 单位: Word, 无默认需定义 */
#define configMAX_TASK_NAME_LEN                         16                     
/* 定义任务名最大字符数, 默认: 16 */
#define configUSE_16_BIT_TICKS                          0                       
/* 1: 定义系统时钟节拍计数器的数据类型为16位无符号数, 无默认需定义 。
当宏 configUSE_16_BIT_TICKS 设置为 1 时,系统节拍计数器的数据类型为 16 位无符号整形; 
当宏 configUSE_16_BIT_TICKS 设置为 0 时,系统节拍计数器的数据类型为 32 为无符号整型。*/
#define configIDLE_SHOULD_YIELD                         1                       
/* 1: 使能在抢占式调度下,同优先级的任务能抢占空闲任务, 默认: 1。当宏 configIDLE_SHOULD_YIELD 设置为 1 时,在抢占调度下,同等优先级的任务可抢占
空闲任务,并延用空闲任务剩余的时间片。 */
#define configUSE_TASK_NOTIFICATIONS                    1                       
/* 1: 使能任务间直接的消息传递,包括信号量、事件标志组和消息邮箱, 默认: 1 */
#define configTASK_NOTIFICATION_ARRAY_ENTRIES           1                       
/* 定义任务通知数组的大小, 默认: 1。
当宏 configUSE_TASK_NOTIFICATIONS 设置为 1 时,开启任务通知功能。 当开启任务通知功能后,每个任务将多占用 8 字节的内存空间。*/
#define configUSE_MUTEXES                               1                       /* 1: 使能互斥信号量, 默认: 0 */
#define configUSE_RECURSIVE_MUTEXES                     1                       /* 1: 使能递归互斥信号量, 默认: 0 */
#define configUSE_COUNTING_SEMAPHORES                   1                       /* 1: 使能计数信号量, 默认: 0 */
#define configUSE_ALTERNATIVE_API                       0                       /* 已弃用!!! */
#define configQUEUE_REGISTRY_SIZE                       8                       /* 定义可以注册的信号量和消息队列的个数, 默认: 0 */
#define configUSE_QUEUE_SETS                            1                       /* 1: 使能队列集, 默认: 0 */
#define configUSE_TIME_SLICING                          1                       /* 1: 使能时间片调度, 默认: 1 */
#define configUSE_NEWLIB_REENTRANT                      0                       /* 1: 任务创建时分配Newlib的重入结构体, 默认: 0 */
#define configENABLE_BACKWARD_COMPATIBILITY             0                       /* 1: 使能兼容老版本, 默认: 1 */
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS         0                       /* 定义线程本地存储指针的个数, 默认: 0 */
#define configSTACK_DEPTH_TYPE                          uint16_t                /* 定义任务堆栈深度的数据类型, 默认: uint16_t */
#define configMESSAGE_BUFFER_LENGTH_TYPE                size_t                  /* 定义消息缓冲区中消息长度的数据类型, 默认: size_t */
#define configSUPPORT_STATIC_ALLOCATION                 0                       
/* 1: 支持静态申请内存, 默认: 0
当宏 configSUPPORT_STSTIC_ALLOCATION 设置为 1 时, FreeRTOS 支持使用静态方式管理内存,此宏默认设置为 0。 如果将 configSUPPORT_STATIC_ALLOCATION 设置为 1,用户还 需 要 提 供 两 个 回 调 函 数 : vApplicationGetIdleTaskMemory() 和
vApplicationGetTimerTaskMemory(), */
#define configSUPPORT_DYNAMIC_ALLOCATION                1                       
/* 1: 支持动态申请内存, 默认: 1 */
#define configTOTAL_HEAP_SIZE                           ((size_t)(10 * 1024))   
/* FreeRTOS堆中可用的RAM总量, 单位: Byte, 无默认需定义
此宏用于定义用于 FreeRTOS 动态内存管理的内存大小,即 FreeRTOS 的内存堆。 */
#define configAPPLICATION_ALLOCATED_HEAP                0                       
/* 1: 用户手动分配FreeRTOS内存堆(ucHeap), 默认: 0 
此宏用于自定义 FreeRTOS 的内存堆,当宏configAPPLICATION_ALLOCATED_HEAP 设置为1时,用户需要自行创建FreeRTOS的内存堆,
否则FreeRTOS的内存堆将由编译器进行分配。利用此宏定义,可以使用 FreeRTOS 动态管理外扩内存。*/
#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP       0                       
/* 1: 用户自行实现任务创建时使用的内存申请与释放函数, 默认: 0 
此宏用于自定义动态创建和删除任务时,任务栈内存的申请与释放函数pvPortMallocStack()和vPortFreeStack(),
当宏configSTACK_ALLOCATION_FROM_SEPARATE_HEAP 设置为1是,用户需提供 pvPortMallocStack()和 vPortFreeStack()函数。*/

3.3 “INCLUDE”配置项

FreeRTOS 使用“INCLUDE”配置项对部分 API 函数进行条件编译,当“INCLUDE”配置项被定义为 1 时,其对应的 API 函数则会加入编译。对于用不到的 API 函数,用户则可以将其对应的“INCLUDE”配置项设置为 0,那么这个 API 函数就不会加入编译,以减少不必要的系统开销。
 

3.4其他配置项

1)secureconfigMAX_SECURE_CONTEXTS

此宏为 ARMv8-M 安全侧端口的相关配置项,本实验暂不涉及ARMv8-M 安全侧端口的相关内容。

2)xPortPendSVHandler 和 vPortSVCHandler

这两个宏为 PendSV 和 SVC 的中断服务函数,主要用于 FreeRTOS 操作系统的任务切换,有关 FreeRTOS 操作系统中任务切换的相关内容。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值