stm32-giop

本文详细介绍了STM32的GPIO输入模式,包括浮空、上拉、下拉和模拟输入,并解析了四种输出模式:开漏、开漏复用、推挽和推挽复用。通过STM32CubeMX配置GPIO的基本步骤和相关函数使用,为读者提供了清晰的GPIO操作指南。


一、GPIO输入模式

GPIO分为四种输入模式

-输入浮空(GPIO_Mode_IN_FLOATING)

-输入上拉(GPIO_Mode_IPU)

-输入下拉(GPIO_Mode_IPD)

-模拟输入(GPIO_Mode_AIN)

1.1 输入浮空(GPIO_Mode_IN_FLOATING)

输入浮空:浮空就是逻辑器件与引脚即不接高电平,也不接低电平。由于逻辑器件的内部结构,当它输入引脚悬空时,相当于该引脚接了高电平。

1.2 输入上拉(GPIO_Mode_IPU)

输入上拉模式:上拉就是把点位拉高,比如拉到Vcc。
上拉就是将不确定的信号通过一个电阻嵌位在高电平。
电阻同时起到限流的作用。

1.3 输入下拉(GPIO_Mode_IPD)

输入下拉:就是把电压拉低,拉到GND。与上拉原理相似

1.4 模拟输入(GPIO_Mode_AIN)

模拟输入:模拟输入是指传统方式的输入,数字输入是输入PCM数字信号,即0,1的二进制数字信号,通过数模转换,转换成模拟信号,经前级放大进入功率放大器,功率放大器还是模拟的

二、GPIO输出模式输出模式

GPIO分为四种输出模式

-开漏输出(GPIO_Mode_Out_OD)

-开漏复用功能(GPIO_Mode_AF_OD)

-推挽式输出(GPIO_Mode_Out_PP)

-推挽式复用功能(GPIO_Mode_AF_PP)

2.1 开漏输出(GPIO_Mode_Out_OD)

开漏输出:输出端相当于三极管的集电极,要得到高电平状态需要上拉电阻才行,适合于做电流型的驱动,其吸收电流的能力相对强(一般20mA以内)

2.2 开漏复用功能(GPIO_Mode_AF_OD)

开漏复用功能:可以理解为GPIO口被用作第二功能时的配置情况(即并非作为通用IO口使用)。端口必须配置成复用功能输出模式(推挽或开漏)

2.3 推挽式输出(GPIO_Mode_Out_PP)

推挽式输出:可以输出高,低电平,连接数字器件;推挽结构一般是指两个三级管分别受到互补信号的控制,总是在一个三极管导通的时候另一个截止。高低电平由IC的电源低定。

推挽电路是两个参数相同的三极管或MOSFET,以推挽方式存在于电路中,各负责正负半周的波形方法任务,电路工作时,两只对称的功率开关管每次只有一个导通,所以导通损耗小,效率高。输出即可以向负载灌电流。推拉式输出级即提高电路的负载能力,又提高开关速度.

2.4 推挽式复用功能(GPIO_Mode_AF_PP)

推挽式复用功能:可以理解为GPIO口被用作第二功能时的配置情况

三、stm32cubemx配置

3.1 基本配置

首先是在STM32cubemx中点击所选引脚配置
1
然后点击GPIO页面进行详细参数的配置
2
然后再配置界面配置参数
GPIO output level 引脚电平设置 高/低
GPIO mode GPIO模式 推挽输出/开漏输出
GPIO Pull-up/Pull-dowm 上拉下拉电阻 上拉电阻/下拉电阻/无上拉或下拉
Maxinum output speed 引脚速度设置 低速/中速/高速
User Label 用户标签 给引脚设置名称 如SY1

3.2 关于GPIO的函数

在HAL库 GPIO函数库

首先打开stm32f4xx_hal_gpio.h 发现一共定义有8个函数

void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);
void HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin);
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);
void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);

功能: GPIO初始化

实例:HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
void HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin);
功能:在函数初始化之后的引脚恢复成默认的状态,即各个寄存器复位时的值

实例:HAL_GPIO_Init(GPIOC, GPIO_PIN_4);
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
功能:读取引脚的电平状态、函数返回值为0或1

实例:HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_4);
void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);
功能:引脚写0或1

实例:HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4,0);
void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
翻转引脚的电平状态   

实例:HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_4);    常用在LED上
HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
功能:锁住引脚电平,比如说一个管脚的当前状态是1,当这个管脚电平变化时保持锁定时的值。

实例:HAL_GPIO_LockPin(GPIOC, GPIO_PIN_4);    
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin);
功能:  外部中断服务函数,清除中断标志位

实例:HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);    
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);
功能:  中断回调函数,可以理解为中断函数具体要响应的动作。

实例:HAL_GPIO_EXTI_Callback(GPIO_PIN_4);    

总结

GPIO 输入 输出
以及STM32cube的使用

