GPIO实验代码

Tutorial: 电子电路 Category: 嵌入式 Published: 2026-08-29 08:09:36 Views: 0 Likes: 0 Comments: 0

100.2.3GPIO实验代码

/* ==================== Core\Inc\main.h ==================== */
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.h
  * @brief          : Header for main.c file.
  *                   This file contains the common defines of the application.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H
#define __MAIN_H

#ifdef __cplusplus
extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "stm32f1xx_hal.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Exported types ------------------------------------------------------------*/
/* USER CODE BEGIN ET */

/* USER CODE END ET */

/* Exported constants --------------------------------------------------------*/
/* USER CODE BEGIN EC */

/* USER CODE END EC */

/* Exported macro ------------------------------------------------------------*/
/* USER CODE BEGIN EM */

/* USER CODE END EM */

/* Exported functions prototypes ---------------------------------------------*/
void Error_Handler(void);

/* USER CODE BEGIN EFP */

/* USER CODE END EFP */

/* Private defines -----------------------------------------------------------*/
#define KEY1_Pin GPIO_PIN_3
#define KEY1_GPIO_Port GPIOE
#define KEY2_Pin GPIO_PIN_4
#define KEY2_GPIO_Port GPIOE
#define LED2_Pin GPIO_PIN_5
#define LED2_GPIO_Port GPIOE
#define SUN_Pin GPIO_PIN_11
#define SUN_GPIO_Port GPIOB
#define LED1_Pin GPIO_PIN_5
#define LED1_GPIO_Port GPIOB
#define BEEP_Pin GPIO_PIN_8
#define BEEP_GPIO_Port GPIOB

/* USER CODE BEGIN Private defines */

/* USER CODE END Private defines */

#ifdef __cplusplus
}
#endif

#endif /* __MAIN_H */

/* ==================== Core\Inc\gpio.h ==================== */
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    gpio.h
  * @brief   This file contains all the function prototypes for
  *          the gpio.c file
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __GPIO_H__
#define __GPIO_H__

#ifdef __cplusplus
extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* USER CODE BEGIN Private defines */

/* USER CODE END Private defines */

void MX_GPIO_Init(void);

/* USER CODE BEGIN Prototypes */

/* USER CODE END Prototypes */

#ifdef __cplusplus
}
#endif
#endif /*__ GPIO_H__ */


/* ==================== Core\Src\gpio.c ==================== */
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    gpio.c
  * @brief   This file provides code for the configuration
  *          of all used GPIO pins.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "gpio.h"

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/*----------------------------------------------------------------------------*/
/* Configure GPIO                                                             */
/*----------------------------------------------------------------------------*/
/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

/** Configure pins as
        * Analog
        * Input
        * Output
        * EVENT_OUT
        * EXTI
*/
void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOE_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(BEEP_GPIO_Port, BEEP_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pins : PEPin PEPin */
  GPIO_InitStruct.Pin = KEY1_Pin|KEY2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

  /*Configure GPIO pin : PtPin */
  GPIO_InitStruct.Pin = LED2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(LED2_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pin : PtPin */
  GPIO_InitStruct.Pin = SUN_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(SUN_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pins : PBPin PBPin */
  GPIO_InitStruct.Pin = LED1_Pin|BEEP_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

}

/* USER CODE BEGIN 2 */

/* USER CODE END 2 */

/* ==================== Core\Src\main.c ==================== */
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
//	  HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
//	  HAL_Delay(1000);
	  if(0 == HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin))
	  {
		  HAL_Delay(120);
		  if(0 == HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin))
		  {
			  HAL_GPIO_TogglePin(BEEP_GPIO_Port, BEEP_Pin);
		  }
	  }
	  if(0 == HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin))
	  {
		  HAL_Delay(120);
		  if(0 == HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin))
		  {
			  HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
		  }
	  }

	  if(0 == HAL_GPIO_ReadPin(SUN_GPIO_Port, SUN_Pin))
	  {
		  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, 1);
	  }else{
		  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, 0);
	  }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

一、实验二

本实验实现三条业务逻辑:KEY1 切换蜂鸣器、KEY2 切换 LED2、光敏模块控制 LED1。引脚和板载电路的有效电平不同,写判断前先列真值表:

对象引脚空闲/初始动作状态
KEY1PE3上拉为 1按下接 GND,读 0
KEY2PE4上拉为 1按下接 GND,读 0
LED1PB5写 1,灭写 0,亮
LED2PE5写 1,灭写 0,亮
BEEPPB8写 0,不响写 1,三极管导通并响
SUNPB11模块驱动本板实测亮为 0、暗为 1

1.1 引脚初始化

GPIO 初始化顺序为:使能 GPIOE/GPIOB/GPIOA 时钟,先设置输出锁存电平,再成组配置输入和输出。GPIOA 本实验没有普通业务引脚,但 SWD 等系统配置或生成器模板可能保留其时钟;实际可根据 .ioc 和生成代码核对。

1.1.1 按键配置为上拉输入模式

PE3、PE4 的按键另一端接 GND:

             MCU 内部
3.3 V ──[弱上拉]──┬── PE3/PE4 ── 按键 ── GND
                   │
                   └──> 输入缓冲 ──> IDR

松开:弱上拉使输入为 1
按下:按键把输入强拉为 0

