FreeRTOS中临界段的保护(笔记)

文章详细介绍了在Cortex-M微控制器和FreeRTOS操作系统中,如何使用临界段保护代码段不受中断打扰。临界段的定义、内核级别的快速开关中断指令如CPSID/CPSIE,以及FreeRTOS提供的关中断宏如portDISABLE_INTERRUPTS和portENTER_CRITICAL,都在文中进行了阐述。此外,还讨论了不同类型的进入和退出临界段的宏,包括是否带中断保护和能否嵌套,并给出了临界段在中断场合和非中断场合的应用示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

临界段的定义

临界段是指在执行时不能被中断的代码段。在FreeRTOS中,临界段最经常出现的地方就是对全局变量的操作。

Q1:什么情况下临界段会被打断?
系统调度和外部中断。
在FreeRTOS中,系统调度最终也是产生PendSV中断,在PendSVHandler中实现任务的切换,所以还是归结为中断。
FreeRTOS对临界段的保护就是对中断的开和关的控制。

Cortex-M内核快速关开关中断的指令

CPSID I ;PRIMASK=1   ;关中断
CPSIE I ;PRIMASK=0   ;开中断
CPSID F ;PRIMASK=1   ;关异常
CPSIE F ;PRIMASK=1   ;开异常

PRIMASK 、 FAULTMAST 和 BASEPRI 是Cotex-M内核中3个中断屏蔽寄存器。

在这里插入图片描述

关中断

FreeRTOS关中断函数在portmacro.h中定义,分为带返回值和不带返回值两种。

/*不带返回值的关中断函数不能嵌套,不能在中断中使用 */
#define portDISABLE_INTERRUPTS()	vPortRaiseBASEPRI()
static portFORCE_INLINE void vPortRaiseBASEPRI( void )
{
uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;

	__asm
	{
		/* Set BASEPRI to the max syscall priority to effect a critical
		section. */
		msr basepri, ulNewBASEPRI
		dsb
		isb
	}
}

不带返回值的意思是,在向BASEPRI写入新值时,不用先将BASEPRI的值保存起来,即不用考虑当前中断状态是怎么样的,这意味着这样的函数不能在中断中调用。

#define configMAX_SYSCALL_INTERRUPT_PRIORITY 191 /* 高四位有效,即等于0xb0,或者是11 */
它是在FreeConfig.h中定义的宏,即大于等于11的中断都会被屏蔽。
ps:这个值可以自己设置,实际开发中,一般设置为5.
在FreeRTOS中,凡是看到FROM_ISR结尾的函数/宏定义都是在中断中使用的。

#define portSET_INTERRUPT_MASK_FROM_ISR()		ulPortRaiseBASEPRI()
static portFORCE_INLINE uint32_t ulPortRaiseBASEPRI( void )
{
uint32_t ulReturn, ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;

	__asm
	{
		/* Set BASEPRI to the max syscall priority to effect a critical
		section. */
		mrs ulReturn, basepri
		msr basepri, ulNewBASEPRI
		dsb
		isb
	}

	return ulReturn;
}

带返回值的关中断函数可以嵌套,可以在中断中使用。带返回值的意思是,在BASEPRI写入新值时,先将BASEPRI的值先保存起来,在更新完成BASEPRI的值时,将之前保存好的BASEPRI的值返回,作为形参传入开中断函数中。

开中断

FreeRTOS开中断函数在portmacro.h中定义

/*不带中断保护的开中断函数*/
#define portSET_INTERRUPT_MASK_FROM_ISR()		ulPortRaiseBASEPRI()
/*带中断保护的开中断函数*/
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x)	vPortSetBASEPRI(x)
static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
	__asm
	{
		/* Barrier instructions are not used as this function is only used to
		lower the BASEPRI value. */
		msr basepri, ulBASEPRI
	}
}

根据ulBASEPRI 的参数,选择保护和不带中断中断保护的版本
不带中断保护的开中断函数,直接将BASEPRI设置为0,与portDISABLE_INTERRUPTS()成对使用。
带中断保护的开中断函数,将上一次关中断保存的BASEPRI的值作为形参,与portSET_INTERRUPT_MASK_FROM_ISR()成对使用

进入临界段的宏

/*进入临界段,不带中断保护版本,不能嵌套*/
/* 在task.h中定义 */
#define taskENTER_CRITICAL()		portENTER_CRITICAL()

/* 在portmacro.h中定义 */
#define portENTER_CRITICAL()		vPortEnterCritical()


/*进入临界段,带中断保护版本,可以嵌套*/
/* 在portmacro.h中定义 */
#define 	taskENTER_CRITICAL_FROM_ISR()  portSET_INTERRUPT_MASK_FROM_ISR()

/* 在portmacro.h中定义 */
#define     portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI()

退出临界段的宏

/*退出临界段,不带中断保护版本,不能嵌套*/
/* 在task.h中定义 */
#define taskEXIT_CRITICAL()		portEXIT_CRITICAL()

