LED实验

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

099.2.2LED实验

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

一、LED实验

1.1 实验一灯闪烁

目标是让板载 LED1 周期性亮灭。硬件连接是 PB5,软件需要完成三件事:打开 GPIOB 时钟、把 PB5 配为普通推挽输出、在主循环中周期性改变输出电平。

flowchart LR
    RCC[RCC 打开 GPIOB 时钟] --> INIT[PB5 推挽输出\n初始高电平]
    INIT --> WRITE[写低或翻转 PB5]
    WRITE --> DELAY[延时]
    DELAY --> WRITE
1.1.1 按键PE3控制蜂鸣器开关

完整工程后续还使用 PE3 控制 PB8 蜂鸣器;本节先完成其中最简单的 PB5 LED 闪烁。模式判断方法相同:LED 和蜂鸣器控制脚都需要输出数字高/低电平,因此选择普通推挽输出;PE3 是读取按键状态,才配置为上拉输入。

PB5 不能因为“开漏也能拉低 LED”就直接选开漏。本实验还需要可靠输出高电平让 LED 熄灭;使用推挽输出能主动拉高、主动拉低,不依赖外部上拉形成高电平。

1.1.2 新建工程

工程配置要点:

  1. 选择 STM32F103ZET6/LQFP144;
  2. SYS → Debug 选择 Serial Wire,保留 SWD 下载调试;
  3. RCC 的 HSE 选择 Crystal/Ceramic Resonator
  4. 时钟树配置为 HSE 8 MHz、PLL ×9、SYSCLK 72 MHz,APB1 分频为 2;
  5. Project Manager → Code Generator 中按课程方式为各外设生成独立 .c/.h
  6. PB5 设置为 GPIO_Output

只填写“72”并不保证所有时钟都正确。生成前还应确认没有红色超限提示,尤其是 PCLK1 不超过 36 MHz、Flash 延迟为 2 个等待周期。

1.1.3 配置推广输出模式

这里的“推广输出”指推挽输出(Push-Pull)。PB5 参数建议:

参数配置原因
Output LevelHigh板载 LED 为低电平点亮,上电先灭
GPIO modeOutput Push Pull主动输出高、低电平
Pull-up/Pull-downNo pull 或保持生成配置推挽输出由输出级强驱动,内部弱上下拉不是决定因素
Maximum output speedLowLED 慢速开关不需要快边沿,可减少 EMI 和瞬态电流
User LabelLED1生成可读的 LED1_PinLED1_GPIO_Port

STM32F1 的输出速度编码为 2/10/50 MHz 最大速率。HAL 中 GPIO_SPEED_FREQ_LOW 对应 2 MHz。这是输出边沿/驱动配置,不代表调用写引脚函数后自动产生 2 MHz 方波。

1.1.4 电路图

开发板的 LED1 连接如下:

3.3 V ── LED1 ── R2 1 kΩ ── PB5
         阳极                 GPIO 灌电流

PB5 = 0 V:3.3 V → LED1 → 1 kΩ → PB5 → GND,灯亮
PB5 = 3.3 V:回路两端压差接近 0,灯灭

这称为“低电平有效”或“灌电流点亮”。它与常见的 GPIO → 电阻 → LED → GND 高电平点亮电路方向相反,所以写代码前一定要看原理图。

LED 电流可粗略估算为:

$I_{LED}\approx\frac{3.3\text{ V}-V_F-V_{OL}}{1\text{ k}\Omega}$

假设绿色 LED 正向压降约 2.0 V、PB5 低电平压降较小,电流约为 1.3 mA。实际值随 LED、GPIO 输出能力和温度变化;精确设计要查器件参数。

推挽输出低电平时,STM32 内部 NMOS 导通,电流从板上 3.3 V 流入 PB5 再到地;高电平时内部 PMOS 导通,把 PB5 拉向 VDD。限流电阻 R2 保护 LED 和 GPIO,不能省略。

1.1.5 生成代码

生成后的关系是:

main.h:LED1_Pin = GPIO_PIN_5,LED1_GPIO_Port = GPIOB
gpio.c:打开 GPIOB 时钟,先写高电平,再配置 PB5 推挽低速输出
main.c:调用 MX_GPIO_Init(),在 while(1) 中实现闪烁

先设置输出锁存值、再切换为输出模式,可以减少初始化瞬间的错误脉冲。__HAL_RCC_GPIOB_CLK_ENABLE() 必须在配置 GPIOB 寄存器之前执行。

源码中输出模式虽然填写了 GPIO_PULLUP,但在 STM32F1 的普通推挽输出配置中,高低电平由输出驱动级和 ODR 决定;内部上拉/下拉主要用于输入模式。为了表达意图更清楚,实际工程可在图形界面选择 No pull

1.1.6 应用案例
1.1.6.1 例题: GPIO库函数应用

HAL_GPIO_WritePin(GPIOx, GPIO_Pin, PinState) 有三个参数:端口、引脚掩码和目标状态。推荐使用枚举名而不是裸写 0/1

HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET); /* PB5=0,LED1 亮 */
HAL_Delay(1000);
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);   /* PB5=1,LED1 灭 */
HAL_Delay(1000);

HAL_Delay(1000) 的单位是毫秒,依赖 HAL 时基中断正常运行。如果全局中断被长时间关闭,或在高优先级中断中不当调用,它可能无法按预期返回。

用户逻辑必须放在对应的 USER CODE BEGIN / END 保护区内,尤其是主循环代码。生成器注释本身也是保留位置的标记,不要删除。

1.1.6.2 例题: GPIO写入不同电平

HAL_GPIO_WritePin() 在 STM32F1 HAL 中使用 BSRR/BRR 完成置位或复位,避免对整个 ODR 做一次易受并发影响的读—改—写。

GPIO_PIN_SET   → 写 BSRR 对应位 → ODR 位置 1 → PB5 高 → LED 灭
GPIO_PIN_RESET → 写 BRR  对应位 → ODR 位清 0 → PB5 低 → LED 亮

第二个参数是位掩码,因此可以把多个同端口引脚用按位或组合,一次设为相同状态;不同 GPIO 端口不能放进同一次调用。

1.1.6.3 例题: GPIO灯电极反转

正确函数名是 HAL_GPIO_TogglePin(),不是 token。它读取当前 ODR 状态并把指定引脚翻转:

while (1)
{
    HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
    HAL_Delay(1000);
}

初始值为高时,第一次翻转变低,LED 亮;下一次变高,LED 灭。若其他执行路径也会改同一引脚,翻转后的绝对状态可能不易判断,此时直接写 SET/RESET 更清晰。