STMicroelectronics 系列:STM32G0 系列_(8).STM32G0系列的GPIO接口
STM32G0系列的GPIO接口
1. GPIO接口概述
GPIO(General Purpose Input/Output)接口是STM32G0系列单片机中非常重要的外设之一,用于实现单片机与外部世界的交互。GPIO接口可以配置为多种不同的模式,包括输入、输出、复用功能等,以满足不同的应用需求。STM32G0系列单片机的GPIO接口具有以下特点:
丰富的端口:STM32G0系列单片机通常具有多个GPIO端口,每个端口包含多个引脚。
灵活的配置:每个GPIO引脚可以独立配置为不同的模式,如推挽输出、开漏输出、上拉或下拉输入等。
增强的防护:GPIO引脚具有内置的ESD(静电放电)防护和过压保护功能。
中断功能:GPIO引脚可以配置为中断输入,用于快速响应外部事件。
2. GPIO寄存器
STM32G0系列单片机的GPIO接口通过一组寄存器进行配置和控制。这些寄存器包括:
MODER(Mode Register):模式寄存器,用于配置每个引脚的工作模式。
OTYPER(Output Type Register):输出类型寄存器,用于配置每个引脚的输出类型。
OSPEEDR(Output Speed Register):输出速度寄存器,用于配置每个引脚的输出速度。
PUPDR(Pull-Up/Pull-Down Register):上拉/下拉寄存器,用于配置每个引脚的上拉或下拉电阻。
ODR(Output Data Register):输出数据寄存器,用于设置每个引脚的输出电平。
IDR(Input Data Register):输入数据寄存器,用于读取每个引脚的输入电平。
AFR(Alternate Function Register):复用功能寄存器,用于配置每个引脚的复用功能。
2.1 MODER寄存器
MODER寄存器用于配置每个引脚的工作模式。每个引脚的模式由2位来表示,具体如下:
00:输入模式(通用输入)
01:输出模式(通用推挽输出)
10:输出模式(通用开漏输出)
11:复用功能模式
代码示例:配置GPIO引脚为推挽输出模式
// 配置GPIOA的第5引脚为推挽输出模式
GPIOA->MODER |= (1 << 10); // 设置为01
GPIOA->MODER &= ~(1 << 11); // 清除第11位
2.2 OTYPER寄存器
OTYPER寄存器用于配置每个引脚的输出类型。每个引脚的输出类型由1位来表示,具体如下:
0:推挽输出
1:开漏输出
代码示例:配置GPIO引脚为开漏输出模式
// 配置GPIOA的第5引脚为开漏输出模式
GPIOA->OTYPER |= (1 << 5); // 设置第5位为1
2.3 OSPEEDR寄存器
OSPEEDR寄存器用于配置每个引脚的输出速度。每个引脚的输出速度由2位来表示,具体如下:
00:低速(2 MHz)
01:中速(25 MHz)
10:高速(50 MHz)
11:非常高(100 MHz)
代码示例:配置GPIO引脚为高速输出
// 配置GPIOA的第5引脚为高速输出
GPIOA->OSPEEDR |= (1 << 10); // 设置为10
GPIOA->OSPEEDR &= ~(1 << 11); // 清除第11位
2.4 PUPDR寄存器
PUPDR寄存器用于配置每个引脚的上拉或下拉电阻。每个引脚的上拉/下拉电阻由2位来表示,具体如下:
00:无上拉/下拉
01:上拉
10:下拉
11:保留
代码示例:配置GPIO引脚为上拉输入
// 配置GPIOA的第5引脚为上拉输入
GPIOA->MODER &= ~(1 << 10); // 清除第10位
GPIOA->MODER &= ~(1 << 11); // 清除第11位
GPIOA->PUPDR |= (1 << 10); // 设置为01
GPIOA->PUPDR &= ~(1 << 11); // 清除第11位
2.5 ODR寄存器
ODR寄存器用于设置每个引脚的输出电平。每个引脚的输出电平由1位来表示,具体如下:
0:低电平
1:高电平
代码示例:设置GPIO引脚为高电平
// 设置GPIOA的第5引脚为高电平
GPIOA->ODR |= (1 << 5); // 设置第5位为1
2.6 IDR寄存器
IDR寄存器用于读取每个引脚的输入电平。每个引脚的输入电平由1位来表示,具体如下:
0:低电平
1:高电平
代码示例:读取GPIO引脚的输入电平
// 读取GPIOA的第5引脚的输入电平
uint32_t input_level = GPIOA->IDR & (1 << 5);
2.7 AFR寄存器
AFR寄存器用于配置每个引脚的复用功能。每个引脚的复用功能由4位来表示,具体如下:
0000:复用功能0
0001:复用功能1
…
1111:复用功能15
代码示例:配置GPIO引脚为复用功能
// 配置GPIOA的第5引脚为复用功能1
GPIOA->MODER |= (3 << 10); // 设置为11(复用功能模式)
GPIOA->AFR[0] &= ~(0xF << 20); // 清除复用功能配置
GPIOA->AFR[0] |= (1 << 20); // 设置复用功能为1
3. GPIO初始化
在使用GPIO之前,需要对其进行初始化。STM32G0系列单片机提供了HAL库来简化GPIO的初始化过程。通过HAL库,可以轻松地配置GPIO的工作模式、输出类型、输出速度和上拉/下拉电阻。
3.1 使用HAL库初始化GPIO
3.1.1 配置GPIO结构体
首先,需要定义一个GPIO_InitTypeDef
结构体来配置GPIO的属性。该结构体包含以下字段:
Pin:指定要配置的引脚。
Mode:指定引脚的工作模式。
Pull:指定引脚的上拉/下拉电阻。
Speed:指定引脚的输出速度。
Alternate:指定引脚的复用功能。
3.1.2 初始化GPIO
使用HAL_GPIO_Init
函数来初始化GPIO引脚。该函数需要两个参数:GPIO_TypeDef
类型的指针和GPIO_InitTypeDef
类型的指针。
代码示例:使用HAL库初始化GPIO
#include "stm32g0xx_hal.h"
// 定义GPIO结构体
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 初始化GPIOA的第5引脚为推挽输出模式,高电平,无上拉/下拉
void GPIO_Init(void) {
// 使能GPIOA时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
// 配置GPIOA的第5引脚
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
// 初始化GPIOA
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
int main(void) {
// 初始化系统
HAL_Init();
// 初始化GPIO
GPIO_Init();
// 设置GPIOA的第5引脚为高电平
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
// 无限循环
while (1) {
// 读取GPIOA的第5引脚的输入电平
uint32_t input_level = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5);
// 根据输入电平切换输出电平
if (input_level == GPIO_PIN_RESET) {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
} else {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
}
// 延时100ms
HAL_Delay(100);
}
}
3.2 使用寄存器直接初始化GPIO
除了使用HAL库外,还可以通过直接操作寄存器来初始化GPIO。这种方式更加灵活,但需要对寄存器有深入的了解。
代码示例:使用寄存器直接初始化GPIO
#include "stm32g0xx.h"
void GPIO_Init(void) {
// 使能GPIOA时钟
RCC->IOPENR |= RCC_IOPENR_IOPAEN;
// 配置GPIOA的第5引脚为推挽输出模式
GPIOA->MODER |= (1 << 10); // 设置为01
GPIOA->MODER &= ~(1 << 11); // 清除第11位
// 配置GPIOA的第5引脚为高速输出
GPIOA->OSPEEDR |= (1 << 10); // 设置为10
GPIOA->OSPEEDR &= ~(1 << 11); // 清除第11位
// 配置GPIOA的第5引脚为无上拉/下拉
GPIOA->PUPDR &= ~(1 << 10); // 清除第10位
GPIOA->PUPDR &= ~(1 << 11); // 清除第11位
}
int main(void) {
// 初始化GPIO
GPIO_Init();
// 设置GPIOA的第5引脚为高电平
GPIOA->ODR |= (1 << 5);
// 无限循环
while (1) {
// 读取GPIOA的第5引脚的输入电平
uint32_t input_level = GPIOA->IDR & (1 << 5);
// 根据输入电平切换输出电平
if (input_level == 0) {
GPIOA->ODR |= (1 << 5); // 设置为高电平
} else {
GPIOA->ODR &= ~(1 << 5); // 设置为低电平
}
// 延时100ms
for (uint32_t i = 0; i < 1000000; i++);
}
}
4. GPIO中断配置
GPIO中断功能允许单片机在检测到外部事件时快速响应。STM32G0系列单片机的GPIO中断可以通过配置EXTI(External Interrupt/Event Controller)来实现。
4.1 EXTI中断配置
EXTI中断配置包括以下步骤:
-
配置GPIO引脚为中断输入模式。
-
配置EXTI线。
-
配置NVIC(Nested Vectored Interrupt Controller)。
-
编写中断服务函数。
4.1.1 配置GPIO引脚为中断输入模式
首先,需要将GPIO引脚配置为中断输入模式。可以通过设置MODER
寄存器来实现。
代码示例:配置GPIO引脚为中断输入模式
// 配置GPIOA的第5引脚为中断输入模式
GPIOA->MODER &= ~(1 << 10); // 清除第10位
GPIOA->MODER &= ~(1 << 11); // 清除第11位
GPIOA->PUPDR |= (1 << 10); // 设置为上拉
GPIOA->PUPDR &= ~(1 << 11); // 清除第11位
4.1.2 配置EXTI线
EXTI线需要配置为与GPIO引脚对应的中断线。通过设置EXTICR
寄存器来实现。
代码示例:配置EXTI线
// 配置EXTI线5为GPIOA的第5引脚
SYSCFG->EXTICR[1] &= ~(0xF << 0); // 清除配置
SYSCFG->EXTICR[1] |= (0x0 << 0); // 设置为GPIOA
4.1.3 配置NVIC
NVIC用于管理中断优先级和使能中断。通过设置ISER
寄存器来使能中断,并通过IPR
寄存器来配置中断优先级。
代码示例:配置NVIC
// 使能EXTI线5的中断
NVIC->ISER[0] |= (1 << 5); // 使能EXTI线5的中断
// 配置EXTI线5的中断优先级
NVIC_SetPriority(EXTI0_1_IRQn, 1); // 设置优先级为1
4.1.4 编写中断服务函数
中断服务函数(ISR)是在中断发生时执行的函数。需要在stm32g0xx_it.c
文件中编写ISR。
代码示例:编写中断服务函数
#include "stm32g0xx_hal.h"
// 中断服务函数
void EXTI0_1_IRQHandler(void) {
// 清除中断标志
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_5);
// 处理中断
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_RESET) {
// 按键按下
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); // 设置为高电平
} else {
// 按键释放
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); // 设置为低电平
}
}
// 初始化GPIO中断
void GPIO_Interrupt_Init(void) {
// 使能GPIOA时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
// 配置GPIOA的第5引脚为中断输入模式
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 配置EXTI线5为GPIOA的第5引脚
HAL_NVIC_SetPriority(EXTI0_1_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(EXTI0_1_IRQn);
// 使能EXTI线5的中断
EXTI->IMR1 |= (1 << 5); // 使能中断
EXTI->FTSR1 |= (1 << 5); // 选择下降沿触发
}
int main(void) {
// 初始化HAL库
HAL_Init();
// 初始化GPIO中断
GPIO_Interrupt_Init();
// 无限循环
while (1) {
// 主程序逻辑
}
}
5. GPIO常用配置
5.1 LED控制
LED控制是GPIO应用中最常见的场景之一。通过配置GPIO引脚为输出模式,可以控制LED的亮灭。
代码示例:控制LED
#include "stm32g0xx_hal.h"
// 定义GPIO结构体
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 初始化LED引脚
void LED_Init(void) {
// 使能GPIOA时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
// 配置GPIOA的第5引脚为推挽输出模式
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
// 点亮LED
void LED_On(void) {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
}
// 熄灭LED
void LED_Off(void) {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
}
int main(void) {
// 初始化HAL库
HAL_Init();
// 初始化LED引脚
LED_Init();
// 无限循环
while (1) {
// 点亮LED
LED_On();
HAL_Delay(500); // 延时500ms
// 熄灭LED
LED_Off();
HAL_Delay(500); // 延时500ms
}
}
5.2 按键检测
按键检测是另一个常见的GPIO应用。通过配置GPIO引脚为输入模式,并使用中断或轮询方式来检测按键状态。
代码示例:按键检测
#include "stm32g0xx_hal.h"
// 定义GPIO结构体
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 初始化按键引脚
void Key_Init(void) {
// 使能GPIOA时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
// 配置GPIOA的第5引脚为上拉输入模式
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
// 检测按键状态
uint32_t Key_Read(void) {
return HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5);
}
int main(void) {
// 初始化HAL库
HAL_Init();
// 初始化按键引脚
Key_Init();
// 无限循环
while (1) {
// 读取按键状态
uint32_t key_status = Key_Read();
// 根据按键状态执行相应操作
if (key_status == GPIO_PIN_RESET) {
// 按键按下
// 执行相应操作,例如点亮LED
LED_On();
} else {
// 按键释放
// 执行相应操作,例如熄灭LED
LED_Off();
}
// 延时100ms
HAL_Delay(100);
}
}
5.3 中断方式的按键检测
使用中断方式检测按键可以提高系统的响应速度和效率。当按键按下时,外部中断会被触发,中断服务函数会立即处理按键事件。
代码示例:中断方式的按键检测
#include "stm32g0xx_hal.h"
// 定义GPIO结构体
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 初始化按键引脚
void Key_Interrupt_Init(void) {
// 使能GPIOA时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
// 配置GPIOA的第5引脚为中断输入模式
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 使能EXTI线5的中断
HAL_NVIC_SetPriority(EXTI0_1_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(EXTI0_1_IRQn);
}
// 中断服务函数
void EXTI0_1_IRQHandler(void) {
// 清除中断标志
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_5);
// 处理中断
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_RESET) {
// 按键按下
LED_On(); // 点亮LED
} else {
// 按键释放
LED_Off(); // 熄灭LED
}
}
// 定义LED控制函数
void LED_Init(void) {
// 使能GPIOA时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
// 配置GPIOA的第5引脚为推挽输出模式
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
void LED_On(void) {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
}
void LED_Off(void) {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
}
int main(void) {
// 初始化HAL库
HAL_Init();
// 初始化LED引脚
LED_Init();
// 初始化按键中断
Key_Interrupt_Init();
// 无限循环
while (1) {
// 主程序逻辑
}
}
5.4 模拟输入
STM32G0系列单片机的GPIO引脚还可以配置为模拟输入模式,用于读取模拟信号。模拟输入模式通常与ADC(模数转换器)配合使用。
代码示例:配置GPIO引脚为模拟输入
#include "stm32g0xx_hal.h"
// 定义GPIO结构体
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 初始化模拟输入引脚
void Analog_Init(void) {
// 使能GPIOA时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
// 配置GPIOA的第5引脚为模拟输入模式
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
// 读取模拟输入值
uint32_t ADC_Read(void) {
// 假设ADC1配置为使用GPIOA的第5引脚
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
return HAL_ADC_GetValue(&hadc1);
}
int main(void) {
// 初始化HAL库
HAL_Init();
// 初始化ADC
ADC_Init();
// 初始化模拟输入引脚
Analog_Init();
// 无限循环
while (1) {
// 读取模拟输入值
uint32_t adc_value = ADC_Read();
// 处理ADC值
if (adc_value > 2048) {
// 模拟输入值大于2048,执行相应操作
LED_On();
} else {
// 模拟输入值小于等于2048,执行相应操作
LED_Off();
}
// 延时100ms
HAL_Delay(100);
}
}
// ADC初始化函数(假设已经配置好ADC1)
void ADC_Init(void) {
// 使能ADC1时钟
__HAL_RCC_ADC1_CLK_ENABLE();
// 配置ADC1
ADC_HandleTypeDef hadc1;
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = DISABLE; // 禁用扫描模式
hadc1.Init.ContinuousConvMode = DISABLE; // 禁用连续转换模式
hadc1.Init.DiscontinuousConvMode = DISABLE; // 禁用不连续转换模式
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; // 软件触发转换
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; // 数据右对齐
hadc1.Init.NbrOfConversion = 1; // 1次转换
// 初始化ADC1
HAL_ADC_Init(&hadc1);
// 配置ADC通道
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_5; // 选择通道5
sConfig.Rank = 1; // 通道排序
sConfig.SamplingTime = ADC_SAMPLETIME_64CYCLES; // 采样时间
// 配置ADC通道
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
}
5.5 复用功能配置
GPIO引脚的复用功能允许引脚用于多个外设。例如,可以将GPIO引脚配置为USART的TX或RX引脚。
代码示例:配置GPIO引脚为USART的TX和RX
#include "stm32g0xx_hal.h"
// 定义GPIO结构体
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 初始化USART的GPIO引脚
void USART_GPIO_Init(void) {
// 使能GPIOA时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
// 配置GPIOA的第9引脚为USART1的TX引脚
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF6_USART1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 配置GPIOA的第10引脚为USART1的RX引脚
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_AF_INPUT;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
// USART初始化函数(假设已经配置好USART1)
void USART_Init(void) {
// 使能USART1时钟
__HAL_RCC_USART1_CLK_ENABLE();
// 配置USART1
USART_HandleTypeDef huart1;
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200; // 波特率
huart1.Init.WordLength = UART_WORDLENGTH_8B; // 8位数据
huart1.Init.StopBits = UART_STOPBITS_1; // 1位停止位
huart1.Init.Parity = UART_PARITY_NONE; // 无奇偶校验
huart1.Init.Mode = UART_MODE_TX_RX; // 传输和接收模式
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 无硬件流控制
huart1.Init.OverSampling = UART_OVERSAMPLING_16; // 16倍过采样
// 初始化USART1
HAL_UART_Init(&huart1);
}
int main(void) {
// 初始化HAL库
HAL_Init();
// 初始化USART的GPIO引脚
USART_GPIO_Init();
// 初始化USART
USART_Init();
// 无限循环
while (1) {
// 发送数据
uint8_t data = 'A';
HAL_UART_Transmit(&huart1, &data, 1, HAL_MAX_DELAY);
// 延时100ms
HAL_Delay(100);
}
}
5.6 总结
STM32G0系列单片机的GPIO接口非常灵活和强大,可以通过多种方式配置引脚的工作模式、输出类型、输出速度和上拉/下拉电阻。此外,GPIO还支持中断功能,可以快速响应外部事件。通过HAL库或直接操作寄存器,可以轻松实现各种GPIO应用,如LED控制、按键检测、模拟输入和复用功能配置。
希望本文档对您理解和使用STM32G0系列单片机的GPIO接口有所帮助。如有任何疑问或需要进一步的帮助,请参阅STM32官方文档或联系技术支持。
作者:kkchenkx