单片机
返回首页

stm32学习笔记 GPIO通用输入输出口

2025-09-28 来源:cnblogs

相对于51直接对寄存器的设置,stm32因为是32位修改起来比较麻烦,江协课程是基于标准库的,是对封装函数进行操作,这要求我们对于模块的使用在开始就规划好。


GPIO通用输入输出口

APB2是外设总线

输入模式

  1. 浮空输入

  2. 原理:GPIO 端口无内部上拉或下拉电阻,电平状态完全由外部输入决定,引脚悬空时电平不确定。

  3. 使用场景:用于串口通信接收端(如 UART、USART 的 RX 引脚 ),接收外部设备电平变化信号;检测有稳定高低电平的外部传感器信号(如霍尔、红外传感器 );外部中断信号输入检测;数字输入信号检测。

  4. 特点:能真实反映外部电平,但易受干扰,引脚悬空时读数无参考意义。

  5. 上拉输入

  6. 原理:内部连接上拉电阻,无外部输入信号时,GPIO 端口保持高电平。

  7. 使用场景:机械按键或拨动开关输入,未按下时为高电平,按下接地变低电平;IIC 通信 SDA 引脚,保证总线数据传输默认高电平;SPI 通信从设备选择引脚(NSS),无信号时保持高,主设备选择时拉低 ;继电器状态等开关量信号输入检测;电源检测引脚,检测电源供电状态。

  8. 特点:确保无外部信号时输入为高电平,增强信号稳定性,防信号漂移。

  9. 下拉输入

  10. 原理:内部连接下拉电阻,无外部输入信号时,GPIO 端口保持低电平。

  11. 使用场景:CAN_RX 引脚接收 CAN 总线信号,确保总线无信号时引脚低电平;按钮接地触发的按键输入,未按下时低电平,按下拉高 ;默认低电平的数字电路信号输入;下拉电阻保持低电平的光电开关等传感器输入;检测外部设备低电平状态的电路。

  12. 特点:确保无外部信号时输入为低电平,适用于外部信号常态为高的检测场景。

  13. 模拟输入

  14. 原理:输入信号不经施密特触发器处理,直接接入内部 ADC,将模拟信号转为数字信号。

  15. 使用场景:连接温度、光照、湿度、气压等模拟传感器采集信号;电池电压检测;电流检测(通过分流电阻和运算放大器转换为电压信号 );光强检测等。

  16. 特点:用于采集连续变化的模拟量,供 MCU 处理分析。

输出模式

  1. 推挽输出

  2. 原理:由两个互补晶体管组成,可输出高电平(接 VDD )和低电平(接 VSS ),能向负载灌电流或抽取电流,导通损耗小、效率高。

  3. 使用场景:驱动 LED、继电器、蜂鸣器;控制小型直流电机;SPI 通信的 SCK、MOSI、MISO 等需强电平信号的总线通信引脚;各类状态指示灯控制。

  4. 特点:驱动能力强,可快速切换高低电平,适合直接驱动数字负载。

  5. 开漏输出

  6. 原理:输出端类似三极管集电极,只能输出低电平(接 VSS ),输出高电平时为高阻态,需外部上拉电阻拉高。

  7. 使用场景:IIC 总线通信的 SCL 和 SDA 引脚;多设备共享数据线的通信总线;GPIO 中断信号输出(通过外部上拉电阻共享中断信号线 );不同电压域电源管理切换电路;电平转换(适配不同电压器件 )。

  8. 特点:可实现线与逻辑,方便电平匹配,适合多设备通信及跨电压域应用,但高电平需外部上拉。

  9. 复用推挽输出

  10. 原理:GPIO 端口由片上外设控制,如定时器 PWM 输出、SPI 的 MOSI 和 MISO 等,兼具推挽输出特性,能主动提供电流驱动负载。

  11. 使用场景:UART 通信发送端(TX 引脚 );SPI 通信的时钟线(SCK)、主输出从输入(MOSI)、主输入从输出(MISO)引脚 ;CAN 通信发送端(TX 引脚 );伺服电机或 DC 电机控制的 PWM 信号输出;外部设备的时钟、使能等控制信号输出。

  12. 特点:用于特定外设功能,借助推挽输出特性提供稳定驱动。

  13. 复用开漏输出

  14. 原理:GPIO 端口由片上外设控制,输出模式为开漏输出,高电平需外部或内部上拉电阻,可实现线与逻辑。

  15. 使用场景:IIC 通信的 SDA 和 SCL 引脚(多设备共享 );SMBus 通信(类似 IIC 协议 );1 – Wire 单总线通信;MCU 接收多个外设中断信号(避免电平冲突 );电源管理信号(控制电压域转换 )。

  16. 特点:适用于特定外设多设备共享总线通信,需外部上拉电阻配合,可解决电平冲突问题。

这里只需要大概了解一下即可,后面结合具体外设理解


