当前位置: 首页 > news >正文

GPIO详细介绍

一.GPIO是什么

GPIO = 单片机通用引脚,用来输出高低电平、读取外部电平,是单片机和硬件交互最基础的通道。


二. 输入输出模式

2.1、 输入类(4 种)

浮空输入 :引脚无上下拉,悬空时电平随机,仅适合外部自带驱动的信号。

上拉输入 :内部接上拉电阻,默认高电平,按键检测最常用。

下拉输入 :内部接下拉电阻,默认低电平。

图1 上拉输入/下拉输入(两个差不多只是上电默认电平不同)

模拟输入 :关闭数字电路,专供 ADC 电压采集,不能做普通 IO。

图2 模拟输入

2.2、通用输出类(2 种)

推挽输出:高低电平都能驱动,驱动 LED、指示灯首选。

D:漏极 G:栅极 S:源极

N-MOS(下管):栅极电压 > 源极电压(Source接地,即0V)时导通。也就是说,给栅极高电平(1,如3.3V),NMOS就导通。

PMOS(上管):栅极电压<源极电压(Source接VDD,即3.3V)时导通。也就是说,必须给栅极低电平(0,如0V),PMOS才导通。下面两个分别是推挽模式写入高低电平的电流走向图:

图3 推挽输出 置1

图4 推挽输出 置0

为什么电平到输出控制那里变成相反的电平了,因为输出控制那个位置串联着一个反相器(非门)所以变成相反的电平了


开漏输出:只能主动拉低,高电平需要外部上拉,多用于总线通讯。

在开漏输出模式中P-MOS是不工作的,N-MOS又只能输出低电平,在写入1(高电平)时VG==VS,N-MOS管截止,这个时候I/O口属于高阻态的状态,想要输出高电平必须要自己外接上拉电阻。

高阻态(High-Z):这是引脚内部驱动器的状态。指的是芯片内部的 N-MOS 和 P-MOS 全部截止(关闭),导致引脚内部与 VDD 和 VSS 完全断开。这是个“电路动作”,描述的是“芯片有没有在干活”。

为什么非要“外接”而不用内部的?
因为内部上拉电阻阻值太大(40kΩ),驱动能力极弱。在 I2C 高速通信(400kHz 或更快)时,这么大的电阻加上线路寄生电容,会导致上升沿极其缓慢(波形变“钝”),造成通信时序错乱。
所以,外部上拉电阻通常选用 2kΩ ~ 10kΩ(阻值越小,上升沿越快,但功耗越大)。这在硬件电路设计时是必须外挂的元器件。下面是开漏输出的写入低电平的电流走向图:

图5 开漏输出

2.3、复用外设输出类(2 种)

复用推挽输出:引脚分配给串口、定时器 PWM 等外设,推挽驱动。

复用推挽、复用开漏的 MOS 驱动电路结构和普通推挽 / 开漏完全一致,区别仅在于驱动信号来源不同:普通模式由 CPU 写输出寄存器控制,复用模式由片上外设直接输出信号控制,软件寄存器不再干预引脚电平。下面是复用推挽输出的写入高电平的电流走向图:

图6 复用推挽输出

复用开漏输出:外设复用 + 开漏,典型用于 I2C 总线。


三、编写驱动程序

3.1、介绍部分寄存器(GPIO、RCC)

3.1.1、APB2 外设时钟使能寄存器(RCC_APB2ENR)

它是挂载在APB2总线上的所有外设(GPIO、USART1、ADC1等)的“总电闸”——复位后默认全部关闭,使用前必须通过该寄存器把对应位置1打开时钟。

3.1.2、端口配置低寄存器(GPIOx_CRL) (x=A..E)

它用 4 个位(共 32 位)控制一个引脚,同时决定了该引脚是输入/输出、输出速度、以及推挽/开漏/复用/模拟等具体模式。这是配置该GPIO的低8位引脚

高两位CNFy[1:0],低二位MODEy[1:0]

3.1.3、端口配置高寄存器(GPIOx_CRH) (x=A..E)

它用 4 个位(共 32 位)控制一个引脚,同时决定了该引脚是输入/输出、输出速度、以及推挽/开漏/复用/模拟等具体模式。这是配置该GPIO的高8位引脚

高两位CNFy[1:0],低二位MODEy[1:0]

3.1.4、端口输入数据寄存器(GPIOx_IDR) (x=A..E)

