1.按键抖动

按键机械触点断开、闭合时,由于触点的弹性作用,按键开关不会马上稳定接通或一下子断开,使用按键时会产生图按键抖动说明图中的带波纹信号,需要用软件消抖处理滤波,不方便输入检测。可以利用电容充放电的延时,消除了波纹,从而简化软件的处理,软件只需要直接检测引脚的电平即可。
在这里插入图片描述

2.开发板按键原理图

在这里插入图片描述
板载上面的按键设计:
第一个按键电路中,当开关没有按下,此时PA0经过1k电阻,又经过4.7k电阻连接到地,4.7k电阻称之为下拉电阻。也就是PA0端口默认电平状态是低电平。

第三个按键电路中,当按键没有按下,4.7k电阻接的是3.3v,此时给IO口提供一个高电平,这个4.7k电阻称之为上拉电阻。也就是PC13端口默认电平状态是低电平。

2.1 输入模式简单理解

stm32内部可以设置4中输入模式
─ 输入浮空 :
─ 输入上拉
─ 输入下拉
上面这三种时候,只能是高电平低电平,比如说大于2.7V的时候就高电平,小于1.7V就是低电平。

─ 模拟输入 :可以具体输入一个电压值,比如说可以输入一个3.1V,3.2V,3.3V等等一个具体的值

在这里插入图片描述
当我们使用输入模式的时候,只有使用框框部分的,框框外面部分的一个开关是处于断开状态。

3. readme.txt

/***************************************************************************************************************/
】程序介绍

-工程名称 :点亮LED灯之标准库写法版
-实验平台 :野火 STM32FC8T6-STM开发板

【 !】功能介绍:

控制GPIO引脚输出高低电平控制LED灯的亮灭,实现点灯的效果

【 !】实验操作:

1.编译并下载程序到开发板即可观察现象;
2.可以观察红、绿、蓝灯依次点亮然后再一起闪灭。

/****************************************************************************************************************/

【*】引脚分配

【*】 引脚分配

KEY1->PA0 按键按下为高电平 核心板板载默认下拉且硬件有消抖
KEY2->PC13 按键按下为高电平 核心板板载默认下拉且硬件有消抖
NRST->NRST 按键按下为低电平 核心板板载默认上拉且硬件有消抖

KEY3->PB15 按键按下为高电平 用户自定义按键需配置软件消抖且需要软件配置下拉
KEY4->PB6 按键按下为低电平 用户自定义按键需配置软件消抖且需要软件配置上拉
KEY5->PB7 按键按下为低电平 用户自定义按键需配置软件消抖且需要软件配置上拉
KEY6->PB8 按键按下为低电平 用户自定义按键需配置软件消抖且需要软件配置上拉

LED_R->LED1->PA1 核心板板载LED灯低电平亮
LED_G->LED2->PA2 核心板板载LED灯低电平亮
LED_B->LED3->PA3 核心板板载LED灯低电平亮

LED4->PB5 用户自定义LED灯低电平亮
LED5->PB13 用户自定义LED灯高电平亮
LED6->PB14 用户自定义LED灯高电平亮

/****************************************************************************************************************/

【*】程序描述:
<main.c>
1.初始化LED和KEY对应的GPIO,初始化LED和KEY端口
2.控制LED的亮灭

/****************************************************************************************************************/

4.配置key文件

1.在user创建key文件夹
在这里插入图片描述
2.在key里面分别创建一个.c文件和.h文件
其实在led文件夹里面将.c文件和.h文件复制到key文件来,将源文件和头文件名字更改一下。
在这里插入图片描述
3.将key.c源文件导入进来
在这里插入图片描述
key.c



/**
  ******************************************************************************
  * @file    bsp_gpio_key.c
  * @author  作者
  * @version v1.0
  * @date    2026.4.3
  * @brief   扫描按键函数接口
  ******************************************************************************
  * @attention
  *
  * 版权声明
  *
  ******************************************************************************
  */
#include "key/bsp_gpio_key.h"

/**
  * @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);    //设置PA1端口初始化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
    
}

/***************************************END OF FILE *********************************************************/


工作模式定义的是浮空输入模式,因为板载按键有下拉电阻,默认的IO电平是低电平,所以不需要STM32内部设置的输入模式上拉和下拉去设置IO口默认电平状态,所以选择浮空输入模式。

key.h

#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脚 */

                          
                          
void KEY_GPIO_Config(void);

#endif /*__bsp_gpio_led_H*/

