CH32V003的外部中断和事件

发布时间 2023-08-25 21:12:07作者: fxzq

下面再次给出CH32V003的中断和异常的向量表。

从表中可以看到,从编号16~38的23个中断均为外设中断,其余4个为内核中断。

这里需要再说明一下事件与中断的区别:

事件表示检测到某一动作触发事件发生了,而中断则表示有某个事件发生并产生中断,然后会跳转到对应的中断处理程序中去执行。中断有可能被更优先的中断屏蔽,事件则不会。事件只是一个触发信号(脉冲),而中断则是一个固定的电平信号 (标记)。事件本质上是一个触发信号,用来触发特定的外设模块或核心本身(如唤醒等)。事件是中断的触发源,事件可以触发中断,也可以不触发,开放了对应的中断屏蔽位,则事件可以触发相应的中断。 事件还是其它一些操作的触发源,比如DMA等。中断一定要有中断服务函数,而事件却没有对应的函数。事件可以在不需要CPU干预的情况下执行操作,中断则必须要CPU介入(执行中断服务程序)。

下图是CH32V003外部中断(EXTI)接口的结构框图。 

从图中可以看出,外部中断的触发源既可以是软件中断(SWIEVR),也可以是实际的外部中断通道。外部中断通道的信号会先经过边沿检测电路(Edge detect circuit)的筛选,只要产生软件中断或外部中断信号其一,就会通过图中的“或门”电路输出给事件使能和中断使能两个“与门”电路,当有中断被使能或事件被使能,就会产生中断或事件。控制外部中断(事件)的6个寄存器(INTFR、INTENR、SWIEVR、RTENR、FTENR、EVENR)由处理器通过AHB总线进行访问。

下表就给出了这6个寄存器的相关信息。

先来看中断使能寄存器EXTI_INTENR,下面是它的全部位结构。

(1)第31~10位均为保留位。
(2)第9~0位分别对应MR9~MR0等10个通道,其中MR7~MR0对应EXTI7~EXTI0这8个外部中断通道,MR9则对应自动唤醒事件,MR8对应PVD事件。相应的位置1则启用中断,置0则禁止中断。

接下来是事件使能寄存器EXTI_EVENR,它的位结构与上面的EXTI_INTENR寄存器完全一样,只是相应的位置1启用的是事件,置0则禁止事件。

接着看上升沿触发使能寄存器EXTI_RTENR, 下面是它的全部位结构。

(1)第31~10位均为保留位。
(2)第9~0位分别对应TR9~TR0等10个通道,其中TR7~TR0对应EXTI7~EXTI0这8个外部中断通道,TR9则对应自动唤醒事件,TR8对应PVD事件。相应的位置1则使能上升沿触发,置0则禁止。

接下来是下降沿触发使能寄存器EXTI_FTENR,它的位结构与上面的EXTI_RTENR寄存器完全一样,只是相应的位置1使能的是下降沿触发,置0则禁止。

接下来看中断标志位寄存器EXTI_INTFR,下面是它的全部位结构。

(1)第31~10位均为保留位。
(2)第9~0位分别对应IF9~IF0等10个通道,其中IF7~IF0对应EXTI7~EXTI0这8个外部中断通道,IF9则对应自动唤醒事件,IF8对应PVD事件。相应的位为1时表示发生了对应的外部中断,为0时表示没有中断发生,写1可以清除该标志。 

最后是软中断事件寄存器EXTI_SWIEVR, 下面是它的全部位结构。

(1)第31~10位均为保留位。
(2)第9~0位分别对应SWIER9~SWIER0等10个通道,其中SWIER7~SWIER0对应EXTI7~EXTI0这8个外部中断通道,SWIER9则对应自动唤醒事件,SWIER8对应PVD事件。相应的位置1会置位EXTI_INTFR寄存器中对应的位,从而触发中断或事件(如果对应的中断或事件被启用),置0无影响。

以上6个寄存器主要是配置与外部中断相关的一些功能,然而,如何把EXTI7~EXTI0这8个外部中断通道与芯片的具体引脚关联起来,还需要一个名为AFIO_EXTICR的外部中断配置寄存器的参与,下面是它的全部位结构。

