FreeRTOS 中断

简介

FreeRTOS是一个C库集合,包含了实时内核和模块化库实现互补功能。
免费的实时操作系统,可裁减移植。

文件结构

demo、license和source。source为主体。
source有三部分
源码.c 、include文件夹里头文件.h和portable文件夹。其中portable文件将FreeRTOS系统软件与MCU芯片建立连接。

移植

芯片中断

中断由硬件产生,当中断产生以后 CPU 就会中断当前的流程转而去处理中断服务。Cortex-M 内核的 MCU 提供了一个用于中断管理的嵌套向量中断控制器(NVIC)。
Cotex-M3 的 NVIC 最多支持 240 个 IRQ(中断请求)、 1 个不可屏蔽中断(NMI)、 1 个 Systick(滴答定时器)定时器中断和多个系统异常。
Cortex-M 处理器有多个用于**管理中断和异常的可编程寄存器,这些寄存器大多数都在NVIC 和系统控制块(SCB)**中,CMSIS文件 将这些寄存器定义为结构体。NVIC_Type结构体

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;

NVIC 和 SCB 都位于系统控制空间(SCS)内,SCS 的地址从 0XE000E000 开始,SCB 和 NVIC的地址也在 core_cm3.h 中有定义.

中断优先

当多个中断来临时,高优先级的中断(优先级编号小) 肯定是首先得到响应,而且高优先级的中断可以抢占低优先级的中断–中断嵌套。Cortex-M 处理器的有些中断是具有固定最高的优先级的,比如复位、NMI、HardFault。该优先级编号为负数。
Cortex-M 处理器有三个固定优先级和 256 个可编程的优先级,ST芯片厂商精简设计,将STM32设计只使用4个bit位来代表优先级,因此只有16个优先级。有 5 组优先级分组设置。
每个外部中断都有一个对应的优先级寄存器,每个寄存器占 8 位,因此最大宽度是 8 位,但是最小为 3 位。4 个相临的优先级寄存器拼成一个 32 位寄存器。0xE000_ED20~0xE000_ED23 这四个寄存器就可以拼接成一个地址为 0xE000_ED20 的 32 位寄存器。这一点很重要!因为 FreeRTOS 在设置 PendSV 和 SysTick 的中断优先级的时候都是直接操作的地址 0xE000_ED20。

芯片中断屏蔽

PRIMASK寄存器

使用PRIMASK寄存器屏蔽所有的中断,执行时序严格的任务。PRIMASK寄存器可禁止NMI和HardFalut外所有中断和异常,汇编编程可以使用CPS(修改处理器状态)指令修改PRIMASK寄存器的值:

CPSIE	1//清除PRIMASK(使能中断)
CPSID	1//设置PRIMASK	(禁止中断)
//还可以通过MRS和MSR指令。
MOVS R0, #1
MSR PRIMASK,R0 ;//将 1 写入 PRIMASK 禁止所有中断
MOVS R0, #0
MSR PRIMASK,R0 ;//将 0 写入 PRIMASK 以使能中断
FAULTMASK

FAULTMASK寄存器可屏蔽除NMI(不可屏蔽中断)外的所有中断,其中屏蔽包括了HardFalut硬件中断。

CPSIE F ;//清除 FAULTMASK
CPSID F ;//设置 FAULTMASK
BASEPRI 寄存器

FreeRTOS 的开关中断就是操作 BASEPRI 寄存器来实现的!它可以屏蔽比抢占优先级阈值高的中断,低于抢占优先级阈值的中断就不受影响!
e.g. configMAX_SYSCALL_INTERRUPT_PRIORITY为5,抢占优先级域值为5.FreeRTOS抢占优先级为4的中断不受影响,抢占优先级为5的中断将被屏蔽。
在有些场合需要对中断屏蔽进行更细腻的控制,比如屏蔽抢占优先级低于某一个阈值的中断.
BASEPRI 寄存器中,向 BASEPRI 写 0 的话就会停止屏蔽中断。我们要屏蔽优先级于 0X60 的中断,则可以使用如下汇编编程

//屏蔽低于0x60优先级的中断
MOV R0,#0X60
MSR BASEPRI, R0
//停止中断
MOV R0,#0
MSR BASEPRI, R0

FreeRTOS中断配置宏

