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

【GPIO】从STM32F103入门GPIO寄存器

STM32 GPIO寄存器详解与操作对比

核心理论:每个GPIO端口(A-E)由7个寄存器控制,每组寄存器控制特定功能。下面按寄存器类型详细对比标准库和寄存器操作。


一、端口配置寄存器:GPIOx_CRL/CRH

功能:控制引脚工作模式(输入/输出/复用)和输出速度
位结构

  • GPIOx_CRL:控制Pin0-Pin7(低8位)
  • GPIOx_CRH:控制Pin8-Pin15(高8位)
  • 每4位控制1个引脚(共16引脚 × 4bit = 64位)
    CRL/CRH结构
场景1:配置PA2为浮空输入

标准库写法:

// 1.使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);// 2.配置引脚
GPIO_InitTypeDef gpio;
gpio.GPIO_Pin = GPIO_Pin_2;
gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &gpio);

寄存器写法:

// 1.使能时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;// 2.配置CRL寄存器
GPIOA->CRL &= ~(0x0F << 8);  // 清空位[11:8]
GPIOA->CRL |=  (0x04 << 8);  // CNF=01(浮空输入), MODE=00(输入)

场景2:配置PA2为推挽输出(50MHz)

标准库写法:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef gpio;
gpio.GPIO_Pin = GPIO_Pin_2;
gpio.GPIO_Mode = GPIO_Mode_Out_PP;
gpio.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio);

寄存器写法:

RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;GPIOA->CRL &= ~(0x0F << 8);  // 清空原有配置
GPIOA->CRL |=  (0x03 << 8);  // CNF=00(推挽), MODE=11(50MHz)

二、输入数据寄存器:GPIOx_IDR

功能:读取引脚当前电平状态(只读)
位结构:低16位对应引脚电平(0/1)
注意:该寄存器只能以16位的形式读出
IDR结构

场景:读取PA2电平

标准库写法:

uint8_t value = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_2);

寄存器写法:

// 方法1:位与操作(推荐)
uint8_t value = (GPIOA->IDR & GPIO_IDR_IDR2) ? 1 : 0;// 方法2:移位法
uint8_t value = (GPIOA->IDR >> 2) & 0x01;

三、输出数据寄存器:GPIOx_ODR

功能:控制输出电平 + 配置上拉/下拉电阻
位结构:低16位控制输出电平
ODR结构

场景1:设置PA2输出高电平

标准库写法:

GPIO_SetBits(GPIOA, GPIO_Pin_2);

寄存器写法:

GPIOA->ODR |= GPIO_ODR_ODR2;  // 直接置位ODR对应位

场景2:配置PA2为上拉输入

标准库写法:

GPIO_InitTypeDef gpio;
gpio.GPIO_Pin = GPIO_Pin_2;
gpio.GPIO_Mode = GPIO_Mode_IPU;  // 内置上拉配置
GPIO_Init(GPIOA, &gpio);

寄存器写法:

// 配置CRL:CNF2=10(上拉输入), MODE2=00(输入)
GPIOA->CRL &= ~(GPIO_CRL_CNF2 | GPIO_CRL_MODE2); // 清除原有配置
GPIOA->CRL |= GPIO_CRL_CNF2_1;                   // CNF2[1]=1, CNF2[0]=0 → 10
// 或直接操作寄存器地址:
// *(volatile uint32_t*)0x40010800 &= ~(0x0F << 8); // 清除位[11:8]
// *(volatile uint32_t*)0x40010800 |= (0x08 << 8);   // 设置0x8(二进制1000)// 启用上拉电阻
GPIOA->ODR |= GPIO_ODR_ODR2;  // ODR对应位置1
// 或直接操作寄存器地址:*(volatile uint32_t*)0x4001080C |= (1 << 2);

在这里插入图片描述

四、位设置寄存器:GPIOx_BSRR

功能:原子操作输出电平(避免ODR直接操作冲突)
位结构

  • 低16位:置位引脚(1→高电平)
  • 高16位:复位引脚(1→低电平)
    BSRR结构
场景:PA2输出高电平+PA3输出低电平

标准库写法:

