FreeRTOS 教程指南 学习笔记 第六章 中断管理(一)

本文介绍了FreeRTOS中断管理,强调了在ISR中使用API的安全性,讨论了延迟中断处理的策略,展示了如何使用二进制和计数信号量在任务与中断间同步。主要内容包括中断安全API的使用、xHigherPriorityTaskWoken参数、延迟中断处理、二进制信号量同步示例以及计数信号量在事件计数和资源管理中的应用。
摘要由CSDN通过智能技术生成

FreeRTOS 教程指南 学习笔记 第五章 软件计时器

一、简介

事件

嵌入式实时系统必须响应来自环境的事件。例如,到达以太网外围设备(事件)上的数据包可能需要传递给TCP/IP栈以进行处理(操作)。非平凡的系统将不得不服务于来自多个来源的事件,所有这些事件都将有不同的处理开销和响应时间要求。在每种情况下,都必须对最佳的事件处理实施策略做出判断:

  1. 应该如何检测到该事件?通常使用中断,但输入也可以进行轮询。
  2. 当使用中断时,应在中断服务例程(ISR)内部执行多少处理,以及在外部执行多少处理?通常希望保持每个ISR尽可能短。
  3. 如何将事件通信到主(非ISR)代码,以及如何构建这些代码以最好地适应潜在异步发生的处理?

FreeRTOS并不对应用程序设计器强加任何特定的事件处理策略,但确实提供了允许以一种简单和可维护的方式实现所选策略的特性。
区分任务的优先级和中断的优先级是很重要的:

  • 任务是一个与运行FreeRTOS的硬件无关的软件特性。任务的优先级由软件作者在软件中分配,并由软件算法(调度器)决定哪个任务将处于运行状态。
  • 虽然是用软件编写的,但中断服务程序是一种硬件特性,因为硬件控制哪些中断服务程序将运行,以及何时运行。任务只有在没有运行ISR时才会运行,因此,优先级最低的中断将中断优先级最高的任务,并且任务无法抢占ISR。

所有将运行FreeRTOS的硬件体系结构都能够处理中断,但是与中断输入和中断优先级分配相关的细节在不同的硬件体系结构中是不同的。
本章旨在让读者很好地理解:

  • 可以在中断服务程序中使用FreeRTOS API函数。
  • 延迟中断处理的方法。
  • 如何创建和使用二进制信号量和计数信号量。
  • 二进制和计数信号量之间的区别。
  • 如何使用队列将数据进出中断服务例程。
  • 有一些FreeRTOS分支提供的中断嵌套模型。

二、使用一个ISR中的FreeRTOS API

中断安全API

通常有必要使在ISR中调用FreeRTOS API函数提供的功能,但是许多FreeRTOS API函数在ISR内部不可用;如果从ISR调用API函数,那么它就不会从任务调用,因此没有可以进入阻塞状态的调用任务。 FreeRTOS通过提供一些API函数的两个版本来解决这个问题;一个版本用于任务使用,另一个版本用于ISR使用。用于ISR的函数在其名称上附加了“FromISR”。
注意:不要从ISR中调用没有“FromISR”的FreeRTOS API函数

使用独立的中断安全API的好处

有一个单独的API,可以使任务代码更高效,ISR代码更高效,中断输入更简单。如果可以从任务和ISR中同时调用相同版本的API函数,那么:

  • API函数需要额外的逻辑来确定它们是从任务还是ISR调用的。额外的逻辑将通过函数引入新的路径,使函数更长、更复杂、更难测试。
  • 当从任务中调用该函数时,一些API函数参数会过时,而当从ISR中调用该函数时,其他参数会过时。
  • 每个FreeRTOS分支都需要提供一种机制来确定执行上下文(任务或ISR)。
  • 如果架构不容易确定执行上下文(任务或ISR),则需要额外的、浪费的、更复杂的使用,以及允许软件提供执行上下文的非标准中断输入代码。
使用独立的中断安全API的缺点