<think> 我们已经在STM32上实现了EV1527解码程序,现在需要将其移植到ESP32-S3平台(使用Arduino IDE开发环境)。 关键点: 1. 使用GPIO44引脚作为信号输入(外部中断) 2. 使用ESP32的硬件定时器进行精确时间测量(微秒级) 3. 解码逻辑与之前相同,但需要适配ESP32的API 步骤: (1) 配置GPIO44为输入模式,并设置下降沿中断 (2) 配置一个硬件定时器(例如ESP32的timer0)为1微秒计数 (3) 在中断服务函数中记录时间并计算脉冲宽度,进行同步头和数据位判断 (4) 将解码结果输出(串口打印或其它) 注意:ESP32的Arduino环境下,中断服务函数应尽量简短,且不能调用延时函数。 我们参考之前的解码逻辑,结合ESP32的API进行实现。 </think> 以下是在ESP32-S3 Arduino IDE环境下,通过GPIO44引脚实现的EV1527解码程序: ```cpp #include <Arduino.h> // 宏定义时间阈值(单位:μs) #define SYNC_HIGH_MIN 8000 // 同步头高电平最小时间 #define SYNC_HIGH_MAX 10000 // 同步头高电平最大时间 #define SYNC_LOW_MIN 4000 // 同步头低电平最小时间 #define BIT_0_HIGH 400 // 数据0高电平时间 #define BIT_1_HIGH 900 // 数据1高电平时间 #define BIT_TOLERANCE 200 // 时间容差 #define TIMEOUT_MS 20 // 帧超时时间 // 全局变量 volatile uint32_t lastFallTime = 0; // 最近下降沿时间戳 volatile uint8_t dataBuffer[3] = {0}; // 24位数据缓冲区 volatile uint8_t bitCount = 0; // 当前接收位数 volatile bool frameComplete = false; // 帧接收完成标志 // 中断服务函数 (下降沿触发) void IRAM_ATTR handleInterrupt() { uint32_t now = micros(); // 获取当前时间(微秒) uint32_t pulseWidth = now - lastFallTime; // 计算脉冲宽度 // 同步头检测 if(pulseWidth > SYNC_HIGH_MIN && pulseWidth < SYNC_HIGH_MAX) { bitCount = 0; dataBuffer[0] = dataBuffer[1] = dataBuffer[2] = 0; frameComplete = false; } // 数据位处理 else if(bitCount < 24) { // 逻辑0判断: 400±200μs高电平 if(pulseWidth > (BIT_0_HIGH - BIT_TOLERANCE) && pulseWidth < (BIT_0_HIGH + BIT_TOLERANCE)) { dataBuffer[bitCount/8] &= ~(1 << (7 - bitCount%8)); } // 逻辑1判断: 900±200μs高电平 else if(pulseWidth > (BIT_1_HIGH - BIT_TOLERANCE) && pulseWidth < (BIT_1_HIGH + BIT_TOLERANCE)) { dataBuffer[bitCount/8] |= (1 << (7 - bitCount%8)); } bitCount++; if(bitCount == 24) frameComplete = true; } lastFallTime = now; // 更新最后下降沿时间 } void setup() { Serial.begin(115200); // 配置GPIO44为输入模式(下拉电阻) pinMode(44, INPUT_PULLDOWN); // 附加中断处理程序(下降沿触发) attachInterrupt(digitalPinToInterrupt(44), handleInterrupt, FALLING); Serial.println("EV1527 解码器已启动"); } void loop() { static uint32_t lastCheck = 0; // 接收超时处理 if(millis() - lastCheck > TIMEOUT_MS) { bitCount = 0; frameComplete = false; lastCheck = millis(); } // 帧处理 if(frameComplete) { // 组合24位数据 uint32_t fullData = (dataBuffer[0] << 16) | (dataBuffer[1] << 8) | dataBuffer[2]; // 提取信息 uint32_t address = (fullData >> 4) & 0xFFFFF; // 20位地址 uint8_t key = fullData & 0xF; // 4位按键码 Serial.printf("地址: 0x%05X | 按键: %d\n", address, key); frameComplete = false; } } ``` ### 关键实现说明 1. **硬件配置**: - **GPIO44**设置为输入下拉模式,避免悬空状态 - 使用`attachInterrupt()`绑定下降沿中断 - `IRAM_ATTR`确保中断函数存放在IRAM中加速访问[^2] 2. **时间处理**: - 使用`micros()`获取1μs精度时间戳 - 脉冲宽度计算:$当前时间 - 上一跳变时间$ - 超时机制自动重置接收状态 3. **数据结构**: - 24位数据存储在3字节数组`dataBuffer` - 位操作:`dataBuffer[bitCount/8] |= (1 << (7 - bitCount%8))` - 数据重组:`(fullData >> 4) & 0xFFFFF`提取20位地址 ### 使用步骤 1. 将EV1527接收模块输出端连接至ESP32-S3的**GPIO44** 2. 上传程序并打开串口监视器(115200波特率) 3. 按下遥控器按钮,将看到类似输出: ```bash 地址: 0x3A5F1 | 按键: 2 地址: 0x3A5F1 | 按键: 5 ``` ### 优化建议 1. **抗干扰增强**: ```cpp // 添加软件滤波(连续3次相同结果) #define FILTER_COUNT 3 static uint32_t lastAddress = 0; static uint8_t lastKey = 0; static uint8_t matchCount = 0; if(address == lastAddress && key == lastKey) { if(++matchCount >= FILTER_COUNT) { Serial.printf("确认指令 >> 地址: 0x%05X | 按键: %d\n", address, key); } } else { matchCount = 0; } ``` 2. **多引脚扩展**: ```cpp // 定义多个接收引脚 const uint8_t rxPins[] = {44, 45, 46}; void setup() { for(auto pin : rxPins) { pinMode(pin, INPUT_PULLDOWN); attachInterrupt(digitalPinToInterrupt(pin), handleInterrupt, FALLING); } } ``` ### 注意事项 1. 信号线需远离高频干扰源(如WiFi天线) 2. 实测时间参数可能需要微调(不同模块有差异) 3. 高频遥控时建议关闭WiFi/BT减少干扰[^1]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值