【RTOS】中断机制


前言

RTOS中,需要应对各类事件。这些事件很多时候是通过硬件中断产生。假设当前系统正在运行Task1任务,用户按下了按键,触发了按键中断。触发中断后:硬件负责: CPU跳到固定地址去执行代码,这个固定地址通常被称为中断向量。
软件负责:

  1. 保存现场:Task1被打断,需要先保存Task1的运行环境,比如各类寄存器的值;
  2. 调用处理函数(这个函数就被称为ISR,interrupt service
    routine)
  3. 恢复现场:继续运行Task1,或者运行其他优先级更高的任务。

本章主要涉及

  1. 两套API
  2. 两类中断
  3. 优先级

一、ISR的API函数

FreeRTOS中很多API函数都有两套:一套在任务中使用,另一套在ISR中使用。这两套区别主要在于可否阻塞是否挑出最高优先级任务运行

类型在任务中在ISR中
队列(queue)xQueueSendToBackxQueueSendToBackFromISR
xQueueSendToFrontxQueueSendToFrontFromISR
xQueueReceivexQueueReceiveFromISR
xQueueOverwritexQueueOverwriteFromISR
xQueuePeekxQueuePeekFromISR
信号量(semaphore)xSemaphoreGivexSemaphoreGiveFromISR
xSemaphoreTakexSemaphoreTakeFromISR
事件组(event group)xEventGroupSetBitsxEventGroupSetBitsFromISR

以写队列为例,来看一下这两个API有什么区别

xQueueSendToBackxQueueSendToBackFromISR
参数不同xTicksToWait: 队列满阻塞时间没有xTicksToWait
唤醒等待的任务写队列后,会唤醒等待数据的任务,如果唤醒任务优先级高会发起调度写队列后,会唤醒等待数据的任务但不调度,只会记录是否需要发起调度
阻塞如果队列满,可以阻塞 也可以即刻返回如果队列满,不可以阻塞,即刻返回

二、ISR的中断

根据中断优先级我们把中断分为两类,一类处理优先级较低允许调用 API,另一类优先级较高不能调用API,在ISR函数里面会先判断中断优先级是否有效ucCurrentPriority >= ucMaxSysCallPriority来确认是否可以在中断使用ISR API

        /* Obtain the number of the currently executing interrupt. */
        ulCurrentInterrupt = vPortGetIPSR();

        /* Is the interrupt number a user defined interrupt? */
        if( ulCurrentInterrupt >= portFIRST_USER_INTERRUPT_NUMBER )
        {
            /* Look up the interrupt's priority. */
            ucCurrentPriority = pcInterruptPriorityRegisters[ ulCurrentInterrupt ];

            /* The following assertion will fail if a service routine (ISR) for
             * an interrupt that has been assigned a priority above
             * configMAX_SYSCALL_INTERRUPT_PRIORITY calls an ISR safe FreeRTOS API
             * function.  ISR safe FreeRTOS API functions must *only* be called
             * from interrupts that have been assigned a priority at or below
             * configMAX_SYSCALL_INTERRUPT_PRIORITY.
             *
             * Numerically low interrupt priority numbers represent logically high
             * interrupt priorities, therefore the priority of the interrupt must
             * be set to a value equal to or numerically *higher* than
             * configMAX_SYSCALL_INTERRUPT_PRIORITY.
             *
             * Interrupts that	use the FreeRTOS API must not be left at their
             * default priority of	zero as that is the highest possible priority,
             * which is guaranteed to be above configMAX_SYSCALL_INTERRUPT_PRIORITY,
             * and	therefore also guaranteed to be invalid.
             *
             * FreeRTOS maintains separate thread and ISR API functions to ensure
             * interrupt entry is as fast and simple as possible.
             *
             * The following links provide detailed information:
             * https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html
             * https://www.FreeRTOS.org/FAQHelp.html */
            configASSERT( ucCurrentPriority >= ucMaxSysCallPriority );
        }

优先级如下图

在这里插入图片描述

三、优先级划分

SysTick触发任务切换:SysTick中断发生时,RTOS可能会发现当前任务需要被切换。此时,RTOS不会立即切换任务,而是设置PendSV异常挂起。
PendSV执行任务切换:在所有中断处理完后,PendSV异常被触发,RTOS通过它来执行具体的任务切换操作(保存当前任务的上下文、恢复新任务的上下文)。
但上面这两个的中断优先级都处于最低,会被任意中断打断

四、临界资源访问

要独占式地访问临界资源,有3种方法:

  1. 临界资源的互斥访问
  2. 屏蔽/使能中断
  3. 暂停/恢复调度器。

4.1、屏蔽中断

代码样例

int a;
/* 在任务中,当前时刻中断是使能的
 * 执行这句代码后,屏蔽中断
 */
void add_func(int val)
{
	taskENTER_CRITICAL();	

	/* 访问临界资源 */
    a += val;

	/* 重新使能中断 */
	taskEXIT_CRITICAL();
}

这套 taskENTER_CRITICA()/taskEXIT_CRITICAL() 宏,是可以递归使用的,它的内部会记录嵌套的深度,只有嵌套深度变为0时,调用 taskEXIT_CRITICAL() 才会重新使能中断

taskENTER_CRITICA()/taskEXIT_CRITICAL() 之间: 低于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断被屏蔽了。由于任务调度依赖于中断、依赖于API函数,所以:这两段代码之间,不会有任务调度产生

任务中使用: taskENTER_CRITICAL ()/taskEXIT_CRITICAL()
ISR中使用:taskENTER_CRITICAL_FROM_ISR()/taskEXIT_CRITICAL_FROM_ISR()

4.2、暂停调度器

如果有别的任务来跟你竞争临界资源,你可以把中断关掉,但是这代价太大了,它会影响中断的处理。如果只是禁止别的任务来跟你竞争,不需要关中断,暂停调度器就可以了,在这期间,中断还是可以发生、处理。

代码如下:

int a;
void xxx_func(int val)
{
	vTaskSuspendAll();

	/* 访问临界资源 */
    a = val;

	xTaskResumeAll();
}

vTaskSuspendAll()/xTaskResumeAll()也是可以递归使用的,它的内部会记录嵌套的深度,只有嵌套深度变为0时,xTaskResumeAll()才会重新使能调度器。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值