本文介绍 GPIO 中断,包括中断示例及其各种功能。这是上一篇文章的延续,该文章解释了微控制器的并发和中断的概念。
GPIO 外设有什么作用?
GPIO 外设能够检测(或“知道”)四件事:引脚上的值是 1 还是 0,以及该值是从 0 变为 1 还是从 1 变为 0。
这些对于检测许多事件很有用。例如,如果我将簧片开关连接到门上,那么我在微控制器上的程序可以根据簧片开关所连接的引脚上的值变化来判断门是刚刚打开还是刚刚关闭,如图 1 所示。
图 1.使用 GPIO 和簧片开关检测门的状态(打开或关闭)和状态变化。
我将首先解释假设一切都已正确配置,中断是如何工作的,然后我们将查看需要正确配置以使中断工作的各个部分。
外设中断标志
让我们假设与我们之前的示例类似,我们试图检测的事件是引脚值从 1 变为 0 时。在 GPIO 外设内部,将有一个硬件检测到这种变化并指示这种变化已经发生将该引脚的所谓中断标志设置为 1。
如图 2 所示。
图 2.检测到应该产生中断的事件后设置 GPIO 引脚中断标志。
中断控制器和中断控制器标志
微控制器中有许多外围设备,每个外围设备都有自己的一组中断。大多数微控制器都有一个硬件,通常称为中断控制器,它管理来自外设的所有中断,决定运行哪个中断,并中断 CPU 以使其执行正确的 ISR。
通常,中断控制器有一个可能的中断列表以及它们相应的优先级。我们的 GPIO 外设在中断控制器保留的列表中可能有一个或多个中断。
例如,对于 CC2544,有一组 8 个引脚是 GPIO 的一部分,称为 PORT0。每个引脚标记为 P0_0、P0_1,依此类推,直到 P0_7。虽然每个引脚都有自己的中断标志,但中断控制器对整个端口只有一个中断,P0INT。每当设置GPIO 外设中的任何引脚标志时,也会设置整个端口的中断控制器中的标志。
注意这里有两个标志,一个是特定引脚的标志,它是 GPIO 外设的一部分,另一个是整个端口的中断标志,它是中断控制器的一部分。如图 3 所示。
图 3.检测到应该产生中断的事件后,在中断控制器中设置 GPIO 引脚中断标志和中断标志。
向量表
许多微控制器使用所谓的中断向量方法。在这种方法中,内存中有一个向量表,它为每个中断列出了 CPU 必须为该特定中断执行的 ISR 所在的地址。该地址通常称为中断向量。
例如,对于使用 8501 微控制器架构的 CC2544,PORT0 的中断向量是内存地址 0x6B。当中断控制器告诉 CPU 有来自特定向量的中断时,CPU 会做一些记录,然后开始从该中断向量执行 ISR。这如图 4 所示。
图 4.在检测到应该产生中断的事件后,在设置 GPIO 引脚中断标志和中断控制器中的 GPIO 中断标志后执行 GPIO 中断向量的 ISR。
配置中断行为
像 GPIO 这样的外设通常让您可以选择配置哪些类型的事件会导致外设产生中断。对于 GPIO,典型的选项是当值从 0 变为 1 时,当值从 1 变为 0 时,值的任何变化(即,0 到 1 或 1 到 0 但无所谓),或者当值保持为 1 或 0。
根据微控制器的不同,这可以针对每个引脚或端口上的所有引脚完成。ATmega328P 有两个引脚,您可以在其中单独更改。默认情况下,其他 GPIO 引脚会检测引脚上的任何变化(0 到 1 或 1 到 0)。回想一下,在上一篇文章中,我们说明了中断是如何工作的,我们假设引脚配置为仅检测从 1 到 0 的变化。
此外,一些微控制器要求将感兴趣的引脚配置为输入,以便在事件发生时设置中断标志(例如,CC2544)。其他人(例如,ATmega328P)将设置标志,无论引脚配置为输出还是输入。
中断屏蔽
用于描述启用和禁用中断的常用术语是“屏蔽”。通常,可以在多个级别禁用中断。CPU 可以启用或禁用所有中断,但通常有一些至关重要的中断称为不可屏蔽中断,它们永远不会被禁用。
禁用 CPU 中的所有中断实际上会停止中断控制器和 CPU 之间的通信。这意味着 GPIO 外设中的 pin 标志及其在控制器中的相应中断标志将被设置;但是,CPU 不会得到中断请求。这如图 5 所示。
图 5.在 CPU 级别全局禁用中断。
可以屏蔽中断的另一个级别是中断控制器级别。在这里,我们可以启用或禁用控制器内部的特定中断。
中断示例:CC2544
一个具体的例子会有所帮助。假设我们正在使用 CC2544,并且禁用了 PORT0。假设引脚 P0_3 更改了其值,以便在 GPIO 外设中设置其标志。PORT0 中断标志也会在中断控制器中设置,但中断控制器会忽略该标志。
这与 CPU 禁用所有中断不同,因为中断控制器仍在与 CPU 通信。因此,例如,如果 PORT1 已启用其中断,并且引脚 P1_2 更改了其值,因此其标志在 GPIO 外设中设置,并且 PORT1 中断标志也已设置,则中断控制器将中断 CPU 以处理该中断。
中断控制器忽略中断向量标志的情况如图 6 所示。
图 6.中断控制器级别的中断屏蔽。
大多数微控制器还允许在外设级别屏蔽中断。在这里,我们可以启用或禁用 GPIO 外设中特定引脚的中断。
在我遇到的所有微控制器中,当我们要查找的事件发生在 GPIO 外设中时,总是设置中断标志,无论该引脚的中断是启用还是禁用。例如,如果引脚 P0_3 以我们正在寻找的方式更改其值,则其标志将在 GPIO 外设中设置。但是,GPIO 外设不会向中断控制器发出警报,因此不会设置中断控制器中的 PORT0 中断标志,并且由于我们需要设置该标志才能中断 CPU,因此不会发生中断。这如图 7 所示。
图 7. GPIO 外设级别的中断屏蔽
当一个中断被屏蔽时,它仍然会被检测到。CPU只是不响应它。如果中断标志没有被清除并且中断被完全取消屏蔽,那么如果它满足所有其他条件(除了屏蔽),CPU 就会响应它以执行它。已检测到并等待 CPU 执行其 ISR 的中断通常称为待处理中断。
图 8 说明了中断处于未决状态,随后又被取消屏蔽的情况。
图 8. GPIO 级别的中断屏蔽和取消屏蔽,假设中断在中断控制器和 CPU 级别取消屏蔽。
回顾一下,完全取消屏蔽中断,以便在满足中断的所有其他条件时 CPU 可以响应它:
必须在外设中启用中断(如果适用)。
它在中断控制器中的相应中断也必须使能。
所有中断必须由 CPU 启用(即 CPU 和中断控制器之间的通信必须启用可屏蔽中断)。
中断优先级
有时会同时发生两个或多个导致中断的事件。发生这种情况时,中断控制器需要一种机制来知道应该先执行哪个中断等等,因为 CPU 一次只能处理一个中断。中断控制器通常提供一种称为优先级的配置,允许用户通过他们的代码来指定哪些中断是较高优先级,哪些是较低优先级。大多数还为每个中断提供默认设置。
每当多个事件同时发生并导致多个中断在中断控制器处挂起时,中断控制器就会选择最高优先级的中断供 CPU 处理。中断可以中断(或抢占)已经运行的中断,因此如果 CPU 正在处理较低优先级的中断,并且发生与较高优先级中断相关的事件,控制器将中断 CPU 以处理较高优先级的中断,并且 CPU 将恢复处理完成后被抢占的低优先级中断。图 9 说明了中断的优先级和抢占是如何工作的。
图 9.假设只有两个中断的优先级中断处理。
高优先级和低优先级中断标志同时设置。CPU 在低优先级中断之前执行高优先级中断(因为当高优先级中断完成时,它的标志仍处于挂起状态)
在 CPU 开始处理低优先级中断后,高优先级中断标志被设置。高优先级中断抢占低优先级中断,CPU 执行高优先级中断的 ISR 直到完成,然后恢复执行低优先级中断的 ISR。请注意,CPU 会继续执行低优先级中断的 ISR,即使在执行高优先级中断的 ISR 时它的标志仍未设置。这是因为在执行 ISR 之后,除非代码干扰正常的中断过程,否则 CPU 总是会恢复到它开始执行 ISR 之前的任何状态。它返回的这个状态可能是另一个 ISR。
较低优先级的中断标志设置在较高优先级的中断标志之后。由于较低优先级的中断不能抢占较高优先级的中断,CPU 在响应较低优先级的中断之前执行较高优先级的中断 ISR 直到完成。
检查和清除中断标志
前面我们看到,对于像 CC2544 这样的一些微控制器,当 ISR 代码开始执行时,我们只知道哪个端口导致了中断,而不知道具体的引脚。例如,如果 P0_3 改变它的值,它的标志将在 GPIO 外设内部设置,但 CPU 只执行 ISR 以响应来自中断控制器的 PORT0 中断标志。检查 ISR 中的 GPIO 外设中断标志可以告诉我们哪个特定引脚产生了中断,以便我们做出相应的响应。
由于中断标志表明我们要查找的事件已经发生,所以只要设置了中断标志,CPU每次有机会都会响应中断。例如,假设 P0_3 只改变了一次状态并导致其中断标志被设置。如果我们不设置标志位,那么在 CPU 运行了与 PORT0 相关的 ISR 之后,它仍然会认为有一个新的中断,所以它会再次运行 ISR。
为了避免这种情况,我们需要清除中断标志。有时中断标志在 CPU 开始运行 ISR 时会自动清除;其他时候你必须自己清除标志。微控制器的技术文档会让您知道是哪种情况。例如,CC2544 不会自动清除引脚中断标志,但 ATmega328P 会。如果您必须自己清除中断,这通常是您在 ISR 代码中做的第一件事,通常是在确定哪个引脚中断导致 ISR 被执行之后。
回顾:让 GPIO 中断工作
要将以上所有内容放在一起,为了使 GPIO 中断与您的代码一起工作,您必须:
编写一个 ISR,在其中你
确保清除任何需要清除的标志
用所需的动作响应中断
将 ISR 与正确的中断向量相关联
配置要触发中断的 GPIO 事件。可能的选项可能并非全部适用于您的特定微控制器,仅从 0 更改为 1、仅从 1 更改为 0、任何更改(0 到 1 或 1 到 0)、稳定为 1 或稳定为 0 。
为 GPIO 内的引脚启用中断。通常建议在启用引脚中断之前清除引脚的标志。
启用中断控制器内部的中断。
确保 CPU 已启用所有中断。
全部0条评论
快来发表一下你的评论吧 !