前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >i.MX RT1062 嵌套中断向量控制器NVIC

i.MX RT1062 嵌套中断向量控制器NVIC

作者头像
Mculover666
发布2021-12-28 09:42:41
8180
发布2021-12-28 09:42:41
举报
文章被收录于专栏:TencentOS-tinyTencentOS-tiny

一、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 个中断源:

代码语言:javascript
复制
/** Interrupt Number Definitions */
#define NUMBER_OF_INT_VECTORS 174                /**< Number of interrupts in the Vector table */

RT1062支持的所有中断源使用枚举类型映射为中断号表示,类型为 IRQn_Type,在头文件MIMXRT1062.h中定义,代码如下:

代码语言:javascript
复制
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):

代码语言:javascript
复制
#include "core_cm7.h"                  /* Core Peripheral Access Layer */

在该头文件中,我们可以方便的去调用 API 使能某个中断,如下:

代码语言:javascript
复制
/**
  \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 失能某个中断:

代码语言:javascript
复制
/**
  \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再次进行了封装:

代码语言:javascript
复制
/*!
 * @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也有对应的封装:

代码语言:javascript
复制
static inline status_t DisableIRQ(IRQn_Type interrupt)
{
//...
}

3. 中断优先级

174 个中断源非常丰富,但群龙无首可不行,需要按辈分来,大人说话小孩子是不可以打断的~

NVIC最大支持256个中断优先级,疯狂暗示它的中断优先级寄存器有8位起作用,但一般厂商生产MCU时,通常只用到 4 位就够了,所以 RT1062 最大支持 16 个中断优先级。

代码语言:javascript
复制
#define __NVIC_PRIO_BITS               4         /**< Number of priority bits implemented in the NVIC */

4. 中断处理函数

中断处理函数的地址称为中断向量表(标号__Vectors),在RT1062在启动文件定义,每个中断源都提供了默认的中断处理函数弱定义,用户可以重新实现。

比如以Systick中断处理函数 SysTick_Handler 为例:

二、GPIO外设中断

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清空标志位

三、按键检测

1. 基于SDK新建MDK工程

这里使用 NXP 官方提供的 MCUXpresso Config Tools。

修改按键使用的GPIO引脚,设置方向为输入、GPIO中断为低电平触发、默认开启上拉电阻:

查看时钟配置:

点击【更新源代码】,修改引脚会更新文件pin_mux.hpin_mux.c

2. 修改MDK工程

打开生成的keil工程,选择RAM调试版本:

修改按键引脚定义:

修改按键中断类型为低电平触发:

3. 调试

编译,点击调试,全速运行,在串口助手查看日志:

4. 代码分析

该实验的核心逻辑都在 source 文件夹中的 gpio_input_interrupt.c 文件中。

4.1. 通用逻辑

在main函数中,首先完成了IOMUX、时钟的初始化,这两个都可以使用Config Tool 工具来配置生成文件,并初始化了MPU、以及一个自带的组件Debug_console,方便在工程中使用 PRINTF 函数来打印,默认调试串口是LPUART1。

4.2. GPIO操作API

针对GPIO外设,FSL库提供对应的库函数,在fsl_gpio.hfsl_gpio.c中。

关于GPIO FSL库API在上一篇文章中有详细讲解,需要注意的是,这里需要在初始化按键GPIO的时候指明中断类型:

代码语言:javascript
复制
/* Define the init structure for the input switch pin */
gpio_pin_config_t sw_config = {
    kGPIO_DigitalInput,
    0,
    kGPIO_IntLowLevel,
};
4.3. 中断相关API

(1)首先需要在 NVIC 外设使能GPIO中断,允许CPU接收到GPIO中断信号:

代码语言:javascript
复制
static inline status_t EnableIRQ(IRQn_Type interrupt);

中断源在 MIMXRT1062.h头文件中枚举,这里是 GPIO4_Combined_16_31_IRQn

(2)然后需要使能GPIO中断,允许GPIO外设产生中断信号:

代码语言:javascript
复制
/*!
 * @brief Enables the specific pin interrupt.
 *
 * @param base GPIO base pointer.
 * @param mask GPIO pin number macro.
 */
static inline void GPIO_PortEnableInterrupts(GPIO_Type *base, uint32_t mask)
{
    base->IMR |= mask;
}

(3)最后重新实现 GPIO 中断处理函数即可:

代码语言:javascript
复制
/*!
 * @brief Interrupt service fuction of switch.
 */
void GPIO4_Combined_16_31_IRQHandler(void)
{
    /* clear the interrupt status */
    GPIO_PortClearInterruptFlags(EXAMPLE_SW_GPIO, 1U << EXAMPLE_SW_GPIO_PIN);
    /* Change state of switch. */
    g_InputSignal = true;
    SDK_ISR_EXIT_BARRIER;
}

至此,代码就分析完毕啦~

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-12-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Mculover666 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、NVIC
    • 1. 中断源(中断号)
      • 2. 中断使能
        • 3. 中断优先级
          • 4. 中断处理函数
          • 二、GPIO外设中断
          • 三、按键检测
            • 1. 基于SDK新建MDK工程
              • 2. 修改MDK工程
                • 3. 调试
                  • 4. 代码分析
                    • 4.1. 通用逻辑
                    • 4.2. GPIO操作API
                    • 4.3. 中断相关API
                相关产品与服务
                腾讯云代码分析
                腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,支撑团队传承代码文化。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档