GPIO在后面会频繁使用,我们要熟悉使用流程

1.配置时钟2.初始化结构体

GPIO输出

以LED介绍GPIO的使用


#include 'stm32f10x.h'                  // Device header

void LED_Init()

{

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

GPIO_InitTypeDef GPIO_InitStructure;

GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_2;

GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;

GPIO_Init(GPIOA,&GPIO_InitStructure);

GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2);

}

void LED1_ON()

{

GPIO_ResetBits(GPIOA,GPIO_Pin_1);

}

void LED1_OFF()

{

GPIO_SetBits(GPIOA,GPIO_Pin_1);

}

void LED1_Turn()

{

if(GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_1)==0)

{

GPIO_SetBits(GPIOA,GPIO_Pin_1);

}

else

{

GPIO_ResetBits(GPIOA,GPIO_Pin_1);

}

}


LED闪烁

#include 'stm32f10x.h'                  // Device header

#include 'Delay.h'


int main(void)

{

/*开启时钟*/

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟

//使用各个外设前必须开启时钟,否则对外设的操作无效

/*GPIO初始化*/

GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //GPIO模式,赋值为推挽输出模式

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //GPIO引脚,赋值为第0号引脚

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //GPIO速度,赋值为50MHz

GPIO_Init(GPIOA, &GPIO_InitStructure); //将赋值后的构体变量传递给GPIO_Init函数

//函数内部会自动根据结构体的参数配置相应寄存器

//实现GPIOA的初始化

/*主循环,循环体内的代码会一直循环执行*/

while (1)

{

/*设置PA0引脚的高低电平,实现LED闪烁,下面展示3种方法*/

/*方法1:GPIO_ResetBits设置低电平,GPIO_SetBits设置高电平*/

GPIO_ResetBits(GPIOA, GPIO_Pin_0); //将PA0引脚设置为低电平

Delay_ms(500); //延时500ms

GPIO_SetBits(GPIOA, GPIO_Pin_0); //将PA0引脚设置为高电平

Delay_ms(500); //延时500ms

/*方法2:GPIO_WriteBit设置低/高电平,由Bit_RESET/Bit_SET指定*/

GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET); //将PA0引脚设置为低电平

Delay_ms(500); //延时500ms

GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET); //将PA0引脚设置为高电平

Delay_ms(500); //延时500ms

/*方法3:GPIO_WriteBit设置低/高电平,由数据0/1指定,数据需要强转为BitAction类型*/

GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)0); //将PA0引脚设置为低电平

Delay_ms(500); //延时500ms

GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)1); //将PA0引脚设置为高电平

Delay_ms(500); //延时500ms

}

}


LED流水灯

#include 'stm32f10x.h'                  // Device header

#include 'Delay.h'


int main(void)

{

/*开启时钟*/

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟

//使用各个外设前必须开启时钟,否则对外设的操作无效

/*GPIO初始化*/

GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //GPIO模式,赋值为推挽输出模式

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All; //GPIO引脚,赋值为所有引脚

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //GPIO速度,赋值为50MHz

GPIO_Init(GPIOA, &GPIO_InitStructure); //将赋值后的构体变量传递给GPIO_Init函数

//函数内部会自动根据结构体的参数配置相应寄存器

//实现GPIOA的初始化

/*主循环,循环体内的代码会一直循环执行*/

while (1)

{

/*使用GPIO_Write,同时设置GPIOA所有引脚的高低电平,实现LED流水灯*/

GPIO_Write(GPIOA, ~0x0001); //0000 0000 0000 0001,PA0引脚为低电平,其他引脚均为高电平,注意数据有按位取反

Delay_ms(100); //延时100ms

GPIO_Write(GPIOA, ~0x0002); //0000 0000 0000 0010,PA1引脚为低电平,其他引脚均为高电平

Delay_ms(100); //延时100ms

GPIO_Write(GPIOA, ~0x0004); //0000 0000 0000 0100,PA2引脚为低电平,其他引脚均为高电平

Delay_ms(100); //延时100ms

GPIO_Write(GPIOA, ~0x0008); //0000 0000 0000 1000,PA3引脚为低电平,其他引脚均为高电平

Delay_ms(100); //延时100ms

GPIO_Write(GPIOA, ~0x0010); //0000 0000 0001 0000,PA4引脚为低电平,其他引脚均为高电平

Delay_ms(100); //延时100ms

GPIO_Write(GPIOA, ~0x0020); //0000 0000 0010 0000,PA5引脚为低电平,其他引脚均为高电平

Delay_ms(100); //延时100ms

GPIO_Write(GPIOA, ~0x0040); //0000 0000 0100 0000,PA6引脚为低电平,其他引脚均为高电平

Delay_ms(100); //延时100ms

GPIO_Write(GPIOA, ~0x0080); //0000 0000 1000 0000,PA7引脚为低电平,其他引脚均为高电平

Delay_ms(100); //延时100ms

}

}


   蜂鸣器