在readme文件中看到,按键1是PA0控制,所以GPIO定义GPIOA,引脚定义GPIO_Pin_0
时钟端口是GPIOA。

5.LED文件

bsp_gpio_led.c


/**
  ******************************************************************************
  * @file    bsp_gpio_led.c
  * @author  作者
  * @version v1.0
  * @date    2026.4.3
  * @brief   LED灯的函数接口
  ******************************************************************************
  * @attention
  *
  * 版权声明
  *
  ******************************************************************************
  */
#include "led/bsp_gpio_led.h"

/**
  * @brief  初始化控制 LED 的IO
  * @param  无
  * @retval 无
  */
  
void LED_GPIO_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = { 0 };

#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
    
}

/**
  * @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);
    }
}

/***************************************END OF FILE *********************************************************/

bsp_gpio_led.h

#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脚 */

/***************核心板载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);

#endif /*__bsp_gpio_led_H*/



其中在.c源文件中

/**
  * @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);
    }
}

以及.h文件中枚举

/* LED 灯亮时的IO电平 */
typedef enum {
    LED_LOW_TRIGGER = 0,
    LED_HIGH_TRIGGER = 1,
}LED_TriggerLever;

在ON函数和OFF函数中,添加了第三个参数,这个参数是枚举类型,可以设置0和1,用来表示LED状态。
其中LED_TriggerLever 命名的含义:LED触发方式
LED:发光二极管
Trigger:触发
Lever:电平

led_brightstatus 命名的含义:LED点亮的状态
LED:发光二极管
bright:点亮
status:状态

这里定义了一个枚举类型,名字LED_TriggerLever,表示LED触发方式,然后在ON和OFF函数第三个参数,变量名是led_brightstatus,点亮时候的状态。

ON函数与OFF函数的理解:
当LED的逻辑状态是低电平点亮,高电平熄灭。
此时我在ON函数第三个参数传递LED_LOW_TRIGGER,if成立,执行IO口置0,LED点亮,逻辑正确
此时我在OFF函数第三个参数传递LED_LOW_TRIGGER,if成立,执行IO口置1,LED熄灭,逻辑正确
符合低电平点亮,高电平熄灭的逻辑。

当LED的逻辑状态是高电平点亮,低电平熄灭。
此时我在ON函数第三个参数传递LED_LOW_TRIGGER,if不成立,执行IO口置1,LED点亮,逻辑正确
此时我在OFF函数第三个参数传递LED_LOW_TRIGGER,if不成立,执行IO口置0,LED熄灭,逻辑正确
符合高电平点亮,低电平熄灭的逻辑

所以在ON函数与OFF函数里面,当传递的第三个参数同为低电平逻辑或者高电平逻辑,ON里面的if执行的逻辑与OFF里面的if执行的逻辑相反,ON里面的else执行的逻辑与OFF里面的else执行的逻辑相反就行。

6.key文件

bsp_gpio_key.c


/**
  ******************************************************************************
  * @file    bsp_gpio_key.c
  * @author  作者
  * @version v1.0
  * @date    2026.4.3
  * @brief   扫描按键函数接口
  ******************************************************************************
  * @attention
  *
  * 版权声明
  *
  ******************************************************************************
  */
#include "key/bsp_gpio_key.h"
#include "delay/bsp_delay.h"

/**
  * @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);    //设置PA1端口初始化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
    
}
/**
  * @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 *********************************************************/

bsp_gpio_key.h

#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脚 */

/* 按键按下时的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_GPIO_Config(void);
KEY_Status KEY_Scan(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, KEY_TriggerLever key_pressstatus);

#endif /*__BSP_GPIO_KEY_H*/



其中以下部分代码的理解

