16. ICU——外部中断

本章配套视频介绍:

../../_images/video.png

《22-ICU–外部中断(第1节)——中断过程介绍》

https://www.bilibili.com/video/BV1rb4y1G7Ud/

../../_images/video.png

《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框图-外部中断介绍

图 16-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控制寄存器:

图 16‑2 IRQCRi寄存器
IRQCRi寄存器

位域名

说明

0

IRQMD

外部引脚中断源的检测方法
  • Falling edge 下降沿

  • Rising edge 上升沿

  • Rising and falling edges 上升和下降沿

  • Low level 低电平

1

FCLKSEL

数字滤波采样时钟选择
  • PCLKB

  • PCLKB/8

  • PCLKB/32

  • PCLKB/64

2

FLTEN

IRQ数字滤波器使能

图 16‑3 IELSR寄存器
IELSR寄存器

位域名

说明

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 属性介绍

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. 按键外部中断初始化函数

代码清单 16_1:外部中断初始化函数
/* 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);
}
  1. R_ICU_ExternalIrqOpen() 用于打开一个外部中断模块实例。

  2. R_ICU_ExternalIrqEnable() 用于使能外部中断,使能后即可触发产生外部中断。

16.3.2.4. 按键中断回调函数

因为之前的 FSP 配置里面 Callback 属性配置的都是同一个中断回调函数, 因此,我们仅需实现一个中断回调函数即可。当然,中断回调函数也可设置为不同的两个函数,由用户定义。

两个按键共用 key_external_irq_callback 中断回调函数, 并通过 p_args->channel 参数来判断是哪个中断通道产生了中断从而判断是哪个按键按下,该函数如下:

代码清单 16_2:按键中断回调函数
/* 按键按下标志 */
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 入口函数如下所示:

代码清单 16_3: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灯在每秒钟翻转一次状态。