这是一个“只读”寄存器,它的 16 个有效位(Bit0~Bit15)直接反映了 GPIO 引脚 Px0~Px15 当前的实时电平状态(高/低)。 你读到的 1 就是高电平(3.3V),读到的 0 就是低电平(0V)。

3.1.5、端口输出数据寄存器(GPIOx_ODR) (x=A..E)

它是一个“可读可写”的寄存器,向对应位写入 1 让引脚输出高电平(3.3V),写入 0 让引脚输出低电平(0V)。 但写入的效果取决于当前引脚的模式:推挽输出时直接驱动电压,开漏输出时只能控制拉低,而在输入模式下它另有妙用(控制上下拉)。

3.1.6、端口位设置/清除寄存器(GPIOx_BSRR) (x=A..E)

这是一个 32 位的“只写”寄存器(读回值无效),低 16 位用来将对应引脚输出高电平,高 16 位用来将对应引脚输出低电平。 向任意位写 1 执行动作,写 0 则无影响。最核心的优势是:一条硬件指令即可完成置位或清零,绝对原子,永不被打断。

3.1.7、端口位清除寄存器(GPIOx_BRR) (x=A..E)

这是一个 32 位的“只写”寄存器,只有低 16 位有效(对应 Px0~Px15)。向某个位写 1,对应引脚就被拉低为 0V;写 0 则无任何影响。 它和 BSRR 配合,构成了 F10x 下原子操作 GPIO 的“黄金搭档”。

3.1.8、端口配置锁定寄存器(GPIOx_LCKR) (x=A..E)

一旦按特定序列锁定,GPIO 引脚的模式配置(输入/输出/复用等)就会被硬件冻结,直到下次复位前都无法再修改。 它像一个保险开关,专门用来防止关键引脚的配置被意外篡改。

3.2、STM32编程

我们今天的任务是使用STM32F103C8T6最小系统板,点亮一个led灯让其进行闪烁,在原理图上led灯连接的是PC13低电平点亮,所以我们控制PC13一直产生一个高低电平即可

3.2.1、寄存器编程

先找到外设基地址,打开STM32f10x-中文参考手册,在储存器映射这里最下面就是外设基地址也是APB1的基地址

直接写在代码里面

/*片上外设基地址 */ #define PERIPH_BASE ((unsigned int)0x40000000) #define APB1PERIPH_BASE PERIPH_BASE

一下是APB2和AHB的基地址,AHB原本应该用0x40018000的我们这边为了方便使用0x40020000

/*APB2 总线基地址 */ #define APB2PERIPH_BASE (PERIPH_BASE+0x10000) /* AHB总线基地址 */ #define AHBPERIPH_BASE (PERIPH_BASE+0x20000)

找到GPIOC的外设基地址

/*GPIOC外设基地址*/ #define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)

GPIO的寄存器我们只使用到了GPIOC_CRH,GPIOC_ODR,直接用宏定义#define定义用GPIOC的外设基地址加上我们要使用到的寄存器的偏移地址就可以了

/* GPIOC寄存器地址,强制转换成指针 */ #define GPIOC_CRH *(unsigned int *)(GPIOC_BASE + 0x04) #define GPIOC_ODR *(unsigned int *)(GPIOC_BASE + 0x0C)

除了以上的几个寄存器还是不够的,我们还需要启动该GPIO的时钟

因为RCC是在AHB上面,所以我们用AHB的基地址加上RCC的基地址,然后再加RCC_APB2ENR

寄存器的偏移地址就可以了

(unsigned int *):强制转换成无符号32位的指针类型

*:解引用(找到地址里的数据)

/*RCC外设基地址*/ #define RCC_BASE (AHBPERIPH_BASE + 0x1000) /*RCC的AHB1时钟使能寄存器地址,强制转换成指针*/ #define RCC_APB2ENR *(unsigned int *)(RCC_BASE + 0x18)

主函数文件夹