这个是有源蜂鸣器   ,没法唱天空之城,差评


#include 'stm32f10x.h'                  // Device header

#include 'Delay.h'


int main(void)

{

/*开启时钟*/

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟

//使用各个外设前必须开启时钟,否则对外设的操作无效

/*GPIO初始化*/

GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //GPIO模式,赋值为推挽输出模式

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; //GPIO引脚,赋值为第12号引脚

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //GPIO速度,赋值为50MHz

GPIO_Init(GPIOB, &GPIO_InitStructure); //将赋值后的构体变量传递给GPIO_Init函数

//函数内部会自动根据结构体的参数配置相应寄存器

//实现GPIOB的初始化

/*主循环,循环体内的代码会一直循环执行*/

while (1)

{

GPIO_ResetBits(GPIOB, GPIO_Pin_12); //将PB12引脚设置为低电平,蜂鸣器鸣叫

Delay_ms(100); //延时100ms

GPIO_SetBits(GPIOB, GPIO_Pin_12); //将PB12引脚设置为高电平,蜂鸣器停止

Delay_ms(100); //延时100ms

GPIO_ResetBits(GPIOB, GPIO_Pin_12); //将PB12引脚设置为低电平,蜂鸣器鸣叫

Delay_ms(100); //延时100ms

GPIO_SetBits(GPIOB, GPIO_Pin_12); //将PB12引脚设置为高电平,蜂鸣器停止

Delay_ms(700); //延时700ms

}

}

GPIO输入

 按键控制LED


IPU为上拉输入(这里还没学定时器,还是用delay消抖)


#include 'stm32f10x.h'                  // Device header

#include 'Delay.h'


/**

  * 函    数:按键初始化

  * 参    数:无

  * 返 回 值:无

  */

void Key_Init(void)

{

/*开启时钟*/

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟

/*GPIO初始化*/

GPIO_InitTypeDef GPIO_InitStructure;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOB, &GPIO_InitStructure); //将PB1和PB11引脚初始化为上拉输入

}


/**

  * 函    数:按键获取键码

  * 参    数:无

  * 返 回 值:按下按键的键码值,范围:0~2,返回0代表没有按键按下

  * 注意事项:此函数是阻塞式操作,当按键按住不放时,函数会卡住,直到按键松手

  */

uint8_t Key_GetNum(void)

{

uint8_t KeyNum = 0; //定义变量,默认键码值为0

if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) //读PB1输入寄存器的状态,如果为0,则代表按键1按下

{

Delay_ms(20); //延时消抖

while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0); //等待按键松手

Delay_ms(20); //延时消抖

KeyNum = 1; //置键码为1

}

if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0) //读PB11输入寄存器的状态,如果为0,则代表按键2按下

{

Delay_ms(20); //延时消抖

while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0); //等待按键松手

Delay_ms(20); //延时消抖

KeyNum = 2; //置键码为2

}

return KeyNum; //返回键码值,如果没有按键按下,所有if都不成立,则键码为默认值0

}


光敏电阻控制蜂鸣器

也是化身电报专家了


#include 'stm32f10x.h'                  // Device header


/**

  * 函    数:光敏传感器初始化

  * 参    数:无

  * 返 回 值:无

  */

void LightSensor_Init(void)

{

/*开启时钟*/

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟

/*GPIO初始化*/

GPIO_InitTypeDef GPIO_InitStructure;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOB, &GPIO_InitStructure); //将PB13引脚初始化为上拉输入

}


/**

  * 函    数:获取当前光敏传感器输出的高低电平

  * 参    数:无

  * 返 回 值:光敏传感器输出的高低电平,范围:0/1

  */

uint8_t LightSensor_Get(void)

{

return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13); //返回PB13输入寄存器的状态

}


学习stm32过程中,感觉自己真的要成为工程师了,cv工程师(bushi)。在使用外设前,要先去函数库找到需要使用的函数,然后一个个复制过去,然后再去查看函数的定义,配置里面的参数,需要逻辑的大多都是在主函数里面写(也有可能是入门学习的原因)


进入单片机查看更多内容>>
相关视频
  • 【TI MSPM0 应用实战】智能小车+工业角度编码器+血氧仪+烟雾探测器!硬核参考设计详解!

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

  • 直播回放: Microchip Timberwolf™ 音频处理器在线研讨会

  • 基于灵动MM32W0系列MCU的指夹血氧仪控制及OTA升级应用方案分享

精选电路图
  • 1瓦四级调频发射机

  • 500W MOS场效应管电源逆变器,12V转110V/220V

  • 12V 转 28V DC-DC 变换器(基于 LM2585)

  • 红外开关

  • 12V转110V/220V 500W逆变器

  • DS1669数字电位器

    相关电子头条文章