16. ICU——外部中断¶
本章配套视频介绍:
《22-ICU–外部中断(第1节)——中断过程介绍》
https://www.bilibili.com/video/BV1rb4y1G7Ud/
《23-ICU–外部中断(第2节)——按键外部中断实验》
https://www.bilibili.com/video/BV1D64y1p7KQ/
本章参考资料:《RA6M5 Group User’s Manual: Hardware》 主要参考章节:13. Interrupt Controller Unit (ICU)
16.1. 外部引脚中断¶
上一章节我们已经详细介绍了NVIC、ICU、ELC、NMI 并对RA系列MCU的中断管理系统有个全局的了解, 我们这一章节的内容是如何控制外部中断,也是内核里的NVIC的实例应用,这也是RA系列MCU非常重要的一个外部中断资源。
16.1.1. ICU框图-外部中断介绍¶
根据ICU的功能框图我们知道,首先需要配置IRQCR寄存器(IRQ Control Register,IRQ英文全称:Interrupt ReQuest,中文名:中断请求)。之后我们要配置IELSR寄存器(英文全称:ICU Event Link Setting Register,中文名:中断控制单元事件链接设置寄存器)。之后使能IR寄存器。 最后我们需要配置NVIC,关于NVIC详情可以了解上一节。
16.1.2. 相应寄存器介绍¶
16.1.2.1. IRQCR介绍¶
Interrupt Request Control Register(IRQCR) — 中断请求控制寄存器,这个寄存器拥有0-15个通道,有三个标志位: (1) IRQMD[1:0]检测感知选择 (2) FCLKSEL[1:0]数字滤波采样时钟选择 (3) FLTEN数字滤波器使能
IRQ控制寄存器:
位 |
位域名 |
说明 |
---|---|---|
0 |
IRQMD |
|
1 |
FCLKSEL |
|
2 |
FLTEN |
IRQ数字滤波器使能 |
位 |
位域名 |
说明 |
---|---|---|
0 |
IELS |
ICU事件链接选择 |
1 |
IR |
中断状态标志,置0不产生中断请求,置1产生中断请求 |
2 |
DTCE |
DTCE位(DTC激活启用):当DTCE位设置为1时,相关事件被选择为DTC激活源 |
中断控制单元事件链接设置寄存器(IELSRn),在整个的RA6M5中拥有的数量有96个。
16.2. 中断的过程介绍¶
当中断来临的时候会最先经过IRQ寄存器,IRQ寄存器检测到中断的时候,会向中央处理嵌套向量中断控制器NVIC发送中断信号, 当NVIC检测到中断请求的时候,会将相应的中断服务函数进行挂起。之后将运行的八个寄存器进行压栈,压栈完成之后将中断服务程序进行激活。 之后将原先压栈的寄存器取出,继续运行之前的程序。
中断抢占的过程:
当中断来临的时候会最先经过IRQ寄存器,IRQ寄存器检测到中断的时候,会向中央处理嵌套向量中断控制器NVIC发送中断信号, 当NVIC检测到中断请求的时候,会将相应的中断服务函数进行挂起。之后将运行的八个寄存器进行压栈,堆栈完成之后将中断服务程序进行激活。 此时当另一个优先级更高的中断源发生请求的时候,会将相应的中断服务函数进行挂起。之后将当前运行的八个寄存器进行压栈,压栈完成之后将中断服务程序进行激活。 当中断服务程序运行完之后会将原先压栈的寄存器取出,之后继续运行之前中断服务的程序。中断服务程序运行完成之后之后将原先压栈的寄存器取出,继续运行之前的程序。
16.3. 实验:按键外部中断¶
中断在嵌入式应用中的地位不言而喻,几乎每个控制器都有中断功能。中断对事件得到第一时间处理是非常重要的。 我们设计使用按键来作为触发源,使得控制器产生中断,并在中断服务函数中实现控制LED灯的任务。
16.3.1. 硬件设计¶
轻触按键在按下时会使得按键的两个引脚接通,通过电路设计可以使得在按键按下时引脚产生电平变化。
野火启明6M5开发板的按键电路设计如下:
野火启明4M2开发板的按键电路设计如下:
野火启明2L1开发板的按键电路设计如下:
其中,RES 按键是开发板的芯片复位按键,用于复位MCU芯片。 另外两个按键为用户可自定义的按键,可用于实现用户所需的功能。
在按键的电路设计中,与按键相并联的电容属于硬件滤波电容,可以在一定程度上防止按键抖动带来的误触发。 其实,用户也可以使用瑞萨RA MCU的IO引脚内部自带的数字滤波功能,这样可以去掉该电容,从而减少电路板的硬件成本。
开发板 |
引脚及其对应中断号 |
---|---|
启明6M5 |
KEY1_SW2: P004 (IRQ9-DS) KEY2_SW3: P005 (IRQ10-DS) |
启明4M2 |
KEY1_SW2: P403 (IRQ14-DS) KEY2_SW3: P404 (IRQ15-DS) |
启明2L1 |
KEY1_SW2: P001 (IRQ7) KEY2_SW3: P002 (IRQ2) |
16.3.2. 软件设计¶
16.3.2.1. 新建工程¶
- 对于 e2 studio 开发环境:
拷贝一份我们之前的 e2s 工程 “11_GPIO_LED”, 然后将工程文件夹重命名为 “16_ICU_External_IRQ”,最后再将它导入到我们的 e2 studio 工作空间中。
- 对于 Keil 开发环境:
拷贝一份我们之前的 Keil 工程 “11_GPIO_LED”, 然后将工程文件夹重命名为 “16_ICU_External_IRQ”,并进入该文件夹里面双击 Keil 工程文件,打开该工程。
工程新建好之后,在工程根目录的 “src” 文件夹下面新建 “key” 文件夹, 再进入 “key” 文件夹里面新建源文件和头文件: “bsp_key_irq.c” 和 “bsp_key_irq.h”。 工程文件结构如下。
16_ICU_External_IRQ
├─ ......
└─ src
├─ led
│ ├─ bsp_led.c
│ └─ bsp_led.h
├─ key
│ ├─ bsp_key_irq.c
│ └─ bsp_key_irq.h
└─ hal_entry.c
16.3.2.2. FSP配置¶
首先打开 “16_ICU_External_IRQ” 项目的 FSP 配置界面,接下来我们要在这个界面里配置芯片的引脚及其相应的功能。
以野火启明6M5开发板为例,首先配置引脚连接到 ICU 的中断通道。
然后,依次点击 “Stacks” -> “New Stack” -> “Input”-> “External IRQ” 来加入外部中断模块, 如下图所示。由于使用两个按键,需要两个外部中断,所以这里需要加入2个外部中断模块。
第一个外部中断模块设置的是按键1的外部中断配置,按如下图所示进行设置:
第二个外部中断模块设置的是按键2的外部中断配置,按如下图所示进行设置:
外部中断模块(r_icu)的属性介绍如下表所示。
External IRQ 属性 |
描述 |
---|---|
Name |
名称 |
Channel |
通道(IRQ中断号) |
Trigger |
触发方式:上升沿、下降沿、双边沿或低电平触发 |
Digital Filtering |
数字滤波使能 |
Digital Filtering Sample Clock (Only valid when Digital Filtering is Enabled) |
数字滤波采样时钟 |
Callback |
中断回调函数 2个按键外部中断的中断回调函数可以设置为相同, 只需要在函数中判断触发中断的中断号即可互相区分 |
Pin Interrupt Priority |
中断优先级 |
配置完成之后可以按下快捷键“Ctrl + S”保存, 最后点右上角的 “Generate Project Content” 按钮,让软件自动生成配置代码即可。
16.3.2.3. 按键外部中断初始化函数¶
/* KEY 外部中断初始化函数 */
void Key_IRQ_Init(void)
{
fsp_err_t err = FSP_SUCCESS;
/* Open ICU module */
err = R_ICU_ExternalIrqOpen(&g_external_irq9_ctrl, &g_external_irq9_cfg);
err = R_ICU_ExternalIrqOpen(&g_external_irq10_ctrl, &g_external_irq10_cfg);
/* 允许中断 */
err = R_ICU_ExternalIrqEnable(&g_external_irq9_ctrl);
err = R_ICU_ExternalIrqEnable(&g_external_irq10_ctrl);
}
R_ICU_ExternalIrqOpen() 用于打开一个外部中断模块实例。
R_ICU_ExternalIrqEnable() 用于使能外部中断,使能后即可触发产生外部中断。
16.3.2.4. 按键中断回调函数¶
因为之前的 FSP 配置里面 Callback 属性配置的都是同一个中断回调函数, 因此,我们仅需实现一个中断回调函数即可。当然,中断回调函数也可设置为不同的两个函数,由用户定义。
两个按键共用 key_external_irq_callback 中断回调函数, 并通过 p_args->channel 参数来判断是哪个中断通道产生了中断从而判断是哪个按键按下,该函数如下:
/* 按键按下标志 */
volatile bool key1_sw2_press = false;
volatile bool key2_sw3_press = false;
/* 按键中断回调函数 */
void key_external_irq_callback(external_irq_callback_args_t *p_args)
{
/* 判断中断通道 */
if (9 == p_args->channel)
{
key1_sw2_press = true; // 按键KEY1_SW2按下
}
else if (10 == p_args->channel)
{
key2_sw3_press = true; // 按键KEY2_SW3按下
}
}
16.3.2.5. hal_entry入口函数¶
hal_entry 入口函数如下所示:
/* 用户头文件包含 */
#include "led/bsp_led.h"
#include "key/bsp_key_irq.h"
extern volatile bool key1_sw2_press;
extern volatile bool key2_sw3_press;
void hal_entry(void)
{
/* TODO: add your own code here */
LED_Init(); // LED 初始化
Key_IRQ_Init(); // KEY 外部中断初始化
while(1)
{
/* 判断按键 KEY1_SW2 是否被按下 */
if (key1_sw2_press)
{
key1_sw2_press = false; //标志位清零
LED2_TOGGLE; //LED2 翻转
}
/* 判断按键 KEY2_SW3 是否被按下 */
if (key2_sw3_press)
{
key2_sw3_press = false; //标志位清零
LED3_TOGGLE; //LED3 翻转
}
// LED1 闪烁指示程序正在运行
LED1_TOGGLE;
R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS); //延时1秒
// 按键被按下触发中断后,在主循环中LED2或LED3最长会被推迟1秒翻转
}
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
}
16.3.3. 下载验证¶
把编译好的程序下载到开发板并复位,按下用户按键1和按键2即可分别控制LED2和LED3灯的亮灭。 同时LED1灯在每秒钟翻转一次状态。