/* 在portmacro.h中定义 */
#define portEXIT_CRITICAL()		vPortExitCritical()

/*退出临界段,带中断保护版本,可以嵌套*/
/* 在task.h中定义 */
#define 	taskEXIT_CRITICAL_FROM_ISR( x ) 	portCLEAR_INTERRUPT_MASK_FROM_ISR( x )

/* 在portmacro.h中定义 */
#define 	portCLEAR_INTERRUPT_MASK_FROM_ISR(x)	vPortSetBASEPRI(x)

进入临界段,不带中断保护, 不能嵌套

/*task.h中定义*/
#define taskENTER_CRITICAL()  portEXIT_CRITICAL()
/*在portmacro.h中定义*/
#define portENTER_CRITICAL()		vPortEnterCritical()
/*在port.c中定义*/
void vPortEnterCritical( void )
{
	portDISABLE_INTERRUPTS();
	uxCriticalNesting++;

	/* This is not the interrupt safe version of the enter critical function so
	assert() if it is being called from an interrupt context.  Only API
	functions that end in "FromISR" can be used in an interrupt.  Only assert if
	the critical nesting count is 1 to protect against recursive calls if the
	assert function also uses a critical section. */
	if( uxCriticalNesting == 1 )
	{
		configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
	}
}
/*在portmacro.h中定义*/
#define portDISABLE_INTERRUPTS()				vPortRaiseBASEPRI()
static portFORCE_INLINE void vPortRaiseBASEPRI( void )
{
uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;

	__asm
	{
		/* Set BASEPRI to the max syscall priority to effect a critical
		section. */
		msr basepri, ulNewBASEPRI
		dsb
		isb
	}
}

ps: uxCriticalNesting是prot.c中定义的静态变量,表示临界段嵌套计数器,默认初始化为0xaaaaaaaa,在调度器启动时会被重新初始化为0:vTaskScheduler() -> xPortStartScheduler() -> uxCriticalNesting = 0
如果uxCriticalNesting=1,即一层嵌套,要确保当前没有中断活跃,即内核外设SCB中的中断和控制寄存器SCB_ICSR的低8位要等于0.

进入临界段,带中断保护版本,可以嵌套

/*在task.c中定义*/
#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()

/*在portmacro.h中定义*/
#define portSET_INTERRUPT_MASK_ISR() u1PortRaiseBASEPRI()

static portFORCE_INLINE uint32_t ulPortRaiseBASEPRI( void )
{
	uint32_t ulReturn, ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;

	__asm
	{
		/* Set BASEPRI to the max syscall priority to effect a critical
		section. */
		mrs ulReturn, basepri
		msr basepri, ulNewBASEPRI
		dsb
		isb
	}

	return ulReturn;
}

退出临界段,不带中断保护版本,不能嵌套

/*在task.c中定义*/
#define taskEXIT_CRITCAL()                      portEXIT_CRITCAL()
/*在portmacro.h中定义*/
#define portEXIT_CRITICAL()						vPortExitCritical()
/*在port.c中定义*/
void vPortExitCritical( void )
{
	//configASSERT( uxCriticalNesting );
	uxCriticalNesting--;
    
	if( uxCriticalNesting == 0 )
	{
		portENABLE_INTERRUPTS();
	}
}
/*在portmacro.h中定义*/
#define portENABLE_INTERRUPTS()					vPortSetBASEPRI( 0 )
static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
	__asm
	{
		/* Barrier instructions are not used as this function is only used to
		lower the BASEPRI value. */
		msr basepri, ulBASEPRI
	}
}

退出临界段,带中断保护版本,可以嵌套

/*在task.c中定义*/
#define taskEXIT_CRITICAL_FROM_ISR(x)                   portCLEAR_INTERRUPT_MASK_FROM_IS(x)
/*在portmacro.h中定义*/
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x)	        vPortSetBASEPRI(x)


static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
	__asm
	{
		/* Barrier instructions are not used as this function is only used to
		lower the BASEPRI value. */
		msr basepri, ulBASEPRI
	}
}

临界段代码的应用

/*在中断场合,临界段可以嵌套*/
{
	unit32_t ulReturn;
	/*进入临界段,临界段可以嵌套*/
	ulReturn =taskENTER_CRITCAL_FROM_ISR();
	
	/*临界段代码*/
	
	/*退出临界段*/
	taskEXIT_CRITICAL_FROM_ISR(ulReturn );

}
/*在非中断场合,临界段不能嵌套*/
{

	/*进入临界段,临界段可以嵌套*/
	taskENTER_CRITICAL();
	
	/*临界段代码*/
	
	/*退出临界段*/
	taskEXIT_CRITICAL();

}

学习于《FreeRTOS内核实现与应用开发实战指南–基于stm32》
b站视频地址:https://www.bilibili.com/video/BV1Jx411X7NS/