GPIO_SetBits(GPIOA, GPIO_Pin_2);
GPIO_ResetBits(GPIOA, GPIO_Pin_3);

寄存器写法:

// 单条指令完成双操作
GPIOA->BSRR = GPIO_BSRR_BS2 | GPIO_BSRR_BR3;

五、位清除寄存器:GPIOx_BRR

功能:快速清除输出电平(专用于低电平输出)
位结构:低16位控制清零操作
BRR结构

场景:设置PA2输出低电平

标准库写法:

GPIO_ResetBits(GPIOA, GPIO_Pin_2);

寄存器写法:

// 专用清零寄存器
GPIOA->BRR = GPIO_BRR_BR2;  

六、锁定寄存器:GPIOx_LCKR

功能:锁定CRL/CRH配置防止意外修改
锁定序列:写1→写0→写1→读0→读1
LCKR结构

场景:锁定PA2配置

标准库写法:

GPIO_PinLockConfig(GPIOA, GPIO_Pin_2);

寄存器写法:

// 锁定序列实现
GPIOA->LCKR = GPIO_LCKR_LCK2;    // 选择锁定PA2
GPIOA->LCKR |= GPIO_LCKR_LCKK;   // Step1: LCKK=1
GPIOA->LCKR &= ~GPIO_LCKR_LCKK;  // Step2: LCKK=0
GPIOA->LCKR |= GPIO_LCKR_LCKK;   // Step3: LCKK=1
volatile uint32_t tmp = GPIOA->LCKR;  // Step4: 读LCKK(应为0)
tmp = GPIOA->LCKR;               // Step5: 读LCKK(应为1)

