0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

【GD32 MCU 入门教程】GD32 MCU 常见外设介绍(3)NVIC 介绍

聚沃科技 2024-08-12 10:02 次阅读

NVIC(Nested vectored interrupt controller,嵌套向量中断控制器)是Cortex-M处理器的一部分,它是可编程的,且寄存器位于存储器映射的系统控制空间(SCS)。NVIC与内核相辅相成,共同完成对中断的响应。本章将介绍中断的优先级设置、如何定义中断函数名称、中断向量如何偏移。有关NVIC的更多知识,请见《ARM Coretex-M3权威指南》。

3.1.优先级的设置

在Cortex-M中,优先级对于异常来说很关键的,它会影响一个异常是否能被响应,以及何时可以响应。优先级的数值越小,则优先级越高。Cortex-M支持中断嵌套,使得高优先级异常会抢占低优先级异常。有3个系统异常:复位,NMI以及硬fault,它们有固定的优先级,并且它们的优先级号是负数,从而高于所有其它异常。所有其它异常的优先级则都是可编程的,但不能编程为负数。

原则上,Cortex-M支持3 个固定的高优先级和多达256 级的可编程优先级,并且支持128级抢占。但是,绝大多数CM3芯片都会精简设计,以致实际上支持的优先级数会更少,如8级,16级,32级 等。它们在设计时会裁掉表达优先级的几个低端有效位,以达到减少优先级数的目的。

举例来说,如果只使用了4位来表达优先级,则优先级配置寄存器的结构如图所示。

使用 4bit 表达优先级

输入图片说明

GD32Fxxx系列、GD32E50x系列、GD32H7xx系 列、GD32A5x系列、GD32W51x系列和GD32VF103、GD32E10X系列使用了4位来表达优先级。GD32E23x和GD32L23x使用的是M23内核,只使用了2位表达优先级。

说明:GD32Fxxx系列是指:GD32F10x、GD32F1x0、GD32F20x、GD32F30x、GD32F3x0、GD32F40x、GD32F4xx。

用于表达优先级的这4bit,又被分组成抢占优先级和子优先级。如果有多个中断同时响应,抢占优先级高的就会抢占抢占优先级低的优先得到执行。如果抢占优先级相同,就比较子优先级。如果抢占优先级和子优先级都相同的话,就比较他们的硬件中断编号,编号越小,优先级越高。

GD32Fxxx系列、GD32E10x系列、GD32E50x系列、GD32H7xx系列、GD32A5x系列、GD32W51X系列和GD32VF103和GD32E10x系列可以设置抢占优先级和子优先级的等级,GD32E23x和GD32L23x系列没有抢占优先级和子优先级的说法,只可以设置优先级。

下面以GD32F10x举例说明如何设置优先级位数以及抢占优先级和子优先级的等级。在GD32f10x_misc.c文件中,nvic_priority_group_set函数用于设置多少位用于抢占优先级,多少位用于子优先级;nvic_irq_enable函数用于设置相应中断的抢占优先级和子优先级的等级。比如现在要设置SPI0的中断,其抢占优先级和子优先级的位数均为2,抢占优先级的等级为0,子优先级 的等级为1,那么代码如代码清单SPI0中断优先级设置所示。

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教程,关注聚沃科技官网

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 单片机
    +关注

    关注

    6020

    文章

    44349

    浏览量

    628054
  • 嵌入式
    +关注

    关注

    5042

    文章

    18795

    浏览量

    298238
  • 开发板
    +关注

    关注

    25

    文章

    4754

    浏览量

    96095
  • GD32
    +关注

    关注

    7

    文章

    398

    浏览量

    23961
