什么是中断
举一个例子,比如我们正在敲代码,突然有一个电话打过来,这时我 们停止敲代码转而去接电话,然后在电话聊完事情之后继续敲代码。这里面的电话就相当于一个中 断,打断我们当前做的事情,接电话聊事情就相当于中断需要去执行的事情,也就是中断服务程序。
外部中断
EXTI(中断/事件控制器)包括 23 个相互独立的边沿检测电路并且能够向处理器内核产生中断请 求或唤醒事件。EXTI 有三种触发类型:上升沿触发、下降沿触发和任意沿触发。EXTI 中的每一个边沿检测电路都可以独立配置和屏蔽。每一个中断都有 4 位中断优先级配置位,可提供 16 个中断优先等级。
中断触发源
EXTI 触发源包括来自 I/O 管脚的 16 根线以及来自内部模块的 7 根线。(包括 LVD、RTC 闹钟、 USB 唤醒、以太网唤醒、RTC 侵入和时间戳、RTC 唤醒)。通过配置 SYSCFG_EXTISSx 寄存器,所有的 GPIO 管脚都可以被选作 EXTI 的触发源。
中断事件
除了中断,EXTI 还可以向处理器提供事件信号。芯片内部有一个唤醒中断控制器(WIC),用户 可以让处理器和 NVIC 进入功耗极低的省电模式,由 WIC 来识别中断和事件以及判断优先级。当某些 预期的事件发生时,EXTI 能唤醒处理器及整个系统。
中断优先级
抢占优先级:抢占优先级决定是否可以产生中断嵌套, 响应优先级决定中断响应顺序,若两种优先级一样则看中断在中断向量表中的位置,位置越靠前越 先响应。(食堂吃饭,有人正在打饭,你可以终止他打饭,不管他打了多少,自己立即打饭)
响应优先级:比抢占优先级低,比普通事件高,但是不可以打断嵌套。(食堂吃饭,有人正在打饭,你不可以终止他打饭,但是你会在他后边插队,他打完饭,你再打饭)
通常中断优先级分组只设置一次,它针对的是系统中所有的中断。后续设置某个中断的中断优 先级时,只需要设置在这个分组内的抢占优先级和响应优先级。
中断配置
一般使用 GPIO 的外部中断功能,都需要有以下几个步骤。
1. 开启时钟(包括 GPIO 时钟和系统配置时钟)
2. 配置 GPIO 的模式
3. 使能 NVIC 中断并配置优先级
4. 配置 GPIO 中断
5. 使能中断和清除中断标志位
6. 编写中断服务函数
1 开启时钟
GD32 单片机的任意引脚都可以配置为外部中断触发,开发板的按键使用的是 KEY_UP,接到了 单片机的 PA0 引脚上,因此我们可以使能 PA0 的外部中断功能。
/* 开启时钟 */
rcu_periph_clock_enable(BSP_KEY_RCU); //RCU_GPIOA
rcu_periph_clock_enable(RCU_SYSCFG); // 系统配置时钟
2 配置GPIO的模式
gpio_mode_set(BSP_KEY_PORT,GPIO_MODE_INPUT,GPIO_PUPD_PULLDOWN,BSP_KEY_PIN); // 按键默认状态是低电平,配置为下拉
3 使能 NVIC 中断并配置优先级
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2); // 设置中断优先级组 2 位用于抢 占优先级,2 位用于响应优先级
//这个可以在main.c里配置
nvic_irq_enable(EXTI0_IRQn,3U,3U); // 使能NVIC中断,并且设置抢占优先级3,子优先级3 PA0管脚对应外部中断线0
//设置按键点灯这个操作的抢占优先级和响应优先级(子优先级)
4 配置GPIO中断(将GPIO管脚设置为中断源)
syscfg_exti_line_config(EXTI_SOURCE_GPIOA,EXTI_SOURCE_PIN0);
exti_init(EXTI_0 ,EXTI_INTERRUPT,EXTI_TRIG_BOTH);//初始化中断线配置为中断模式,上升沿和下降沿均触发。
5 使能中断和清除中断标志位
/* 使能外部中断 */
exti_interrupt_enable(EXTI_0);
/* 清除外部中断标志位 */
exti_interrupt_flag_clear(EXTI_0);
6 编写中断服务函数
使能中断之后,如果有中断触发,就会跳转到中断处理函数里面执行。需要编写中断处理函数。 首先是中断函数名, 这个是固定的,在 startup_gd32f450_470.s 启动文件中有定义 注意,中断函数执行完成之后要清除中断标志位
exti_interrupt_flag_get(EXTI_0)//这个函数用于获取中断标志位,来判断中断标志位是否被重置
void EXTI0_IRQHandler(void)
{
if(exti_interrupt_flag_get(BSP_KEY_EXTI_LINE) == SET) // 中断标志位为1,按键按下
{
if(gpio_input_bit_get(BSP_KEY_PORT,BSP_KEY_PIN) == SET) // 按键按下
{
/* 按键按下操作的功能 */
gpio_bit_toggle(LED1_port,LED1_pin); // led电平状态翻转
printf("key press!\r\n"); // 串口打印key press!
}else{ // 按键释放
/* 按键松开操作的功能 */
printf("key release!\r\n"); // 串口打印key release!
}
exti_interrupt_flag_clear(BSP_KEY_EXTI_LINE); // 清中断标志位
}
}
源代码
/*
* Author: carpe diem 2116815480@qq.com
* Date: 2023-03-30 13:45:14
* LastEditTime: 2023-04-04 20:30:06
* FilePath: \Projectf:\Desktop\file\KEIL CODE\梁山派开发板\007外部中断点灯\Hardware\key\bsp_key.c
* Description: bsp_key.c 外部中断点灯
*
* Copyright (c) 2023 by carpe diem, All Rights Reserved.
*/
#include "bsp_key.h"
#include "stdio.h"
/**
* description:按键初始化
* return
*/
void key_gpio_config(void)
{
/* 开启时钟 */
rcu_periph_clock_enable(BSP_KEY_RCU);
rcu_periph_clock_enable(RCU_SYSCFG); // 系统配置时钟
/* 配置为输入模式 下拉模式 */
gpio_mode_set(BSP_KEY_PORT,GPIO_MODE_INPUT,GPIO_PUPD_PULLDOWN,BSP_KEY_PIN); // 按键默认状态是低电平,配置为下拉
/* 使能NVIC中断 中断分组为2位抢占优先级,2位子优先级 */
nvic_irq_enable(BSP_KEY_EXTI_IRQN,3U,3U); // 抢占优先级3,子优先级3
/* 连接中断线到GPIO */
syscfg_exti_line_config(BSP_KEY_EXTI_PORT_SOURCE,BSP_KEY_EXTI_PIN_SOURCE);
/* 初始化中断线 */
exti_init(BSP_KEY_EXTI_LINE,EXTI_INTERRUPT,EXTI_TRIG_BOTH);//初始化中断线配置为中断模式,上升沿和下降沿均触发。
/* 使能外部中断 */
exti_interrupt_enable(BSP_KEY_EXTI_LINE);
/* 清除外部中断标志位 */
exti_interrupt_flag_clear(BSP_KEY_EXTI_LINE);
}
/**
* description: 中断处理函数 注意中断函数执行完成之后要清除中断标志位
* return {*}
*/
void BSP_KEY_EXTI_IRQHANDLER(void)
{
if(exti_interrupt_flag_get(BSP_KEY_EXTI_LINE) == SET) // 中断标志位为1,按键按下
{
if(gpio_input_bit_get(BSP_KEY_PORT,BSP_KEY_PIN) == SET) // 按键按下
{
/* 按键按下操作的功能 */
gpio_bit_toggle(LED1_port,LED1_pin); // led电平状态翻转
printf("key press!\r\n"); // 串口打印key press!
}else{ // 按键释放
/* 按键松开操作的功能 */
printf("key release!\r\n"); // 串口打印key release!
}
exti_interrupt_flag_clear(BSP_KEY_EXTI_LINE); // 清中断标志位
}
}
/**
* description: KEY_UP按下,实现某个LED灯的翻转
* param {unsigned int} LED_port
* param {unsigned int} LED_pin
* return {*}
*/
int num=0;
/**
* description: KEY_UP按键按下一次LED灯点亮一个,按下4次以后所有LED灯全点亮
* return {*}
*/
void bsp_key_fun1(void)
{
if(gpio_input_bit_get(GPIOA, GPIO_PIN_0)==1)
{
delay_1ms(20);
if(gpio_input_bit_get(GPIOA,GPIO_PIN_0)==1)
{
switch (num % 4)
{
case 0: gpio_bit_toggle(LED1_port, LED1_pin);break;
case 1: gpio_bit_toggle(LED2_port, LED2_pin);break;
case 2: gpio_bit_toggle(LED3_port, LED3_pin);break;
case 3: gpio_bit_toggle(LED4_port, LED4_pin);break;
}
while(gpio_input_bit_get(GPIOA,GPIO_PIN_0) == SET);
num++;
}
}
}
/*
* Author: carpe diem 2116815480@qq.com
* Date: 2023-03-30 13:45:39
* LastEditTime: 2023-04-04 20:29:14
* FilePath: \Projectf:\Desktop\file\KEIL CODE\梁山派开发板\007外部中断点灯\Hardware\key\bsp_key.h
* Description: bsp_key.h 外部中断点灯
*
* Copyright (c) 2023 by carpe diem, All Rights Reserved.
*/
#ifndef _BSP_KEY_H_
#define _BSP_KEY_H_
#include "gd32f4xx.h"
#include "systick.h"
#define LED1_port GPIOE
#define LED1_pin GPIO_PIN_3
#define LED2_port GPIOD
#define LED2_pin GPIO_PIN_7
#define LED3_port GPIOG
#define LED3_pin GPIO_PIN_3
#define LED4_port GPIOA
#define LED4_pin GPIO_PIN_5
#define BSP_KEY_RCU RCU_GPIOA // 按键端口时钟
#define BSP_KEY_PORT GPIOA // 按键端口
#define BSP_KEY_PIN GPIO_PIN_0 // 按键引脚
#define BSP_KEY_EXTI_IRQN EXTI0_IRQn // 外部中断0
#define BSP_KEY_EXTI_PORT_SOURCE EXTI_SOURCE_GPIOA // 外部中断端口资源
#define BSP_KEY_EXTI_PIN_SOURCE EXTI_SOURCE_PIN0 // 外部中断引脚资源
#define BSP_KEY_EXTI_LINE EXTI_0 // 外部中断
#define BSP_KEY_EXTI_IRQHANDLER EXTI0_IRQHandler // 外部中断函数名
void key_gpio_config(void);
/**
* description: KEY_UP按下,实现某个LED灯的翻转
* param {unsigned int} LED_port
* param {unsigned int} LED_pin
* return {*}
*/
void bsp_key_fun1(void);
#endif
/*!
\file main.c
\brief led spark with systick
\version 2016-08-15, V1.0.0, firmware for GD32F4xx
\version 2018-12-12, V2.0.0, firmware for GD32F4xx
\version 2020-09-30, V2.1.0, firmware for GD32F4xx
\version 2022-03-09, V3.0.0, firmware for GD32F4xx
*/
/*
Copyright (c) 2022, 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 "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include "main.h"
#include "bsp_led.h"
#include "sys.h"
#include "bsp_usart.h"
#include "bsp_key.h"
/*!
\brief main function
\param[in] none
\param[out] none
\retval none
*/
int main(void)
{
systick_config();
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);/* 使能NVIC中断 中断分组为2位抢占优先级,2位子优先级 */
led_gpio_config();//led的初始化
key_gpio_config();//按键的初始化
//gpio_bit_set(GPIOD,GPIO_PIN_7);
usart_gpio_config(9600);
while(1)
{
}
}