有两个版本的某些API函数可以使任务和ISR更有效,但引入了一个新问题;有时需要调用一个非FreeRTOS的API函数,但该函数同时使用了任务和ISR的FreeRTOS API。
这通常只是集成第三方代码时的一个问题,因为这是软件设计脱离应用程序编写者控制的唯一时间。如果这确实成为一个问题,那么可以使用以下技术之一来克服这个问题:

  1. 将中断处理延迟到任务,因此API函数只能从任务的上下文中调用。
  2. 如果你使用一个支持中断嵌套的FreeRTOS分支,然后任务中可以使用“FromISR”的版本执行(相反ISR中不能执行不带“FromISR”版本)。
  3. 第三方代码通常包括一个RTOS抽象层,可以实现来检测调用函数的上下文(是任务还中断),然后调用适合该上下文的API函数。
xHigherPriorityTaskWoken参数

本节将介绍xHigherPriorityTaskWoken参数的概念。如果您还没有完全理解本节,请不要担心,因为下面的部分将提供实际的例子。
如果上下文切换是由中断执行的,那么当中断退出时运行的任务可能与输入中断时运行的任务不同——中断将中断一个任务,但返回到另一个任务。
一些FreeRTOS API函数可以将任务从阻塞状态移动到就绪状态。这已经在xQueueSendToBack()等函数中看到了,如果有一个任务在等待数据在队列中可用,它将解锁任务。
如果被FreeRTOS API函数解除阻塞的任务的优先级高于正在运行状态下的任务的优先级,那么根据FreeRTOS调度策略,应该会切换到更高优先级的任务。当切换到更高优先级的任务时,具体怎样执行取决于调用API函数的上下文:

  • 如果API函数是从任务中调用的
    如果configUSE_PREEMPTION在FreeRTOSConfig.h中设置为1,那么在API函数中自动切换到高优先级任务——所以在API函数退出之前。这在图43中已经看到了,其中写入到计时器命令队列会导致在写入到命令队列的函数退出之前切换到RTOS守护进程任务。
  • 如果从中断中调用了API函数
    那么切换到更高优先级的任务将不会在中断中自动发生。相反,将设置一个变量来通知应用程序作者应该执行上下文切换。中断安全的API函数(那些以“FromISR”结尾的函数)有一个名为xHigherPriorityTaskWoken参数,用于此目的。
    如果应该执行上下文切换,那么中断安全API函数将xHigherPriorityTaskWoken设置为pdTRUE。为了能够检测到这种情况,在第一次使用之前,必须将其指向的变量初始化为pdFALSE。
    如果应用程序作者选择不从ISR请求上下文切换,那么高优先级任务将保持在“就绪”状态,直到调度程序下次运行时运行——在最坏的情况下将在下一次滴答中断期间。
    FreeRTOS API函数只能将
    xHigherPriorityTaskWoken设置为pdTRUE。如果一个ISR调用多个FreeRTOS API函数,那么该变量可以在每个API函数调用中作为相同的参数传递,并且该变量在第一次使用之前只需要初始化为pdFALSE。

在API函数的中断安全版本中,上下文切换不会自动发生有几个原因:

  1. 避免不必要的上下文切换
    在任务需要执行任何处理之前,中断可以执行不止一次。例如,考虑一个场景,即任务处理中断驱动的UART接收的字符串;对于UART ISR来说,每次接收到字符时都切换到任务是很浪费的,因为任务只有在收到完整的字符串后才能执行处理。
  2. 对执行序列的控制中断可能会偶尔或在不可预测的时间发生。
    专家FreeRTOS用户可能希望在其应用程序中的特定点上暂时避免不可预测地切换到不同的任务——尽管这也可以使用FreeRTOS调度程序锁定机制来实现。
  3. 可移植性
    它是可以在所有FreeRTOS端口上使用的最简单的机制。
  4. 效率
    针对较小处理器架构的分支只允许在ISR的最末端请求上下文切换,而删除该限制将需要额外的和更复杂的代码。它还允许在同一ISR中对一个FreeRTOS API函数进行多个调用,而无需在同一个ISR中为一个上下文切换生成多个请求
  5. 在RTOS tick interrupt中执行
    将在本书后面看到,可以在RTOS tick interrupt中断添加应用程序代码。在tick interrupt中尝试上下文切换的结果取决于正在使用的FreeRTOS分支。最好的情况是,它只会导致对调度程序进行不必要的调用。

