- nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
- nvic_irq_enable(SPI0_IRQn,0,1);
有关这两个函数的原型以及函数参数的说明,请见代码清单nvic_priority_group_set函数原型、参数nvic_prigroup说明表、代码清单nvic_irq_enable函数原型、nvic_irq_enable()函数的参数说明表。
代码清单nvic_priority_group_set 函数原型
- void nvic_priority_group_set(uint32_t nvic_prigroup)
- {
- /* set the priority group value */
- SCB->AIRCR = NVIC_AIRCR_VECTKEY_MASK | nvic_prigroup;
- }
参数 nvic_prigroup 说明表
代码清单nvic_irq_enable 函数原型
- void nvic_irq_enable(uint8_t nvic_irq,
- uint8_t nvic_irq_pre_priority,
- uint8_t nvic_irq_sub_priority)
- {
- uint32_t temp_priority = 0x00U, temp_pre = 0x00U, temp_sub = 0x00U;
- /* use the priority group value to get the temp_pre and the temp_sub */
- switch ((SCB->AIRCR) & (uint32_t)0x700U) {
- case NVIC_PRIGROUP_PRE0_SUB4:
- temp_pre = 0U;
- temp_sub = 0x4U;
- break;
- case NVIC_PRIGROUP_PRE1_SUB3:
- temp_pre = 1U;
- temp_sub = 0x3U;
- break;
- case NVIC_PRIGROUP_PRE2_SUB2:
- temp_pre = 2U;
- temp_sub = 0x2U;
- break;
- case NVIC_PRIGROUP_PRE3_SUB1:
- temp_pre = 3U;
- temp_sub = 0x1U;
- break;
- case NVIC_PRIGROUP_PRE4_SUB0:
- temp_pre = 4U;
- temp_sub = 0x0U;
- break;
- default:
- nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
- temp_pre = 2U;
- temp_sub = 0x2U;
- break;
- }
- /* get the temp_priority to fill the NVIC->IP register */
- temp_priority = (uint32_t)nvic_irq_pre_priority << (0x4U - temp_pre);
- temp_priority |= nvic_irq_sub_priority &(0x0FU >> (0x4U - temp_sub));
- temp_priority = temp_priority << 0x04U;
- NVIC->IP[nvic_irq] = (uint8_t)temp_priority;
- /* enable the selected IRQ */
- NVIC->ISER[nvic_irq >> 0x05U] = (uint32_t)0x01U << (nvic_irq & (uint8_t)0x1FU);
- }
nvic_irq_enable()函数的参数说明表
参数nvic_irq是一个枚举变量,它定义了每一个中断的编号,具体定义在gd32f10x.h文件中,如代码清单中断号定义所示。
- typedef enum IRQn
- {
- /* Cortex-M3 processor exceptions numbers */
- NonMaskableInt_IRQn = -14, /*!< 2 non maskable interrupt */
- MemoryManagement_IRQn = -12, /*!< 4 Cortex-M3 memory management interrupt */
- BusFault_IRQn = -11, /*!< 5 Cortex-M3 bus fault interrupt */
- UsageFault_IRQn = -10, /*!< 6 Cortex-M3 usage fault interrupt */
- SVCall_IRQn = -5, /*!< 11 Cortex-M3 SV call interrupt */
- DebugMonitor_IRQn = -4, /*!< 12 Cortex-M3 debug monitor interrupt */
- PendSV_IRQn = -2, /*!< 14 Cortex-M3 pend SV interrupt */
- SysTick_IRQn = -1, /*!< 15 Cortex-M3 system tick interrupt */
- /* interruput numbers */
- WWDGT_IRQn = 0, /*!< window watchDog timer interrupt */
- LVD_IRQn = 1, /*!< LVD through EXTI line detect interrupt */
- TAMPER_IRQn = 2, /*!< tamper through EXTI line detect */
- RTC_IRQn = 3, /*!< RTC through EXTI line interrupt */
- FMC_IRQn = 4, /*!< FMC interrupt */
- RCU_CTC_IRQn = 5, /*!< RCU and CTC interrupt */
- EXTI0_IRQn = 6, /*!< EXTI line 0 interrupts */
- EXTI1_IRQn = 7, /*!< EXTI line 1 interrupts */
- EXTI2_IRQn = 8, /*!< EXTI line 2 interrupts */
- EXTI3_IRQn = 9, /*!< EXTI line 3 interrupts */
- EXTI4_IRQn = 10, /*!< EXTI line 4 interrupts */
- DMA0_Channel0_IRQn = 11, /*!< DMA0 channel0 interrupt */
- DMA0_Channel1_IRQn = 12, /*!< DMA0 channel1 interrupt */
- DMA0_Channel2_IRQn = 13, /*!< DMA0 channel2 interrupt */
- DMA0_Channel3_IRQn = 14, /*!< DMA0 channel3 interrupt */
- DMA0_Channel4_IRQn = 15, /*!< DMA0 channel4 interrupt */
- DMA0_Channel5_IRQn = 16, /*!< DMA0 channel5 interrupt */
- DMA0_Channel6_IRQn = 17, /*!< DMA0 channel6 interrupt */
- ADC0_1_IRQn = 18, /*!< ADC0 and ADC1 interrupt */
- #ifdef GD32F10X_MD
- USBD_HP_CAN0_TX_IRQn = 19, /*!< CAN0 TX interrupts */
- USBD_LP_CAN0_RX0_IRQn = 20, /*!< CAN0 RX0 interrupts */
- CAN0_RX1_IRQn = 21, /*!< CAN0 RX1 interrupts */
- CAN0_EWMC_IRQn = 22, /*!< CAN0 EWMC interrupts */
- EXTI5_9_IRQn = 23, /*!< EXTI[9:5] interrupts */
- TIMER0_BRK_IRQn = 24, /*!< TIMER0 break interrupts */
- TIMER0_UP_IRQn = 25, /*!< TIMER0 update interrupts */
- TIMER0_TRG_CMT_IRQn = 26, /*!< TIMER0 trigger and commutation interrupts */
- TIMER0_Channel_IRQn = 27, /*!< TIMER0 channel capture compare interrupts */
- TIMER1_IRQn = 28, /*!< TIMER1 interrupt */
- TIMER2_IRQn = 29, /*!< TIMER2 interrupt */
- TIMER3_IRQn = 30, /*!< TIMER3 interrupts */
- I2C0_EV_IRQn = 31, /*!< I2C0 event interrupt */
- I2C0_ER_IRQn = 32, /*!< I2C0 error interrupt */
- I2C1_EV_IRQn = 33, /*!< I2C1 event interrupt */
- I2C1_ER_IRQn = 34, /*!< I2C1 error interrupt */
- SPI0_IRQn = 35, /*!< SPI0 interrupt */
- SPI1_IRQn = 36, /*!< SPI1 interrupt */
- USART0_IRQn = 37, /*!< USART0 interrupt */
- USART1_IRQn = 38, /*!< USART1 interrupt */
- USART2_IRQn = 39, /*!< USART2 interrupt */
- EXTI10_15_IRQn = 40, /*!< EXTI[15:10] interrupts */
- RTC_Alarm_IRQn = 41, /*!< RTC alarm interrupt */
- USBD_WKUP_IRQn = 42, /*!< USBD Wakeup interrupt */
- EXMC_IRQn = 48, /*!< EXMC global interrupt */
- #endif /* GD32F10X_MD */
- } IRQn_Type;
3.2.中断服务函数的命名上一小节介绍了如何设置中断的优先级,那么中断服务函数如何命名和使用呢? 本小结将介绍这方面的内容。
下面以GD32F103C8T6产品为例,介绍如何命名中断服务函数名。GD32F103C8T6的flash容量为64KB,属于中密度产品,其对应的启动文件为startup_gd32f10x_md.s。在该启动文件中我们预先为每个中断都命名了一个中断服务函数,为的是初始化中断向量表。实际的中断服务函数里面的内容需要我们重新编写,中断服务函数我们统一写在gd32f10x_it.c文件里。
需要注意的是,中断服务函数的函数名必须和启动文件里面的一样,如果写错了,系统在中断向量表中就会找不到中断服务函数的入口,从而导致进不了中断。为了避免该错误,简单的处理方法是:打开startup_gd32f10x_md.s,找到需要的中断服务函数名,复制该函数名到gd32f10x_it.c文 件 中 即 可 。 以 SPI0 中 断 为 例 , 打 开 startup_gd32f10x_md.s , 找 到 SPI0_IRQHandler (SPI0_IRQHandler就是SPI0中断服务函数的名称),复制SPI0_IRQHandler到gd32f10x_it.c,修改其如代码清单SPI0中断服务函数所示即可。在该函数中就可以添加用户所需的中断服务 代码了。
- void SPI0_IRQHandler(void)
- {
- }
3.3.中断向量偏移当发生了异常并且要响应它时,Cortex-M 需要定位其处理例程的入口地址。这些入口地址存储在所谓的“异常向量表”中。默认情况下,Cortex-M认为该表位于零地址处,且各向量占用4 节,因此每个表项占用4 字节,如上电后的向量表所示。
因为地址0处应该存储引导代码,所以它通常是Flash或者是ROM器件,并且它们的值不得在运行时改变。然而,为了动态重分发中断,Cortex-M允许向量表重定位,从其它地址处开始定位各异常向量。这些地址对应的区域可以是代码区,但更多在RAM区。在RAM区就可以修改向量的入口地址了。为了实现这个功能,NVIC中有一个寄存器,称为“向量表偏移量寄存器”(在地址0xE000_ED08处),通过修改它的值就能定位向量表。但必须注意的是:向量表的起始地址是有要求的:必须先求出系统中共有多少个向量,再把这个数字向上增大到是2 的整次幂,而起始地址必须对齐到后者的边界上。例如,如果一共有32个中断,则共有32+16(系统异常)=48个向量,向上增大到2 的整次幂后值为64,因此地址地址必须能被64*4=256 整除,从而合法的起始地址可以是:0x0, 0x100, 0x200等。向量表偏移量寄存器的定义如向量表偏移寄存器(VTOR)表所示。
在gd32f10x_misc.c文件中,nvic_vector_table_set函数就是用来定义中断向量偏移的,该函数的原型如代码清单 0-16 nvic_vector_table_set函数原型所示,函数参数说明如参数说明表所示。
代码清单nvic_vector_table_set 函数原型
- void nvic_vector_table_set(uint32_t nvic_vict_tab, uint32_t offset)
- {
- SCB->VTOR = nvic_vict_tab | (offset & NVIC_VECTTAB_OFFSET_MASK);
- }
参数说明表
下面举例说明如何使用该函数。
在实际使用中,用户会把FALSH分成BOOT区和APP区。BOOT区只用于代码升级,实际应用的程序在APP区里运行。假设客户把FLASH的第0页(大小为1KB)作为BOOT区,该页的地址范围为0x08000000~0x080003FF,第2页、第3页作为APP区,地址范围为0x08000800~0x08000FFF。执行完BOOT区的代码后,程序会跳转到0x08000800的地址开始执行APP程序。0x08000800相对于基地址0x08000000的偏移地址为0x800,此时调用nvic_vector_table_set函数的格式如代码清单调用nvic_vector_table_set函数所示。
- nvic_vector_table_set(NVIC_VECTTAB_FLASH, 0x800);
3.4.NVIC 使用注意事项E23x 系列使用的是 M23 内核,该内核的 NVIC 使用 2bit 定义优先级,并且不分抢占优先级和子优先级。在 gd32e23x_misc.c 文件中,nvic_irq_enable(uint8_t nvic_irq, uint8_t nvic_irq_priority)函数用于设置优先级,该函数的参数说明如图所示。
教程由GD32 MCU方案商聚沃科技原创发布,了解更多GD32 MCU教程,关注聚沃科技官网,GD32MCU技术交流群:859440462