关键区别总结

  1. 配置效率

    • 标准库:封装性好,但存在函数调用开销
    • 寄存器:直接操作硬件,效率更高
  2. 多引脚操作

    • 标准库:需多次调用函数
    GPIO_SetBits(GPIOA, GPIO_Pin_0);
    GPIO_ResetBits(GPIOA, GPIO_Pin_1);
    
    • 寄存器:单条指令完成
    GPIOA->BSRR = GPIO_BSRR_BS0 | GPIO_BSRR_BR1;
    
  3. 代码可读性

    • 标准库:函数名自解释(GPIO_Mode_IN_FLOATING
    • 寄存器:需查阅手册理解位含义(CNF=01, MODE=00
  4. 安全性

    • 标准库:内置参数检查
    • 寄存器:直接操作硬件,需开发者保证正确性

扩展:✅位操作分析GPIOA_CRL &= ~(0xF << 8);

// 正确操作
GPIOA_CRL &= ~(0xF << 8);  // 等价于 GPIOA_CRL &= 0xFFFFF0FF;
操作二进制表示(32位)作用范围
0xF0000 0000 0000 1111
0xF << 80000 0000 1111 0000 0000 0000位[11:8]区域
~(0xF << 8)1111 1111 0000 1111 1111 1111掩码(取反后)
最终效果仅清空位[11:8]PA2专属区域

关键区别图示:
假设原始CRL值:0x12345678

目标:仅清除PA2配置(位[11:8])✅ 正确操作:
原始值:0001 0010 0011 0100 0101 0110 0111 1000
掩码:  1111 1111 1111 1111 0000 1111 1111 1111 (0xFFFFF0FF)
结果:  0001 0010 0011 0100 0000 0110 0111 1000 → 仅位[11:8]清零❌ 错误操作:
原始值:0001 0010 0011 0100 0101 0110 0111 1000
掩码:  0000 0000 0000 0000 0000 0000 0000 0000 (全0)
结果:  0000 0000 0000 0000 0000 0000 0000 0000 → 整个寄存器清零!

最佳实践建议:

// 推荐写法(可读性更高):
#define PA2_CLEAR_MASK  (0xF << 8)   // 定义清除掩码
GPIOA_CRL &= ~PA2_CLEAR_MASK;// 或直接使用十六进制:
GPIOA_CRL &= ~0x00000F00;  // 0x00000F00 = (0xF << 8)

永远记住:在嵌入式寄存器操作中,清零特定区域必须使用位掩码+取反,直接赋0会导致整个寄存器被意外清除!这是嵌入式开发中最常见的错误之一。


GPIO操作中移位运算的风险与BSRR/BRR解决方案

问题本质:直接操作ODR的风险
当直接使用移位运算操作GPIOx_ODR寄存器时,主要存在两个问题:

  1. 非原子操作:读-改-写过程可能被中断打断
  2. 位覆盖风险:移位操作可能意外改变其他引脚状态

风险代码示例

// 危险操作:使用移位设置PA2输出高电平
GPIOA->ODR = (1 << 2);  // 将1左移2位后赋值给ODR// 等效操作:
// 假设原始ODR = 0xFFFF (所有引脚高电平)
// 操作后ODR = 0x0004 (仅PA2高电平,其他全低)

移位操作风险详解

场景:同时控制PA2和PA3

// 目标:PA2输出高,PA3输出低// 错误实现:
GPIOA->ODR = (1 << 2);  // 设置PA2高
GPIOA->ODR = (0 << 3);  // 设置PA3低 → 实际清除了PA2!// 实际效果:
// 第一条指令后:ODR = 0000 0000 0000 0100
// 第二条指令后:ODR = 0000 0000 0000 0000 (PA2也被清除)

位运算分析

// 看似正确的错误写法:
GPIOA->ODR |= (1 << 2);  // 设置PA2高
GPIOA->ODR &= ~(1 << 3); // 清除PA3低// 风险点:
// 1. 非原子操作:两条指令间可能被中断打断
// 2. 若PA2和PA3都需要改变,需要执行两次寄存器访问
// 3. 当多个任务操作同一GPIO端口时可能冲突

BSRR/BRR寄存器的解决方案

BSRR寄存器工作原理
BSRR结构

  • 低16位 (BSy):置位操作 (1→高电平)
  • 高16位 (BRy):复位操作 (1→低电平)
  • 关键特性:写0的位不影响当前状态

安全实现方案

// 原子操作:同时设置PA2高+PA3低
GPIOA->BSRR = (1 << 2) | (1 << (16 + 3)); // 位运算分解:
//  低16位: 0000 0000 0000 0100 (设置PA2)
//  高16位: 0000 0000 0000 1000 (清除PA3)
//  合并值: 0x00040008

BRR寄存器补充

// 专用清零寄存器 (等效BSRR高16位)
GPIOA->BRR = (1 << 3);  // 清除PA3// 等效于:
GPIOA->BSRR = (1 << (16 + 3));

对比实验

测试场景
控制开发板上两个LED:

  • LED1 (PA2):高电平点亮
  • LED2 (PA3):低电平点亮

危险代码(ODR移位)

while(1) {// 尝试同时点亮两个LEDGPIOA->ODR = (1 << 2);    // PA2高 (点亮LED1)GPIOA->ODR &= ~(1 << 3);  // PA3低 (点亮LED2)// 实际效果:LED1短暂亮后熄灭// 原因:第二行清除了PA2
}

安全代码(BSRR)

while(1) {// 原子操作同时控制两个LEDGPIOA->BSRR = (1 << 2) | (1 << (16 + 3)); // LED1亮(PA2高) + LED2亮(PA3低)// 延时后关闭delay_ms(500);GPIOA->BSRR = (1 << (16 + 2)) | (1 << 3); // LED1灭(PA2低) + LED2灭(PA3高)
}

位运算原理图解

ODR直接操作风险

初始状态:PA0-PA15全高 (ODR=0xFFFF)
目标:设置PA2高,保持其他位不变错误操作:ODR = (1 << 2) → 二进制 0000 0100结果:PA2高,但其他所有引脚被强制置低!

BSRR安全操作

初始状态:任意
操作:BSRR = (1 << 2) | (1 << 19) 位分解:[31:16] BR: 0000 0000 0000 1000 (清除PA3)[15:0]  BS: 0000 0000 0000 0100 (设置PA2)效果:仅修改PA2和PA3,其他引脚保持不变

使用原则总结

  1. 设置单个引脚高电平

    // 推荐
    GPIOx->BSRR = (1 << Pin);// 避免
    GPIOx->ODR |= (1 << Pin);
    
  2. 设置单个引脚低电平

    // 推荐
    GPIOx->BRR = (1 << Pin);
    // 或
    GPIOx->BSRR = (1 << (16 + Pin));
    
  3. 同时设置多个引脚

    // 原子操作
    GPIOx->BSRR = (1 << PinA) | (1 << (16 + PinB));
    
  4. 切换引脚状态

    // 最优方案
    GPIOx->ODR ^= (1 << Pin);  // 异或操作切换状态// 替代方案(两条指令)
    if(GPIOx->ODR & (1 << Pin)) GPIOx->BRR = (1 << Pin);
    elseGPIOx->BSRR = (1 << Pin);
    

关键结论:BSRR/BRR寄存器通过"只影响目标位"的设计,从根本上解决了ODR直接操作时的位覆盖问题,同时提供原子操作保证,是多引脚控制场景的最佳选择。


文章转载自:
http://buffoonery.wsgyq.cn
http://blutwurst.wsgyq.cn
http://aeriality.wsgyq.cn
http://affectingly.wsgyq.cn
http://calorescence.wsgyq.cn
http://canned.wsgyq.cn
http://ameer.wsgyq.cn
http://bimolecular.wsgyq.cn
http://chincherinchee.wsgyq.cn
http://aye.wsgyq.cn
http://alienability.wsgyq.cn
http://abjective.wsgyq.cn
http://agenize.wsgyq.cn
http://awoken.wsgyq.cn
http://camelopardalis.wsgyq.cn
http://characterology.wsgyq.cn
http://bragi.wsgyq.cn
http://chatelain.wsgyq.cn
http://amyotrophy.wsgyq.cn
http://bulbaceous.wsgyq.cn
http://campari.wsgyq.cn
http://boliviano.wsgyq.cn
http://bottomland.wsgyq.cn
http://bepuzzlement.wsgyq.cn
http://brucellosis.wsgyq.cn
http://autocoid.wsgyq.cn
http://barbuda.wsgyq.cn
http://bacteriocin.wsgyq.cn
http://beekeeper.wsgyq.cn
http://aswoon.wsgyq.cn
http://www.dtcms.com/a/281022.html

相关文章:

  • Video Python(Pyav)解码一
  • 面试150 完全二叉树的节点数
  • 力扣73:矩阵置零
  • 20250715_Sneak_neuro 靶机复盘
  • 三种深度学习模型(LSTM、CNN-LSTM、贝叶斯优化的CNN-LSTM/BO-CNN-LSTM)对北半球光伏数据进行时间序列预测
  • 【15】MFC入门到精通——MFC弹窗提示 MFC关闭对话框 弹窗提示 MFC按键触发 弹窗提示
  • C++(STL源码刨析/stack/queue/priority_queue)
  • Linux操作系统之信号:保存与处理信号
  • 23种设计模式--#1工厂模式
  • 运维打铁: 软件定义网络(SDN)的实践应用
  • tun2socks原理浅析
  • 在新闻资讯 APP 中添加不同新闻分类页面,通过 ViewPager2 实现滑动切换
  • 【LeetCode 热题 100】226. 翻转二叉树——DFS
  • Halcon双相机单标定板标定实现拼图
  • 野卡恢复运营?关服已成趋势
  • 路由器的核心原理以及作用
  • 某东 jdgs参数unidbg环境检测patch分析
  • 健康监测的微泰医疗的公司创始人背景、股权构成、产品类型及技术能力的全方位解读
  • 腾讯元器开发流程解析
  • Enhancing Input-Label Mapping in In-Context Learning withContrastive Decoding
  • 每日钉钉API探索:chooseUserFromList灵活选取自定义联系人
  • 基于 p5.js 实现的未来感神经网络可视化特效
  • Jupyer 魔法方法
  • 专题 函数闭包
  • 海狸IM - 一个功能完整的开源即时通讯系统
  • 数据库中索引到底对哪些sql操作具有提速作用?
  • Python 模块导入常见错误及解决方法
  • 公网ip到服务器流程
  • 智慧水务平台,智慧水务,惠及民生,提升水务管理效率与服务质量
  • 开发者工具在爬虫开发中的应用与面板功能详解