GPIO相关寄存器

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

104.2.7GPIO相关寄存器

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

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

一、GPIO相关寄存器

STM32F103 的每组 GPIO 端口由一组寄存器控制:

寄存器偏移用途
CRL0x00配置 0~7 号引脚
CRH0x04配置 8~15 号引脚
IDR0x08读取引脚实际输入电平
ODR0x0C输出数据锁存值,也用于选择输入上拉/下拉
BSRR0x10原子置位或复位输出位
BRR0x14原子复位输出位
LCKR0x18锁定端口配置,直到下次复位

课程工程的 PB5 连接 LED1。原理图中 LED 的阳极接 3.3 V,阴极经 1 kΩ 电阻接 PB5,因此 MCU 需要向外“吸电流”才能点亮:

3.3 V ── LED ── 1 kΩ ── PB5

PB5=0:3.3 V → LED → 电阻 → PB5 → GND,LED 亮
PB5=1:两端接近 3.3 V,几乎无电流,LED 灭

这就是低电平有效。GPIO 读写函数中的 SET/RESET 表示引脚电平,不直接等同于外设“开/关”。

1.1 上节回顾

使用 GPIOB 的基本顺序:

  1. 置 RCC_APB2ENR.IOPBEN,开启 GPIOB 时钟;
  2. 通过 CRL/CRH 配置引脚方向、电气类型和最大翻转速度;
  3. 输入用 IDR 读取,输出用 BSRR/BRR 改变;
  4. 根据板载电路确认高低电平与灯亮、蜂鸣器响之间的关系。

GPIOB 基地址为:

APB2PERIPH_BASE = 0x40010000
GPIOB_BASE      = APB2PERIPH_BASE + 0x0C00
                = 0x40010C00

因此 GPIOB_IDR 位于 0x40010C08,GPIOB_BSRR 位于 0x40010C10。

1.2 GPIO初始化

课程代码把 PB5 配置为低速通用推挽输出并启用上拉:

GPIO_InitStruct.Pin = LED1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LED1_GPIO_Port, &GPIO_InitStruct);

STM32F1 的每个引脚占用 CRL/CRH 中连续 4 位:

高两位 CNFy[1:0]    低两位 MODEy[1:0]
┌───────────────┬────────────────┐
│  CNF1  CNF0   │  MODE1  MODE0  │
└───────────────┴────────────────┘
bit3       bit2    bit1       bit0

MODE 的含义:

MODE[1:0]模式
00输入模式
01输出,最大 10 MHz
10输出,最大 2 MHz
11输出,最大 50 MHz

当 MODE=00 时,CNF 表示输入类型:

CNF[1:0]输入类型
00模拟输入
01浮空输入
10上拉/下拉输入,由 ODR 对应位选择方向
11保留

当 MODE≠00 时,CNF 表示输出类型:

CNF[1:0]输出类型
00通用推挽
01通用开漏
10复用推挽
11复用开漏

PB5 位于 0~7,所以使用 CRL;其四位字段位于 CRL[23:20]。低速在当前 HAL 中对应最大 2 MHz,即 MODE=10;通用推挽 CNF=00,因此字段值为二进制 0010。

GPIOB_CRL[23:20] = CNF5[1:0] MODE5[1:0]
                  =      00            10
                  = 0x2

“速度”指输出驱动的最大翻转能力,不是让 CPU 或程序自动变慢。普通 LED 选择低速可减小边沿噪声和电磁干扰。

1.3 GPIO功能描述

GPIO 输入和输出并非只经过一个寄存器:

flowchart LR
    PIN[物理引脚] --> SCH[施密特输入缓冲]
    SCH --> IDR[IDR 输入数据寄存器]
    ODR[ODR 输出锁存器] --> DRV[推挽/开漏输出驱动]
    BSRR[BSRR 原子置位/复位] --> ODR
    BRR[BRR 原子复位] --> ODR
    DRV --> PIN
    CR[CRL/CRH 模式配置] --> SCH
    CR --> DRV

IDR 反映输入缓冲器看到的引脚电平;ODR 反映输出锁存器的值。输出引脚被外部强拉、发生短路或配置成开漏时,IDR 与 ODR 不一定相同。

1.3.1 端口配置高寄存器

CRH 控制 8~15 号引脚,每个引脚仍占四位:

引脚CRH 位段
8[3:0]
9[7:4]
10[11:8]
11[15:12]
12[19:16]
13[23:20]
14[27:24]
15[31:28]

计算某引脚字段位置的方法:

pin 0~7:使用 CRL,shift = pin × 4
pin 8~15:使用 CRH,shift = (pin - 8) × 4

HAL_GPIO_Init 会先清除目标四位,再写入新配置。这样不会改动同一端口的其他引脚。

1.3.2 端口输入数据寄存器

正确的输入寄存器名称是 IDR,位 15:0 分别对应 Px15:Px0,位 31:16 保留:

GPIOx_IDR
31                         16 15                      0
┌────────────────────────────┬─────────────────────────┐
│          保留              │ IDR15 ... IDR1 IDR0    │
└────────────────────────────┴─────────────────────────┘

HAL_GPIO_ReadPin() 的核心是:

if ((GPIOx->IDR & GPIO_Pin) != GPIO_PIN_RESET)
    return GPIO_PIN_SET;
else
    return GPIO_PIN_RESET;

GPIO_Pin 是位掩码。例如 GPIO_PIN_3=0x0008。按位与后只保留第 3 位,结果非零表示高电平,结果为零表示低电平。

输出时 HAL 使用 BSRR:

if (PinState != GPIO_PIN_RESET)
    GPIOx->BSRR = GPIO_Pin;                  /* 低 16 位:置 1 */
else
    GPIOx->BSRR = (uint32_t)GPIO_Pin << 16U; /* 高 16 位:复位 */

BSRR 的关键优势是原子写操作:

BSRR 位写 1 的作用写 0 的作用
[15:0] BSy将 ODRy 置 1无作用
[31:16] BRy将 ODRy 清 0无作用

若用 ODR = ODR | mask 进行读—改—写,中断可能夹在读取和写回之间,造成并发更新丢失;直接向 BSRR 写入掩码没有这个窗口。HAL_GPIO_TogglePin 则先读取 ODR,再一次性构造 BSRR 的置位与复位掩码,因此“翻转”本身仍依赖读到的旧状态,多个执行上下文同时翻转同一位时要自行同步。

1.3.3 应用案例
1.3.3.1 例题: GPIO读取引脚状态值

读取 PE3 按键:

GPIO_PinState key = HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_3);
if (key == GPIO_PIN_RESET)
{
    /* PE3 按键按下,低电平有效 */
}

它对应的寄存器运算是:

uint32_t masked = GPIOE->IDR & (1U << 3);

若 IDR=0x0008:

IDR       0000 0000 0000 1000
GPIO_PIN3 0000 0000 0000 1000
按位与    0000 0000 0000 1000  → 非 0 → SET

若按键把 PE3 拉到地,IDR 第 3 位为 0:

IDR       xxxx xxxx xxxx 0xxx
GPIO_PIN3 0000 0000 0000 1000
按位与    0000 0000 0000 0000  → 0 → RESET

实际按键有机械抖动。轮询时要延时确认或使用状态机;中断方式下不应在中断服务程序中长时间阻塞,而应记录事件后交给主循环消抖。