目录
- 1.readme
- 2.配置外部中断
-
- [2.1 配置外部中断的详细步骤](#2.1 配置外部中断的详细步骤)
- [2.2 分组配置](#2.2 分组配置)
- [2.3 外部中断初始化](#2.3 外部中断初始化)
-
- [2.3.1 misc.h文件](#2.3.1 misc.h文件)
- [2.3.2 代码实现](#2.3.2 代码实现)
- [2.4 中断模式初始化](#2.4 中断模式初始化)
-
- [2.4.1 exti.h文件](#2.4.1 exti.h文件)
- [2.4.2 代码实现](#2.4.2 代码实现)
- [2.4 中断函数](#2.4 中断函数)
-
- [2.4.1 stm32f10x_it.c与启动文件(md.s)文件](#2.4.1 stm32f10x_it.c与启动文件(md.s)文件)
- [2.4.2 代码实现](#2.4.2 代码实现)
- 3.项目代码
-
- [3.1 main.c](#3.1 main.c)
- [3.2 延时函数](#3.2 延时函数)
-
- [3.2.1 bsp_delay.c](#3.2.1 bsp_delay.c)
- [3.2.2 bsp_delay.h](#3.2.2 bsp_delay.h)
- [3.3 LED函数](#3.3 LED函数)
-
- [3.3.1 bsp_gpio_led.c](#3.3.1 bsp_gpio_led.c)
- [3.3.2 bsp_gpio_led.h](#3.3.2 bsp_gpio_led.h)
- [3.4 key函数](#3.4 key函数)
-
- [3.4.1 bsp_gpio_key.c](#3.4.1 bsp_gpio_key.c)
- [3.4.2 bsp_gpio_key.h](#3.4.2 bsp_gpio_key.h)
- [3.5 中断函数](#3.5 中断函数)
- 4.实验现象
1.readme
/*********************************************************************************************/
【 * 】程序简介
-工程名称 :按键中断信号点灯
-实验平台 :野火 STM32F103C8T6-STM32开发板
【 !】功能简介:
使用按键中断来点灯,了解到中断的响应速度以及对比于轮询
【 !】实验操作:
1.编译并下载程序到开发板即可观察现象;
2.每一个用户按键控制控制一个灯。
3.KEY1控制红灯是表现为反应灵敏,KEY2控制绿灯时表现为偶尔响应
/*********************************************************************************************/
【*】 引脚分配
KEY1->PA0 按键按下为高电平 核心板板载默认下拉且硬件有消抖
KEY2->PC13 按键按下为高电平 核心板板载默认下拉且硬件有消抖
NRST->NRST 按键按下为低电平 核心板板载默认上拉且硬件有消抖
LED_R->LED1->PA1 核心板板载LED灯低电平亮
LED_G->LED2->PA2 核心板板载LED灯低电平亮
/*********************************************************************************************/
【*】程序描述:
< main.c >
1.初始化LED和KEY对应的GPIO,初始化LED和KEY端口
2.KEY1控制红灯,使用输入中断的形式进行检测
3.KEY2控制绿灯,使用轮询的形式进行检测
4.while循环内,增加轮询的间隔(用于模拟庞大的代码执行量)
/*********************************************************************************************/
2.配置外部中断
2.1 配置外部中断的详细步骤
1.在main定义NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);函数
2.配置2个初始化,外部中断初始化,模式初始化
外部中断初始化配置:
2.1开启AFIO时钟
2.2定义NVIC,嵌套向量中断控制器,结构体变量
NVIC_InitTypeDef nvic_initstauct = { 0 };
2.3它有四个成员分别是:配置中断向量号,配置抢占优先级,配置响应优先级,配置开启/关闭中断
2.4最终通过函数NVIC_Init(&nvic_initstauct);将配置好了进行初始化
3.中断模式初始化
3.1 开启AFIO时钟
3.2 选择中断信号源
3.3 定义EXTI,结构体
EXTI_InitTypeDef exti_initstauct = { 0 };
3.4它有四个成员分别是:选择EXTI线,选择模式(中断模式/事件模式),选择触发方式(上升沿,下降沿,双边沿),开启中断
3.5 最终通过函数EXTI_Init(&exti_initstauct);将配置好了进行初始化
4.在it.c官方提供的源文件里面写中断函数
4.1函数名的选择
在启动文件md.s中找到 Line 对应的中断函数名
md.s中文件的内容:
DCD EXTI0_IRQHandler ; EXTI Line 0
DCD EXTI1_IRQHandler ; EXTI Line 1
DCD EXTI2_IRQHandler ; EXTI Line 2
DCD EXTI3_IRQHandler ; EXTI Line 3
DCD EXTI4_IRQHandler ; EXTI Line 4
DCD EXTI9_5_IRQHandler ; EXTI Line 9...5
DCD EXTI15_10_IRQHandler ; EXTI Line 15...10
我在配置第二个结构体(中断模式配置)的第一个参数是选择EXTI线,就需要在md.s文件中找到对应的函数名,因为我选择的是EXTI Line 0,所以对应的函数名是:EXTI0_IRQHandler 作为中断函数的函数名,这个是固定的名字。EXTI Line X与函数名一 一对应
4.2 获取指定的EXTI Line 线号中断的标志位
if (EXTI_GetITStatus(EXTI Line 0) == SET),如果进入中断就设置1,否则是0,SET是1,结束后需要使用函数EXTI_ClearITPendingBit(EXTI Line 0); 将指定的EXTI Line 中断标注清除0。
2.2 分组配置
在main函数对抢占优先级和响应优先级的分组设置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);


可以看到官方提供的中断函数在misc.c文件中,找到这个函数
c
/**
* @brief Configures the priority grouping: pre-emption priority and subpriority.
* @param NVIC_PriorityGroup: specifies the priority grouping bits length.
* This parameter can be one of the following values:
* @arg NVIC_PriorityGroup_0: 0 bits for pre-emption priority
* 4 bits for subpriority
* @arg NVIC_PriorityGroup_1: 1 bits for pre-emption priority
* 3 bits for subpriority
* @arg NVIC_PriorityGroup_2: 2 bits for pre-emption priority
* 2 bits for subpriority
* @arg NVIC_PriorityGroup_3: 3 bits for pre-emption priority
* 1 bits for subpriority
* @arg NVIC_PriorityGroup_4: 4 bits for pre-emption priority
* 0 bits for subpriority
* @retval None
*/
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
/* Check the parameters */
assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
/* Set the PRIGROUP[10:8] bits according to NVIC_PriorityGroup value */
SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}
这个函数用来设置抢占优先级和响应优先级数量
下面对函数上面的注释进行说明:
@brief Configures the priority grouping: pre-emption priority and subpriority.
这个函数的功能概述,告诉开发者这个函数是用来划分抢占优先级和子优先级的位数分配的。
@param NVIC_PriorityGroup: specifies the priority grouping bits length.
这个参数用来选择用哪一种分组方式,决定4个优先级位里,多少位给抢占,多少位给子优先级。
This parameter can be one of the following values:
@arg NVIC_PriorityGroup_0: 0 bits for pre-emption priority
4 bits for subpriority
@arg NVIC_PriorityGroup_1: 1 bits for pre-emption priority
3 bits for subpriority
@arg NVIC_PriorityGroup_2: 2 bits for pre-emption priority
2 bits for subpriority
@arg NVIC_PriorityGroup_3: 3 bits for pre-emption priority
1 bits for subpriority
@arg NVIC_PriorityGroup_4: 4 bits for pre-emption priority
0 bits for subpriority
解释说明:
- NVIC_PriorityGroup_0 :0位抢占优先级,4位子优先级 → 所有中断不能互相抢占,只按子优先级排队
- NVIC_PriorityGroup_1 :1位抢占优先级,3位子优先级 → 2级抢占,8级子优先级
- NVIC_PriorityGroup_2 :2位抢占优先级,2位子优先级 → 4级抢占,4级子优先级(常用)
- NVIC_PriorityGroup_3 :3位抢占优先级,1位子优先级 → 8级抢占,2级子优先级
- NVIC_PriorityGroup_4 :4位抢占优先级,0位子优先级 → 16级抢占,无子优先级(STM32 HAL库默认)
0bit
只有一种状态 0级
1bit
2的1次方 - 1得到1,等级范围:0 ~ 1级
2bit
2的2次方 - 1得到3,等级范围:0 ~ 3级
3bit
2的3次方 - 1得到7,等级范围:0 ~ 7级
4bit
2的4次方 - 1得到15,等级范围:0 ~ 15级
2.3 外部中断初始化
2.3.1 misc.h文件

c
typedef struct
{
uint8_t NVIC_IRQChannel; /*!< Specifies the IRQ channel to be enabled or disabled.
This parameter can be a value of @ref IRQn_Type
(For the complete STM32 Devices IRQ Channels list, please
refer to stm32f10x.h file) */
uint8_t NVIC_IRQChannelPreemptionPriority; /*!< Specifies the pre-emption priority for the IRQ channel
specified in NVIC_IRQChannel. This parameter can be a value
between 0 and 15 as described in the table @ref NVIC_Priority_Table */
uint8_t NVIC_IRQChannelSubPriority; /*!< Specifies the subpriority level for the IRQ channel specified
in NVIC_IRQChannel. This parameter can be a value
between 0 and 15 as described in the table @ref NVIC_Priority_Table */
FunctionalState NVIC_IRQChannelCmd; /*!< Specifies whether the IRQ channel defined in NVIC_IRQChannel
will be enabled or disabled.
This parameter can be set either to ENABLE or DISABLE */
} NVIC_InitTypeDef;
第一个参数:用来配置中断源,注释告诉我们在stm32f10x.h这个头文件里面去找


c
EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt */
EXTI1_IRQn = 7, /*!< EXTI Line1 Interrupt */ EXTI2_IRQn = 8, /*!< EXTI Line2 Interrupt */
EXTI3_IRQn = 9, /*!< EXTI Line3 Interrupt */
EXTI4_IRQn = 10, /*!< EXTI Line4 Interrupt */
EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */
EXTI15_10_IRQn = 40, /*!< External Line[15:10]Interrupts*/
例如使用
PA0,PB0,PC0,PD0都是设置EXTI0_IRQn
PA1,PB1,PC1,PD1都是设置EXTI1_IRQn
PA2,PB2,PC2,PD2都是设置EXTI2_IRQn
PA3,PB3,PC3,PD3都是设置EXTI3_IRQn
其中如果使用5-9,PA5,PB5,PC5一直到PA9,PB9,PC9设置EXTI9_5_IRQn
还有10到15就设置EXTI15_10_IRQn
第二个参数和第三个参数分别是对抢占优先级和响应优先级的序号设置,因为在在main函数设置的是分组4,而分组4其中抢占优先级是0~15级范围,响应优先级只有0级,所以第二个参数可以在0 ~15范围选择设置,第三个参数只能设置0,代表两个不同的序号,抢占优先级的序号,响应优先级的序号。
抢占优先级:可以插队,数字越小,优先插队执行
响应优先级:只有抢占优先级一样的时候,才看响应优先级,序号越小排在前面
举个最简单现实例子(秒懂)
教室里面有老师讲课(主程序)来了 3 个请假学生(中断):
A:抢占 0,响应 0
B:抢占 1,响应 2
C:抢占 1,响应 1
规则:
A 抢占最高 → A 可以直接打断 B、C(插队)
B 和 C 抢占都是 1 → 互相不能插队
那就看响应优先级:C 响应 1<B 响应 2
→ C 排在 B 前面,先处理 C
所以排队的顺序是A C B
参数四,用来设置开启或者关闭中断
ENABLE:开中断
DISABLE:关中断
2.3.2 代码实现
c
/**
* @brief 配置 KEY 中断配置
* @param 无
* @retval 无
*/
#define KEY1_EXTI_IRQ EXTI0_IRQn /*外部中断向量号*/
#define KEY2_EXTI_IRQ EXTI15_10_IRQn /*外部中断向量号*/
void KEY_NVIC_Config()
{
// 定义一个 NVIC 结构体
NVIC_InitTypeDef nvic_initstauct = { 0 };
/* 开启 AFIO 相关时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
#if 1
/*配置中断源*/
nvic_initstauct.NVIC_IRQChannel = KEY1_EXTI_IRQ;
/*配置抢占优先级*/
nvic_initstauct.NVIC_IRQChannelPreemptionPriority = 1;
/*配置响应优先级*/
nvic_initstauct.NVIC_IRQChannelSubPriority = 0;
/*开启中断*/
nvic_initstauct.NVIC_IRQChannelCmd = ENABLE;
// 把配置写入硬件,生效
NVIC_Init(&nvic_initstauct);
#endif
#if 1
/*配置中断源*/
nvic_initstauct.NVIC_IRQChannel = KEY2_EXTI_IRQ;
/*配置抢占优先级*/
nvic_initstauct.NVIC_IRQChannelPreemptionPriority = 1;
/*配置响应优先级*/
nvic_initstauct.NVIC_IRQChannelSubPriority = 0;
/*开启中断*/
nvic_initstauct.NVIC_IRQChannelCmd = ENABLE;
// 把配置写入硬件,生效
NVIC_Init(&nvic_initstauct);
#endif
}
这里还需要有这个函数
c
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
因为我需要用到外部中断,所以需要开启AFIO时钟。
2.4 中断模式初始化
2.4.1 exti.h文件

c
typedef struct
{
uint32_t EXTI_Line; /*!< Specifies the EXTI lines to be enabled or disabled.
This parameter can be any combination of @ref EXTI_Lines */
EXTIMode_TypeDef EXTI_Mode; /*!< Specifies the mode for the EXTI lines.
This parameter can be a value of @ref EXTIMode_TypeDef */
EXTITrigger_TypeDef EXTI_Trigger; /*!< Specifies the trigger signal active edge for the EXTI lines.
This parameter can be a value of @ref EXTITrigger_TypeDef */
FunctionalState EXTI_LineCmd; /*!< Specifies the new state of the selected EXTI lines.
This parameter can be set either to ENABLE or DISABLE */
}EXTI_InitTypeDef;
第一个参数配置中断线
意思:你要用哪一根外部中断线?
比如你用 PA0 → 填 EXTI_Line0
比如你用 PB1 → 填 EXTI_Line1

在exti.h文件中能找到这些Line的一些配置选择。
c
#define EXTI_Line0 ((uint32_t)0x00001) /*!< External interrupt line 0 */
#define EXTI_Line1 ((uint32_t)0x00002) /*!< External interrupt line 1 */
#define EXTI_Line2 ((uint32_t)0x00004) /*!< External interrupt line 2 */
#define EXTI_Line3 ((uint32_t)0x00008) /*!< External interrupt line 3 */
#define EXTI_Line4 ((uint32_t)0x00010) /*!< External interrupt line 4 */
#define EXTI_Line5 ((uint32_t)0x00020) /*!< External interrupt line 5 */
#define EXTI_Line6 ((uint32_t)0x00040) /*!< External interrupt line 6 */
#define EXTI_Line7 ((uint32_t)0x00080) /*!< External interrupt line 7 */
#define EXTI_Line8 ((uint32_t)0x00100) /*!< External interrupt line 8 */
#define EXTI_Line9 ((uint32_t)0x00200) /*!< External interrupt line 9 */
#define EXTI_Line10 ((uint32_t)0x00400) /*!< External interrupt line 10 */
#define EXTI_Line11 ((uint32_t)0x00800) /*!< External interrupt line 11 */
#define EXTI_Line12 ((uint32_t)0x01000) /*!< External interrupt line 12 */
#define EXTI_Line13 ((uint32_t)0x02000) /*!< External interrupt line 13 */
#define EXTI_Line14 ((uint32_t)0x04000) /*!< External interrupt line 14 */
#define EXTI_Line15 ((uint32_t)0x08000) /*!< External interrupt line 15 */
#define EXTI_Line16 ((uint32_t)0x10000) /*!< External interrupt line 16 Connected to the PVD Output */
#define EXTI_Line17 ((uint32_t)0x20000) /*!< External interrupt line 17 Connected to the RTC Alarm event */
#define EXTI_Line18 ((uint32_t)0x40000) /*!< External interrupt line 18 Connected to the USB Device/USB OTG FS
Wakeup from suspend event */
#define EXTI_Line19 ((uint32_t)0x80000) /*!< External interrupt line 19 Connected to the Ethernet Wakeup event */
第二个参数设置中断模式还是事件模式
c
typedef enum
{
EXTI_Mode_Interrupt = 0x00,
EXTI_Mode_Event = 0x04
}EXTIMode_TypeDef;
其中
EXTI_Mode_Interrupt 中断模式
EXTI_Mode_Event 事件模式
一般经常设置的是中断模式。
第三个参数配置触发方式
c
typedef enum
{
EXTI_Trigger_Rising = 0x08,
EXTI_Trigger_Falling = 0x0C,
EXTI_Trigger_Rising_Falling = 0x10
}EXTITrigger_TypeDef;
下降沿(按键按下):EXTI_Trigger_Falling
上升沿(松开):EXTI_Trigger_Rising
双边沿(变就触发):EXTI_Trigger_Rising_Falling
第四个参数配置开启或者关闭
ENABLE:开
DISABLE:关
2.4.2 代码实现
c
#define KEY1_EXTI_PortSource GPIO_PortSourceGPIOA /*端口源*/
#define KEY1_EXTI_PinSource GPIO_PinSource0 /*PIN脚源*/
#define KEY1_EXTI_LINE EXTI_Line0 /*外部中断线*/
#define KEY2_EXTI_PortSource GPIO_PortSourceGPIOC /*端口源*/
#define KEY2_EXTI_PinSource GPIO_PinSource13 /*PIN脚源*/
#define KEY2_EXTI_LINE EXTI_Line13 /*外部中断线*/
void KEY_Mode_Config(void)
{
/* 定义一个 EXTI 结构体 */
EXTI_InitTypeDef exti_initstauct = { 0 };
/* 开启 AFIO 相关时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
#if 1
/*选择中断信号源*/
GPIO_EXTILineConfig(KEY1_EXTI_PortSource, KEY1_EXTI_PinSource);
/* 1. 选择EXTI线0(对应PA0、PB0等引脚) */
exti_initstauct.EXTI_Line = KEY1_EXTI_LINE;
/* 2. 模式:中断模式 */
exti_initstauct.EXTI_Mode = EXTI_Mode_Interrupt;
/* 3. 触发:下降沿触发(按键按下常用) */
exti_initstauct.EXTI_Trigger = EXTI_Trigger_Falling;
/* 4. 开启外部中断 */
exti_initstauct.EXTI_LineCmd = ENABLE;
EXTI_Init(&exti_initstauct);
#endif
#if 0
/*选择中断信号源*/
GPIO_EXTILineConfig(KEY2_EXTI_PortSource, KEY2_EXTI_PinSource);
/* 1. 选择EXTI线0(对应PA0、PB0等引脚) */
exti_initstauct.EXTI_Line = KEY2_EXTI_LINE;
/* 2. 模式:中断模式 */
exti_initstauct.EXTI_Mode = EXTI_Mode_Interrupt;
/* 3. 触发:下降沿触发(按键按下常用) */
exti_initstauct.EXTI_Trigger = EXTI_Trigger_Falling;
/* 4. 开启外部中断 */
exti_initstauct.EXTI_LineCmd = ENABLE;
EXTI_Init(&exti_initstauct);
#endif
}
这里需要这两个代码
c
/* 开启 AFIO 相关时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
/*选择中断信号源*/
GPIO_EXTILineConfig(KEY1_EXTI_PortSource, KEY1_EXTI_PinSource);
选择中断信号源这个函数有两个参数
第一个参数选择端口号,第二个参数选择引脚
参数1选择
c
#define GPIO_PortSourceGPIOA ((uint8_t)0x00)
#define GPIO_PortSourceGPIOB ((uint8_t)0x01)
#define GPIO_PortSourceGPIOC ((uint8_t)0x02)
#define GPIO_PortSourceGPIOD ((uint8_t)0x03)
#define GPIO_PortSourceGPIOE ((uint8_t)0x04)
#define GPIO_PortSourceGPIOF ((uint8_t)0x05)
#define GPIO_PortSourceGPIOG ((uint8_t)0x06)
参数2选择
c
#define GPIO_PinSource0 ((uint8_t)0x00)
#define GPIO_PinSource1 ((uint8_t)0x01)
#define GPIO_PinSource2 ((uint8_t)0x02)
#define GPIO_PinSource3 ((uint8_t)0x03)
#define GPIO_PinSource4 ((uint8_t)0x04)
#define GPIO_PinSource5 ((uint8_t)0x05)
#define GPIO_PinSource6 ((uint8_t)0x06)
#define GPIO_PinSource7 ((uint8_t)0x07)
#define GPIO_PinSource8 ((uint8_t)0x08)
#define GPIO_PinSource9 ((uint8_t)0x09)
#define GPIO_PinSource10 ((uint8_t)0x0A)
#define GPIO_PinSource11 ((uint8_t)0x0B)
#define GPIO_PinSource12 ((uint8_t)0x0C)
#define GPIO_PinSource13 ((uint8_t)0x0D)
#define GPIO_PinSource14 ((uint8_t)0x0E)
#define GPIO_PinSource15 ((uint8_t)0x0F)
2.4 中断函数
2.4.1 stm32f10x_it.c与启动文件(md.s)文件

当触发中断的时候需要执行中断函数,此时的中断函数在官方提供的stm32f10x_it.c文件编写。


在md.s文件中找到这些
c
DCD EXTI0_IRQHandler ; EXTI Line 0
DCD EXTI1_IRQHandler ; EXTI Line 1
DCD EXTI2_IRQHandler ; EXTI Line 2
DCD EXTI3_IRQHandler ; EXTI Line 3
DCD EXTI4_IRQHandler ; EXTI Line 4
DCD EXTI9_5_IRQHandler ; EXTI Line 9..5
DCD EXTI15_10_IRQHandler ; EXTI Line 15..10
每一个Line x都有对应的中断函数名,这些函数名是固定的,当我使用EXTI Line 0时候中断函数名就是EXTI0_IRQHandler
2.4.2 代码实现
c
/**
* @brief This function handles KEY1 interrupt request.
* @param None
* @retval None
*/
#define KEY1_EXTI_LINE EXTI_Line0 /*外部中断线*/
#define KEY1_EXTI_IRQHANDLER EXTI0_IRQHandler /*中断处理函数*/
void KEY1_EXTI_IRQHANDLER(void)
{
if (EXTI_GetITStatus(KEY1_EXTI_LINE) == SET)
{
LED_TOGGLE(R_LED1_GPIO_PORT, R_LED1_GPIO_PIN);
EXTI_ClearITPendingBit(KEY1_EXTI_LINE);
}
}
因为我在配置中断模式时候,结构体变量第一个参数的时候,配置中断线 EXTI_Line0 对应的中断函数名是EXTI0_IRQHandler
接着在if里面有这个函数
c
(EXTI_GetITStatus(EXTI_Line0)
这个代码是用来获取0号中断线,是否触发中断标志位,返回值有两个
c
typedef enum {RESET = 0, SET = !RESET} FlagStatus, ITStatus;
RESET = 0
SET = 1
如果触发了中断,此时返回的是SET
当if成立就触发了中断
执行
c
LED_TOGGLE(R_LED1_GPIO_PORT, R_LED1_GPIO_PIN);
EXTI_ClearITPendingBit(KEY1_EXTI_LINE);
第一个函数是R灯亮灭过程
第二个参数用来清除0号线的中断标志位,为下一次进入中断做准备。
3.项目代码
3.1 main.c
c
/**
******************************************************************************
* @file main.c
* @author 作者
* @version v1.0
* @date 2026.4.19
* @brief 按键中断信号点灯标准库写法
******************************************************************************
* @attention
*
* 版权声明
*
******************************************************************************
*/
#include "led/bsp_gpio_led.h"
#include "delay/bsp_delay.h"
#include "key/bsp_gpio_key.h"
#include "stm32f10x.h"
/**
* @brief 主函数
* @param 无
* @note 无
* @retval 无
*/
int main (void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
LED_GPIO_Config();
KEY_Init();
while(1)
{
if (KEY_Scan(KEY2_GPIO_PORT, KEY2_GPIO_PIN, KEY_HIGH_TRIGGER) == KEY_DOWN)
{
LED_TOGGLE(G_LED2_GPIO_PORT, G_LED2_GPIO_PIN);
}
Rough_Delay_Ms(5000);
}
}
3.2 延时函数
3.2.1 bsp_delay.c
c
/**
******************************************************************************
* @file bsp_delay.c
* @author 作者
* @version v1.0
* @date 2026.4.19
* @brief 粗略阻塞延时函数接口
******************************************************************************
* @attention
*
* 版权声明
*
******************************************************************************
*/
#include "delay/bsp_delay.h"
/**
* @brief 粗略阻塞延时基本函数接口
* @param ncount:传入的计数值
* @note 软件延时函数,使用不同的系统时钟,延时不一样,还会存在函数调用以及其他计算损耗,只能粗略使用
* @retval 无
*/
void Rough_Delay(__IO uint32_t ncount)
{
for (uint32_t i = 0; i < ncount; i++)
{
__nop();
}
}
/**
* @brief 粗略阻塞延时基本函数接口 单位:US
* @param ncount:传入的计数值
* @note 软件延时函数,使用不同的系统时钟,延时不一样,还会存在函数调用以及其他计算损耗,只能粗略使用
* @retval 无
*/
void Rough_Delay_Us(__IO uint32_t time)
{
Rough_Delay(time);
}
/**
* @brief 粗略阻塞延时基本函数接口 单位:MS
* @param ncount:传入的计数值
* @note 软件延时函数,使用不同的系统时钟,延时不一样,还会存在函数调用以及其他计算损耗,只能粗略使用
* @retval 无
*/
void Rough_Delay_Ms(__IO uint32_t time)
{
Rough_Delay(time * 1000);
}
/**
* @brief 粗略阻塞延时基本函数接口 单位:S
* @param ncount:传入的计数值
* @note 软件延时函数,使用不同的系统时钟,延时不一样,还会存在函数调用以及其他计算损耗,只能粗略使用
* @retval 无
*/
void Rough_Delay_S(__IO uint32_t time)
{
Rough_Delay(time * 1000 * 1000);
}
3.2.2 bsp_delay.h
c
#ifndef __bsp_delay_H
#define __bsp_delay_H
#include "stm32f10x.h"
void Rough_Delay(__IO uint32_t ncount);
void Rough_Delay_Us(__IO uint32_t ncount);
void Rough_Delay_Ms(__IO uint32_t time);
void Rough_Delay_S(__IO uint32_t time);
#endif /*__bsp_delay_H*/
3.3 LED函数
3.3.1 bsp_gpio_led.c
c
/**
******************************************************************************
* @file bsp_gpio_led.c
* @author 作者
* @version v1.0
* @date 2026.4.19
* @brief LED灯的函数接口
******************************************************************************
* @attention
*
* 版权声明
*
******************************************************************************
*/
#include "led/bsp_gpio_led.h"
/**
* @brief 初始化控制 LED 的IO
* @param 无
* @retval 无
*/
void LED_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct = { 0 };
/*****************************核心板载LED****************************************/
#if 1
RCC_APB2PeriphClockCmd(R_LED1_GPIO_CLK_PORT, ENABLE); //开启GPIOA 端口时钟
GPIO_SetBits(R_LED1_GPIO_PORT, R_LED1_GPIO_PIN); //设置PA1端口初始化1 让LED灭
// 配置引脚:选择GPIOA的Pin1
GPIO_InitStruct.GPIO_Pin = R_LED1_GPIO_PIN;
// 配置输出速度:50MHz
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
// 配置工作模式:推挽输出
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(R_LED1_GPIO_PORT, &GPIO_InitStruct);
#endif
#if 1
RCC_APB2PeriphClockCmd(G_LED2_GPIO_CLK_PORT, ENABLE); //开启GPIOA 端口时钟
GPIO_SetBits(G_LED2_GPIO_PORT, G_LED2_GPIO_PIN); //设置PA2端口初始化1 让LED灭
// 配置引脚:选择GPIOA的Pin1
GPIO_InitStruct.GPIO_Pin = G_LED2_GPIO_PIN;
// 配置输出速度:50MHz
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
// 配置工作模式:推挽输出
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(G_LED2_GPIO_PORT, &GPIO_InitStruct);
#endif
#if 1
RCC_APB2PeriphClockCmd(B_LED3_GPIO_CLK_PORT, ENABLE); //开启GPIOA 端口时钟
GPIO_SetBits(B_LED3_GPIO_PORT, B_LED3_GPIO_PIN); //设置PA3端口初始化1 让LED灭
// 配置引脚:选择GPIOA的Pin1
GPIO_InitStruct.GPIO_Pin = B_LED3_GPIO_PIN;
// 配置输出速度:50MHz
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
// 配置工作模式:推挽输出
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(B_LED3_GPIO_PORT, &GPIO_InitStruct);
#endif
/*****************************用户自定义LED*******************************/
#if 0
RCC_APB2PeriphClockCmd(LED4_GPIO_CLK_PORT, ENABLE);
GPIO_SetBits(LED4_GPIO_PORT, LED4_GPIO_PIN);
// 配置引脚:选择GPIOA的Pin1
GPIO_InitStruct.GPIO_Pin = LED4_GPIO_PIN;
// 配置输出速度:50MHz
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
// 配置工作模式:推挽输出
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(LED4_GPIO_PORT, &GPIO_InitStruct);
#endif
#if 0
RCC_APB2PeriphClockCmd(LED5_GPIO_CLK_PORT, ENABLE);
GPIO_ResetBits(LED5_GPIO_PORT, LED5_GPIO_PIN);
// 配置引脚:选择GPIOA的Pin1
GPIO_InitStruct.GPIO_Pin = LED5_GPIO_PIN;
// 配置输出速度:50MHz
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
// 配置工作模式:推挽输出
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(LED5_GPIO_PORT, &GPIO_InitStruct);
#endif
#if 0
RCC_APB2PeriphClockCmd(LED6_GPIO_CLK_PORT, ENABLE);
GPIO_ResetBits(LED6_GPIO_PORT, LED6_GPIO_PIN);
// 配置引脚:选择GPIOA的Pin1
GPIO_InitStruct.GPIO_Pin = LED6_GPIO_PIN;
// 配置输出速度:50MHz
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
// 配置工作模式:推挽输出
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(LED6_GPIO_PORT, &GPIO_InitStruct);
#endif
}
/**
* @brief 开启对应 LED 灯
* @param GPIOX : x可以是 A,B,C等
* @param GPIO_PIN: 待操作的PIN
* @param led_brightstatus LED灯亮时的状态
* @retval 无
*/
void LED_GPIO_ON(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, LED_TriggerLever led_brightstatus)
{
if (led_brightstatus == LED_LOW_TRIGGER)
{
GPIO_ResetBits(GPIOx, GPIO_Pin);
}
else
{
GPIO_SetBits(GPIOx, GPIO_Pin);
}
}
/**
* @brief 关闭对应 LED 灯
* @param GPIOX : x可以是 A,B,C等
* @param GPIO_PIN: 待操作的PIN
* @param led_brightstatus LED灯亮时的状态
* @retval 无
*/
void LED_GPIO_OFF(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, LED_TriggerLever led_brightstatus)
{
if (led_brightstatus == LED_LOW_TRIGGER)
{
GPIO_SetBits(GPIOx, GPIO_Pin);
}
else
{
GPIO_ResetBits(GPIOx, GPIO_Pin);
}
}
/**
* @brief 翻转对应 LED 灯
* @param GPIOX : x可以是 A,B,C等
* @param GPIO_PIN: 待操作的PIN
* @retval 无
*/
void LED_TOGGLE(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
GPIOx->ODR ^= GPIO_Pin;
}
/***************************************END OF FILE *********************************************************/
3.3.2 bsp_gpio_led.h
c
#ifndef __bsp_gpio_led_H
#define __bsp_gpio_led_H
#include "stm32f10x.h"
/*定义连接GPIO端口,用户只需要修改下面代码即可改变控制的LED引脚 */
//LED1
#define LED1_GPIO_PORT GPIOA /* GPIO 端口 */
#define LED1_GPIO_CLK_PORT RCC_APB2Periph_GPIOA /* GPIO 端口时钟 */
#define LED1_GPIO_PIN GPIO_Pin_1 /* 对应PIN脚 */
//LED2
#define LED2_GPIO_PORT GPIOA /* GPIO 端口 */
#define LED2_GPIO_CLK_PORT RCC_APB2Periph_GPIOA /* GPIO 端口时钟 */
#define LED2_GPIO_PIN GPIO_Pin_2 /* 对应PIN脚 */
//LED3
#define LED3_GPIO_PORT GPIOA /* GPIO 端口 */
#define LED3_GPIO_CLK_PORT RCC_APB2Periph_GPIOA /* GPIO 端口时钟 */
#define LED3_GPIO_PIN GPIO_Pin_3 /* 对应PIN脚 */
//LED4
#define LED4_GPIO_PORT GPIOB /* GPIO 端口 */
#define LED4_GPIO_CLK_PORT RCC_APB2Periph_GPIOB /* GPIO 端口时钟 */
#define LED4_GPIO_PIN GPIO_Pin_5 /* 对应PIN脚 */
//LED5
#define LED5_GPIO_PORT GPIOB /* GPIO 端口 */
#define LED5_GPIO_CLK_PORT RCC_APB2Periph_GPIOB /* GPIO 端口时钟 */
#define LED5_GPIO_PIN GPIO_Pin_13 /* 对应PIN脚 */
//LED6
#define LED6_GPIO_PORT GPIOB /* GPIO 端口 */
#define LED6_GPIO_CLK_PORT RCC_APB2Periph_GPIOB /* GPIO 端口时钟 */
#define LED6_GPIO_PIN GPIO_Pin_14 /* 对应PIN脚 */
/***************核心板载LED灯****************************************/
//R_LED
#define R_LED1_GPIO_PORT LED1_GPIO_PORT /* GPIO 端口 */
#define R_LED1_GPIO_CLK_PORT LED1_GPIO_CLK_PORT /* GPIO 端口时钟 */
#define R_LED1_GPIO_PIN LED1_GPIO_PIN /* 对应PIN脚 */
//G_LED
#define G_LED2_GPIO_PORT LED2_GPIO_PORT /* GPIO 端口 */
#define G_LED2_GPIO_CLK_PORT LED2_GPIO_CLK_PORT /* GPIO 端口时钟 */
#define G_LED2_GPIO_PIN LED2_GPIO_PIN /* 对应PIN脚 */
//B_LED
#define B_LED3_GPIO_PORT LED3_GPIO_PORT /* GPIO 端口 */
#define B_LED3_GPIO_CLK_PORT LED3_GPIO_CLK_PORT /* GPIO 端口时钟 */
#define B_LED3_GPIO_PIN LED3_GPIO_PIN /* 对应PIN脚 */
/***************用户自定义宏****************************************/
//R_LED
#define R_LED_ON_ONLY LED_GPIO_ON(R_LED1_GPIO_PORT, R_LED1_GPIO_PIN, LED_LOW_TRIGGER); \
LED_GPIO_OFF(G_LED2_GPIO_PORT, G_LED2_GPIO_PIN, LED_LOW_TRIGGER); \
LED_GPIO_OFF(B_LED3_GPIO_PORT, B_LED3_GPIO_PIN, LED_LOW_TRIGGER);
//G_LED
#define G_LED_ON_ONLY LED_GPIO_OFF(R_LED1_GPIO_PORT, R_LED1_GPIO_PIN, LED_LOW_TRIGGER); \
LED_GPIO_ON(G_LED2_GPIO_PORT, G_LED2_GPIO_PIN, LED_LOW_TRIGGER); \
LED_GPIO_OFF(B_LED3_GPIO_PORT, B_LED3_GPIO_PIN, LED_LOW_TRIGGER);
//B_LED
#define B_LED_ON_ONLY LED_GPIO_OFF(R_LED1_GPIO_PORT, R_LED1_GPIO_PIN, LED_LOW_TRIGGER); \
LED_GPIO_OFF(G_LED2_GPIO_PORT, G_LED2_GPIO_PIN, LED_LOW_TRIGGER); \
LED_GPIO_ON(B_LED3_GPIO_PORT, B_LED3_GPIO_PIN, LED_LOW_TRIGGER);
//B_G_B_LED全亮
#define RGB_LED_ALL_ON LED_GPIO_ON(R_LED1_GPIO_PORT, R_LED1_GPIO_PIN, LED_LOW_TRIGGER); \
LED_GPIO_ON(G_LED2_GPIO_PORT, G_LED2_GPIO_PIN, LED_LOW_TRIGGER); \
LED_GPIO_ON(B_LED3_GPIO_PORT, B_LED3_GPIO_PIN, LED_LOW_TRIGGER);
//B_G_B_LED全灭
#define RGB_LED_ALL_OFF LED_GPIO_OFF(R_LED1_GPIO_PORT, R_LED1_GPIO_PIN, LED_LOW_TRIGGER); \
LED_GPIO_OFF(G_LED2_GPIO_PORT, G_LED2_GPIO_PIN, LED_LOW_TRIGGER); \
LED_GPIO_OFF(B_LED3_GPIO_PORT, B_LED3_GPIO_PIN, LED_LOW_TRIGGER);
/* LED 灯亮时的IO电平 */
typedef enum {
LED_LOW_TRIGGER = 0,
LED_HIGH_TRIGGER = 1,
}LED_TriggerLever;
void LED_GPIO_Config(void);
void LED_GPIO_ON(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, LED_TriggerLever led_brightstatus);
void LED_GPIO_OFF(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, LED_TriggerLever led_brightstatus);
void LED_TOGGLE(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
#endif /*__bsp_gpio_led_H*/
3.4 key函数
3.4.1 bsp_gpio_key.c
c
/**
******************************************************************************
* @file bsp_gpio_key.c
* @author 作者
* @version v1.0
* @date 2026.4.19
* @brief 扫描按键函数接口
******************************************************************************
* @attention
*
* 版权声明
*
******************************************************************************
*/
#include "key/bsp_gpio_key.h"
#include "delay/bsp_delay.h"
/**
* @brief 配置 KEY 中断配置
* @param 无
* @retval 无
*/
void KEY_NVIC_Config()
{
// 定义一个 NVIC 结构体
NVIC_InitTypeDef nvic_initstauct = { 0 };
/* 开启 AFIO 相关时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
#if 1
/*配置中断源*/
nvic_initstauct.NVIC_IRQChannel = KEY1_EXTI_IRQ;
/*配置抢占优先级*/
nvic_initstauct.NVIC_IRQChannelPreemptionPriority = 1;
/*配置响应优先级*/
nvic_initstauct.NVIC_IRQChannelSubPriority = 0;
/*开启中断*/
nvic_initstauct.NVIC_IRQChannelCmd = ENABLE;
// 把配置写入硬件,生效
NVIC_Init(&nvic_initstauct);
#endif
#if 1
/*配置中断源*/
nvic_initstauct.NVIC_IRQChannel = KEY2_EXTI_IRQ;
/*配置抢占优先级*/
nvic_initstauct.NVIC_IRQChannelPreemptionPriority = 1;
/*配置响应优先级*/
nvic_initstauct.NVIC_IRQChannelSubPriority = 0;
/*开启中断*/
nvic_initstauct.NVIC_IRQChannelCmd = ENABLE;
// 把配置写入硬件,生效
NVIC_Init(&nvic_initstauct);
#endif
}
/**
* @brief 初始化控制 KEY 的IO
* @param 无
* @retval 无
*/
void KEY_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct = { 0 };
/*****************************核心板载按键****************************************/
#if 1
RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK_PORT, ENABLE); //开启GPIOA 端口时钟
GPIO_SetBits(KEY1_GPIO_PORT, KEY1_GPIO_PIN); //设置PA0端口初始化1 让LED灭
// 配置引脚:选择GPIOA的Pin1
GPIO_InitStruct.GPIO_Pin = KEY1_GPIO_PIN;
// 配置输出速度:50MHz
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
// 配置工作模式:浮空输入模式
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStruct);
#endif
#if 1
RCC_APB2PeriphClockCmd(KEY2_GPIO_CLK_PORT, ENABLE); //开启GPIOA 端口时钟
GPIO_SetBits(KEY2_GPIO_PORT, KEY2_GPIO_PIN);
// 配置引脚:选择GPIOA的Pin1
GPIO_InitStruct.GPIO_Pin = KEY2_GPIO_PIN;
// 配置输出速度:50MHz
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
// 配置工作模式:浮空输入模式
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStruct);
#endif
/*****************************用户自定义扩展按键****************************************/
#if 0
RCC_APB2PeriphClockCmd(KEY3_GPIO_CLK_PORT, ENABLE);
GPIO_SetBits(KEY3_GPIO_PORT, KEY3_GPIO_PIN);
// 配置引脚:选择GPIOA的Pin1
GPIO_InitStruct.GPIO_Pin = KEY3_GPIO_PIN;
// 配置输出速度:50MHz
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
// 配置工作模式:下拉输入模式
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(KEY3_GPIO_PORT, &GPIO_InitStruct);
#endif
#if 0
RCC_APB2PeriphClockCmd(KEY4_GPIO_CLK_PORT, ENABLE);
GPIO_SetBits(KEY4_GPIO_PORT, KEY4_GPIO_PIN);
// 配置引脚:选择GPIOA的Pin1
GPIO_InitStruct.GPIO_Pin = KEY4_GPIO_PIN;
// 配置输出速度:50MHz
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
// 配置工作模式:上拉输入模式
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(KEY4_GPIO_PORT, &GPIO_InitStruct);
#endif
#if 0
RCC_APB2PeriphClockCmd(KEY5_GPIO_CLK_PORT, ENABLE);
GPIO_SetBits(KEY5_GPIO_PORT, KEY5_GPIO_PIN);
// 配置引脚:选择GPIOA的Pin1
GPIO_InitStruct.GPIO_Pin = KEY5_GPIO_PIN;
// 配置输出速度:50MHz
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
// 配置工作模式:上拉输入模式
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(KEY5_GPIO_PORT, &GPIO_InitStruct);
#endif
#if 0
RCC_APB2PeriphClockCmd(KEY6_GPIO_CLK_PORT, ENABLE);
GPIO_SetBits(KEY6_GPIO_PORT, KEY6_GPIO_PIN);
// 配置引脚:选择GPIOA的Pin1
GPIO_InitStruct.GPIO_Pin = KEY6_GPIO_PIN;
// 配置输出速度:50MHz
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
// 配置工作模式:上拉输入模式
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(KEY6_GPIO_PORT, &GPIO_InitStruct);
#endif
}
/**
* @brief 配置 KEY 模式
* @param 无
* @retval 无
*/
void KEY_Mode_Config(void)
{
/* 定义一个 EXTI 结构体 */
EXTI_InitTypeDef exti_initstauct = { 0 };
/* 开启 AFIO 相关时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
#if 1
/*选择中断信号源*/
GPIO_EXTILineConfig(KEY1_EXTI_PortSource, KEY1_EXTI_PinSource);
/* 1. 选择EXTI线0(对应PA0、PB0等引脚) */
exti_initstauct.EXTI_Line = KEY1_EXTI_LINE;
/* 2. 模式:中断模式 */
exti_initstauct.EXTI_Mode = EXTI_Mode_Interrupt;
/* 3. 触发:下降沿触发(按键按下常用) */
exti_initstauct.EXTI_Trigger = EXTI_Trigger_Falling;
/* 4. 开启外部中断 */
exti_initstauct.EXTI_LineCmd = ENABLE;
EXTI_Init(&exti_initstauct);
#endif
#if 0
/*选择中断信号源*/
GPIO_EXTILineConfig(KEY2_EXTI_PortSource, KEY2_EXTI_PinSource);
/* 1. 选择EXTI线0(对应PA0、PB0等引脚) */
exti_initstauct.EXTI_Line = KEY2_EXTI_LINE;
/* 2. 模式:中断模式 */
exti_initstauct.EXTI_Mode = EXTI_Mode_Interrupt;
/* 3. 触发:下降沿触发(按键按下常用) */
exti_initstauct.EXTI_Trigger = EXTI_Trigger_Falling;
/* 4. 开启外部中断 */
exti_initstauct.EXTI_LineCmd = ENABLE;
EXTI_Init(&exti_initstauct);
#endif
}
/**
* @brief KEY初始化
* @param 无
* @retval 无
*/
void KEY_Init(void)
{
/* 配置KEY 中断配置 */
KEY_NVIC_Config();
/*配置 KEY 模式*/
KEY_Mode_Config();
/*对应的GPIO 初始化*/
KEY_GPIO_Config();
}
/**
* @brief 基础检测按键
* @param GPIOX : x可以是 A,B,C等
* @param GPIO_PIN: 待操作的PIN
* @param key_pressstatus:按键按下时IO电平状态
* @retval KEY_UP(没有触发按键)、KEY_DOWN(触发按键)
*/
KEY_Status KEY_Scan(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, KEY_TriggerLever key_pressstatus)
{
if (GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == key_pressstatus)
{
Rough_Delay_Ms(20);
while(GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == key_pressstatus);
Rough_Delay_Ms(20);
return KEY_DOWN;
}
else
{
return KEY_UP;
}
}
/***************************************END OF FILE *********************************************************/
3.4.2 bsp_gpio_key.h
c
#ifndef __BSP_GPIO_KEY_H
#define __BSP_GPIO_KEY_H
#include "stm32f10x.h"
/*定义连接GPIO端口,用户只需要修改下面代码即可改变控制的LED引脚 */
/**********************核心板载按键****************************************/
//KEY1
#define KEY1_GPIO_PORT GPIOA /* GPIO 端口 */
#define KEY1_GPIO_CLK_PORT RCC_APB2Periph_GPIOA /* GPIO 端口时钟 */
#define KEY1_GPIO_PIN GPIO_Pin_0 /* 对应PIN脚 */
#define KEY1_EXTI_PortSource GPIO_PortSourceGPIOA /*端口源*/
#define KEY1_EXTI_PinSource GPIO_PinSource0 /*PIN脚源*/
#define KEY1_EXTI_LINE EXTI_Line0 /*外部中断线*/
#define KEY1_EXTI_IRQ EXTI0_IRQn /*外部中断向量号*/
#define KEY1_EXTI_IRQHANDLER EXTI0_IRQHandler /*中断处理函数*/
//KEY2
#define KEY2_GPIO_PORT GPIOC /* GPIO 端口 */
#define KEY2_GPIO_CLK_PORT RCC_APB2Periph_GPIOC /* GPIO 端口时钟 */
#define KEY2_GPIO_PIN GPIO_Pin_13 /* 对应PIN脚 */
#define KEY2_EXTI_PortSource GPIO_PortSourceGPIOC /*端口源*/
#define KEY2_EXTI_PinSource GPIO_PinSource13 /*PIN脚源*/
#define KEY2_EXTI_LINE EXTI_Line13 /*外部中断线*/
#define KEY2_EXTI_IRQ EXTI15_10_IRQn /*外部中断向量号*/
#define KEY2_EXTI_IRQHANDLER EXTI15_10_IRQHandler /*中断处理函数*/
/**********************用户自定义按键****************************************/
//KEY3
#define KEY3_GPIO_PORT GPIOB /* GPIO 端口 */
#define KEY3_GPIO_CLK_PORT RCC_APB2Periph_GPIOB /* GPIO 端口时钟 */
#define KEY3_GPIO_PIN GPIO_Pin_15 /* 对应PIN脚 */
//KEY4
#define KEY4_GPIO_PORT GPIOB /* GPIO 端口 */
#define KEY4_GPIO_CLK_PORT RCC_APB2Periph_GPIOB /* GPIO 端口时钟 */
#define KEY4_GPIO_PIN GPIO_Pin_6 /* 对应PIN脚 */
//KEY5
#define KEY5_GPIO_PORT GPIOB /* GPIO 端口 */
#define KEY5_GPIO_CLK_PORT RCC_APB2Periph_GPIOB /* GPIO 端口时钟 */
#define KEY5_GPIO_PIN GPIO_Pin_7 /* 对应PIN脚 */
//KEY6
#define KEY6_GPIO_PORT GPIOB /* GPIO 端口 */
#define KEY6_GPIO_CLK_PORT RCC_APB2Periph_GPIOB /* GPIO 端口时钟 */
#define KEY6_GPIO_PIN GPIO_Pin_8 /* 对应PIN脚 */
/* 按键按下时的IO电平 */
typedef enum {
KEY_LOW_TRIGGER = 0,
KEY_HIGH_TRIGGER = 1,
}KEY_TriggerLever;
/* 按键的状态 */
typedef enum {
KEY_UP = 0,
KEY_DOWN = 1,
KEY_INIT = 2,
}KEY_Status;
void KEY_Init(void);
void KEY_GPIO_Config(void);
void KEY_NVIC_Config(void);
void KEY_Mode_Config(void);
KEY_Status KEY_Scan(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, KEY_TriggerLever key_pressstatus);
#endif /*__BSP_GPIO_KEY_H*/
3.5 中断函数
在stm32f10x_it.c文件中添加
c
/**
* @brief This function handles KEY1 interrupt request.
* @param None
* @retval None
*/
void KEY1_EXTI_IRQHANDLER(void)
{
if (EXTI_GetITStatus(KEY1_EXTI_LINE) == SET)
{
LED_TOGGLE(R_LED1_GPIO_PORT, R_LED1_GPIO_PIN);
EXTI_ClearITPendingBit(KEY1_EXTI_LINE);
}
}
4.实验现象
外部中断触发点灯
1.编译并下载程序到开发板即可观察现象;
2.每一个用户按键控制控制一个灯。
3.KEY1控制红灯是表现为反应灵敏,KEY2控制绿灯时表现为偶尔响应
根据需求,红灯反应很灵敏,按下亮,按下灭,反应速度快,按键2控制绿灯,按下有时候响应,有时候没有响应,这是因为在代码中
c
while(1)
{
if (KEY_Scan(KEY2_GPIO_PORT, KEY2_GPIO_PIN, KEY_HIGH_TRIGGER) == KEY_DOWN)
{
LED_TOGGLE(G_LED2_GPIO_PORT, G_LED2_GPIO_PIN);
}
Rough_Delay_Ms(5000);
}
有一个延时5000ms,当我按键2按下的时候,有可能程序正在跑延时函数里面,导致当我按下时候没有成功执行到if里面去,延时函数进行了阻塞,偶尔成功是因为我碰巧在延时函数执行完成的时候,此时我按键2按下正好碰巧执行到了,所以按键2控制绿灯偶尔亮,偶尔不亮的情况。
按键1是通过外部中断响应执行的,当我按键1按下,就会进入中断函数,执行红灯亮灭过程,不会被延时5000ms函数所影响到。