中断
当CPU在执行某一事件A时,发生另外一个更重要紧急的事件B请求CPU去处理(产生了中断),于是CPU暂时中断当前正在执行的事件A任务而对对事件B进行处理,CPU处理完事件B后再返回之前中断的位置继续执行原来的事件A,这一过程统称为中断。
单片机通常拥有丰富的片内外设和很多中断源,中断程序相比于在主程序中使用循环语句轮询系统状态(Polling)能有效提高CPU的利用效率,同时能够更加及时地响应外部事件。
注意
在FreeRTOS中,任务程序由内核调度器统一调度和执行,程序的运行情况在一定程度上是稳定可预测的;中断相对而言是由外部环境决定是不可预测的,会对系统引入不确定性,如果使用不当的话会导致系统的失效和不稳定。
对中断的使用要根据实际需求和使用场景来决定,通过合理的配置和与FreeRTOS的中断管理(Interrupt Management)配合可以达到系统的高效能和高稳定性。
FreeRTOS的中断管理
FreeRTOS对于中断没有特别的处理程序,但提供一些特性和系统API函数方便用户可以简单地实现和维护中断管理。
在FreeRTOS中中断的优先级和任务的优先级是有区别的。可以把任务类比是人间模式,而中断是上帝模式,拥有更高的权限。
- 任务的优先级是由用户设置内核管理器管理的软件特性(software feature),与操作系统所在的硬件平台无关
- ISR 虽然也是使用软件实现的,但是它被认为是硬件特性的一部分,因为它跟硬件密切相关
- 中断的优先级是由硬件平台相关的硬件特性(hardware feature),在中断代码运行的时候任务的代码将无法运行。即使是拥有最小优先级的中断也会打断拥有最高优先级的任务
FreeRTOS对于一些系统API函数提供两种版本,一种是供任务调用的,一种是供中断调用的(Interrupt Safe API)。由中断调用的API函数后缀上会有“FromISR”。
在任务中系统API函数被调用后,如果有更高优先级的任务处于就绪状态的话,内核调度器会自动执行有更高优先级的任务。(需要在FreeRTOSConfig.h头文件中设置configUSE_PREEMPTION为1)。在中断处理代码中系统API函数被调用后,内核调度器不会自动切换到有更高优先级的任务,会把系统API函数中指针参数pxHigherPriorityTaskWoken设置为pdTRUE,然后由用户根据pxHigherPriorityTaskWoken的值自行决定是否切换任务。在中断处理代码中,切换任务的系统API函数如下
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
如果xHigherPriorityTaskWoken的值是pdFALSE的话,任务切换不会发生;如果xHigherPriorityTaskWoken的值是pdTRUE的话,任务切换会发生。
延迟中断处理
中断处理程序中代码可以尽量简短,并把处理中断的代码放在一个中断任务里。这种方法称为延迟中断处理(deferred interrupt processing)。
通过延迟中断处理用户可以把中断处理当成由调度器调度的一个任务并分配优先级,此时中断的优先级和任务的优先级是一致的。
用户可以把中断任务的优先级设置为最高级,当中断发生在中断处理程序调用portYIELD_FROM_ISR()函数后中断任务将会优先执行。当然如果程序简单的话用户可以直接在中断处理程序中处理中断。
两套API函数列表
FreeRTOS使用两套API 可以让程序更高效。
类型 |
在任务中 |
在 ISR 中 |
队列(queue) |
xQueueSendToBack |
xQueueSendToBackFromISR |
|
xQueueSendToFront |
xQueueSendToFrontFromISR |
|
xQueueReceive |
xQueueReceiveFromISR |
|
xQueueOverwrite |
xQueueOverwriteFromISR |
|
xQueuePeek |
xQueuePeekFromISR |
信号量(semaphore) |
xSemaphoreGive |
xSemaphoreGiveFromISR |
|
xSemaphoreTake |
xSemaphoreTakeFromISR |
事件组(event group) |
xEventGroupSetBits |
xEventGroupSetBitsFromISR |
|
xEventGroupGetBits |
xEventGroupGetBitsFromISR |
任务通知(task notification) |
xTaskNotifyGive |
vTaskNotifyGiveFromISR |
|
xTaskNotify |
xTaskNotifyFromISR |
软件定时器(software timer) |
xTimerStart |
xTimerStartFromISR |
|
xTimerStop |
xTimerStopFromISR |
|
xTimerReset |
xTimerResetFromISR |
|
xTimerChangePeriod |
xTimerChangePeriodFromISR |
怎么切换任务
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
xHigherPriorityTaskWoken 的含义是:是否有更高优先级的任务被唤醒了。如果为pdTRUE,则意味着后面要进行任务切换。
资源管理
在任务中屏蔽中断
* 在任务中,当前时刻中断是使能的
* 执行这句代码后,屏蔽中断
*/
taskENTER_CRITICAL();
/* 访问临界资源 */
/* 重新使能中断 */
taskEXIT_CRITICAL();
这套 taskENTER_CRITICA()/taskEXIT_CRITICAL()宏,是可以递归使用的,它的内部会记录嵌套的深度,只有嵌套深度变为 0 时,调用 taskEXIT_CRITICAL()才会重新使能中断。
**在 ISR 中屏蔽中断要使用含有"FROM_ISR"后缀的宏 **
暂停调度器
如果需要禁止别的任务来竞争临界资源,不需要关中断,暂停调度器就行了;在这期间,中断正常运行。
/* 暂停调度器 */
void vTaskSuspendAll( void );
/* 恢复调度器
* 返回值: pdTRUE 表示在暂定期间有更高优先级的任务就绪了
* 可以不理会这个返回值
*/
BaseType_t xTaskResumeAll( void );
这套 vTaskSuspendScheduler()/xTaskResumeScheduler()宏,是可以递归使用的,它的内部会记录嵌套的深度,只有嵌套深度变为 0 时,调用 taskEXIT_CRITICAL()才会重新使能中断。