GPIO的简单介绍
内部结构

- 施密特触发器(TTL肖特基触发器) 的工作原理:
施密特触发电路(简称)是一种波形整形电路,当任何波形的信号进入电路时,输出在正、负饱和之间跳动,产生方波或脉波输出。
不同于比较器,施密特触发电路有两个临界电压且形成一个滞后区,可以防止在滞后范围内之噪声干扰电路的正常工作。**如遥控接收线路,传感器输入电路都会用到它整形。

GPIO的工作模式 (八种)
(十分重要,面试会考!!!)
|------------|--------|--------------------------------------------------------------------------|
| 模式 | 类型 | 模式介绍 |
| 浮空输入 | 数字输入 | 浮空输入状态下, IO 的电平状态是不确定的,完全由外部输入决定,如果在该引脚悬空的情况下,读取该端口的电平是不确定的 |
| 上拉输入 | 数字输入 | IO 口在无输入的情况下,保持高电平 |
| 下拉输入 | 数字输入 | IO 口在无输入的情况下,保持低电平 |
| 模拟输入 | 模拟输入 | 输入信号不经施密特触发器直接接入,输入信号为模拟量而非数字量,其余输入方式输入数字量 |
| 开漏输出 | 数字输出 | 只能输出低电平 |
| 推挽输出 | 数字输出 | 可以输出高、低电平 |
| 复用开漏输出 | 数字输出 | 参考复用推挽 |
| 复用推挽输出 | 数字输出 | 此时 IO 受内部外设控制,比如定时器的 PWM ,比如 SPI 的 MOSI , MISO 等。 而普通的推挽输出,则 IO 受 ODR 控制 |
- 上拉/下拉/浮空输入

- 推挽/开漏/复用推挽/复用开漏输出

|------------------|-------------------------|
| 推挽输出 | 开漏输出 |
| 既能输出高电平,又能输出低电平。 | 只能输出低电平,若要输出高电平则外接上拉电阻。 |
[推挽和开漏的区别]
- 复用功能的配置:

GPIO的寄存器

常用的是前五种寄存器:
- GPIOx_CRL:

|------------------|------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| CNFy[1:0] | 位: 31:30 27:26 23:22 19:18 15:14 11:10 7:6 3:2 | CNFy[1:0]:端口x配置位(y = 0...7) (Port x configuration bits) 软件通过这些位配置相应的I/O端口,请参考表17端口位配置表。 * 在输入模式(MODE[1:0]=00): 00:模拟输入模式 01:浮空输入模式(复位后的状态) 10:上拉/下拉输入模式 11:保留 * 在输出模式(MODE[1:0]>00): 00:通用推挽输出模式 01:通用开漏输出模式 10:复用功能推挽输出模式 11:复用功能开漏输出模式 |
| MODEy[1:0] | 位: 29:28 25:24 21:20 17:16 13:12 9:8, 5:4 1:0 | MODEy[1:0]:(输出的频率)端口x的模式位(y = 0...7) (Port x mode bits)软件通过这些位配置相应的I/O端口,请参考表17端口位配置表。 00:输入模式(复位后的状态) 01:输出模式,最大速度10MHz 10:输出模式,最大速度20MHz 11:输出模式,最大速度50MHz |
**说明:**这个寄存器有32位,其中一个GPIOA口,例如:GPIOA1需要 4位进行控制,因此,这个寄存器可以控制 8 个 GPIOA的口。但是一个单片机中GPIOA外设最多可以有16个口,因此还需要一个寄存器:GPIOx_CRH。
- GPIOx_CRH:(与GPIOx_CRL同理)

- GPIOx_IDR:

**说明:**表示的是GPIO的16的端口,写1输入高电平,写0输入低电平。
- GPIOx_ODR:

**说明:**表示的是GPIO的16的端口,写1输出高电平,写0输出低电平。
GPIOx_BSRR(位设置/清除寄存器):

**说明:**置1清除位置0,设置位置1。置0保留。
GPIO口的库函数

模块:LED灯
LED的介绍
定义: LED是发光二极体(Light Emitting Diode, LED)的简称,也被称作发光二极管,正向通电点亮,反向通电不亮。
**分类:**引脚二极管 和 贴边二极管
|----------------------------------------------------------------------------|----------------------------------------------------------------------------|
| |
|
判断正负级:
|--------------------|--------------------------------------------------|
| 引脚二极管 | 贴片二极管 |
| 方式1:引脚长为正极,短引脚为负极 | 有记号的是负极 |
| 方式2:灯里面宽的是负极,窄的是正极 | "T"字形:一横的一边是正极,另一边是负极;三角形符号:"边"靠近的是正极,"角"靠近的是负极。 |
接线方式:

