前言
本篇文章来分析FreeRTOS中的中断,中断在FreeRTOS中也是非常重要的,那么这篇文章将带大家来学习一下FreeRTOS中的中断处理以及涉及到的API等。
一、为什么要为中断设计一套API
1.实时性要求:
中断服务程序通常用于响应实时事件,例如硬件中断、定时器中断等。为了满足实时性要求,中断服务程序必须迅速执行,不能因等待某些资源而阻塞。中断处理必须在规定的时间内完成以确保系统的及时响应。
2.代码清晰度:
将任务和中断的操作分开可以提高代码的清晰度和可读性。任务上下文的操作通常涉及到任务切换、阻塞等,而中断上下文的操作需要考虑中断的实时性和不能阻塞的要求。通过使用两套函数,可以在代码中清晰地区分任务上下文和中断上下文的操作,使代码更易于理解。
3.可维护性:
分离任务和中断的操作使得系统更易于维护和修改。如果所有的操作都混合在一起,代码会变得复杂且难以理解。通过采用清晰的接口,可以更容易地理解和修改任务和中断的行为,降低引入错误的风险。
二、两套函数区别对比
这里使用xQueueSendToBack和xQueueSendToBackFromISR进行比较说明:
xQueueSendToBack:
调用上下文: 该函数用于在任务上下文中发送数据到队列。任务上下文是正常的程序执行上下文,不是中断上下文。
任务切换: 如果向队列发送数据导致接收方任务变得可运行(即有任务等待接收数据),则该函数可能会导致任务切换。这是因为向队列发送数据可能导致等待接收数据的任务变为就绪态,而当前任务可能因此让出 CPU。
xQueueSendToBackFromISR:
调用上下文: 该函数用于在中断服务程序(ISR)或软中断上下文中发送数据到队列。中断上下文是由硬件中断或软中断触发的执行上下文。
任务切换: 该函数不会导致任务切换。因为中断上下文具有实时性的要求,任务切换可能引入不确定性和延迟,所以在中断上下文中,通常不允许发生任务切换。
下面是两个函数的使用示例:
// 任务上下文中发送数据到队列 xQueueSendToBack(xQueue, &data, portMAX_DELAY); // 中断上下文中发送数据到队列 BaseType_t xHigherPriorityTaskWoken = pdFALSE; xQueueSendToBackFromISR(xQueue, &data, &xHigherPriorityTaskWoken);
if(xHigherPriorityTaskWoken == pdTRUE) { /*任务切换*/ portYIELD_FROM_ISR(); }
需要注意的是,在中断上下文中使用 xQueueSendToBackFromISR 时,通常会检查 xHigherPriorityTaskWoken 的值。如果它在调用之后被设置为 pdTRUE,则意味着有一个任务等待接收数据并且其优先级高于当前运行的任务,可能需要进行任务切换。在这种情况下,通常会调用适当的任务切换函数,如 portYIELD_FROM_ISR,以确保在适当的时机进行任务切换。
三、两类中断
前面我们在分析信号量,互斥量,队列等源码时都会涉及到需要关闭中断的操作,那么这里的关闭中断是直接关闭全部的中断吗?其实是不是的,这里关闭中断只是关闭了特定的中断,并没有将全部的中断关闭。
FreeRTOS会将中断分为两类,第一类更高优先级的中断中无法使用FreeRTOS中提供的函数,而更低优先级的一类可以使用到FreeRTOS中提供的函数。
实际关闭中断会调用到portDISABLE_INTERRUPTS函数,这个函数只是关闭更低优先级的中断,不会关闭其他高优先级的中断,SYStick中断也属于低优先级的中断。
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 ); } }
四、FreeRTOS中SYSTICK和PendSV中断的作用
在FreeRTOS中,SYSTICK和PendSV中断是两个关键的系统中断,它们在任务调度和协作方面发挥着重要的作用。
SYSTICK中断:
作用: SYSTICK中断是由系统定时器触发的中断。在FreeRTOS中,它用于实现任务调度的时基。任务调度器通过SYSTICK中断定期触发,根据任务的优先级和调度策略来决定是否切换当前运行的任务。
配置: SYSTICK中断的配置在FreeRTOS中是由configTICK_RATE_HZ参数控制的,该参数定义了系统定时器的时基。SYSTICK中断的频率通常设置为1kHz,但可以根据具体的应用需求进行调整。
示例: 当SYSTICK中断发生时,FreeRTOS的任务调度器会检查任务的状态,决定是否进行任务切换。这是实现抢占式多任务的基础。
PendSV中断:
作用: PendSV(Pending Supervisor Call)中断是一种特殊的中断,用于在任务切换时执行一些必要的操作。通常,当任务调度器决定切换到另一个任务时,它会生成一个PendSV中断请求,将切换的工作推迟到稍后在PendSV中断服务函数中执行。
配置: PendSV中断的优先级设置为最低,以确保它能够在其他中断完成后执行。PendSV中断不会被任务调度器直接触发,而是通过软件触发。
示例: 当SYSTICK中断触发任务切换时,实际的任务切换操作会被延迟到PendSV中断服务函数中执行。这样的设计有助于减小任务切换的上下文开销,并提高系统的响应速度。
总体而言,SYSTICK中断提供了任务调度的时基,而PendSV中断用于执行实际的任务切换操作。这两者共同协作,实现了FreeRTOS的多任务调度机制。
总结
本篇文章主要讲解了FreeRTOS中的中断管理,这一个部分还是比较重要的,那么希望大家可以深入的理解一下这部分的知识,有任何疑问都可以留言。