弱上拉限制按下时的电流,近似为 3.3 V / Rpull。内部上拉阻值范围较宽,不适合当作精密电阻;线路很长或干扰严重时可使用合适的外部上拉并加强滤波。

代码把两个同端口、同配置的引脚合并初始化:

GPIO_InitStruct.Pin = KEY1_Pin | KEY2_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
1.1.2 灯配置为推广输出

这里的“推广输出”仍指推挽输出。PB5 与 PE5 分属 GPIOB、GPIOE,不能在一次 HAL_GPIO_Init() 中共同配置;即使参数相同,也要分别传入各自端口。

两只 LED 均先写 GPIO_PIN_SET,上电默认熄灭,再配置为低速推挽输出。输出端采用内部强驱动,建议 No pull;配套工程生成的 GPIO_PULLUP 不决定 LED 的点亮逻辑。

1.1.3 光敏传感器配置为浮空输入

光敏模块数字输出 DO 接 PB11。模块内部通常由光敏电阻分压、比较器和电位器阈值组成:

flowchart LR
    LIGHT[环境光] --> LDR[光敏电阻分压]
    POT[电位器阈值] --> CMP[比较器]
    LDR --> CMP
    CMP --> DO[DO 高/低电平]
    DO --> PB11[PB11 浮空数字输入]

“浮空输入”在这里表示 STM32 不再附加内部上拉或下拉,由模块的 DO 主动驱动。若模块输出是开漏而板上没有上拉,浮空就不合适;应查模块原理图或测量空闲电平。本实验现象证明模块能够给出稳定数字电平。

PB8 蜂鸣器脚应配置为推挽输出并默认写低。板上 10 kΩ 基极下拉使 MCU 复位和高阻阶段的三极管保持截止;软件初始化低电平再提供第二层保障。

1.1.4 写代码儿

完整执行关系:

flowchart TD
    LOOP[while 1] --> K1{PE3 == 0?}
    K1 -- 是 --> D1[延时并复查] --> B[翻转 PB8 蜂鸣器]
    K1 -- 否 --> K2
    B --> K2{PE4 == 0?}
    K2 -- 是 --> D2[延时并复查] --> L2[翻转 PE5 LED2]
    K2 -- 否 --> SUN
    L2 --> SUN{读取 PB11}
    SUN -- 亮处 0 --> OFF[PB5=1,LED1 灭]
    SUN -- 暗处 1 --> ON[PB5=0,LED1 亮]
    OFF --> LOOP
    ON --> LOOP

GPIO_PinState 的定义是 GPIO_PIN_RESET=0GPIO_PIN_SET=1。直接写枚举名比把函数结果和裸数字 0 比较更清晰。

1.1.4.1 按键控制蜂鸣器开关
  • 1.1.4.1.1 读取按键信号

    HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) 返回引脚的数字状态。读取的是 IDR 路径,不是输出锁存器。由于 KEY1 低电平有效,判断可以写为:

    if (HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_RESET)
    {
        /* 检测到按下 */
    }
    
  • 1.1.4.1.2 灯进行反转

    本功能实际翻转的是蜂鸣器 PB8:低变高时三极管导通并响,高变低时停止。HAL_GPIO_TogglePin(BEEP_GPIO_Port, BEEP_Pin) 的端口和引脚必须配套,不能把某个端口宏与另一个引脚随意组合。

  • 1.1.4.1.3 消抖操作

    机械触点在按下和松开瞬间会反复接通/断开。配套代码先检测低电平、延时 120 ms、再检测一次,能滤掉按下沿的一部分抖动:

    初次读到低 → 延时 → 再读仍低 → 确认为按下
    

    120 ms 是课程开发板的实验取值,不是通用固定值;常见消抖窗口约 5~20 ms,实际应结合按键和交互要求测量。更重要的是,配套代码没有等待按键释放:长按超过 120 ms 时,主循环可能反复翻转。若要求“按一次只切换一次”,应在确认按下后等待释放,或用定时扫描状态机检测按下沿:

    RELEASED ──连续稳定低──> PRESSED(只在这里触发一次)
    PRESSED  ──连续稳定高──> RELEASED
    

    不要在中断服务函数里使用长时间阻塞延时;可用周期定时器、系统节拍或主循环状态机消抖。

1.1.4.2 按键控制LED2亮灭

控制 LED2 的按键是 PE4/KEY2,不是 PB4;被控 LED2 是 PE5。逻辑与 KEY1 相同:按下读 0,经消抖后调用:

HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);

由于 LED2 低电平点亮,翻转只是改变亮灭,不必在每次按键中单独计算有效电平。若程序还会从其他位置修改 PE5,则使用明确的状态变量和 WritePin() 更容易保持软件状态与硬件一致。

1.1.4.3 光敏传感器控制LED1亮灭

课程先写一版程序观察极性,再根据现象调整,这是调试数字传感器的有效方法。最终实测关系为:

PB11/SUN光照状态写 PB5LED1
0较亮1
1较暗0

配套源码正是这套逻辑。PB5 低电平点亮,所以“给 LED 写 1”代表熄灭,不能把 GPIO 数值直接当成灯的布尔状态。

光敏 DO 持续输出电平,不需要机械按键式消抖,但比较器在阈值附近仍可能因光照波动而快速翻转。若出现闪烁,可调整模块电位器、增加光学/电气迟滞,或在软件中要求状态连续保持若干次后再切换。