在上官二号开发板上的LED的原理图:

接到上官二号芯片上的端口:

小实验1:点亮一颗LED灯
- led.c文件中代码
cs
#include "led.h"
#include "stm32f1xx.h" // 外设的驱动函数
//初始化GPIO口
void led_init(void){
//打开时钟
__HAL_RCC_GPIOB_CLK_ENABLE();
//调用GPIO的初始化函数
GPIO_InitTypeDef GPIO_Initstruct;
GPIO_Initstruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_Initstruct.Pin = GPIO_PIN_8;
GPIO_Initstruct.Pull = GPIO_PULLUP;
GPIO_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_Initstruct);
//关闭LED
led1_off();
}
//点亮LED1的函数
void led1_on(void){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET);
}
//反转LED1状态的函数
void led1_toggle(void){
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
}
//熄灭LED1状态的函数
void led1_off(void){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET);
}
- main.c文件中
cs
#include "sys.h"
#include "led.h"
#include "delay.h"
void led_init(void); /* LED初始化函数声明 */
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
led_init(); /* LED初始化 */
while(1)
{
led1_on();
delay_ms(500);
led1_off();
delay_ms(500);
}
}
小实验2:流水灯
- led.c文件中的代码:
cs
#include "led.h"
#include "stm32f1xx.h" // 外设的驱动函数
//初始化GPIO口
void led_init(void){
//打开时钟
__HAL_RCC_GPIOB_CLK_ENABLE();
//调用GPIO的初始化函数
GPIO_InitTypeDef GPIO_Initstruct;
GPIO_Initstruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_Initstruct.Pin = GPIO_PIN_8|GPIO_PIN_9;
GPIO_Initstruct.Pull = GPIO_PULLUP;
GPIO_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_Initstruct);
//关闭LED
led1_off();
led2_off();
}
//点亮LED1的函数
void led1_on(void){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET);
}
//反转LED1状态的函数
void led1_toggle(void){
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
}
//熄灭LED1状态的函数
void led1_off(void){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET);
}
//点亮LED2的函数
void led2_on(void){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET);
}
//反转LED2状态的函数
void led2_toggle(void){
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);
}
//熄灭LED2状态的函数
void led2_off(void){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET);
}
- mian.c中的文件
cs
#include "sys.h"
#include "led.h"
#include "delay.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
led_init(); /* LED初始化 */
while(1)
{
led1_on();
led2_off();
delay_ms(500);
led1_off();
led2_on();
delay_ms(500);
}
}
模块:蜂鸣器
蜂鸣器的介绍
实物图:

低电平触发驱动电路:
- 工作电压:3.3v
- 电平:将I/O引脚拉低

|-------------------------------------------|-------------------------------------------------------------------------------------------------|
| 有源蜂鸣器 | 无源蜂鸣器 |
| 内部带有震荡源,只要给它通电,它就会发出声音,但是声音音调是单一的,频率是固定的。 | 内部没有震荡源,需要用一定频率的方波(常见的频率范围在2K到5K之间)去驱动它才可以发声。 由于可以通过不同频率的方波驱动,其声音频率是可控的,可以模拟出多种声音效果,甚至达到唱歌的 效果。 |
[有源蜂鸣器和无源蜂鸣器的区别]
小实验:让蜂鸣器响起来
- beep.c文件
cs
#include "stm32f1xx.h"
#include "beep.h"
//初始化GPIO口
void GPIO_Init(void){
//打开时钟
__HAL_RCC_GPIOB_CLK_ENABLE();
//打开GPIO口
GPIO_InitTypeDef GPIO_Initstruct;
GPIO_Initstruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_Initstruct.Pin = GPIO_PIN_8;
GPIO_Initstruct.Pull = GPIO_NOPULL;
GPIO_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB,&GPIO_Initstruct);
//关闭蜂鸣器
beep_off();
}
//打开蜂鸣器函数
void beep_on(void){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET);
}
//关闭蜂鸣器
void beep_off(void){
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET);
}
//反转蜂鸣器
void beep_toggle(void){
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
}
- mian.c文件
cs
#include "sys.h"
#include "delay.h"
#include "beep.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
GPIO_Init(); /*GPIO口初始化 */
while(1)
{
beep_on();
delay_ms(500);
beep_off();
delay_ms(500);
}
}
模块:按键
按键的介绍
实物图:

按键抖动:

定义: 按键抖动是指在按键开关被按下或释放的瞬间,由于机械触点的弹性作用或电信号的短暂波动,导致开关状态不稳定,出现短暂的抖动现象。这种抖动现象会影响按键的识别和处理,可能导致按一次键而输入多次,影响设备的正常运行和用户的使用体验。
软件消除:通过延时跳过抖动的时间段,再判断IO输入电平。
上官二号按键的电路图:

接到上官二号板的PA0和PA1口上:

小实验:按键控制LED灯
- led.文件和点亮led的代码相同。
- key.c文件
cs
#include "key.h"
#include "stm32f1xx.h"
#include "Delay.h"
//初始化GPIO口
void key_init(void){
//打开时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
//调用GPIO初始化函数
GPIO_InitTypeDef GPIO_Initstruct;
GPIO_Initstruct.Mode = GPIO_MODE_INPUT;
GPIO_Initstruct.Pin = GPIO_PIN_0|GPIO_PIN_1;
GPIO_Initstruct.Pull = GPIO_PULLUP;
GPIO_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&GPIO_Initstruct);
}
//按键检测函数
uint8_t key_scan(void){
//检测按键是否按下,消除抖动
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
delay_ms(15);//消除抖动
//再次判断按键是否按下
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
//如果按键按下,等待按键松开
while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET);
//返回按键的值
return 1;
}
}
//检测按键是否按下,消除抖动
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){
delay_ms(15);//消除抖动
//再次判断按键是否按下
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){
//如果按键按下,等待按键松开
while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET);
//返回按键的值
return 2;
}
}
//返回默认值
return 0;
}
- main.c文件
cs
#include "sys.h"
#include "led.h"
#include "delay.h"
#include "key.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
led_init(); /* LED初始化 */
key_init(); /* KEY初始化 */
uint8_t key_num = 0;
while(1)
{
key_num = key_scan();
if(key_num == 1){
led1_toggle();
}
if(key_num == 2){
led2_toggle();
}
}
}
中断
中断的相关概念
什么是中断?
答: 中断是单片机正在执行程序时,由于内部或外部事件的触发,打断当前程序,转而去处理这一事件,当处理完成后再回到原来被打断的地方继续执行原程序的过程。

产生中断的来源:
在ARM体系结构中,中断通常由外设或外部输入产生,有时也可以由软件触发。中断是单片机系统处理紧急或突发事件的重要方式,如定时器溢出、按键输入、串口数据到达等
中断的意义:
中断的主要意义在于提高CPU的效率,而不会一直占用CPU,实现对突发事件的实时处理,以及实现程序的并行化和嵌入式系统进程之间的切换。相较于轮询方式(即按照一定的频率和周期不断地检测某些事件的发生) ,中断 在处理一些偶然发生的事情时效率更高。
中断嵌套:
如果一个高优先级的中断发生,它会立即打断当前正在处理的中断(如果其优先级较低),并首先处理这个高优先级的中断,这就是所谓的中断嵌套。

中断的执行流程:
当中断发生时,STM32的执行流程如下 :首先, 由外设发出中断请求;然后, 处理器暂停当前执行的任务,保护现场(如将当前位置的PC地址压栈);**接着,**程序跳转到对应的中断服务程序(ISR)并执行;中断服务程序执行完毕后,恢复现场(如将栈顶的值送回PC);**最后,**处理器返回到被中断的位置,继续执行下一个指令。

STM32中断
STM32F103C8T6中断数量
- STM32F103C8T6 支持的中断共有 70****个,其中包括 10****个内核中断和60 个外部中断。
- 外部中断包含 :EXTI、TIM、USART、ADC、I2C、SPI
- 内核中断包含:

中断向量表
定义: STM32的中断向量表是一个存储中断处理函数地址的数组,位于Flash区的起始位置。每个数组元素对应一个中断源,其地址指向相应的中断服务程序。当中断发生时,处理器会根据中断号查找向量表,然后跳转到对应的中断服务程序执行。

中断向量表的作用: 是解决中断函数地址不固定与中断必须跳转到固定地方执行程序之间的矛盾。由于编译器每次编译都会为中断函数随机分配地址 ,但硬件要求 :中断必须跳转到固定的位置,因此,中断向量表就作为这样一个固定的地址列表,其中包含了中断函数的地址以及跳转到这些地址的程序。当中断发生时,处理器会跳转到这个固定的中断向量表,然后根据其中的信息跳转到相应的中断处理函数,从而执行中断。
STM32中断框图