收藏 人收藏

    评论

    相关推荐

    GD32 MCU 入门教程GD32 MCU 常见外设介绍(12)FMC 模块介绍

    闪存控制器(FMC),提供了片上闪存需要的所有功能。FMC 也提供了页擦除,整片擦除,以及32 位整字或 16 位半字编程闪存等操作。 GD32 MCU 支持不同类型编程的具体说明如下表 GD32
    的头像 发表于 08-21 09:56 648次阅读
    <b class='flag-5'>GD32</b> <b class='flag-5'>MCU</b> <b class='flag-5'>入门教程</b>】<b class='flag-5'>GD32</b> <b class='flag-5'>MCU</b> <b class='flag-5'>常见外设</b><b class='flag-5'>介绍</b>(12)FMC 模块<b class='flag-5'>介绍</b>

    GD32 MCU移植

    gd32是一款国产单片机。该芯片在很多方面和STM32有异曲同工之处。资料不是很多! GD32外设方面、和STM32没有多大区别。 只是需要注意资源的引脚分配。虽然和STM32使用方式一样、但是也存在
    发表于 03-23 13:40

    GD32MCU介绍

    其实兆芯的MCU为了兼容ST,外设的寄存器设计与ST保持一致了。比如GD32VF103或者GD32E103系列的USBFS这个外设,基本等同
    发表于 11-01 06:03

    GD32 MCU原理及固件库开发指南》+读后感

    。 第2章介绍GD32 MCU快速入门与开发平台搭建的方法,包括对软硬件开发平台、调试工具、GD32 M
    发表于 06-06 21:52

    兆易创新GD32 MCU选型手册,适用于GD32全系列MCU

    兆易创新GD32MCU选型手册,适用于GD32全系列MCUGD32MCU选型手册,适用于GD32全系列MCU
    发表于 10-19 17:26 48次下载

    你了解GD32 MCU的命名规则吗

    下面为大家介绍GD32 MCU的通用命名规则,以GD32F303ZGT6为例,其中,GD32代表GD32
    的头像 发表于 01-13 09:38 3032次阅读
    你了解<b class='flag-5'>GD32</b> <b class='flag-5'>MCU</b>的命名规则吗

    GD32 MCU的选项字节是什么?

    GD32 MCU的选项字节是什么,有什么功能呢?选项字节被误篡改如何回复?
    的头像 发表于 01-17 09:42 1023次阅读
    <b class='flag-5'>GD32</b> <b class='flag-5'>MCU</b>的选项字节是什么?

    不同型号的GD32 MCU如何区分?

    大家是否碰到过以下应用场景:同一套软件代码希望跑在不同型号的GD32 MCU中,但有些地方需要根据MCU型号进行调整?或者上位机或其他MCUGD3
    的头像 发表于 01-27 09:32 789次阅读
    不同型号的<b class='flag-5'>GD32</b> <b class='flag-5'>MCU</b>如何区分?

    GD32 MCU 入门教程】一、GD32 MCU 开发环境搭建(1)使用Keil开发GD32

    GD32系列为通用型MCU,所以开发环境也可以使用通用型的IDE,目前使用较多的是KEIL、IAR、 GCC和Embedded Builder,客户可以根据个人喜好来选择相应的开发环境。
    的头像 发表于 08-08 15:01 492次阅读
    【<b class='flag-5'>GD32</b> <b class='flag-5'>MCU</b> <b class='flag-5'>入门教程</b>】一、<b class='flag-5'>GD32</b> <b class='flag-5'>MCU</b> 开发环境搭建(1)使用Keil开发<b class='flag-5'>GD32</b>

    GD32 MCU 入门教程】一、GD32 MCU 开发环境搭建(2)使用 IAR 开发 GD32

    GD32系列为通用型MCU,所以开发环境也可以使用通用型的IDE,目前使用较多的是KEIL、IAR、 GCC和Embedded Builder,客户可以根据个人喜好来选择相应的开发环境。
    的头像 发表于 08-08 15:40 327次阅读
    【<b class='flag-5'>GD32</b> <b class='flag-5'>MCU</b> <b class='flag-5'>入门教程</b>】一、<b class='flag-5'>GD32</b> <b class='flag-5'>MCU</b> 开发环境搭建(2)使用 IAR 开发 <b class='flag-5'>GD32</b>

    GD32 MCU 入门教程】一、GD32 MCU 开发环境搭建(3)使用 Embedded Builder 开发 GD32

    GD32系列为通用型MCU,所以开发环境也可以使用通用型的IDE,目前使用较多的是KEIL、IAR、 GCC和Embedded Builder,客户可以根据个人喜好来选择相应的开发环境。
    的头像 发表于 08-08 16:03 458次阅读
    【<b class='flag-5'>GD32</b> <b class='flag-5'>MCU</b> <b class='flag-5'>入门教程</b>】一、<b class='flag-5'>GD32</b> <b class='flag-5'>MCU</b> 开发环境搭建(<b class='flag-5'>3</b>)使用 Embedded Builder 开发 <b class='flag-5'>GD32</b>

    GD32 MCU 入门教程】二、GD32 MCU 烧录说明(1)ISP 烧录

    ISP:In System Programing,在系统编程,通过MCU片内的引导程序进行Flash编程。 GD32片内有一个只读信息块,用于存放引导装载程序,引导程序在MCU出厂前就会提前烧录好
    的头像 发表于 08-08 16:20 340次阅读
    【<b class='flag-5'>GD32</b> <b class='flag-5'>MCU</b> <b class='flag-5'>入门教程</b>】二、<b class='flag-5'>GD32</b> <b class='flag-5'>MCU</b> 烧录说明(1)ISP 烧录

    GD32 MCU 入门教程GD32 MCU 常见外设介绍(14)RTC 模块介绍

    GD32 MCU内部提供了一个RTC(实时时钟)模块,通过RTC可以实现日历时钟、闹钟等功能。RTC也可以用于深度睡眠或待机模式的低功耗唤醒。不同系列的GD32 MCU在RTC设计和功
    的头像 发表于 08-23 09:18 196次阅读
    【<b class='flag-5'>GD32</b> <b class='flag-5'>MCU</b> <b class='flag-5'>入门教程</b>】<b class='flag-5'>GD32</b> <b class='flag-5'>MCU</b> <b class='flag-5'>常见外设</b><b class='flag-5'>介绍</b>(14)RTC 模块<b class='flag-5'>介绍</b>

    GD32 MCU 入门教程GD32 MCU FPU 使用方法

    GD32 MCU FPU 使用方法
    的头像 发表于 08-25 09:24 186次阅读
    【<b class='flag-5'>GD32</b> <b class='flag-5'>MCU</b> <b class='flag-5'>入门教程</b>】<b class='flag-5'>GD32</b> <b class='flag-5'>MCU</b> FPU 使用方法

    GD32 MCU入门教程GD32 MCU GPIO 结构与使用注意事项

    本文是专门为基于GD32 MCU开发的工程设计人员提供,主要介绍了GPIO的功能配置、内部结构以及在不同场景使用时的注意事项,旨在帮助GD32 MC
    的头像 发表于 09-07 10:34 181次阅读
    【<b class='flag-5'>GD32</b> <b class='flag-5'>MCU</b><b class='flag-5'>入门教程</b>】<b class='flag-5'>GD32</b> <b class='flag-5'>MCU</b> GPIO 结构与使用注意事项