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

STM32---GPIO

目录

一、GPIO原理图

二、操作BSRR/BRR寄存器保证原子性

1. I/O端口位的编程和访问限制

2. GPIOx_BSRR和GPIOx_BRR寄存器的作用

3. IRQ不会发生危险的含义

4. 具体例子

5. 总结

三、C++封装标准库的GPIO示例


        在学习STM32的时候,我们最开始学习的就是控制GPIO成为点灯大师。本文将基于STM32系统结构图,解读GPIO的电路结构与工作模式,帮助初学者快速掌握STM32的GPIO控制核心基础。并使用C++进行封装,提高代码的可读性、可移植性。

一、GPIO原理图

        在芯片手册中有一个描述了GPIO的电路图,通过对电路图的不同配置可以配置成不同的模式。

下面我们根据这个原理图分析一下各个电路模块有什么作用

VDD/VSS保护二极管:

位于 GPIO 引脚的上下两端,用于防止引脚外部过高或过低的电压输入。当引脚电压高于芯片供电电压(VDD)时,上方的二极管导通,将多余的电流引导到电源;当引脚电压低于地电位(VSS)时,下方的二极管导通,将电流引导到地,从而保护芯片免受损坏。

P-MOS 管和 N-MOS 管

组成推挽输出电路的核心部分。在推挽输出模式下,当输出高电平时,P-MOS 管导通,N-MOS 管截止,引脚输出高电平;当输出低电平时,P-MOS 管截止,N-MOS 管导通,引脚输出低电平。推挽输出模式具有输出电流能力强、负载能力大等优点,适用于驱动外部负载。

不过单片机的P-MOS通常设置为弱上拉,主要是为了平衡驱动能力与功耗。让单片机既能生成高电平防止浮空状态的干扰,又能有效降低功耗。如果真的需要大电流的上拉功能,可以自行在芯片外部接一个上拉电阻或者驱动增强模块,一般情况下,这个弱上拉只是用做控制信号,而并非直接驱动。

施密特触发器

图中写的是TTL肖特基触发器,可能是翻译的时候出问题了,实际上是叫做施密特触发器。它具有稳压和滤波的作用,能够将输入的模拟信号转换为稳定的数字信号。在输入模式下,施密特触发器可以过滤掉输入信号中的噪声干扰,确保输入电平的稳定性。

二、操作BSRR/BRR寄存器保证原子性

使用手册中有这样一句话:每个I/O端口位可以自由编程,然而必须按照32位字访问I/O端口寄存器(不允许半字或字节访问)。GPIOx_BSRR和GPIOx_BRR寄存器允许对任何GPIO寄存器进行读/更改的独立访问;这样,在读和更改访问之间产生IRQ时不会发生危险。

如何理解

1. I/O端口位的编程和访问限制

  • 每个I/O端口位可以自由编程
    • 这意味着GPIO的每个引脚(位)都可以独立设置为输入或输出,并且可以单独控制其状态(高电平或低电平)。
  • 必须按照32位字访问I/O端口寄存器
    • GPIO寄存器通常是一个32位的寄存器,访问时必须以32位为单位进行操作。尽管每个位都可以单独控制,但是必须要先记录所有位,然后统一写回操作。
    • 不允许半字(16位)或字节(8位)访问
      • 如果尝试以16位或8位的方式访问这些寄存器,可能会导致未定义的行为或硬件错误。

2. GPIOx_BSRRGPIOx_BRR寄存器的作用

  • GPIOx_BSRR(端口位设置/复位寄存器)
    • 这是一个特殊的寄存器,允许同时设置(置1)或复位(清0)GPIO引脚的状态,而无需读取或修改其他引脚的状态。
    • 通过写入不同的位,可以独立地设置或复位某个引脚,而不会影响其他引脚。
  • GPIOx_BRR(端口位复位寄存器)
    • 类似于BSRR,但专门用于复位(清0)GPIO引脚的状态。
    • 在某些微控制器中,GPIOx_BRR可能被整合到GPIOx_BSRR中,通过特定的位操作实现复位功能。
  • 独立访问
    • GPIOx_BSRRGPIOx_BRR的设计允许对GPIO寄存器进行独立的读/更改操作,而不需要先读取整个寄存器的值,修改后再写回。
    • 这种设计避免了在多任务或中断环境下可能出现的竞争条件(Race Condition)或数据不一致问题。

3. IRQ不会发生危险的含义

  • IRQ(中断请求)
    • 当一个中断发生时,CPU会暂停当前任务,转而执行中断服务程序(ISR)。在ISR执行期间,如果其他任务或中断试图访问同一个GPIO寄存器,可能会导致数据竞争或不一致。
  • 避免危险
    • 使用GPIOx_BSRRGPIOx_BRR寄存器时,由于它们是独立访问的,不会读取或修改其他引脚的状态,因此即使在中断发生时,也不会影响其他引脚的状态。
    • 这种设计减少了因中断导致的潜在错误,确保了系统的稳定性和可靠性。

4. 具体例子

假设你正在修改某个GPIO引脚的状态(如设置高电平或低电平),以下操作可能导致问题:

  • 传统方式:先读取当前状态,修改后再写回,可能导致数据竞争或冲突。
  • **使用GPIOx_BSRRGPIOx_BRR直接修改寄存器值,可能导致其他任务(如显示控制)被中断或覆盖。
  • 使用GPIOx_BSRRGPIOx_BRR
    • GPIOx_BSRR通过位操作直接修改寄存器值,不会影响其他引脚。
    • GPIOx_BRR通过位清除操作实现复位,但可能覆盖其他位。