NVIC
NVIC介绍
- NVIC,即Nested Vectored Interrupt Controller**(嵌套向量中断控制器),是STM32中的中断控制器。它负责管理和协调处理器的中断请求**,是STM32中处理异步事件的重要机制。
- NVIC 支持:256 个中断(16内核+240外部)。每个 ISER 寄存器处理 32 个中断,如果有 8 个 ISER 寄存器,就能处理 256 个中断。因此,也支持:256个优先级,允许裁剪。(STM32F103C8T6有10个内核和60个外部中断)。
NVIC的工作原理
NVIC提供了灵活、高效、可扩展的中断处理机制,支持 多级优先级、多向中断、嵌套向量中断等特性。当一个中断请求到达时,NVIC会确定其优先级并决定是否应该中断当前执行的程序,以便及时响应和处理该中断请求。这种设计有助于提高系统的响应速度和可靠性,特别是在需要处理大量中断请求的实时应用程序中。

由上面的流程图可知NVIC的的作用:
- 中断使能/失能控制;
- 中断优先级控制;
- 优先级分组控制。
中断优先级(抢占、响应和自然优先级)
|--------------------------------------------------------------|-------------------------------------------------------------|------------------------------------------------------------------|
| 抢占优先级 | 响应优先级 (抢占优先级相同) | 自然优先级 (抢占和响应优先级相同) |
| 如果一个中断的抢占优先级高于当前正在执行的中断,那么它可以打断当前中断,优先得到执行。 数值越小,优先级越高 。 | 如果两个中断同时到达,且它们的抢占优先级相同,那么响应优先级高的中断将首先得到响应。 数值越小,优先级越高 。 | 自然优先级是由硬件固定并预先设定的,用户无法更改。当抢占优先级和响应优先级都相同时,自然优先级将决定哪个中断先得到处理。 |
优先级的执行顺序:
多个中断同时发生时,
- 执行顺序首先由 抢占优先级决定 。如果抢占优先级相同,则进一步由响应优先级决定。如果响应优先级也相同,则最终由自然优先级决定。
中断嵌套的情况下,
- 高抢占优先级的中断可以打断低抢占优先级的中断;(图例如下)
- 但高响应优先级的中断不能打断低响应优先级的中断(当它们具有相同的抢占优先级时)。(图例如下)
优先级分组(对抢占和响应优先级分组)
优先级寄存器(IPR): 8位。但实际上只用到高4位(由AIRCR寄存器控制) 如下面表格所示**。**用于决定抢占优先级、响应优先级的等级。
|-----------|-------------------|---------------|---------------|
| 优先级分组 | AIRCR[10:8] | 抢占优先级 | 响应优先级 |
| 0 | 111 | 0 位,取值为 0 | 4 位,取值为 0~15 |
| 1 | 110 | 1 位,取值为 0~1 | 3 位,取值为 0~7 |
| 2 | 101 | 2 位,取值为 0~3 | 2 位,取值为 0~3 |
| 3 | 100 | 3 位,取值为 0~7 | 1 位,取值为 0~1 |
| 4 | 011 | 4 位,取值为 0~15 | 0 位,取值为 0 |
NVIC寄存器

NVIC相关函数介绍

- voidHAL_NVIC_DisableIRQ(IRQn_Type IRQn)
**ICER:**外设中断使能寄存器
- void HAL_NVIC_EnableIRQ(IRQn_Type IRQn)
**ISER:**外设中断失能寄存器
- void HAL_NVIC_SetPriority()
**IPR:**外设优先级寄存器
**SHPR:**内核外设优先级寄存器
- void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
**AIRCR :**优先级分组寄存器
NVIC配置方法
- 设置中断分组 → 设置中断优先级 → 使能中断
- 设置中断分组一般在HAL_Init 函数中进行
EXTI
EXIT介绍
定义: EXTI 是 External Interrupt 的缩写,表示外部 中断****事件控制器。EXTI 可以监测指定 GPIO 口的电平信号变化,并在检测到指定条件时,向内核的中断控制器 NVIC 发出中断申请。NVIC 在裁决后,如果满足条件,会中断CPU的主程序,使 CPU 转而执行 EXTI 对应的中断服务程序。
|----------------------------------------------------------------------------|----------------------------------------------------------------------------|----------------------------------------------------------------------------|------------|
| 上升沿 | 下降沿 | 双边沿 | 软件触发 |
| |
|
| ---------- |
[EXTI触发方式]
注意事项:
- EXTI 支持所有的 GPIO 口,但需要注意的是,相同的 Pin 不能同时触发中断。例如,PA0 和 PB0 不能同时被配置为中断源。
- EXTI 提供了 16 个 GPIO_Pin 的中断线,以及额外的中断线如 PVD 输出、 RTC 闹钟、 USB 唤醒 和以太网唤醒(互联型芯片才具备)。
中断/事件

