ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

STM32系列1:GPIO

STM32系列1:GPIO 1. 什么是GPIO?GPIOGeneral Purpose Input/Output通用输入输出是微控制器、单片机以及树莓派等开发板上普遍提供的一类可编程引脚。我们可以通过软件把这些引脚配置为输入或输出用来读取外部信号或者控制外部设备。简单来说GPIO就是一块开发板与外界硬件“打交道”的通道既能感知外部世界发生的变化输入也能对外部世界产生影响输出。2. GPIO的核心概念2.1 GPIO的8种工作模式GPIO functional description定义了STM32中每个GPIO引脚可以通过软件配置成的8种工作模式。2.1.1 输入模式Input floating浮空输入引脚内部既不上拉也不下拉处于高阻态电平完全由外部信号决定。典型应用外接上拉或下拉电阻的按键、I2C总线等在外部已有上拉的场景。Input pull-up上拉输入内部通过电阻连接到VDD3.3V默认读到高电平。典型应用按键一端接引脚、另一端接GND按下时产生低电平。Input pull-down下拉输入内部通过电阻连接到GND默认读到低电平。典型应用按键一端接引脚、另一端接VCC按下时产生高电平。2.1.2 模拟模式Analog模拟输入或输出关闭内部的施密特触发器让引脚直接连接到芯片内部的ADC、DAC或比较器。典型应用ADC采集电池电压、光敏电阻分压或DAC输出模拟波形。配置为模拟模式还能大幅降低引脚功耗并防止数字噪声干扰模拟信号。2.1.3 输出模式Output push-pull推挽输出这是STM32 GPIO最常见的输出模式之一。引脚内部由两个晶体管配合驱动一个是N型晶体管另一个是P型晶体管。输出高电平时P型晶体管导通、N型晶体管截止引脚被拉高至VDD输出低电平时N型晶体管导通、P型晶体管截止引脚被拉低至GND。因此推挽输出可以主动输出高电平和低电平驱动能力较强。典型应用直接驱动LED、蜂鸣器、继电器或输出SPI、UART等高速数字信号。Output open-drain开漏输出与推挽输出不同开漏输出只有一个N型晶体管连接到引脚。输出低电平时N型晶体管导通引脚被拉低至GND输出高电平时N型晶体管截止引脚处于高阻态此时需要外部上拉电阻将引脚拉至高电平。因此开漏输出只能主动拉低电平不能主动拉高电平。典型应用I2C 通信、多个设备共享一根信号线线与逻辑以及 3.3V 器件驱动 5V 器件等电平转换场景。2.1.4 复用功能模式当引脚交给芯片内部外设如 USART、SPI、I2C、TIM、CAN控制时就属于复用功能模式。Alternate function push-pull复用推挽由内部外设控制采用推挽输出。典型应用UART_TX、SPI_SCK、PWM输出。Alternate function open-drain复用开漏由内部外设控制采用开漏输出。典型应用I2C的SCL和SDA、部分CAN总线收发器。2.2 输出速度GPIO speedGPIO的输出速度并不是通信速率而是指输出驱动器翻转电平时的边沿变化速度也就是从低电平跳到高电平、或从高电平跳到低电平的转换速率slew rate。常见档位如下Low speed低速档约2MHz翻转较慢噪声低。Medium speed中速档约10-25MHz。High speed高速档约50MHz边沿更陡峭。Very high speed超高速档部分型号可达100MHz以上。速度越高电平翻转越快但代价是更明显的电磁干扰EMI、更高的瞬时功耗和更多振铃速度越低信号越干净但可能无法满足高速通信需求。因此通常建议在满足信号要求的前提下选择尽量低的速度档。2.3 施密特触发器的作用施密特触发器位于GPIO输入通路中它的核心作用是把外部不够干净的信号整形为稳定、干净的数字电平。真实环境里的输入信号通常不是完美的方波可能带有毛刺、抖动或者电平上升、下降非常缓慢。如果直接用这类信号驱动后续数字电路引脚电平会在高、低阈值附近来回波动CPU可能反复读到0和1造成抖动或误触发。施密特触发器通过滞回特性解决这个问题它给上升和下降过程设置了两个不同的阈值上升判断输入电压升高并超过较高阈值时输出才翻转为高电平。下降判断输入电压降低并低于较低阈值时输出才翻转为低电平。中间区间输入电压处在两个阈值之间时输出保持上一次的状态不会反复跳变。经过施密特触发器整形后带噪声、缓变的模拟波形会变成干净、锐利的0或1再送入输入数据寄存器。这就是为什么模拟模式要关闭施密特触发器ADC或DAC需要的是原始、连续的模拟信号施密特触发器的滞回判断会改变信号形态并引入数字干扰因此模拟输入时通常直接旁路掉这一级。2.4 复用功能复用功能就像给同一个引脚分配了多张“身份卡”。前面讲过当引脚交给USART、SPI、I2C、TIM、CAN等内部外设控制时就进入复用功能模式。但一个引脚通常不只对应一个外设例如 PA9可以复用为USART1_TX也可能复用为TIM1_CH2或其他外设接口。具体轮到哪一个外设由复用功能编号决定。STM32用AF编号来区分同一引脚的多个外设连接。不同型号的映射表不完全相同因此编写代码时必须查阅对应型号数据手册中的复用功能表。常见的AF选择包括AF0至AF15不同编号通常对应不同外设例如AF7可能对应USART1、AF5可能对应SPI1 等具体以数据手册为准。绑定关系同一个引脚可能支持多个AF但同一时刻只能选择一个例如PA9选AF7时作为USART1_TX选其他AF时就可能变成定时器通道。特殊编号AF0通常表示普通GPIO或系统功能并非所有引脚都具备全部AF选项。2.5 拉电流与灌电流能力在GPIO输出和控制负载时经常会提到拉电流和灌电流。它们描述的是电流相对芯片引脚的方向拉电流引脚输出高电平时芯片通过引脚向外部负载“流出”的电流。例如LED正极接GPIO、负极接地时点亮LED需要走拉电流。灌电流引脚输出低电平时外部电流“流入”芯片引脚、再回到地。例如LED正极接VDD、负极接GPIO时点亮LED靠的是灌电流。3. GPIO外设接口原理分析ST公司提供的关于GPIO的案例代码中调用了很多关于GPIO外设相关的函数接口以及数据类型作为初学者必须认真分析代码才能掌握GPIO外设的核心原理。/** * brief GPIO Init structure definition */ typedef struct { uint32_t Pin; /*! Specifies the GPIO pins to be configured. This parameter can be any value of ref GPIO_pins_define */ uint32_t Mode; /*! Specifies the operating mode for the selected pins. This parameter can be a value of ref GPIO_mode_define */ uint32_t Pull; /*! Specifies the Pull-up or Pull-Down activation for the selected pins. This parameter can be a value of ref GPIO_pull_define */ uint32_t Speed; /*! Specifies the speed for the selected pins. This parameter can be a value of ref GPIO_speed_define */ uint32_t Alternate; /*! Peripheral to be connected to the selected pins. This parameter can be a value of ref GPIO_Alternate_function_selection */ }GPIO_InitTypeDef; /** brief GPIO Bit SET and Bit RESET enumeration */ typedef enum { GPIO_PIN_RESET 0, GPIO_PIN_SET }GPIO_PinState; /** defgroup GPIO_pins_define GPIO pins define { / #define GPIO_PIN_0 ((uint16_t)0x0001) / Pin 0 selected / #define GPIO_PIN_1 ((uint16_t)0x0002) / Pin 1 selected / #define GPIO_PIN_2 ((uint16_t)0x0004) / Pin 2 selected / #define GPIO_PIN_3 ((uint16_t)0x0008) / Pin 3 selected / #define GPIO_PIN_4 ((uint16_t)0x0010) / Pin 4 selected / #define GPIO_PIN_5 ((uint16_t)0x0020) / Pin 5 selected / #define GPIO_PIN_6 ((uint16_t)0x0040) / Pin 6 selected / #define GPIO_PIN_7 ((uint16_t)0x0080) / Pin 7 selected / #define GPIO_PIN_8 ((uint16_t)0x0100) / Pin 8 selected / #define GPIO_PIN_9 ((uint16_t)0x0200) / Pin 9 selected / #define GPIO_PIN_10 ((uint16_t)0x0400) / Pin 10 selected / #define GPIO_PIN_11 ((uint16_t)0x0800) / Pin 11 selected / #define GPIO_PIN_12 ((uint16_t)0x1000) / Pin 12 selected / #define GPIO_PIN_13 ((uint16_t)0x2000) / Pin 13 selected / #define GPIO_PIN_14 ((uint16_t)0x4000) / Pin 14 selected / #define GPIO_PIN_15 ((uint16_t)0x8000) / Pin 15 selected / #define GPIO_PIN_All ((uint16_t)0xFFFF) / All pins selected */ #define GPIO_PIN_MASK 0x0000FFFFU /* PIN mask for assert test */ /** defgroup GPIO_mode_define GPIO mode define brief GPIO Configuration Mode Elements values convention: 0x00WX00YZ - W : EXTI trigger detection on 3 bits - X : EXTI mode (IT or Event) on 2 bits - Y : Output type (Push Pull or Open Drain) on 1 bit - Z : GPIO mode (Input, Output, Alternate or Analog) on 2 bits { / #define GPIO_MODE_INPUT MODE_INPUT /! Input Floating Mode / #define GPIO_MODE_OUTPUT_PP (MODE_OUTPUT | OUTPUT_PP) /! Output Push Pull Mode / #define GPIO_MODE_OUTPUT_OD (MODE_OUTPUT | OUTPUT_OD) /! Output Open Drain Mode / #define GPIO_MODE_AF_PP (MODE_AF | OUTPUT_PP) /! Alternate Function Push Pull Mode / #define GPIO_MODE_AF_OD (MODE_AF | OUTPUT_OD) /! Alternate Function Open Drain Mode */ #define GPIO_MODE_ANALOG MODE_ANALOG /*! Analog Mode */ #define GPIO_MODE_IT_RISING (MODE_INPUT | EXTI_IT | TRIGGER_RISING) /*! External Interrupt Mode with Rising edge trigger detection / #define GPIO_MODE_IT_FALLING (MODE_INPUT | EXTI_IT | TRIGGER_FALLING) /! External Interrupt Mode with Falling edge trigger detection / #define GPIO_MODE_IT_RISING_FALLING (MODE_INPUT | EXTI_IT | TRIGGER_RISING | TRIGGER_FALLING) /! External Interrupt Mode with Rising/Falling edge trigger detection */ #define GPIO_MODE_EVT_RISING (MODE_INPUT | EXTI_EVT | TRIGGER_RISING) /*! External Event Mode with Rising edge trigger detection / #define GPIO_MODE_EVT_FALLING (MODE_INPUT | EXTI_EVT | TRIGGER_FALLING) /! External Event Mode with Falling edge trigger detection / #define GPIO_MODE_EVT_RISING_FALLING (MODE_INPUT | EXTI_EVT | TRIGGER_RISING | TRIGGER_FALLING) /! External Event Mode with Rising/Falling edge trigger detection */ /** } */ /** defgroup GPIO_speed_define GPIO speed define brief GPIO Output Maximum frequency { / #define GPIO_SPEED_FREQ_LOW 0x00000000U /! IO works at 2 MHz, please refer to the product datasheet / #define GPIO_SPEED_FREQ_MEDIUM 0x00000001U /! range 12,5 MHz to 50 MHz, please refer to the product datasheet / #define GPIO_SPEED_FREQ_HIGH 0x00000002U /! range 25 MHz to 100 MHz, please refer to the product datasheet / #define GPIO_SPEED_FREQ_VERY_HIGH 0x00000003U /! range 50 MHz to 200 MHz, please refer to the product datasheet / /* } */ /** defgroup GPIO_pull_define GPIO pull define brief GPIO Pull-Up or Pull-Down Activation { / #define GPIO_NOPULL 0x00000000U /! No Pull-up or Pull-down activation / #define GPIO_PULLUP 0x00000001U /! Pull-up activation / #define GPIO_PULLDOWN 0x00000002U /! Pull-down activation */ /** addtogroup GPIO_Exported_Functions_Group1 { / / Initialization and de-initialization functions *****************************/ void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init); void HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin); /** addtogroup GPIO_Exported_Functions_Group2 { / / IO operation functions *************************************************/ GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef GPIOx, uint16_t GPIO_Pin); void HAL_GPIO_WritePin(GPIO_TypeDef GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState); void HAL_GPIO_TogglePin(GPIO_TypeDef GPIOx, uint16_t GPIO_Pin); HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef GPIOx, uint16_t GPIO_Pin); void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin); void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);4. 完整示例按键控制LED翻转下面是一个完整的按键控制LED翻转。该程序使用PA0按键控制PF9上的LED使用PE2按键控制PF10上的LED采用主循环轮询加软件消抖的方式并在检测到按键按下后等待释放避免一次按压被重复触发。/** ****************************************************************************** * #文件: main.c * #简要描述: 通过按键来实现LED灯的翻转(轮询方式) * #详细描述: 该程序通过检测按键的状态来控制LED灯的翻转。当按下按键时LED灯的状态会发生改变。 * #作者 游走于孤独的码农 * #时间 2026-10-06 ****************************************************************************** */ /* 头文件 */ #include main.h /* 函数声明 */ void SystemClock_Config(void); static void Key_Init(void); static void Led_Init(void); /** * #简要描述所有程序的入口点 * #返回值 void */ int main(void) { /* MCU配置 */ /* 重启所有的外设 */ HAL_Init(); /* 配置系统时钟 */ SystemClock_Config(); /* 初始化所有配置外设 */ Key_Init(); Led_Init(); /* 死循环 */ while (1) { /* 消抖 */ HAL_Delay(10); /* 检测按键是否按下 */ if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) GPIO_PIN_RESET) { /* 翻转LED灯 */ HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_9); while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) GPIO_PIN_RESET) { /* 等待按键释放 */ } } /* 检测按键是否按下 */ if (HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_2) GPIO_PIN_RESET) { /* 翻转LED灯 */ HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_10); while(HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_2) GPIO_PIN_RESET) { /* 等待按键释放 */ } } } } /** * #简要描述系统时钟配置HAL库自带 * #返回值 void */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct {0}; /** Configure the main internal regulator output voltage */ __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState RCC_PLL_NONE; 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_HSI; RCC_ClkInitStruct.AHBCLKDivider RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(RCC_ClkInitStruct, FLASH_LATENCY_0) ! HAL_OK) { Error_Handler(); } } /** * #简要描述 按键初始化 * #详细描述 该函数用于初始化按键引脚PA0和PE2配置为输入模式。 * #返回值 void */ static void Key_Init(void) { /* GPIOA时钟使能 */ __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOE_CLK_ENABLE(); /* 配置PA0为输入模式 */ GPIO_InitTypeDef Btn_InitStruct_PA0 { .Pin GPIO_PIN_0, .Mode GPIO_MODE_INPUT, .Pull GPIO_NOPULL }; HAL_GPIO_Init(GPIOA, Btn_InitStruct_PA0); /* 配置PE2为输入模式 */ GPIO_InitTypeDef Btn_InitStruct_PE2 { .Pin GPIO_PIN_2, .Mode GPIO_MODE_INPUT, .Pull GPIO_NOPULL }; HAL_GPIO_Init(GPIOE, Btn_InitStruct_PE2); } /** * #简要描述 led初始化 * #详细描述 该函数用于初始化LED引脚PF9和PF10配置为输出模式。 * #返回值 void */ static void Led_Init(void) { /* GPIOF时钟使能 */ __HAL_RCC_GPIOF_CLK_ENABLE(); /* 配置PF9和PF10为输出模式 */ GPIO_InitTypeDef Led_InitStruct { .Pin GPIO_PIN_9 | GPIO_PIN_10, .Mode GPIO_MODE_OUTPUT_PP, .Pull GPIO_NOPULL, .Speed GPIO_SPEED_FREQ_LOW }; HAL_GPIO_Init(GPIOF, Led_InitStruct); } /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /** * 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 */
返回列表