<think>嗯,用户这次明确要查STM32F103C8T6在FreeRTOS中的接口实现文档。从引用内容看,用户可能正在做嵌入式开发,手头有micro-ROS和uC/OS-III的资料,但真正需要的是FreeRTOS的底层接口实现。用户提到的芯片STM32F103C8T6是经典款,FreeRTOS又有完善的STM32支持,所以重点应该是教用户如何定位官方文档里的核心内容。记得上次回答过FreeRTOS任务创建和中断配置,这次需要深化到外设接口层,特别是GPIO、UART这种最常用的部分。注意到引用[3]提到uC/OS-III的移植文件,这提示我应该强调FreeRTOS配置文件的关键作用。用户可能需要从CubeMX生成的FreeRTOSConfig.h出发,再结合官方接口手册。另外用户用“implementationdocumentation”这样的术语,大概率是要开发调试用,所以官网文档和标准库文件路径必须给具体。要特别注意避免混淆其他RTOS内容。引用[2]提到过STM32F7的资料,但F103资源较少,得强调内存优化和寄存器直接操作。最好拆解成配置层、驱动层、应用层三个部分说明,这样工程师容易对照代码查找。中断服务例程那里要加警告,因为FreeRTOS的API在ISR里有限制。</think> 根据STM32F103C8T6在FreeRTOS中的接口实现需求,核心资料和方法如下(关键内容按执行顺序排列): --- ### **一、官方文档定位** 1. **FreeRTOS官方接口手册** - 访问官网:[FreeRTOS Kernel API Reference](https://www.freertos.org/a00106.html) - 关键章节:任务管理(`xTaskCreate`)、队列(`xQueueCreate`)、信号量(`xSemaphoreCreateBinary`) *示例:任务创建接口原型* ```c BaseType_t xTaskCreate(TaskFunction_t pvTaskCode, const char *const pcName, configMINIMAL_STACK_SIZE, void *pvParameters, UBaseType_t uxPriority, TaskHandle_t *pxCreatedTask); ``` 2. **STM32标准外设库文档** - 文件:`stm32f10x_stdperiph_lib_um.chm`(库V3.5及以上) - 重点章节:GPIO(`GPIO_Init`)、USART(`USART_SendData`)、定时器(`TIM_Cmd`)与外设操作规范 --- ### **二、接口实现关键步骤** #### **1. FreeRTOS配置层** - **修改`FreeRTOSConfig.h`**(位于`Middlewares/Third_Party/FreeRTOS/Source/include`) ```c #define configUSE_PREEMPTION 1 // 启用抢占式调度 #define configTICK_RATE_HZ 1000 // 系统时钟1kHz #define configMINIMAL_STACK_SIZE 128 // 最小任务栈大小 ``` #### **2. 外设驱动层** - **GPIO接口封装示例**(结合FreeRTOS队列) ```c // 创建任务间通信队列 QueueHandle_t xLedQueue = xQueueCreate(5, sizeof(uint8_t)); // GPIO任务 void vLEDTask(void *pvParameters) { uint8_t led_cmd; while(1) { if(xQueueReceive(xLedQueue, &led_cmd, portMAX_DELAY)) { GPIO_WriteBit(GPIOC, GPIO_Pin_13, (led_cmd) ? Bit_SET : Bit_RESET); } } } ``` #### **3. 中断服务例程(ISR)** - **USART接收中断中发送信号量** ```c void USART1_IRQHandler(void) { if(USART_GetITStatus(USART1, USART_IT_RXNE)) { uint8_t data = USART_ReceiveData(USART1); xSemaphoreGiveFromISR(xUartSemaphore, NULL); // 发送二值信号量 } } ``` --- ### **三、验证与调试** 1. **内存占用监测** 使用`uxTaskGetStackHighWaterMark()`检查任务栈溢出 ```c UBaseType_t watermark = uxTaskGetStackHighWaterMark(NULL); ``` 2. **实时性分析** 启用`configGENERATE_RUN_TIME_STATS`统计CPU利用率 --- ### **文档资源推荐** 1. **源码级文档** - `FreeRTOS/Source`目录下的`.c`文件含详细接口注释(如`tasks.c`中`vTaskDelay`实现机制) 2. **应用笔记** - ST官方AN4539:*FreeRTOS on STM32 microcontrollers* - FreeRTOS官方《Mastering the FreeRTOS Real Time Kernel》电子书 > ⚠️ 注:使用`vPortEnterCritical()`保护共享外设时**必须保证临界区短于中断延迟上限**,否则引发任务抖动[^1:FreeRTOS时序约束]。 --- ### **实操参考项目** Github资源:[STM32F1-FreeRTOS-Base](https://github.com/STMicroelectronics/STM32CubeF1/tree/master/Projects/FreeRTOS_Examples) 包含UART/DMA/ADC等外设的FreeRTOS驱动模板(查看`/Project/FreeRTOS_*`目录)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Asita_c

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

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

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

打赏作者

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

抵扣说明:

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

余额充值