一、简介
Cortex-M3集成了嵌套式矢量型中断控制器(Nested Vectored Interrupt Controller (NVIC))来实现高效的异常和中断处理。NVIC实现了低延迟的异常和中断处理,以及电源管理控制。它和内核是紧密耦合的。更多关于NVIC 的说明请参考《Cortex-M3 技术参考手册》。
EXTI(中断/事件控制器)包括20个相互独立的边沿检测电路并且能够向处理器内核产生中断
请求或唤醒事件。EXTI有三种触发类型:上升沿触发、下降沿触发和任意沿触发。EXTI中的每一个边沿检测电路都可以独立配置和屏蔽。
二、API说明
以下 NVIC 接口位于 GD32F10x_Firmware_Library_V2.2.2\Firmware\GD32F10x_standard_peripheral\Include\gd32f10x_misc.h。
2.1 nvic_priority_group_set
功能 | 设置优先级组 |
---|---|
函数定义 | void nvic_priority_group_set(uint32_t nvic_prigroup) |
参数 | nvic_prigroup:优先级组 |
返回 | 无 |
nvic_prigroup: 优先级组,详细列表如下:
值 | 含义 |
---|---|
NVIC_PRIGROUP_PRE0_SUB4 | 0位用于抢占优先级,4位用于响应优先级 |
NVIC_PRIGROUP_PRE1_SUB3 | 1位用于抢占优先级,3位用于响应优先级 |
NVIC_PRIGROUP_PRE2_SUB2 | 2位用于抢占优先级,2位用于响应优先级 |
NVIC_PRIGROUP_PRE3_SUB1 | 3位用于抢占优先级,1位用于响应优先级 |
NVIC_PRIGROUP_PRE4_SUB0 | 4位用于抢占优先级,0位用于响应优先级 |
2.2 nvic_irq_enable
功能 | 使能NVIC的中断 |
---|---|
函数定义 | void nvic_irq_enable(uint8_t nvic_irq, uint8_t nvic_irq_pre_priority, uint8_t nvic_irq_sub_priority) |
参数 | nvic_irq:NVIC中断 nvic_irq_pre_priority:抢占优先级(04)<br>nvic_irq_sub_priority:响应优先级(04) |
返回 | 无 |
以下 GPIO 接口位于 GD32F10x_Firmware_Library_V2.2.2\Firmware\GD32F10x_standard_peripheral\Include\gd32f10x_gpio.h。
2.3 gpio_exti_source_select
功能 | 选择哪个引脚作为EXTI源 |
---|---|
函数定义 | void gpio_exti_source_select(uint8_t output_port, uint8_t output_pin) |
参数 | output_port:EXTI源端口x(x = A,B,C,D,E,F,G) output_pin:源端口引脚x(x=0..15) |
返回 | 无 |
以下 EXTI 接口位于 GD32F10x_Firmware_Library_V2.2.2\Firmware\GD32F10x_standard_peripheral\Include\gd32f10x_exti.h。
2.4 exti_init
功能 | 初始化EXTI线x |
---|---|
函数定义 | void exti_init(exti_line_enum linex, exti_mode_enum mode, exti_trig_type_enum trig_type) |
参数 | linex:EXTI线x(x=0,1,2..19) mode:EXTI模式 trig_type:触发类型 |
返回 | 无 |
mode: EXTI模式,详细列表如下:
值 | 含义 |
---|---|
EXTI_INTERRUPT | 中断模式 |
EXTI_EVENT | 事件模式 |
trig_type: 触发类型,详细列表如下:
值 | 含义 |
---|---|
EXTI_TRIG_RISING | 上升沿触发 |
EXTI_TRIG_FALLING | 下降沿触发 |
EXTI_TRIG_BOTH | 上升沿和下降沿均触发 |
2.5 exti_interrupt_flag_get
功能 | 获取EXTI线x中断标志位 |
---|---|
函数定义 | FlagStatus exti_interrupt_flag_get(exti_line_enum linex) |
参数 | linex:EXTI线x(x=0,1,2..19) |
返回 | SET或RESET |
2.6 exti_interrupt_flag_clear
功能 | 清除EXTI线x中断标志位 |
---|---|
函数定义 | void exti_interrupt_flag_clear(exti_line_enum linex) |
参数 | linex:EXTI线x(x=0,1,2..19) |
返回 | 无 |
三、EXTI外部中断
3.1 引脚确定
我使用的是 光子MINI-GD32F103RCT6
开发板
有个 IO 口为 PA1 的按键
3.2 编程要点
- 初始化用来产生中断的 GPIO
- 初始化 EXTI
- 配置 NVIC
- 编写中断服务函数
3.3 GPIO初始化
- 如果硬件上已外部上拉或下拉,则选择浮空输入模式。
- 如果硬件外部没有上拉,则选择内部上拉模式。
//GPIOA时钟使能
rcu_periph_clock_enable(RCU_GPIOA);
//PA1配置成上拉输入
gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_1);
3.4 外部中断初始化
- 配置 EXTI 信号源的时候需要用到 AFIO 的外部中断控制寄存器 AFIO_EXTISSx,所以
用到 EXTI 必须开启 AFIO 时钟
- 抢占优先级,数字越小,优先级越高
- 若抢占优先级相同,判断子优先级,同样,数字越小,优先级越高
//使能复用功能时钟
rcu_periph_clock_enable(RCU_AF);
//设置优先级分组
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
//设置优先级
nvic_irq_enable(EXTI1_IRQn, 2U, 2U);
//设置EXTI触发源
gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOA, GPIO_PIN_SOURCE_1);
//下降沿中断
exti_init(EXTI_1, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
//清中断标志
exti_interrupt_flag_clear(EXTI_1);
NVIC IRQn_Type:
名称 | 功能 |
---|---|
EXTI0_IRQn | EXTI线0中断 |
EXTI1_IRQn | EXTI线1中断 |
EXTI2_IRQn | EXTI线2中断 |
EXTI3_IRQn | EXTI线3中断 |
EXTI4_IRQn | EXTI线4中断 |
EXTI5_9_IRQn | EXTI线[9:5]中断 |
EXTI10_15_IRQn | EXTI线[15:10]中断 |
3.5 外部中断处理函数
当中断发生时,对应的中断服务函数就会被执行,我们可以在中断服务函数实现一些控制。
一般为确保中断确实发生,我们会在中断服务函数中调用中断标志位状态读取函数读取外设中断标志位并判断标志位状态。
exti_interrupt_flag_get()
函数用来获取 EXTI 的中断标志位状态,如果 EXTI 线有中断发生函数返回“SET”否则返回“RESET”。实际上,exti_interrupt_flag_get()
函数是通过读取 EXTI_PD 寄存器值来判断 EXTI 线状态的。
/*!
\brief this function handles external lines 1 interrupt request
\param[in] none
\param[out] none
\retval none
*/
void EXTI1_IRQHandler(void)
{
//判定是否是外部中断线1
if (RESET != exti_interrupt_flag_get(EXTI_1))
{
//在此加入自己的控制,一般为置标志等短暂非阻塞操作
//清除中断标记
exti_interrupt_flag_clear(TAMPER_KEY_EXTI_LINE);
}
}
外部中断处理函数名称要与EXTI线相对应,可在 startup_gd32f10x_hd.s
启动文件中查看:
; /* external interrupts handler */
DCD WWDGT_IRQHandler ; 16:Window Watchdog Timer
DCD LVD_IRQHandler ; 17:LVD through EXTI Line detect
DCD TAMPER_IRQHandler ; 18:Tamper Interrupt
DCD RTC_IRQHandler ; 19:RTC through EXTI Line
DCD FMC_IRQHandler ; 20:FMC
DCD RCU_IRQHandler ; 21:RCU
DCD EXTI0_IRQHandler ; 22:EXTI Line 0
DCD EXTI1_IRQHandler ; 23:EXTI Line 1
DCD EXTI2_IRQHandler ; 24:EXTI Line 2
DCD EXTI3_IRQHandler ; 25:EXTI Line 3
DCD EXTI4_IRQHandler ; 26:EXTI Line 4
DCD DMA0_Channel0_IRQHandler ; 27:DMA0 Channel 0
DCD DMA0_Channel1_IRQHandler ; 28:DMA0 Channel 1
DCD DMA0_Channel2_IRQHandler ; 29:DMA0 Channel 2
DCD DMA0_Channel3_IRQHandler ; 30:DMA0 Channel 3
DCD DMA0_Channel4_IRQHandler ; 31:DMA0 Channel 4
DCD DMA0_Channel5_IRQHandler ; 32:DMA0 Channel 5
DCD DMA0_Channel6_IRQHandler ; 33:DMA0 Channel 6
DCD ADC0_1_IRQHandler ; 34:ADC0 and ADC1
DCD USBD_HP_CAN0_TX_IRQHandler ; 35:USBD and CAN0 TX
DCD USBD_LP_CAN0_RX0_IRQHandler ; 36:USBD and CAN0 RX0
DCD CAN0_RX1_IRQHandler ; 37:CAN0 RX1
DCD CAN0_EWMC_IRQHandler ; 38:CAN0 EWMC
DCD EXTI5_9_IRQHandler ; 39:EXTI Line 5 to EXTI Line 9
DCD TIMER0_BRK_IRQHandler ; 40:TIMER0 Break
DCD TIMER0_UP_IRQHandler ; 41:TIMER0 Update
DCD TIMER0_TRG_CMT_IRQHandler ; 42:TIMER0 Trigger and Commutation
DCD TIMER0_Channel_IRQHandler ; 43:TIMER0 Channel Capture Compare
DCD TIMER1_IRQHandler ; 44:TIMER1
DCD TIMER2_IRQHandler ; 45:TIMER2
DCD TIMER3_IRQHandler ; 46:TIMER3
DCD I2C0_EV_IRQHandler ; 47:I2C0 Event
DCD I2C0_ER_IRQHandler ; 48:I2C0 Error
DCD I2C1_EV_IRQHandler ; 49:I2C1 Event
DCD I2C1_ER_IRQHandler ; 50:I2C1 Error
DCD SPI0_IRQHandler ; 51:SPI0
DCD SPI1_IRQHandler ; 52:SPI1
DCD USART0_IRQHandler ; 53:USART0
DCD USART1_IRQHandler ; 54:USART1
DCD USART2_IRQHandler ; 55:USART2
DCD EXTI10_15_IRQHandler ; 56:EXTI Line 10 to EXTI Line 15
DCD RTC_Alarm_IRQHandler ; 57:RTC Alarm through EXTI Line
DCD USBD_WKUP_IRQHandler ; 58:USBD WakeUp from suspend through EXTI Line
DCD TIMER7_BRK_IRQHandler ; 59:TIMER7 Break Interrupt
DCD TIMER7_UP_IRQHandler ; 60:TIMER7 Update Interrupt
DCD TIMER7_TRG_CMT_IRQHandler ; 61:TIMER7 Trigger and Commutation Interrupt
DCD TIMER7_Channel_IRQHandler ; 62:TIMER7 Channel Capture Compare
DCD ADC2_IRQHandler ; 63:ADC2
DCD EXMC_IRQHandler ; 64:EXMC
DCD SDIO_IRQHandler ; 65:SDIO
DCD TIMER4_IRQHandler ; 66:TIMER4
DCD SPI2_IRQHandler ; 67:SPI2
DCD UART3_IRQHandler ; 68:UART3
DCD UART4_IRQHandler ; 69:UART4
DCD TIMER5_IRQHandler ; 70:TIMER5
DCD TIMER6_IRQHandler ; 71:TIMER6
DCD DMA1_Channel0_IRQHandler ; 72:DMA1 Channel0
DCD DMA1_Channel1_IRQHandler ; 73:DMA1 Channel1
DCD DMA1_Channel2_IRQHandler ; 74:DMA1 Channel2
DCD DMA1_Channel3_4_IRQHandler ; 75:DMA1 Channel3 and Channel4
四、按键外部中断,反转LED灯
4.1 board_gpio.c
/*********************************************************************
* INCLUDES
*/
#include "gd32f10x.h"
#include "board_gpio.h"
/*********************************************************************
* PUBLIC FUNCTIONS
*/
/*=========================================================================*/
/* 输入 */
/*=========================================================================*/
/**
@brief 按键驱动初始化
@param 无
@return 无
*/
void Key_GPIO_Init(void)
{
// GPIO时钟使能
rcu_periph_clock_enable(RCU_GPIOA);
// 配置为内部上拉输入模式
gpio_init(KEY1_GPIO_PORT, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, KEY1_GPIO_PIN);
}
/**
@brief 获取按键状态
@param keyNum -[in] 按键编号
@return 1 - 按下;0 - 松开
*/
uint8_t Key_GPIO_Read(uint8_t keyNum)
{
uint8_t value = 0;
if(KEY1 == keyNum)
{
value = gpio_input_bit_get(KEY1_GPIO_PORT, KEY1_GPIO_PIN);
}
// else if(KEY2 == keyNum)
// {
// value = gpio_input_bit_get(KEY2_GPIO_PORT, KEY2_GPIO_PIN);
// }
return value;
}
/*=========================================================================*/
/* 输出 */
/*=========================================================================*/
/**
@brief LED灯驱动初始化
@param 无
@return 无
*/
void LED_GPIO_Init(void)
{
// PB4管脚默认是NJTRST,要当GPIO,需要重映射
rcu_periph_clock_enable(RCU_AF); // 管脚复用时钟使能
gpio_pin_remap_config(GPIO_SWJ_NONJTRST_REMAP, ENABLE);
// GPIO时钟使能
rcu_periph_clock_enable(RCU_GPIOB);
// 配置为推挽输出模式
gpio_init(LED1_GPIO_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, LED1_GPIO_PIN);
LED_GPIO_Write(LED1, LED_OFF);
}
/**
@brief 配置LED灯工作模式
@param ledNum -[in] LED灯编号
@param ledMode -[in] 工作模式
@return 无
*/
void LED_GPIO_Write(uint8_t ledNum, uint8_t ledMode)
{
if(LED1 == ledNum)
{
gpio_bit_write(LED1_GPIO_PORT, LED1_GPIO_PIN, ledMode);
}
// else if(LED2 == ledNum)
// {
// gpio_bit_write(LED2_GPIO_PORT, LED2_GPIO_PIN, ledMode);
// }
}
/**
@brief 获取LED灯工作模式
@param ledNum -[in] LED灯编号
@return 工作模式
*/
uint8_t LED_GPIO_Read(uint8_t ledNum)
{
uint8_t ledMode = 0;
if(LED1 == ledNum)
{
ledMode = gpio_output_bit_get(LED1_GPIO_PORT, LED1_GPIO_PIN);
}
// else if(LED2 == ledNum)
// {
// ledMode = gpio_output_bit_get(LED2_GPIO_PORT, LED2_GPIO_PIN, &ledMode);
// }
return ledMode;
}
/****************************************************END OF FILE****************************************************/
4.2 board_gpio.h
#ifndef _BOARD_GPIO_H_
#define _BOARD_GPIO_H_
/*********************************************************************
* INCLUDES
*/
#include "gd32f10x_gpio.h"
/*********************************************************************
* DEFINITIONS
*/
/*=========================================================================*/
/* 输入 */
/*=========================================================================*/
#define KEY1_GPIO_PORT GPIOA
#define KEY1_GPIO_PIN GPIO_PIN_1
#define KEY_OFF 0x01
#define KEY_ON 0x00
#define KEY1 1
#define KEY2 2
/*=========================================================================*/
/* 输出 */
/*=========================================================================*/
#define LED1_GPIO_PORT GPIOB
#define LED1_GPIO_PIN GPIO_PIN_4
#define LED_OFF 0x01
#define LED_ON 0x00
#define LED1 1
#define LED2 2
/*********************************************************************
* API FUNCTIONS
*/
void Key_GPIO_Init(void);
uint8_t Key_GPIO_Read(uint8_t keyNum);
void LED_GPIO_Init(void);
void LED_GPIO_Write(uint8_t ledNum, uint8_t ledMode);
uint8_t LED_GPIO_Read(uint8_t ledNum);
#endif /* _BOARD_GPIO_H_ */
4.3 board_exti.c
/*********************************************************************
* INCLUDES
*/
#include "gd32f10x.h"
#include "board_exti.h"
/*********************************************************************
* PUBLIC FUNCTIONS
*/
/**
@brief 按键外部中断初始化
@param 无
@return 无
*/
void Key_EXTI_Init(void)
{
// AFIO时钟使能
rcu_periph_clock_enable(RCU_AF);
// 设置优先级分组
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
// 设置优先级
nvic_irq_enable(EXTI1_IRQn, 2U, 2U);
// 设置EXTI触发源
gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOA, GPIO_PIN_SOURCE_1);
// 下降沿中断
exti_init(EXTI_1, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
// 清中断标志
exti_interrupt_flag_clear(EXTI_1);
}
/****************************************************END OF FILE****************************************************/
4.4 board_exti.h
#ifndef _BOARD_EXTI_H_
#define _BOARD_EXTI_H_
/*********************************************************************
* INCLUDES
*/
#include "gd32f10x_exti.h"
/*********************************************************************
* DEFINITIONS
*/
/*********************************************************************
* API FUNCTIONS
*/
void Key_EXTI_Init(void);
#endif /* _BOARD_EXTI_H_ */
4.5 user_key.c
/*********************************************************************
* INCLUDES
*/
#include "board_exti.h"
#include "board_gpio.h"
/*********************************************************************
* PUBLIC FUNCTIONS
*/
/**
@brief 按键模块初始化
@param 无
@return 无
*/
void Key_Init(void)
{
Key_GPIO_Init();
Key_EXTI_Init();
}
/**
@brief 按键中断回调函数
@param 无
@return 无
*/
void Key_GPIO_IrqCallback(void)
{
static uint8_t toggle;
if(0 == toggle)
{
LED_GPIO_Write(LED1, LED_ON);
toggle = 1;
}
else
{
LED_GPIO_Write(LED1, LED_OFF);
toggle = 0;
}
}
/****************************************************END OF FILE****************************************************/
4.6 user_key.h
#ifndef _USER_KEY_H_
#define _USER_KEY_H_
/*********************************************************************
* INCLUDES
*/
/*********************************************************************
* API FUNCTIONS
*/
void Key_Init(void);
#endif /* _USER_KEY_H_ */
4.7 gd32f10x_it.c
/*!
\file gd32f10x_it.c
\brief interrupt service routines
\version 2014-12-26, V1.0.0, firmware for GD32F10x
\version 2017-06-20, V2.0.0, firmware for GD32F10x
\version 2018-07-31, V2.1.0, firmware for GD32F10x
\version 2020-09-30, V2.2.0, firmware for GD32F10x
*/
/*
Copyright (c) 2020, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/
#include "gd32f10x_it.h"
#include "systick.h"
void Key_GPIO_IrqCallback(void);
/*!
\brief this function handles NMI exception
\param[in] none
\param[out] none
\retval none
*/
void NMI_Handler(void)
{
}
/*!
\brief this function handles HardFault exception
\param[in] none
\param[out] none
\retval none
*/
void HardFault_Handler(void)
{
/* if Hard Fault exception occurs, go to infinite loop */
while(1){
}
}
/*!
\brief this function handles MemManage exception
\param[in] none
\param[out] none
\retval none
*/
void MemManage_Handler(void)
{
/* if Memory Manage exception occurs, go to infinite loop */
while(1){
}
}
/*!
\brief this function handles BusFault exception
\param[in] none
\param[out] none
\retval none
*/
void BusFault_Handler(void)
{
/* if Bus Fault exception occurs, go to infinite loop */
while(1){
}
}
/*!
\brief this function handles UsageFault exception
\param[in] none
\param[out] none
\retval none
*/
void UsageFault_Handler(void)
{
/* if Usage Fault exception occurs, go to infinite loop */
while(1){
}
}
/*!
\brief this function handles SVC exception
\param[in] none
\param[out] none
\retval none
*/
void SVC_Handler(void)
{
}
/*!
\brief this function handles DebugMon exception
\param[in] none
\param[out] none
\retval none
*/
void DebugMon_Handler(void)
{
}
/*!
\brief this function handles PendSV exception
\param[in] none
\param[out] none
\retval none
*/
void PendSV_Handler(void)
{
}
/*!
\brief this function handles SysTick exception
\param[in] none
\param[out] none
\retval none
*/
void SysTick_Handler(void)
{
delay_decrement();
}
/*!
\brief this function handles external lines 1 interrupt request
\param[in] none
\param[out] none
\retval none
*/
void EXTI1_IRQHandler(void)
{
if(RESET != exti_interrupt_flag_get(EXTI_1))
{
Key_GPIO_IrqCallback();
exti_interrupt_flag_clear(EXTI_1);
}
}
/**
@brief 弱化函数,请在其他地方实现
@param 无
@return 无
*/
__attribute__((weak)) void Key_GPIO_IrqCallback(void)
{
/* NOTE : This function should not be modified, when the callback is needed,
it could be implemented in the user file
*/
}
4.8 main.c
#include "gd32f10x.h"
#include "systick.h"
#include "board_gpio.h"
#include "user_key.h"
int main(void)
{
systick_config();//系统主频108MHZ,采用外部晶振,由两个宏决定(__SYSTEM_CLOCK_108M_PLL_HXTAL与HXTAL_VALUE)
Key_Init(); // 按键模块初始化
LED_GPIO_Init();// LED灯模块初始化
while(1)
{
}
}
4.9 工程代码
百度网盘:https://pan.baidu.com/s/1wMrs0xfVbFU5qNGYovO8ow?pwd=lszm 提取码:lszm
• 由 Leung 写于 2022 年 4 月 13 日
网友评论