/* 按键按下时的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;
/**
  * @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;
    }
}

KEY_Scan函数第三个参数传递的是我按键按下后当前IO口的状态,比如说这个按键一端是连接的是PA0的IO口,另外一端接到VCC,然后PA0有下拉电阻,就是当我给单片机供电的时候,PA0的默认初始状态是低电平,当我此时按键按下,此时VCC之间通过开关流入到IO口,此时的IO口状态是高电平。

所以我定义了按键按下后的枚举,KEY_LOW_TRIGGER = 0,KEY_HIGH_TRIGGER = 1。
当我在main函数去调用KEY_Scan函数的时候如下

while(1) 
    {
        if (KEY_Scan(KEY1_GPIO_PORT, KEY1_GPIO_PIN, KEY_HIGH_TRIGGER) == KEY_DOWN)
        {
            LED_TOGGLE(R_LED1_GPIO_PORT, R_LED1_GPIO_PIN);
        }
    }

我的第三个参数传递的是KEY_HIGH_TRIGGER,然后进入到函数KEY_Scan里面

/**
  * @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;
    }
}

key_pressstatus是KEY_HIGH_TRIGGER,然后函数:
GPIO_ReadInputDataBit(GPIOx, GPIO_Pin)与key_pressstatus判断

GPIO_ReadInputDataBit(GPIOx, GPIO_Pin)这个函数是GPIO.c官方提供的

typedef enum
{ Bit_RESET = 0,
  Bit_SET
}BitAction;

/**
  * @brief  Reads the specified input port pin.
  * @param  GPIOx: where x can be (A..G) to select the GPIO peripheral.
  * @param  GPIO_Pin:  specifies the port bit to read.
  *   This parameter can be GPIO_Pin_x where x can be (0..15).
  * @retval The input port pin value.
  */
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  uint8_t bitstatus = 0x00;
  
  /* Check the parameters */
  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
  assert_param(IS_GET_GPIO_PIN(GPIO_Pin)); 
  
  if ((GPIOx->IDR & GPIO_Pin) != (uint32_t)Bit_RESET)
  {
    bitstatus = (uint8_t)Bit_SET;
  }
  else
  {
    bitstatus = (uint8_t)Bit_RESET;
  }
  return bitstatus;
}

这个函数的功能是判断当前IO口状态,GPIOX是GPIOA,GPIO_Pin是GPIO_Pin_0
这个函数里面有个if

if ((GPIOx->IDR & GPIO_Pin) != (uint32_t)Bit_RESET)

其中IDR是寄存器
在这里插入图片描述
这个if的!=左边的意思是:GPIOA端口,GPIO_Pin_0的电平状态
!= 右边的是0的意思,
因为GPIOA的pin0引脚有下拉电阻,默认电平状态是低电平,当开关按下,然后VCC通过开关到GPIOA的pin0引脚,此时的状态是1。

所以当开关按下,!=左边是1,右边是0,不相等的,判断成立,执行if,

if ((GPIOx->IDR & GPIO_Pin) != (uint32_t)Bit_RESET)
  {
    bitstatus = (uint8_t)Bit_SET;
  }
  return bitstatus;

此时bitstatus 获取的状态是Bit_SET,而Bit_SET是1,返回bitstatus。

while(1) 
    {
        if (KEY_Scan(KEY1_GPIO_PORT, KEY1_GPIO_PIN, KEY_HIGH_TRIGGER) == KEY_DOWN)
        {
            LED_TOGGLE(R_LED1_GPIO_PORT, R_LED1_GPIO_PIN);
        }
    }