(1)第31~16位均为保留位。
(2)第15~14、13~12、11~10、9~8、7~6、5~4、3~2及1~0等每两位分别为EXTI7、EXTI6、EXTI5、EXTI4、EXTI3、EXTI2、EXTI1、EXTI0等8个外部中断的引脚配置位(分别对应7~0号引脚),EXTI的值为00时表示使用PA端口的引脚,为10时表示使用PC端口的引脚,为11时表示使用PD端口的引脚。

使用外部中断要配置相应的外部中断通道,即选择相应触发沿,使能相应中断。当外部中断通道上出现了设定的触发沿时,将产生一个中断请求,对应的中断标志位也会被置位,对标志位写1可以清除该标志位。下面给出相应的操作步骤。
使用外部硬件中断步骤:
1、配置GPIO操作;
2、配置对应的外部中断通道的中断使能位(EXTI_INTENR);
3、配置触发沿(EXTI_RTENR或EXTI_FTENR),选择上升沿触发、下降沿触发或双边沿触发;
4、在内核的PFIC中配置EXTI中断,以保证其可以正确响应。
使用外部硬件事件步骤:
1、配置GPIO操作;
2、配置对应的外部中断通道的事件使能位(EXTI_EVENR);
3、配置触发沿(EXTI_RTENR或EXTI_FTENR),选择上升沿触发、下降沿触发或双边沿触发。
使用软件中断/事件步骤:
1、使能外部中断(EXTI_INTENR)或外部事件(EXTI_EVENR);
2、如果使用中断服务函数,需要设置内核的PFIC里EXTI中断;
3、设置软件中断触发(EXTI_SWIEVR),即会产生中断。

对于外部中断讨论了这么多,接下来看一个按键控制LED灯的例子,要求通过接在CH32V003芯片PD0上的一个按键来控制接在PD6引脚上的一个LED亮灭,程序代码如下。 

#include <ch32v00x.h>
void EXTI7_0_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void SystemInit (void)
{
    RCC->CFGR0 = 0;   //AHB时钟不分频
    RCC->APB2PCENR |= 0x00000001;  //使能I/O辅助功能模块时钟
    AFIO->PCFR1 |= (1<<15);  //引脚PA1&PA2重映射位,改为接外部晶振引脚
    RCC->CTLR |= 0x00010000;  //使能外部振荡器
    while(!(RCC->CTLR & 0x00020000));  //等待外部晶振稳定
    FLASH->ACTLR &= 0x00000003;
    FLASH->ACTLR |= 0x00000001; //设置FLASH等待
    RCC->CFGR0 |= 0x00010000;  //选择外部晶振
    RCC->CTLR |= 0x01000000;  //开启PLL
    while(!(RCC->CTLR & 0x02000000));  //等待PLL稳定
    RCC->CFGR0 &= ~0x00000003;
    RCC->CFGR0 |= 0x00000002;  //选择PLL作为系统时钟
}
int main(void)
{
    RCC->APB2PCENR |= 1 << 5;   //使能PD端口的时钟
    GPIOD->CFGLR &= ~0x4000000;
    GPIOD->CFGLR |= 0x1000000;   //通过以上两句配置PD6为通用推挽输出模式,速度为10MHz
    GPIOD->BCR |= 0x40; //设置PD6为低电平

    GPIOD->CFGLR &= ~0x04;
    GPIOD->CFGLR |= 0x08; //设置PD0为输入方向,带上下拉电阻
    GPIOD->OUTDR |= 0x01; //设置PD0为带上拉的输入方式

    AFIO->EXTICR |= 0x03; //设置PD0为中断触发引脚

    EXTI->FTENR |= 0x01; //设置0线通道为下降沿触发
    EXTI->INTENR |= 0x01; //使能0线通道中断

    NVIC->IENR[0] |= 0x00100000;  //使全局外部中断
//    NVIC_EnableIRQ(EXTI7_0_IRQn); //使全局外部中断
//    SetVTFIRQ((u32)EXTI7_0_IRQHandler, EXTI7_0_IRQn, 0, ENABLE); //设置为免表中断
    while(1)
    {
        ;
    }
}
void EXTI7_0_IRQHandler(void)
{
    if(EXTI->INTFR == 0x01) //若是0线通道中断则执行中断服务程序
    {
        EXTI->INTFR |= 0x01;  //清除0线通道中断标志位
        GPIOD->OUTDR ^= 0x0040;   //PD6取反(通过异或方式)
    }
}

把程序编译后下载到CH32V003中,给系统上电,按动按键,就可以看到LED点亮或熄灭。