i.MX RT开发笔记-08 | i.MX RT1062嵌套中断向量控制器NVIC(按键中断检测)
Posted Mculover666
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了i.MX RT开发笔记-08 | i.MX RT1062嵌套中断向量控制器NVIC(按键中断检测)相关的知识,希望对你有一定的参考价值。
系列文章目录
- i.MX RT开发笔记-01 | 初识 i.MX RT1062 跨界MCU
- i.MX RT开发笔记-02 | i.MX RT1062开发环境搭建(MDK芯片包、NXP SDK详解)
- i.MX RT开发笔记-03 | i.MX RT1062地址空间映射
- i.MX RT开发笔记-04 | i.MX RT1062启动方式
- i.MX RT开发笔记-05 | 新建 MDK 不同版本工程(SRAM调试版本、Nor Flash下载版本)
- i.MX RT开发笔记-06 | i.MX RT1062 使用 IOMUXC 和 GPIO 点亮LED
- i.MX RT开发笔记-07 | 创建基于 SDK 的 Keil-MDK 工程模板
环境说明:
- MDK版本:5.30
- SDK版本:2.9.3
一、NVIC
NVIC全称Nested Vectored Interrupt Controller,嵌套中断向量控制器,属于ARM Cortex-M7内核的外设,使用方法基本和STM32相同,如果想了解更多关于 NVIC的描述,可以阅读ARM官方手册:
- 《DDI0489D_cortex_m7_trm》(Cortex-M7技术参考手册)
1. 中断源(中断号)
中断系统首先需要可以产生中断信号的中断源,ARM Cortex-M7 内核中的 NVIC 最大支持 240 个中断源,NXP在 RT1062 中支持 174 个中断源:
/** Interrupt Number Definitions */
#define NUMBER_OF_INT_VECTORS 174 /**< Number of interrupts in the Vector table */
RT1062支持的所有中断源使用枚举类型映射为中断号表示,类型为 IRQn_Type,在头文件MIMXRT1062.h
中定义,代码如下:
typedef enum IRQn {
/* Auxiliary constants */
NotAvail_IRQn = -128, /**< Not available device specific interrupt */
/* Core interrupts */
NonMaskableInt_IRQn = -14, /**< Non Maskable Interrupt */
HardFault_IRQn = -13, /**< Cortex-M7 SV Hard Fault Interrupt */
MemoryManagement_IRQn = -12, /**< Cortex-M7 Memory Management Interrupt */
BusFault_IRQn = -11, /**< Cortex-M7 Bus Fault Interrupt */
UsageFault_IRQn = -10, /**< Cortex-M7 Usage Fault Interrupt */
SVCall_IRQn = -5, /**< Cortex-M7 SV Call Interrupt */
DebugMonitor_IRQn = -4, /**< Cortex-M7 Debug Monitor Interrupt */
PendSV_IRQn = -2, /**< Cortex-M7 Pend SV Interrupt */
SysTick_IRQn = -1, /**< Cortex-M7 System Tick Interrupt */
/* Device specific interrupts */
DMA0_DMA16_IRQn = 0, /**< DMA channel 0/16 transfer complete */
//...
GPIO6_7_8_9_IRQn = 157 /**< GPIO6, GPIO7, GPIO8, GPIO9 interrupt */
} IRQn_Type;
在 RT1062 中有 174 个中断源, 其中有些中断信号是内核在运行程序的过程中发出的,大部分中断信号都是由外设产生的。
外设虽然可以产生中断信号,但默认时关闭的,需要我们手动去配置才可以产生,比如串口中断、定时器中断等。
2. 中断使能
如果每个外设产生中断信号后都可以直接触发 CPU 去响应中断,那系统可就乱套了~
所以按照中断源的紧急程度,将这些中断源分为三类:
- 不可用中断NotAvail_IRQn:系统保留,不使用
- 不可屏蔽中断NonMaskableInt_IRQn(NMI):不可屏蔽中断,任何情况下都会被CPU响应
- 可屏蔽中断:剩下的所有,都可以由NVIC控制是否被CPU响应
所以,NVIC->ICERx 寄存器用来控制每个中断源是否使能:
那么,如何来访问内核中NVIC的寄存器呢?这个时候CMSIS-Core就要派上用场啦!
CMSIS-Core 是ARM为了屏蔽不同厂商之间操作内核的差异,提供了一层抽象层,只要是ARM Cortex-M的内核都可以调用CMSIS-Core的API去操作,其核心就是一个头文件core_cm7.h
(7是指Cortex-M7):
#include "core_cm7.h" /* Core Peripheral Access Layer */
在该头文件中,我们可以方便的去调用 API 使能某个中断,如下:
/**
\\brief Enable Interrupt
\\details Enables a device specific interrupt in the NVIC interrupt controller.
\\param [in] IRQn Device specific interrupt number.
\\note IRQn must not be negative.
*/
__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn)
{
if ((int32_t)(IRQn) >= 0)
{
__COMPILER_BARRIER();
NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));
__COMPILER_BARRIER();
}
}
也可以方便的去调用 API 失能某个中断:
/**
\\brief Disable Interrupt
\\details Disables a device specific interrupt in the NVIC interrupt controller.
\\param [in] IRQn Device specific interrupt number.
\\note IRQn must not be negative.
*/
__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn)
{
if ((int32_t)(IRQn) >= 0)
{
NVIC->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));
__DSB();
__ISB();
}
}
细心的同学不难发现,这两个API传入的参数,刚好是在MIMXRT1062.h
头文件中的中断号枚举类型 IRQn_Type,所以,你应该知道如何使用了吧~
在 NXP 提供的 FSL固件库fsl_common.h
中,对CMSIS-Core提供的API再次进行了封装:
/*!
* @brief Enable specific interrupt.
*
* Enable LEVEL1 interrupt. For some devices, there might be multiple interrupt
* levels. For example, there are NVIC and intmux. Here the interrupts connected
* to NVIC are the LEVEL1 interrupts, because they are routed to the core directly.
* The interrupts connected to intmux are the LEVEL2 interrupts, they are routed
* to NVIC first then routed to core.
*
* This function only enables the LEVEL1 interrupts. The number of LEVEL1 interrupts
* is indicated by the feature macro FSL_FEATURE_NUMBER_OF_LEVEL1_INT_VECTORS.
*
* @param interrupt The IRQ number.
* @retval kStatus_Success Interrupt enabled successfully
* @retval kStatus_Fail Failed to enable the interrupt
*/
static inline status_t EnableIRQ(IRQn_Type interrupt)
{
//...
}
同样,失能的API也有对应的封装:
static inline status_t DisableIRQ(IRQn_Type interrupt)
{
//...
}
3. 中断优先级
174 个中断源非常丰富,但群龙无首可不行,需要按辈分来,大人说话小孩子是不可以打断的~
NVIC最大支持256个中断优先级,疯狂暗示它的中断优先级寄存器有8位起作用,但一般厂商生产MCU时,通常只用到 4 位就够了,所以 RT1062 最大支持 16 个中断优先级。
#define __NVIC_PRIO_BITS 4 /**< Number of priority bits implemented in the NVIC */
4. 中断处理函数
中断处理函数的地址称为中断向量表(标号__Vectors),在RT1062在启动文件定义,每个中断源都提供了默认的中断处理函数弱定义,用户可以重新实现。
比如以Systick中断处理函数 SysTick_Handler
为例:
二、GPIO外设中断
1. 概述
RT1062每个GPIO外设的32个引脚都可以触发输入中断,但需要注意的是:每个GPIO外设只拥有两个中断编号(GPIO1有点特殊),以GPIO1外设为例:
- GPIO1_Combined_0_15_IRQHandler:低16位的引脚共用该编号;
- GPIO1_Combined_16_31_IRQHandler:高16位的引脚共用该编号;
GPIO外设拥有三个寄存器用于中断相关:
① GPIOx_IMR寄存器用于控制是否使能该引脚的中断:
② GPIOx_ICR1和GPIO_ICR2寄存器用于配置中断触发条件:
其中每 2 位用来配置一个引脚,比如ICR0和ICR1用来配置该外设第0个引脚:
③ 16个引脚共用一个中断源,所以需要查询中断状态寄存器 GPIOx_ISR 来判断哪个引脚发生了中断:
该标志位由硬件自动置位,软件中向该位写1清空标志位。
2. 添加按键中断检测驱动
在 BSP 文件夹中新建 bsp_key.h
和bsp_key.c
,并添加到工程中。
编写bsp_key.h
文件:
#ifndef _BSP_KEY_H_
#define _BSP_KEY_H_
#include "fsl_gpio.h"
#define KEY1_GPIO GPIO4
#define KEY1_GPIO_PIN (20U)
#define KEY1_GPIO_IRQ GPIO4_Combined_16_31_IRQn
#define KEY1_GPIO_IRQ_HANDLER GPIO4_Combined_16_31_IRQHandler
#define KEY2_GPIO GPIO1
#define KEY2_GPIO_PIN (11U)
#define KEY2_GPIO_IRQ GPIO1_Combined_0_15_IRQn
#define KEY2_GPIO_IRQ_HANDLER GPIO1_Combined_0_15_IRQHandler
extern volatile bool g_KEY1_Signal;
extern volatile bool g_KEY2_Signal;
void bsp_key_init(char num);
#endif /* _BSP_LED_H_ */
编写bsp_key.c
文件:
#include "bsp_key.h"
volatile bool g_KEY1_Signal = false;
volatile bool g_KEY2_Signal = false;
void bsp_key_init(char num)
{
/* Define the init structure for the input switch pin */
gpio_pin_config_t sw_config = {kGPIO_DigitalInput, 0, kGPIO_IntLowLevel};
switch (num) {
case 1:
/* Init input switch GPIO. */
GPIO_PinInit(KEY1_GPIO, KEY1_GPIO_PIN, &sw_config);
/* Enable GPIO pin interrupt */
GPIO_PortEnableInterrupts(KEY1_GPIO, 1U << KEY1_GPIO_PIN);
/* NVIC enable. */
EnableIRQ(KEY1_GPIO_IRQ);
break;
case 2:
/* Init input switch GPIO. */
GPIO_PinInit(KEY2_GPIO, KEY2_GPIO_PIN, &sw_config);
/* Enable GPIO pin interrupt */
GPIO_PortEnableInterrupts(KEY2_GPIO, 1U << KEY2_GPIO_PIN);
/* NVIC enable. */
EnableIRQ(KEY2_GPIO_IRQ);
break;
default:
break;
}
}
void KEY1_GPIO_IRQ_HANDLER(void)
{
GPIO_PortClearInterruptFlags(KEY1_GPIO, 1U << KEY1_GPIO_PIN);
g_KEY1_Signal = true;
}
void KEY2_GPIO_IRQ_HANDLER()
{
GPIO_PortClearInterruptFlags(KEY2_GPIO, 1U << KEY2_GPIO_PIN);
g_KEY2_Signal = true;
}
3. 编写测试程序
#include "pin_mux.h"
#include "clock_config.h"
#include "board.h"
#include "bsp_led.h"
#include "bsp_key.h"
int main(void)
{
/* Board pin, clock */
BOARD_ConfigMPU();
BOARD_InitPins();
BOARD_BootClockRUN();
/* bsp init */
bsp_led_init();
bsp_key_init(1);
bsp_key_init(2);
/* led on */
bsp_led_ctl(0);
while (1) {
if (g_KEY1_Signal) {
g_KEY1_Signal = false;
bsp_led_ctl(1);
}
if (g_KEY2_Signal) {
g_KEY2_Signal = false;
bsp_led_ctl(0);
}
}
}
以上是关于i.MX RT开发笔记-08 | i.MX RT1062嵌套中断向量控制器NVIC(按键中断检测)的主要内容,如果未能解决你的问题,请参考以下文章
i.MX RT开发笔记-03 | i.MX RT1062地址空间映射
i.MX RT开发笔记-06 | i.MX RT1062 使用 IOMUXC 和 GPIO 点亮LED
i.MX RT开发笔记-02 | i.MX RT1062开发环境搭建(MDK芯片包NXP SDK详解)
痞子衡嵌入式:恩智浦i.MX RT1xxx系列MCU启动那些事(11.B)- FlexSPI NOR连接方式大全(RT1160/1170)...