/**
  * @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;
    }
}

此时

if (GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) == key_pressstatus)

当按键按下,等号左边返回的是1,等号右边的值是 KEY_HIGH_TRIGGER,也是1,成立的。
执行if里面的语句,if里面的语句

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;
    }

有个while,这个是用来松手检测,如果我按键一直长按,会停在while里面,上面下面都有延时20ms的函数用来消抖,最后通过return 返回按键的状态。

while(1) 
    {
        if (KEY_Scan(KEY1_GPIO_PORT, KEY1_GPIO_PIN, KEY_HIGH_TRIGGER) == KEY_DOWN)
        {
            LED_TOGGLE(R_LED1_GPIO_PORT, R_LED1_GPIO_PIN);
        }
    }

if成立执行

/**
  * @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;
}

在这里插入图片描述
不断的翻转R灯的引脚状态。
ODR寄存器能给指定的引脚设置1或者清除0操作。我需要执行不断的翻转IO口,就需要不断给当前IO口设置1和清除0操作。

最终在

while(1) 
    {
        if (KEY_Scan(KEY1_GPIO_PORT, KEY1_GPIO_PIN, KEY_HIGH_TRIGGER) == KEY_DOWN)
        {
            LED_TOGGLE(R_LED1_GPIO_PORT, R_LED1_GPIO_PIN);
        }
    }

我按键按下,LED灯执行翻转的操作,按下亮,按一下灭。

在这个while(1)里面的函数KEY_Scan的第三个参数传递的是按键按下后的电平状态。

7.引脚配置

在这里插入图片描述

8.代码

8.1 main

/**
  ******************************************************************************
  * @file    main.c
  * @author  作者
  * @version v1.0
  * @date    2026.4.3
  * @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)
{
    LED_GPIO_Config();
    KEY_GPIO_Config();
    
    while(1) 
    {
        if (KEY_Scan(KEY1_GPIO_PORT, KEY1_GPIO_PIN, KEY_HIGH_TRIGGER) == KEY_DOWN)
        {
            LED_TOGGLE(R_LED1_GPIO_PORT, R_LED1_GPIO_PIN);
        }
        
        if (KEY_Scan(KEY2_GPIO_PORT, KEY2_GPIO_PIN, KEY_HIGH_TRIGGER) == KEY_DOWN)
        {
            LED_TOGGLE(G_LED2_GPIO_PORT, G_LED2_GPIO_PIN);
        }
        
        if (KEY_Scan(KEY3_GPIO_PORT, KEY3_GPIO_PIN, KEY_HIGH_TRIGGER) == KEY_DOWN)
        {
            LED_TOGGLE(B_LED3_GPIO_PORT, B_LED3_GPIO_PIN);
        }
        
        if (KEY_Scan(KEY4_GPIO_PORT, KEY4_GPIO_PIN, KEY_LOW_TRIGGER) == KEY_DOWN)
        {
            LED_TOGGLE(LED4_GPIO_PORT, LED4_GPIO_PIN);
        }
        
        if (KEY_Scan(KEY5_GPIO_PORT, KEY5_GPIO_PIN, KEY_LOW_TRIGGER) == KEY_DOWN)
        {
            LED_TOGGLE(LED5_GPIO_PORT, LED5_GPIO_PIN);
        }
        
        if (KEY_Scan(KEY6_GPIO_PORT, KEY6_GPIO_PIN, KEY_LOW_TRIGGER) == KEY_DOWN)
        {
            LED_TOGGLE(LED6_GPIO_PORT, LED6_GPIO_PIN);
        }
    }
}

其中KEY4,KEY5,KEY6函数第三个参数配置KEY_LOW_TRIGGER,低电平,按键按下IO口的状态是低电平。

8.2 key

8.2.1 bsp_gpio_key.c


/**
  ******************************************************************************
  * @file    bsp_gpio_key.c
  * @author  作者
  * @version v1.0
  * @date    2026.4.3
  * @brief   扫描按键函数接口
  ******************************************************************************
  * @attention
  *
  * 版权声明
  *
  ******************************************************************************
  */
#include "key/bsp_gpio_key.h"
#include "delay/bsp_delay.h"

/**
  * @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 1

    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 1

    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 1

    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 1

    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  基础检测按键
  * @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 *********************************************************/

按键1和按键2是板载的按键,按下后IO口状态是高电平,外接入了下拉电阻,所以配置输入浮空模式
按键3,按下后IO口状态是高电平,没有外接下拉电阻,所以配置下拉输入模式
按键4,5,6,按下后IO口状态是低电平,没有外接上拉电阻,所以需要配置上拉输入模式。

上下拉电阻用来初始化IO口状态,然后按键按下后,改变IO口状态。

8.2.2 bsp_gpio_key.h

#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脚 */

//KEY2
#define KEY2_GPIO_PORT      GPIOC                       /* GPIO 端口 */
#define KEY2_GPIO_CLK_PORT  RCC_APB2Periph_GPIOC        /* GPIO 端口时钟 */
#define KEY2_GPIO_PIN       GPIO_Pin_13                 /* 对应PIN脚 */


/**********************用户自定义按键****************************************/
//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_GPIO_Config(void);
KEY_Status KEY_Scan(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, KEY_TriggerLever key_pressstatus);

#endif /*__BSP_GPIO_KEY_H*/

8.3 LED

8.3.1 bsp_gpio_led.c


/**
  ******************************************************************************
  * @file    bsp_gpio_led.c
  * @author  作者
  * @version v1.0
  * @date    2026.4.3
  * @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 1
    
    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 1
    
    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 1
    
    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 *********************************************************/

LED5和LED6是高电平点亮,所以需要用函数GPIO_ResetBits设置初始状态IO口,是低电平。如果是高电平,那么LED长脚接到IO口,短脚接入GND

LED1234是低电平点亮,所以需要函数GPIO_SetBits设置初始状态IO口,是高电平。
如果低电平点亮,那么LED短脚接入到IO口,长脚接VCC。

8.3.2 bsp_gpio_led.h

#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*/

8.4 延时

8.4.1bsp_delay.c


/**
  ******************************************************************************
  * @file    bsp_delay.c
  * @author  作者
  * @version v1.0
  * @date    2026.4.3
  * @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);
}

8.4.2 bsp_delay_h

#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*/

9.接线图

在这里插入图片描述
在这里插入图片描述

10 实验现象

STM32按键点灯

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