5. 总结

  • 核心优势
    • GPIOx_BSRRGPIOx_BRR寄存器的设计允许对GPIO引脚进行独立、安全的操作,避免了在多任务或中断环境下的数据竞争问题。
  • 应用场景
    • 在实时性要求高的场景(如电机控制、传感器数据采集)中,这种设计可以确保在中断发生时,不会因为数据竞争或覆盖问题导致系统错误。

通过这种方式,系统能够在高并发或中断环境下稳定运行,确保GPIO操作的原子性和一致性。

三、C++封装标准库的GPIO示例

#pragma once
#include "stm32f10x.h" #include "stm32f10x_gpio.h"         //GPIO模块
#include "stm32f10x_rcc.h"          //RCC时钟控制模块 class GPIO_Base 
{
private:GPIO_TypeDef* port;    // GPIO端口,如GPIOA, GPIOB等uint16_t pin;          // GPIO引脚号,如GPIO_Pin_0, GPIO_Pin_1等public:// 构造函数GPIO_Base(GPIO_TypeDef* gpioPort, uint16_t gpioPin): port(gpioPort), pin(gpioPin) {}// 初始化GPIO引脚(假设为输出模式)void initAsOutput(uint32_t speed = GPIO_Speed_50MHz) {GPIO_InitTypeDef GPIO_InitStruct;// 使能GPIO时钟(根据你的具体端口选择)if (port == GPIOA) {RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);} else if (port == GPIOB) {RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);}// 可以根据需要添加更多端口的时钟使能// 配置GPIO引脚GPIO_InitStruct.GPIO_Pin = pin;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;  // 推挽输出模式GPIO_InitStruct.GPIO_Speed = (GPIOSpeed_TypeDef)speed;GPIO_Init(port, &GPIO_InitStruct);}// 初始化GPIO引脚(假设为输入模式)void initAsInput(GPIOMode_TypeDef mode = GPIO_Mode_IN_FLOATING) {GPIO_InitTypeDef GPIO_InitStruct;// 使能GPIO时钟(根据你的具体端口选择)if (port == GPIOA) {RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);} else if (port == GPIOB) {RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);}// 可以根据需要添加更多端口的时钟使能// 配置GPIO引脚GPIO_InitStruct.GPIO_Pin = pin;GPIO_InitStruct.GPIO_Mode = mode;  // 输入模式GPIO_Init(port, &GPIO_InitStruct);}// 设置引脚为高电平void setHigh() {GPIO_SetBits(port, pin);}// 设置引脚为低电平void setLow() {GPIO_ResetBits(port, pin);}// 读取引脚状态bool read() {return GPIO_ReadInputDataBit(port, pin);}
};
#include"GPIO_Base.h"// 示例用法
int main(void) 
{// 初始化系统时钟等(这里省略)//通常不需要手动配置,stm32f10x系列默认是在启动文件中配置好了72MHz// 创建一个GPIO_Base对象,假设使用GPIOA的Pin5GPIO_Base led(GPIOA, GPIO_Pin_5);// 初始化引脚为输出led.initAsOutput();while (1) {// 设置引脚为高电平led.setHigh();// 延时(这里省略具体的延时实现)for (volatile int i = 0; i < 500000; i++); // 简单延时// 设置引脚为低电平led.setLow();// 延时(这里省略具体的延时实现)for (volatile int i = 0; i < 500000; i++); // 简单延时}
}

相关文章:

  • 极狐GitLab 议题和史诗创建的速率限制如何设置?
  • 2025-04-18 李沐深度学习3 —— 线性代数
  • Windows软件界面分析软件-控件识别工具
  • echarts饼图中心呈现一张图片,并且能动态旋转的效果react组件
  • MATLAB 控制系统设计与仿真 - 35
  • YOLOv8 Bug 及解决方案汇总 【2024.1.24更新】【环境安装】【训练 断点续训】OMPError / KeyError
  • Linux根据 PID 进行性能分析
  • 【Spring Boot 源码学习】深入 ConfigurableEnvironment 的初始化过程
  • Android 13 关闭屏幕调节音量大小
  • Docker快速入门
  • yarn的定义
  • PyCharm Flask 使用 Tailwind CSS v3 配置
  • 软件工程中数据一致性的探讨
  • Spark两种运行模式与部署
  • 【ELF2学习板】Ne10进行FFT测试
  • 基于SpringBoot的新闻小程序开发与设计
  • 七牛使用任务工作流对音频进行转码
  • 项目管理基础---引言
  • 树莓派超全系列教程文档--(32)config.txt常用音频配置
  • 银行卡风险画像在社交行业网络安全的应用
  • 大卫·第艾维瑞谈历史学与社会理论②丨马克斯·韦伯与历史学研究
  • 上海之旅相册②俄罗斯Chaika:客居六年,致上海的情书
  • 张文宏:加强基层医疗体系建设,提升传染病早期监测和预警能力
  • 吸引更多开发者,上海智元发布行业首款具身智能一站式开发平台
  • 广西通报桂林、贵港、玉林三市应对不力:管不住山火和露天焚烧
  • 用了半年的洗衣机竟比马桶还脏,别再这样洗衣服了