中断: 上图中的线路①; 中断会打断CPU当前正在执行的程序,转而去执行中断服务程序,待中断服务程序执行完毕后,CPU会返回到原来的程序执行点继续执行。
事件: 上图中线路③;事件只是简单地表示某个动作或状态的变化,而不会打断CPU当前正在执行的程序。
事件触发中断:上图线路②;当事件发生时,它会根据配置来决定是否触发相应的中断。如果开放了对应的中断屏蔽位,事件就可以触发相应的中断**,**否则事件只会作为一个信号存在,不会被CPU处理。
EXTI基本结构

EXTI基本框图

EXTI寄存器
(参考手册上)

- 中断屏蔽寄存器(EXTI_IMR)

|-----------------|----------------------------------------------------------------------------------------------------------------------------|
| 位 31:20 | 保留,必须始终保持为复位状态(0)。 |
| 位19:0 | MRx : 线 x 上的中断屏蔽 (Interrupt Mask on line x) 0 :屏蔽来自线 x 上的中断请求; 1 :开放来自线 x 上的中断请求。 注:位 19 只适用于互联型产品,对于其它产品为保留位。 |
- 上升沿触发选择寄存器(EXTI_RTSR)

- 下降沿触发选择寄存器(EXTI_FTSR)

- 挂起寄存器(EXTI_PR)

EXTI相关函数介绍
EXTI函数没有专门的.c文件。包含在stm32f1xx_hal_gpio.c文件中

- 初始化函数:HAL_GPIO_Init()




- void HAL_GPIO_EXTI_IRQHandler (uint16_t GPIO_Pin) :中断服务函数

**函数①:**操作PR寄存器

**函数②:**回调函数(写服务代码)

AFIO
AFIO的介绍
AFIO 是(Alternate Function Input/Output)的缩写,表示复用功能****IO ,主要用于实现 I/O****端口的复用功能 以及外部中断的控制(本章主要介绍这个功能)。
AFIO与IO口的关系
(参考手册中的示意图)

简化图:

注意: 由上面两个框可知,EXTI 支持所有的 GPIO 口,但需要注意的是,相同的 Pin 不能同时触发中断。例如,PA0 和 PB0 不能同时被配置为中断源。
AFIO外部中断配置寄存器
总共由四个外部中断配置寄存器,如下所示:

每个寄存器控制只有前八位有效,一个控制四个EXTI口,对应四个GPIO口,如下所示,

AFIO函数
在HAL_GPIO_Init( ) 函数里:

对AFIO寄存器进行配置:

EXTI配置流程

小实验:按键点灯(中断法)
实验目的 :使用中断的方法,按下 KEY1 翻转 LED1 状态,而 LED2 一直保持 500ms 的频率闪烁。
注意事项:
- 按键驱动一般用轮询法 (即按照一定的频率和周期不断地检测某些事件的发生**,**主要由CPU直接负责),不需要中断来控制(因为按键要消抖,使用延时函数)。
- 中断服务函数:不能 加延时函数,阻塞性函数,不能操作硬件(简单的可以,例如GPIO口;OLED显示屏等复杂的不允许),写Printf()函数有时也会导致异常,中断服务函数中一般设置标志位,在主函数中看到标志有变化,做到业务的处理。
exti.c文件中代码:
cs
#include "exti.h"
#include "stm32f1xx.h"
#include "delay.h"
#include "led.h"
void exti_init(void){
//打开时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
//初始化GPIO口
GPIO_InitTypeDef GPIO_Initstruct;
GPIO_Initstruct.Mode =GPIO_MODE_IT_FALLING;
GPIO_Initstruct.Pin = GPIO_PIN_0;
GPIO_Initstruct.Pull = GPIO_PULLUP;
GPIO_Initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA,&GPIO_Initstruct);
//在HAL_Init()函数中优先级分组,默认第四种,实际用二种
HAL_NVIC_SetPriority(EXTI0_IRQn,2,0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}
//中断服务函数,
void EXTI0_IRQHandler(void){
//调用GPIO的中断公共函数,不同外设不同
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
}
//回调函数,业务代码:按键;消抖
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
//消抖
delay_ms(20);
if(GPIO_Pin == GPIO_PIN_0){ //判断EXTI0是否是GPIO_PIN0
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){//判断GPIO口是否是低电平
led1_toggle();
}
}
}
main.c文件中的代码:
cs
#include "sys.h"
#include "led.h"
#include "delay.h"
#include "exti.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
led_init(); /* LED初始化 */
exti_init();
while(1)
{
led2_on();
delay_ms(500);
led2_off();
delay_ms(500);
}
}