小猫爪:i.MX RT1050学习笔记5-中断NVIC
1 前言
中断是一款MCU不可或缺的一部分,而RT1050则是搭载了非常强的中断系统。在这里不得不提一下NVIC(嵌套向量中断控制器),它是内核里面的一个模块,属于Cortex-M7内核,但是RT1050的NVIC其实是Cortex-M7里标准NVIC的阉割版,是一个子集。虽然是阉割版,但是也是非常强大了,其中系统异常有10 个,外部中断有160 个。下面让我们具体介绍一下。
2 中断号
CPU 通过中断号来区分不同的中断,每个中断请求拥有一个固定的标号,这个标号就是中断号。RT1050的中断号定义在MIMXRT1052.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 */
DMA1_DMA17_IRQn = 1, /**< DMA channel 1/17 transfer complete */
DMA2_DMA18_IRQn = 2, /**< DMA channel 2/18 transfer complete */
DMA3_DMA19_IRQn = 3, /**< DMA channel 3/19 transfer complete */
.. .. ..
.. .. ..
.. .. ..
PWM3_0_IRQn = 142, /**< PWM3 capture 0, compare 0, or reload 0 interrupt */
PWM3_1_IRQn = 143, /**< PWM3 capture 1, compare 1, or reload 0 interrupt */
PWM3_2_IRQn = 144, /**< PWM3 capture 2, compare 2, or reload 0 interrupt */
PWM3_3_IRQn = 145, /**< PWM3 capture 3, compare 3, or reload 0 interrupt */
PWM3_FAULT_IRQn = 146, /**< PWM3 fault or reload error interrupt */
PWM4_0_IRQn = 147, /**< PWM4 capture 0, compare 0, or reload 0 interrupt */
PWM4_1_IRQn = 148, /**< PWM4 capture 1, compare 1, or reload 0 interrupt */
PWM4_2_IRQn = 149, /**< PWM4 capture 2, compare 2, or reload 0 interrupt */
PWM4_3_IRQn = 150, /**< PWM4 capture 3, compare 3, or reload 0 interrupt */
PWM4_FAULT_IRQn = 151 /**< PWM4 fault or reload error interrupt */
} IRQn_Type;
从代码中我们可以看到,中断号并不是从0开始,而是还存在小于0的中断号,在这里需要说一下,小于零的中断号属于内核中断,大家更容易称其为异常,一般应用我们是接触不到的,异常一般会被用在实时操作系统中,freeRTOS的任务切换就是通过出发pendSV异常实现的。而大于零的部分则是外部中断(这里的外部是相对于内核来说的,并不是相对于chip而言),我们通常所说的中断则是指的这部分,包括所有外设中断(如串口接收中断、GPIO EXT中断等)。我们在C代码中基本上就是对中单号进行操作,这是非常重要的索引符号。
3 中断优先级分组和中断优先级
对于这个部分,则是跟STM32(Cortex-M4)差不多了,也是可以进行中断优先级分组,分别设置主优先级(抢占优先级)和子优先级,在这里就不多介绍了,大家可以参考别人的文章进行详细的了解。下表描述了RT1050的中断优先级分组情况。
优先级的分组由内核外设SCB 的应用程序中断及复位控制寄存器AIRCR 的PRIGROUP[10:8]位决定,RT1050 分为了4 组。
4 相关操作函数
ARM给用户提供了非常方便的NVIC操作接口函数,如下所示:
NVIC 库函数 | 描述 |
---|---|
void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup) | 设置中断优先级分组 |
uint32_t __NVIC_GetPriorityGrouping(void) | 读取当前优先级分组 |
Void __NVIC_EnableIRQ(IRQn_Type IRQn) | 使能中断 |
uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn) | 查看中断是否使能 |
void __NVIC_DisableIRQ(IRQn_Type IRQn) | 禁止中断 |
uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn) | 查看中断是否被挂起 |
Void __NVIC_SetPendingIRQ(IRQn_Type IRQn) | 挂起中断 |
void __NVIC_ClearPendingIRQ(IRQn_Type IRQn) | 清除中断挂起标志位 |
uint32_t __NVIC_GetActive(IRQn_Type IRQn) | 读取当前正在运行的中断 |
void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) | 设置中断优先级 |
uint32_t __NVIC_GetPriority(IRQn_Type IRQn) | 查看一个中断的优先级 |
uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_tPreemptPriority, uint32_t SubPriority) | 将中断优先级分组、抢占优先级、从优先级转化为无符号32 优先级编码 |
void NVIC_DecodePriority (uint32_t Priority, uint32_tPriorityGroup, uint32_t* const pPreemptPriority, uint32_t* constpSubPriority) | 将无符号32 位优先级编码转换为优先级分组、抢占优先级、子优先级 |
void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector) | 设置中断向量(中断跳转地址) |
uint32_t __NVIC_GetVector(IRQn_Type IRQn) | 获得中断向量(中断跳转地址) |
void __NVIC_SystemReset(void) | 发送软件复位请求。 |
5 应用实例
下面我们以GPIO中断为例来介绍一下中断使用。之前先了解一下RT1050 GPIO中断的相关介绍。
5.1 RT1050 GPIO中断
RT1052 拥有5 组GPIO,每组GPIO 拥有32 个GPIO 输入输出引脚,每个输入输出引脚都能够触发中断。每组GPIO 拥有各自的中断相关寄存器,包括一个中断屏蔽寄存器(GPIOx_IMR),一个中断状态寄存(GPIOx_ISR),两个中断配置寄存器(GPIOx_ICR1、GPIOx_ICR2)。通过配置这些寄存器我们可以灵活的设置每一个输入输出引脚是否使用中断、中断触发条件、以及当前中断状态。
①中断屏蔽寄存器(GPIOx_IMR),中断屏蔽寄存用于配置输入输出引脚是否使用中断,该寄存器是32 位寄存,0 到31 位依次控制输入输出引脚0 到31。系统复位后GPIOx_IMR = 0,表示禁止所有中断,如果要使用中断必须设置相应位为1。
②中断状态寄存器(GPIOx_ISR),该寄存器是32 位寄存器,0 到31 位依次标记0 到31 个输入输出引脚的中断状态。如果发生了中断则相应的中断标志位被硬件自动置1,软件写入1 清除中断标志位。
③中断配置寄存器(GPIOx_ICR),每组GPIO 拥有两个中断配置寄存器(32 位),低16 个引脚使用GPIOx_ICR1,高16 个引脚使用GPIOx_ICR2。每个输入/输出引脚占用两位,可配置为低电平触发中断、高电平触发中断、上升沿触发中断、下降沿触发中断。我们根据外部电路和实际需要配置为不同的触发模式。
5.2 中断配置过程
我们在实现一个中断配置过程中,需要考虑以下几个点:
①配置中断优先级分组
②配置中断主、子优先级
③使能对应的中断号的中断请求,
④使能具体中断(如GPIO中断使能)
⑤声明中断服务函数
我们联系NXP官网DEMO来说一下(官方怎么中断优先级设置则是采取默认配置,在我们需要进行具有多个中断的系统设计中,则是需要进行细致的中断优先级配置):
void EXAMPLE_GPIO_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;
}
/*!
* @brief Main function
*/
int main(void)
{
/* Define the init structure for the input switch pin */
gpio_pin_config_t sw_config = {
kGPIO_DigitalInput,
0,
kGPIO_IntRisingEdge,
};
/* hardware initialiize, include IOMUX, Uart debug initialize */
BOARD_ConfigMPU();
BOARD_InitPins();
BOARD_BootClockRUN();
BOARD_InitDebugConsole();
PRINTF("GPIO Driver example.\r\n");
GPIO_PinInit(EXAMPLE_SW_GPIO, EXAMPLE_SW_GPIO_PIN, &sw_config);
/* Init input switch GPIO. */
EnableIRQ(EXAMPLE_SW_IRQ); //对应步骤三
/* Enable GPIO pin interrupt */
GPIO_PortEnableInterrupts(EXAMPLE_SW_GPIO, 1U << EXAMPLE_SW_GPIO_PIN);//对应步骤四
while (1)
{
if (g_InputSignal)
{
delay();
if (1 == GPIO_PinRead(EXAMPLE_SW_GPIO, EXAMPLE_SW_GPIO_PIN))
{
PRINTF("%s is turned on.\r\n", EXAMPLE_SW_NAME);
}
/* Reset state of switch. */
g_InputSignal = false;
}
}
}
END