#include "stm32f10x.h" void Delay(unsigned int us) { for(volatile unsigned int y = 0;y < us;y++) { ; } } /** * 主函数 */ int main(void) { // 开启GPIOC 端口时钟 RCC_APB2ENR |= (1<<4); //先清空控制PC13的端口位 GPIOC_CRH &= ~( 0x0F<< (4*5));//~( 0x0F<< (4*5)) == 1111 1111 0000 1111 | 1111 1111 1111 1111 // 然后再配置PC13为通用推挽输出,速度为10M GPIOC_CRH |= (0x01<<4*5); // PC13 输出 低电平 GPIOC_ODR &= ~(1<<13); while(1) { // PC13 输出 高电平 GPIOC_ODR |= (1<<13); //直接操控ODR来置位/清零可能会被中断打断不安全 //可以用下面这这两个寄存器来做置位/清零 //GPIOC_BSRR = (1 << 13); // 高电平 //GPIOC_BRR = (1 << 13); // 低电平 (F10x 专用) // //延时 Delay(0xffff); // P13 输出 低电平 GPIOC_ODR &= ~(1<<13); //延时 Delay(0xffff); } } // 函数为空,目的是为了骗过编译器不报错 void SystemInit(void) { ; }

3.2.2、标准库操作

#include "stm32f10x.h" // Device header //延时 void Delay(unsigned int us) { for(volatile unsigned int y = 0;y < us;y++) { ; } } int main(void) { //启动GPIOC的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //创建一个GPIO的初始化结构体 GPIO_InitTypeDef GPIO_InitStructure; //使用推挽输出 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //使用PC13 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; //速度配置为50MHz GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //以上程序还没有写入GPIOC的寄存器的 //GPIO_Init将刚刚配置的全部写到寄存器 GPIO_Init(GPIOC, &GPIO_InitStructure); while (1) { //PC13置零 GPIO_ResetBits(GPIOC, GPIO_Pin_13); //延时 Delay(0xffff); //PC13置零 GPIO_SetBits(GPIOC, GPIO_Pin_13); //延时 Delay(0xffff); } }

上面大部分图片来源为《STM32F10x-中文参考手册》


如果你觉得有帮助,欢迎点赞、收藏、评论,让更多人看到!

http://www.cnnetsun.cn/news/3074458.html

相关文章:

  • 汽车软件测试,难点到底在哪?
  • 2026年7月零代码网站搭建与企业无代码建站工具测评:谁更适合你,
  • 手机AI Agent系统级集成实战:从架构到代码的完整指南
  • 告别信息过载:利用聚合平台的 Grok 模型快速提炼长文章核心观点
  • 英伟达“技术没有秘密“合理吗:研发总监拆解护城河的真相
  • Dify实战教程:从零搭建企业级AI应用,掌握低代码开发与工作流设计
  • TEE-TA学习轨迹第八篇:optee_os源码下TA分析之-app_secrets
  • Unsloth量化实战:消费级显卡(12GB)跑通8B大模型
  • 解决方案|腾讯安全天御金融反电诈产品解决方案
  • 09505黄大年茶思屋榜文95期 第5题 三方 CaaS下 CloudOS存储 Bypass关键技术
  • GPU PRO 5 - 4.2 Deferred Rendering Techniques on Mobile Devices 笔记
  • 【Java踩坑笔记】14_Collections.singletonList的坑:不能add也不能set
  • 2026年6月GESP真题及题解(C++一级):去旅行
  • pthread_create通过加锁设置线程启动竞争条件
  • 如何高效使用Diablo Edit2:暗黑破坏神2存档编辑器的完整指南
  • 查新报告分为哪几种?科技查新、查收查引与专利查新区别
  • Advanced XRay技术深度解析:如何通过方块渲染优化实现高效矿石定位
  • 5分钟免费让Windows拥有macOS优雅鼠标指针的完整指南
  • 用Backtrader回测DMI指标:一个Python量化新手的实战踩坑记录(附完整代码)
  • 基于sigrity的TDR/TDT仿真设计
  • 数据安全检查,这3个API盲区最容易被问穿
  • 如何用中文工作流轻松玩转AI绘画?这份完整指南让你从入门到精通
  • 第018章:ComfyUI文生图Z-Image模型创建数字人模特(二)
  • 01 静态分析(Static Analysis)
  • PKMS+AppOps 双权限体系:隐私管控、特权白名单全流程源码剖析
  • 2026年桌面风扇类型选购要点:从四个核心部件看懂一台风
  • Java实现字符串匹配:别再让算法理论画饼,实际应用才是王道
  • 把 ES Repository 纳入 CMS 轨道,一套更稳的 SAP PI 内容传输治理方式
  • Bebas Neue:开源字体设计的几何美学革命
  • 与你的 Elasticsearch 数据对话:使用 Google ADK 和 MCP 构建一个实时语音 agent ,分为 3 个组件