中断宏描述
configPRIO_BITS设置 MCU 使用几位抢占优先级,STM32 使用的是 4 位抢占优先级。
configMAX_PRIORITIES设置最大抢占优先级数。STM32和TDA4VL的R5F都为16个抢占优先级。
configLIBRARY_LOWEST_INTERRUPT_PRIORITY设置最低优先级,STM32 优先级使用了 4 位,优先级数就是 16 个,最低优先级那就是 15
configKERNEL_INTERRUPT_PRIORITYSTM32设置内核中断优先级0XF0,TDA4VL内核中断优先级为0xE0。
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY设置 FreeRTOS 系统可管理的最大优先级,BASEPRI 寄存器的阈值优先级,设置为5。高于 5 的优先级(抢占优先级数小于 5)不归 FreeRTOS 管理
configMAX_SYSCALL_INTERRUPT_PRIORITY上个宏左移4位。低于该中断可以调用FreeRTOS的API,高于则禁止。TDA4VL为0xE0.

e.g.
宏configKERNEL_INTERRUPT_PRIORITY为内核中断优先级。

#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )

由于STM32的PRIGROUP寄存器的优先级分组高4位有效。用来设置 PendSV 和滴答定时器的中断优先级,FreeRTOS中 PendSV 和 SysTick 的中断优先级都是最低的!

FreeRTOS开关中断

FreeRTOS 开关中断函数为 portENABLE_INTERRUPTS ()和 portDISABLE_INTERRUPTS(),本质是操作BASEPRI寄存器进行开关中断。

临界段代码

临界段代码也叫做临界区,是指那些必须完整运行,不能被打断的代码段。比如硬件初始化时,必须严格遵守时序,不能被中断打断。FreeRTOS进入临界段代码都需禁止中断,当临界段代码处理完后,再使能中断。
FreeRTOS与临界代码有两种4个,任务级临界段代码保护和中断级临界段代码保护。

任务级临界代码保护

taskENTER_CRITICAL()和 taskEXIT_CRITICAL()是任务级的临界代码保护,一个是进入临界段,一个是退出临界段,这两个函数是成对使用的,这函数的定义如下:

#define taskENTER_CRITICAL()	portENTER_CRITICAL()
#define taskEXIT_CRITICAL()		portEXIT_CRITICAL()
void vPortEnterCritical( void )
{
	portDISABLE_INTERRUPTS();
	uxCriticalNesting++;
	if( uxCriticalNesting == 1 )
	{
		configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
	}
}
void vPortExitCritical( void )
{
	configASSERT( uxCriticalNesting );
	uxCriticalNesting--;
	if( uxCriticalNesting == 0 )
	{
		portENABLE_INTERRUPTS();
	}
}

看出在进入函数 vPortEnterCritical()以后会首先关闭中断,然后给变量 uxCriticalNesting加一,uxCriticalNesting 是个全局变量,用来记录临界段嵌套次数的。函数 vPortExitCritical()是退出临界段调用的,函数每次将 uxCriticalNesting 减一,只有当 uxCriticalNesting 为 0 的时候才会调用函数 portENABLE_INTERRUPTS()使能中断。
任务级临界代码保护使用方法如下:

void taskcritical_test(void)
{
while(1)
{
	taskENTER_CRITICAL();			//进入临界区
	total_num+=0.01f;
	printf("total_num 的值为: %.4f\r\n",total_num);
	taskEXIT_CRITICAL();			//退出临界区
	vTaskDelay(1000);
}
}

中断级临界段代码保护

函数 taskENTER_CRITICAL_FROM_ISR()和 taskEXIT_CRITICAL_FROM_ISR()中断级别临界段代码保护,是用在中断服务程序中的,而且这个中断的优先级一定要低于configMAX_SYSCALL_INTERRUPT_PRIORITY!

#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR()
#define taskEXIT_CRITICAL_FROM_ISR(x) portCLEAR_INTERRUPT_MASK_FROM_ISR (x)
static portFORCE_INLINE uint32_t ulPortRaiseBASEPRI( void )
{
	uint32_t ulReturn, ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
	__asm
	{
		mrs ulReturn, basepri		//先读出 BASEPRI 的值,保存在 ulReturn 中
		msr basepri, ulNewBASEPRI		//将 configMAX_SYSCALL_INTERRUPT_PRIORITY 写入到寄存器 BASEPRI 中
		dsb
		isb
	}
return ulReturn;		//返回 ulReturn,退出临界区代码保护的时候要使用到此值!
}

中断级临界代码保护使用方法如下:

//定时器 3 中断服务函数
void TIM3_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) //溢出中断
	{
		status_value=taskENTER_CRITICAL_FROM_ISR();	//进入临界区
		total_num+=1;
		printf("float_num 的值为: %d\r\n",total_num);
		taskEXIT_CRITICAL_FROM_ISR(status_value);		//退出临界区
		TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除中断标志位
	}		
}

链接

官方线上资料:RTOS
线下pdf:pdf
源码链接:源码
官方中断介绍:中断

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值