使用xHigherPriorityTaskWoken参数是可选的。如果不需要,则将任务设置为NULL。

portYIELD_FROM_ISR()和portEND_SWITCHING_ISR()宏

本节将介绍用于从ISR请求上下文切换的宏。如果您还没有完全理解本节,请不要担心,因为下面的部分将提供实际的例子。
taskYIELD()是一个宏,可以在任务中调用它来请求上下文切换。portYIELD_FROM_ISR()和portEND_SWITCHING_ISR()都是taskYIELD()的中断安全版本。portYIELD_FROM_ISR()和portEND_SWITCHING_ISR()的使用方式相同,并且做同样的事情。一些FreeRTOS分支只提供这两个宏中的一个。较新的FreeRTOS分支提供了这两个宏。本书中的例子使用了portYIELD_FROM_ISR()。

portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );

portYIELD_FROM_ISR( xHigherPriorityTaskWoken );

从中断安全API函数中传递的xHigherPriorityTaskWoken 参数可以直接用作调用portYIELD_FROM_ISR()时的参数。
如果portYIELD_FROM_ISR()的xHigherPriorityTaskWoken 参数为pdFALSE(零),则不请求上下文切换,宏也没有作用。如果portYIELD_FROM_ISR()的xHigherPriorityTaskWoken 参数不是pdFALSE,则会请求一个上下文切换,并且处于正在运行状态的任务可能会发生更改。
中断将始终返回到处于运行状态下的任务,即使在中断执行时,任务在运行状态下发生了变化。
大多数FreeRTOS分支都允许在ISR中的任何地方调用portYIELD_FROM_ISR()。一些FreeRTOS分支(主要是针对较小架构的分支),只允许在ISR的最末端调用portYIELD_FROM_ISR()。

三、延迟中断处理

通常认为保持isr尽可能短是最佳做法。其原因包括:

  • 即使任务被分配了非常高的优先级,它们也只有在硬件没有服务于任何中断时才会运行。
  • ISRs可以扰乱任务的开始时间和执行时间(添加“抖动”)。
  • 根据运行FreeRTOS的体系结构,在执行ISR时可能无法接受任何新的中断,或者至少是新中断的子集。
  • 应用程序作者需要考虑任务和ISR同时访问的变量、外设和内存缓冲区等资源的后果,并防止它发生。
  • 一些FreeRTOS分支允许中断嵌套,但是中断嵌套会增加复杂性并降低可预测性。中断的时间越短,它嵌套的可能性就越小。

中断服务例程必须记录该中断的原因,并清除该中断。中断所需要的任何其他处理通常都可以在任务中执行,从而允许中断服务例程尽可能快地退出。这被称为“延迟中断处理”,因为中断所需要的处理是从ISR“延迟”到一个任务的。
将中断处理延迟到任务,还允许应用程序作者相对于应用程序中的其他任务确定处理的优先级,并使用所有FreeRTOS API函数。
如果延迟中断处理的任务的优先级高于任何其他任务的优先级,那么处理将立即执行,就像处理是在ISR本身中执行的一样。这个场景如图48所示,其中Task 1是一个正常的应用程序任务,而Task 2是延迟中断处理的任务。
在这里插入图片描述
在图48中,中断处理从时间t2开始,有效结束时间在t4,但只有在时间t2和t3之间的周期在ISR中花费。如果没有使用延迟中断处理,那么时间t2和t4之间的整个时间段将会在ISR中花费。
对于何种情况下在ISR中的进行所有处理最佳,以及何种情况下将部分处理推迟到任务最佳,并没有绝对的规则。在以下情况下,延迟处理到任务最有用:

  • 中断所需要的处理并不简单。例如,如果中断只是存储模拟数字转换的结果,那么几乎可以肯定这最好在ISR中执行,但如果转换的结果也必须通过软件滤波器,那么最好延迟执行滤波任务。
  • 中断处理任务可以方便地执行不能在ISR中执行的操作,例如写入控制台&#x
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值