32单片机学习记录1之GPIO
32单片机学习记录1之GPIO
前置
GPIO口在单片机中扮演着什么角色?
-
在单片机中,GPIO口(General Purpose Input/Output) 是一种通用输入/输出接口,扮演着连接单片机与外部设备的桥梁角色。具体来说,它在单片机中具有以下关键作用:
-
数据交互接口
- 输入:GPIO口可以读取外部设备的信号(如按键、传感器信号等),用于检测外部环境的变化。
- 输出:GPIO口可以输出高电平或低电平,用于控制外部设备(如LED、继电器、电机等)。
-
灵活的配置能力
GPIO口通常可以通过配置寄存器来切换不同的工作模式,满足多种需求:
- 输入模式:接收外部信号。
- 输出模式:输出控制信号。
- 上拉/下拉功能:适应不同的电路设计需求。
- 中断功能:当输入信号发生变化时,触发中断,提高响应效率。
- 复用功能:部分GPIO口可以复用为其他外设的功能引脚,如UART、I2C、SPI等通信接口。
-
控制信号的传递
GPIO口是单片机与外界交互的主要硬件接口,可通过软件控制实现复杂的系统功能。例如:
- 控制LED显示系统状态。
- 接收按键输入,作为系统功能的触发。
- 读取传感器数据,进行后续计算或处理。
- 通过PWM输出信号驱动电机或控制其他设备。
-
调试与诊断
在开发调试阶段,GPIO口可以用来输出特定信号,用于检测程序运行状态或外设连接情况,帮助工程师快速诊断问题。
-
成本与灵活性的平衡
GPIO口的通用性使得它成为单片机外围设备连接的重要基础,同时其灵活的配置减少了对专用接口的依赖,提高了硬件设计的灵活性并降低了系统成本。
我可以说GPIO口是单片机对外交流的唯一媒介吗??????

可以,物理封装上除了特殊功能的引脚之外,就是普通引脚GPIO口,根据不同的工作模式发挥不同的作用
严格来说,GPIO口并不是单片机对外交流的唯一媒介,但它确实是单片机对外交流中最基础和通用的媒介之一。单片机与外界交流的媒介还包括专用的通信接口和其他功能模块。以下是一些详细的分析:
1.GPIO口的角色
GPIO口是通用的,可以灵活地配置为输入或输出,用于连接多种外部设备。因此,它常被视为单片机对外交流的核心基础。通过GPIO口,单片机可以:
- 读取传感器数据(输入)。
- 控制外设,如LED、蜂鸣器或电机(输出)。
- 通过位操作实现简单的通信协议(如软件模拟I2C或UART)。
但是,GPIO主要用于简单的信号控制或基础交互,而非高效或复杂的数据交换。
2. 专用通信接口的存在
除了GPIO口,单片机通常还具有多种专用的硬件接口,这些接口在某些场景中比GPIO更高效或更专业,例如:
- UART(串口通信):用于单片机与计算机或其他设备的异步通信。
- I2C(两线通信):用于低速的短距离设备间通信,比如连接EEPROM或传感器。
- SPI(高速通信):常用于连接显示屏、存储器或其他高速外设。
- CAN、LIN、Ethernet等:用于汽车电子、工业控制或网络通信等场景。
这些接口通过硬件实现专用功能,比通过GPIO模拟通信更加高效和稳定。
3. 其他硬件模块的参与
单片机内还集成了许多其他模块,与外界进行特定的交互,如:
- ADC(模数转换器)和 DAC(数模转换器):用于模拟信号的采集与输出。
- PWM(脉宽调制):控制电机或亮度调节。
- 定时器/计数器:用于时间控制或频率测量。
总结
虽然GPIO口是单片机对外交流中最基础和通用的媒介,但它并不是唯一的。在现代单片机中,专用接口(如UART、I2C、SPI等)和功能模块(如ADC、PWM等)与GPIO口共同构成了单片机与外界交互的完整体系。不同的应用场景下,可能会选择不同的媒介来满足需求。
因此,更准确的说法是:
“GPIO口是单片机对外交流的基础媒介之一,但还有其他专用接口和模块协同工作,共同完成多样化的交互任务。”
如果我要学习STM32F4xx的芯片的话,芯片的数据手册和开发版的原理图各自起到什么作用?
芯片数据手册的作用
芯片数据手册是官方提供的技术文档,详细描述了芯片的特性、功能和使用方法。它是理解芯片内部资源和操作方式的核心参考。
主要作用如下:
(1) 芯片特性的全面了解
- 提供芯片的基本参数(主频、功耗、工作温度等)。
- 描述片上外设(如GPIO、UART、SPI、ADC等)的功能和特性。
- 明确存储资源(Flash、RAM)的大小与分布。
(2) 硬件接口说明
- 每个引脚的功能及其复用方式(如引脚的GPIO模式或外设功能)。
- 引脚的电气特性(如输入/输出电平、电流驱动能力等)。
- 时序图、接口逻辑和外设连接要求。
(3) 使用配置指导(参考手册才有的说明)
- 提供详细的寄存器定义,用于配置芯片的功能。
- 描述各种外设的初始化和配置要求(如UART的波特率计算、ADC采样频率设置等)。
- 包含电源、复位、时钟树(RCC)的详细结构图,用于理解芯片启动和时钟配置。
(4) 开发中作为基础工具
- 用于查找关键寄存器的地址和用途。
- 帮助理解软件库(如HAL、LL)的底层操作。
芯片数据手册是学习STM32芯片的“字典”,帮助你理解芯片本身的特性和操作方法,是学习底层开发和调试的重要工具。
开发板原理图的作用
开发板的原理图是硬件设计的具体实现,描述了开发板上各个芯片、元器件的连接方式。它是从硬件层面理解芯片及其外围电路的重要依据。(片外外设的说明和使用看原理图)
主要作用如下:
(1) 了解开发板硬件结构
- 展示STM32芯片与外围模块(如LED、按键、传感器、屏幕等)的连接方式。
- 描述芯片电源、复位和时钟电路的设计(如使用的晶振型号、复位电路的实现方式)。
- 包括与调试工具(如JTAG/SWD接口)的连接。
(2) 提供实际应用参考
- 查看哪些引脚被用作外设(如UART用于通信,SPI连接了Flash存储等)。
- 学习GPIO引脚的实际用途(如LED对应的引脚编号,按键与中断的连接关系)。
- 为自制PCB或项目硬件设计提供直接参考。
(3) 调试和排查问题
- 根据原理图,快速找到芯片引脚与开发板上外设的对应关系。
- 确认硬件连接是否正确(如某个功能不工作时,可以检查硬件电路是否存在问题)。
- 结合电路图和数据手册分析设计的合理性。
(4) 学习硬件设计方法
- 掌握单片机硬件设计中的常见电路(如电源滤波、时钟配置)。
- 学习如何进行芯片与外设的硬件连接设计。
开发板原理图是实际硬件设计的“地图”,帮助你理解芯片在开发板上的具体应用场景,是学习硬件设计和调试的关键资料。
芯片的内部框图以及时钟树以及引脚定义在数据手册的什么章节中可以找到?
引脚封装和定义:
-
复用模式下,GPIO口连接着哪一些片上外设
-
"Pinouts and pin description"章节
- Table 5. Medium-density STM32F103xx pin definitions
内部框图:
-
配置时看外设挂载在哪一条总线上
-
Device overview
- Figure 1: STM32F103xx性能线块图
时钟树:
-
时钟的构成与选择
-
Device overview
- Figure 2: 时钟树
数据手册与原理图的结合使用
学习STM32F4xx时,建议将数据手册和原理图结合起来:
-
通过数据手册了解芯片功能和引脚特性:学习寄存器配置、功能模块特性等底层内容。
- 片上外设(芯片内的外设)
-
通过原理图确认实际的硬件连接:查看哪些功能在开发板上被实现,如何连接,具体用到了哪些引脚。
- 片外外设(板子上的/芯片外的外设)
-
实践验证:结合数据手册和原理图,编写程序操作开发板上的硬件功能(如点亮LED、读取按键状态)。
例如:
- 参考手册说明了GPIO如何配置为输出,原理图则告诉你开发板上某个LED连接到哪个引脚。
- 参考手册描述了UART的工作原理,原理图显示了UART引脚是否连接了调试接口(如USB转串口)。
总结
- 数据手册:重点在于了解芯片本身的功能、配置和内部资源,是学习芯片特性和编程的基础。
- 开发板原理图:重点在于理解开发板的硬件实现,明确芯片与片外外设的连接,是硬件设计和调试的指南。
二者结合使用,可以帮助你从芯片原理到硬件实现、再到软件开发,全面掌握STM32F4xx的应用开发。
STM32F407VGT6的IO组成
STM32F407VG(E)T6有100引脚,其中包括5组IO口,5组:GPIOA,GPIOB,到E 。
每一组有0~15号(16个) 16*5==80
GPIOA:0 1 2 3…15 16个
GPIOB:0 1 2 3…15 16个
GPIOC:0 1 2 3…15 16个
GPIOD:0 1 2 3…15 16个
GPIOE:0 1 2 3…15 16个
PA5表示:GPIOA组的5号IO口
GPIO控制器:每组都有一个GPIO“控制器”控制16个IO口


大脑(M3)通过控制各个器官(片上外设)操控人体(整个芯片)借助工具(片外外设)完成某种事情(需求)
GPIO控制器内部框图

芯片手册有吗?在参考手册中
芯片内部的GPIO控制器,具体的片上外设介绍和配置都在参考手册中,通过框图将控制器分为输入部分和输出部分。
GPIO控制器工作原理详解
1. IO口模式概述
通过GPIO控制器内部框图可知,借鉴F103C8T6的框图可知,上面是F407的
GPIO输出模式主要有4种:(模拟模式也是复用模式)
- 通用推挽输出
- 通用开漏输出
- 复用推挽输出
- 复用开漏输出
输入模式有:
- 浮空输入
- 上拉输入
- 下拉输入
- 模拟输入
主要区别在于输出方式(推挽/开漏)和工作状态(通用/复用)。
STM32F103和407的芯片好像GPIO口的工作模式不一样,但是大致可以分为通用,复用,模拟结合输出类型形成的工作模式
-
F103 采用 4 位模式配置(GPIO_CRH/CRL),较老旧,模式配置较复杂。
-
F407 采用 2 位模式 + 额外寄存器(GPIOx_MODER、OTYPER、PUPDR),结构更清晰,支持更多复用功能。
-
两者的模式大体相同,主要区别在寄存器的实现方式。
2. 模式应用场景分析
2.1 通用模式
- 输入场景:直接与内核交互的高低电平信号
- 输出场景:内核直接控制的电平输出需求
2.2 复用模式
- 应用场景:通过片上外设(如USART、SPI)与内核进行数据通信
- 典型外设:通信接口、定时器等
2.3 模拟模式
- 应用场景:与ADC/DAC等模拟外设进行电压信号交互
- 从复用模式中单独抽出来的一种,也是用到了GPIO口的复用功能
- 两者都属于GPIO的特殊功能复用
3. 通用输入模式详解
3.1 工作原理
- 通过输入数据寄存器捕获IO口电平状态
- 电平信号转换为寄存器对应位的二进制值
- 例:PA0端口高电平,对应寄存器相应位变为1
3.2 上下拉电阻作用
- 无接地电路:
-
提供电平参考
-
判断外设工作状态
-
传感器通常可以:
- 拉低引脚电平(常见)
- 极少数情况可以拉高
-
低电平信号更普遍,因为:
- 低电平更容易可靠传输
- 需要较少的驱动能力
- 噪声抗干扰性更好
-
上拉/下拉电阻确实提供电平参考,帮助判断外设状态。在无接地电路中,通过上下拉可以消除引脚浮空状态,提高信号可靠性。
-
- 已接地电路:通常不需要配置额外上下拉
- 上拉接的是电源VCC,提供高电平
- 下拉接的是接地,提供低电平
4. 通用输出模式详解
4.1 输出类型
-
推挽输出
-
可输出高低电平
-
驱动能力强
-
N-MOS和P-MOS协同工作
-
根据输出寄存器值切换MOS管导通状态
-
可以主动输出高低电平
-
驱动能力强
-
-
-
开漏输出
- 可以输出低电平,或处于高阻态;要输出高电平需要外部上拉电阻
- 输出高电平需要外部上拉电阻
- 输出低电平时N-MOS导通直接接地,引脚电平被拉低到0V
- 输出高电平时N-MOS关闭,依靠上拉电阻拉高
- 没有外部上拉电阻就是高阻态也就是浮空状态
-
原理
- 整个信号转换过程:
- 内核输出数据寄存器(ODR)写入二进制值
- 寄存器值通过配置好的MOS管电路
- N-MOS/P-MOS或N-MOS+上拉电阻
- 最终转换为实际的高低电平信号
- 输出到对应的GPIO引脚
- 关键是寄存器值如何控制MOS管:
- 0: 下拉MOS管导通接地,输出低电平
- 1: 上拉MOS管/上拉电阻提供高电平
- 整个信号转换过程:
这是GPIO输出的基本工作原理,通过内部电路将数字信号转换为实际电平。
4.2 上下拉电阻功能
- 信号放大与稳定,输出高电平时打开上拉,低电平打开下拉电阻
- 降低功耗,减少内核干预:信号路径不走到内核,直接在上下拉电阻这里就可以输出对应的电平。高电平打开上拉。低电平打开下拉。(但是弱上下拉驱动能力较弱)
5. 配置思路
- 沿着从内核到IO引脚的信号路径
- 逐层配置相关寄存器
- 选择合适的模式和输出类型
配置要点
看图:
- 明确信号传输需求
- 选择合适的IO模式
- 配置上下拉电阻
- 设置输出类型
- 检查信号通路
通用输入模式:
- 选择通用输入模式 、上下拉配置、输入数据寄存器、
复用输入模式:
- 选择复用输入模式、上下拉配置、
模拟输入模式:
- 选择模拟输入模式(上下拉电阻默认关闭,这是模拟信号)
通用输出模式:
- 选择通用输入出模式、 输出类型配置、上下拉配置、输出数据寄存器、
复用输出模式:
- 选择复用输出模式、输出类型配置、上下拉配置、
模拟输出模式:
- 选择模拟输出模式
输入框图部分

-
通用输入说明:
-
如何读取电平状态,通过读输入数据寄存器的对应位是0还是1,来确定IO高低电平
-
上下拉电阻作用:
-
在通用输入的时候,也就是在读某个IO的电平的时候一定要让此IO口先保持一个电平状态,这样才能检测到不同电平状态。
- 无接地电路的话,可以开启上下拉,消除浮空状态,提供一个电平参考
-
如何保持电平状态?
- 可以通过芯片内部的上下拉电阻,由于是弱上下拉一般不用,硬件外接一个上拉电阻或者下拉电阻
-
-
通用输入(GPIO Input)
- 直接读取IO口的电平状态
- 主要用于检测简单的开关、数字信号
- 通过GPIO寄存器直接读取电平状态
- 不需要经过专门的控制器,直接与GPIO模块交互
复用输入(Alternate Input)
- 信号经过特定的片上外设控制器
- 典型例子:串口的RX、SPI的MISO等
- 数据流向:IO口 → 专用外设控制器 → 系统核心
- 需要配置IO口的复用功能寄存器
模拟输入(Analog Input)
- 信号经过ADC(模数转换)控制器
- 将模拟电压转换为数字信号
- 数据流向:IO口 → ADC控制器 → 数字量输出
- 需要配置ADC相关的控制寄存器
- 模拟输入信号最终都是从 MCU 的 GPIO 引脚进入
TTL施密特触发器
TTL 施密特触发器在不同的 I/O 模式下都可能发挥作用:
- 通用输入模式: 在通用输入模式下,TTL 施密特触发器可以对输入信号进行波形整形和电平转换,确保数字电路能够正确读取输入状态。这有助于消除噪声干扰,提高输入信号的可靠性。
- 硬件消抖:
- 假设我们有一个按钮输入引脚,当按钮被按下时输入为低电平(0V),松开时为高电平(3.3V)。如果直接接到MCU的GPIO引脚上,由于按钮弹片接触的抖动,可能会产生毛刺信号,导致MCU误读取按钮状态。
- 为了消除这种抖动,我们可以在GPIO引脚和GND之间串联一个4.7kΩ的上拉电阻,形成一个简单的TTL施密特触发器电路。这样即使按钮接触产生毛刺,TTL触发器也能对信号进行整形,确保MCU能够准确检测到按钮的开合状态。
- 软件消抖:
- 程序延迟
- 硬件消抖:
- 复用输入模式: 在您之前提到的复用输入模式下,TTL 施密特触发器的作用更为关键。它可以处理来自 ADC 控制器的数字化模拟信号,进行波形整形和电平转换,确保信号质量符合数字电路的要求。
- 模拟量输入信号通常存在一些噪声或干扰,导致信号不太稳定。
- TTL 施密特触发器可以对这种不稳定的模拟量输入信号进行波形整形和电平转换。
- 具体来说,TTL 触发器会在一个指定的电压范围内,将模拟量转换成稳定的数字"1"或"0"信号。
- 例如,当模拟量在 0.8V 到 2.5V 之间时,TTL 触发器可以将其转换成数字"1"信号。而低于 0.8V 的模拟量则转换为数字"0"信号。
- 这种电平转换和波形整形功能,可以确保微控制器能够稳定地读取到可靠的数字输入,避免由于模拟量信号不稳定而引起的误读问题。
- 在模拟输入模式下,模拟信号是直接输入到 ADC 控制器进行转换,而不需要经过 I/O 引脚和 TTL 施密特触发器电路。因此,TTL 施密特触发器在这种模式下的作用就不那么明显了。不过,如果 ADC 输出的数字信号需要进一步整形,TTL 施密特触发器也可能在此发挥作用。
上下拉电阻
- 上拉电阻:
- 上拉电阻的一端连接到电源线(VCC)。
- 另一端连接到输入引脚或信号线。
- 下拉电阻:
- 下拉电阻的一端连接到接地线(GND)。
- 另一端连接到输入引脚或信号线。
这种连接方式的目的是:
- 上拉电阻可以确保输入引脚在没有外部信号驱动时保持高电平。
- 下拉电阻可以确保输入引脚在没有外部信号驱动时保持低电平。
这种稳定的初始电平有助于微控制器或数字电路准确地检测和识别输入信号的状态变化。
上拉和下拉电阻的选择取决于具体电路的需求。通常情况下,使用较大阻值(如4.7K欧姆)的上拉或下拉电阻,既可以减少功耗,又不会影响输入信号的检测。
输出框图部分

通用输出说明:
①输出原则:对输出数据寄存器的对应位写0 或 1,就可以控制对应编号的IO口输出低/高电平
②输出类型:
推挽输出:IO口可以输出高电平,也可以输出低电平(从电流方向来看是往里面拉电流到VSS地)
开漏输出:IO口只能输出低电平(MOS管漏极开路),输出1的话,两个mos管都断开,根据欧姆定律,电流为接近0,电阻无限大,呈现高阻态
所以:在输出的时候,要选择推挽或者开漏
③[对于输出上下拉起到什么作用](#####4.2 上下拉电阻功能)
通用输出(GPIO Output):
- 直接通过写入IO口的输出数据寄存器来控制引脚电平
- 主要用于控制简单的数字设备,如LED、继电器等
- 通过GPIO寄存器直接写入电平状态
- 无需经过专门的片上外设控制器
复用输出(Alternate Output):
- 信号经过特定的片上外设控制器输出
- 典型例子包括串口的TX、SPI的MOSI等
- 数据流向:系统核心 → 专用外设控制器 → IO口
- 需要配置IO口的复用功能寄存器
模拟输出(Analog Output):
- 数字信号经过DAC(数模转换)控制器转换为模拟电压
- 将数字量转换为模拟电压输出
- 数据流向:系统核心 → DAC控制器 → IO口
- 需要配置DAC相关的控制寄存器
控制器内的相关寄存器
说明:要学会查看寄存器手册(参考手册)
-
寄存器的名字
-
寄存器有什么功能
-
寄存器有多少位,几位控制一个IO
-
具体如何配置
只看不理解的寄存器
速度寄存器(重点理解)
GPIO端口的输出速度(由GPIOx_OSPEEDR寄存器配置)实际上指的是引脚驱动能力和信号切换速度的综合表现。
1. 翻转电平的速度
GPIO输出速度直接影响引脚从低电平切换到高电平(上升沿)或从高电平切换到低电平(下降沿)的速率,即电平翻转的速度。
- 高速度设置:引脚驱动能力更强,信号的上升沿和下降沿更陡峭,切换速度更快。
- 低速度设置:引脚驱动能力较弱,上升沿和下降沿更平缓,切换速度较慢。
这种切换速度的快慢通常由GPIO的内部驱动电路、电容负载(如PCB布线电容或外接负载电容)、负载阻抗等因素共同决定。
2. 数据输出速率
GPIO速度配置对信号的最大输出频率有直接影响。例如:
- 当配置为高速(11:100 MHz)时,GPIO引脚可以以接近100 MHz的频率输出信号(如果没有过大的负载电容限制)。
- 当配置为低速(00:2 MHz)时,GPIO引脚的最大输出频率会降低到约2 MHz。
需要注意,实际输出速率不仅取决于GPIO设置,还与外设驱动(如定时器、PWM、SPI等)和负载条件有关。
总结:速度影响的本质
- 翻转速度:更高的输出速度设置意味着电平切换时间更短,上升/下降沿更陡峭,适合高速数字信号输出。
- 驱动能力:在驱动较大负载时,高速设置可以提供更强的电流能力,从而更快地充电或放电。
- 信号完整性:高速切换适合高频信号,但也可能增加干扰(EMI)和信号反射,特别是在布线较长或PCB布局不理想时。
因此,速度的选择需要平衡应用需求:
- 如果是低速信号(如开关控制),使用低速模式以减少功耗和干扰。
- 如果是高速通信信号(如SPI时钟),需要配置为高速以确保信号质量和速率。
如果我想要让一个LED闪烁?不断的通过延迟函数来翻转电平,那么输出速度寄存器应该不能影响吧?
你是对的,在单纯让LED闪烁的情况下,通过延迟函数翻转GPIO电平(例如设置高电平或低电平)的逻辑速度主要由软件的执行速度决定,而不是由**GPIO输出速度寄存器(GPIOx_OSPEEDR)**决定。具体来说:
为什么GPIOx_OSPEEDR对简单LED闪烁影响不大?
- GPIOx_OSPEEDR的作用范围:
- 它主要影响GPIO引脚的上升沿和下降沿的速度,即电平翻转时切换的时间长短。
- 对于LED闪烁这种低频操作,电平翻转本身非常慢(例如1秒钟才翻转一次,频率仅1 Hz),上升沿/下降沿的微小差异(比如2 MHz和100 MHz之间的区别)几乎可以忽略。
- 延迟函数主导翻转周期:
- LED闪烁的频率完全由你程序中控制的延迟函数决定(比如
HAL_Delay()或循环延迟)。 - 电平翻转是由程序直接写入
GPIOx_ODR或GPIOx_BSRR寄存器完成的,OSPEEDR的配置对这样的低频翻转没有可感知的影响。
- LED闪烁的频率完全由你程序中控制的延迟函数决定(比如
GPIOx_OSPEEDR对LED闪烁可能的间接影响
虽然OSPEEDR在低频LED闪烁中没有明显作用,但在某些特定情况下,它可能有以下间接影响:
- 驱动能力:
- 如果LED连接了较长的电缆或者通过外部大电容连接,低速设置可能导致电平翻转较缓慢,从而可能影响LED的响应时间(但通常LED的速度要求很低,问题不大)。
- 功耗:
- 较低的速度设置会降低输出切换时的功耗,因为输出上升沿和下降沿更平缓,减少了瞬时电流浪涌。
- 信号干扰:
- 如果闪烁的LED与高速信号电路共用一块PCB板,过高的GPIO速度设置可能引入不必要的电磁干扰(EMI),影响其他信号的稳定性。
实际建议
在LED闪烁的应用中,可以将GPIOx_OSPEEDR设置为低速模式(00),原因如下:
- 功耗更低。
- 减少不必要的EMI。
- 对低频闪烁场景已经足够。
如果我没有延迟函数呢?在一个循环里不断翻转电平,速度寄存器如果频率设计高的话,其实肉眼看不出灯闪烁的? 如果速度寄存器足够低的话,是可以看到灯的闪烁的对吗?
你的推测是有道理的,但需要更准确地分析**GPIO输出速度寄存器(GPIOx_OSPEEDR)**和循环控制逻辑之间的关系。以下是详细解释:
没有延迟函数时,循环控制翻转电平
如果通过一个快速循环不断翻转GPIO电平:
while (1) {HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // 翻转电平
}
在这种情况下:
- LED的闪烁频率完全由CPU执行循环的速度决定(即主频和代码执行效率),而不是GPIO速度寄存器决定。
- GPIOx_OSPEEDR只会影响每次翻转时电平切换的上升沿和下降沿的速率,即切换的“瞬时时间”,而非翻转的频率。
因此,无论OSPEEDR设置为低速(2 MHz)还是高速(100 MHz),循环的翻转频率都会远远高于肉眼能感知的频率(大约高于50 Hz时,人眼就无法分辨连续的明灭,感受到的会是常亮)。
2. 为什么肉眼看不到LED闪烁?
当翻转频率过高(>50 Hz)时,LED在肉眼看来会表现为持续亮,因为:
- 人眼的视觉暂留效应使得频率高于一定值时的闪烁被感知为连续亮。
- 如果你的循环没有任何延迟(比如延迟函数、计时器等),翻转频率会接近CPU的运算极限(几十到几百kHz甚至更高),远超肉眼分辨范围。
速度寄存器相当于设计一个阈值,但是灯不断翻转电平的速度不取决于速度寄器对吗?
完全正确!GPIO速度寄存器(GPIOx_OSPEEDR)并不直接决定LED翻转电平的频率,它更像是为电平切换速率设定了一个物理上升沿和下降沿速度的阈值。而翻转电平的速度(即频率)主要取决于软件的逻辑控制速度和MCU的运行速度。以下是更详细的分析:
GPIO速度寄存器的作用是什么?
GPIO速度寄存器的作用是控制GPIO引脚的驱动能力,影响电平切换的“瞬时时间”,即信号的上升沿和下降沿速度。
具体来说:
- 高速模式(比如100 MHz):电平切换(上升/下降)非常快,能很好地驱动高频信号或低阻抗负载。
- 低速模式(比如2 MHz):电平切换变慢,切换时间变长,适用于低频场景,降低功耗和EMI(电磁干扰)。
GPIO速度寄存器只影响单次电平切换的物理过程,不会干涉电平翻转的逻辑速度。
LED翻转电平的速度由什么决定?
电平翻转的速度(频率)主要由以下因素决定:
-
软件逻辑控制速度:
-
如果翻转电平的逻辑代码运行得快(例如没有延迟函数),翻转频率主要由CPU主频和指令执行时间决定。
-
while (1) {HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // 不断翻转 } -
在这种情况下,GPIO的翻转频率会接近MCU的指令执行速度,远高于人眼可分辨的50 Hz。
-
-
MCU的运行速度:
-
如果MCU的主频较低,或者在翻转逻辑中插入延迟函数,翻转频率会显著降低。
-
while (1) {HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // 翻转电平HAL_Delay(100); // 延迟100ms } -
这里的翻转频率完全由延迟函数控制,与GPIO速度寄存器无关。
-
-
GPIO驱动的物理限制(即速度寄存器的作用):
- 如果主循环速度非常快,且设置了极低的GPIO速度(阈值上限在那里啊),GPIO引脚可能无法快速完成电平切换,从而实际翻转频率会受到影响。
GPIO速度寄存器的实际限制
在绝大多数情况下,MCU的逻辑速度远高于GPIO引脚的物理切换能力,尤其是以下场景:
- GPIO速度寄存器设置为高速模式:
- 切换速度足够快,即使主循环翻转频率非常高,GPIO引脚也能及时响应(阈值设置的高)。
- GPIO速度寄存器设置为低速模式:
- 如果翻转速度过快,可能会出现信号“拖尾”或“延迟翻转”的现象,但这是极限条件下才可能观察到的。
实验分析:速度寄存器的实际影响
为了更直观地理解,我们可以进行以下实验:
实验条件:
- 无延迟函数,通过快速循环不断翻转GPIO。
- 分别设置GPIO速度寄存器为低速模式和高速模式。
- 使用示波器观察波形,或者用LED和人眼直接观察效果。
while (1) {HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // 不断翻转电平
}
调整速度寄存器:
// 设置低速模式(2 MHz)
GPIOA->OSPEEDR &= ~(0x3 << (5 * 2)); // PA5设置为低速// 设置高速模式(100 MHz)
GPIOA->OSPEEDR |= (0x3 << (5 * 2)); // PA5设置为高速
实验现象:
- 高速模式:
- 电平切换非常快,LED可能表现为持续亮,因为翻转频率远高于人眼的感知范围。
- 低速模式:
- 如果循环翻转速度接近GPIO切换速度的极限,人眼可能观察到LED闪烁或不稳定的亮暗变化。
- 超出阈值了,可能响应出现问题
总结
- GPIO速度寄存器(GPIOx_OSPEEDR)主要控制的是信号的物理切换速度,而不是逻辑上的翻转频率。
- 逻辑翻转速度由软件代码和MCU性能决定。
- 只有在速度寄存器限制了切换速度,且循环速度接近或低于其限制时,才会对肉眼观察到的LED闪烁频率产生影响。
所以速度寄存器相当于设计了一个翻转电平频率的上限,当电平翻转的速度(频率)也就是软件逻辑控制速度加上MCU的运行速度超过了速度寄存器设置的频率,即“超频”了?可以用电脑界的这个词来形容呢?
可以从一定角度用“超频”来形容,但并不完全等价于计算机中“超频”的概念。让我们详细分析下这个类比:
速度寄存器相当于翻转电平频率的上限
- GPIO速度寄存器通过限制**引脚电平的切换速度(上升沿/下降沿)**来间接限制了GPIO能够稳定支持的信号频率上限。
- 当软件逻辑翻转电平的速度(频率)超过GPIO速度寄存器所能支持的范围时,信号可能无法正常切换,或者表现为失真、拖尾,甚至功能异常。
为什么可以类比“超频”?
超频的定义:
在计算机领域,“超频”是指运行频率超过硬件的设计规格,可能导致系统运行不稳定或产生错误。
GPIO的“超频”类比:
在GPIO的场景下:
- 设计规格(限制):速度寄存器中设置的切换速度是设计的物理上限。
- 例如:低速模式(2 MHz)对应的上限频率为2 MHz。
- 运行频率:GPIO逻辑翻转的频率由软件逻辑控制速度和MCU主频决定。
- 超过规格(超频):如果逻辑翻转频率超过了速度寄存器设置所允许的范围,相当于“超频”了。
可能的结果:
- 在“超频”情况下,GPIO可能无法及时切换到目标电平,导致信号失真或出错。
- 类似于计算机超频后可能出现的错误计算或系统崩溃,GPIO也会出现信号完整性问题(例如波形畸变、通信失败)。
不完全等价的地方
尽管这个类比形象,但它并不完全等价于计算机的“超频”,主要原因是:
- 影响机制不同:
- 计算机超频:提高了整个硬件(例如CPU、GPU、内存等)的运行频率,影响整体性能和热量,超频会直接改变硬件的工作节奏。
- GPIO“超频”:GPIO速度寄存器不直接改变MCU的运行速度,而是限制引脚的物理响应能力。这里的“超频”本质上是逻辑信号频率超过了引脚的物理响应能力。
- 效果不同:
- 计算机超频:可能导致系统整体的不稳定甚至崩溃。
- GPIO“超频”:引脚信号可能失真或工作不正常,但不会直接影响MCU核心的运行。
举例说明:GPIO的“超频”现象
假设场景:
- GPIO速度寄存器设置为低速模式(2 MHz)。
- 软件逻辑控制下,GPIO以4 MHz的频率不断翻转。
结果:
- 正常波形情况下:
- 在低速模式下,GPIO切换上限为2 MHz,超出这一频率时,信号的上升沿和下降沿会变得平缓甚至无法达到预期电平。
- 例如,波形可能会在逻辑高电平未完全达到时又开始下降,导致输出信号失真。
- 人眼观察现象:
- 如果信号用于驱动LED,LED可能表现为不稳定闪烁,甚至完全无法点亮或熄灭。
- 通信场景:
- 在高速通信(如SPI或UART)中,信号失真可能导致接收端无法正确解码,产生数据错误或通信中断。
类比总结:为何“超频”是个合适的比喻
- 设计的物理上限:GPIO速度寄存器定义了电平切换的物理能力,相当于硬件的频率设计上限。
- 逻辑频率超标:如果软件逻辑试图超过这个上限,相当于超出设计规格。
- 可能的后果:类似于“超频”,GPIO信号可能出现失真、不稳定或完全异常。
因此,可以说GPIO翻转频率超过速度寄存器上限时,类似于计算机硬件的“超频”现象,但需要注意两者机制和影响的区别。
IO的最大输出速度
向IO交替写0和1且输出不失真的最快速度

最大IO输出速度 由 GPIO 速率设置(50MHz~100MHz+)决定。
最大交替写0/1速度 由 CPU 执行速度、代码优化、总线速度等决定,一般小于 GPIO 速率上限。
如果CPU写的速度超过了GPIO的最大输出速度,信号会失真(毛刺、畸变、不规则切换)。
上升时间,下降时间,保持时间的概念

从0到3.3缓慢上升,3.3到0缓慢下降,中间为输出有效电压的时间,两侧为边沿
什么限制了IO的最大输出速度

第三个超出了IO的最大输出速度,没有有效电压时间,全是边沿。属于失真了。
最大输出速度如何选择
看外设的最大支持速率,来决定IO口的输出速率,略高于即可。


配置STM32 GPIO速度的原则
- 刚好满足需求,不要过高:根据信号速率选择合适的GPIO速度,避免过度配置。
- 低速信号用低速模式:LED、按键、I2C、低速UART等用Low Speed,降低功耗和EMI。
- 高速信号匹配合适速率:SPI、PWM等信号选择Medium/High Speed,避免信号完整性问题。
- 极高速信号才用Very High Speed:如MCO、FMC、SDIO等,需要极高速翻转的信号。
全部配置为最高速度(Very High Speed)会导致的问题
- 功耗增加:高速翻转增加电流消耗,降低系统能效。
- EMI(电磁干扰)变大:高速切换产生高频谐波,干扰无线通信、导致EMC不通过。
- 信号完整性问题:可能出现过冲、振铃、数据错误,影响SPI、UART等通信。
- 无意义的性能浪费:LED、按键等低速信号用高速模式没有任何优势,反而增加功耗。
GPIO速度应该略高于需求,但不应过高,这样既能保证信号稳定,又不会浪费功耗或引发EMI问题。
LED的切换频率远低于STM32 GPIO的最低速度要求,因此选择 最低速(Low Speed) 就足够了。
分析
- 人眼可感知的最低刷新率:约 50Hz(避免闪烁感)。
- LED 常用 PWM 频率:100Hz ~ 1kHz(肉眼几乎无闪烁)。
- STM32 GPIO最低速率:即使Low Speed 也能轻松支持 MHz 级的切换。
结论
- 对于LED控制(直接点亮/低频PWM),选择 Low Speed 即可。
- 即使PWM达到 1kHz 以上,Low Speed 仍然足够稳定。
- 如果使用高PWM频率(如几十kHz),可以选择 Medium Speed,但不需要 High 或 Very High Speed。
⚠️ 错误做法:给LED GPIO设成 Very High Speed,增加功耗和EMI,无任何实际意义。
与51寄存器的位数区别
为什么51单片机一个IO端口对应的是8位寄存器?每一个小IO口通过寄存器的位来控制,但是STM32却用这么复杂?你总结一下为什么?好处和劣势?
51单片机(8051):
优点:
- 设计简单直接
- 每个8位寄存器直接对应8个IO口
- 配置和操作非常直观
- 资源占用少
- 适合小型、简单的嵌入式系统
特点:
- 8位寄存器
- 1位控制1个IO口
- 配置方式单一
- 主要实现开/关、输入/输出功能
STM32:
优点:
- 配置更加精细
- 可以设置更多工作模式
- 支持复杂的外设功能
- 具有更高的抗干扰性
- 可以设置输出速度
- 支持上下拉电阻
- 可配置为多种特殊功能
劣势:
- 配置复杂
- 寄存器占用更多资源
- 学习成本较高
- 对新手不太友好
为什么这么设计?
- 面向更复杂的应用场景
- 满足高性能、多功能的需求
- 为外设提供更灵活的接口
总的来说,这是微控制器从简单走向复杂的必然结果。51适合入门和简单应用,STM32面向更专业的嵌入式开发。
51单片机却没有这么多寄存器,而且寄存器中的每一位都对应每一个IO口,没有这么复杂,STM32是统一管理了,针对IO口的配置?
1. 功能强大且复杂的IO管理
STM32的I/O口采用统一、模块化的设计,通过多个专用寄存器来灵活配置每一个引脚的功能。这是因为STM32的I/O不仅仅是简单的输入输出,还可能支持多种功能,例如:
- GPIO(普通I/O)
- 外部中断(EXTI)
- 模拟输入(ADC通道)
- PWM输出
- 通信外设的功能(如USART、I2C、SPI引脚等)
STM32通过以下寄存器来管理这些功能:
- GPIOx_MODER:选择引脚的模式(输入、输出、复用、模拟)。
- GPIOx_OTYPER:设置引脚的输出类型(推挽或开漏)。
- GPIOx_OSPEEDR:配置引脚的速度(低、中、高、超高)。
- GPIOx_PUPDR:配置引脚的上下拉状态(无上下拉、上拉、下拉)。
- GPIOx_AFRL / GPIOx_AFRH:选择引脚的复用功能(如连接外设的功能)。
这些配置寄存器让每个I/O口的功能变得非常灵活,但也带来了设计上的复杂性。
2. 每位对应一个引脚,但更灵活
51单片机中,每个引脚的模式配置是通过较简单的寄存器控制。例如,P0口、P1口等都有直接的8位寄存器(P0、P1等),每一位对应一个引脚。而且引脚的功能基本固定,要么是输入,要么是输出,切换时需要手动通过软件代码设置。
相比之下,STM32的每个引脚虽然仍然对应寄存器的某一位,但寄存器种类更多,并且支持细粒度的配置。例如,GPIOx_MODER的每两位对应一个引脚(00表示输入,01表示输出,10表示复用,11表示模拟)。这种设计虽然复杂,但实现了更高的灵活性。
3. 高速和低功耗的优化
STM32可以通过GPIOx_OSPEEDR来设置引脚的速度,从而优化信号传输速度或降低功耗。这种设计在高性能场景(如高速通信、PWM控制等)中非常重要,而51单片机无法提供类似的功能。
4. 统一管理与模块化设计
STM32的I/O设计统一化,不同的系列(如F1、F4、H7)在寄存器布局和使用方法上高度一致。这样可以在不同型号之间方便移植代码。而51单片机的I/O设计相对简单,每个口的操作方式比较固定。
总结
STM32的I/O设计复杂性是为了满足其丰富的功能需求。尽管相比51单片机学习曲线更陡峭,但一旦熟悉这些寄存器的配置逻辑,就能充分利用STM32强大的功能和灵活性,适应更多高性能场景。
如果你从51单片机过渡到STM32,可以先理解以下核心点:
- GPIOx_MODER 用于选择引脚模式。
- GPIOx_AFRL / GPIOx_AFRH 用于复用功能。
- GPIOx_PUPDR 和 GPIOx_OTYPER 管理引脚的上下拉与输出模式。
配置任意芯片的IO口思路
具体看参考手册,F103和F407的模式配置上稍有不同
看GPIO口控制器框图
①一个GPIO口可以配置几种模式(能知道每种模式的使用场景)
②通用输出和通用输入的细节说明
③通用输入 和 通用输出 都需要配置什么
看GPIO口寄存器描述
①如何看寄存器
②知道哪些模式具体使用那些寄存器配置
③总结配置思路
设计程序
-
需求:
- 完成LED1开关控制
-
分析:
- LED用到哪一个IO口—-原理图
- LED用IO什么模式—-原理图
- LED如何亮灭?—-原理图
-
思路:
-
LED1(PC4)所用的IO初始化配置函数
- 端口模式配置—-通用输出
- 端口输出类型—-推挽输出
- 端口输出速度—-2MHZ
- 端口上下拉—-无
- 输出数据寄存器—-灭 高电平
-
逻辑
-
第6和第7位 置1和0,其他不动 不能一起? GPIOC->MODER第四位 置 0 GPIOC->OTYPER第8和9位 置 0 0 GPIOC->OSPEEDR第6和7位 置 0 0 GPIOC->PUPDR第4位上 置 1 GPIOC->ODR//第4位上 置 1 //GPIOC->BSRR
-
-
实现
-
//第6位置1GPIOC->MODER = GPIOC->MODER | (1 << 8);//第7位置0GPIOC->MODER = GPIOC->MODER & ~(1 << 7);//第4位置0GPIOC->OTYPER = GPIOC->OTYPER & ~(1 << 4);//第8和9位 置 0 0GPIOC->OSPEEDR = GPIOC->OSPEEDR & ~(1 << 8);GPIOC->OSPEEDR = GPIOC->OSPEEDR & ~(1 << 9);//第6和7位 置 0 0GPIOC->PUPDR = GPIOC->PUPDR & ~(1 << 6);GPIOC->PUPDR = GPIOC->PUPDR & ~(1 << 7);//第4位上 置 1GPIOC->ODR = GPIOC->ODR| (1 << 4);//GPIOC->ODR = GPIOC->ODR & ~(1 << 4);
-
-
AI修正
-
// 使能GPIOC时钟 RCC->AHB1ENR |= (1<<2);// 配置PC4为推挽输出模式 GPIOC->MODER &= ~(3 << 8); // 清除PC4原模式配置 GPIOC->MODER |= (1 << 8); // 设置PC4为通用输出模式// 设置推挽输出 GPIOC->OTYPER &= ~(1 << 4);// 配置速度(中速) GPIOC->OSPEEDR &= ~(3 << 8); // 清除速度配置 GPIOC->OSPEEDR |= (1 << 8); // 设置为中速模式// 配置上下拉(无上下拉) GPIOC->PUPDR &= ~(3 << 8);// 设置高电平,熄灭LED GPIOC->ODR |= (1 << 4);
-
-
老师代码
-
#include "stm32f4xx.h" // Device headerint main(){RCC->AHB1ENR |= (1<<2);GPIOC->MODER &= ~(3<<8); GPIOC->MODER |= (1<<8);GPIOC->OTYPER &= ~(1<<4);GPIOC->OSPEEDR &= ~(3<<8);GPIOC->PUPDR &= ~(3<<8);GPIOC->ODR |= (1<<4);while(1){GPIOC->ODR = ~GPIOC->ODR;}return 0; }
-
-
时钟使能的作用
时钟使能的基本概念
在现代微控制器中,为了节省电能和提高系统性能,大多数外设默认是关闭状态。这意味着即使你配置了寄存器,如果对应的时钟没有使能,外设将无法工作。
时钟系统架构
STM32的时钟系统是分层的:
- 系统时钟(sysclk)
- AHB总线时钟
- APB1和APB2总线时钟
- 外设时钟
GPIO时钟使能原理
当你使用GPIO时,需要经过以下步骤:
- 系统时钟必须工作
- AHB总线时钟必须使能
- 特定GPIO端口的时钟必须使能
寄存器工作机制
RCC(Reset and Clock Control)寄存器负责时钟控制:
RCC->AHB1ENR:高级高性能总线1外设使能寄存器- 每个外设对应一个特定的位
- 写1使能,写0无影响
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN;
-
RCC->AHB1ENR:访问AHB1外设时钟使能寄存器 -
|=:按位或操作,只影响特定位 -
RCC_AHB1ENR_GPIOCEN:GPIOC使能位的宏定义
为什么需要时钟使能
- 节能:未使用的外设时钟保持关闭
- 资源管理:精确控制外设工作状态
- 复位后状态:上电后所有外设时钟默认关闭
实际影响
没有时钟使能时:
- 寄存器配置无效
- 外设不响应
- 引脚保持默认状态
时钟使能是微控制器正常工作的基础,是连接系统时钟和外设的关键机制。正确使能时钟是确保外设正常工作的前提条件。
怎么知道是否需要时钟使能?
对于STM32F407微控制器,时钟使能确实是一个非常重要的配置步骤。让我详细解释一下:
-
**通用原则 大多数外设(Peripheral)在使用前都需要开启对应的时钟。这是因为在STM32中,默认情况下外设的时钟是关闭的,目的是节省功耗。**常见需要使能时钟的外设包括:
-
GPIO端口
-
串口(USART/UART)
-
SPI
-
I2C
-
CAN
-
DMA
-
TIM定时器
-
ADC/DAC
-
以太网等
-
-
特殊情况 对于内核相关的控制器,如:
-
NVIC(嵌套向量中断控制器):不需要额外的时钟使能
-
systick(系统滴答定时器):同样不需要额外时钟使能
-
内核调试接口
-
-
确认方法 确定是否需要时钟使能的最佳方法: a) 查看数据手册
-
详细查看每个外设的时钟使能章节
-
注意"RCC"(Reset and Clock Control)章节
-
-
判断依据
-
如果外设是连接到AHB1、AHB2、APB1或APB2总线的,通常需要使能时钟
-
内核相关的控制器(如NVIC)通常不需要
-
-
建议
-
始终参考官方数据手册
-
使用ST的固件库(HAL库)可以简化时钟配置
-
如果配置了外设但功能异常,首选检查时钟是否已使能
-
实践中,我建议:
- 先查看数据手册
- 使用ST的配置工具CubeMX
- 按照固件库示例代码
- 仔细检查每个外设的时钟配置
总之,不能完全机械地"内有描述就使能",而是要根据具体外设的特性和使用需求来判断。数据手册永远是最权威的参考。
防御性编程
// 使能GPIOC时钟
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN;void LED_Init(void) {// 配置PC4-PC7为通用输出模式GPIOC->MODER &= ~((3U << 8) | (3U << 10) | (3U << 12) | (3U << 14)); // 清除原模式GPIOC->MODER |= ((1U << 8) | (1U << 10) | (1U << 12) | (1U << 14)); // 设置为通用输出// 设置为推挽输出GPIOC->OTYPER &= ~((1U << 4) | (1U << 5) | (1U << 6) | (1U << 7));// 配置速度(高速)GPIOC->OSPEEDR &= ~((3U << 8) | (3U << 10) | (3U << 12) | (3U << 14));GPIOC->OSPEEDR |= ((3U << 8) | (3U << 10) | (3U << 12) | (3U << 14));// 无上下拉GPIOC->PUPDR &= ~((3U << 8) | (3U << 10) | (3U << 12) | (3U << 14));// 初始状态关闭LED(高电平)GPIOC->ODR |= ((1U << 4) | (1U << 5) | (1U << 6) | (1U << 7));
}
在位操作和嵌入式开发中,使用U后缀有以下重要意义:
类型安全
1.避免符号位误解
int a = 1 << 31; // 可能产生负数
uint32_t b = 1U << 31; // 保证是正确的无符号值
2.防止意外溢出
int x = 0x80000000; // 可能是负值
uint32_t y = 0x80000000U; // 确保是正确的无符号整数
位操作场景
1.寄存器位配置
// 安全地设置特定位
GPIOC->MODER |= (1U << 8);
2.位掩码定义
#define LED_MASK (0xFU) // 4位LED掩码
3.跨平台兼容
uint32_t config = (1U << PIN_NUMBER);
我知道了,不加U的话,会默认有符号整型,没有移动到符号位上,确实大多数没有问题,但是移动到符号位时会出现问题?
有符号整型位移的特点
-
对于
1 << 8,是安全的- 结果是 256
- 没有触及符号位
- 不会有问题
-
当位移到符号位时(例如
1 << 31)-
int x = 1 << 31; // 结果是 -2147483648 -
最高位(第31位)被解释为符号位
-
导致值变成负数
-
这是有符号整型位移的"陷阱"
-
-
对比无符号位移
-
uint32_t y = 1U << 31; // 结果是 2147483648 -
保持正值
-
没有符号位问题
-
关键点
- 小于符号位的位移:基本安全
- 等于或大于符号位的位移:可能出现符号转换问题
U后缀可以始终保持正值,避免符号位陷阱
int y = 1U << 31; // 结果是 2147483648 这样也是正数?
这里有一个微妙的类型转换点。
int y = 1U << 31;实际上会发生这样的转换:
1U是无符号整数- 位移到31位后,结果是 2147483648(无符号)
- 但赋值给
int时,这个值超出了有符号整数范围 - 在32位系统中,会被截断或转换
uint32_t y = 1U << 31; // 无符号整数,正确存储
int y = (int)(1U << 31); // 可能会导致负数
具体结果取决于编译器和系统的类型转换规则。通常会变成负数 -2147483648。
这也是为什么在位操作时,尤其是涉及高位时,推荐使用明确的无符号类型。
编译器优化
- 显式告知编译器期望的类型
- 减少隐式类型转换开销
- 提高代码可读性
推荐实践
- 位移操作时使用
U - 定义位掩码时使用
U - 寄存器配置时使用
U - 处理硬件地址和位段时使用
U
总结
使用U后缀是一种防御性编程技术,可以:
- 提高代码安全性
- 避免潜在的类型转换问题
- 增强代码可读性
- 防止未定义行为
相关文章:
32单片机学习记录1之GPIO
32单片机学习记录1之GPIO 前置 GPIO口在单片机中扮演着什么角色? 在单片机中,GPIO口(General Purpose Input/Output) 是一种通用输入/输出接口,扮演着连接单片机与外部设备的桥梁角色。具体来说,它在单片…...
AI 编程助手 Cline
Cline 是一款集成于 Visual Studio Code(VS Code)的 AI 编程助手,旨在提升开发者的编程效率和代码质量。 主要功能: 代码生成与补全:Cline 能根据开发者的输入,自动生成代码片段或完整的函数,减…...
YOLOv11-ultralytics-8.3.67部分代码阅读笔记-patches.py
patches.py ultralytics\utils\patches.py 目录 patches.py 1.所需的库和模块 2.def imread(filename: str, flags: int cv2.IMREAD_COLOR): 3.def imwrite(filename: str, img: np.ndarray, paramsNone): 4.def imshow(winname: str, mat: np.ndarray): 5.PyTorch…...
R语言LCMM多维度潜在类别模型流行病学研究:LCA、MM方法分析纵向数据
全文代码数据:https://tecdat.cn/?p39710 在数据分析领域,当我们面对一组数据时,通常会有已知的分组情况,比如不同的治疗组、性别组或种族组等(点击文末“阅读原文”获取完整代码数据)。 然而,…...
2025 年前端开发现状分析:卷疯了还是卷麻了?
一、前端现状:框架狂飙,开发者崩溃 如果你是个前端开发者,那么你大概率经历过这些场景: 早上打开 CSDN(或者掘金,随便),发现又有新框架发布了,名字可能是 VueXNext.js 之…...
RDK新一代模型转换可视化工具!!!
作者:SkyXZ CSDN:SkyXZ~-CSDN博客 博客园:SkyXZ - 博客园 之前在使用的RDK X3的时候,吴诺老师wunuo发布了新一代量化转换工具链使用教程,这个工具真的非常的方便,能非常快速的完成X3上模型的量化…...
JVM春招快速学习指南
1.说在前面 在Java相关岗位的春/秋招面试过程中,JVM的学习是必不可少的。本文主要是通过《深入理解Java虚拟机》第三版来介绍JVM的学习路线和方法,并对没有过JVM基础的给出阅读和学习建议,尽可能更加快速高效的进行JVM的学习与秋招面试的备战…...
C#中的序列化和反序列化
序列化是指将对象转换为可存储或传输的格式,例如将对象转换为JSON字符串或字节流。反序列化则是将存储或传输的数据转换回对象的过程。这两个过程在数据持久化、数据交换以及与外部系统的通信中非常常见 把对象转换成josn字符串格式 这个过程就是序列化 josn字符…...
xcode常见设置
1、如何使用cmake构建archs为$(ARCHS_STANDARD)的xcode项目 在cmake中使用如下指令 set(CMAKE_OSX_ARCHITECTURES "$(ARCHS_STANDARD)") cmake - nomadli的博客 | nomadli Blog...
PG高可用学习@2
目录标题 一、Patroni 支持在同步复制下备库故障时自动降级为异步复制?参考依据1. PostgreSQL 官方文档2. Patroni 官方文档3. 高可用和容错设计原则 二、patroni 是如何检测备库故障的?1. 心跳机制2. 监控数据库进程状态3. 查询系统视图4. 复制延迟监测5. 网络连接…...
centos 8和centos 9 stream x64的区别
以下是 CentOS 8 与 CentOS Stream 9 的主要区别,从技术架构、更新策略到适用场景等维度进行对比: AI产品独立开发实战营 联系我了解 1. 定位与更新策略 特性CentOS 8CentOS Stream 9定位原为 RHEL 8 的免费稳定复刻版RHEL 9 的上游开发分支ÿ…...
C++基础学习记录—类
1、面向对象的三大特征:封装、继承、多态 2、类和对象 2.1、类的概念 类:类是一个抽象的概念,用于描述同一类对象的特点。 对象:根据类的概念所创造的实体。 类中包含属性和行为 属性:描述类的数据,一…...
云原生时代的后端开发:架构、工具与最佳实践
随着云计算的迅猛发展,云原生(Cloud Native)逐渐成为后端开发的主流趋势。云原生后端不仅能够提高应用的灵活性和可扩展性,还能显著优化开发和运维流程。本文将围绕云原生后端的关键概念、当前热门技术及最佳实践,帮助…...
ARM Cortex-M3/M4 权威指南 笔记【一】技术综述
一、Cortex-M3/M4 处理器的一般信息 1.1 处理器类型 ARM Cortex-M 为 32 位 RISC(精简指令集)处理器,其具有: 32位寄存器32位内部数据通路32位总线接口 除了 32 位数据,Cortex-M 处理器(以及其他任何 A…...
12.项目结构
后端结构 ruoyi-admin 项目启动的入口 提供了两种启动方式 1.RuoYiApplication基于springboot,内置tomcat,直接运行。 2.RuoYiServletInitializer将springboot项目打成一个war包,用外置的servlet容器来运行。 通用功能的controller 后台登录相关的、权限控制相关的、数据字…...
保研考研机试攻略:python笔记(4)
🐨🐨🐨15各类查找 🐼🐼二分法 在我们写程序之前,我们要定义好边界,主要是考虑区间边界的闭开问题。 🐶1、左闭右闭 # 左闭右闭 def search(li, target): h = len(li) - 1l = 0#因为都是闭区间,h和l都可以取到并且相等while h >= l:mid = l + (h - l) // 2…...
高阶C语言|枚举与联合
💬 欢迎讨论:在阅读过程中有任何疑问,欢迎在评论区留言,我们一起交流学习! 👍 点赞、收藏与分享:如果你觉得这篇文章对你有帮助,记得点赞、收藏,并分享给更多对C语言感兴…...
【天梯赛】L1-104 九宫格(C++)
易忽略的错误:开始习惯性地看到n就以为是n*n数组了,实际上应该是9*9的固定大小数组,查了半天没查出来 题面 L1-104 九宫格 - 团体程序设计天梯赛-练习集 代码实现 #include<bits/stdc.h> using namespace std; //易错:开…...
现代C++多线程基础 -忆苦思甜pthread_mutex
c 老古董 文章目录 c 老古董pthread_mutex概念常用apipthread_mutex_initpthread_mutex_lockpthread_mutex_trylockpthread_mutex_unlockpthread_mutex_destroy 案例 pthread_mutex 概念 互斥锁 mutex是一种简单的加锁的方法来控制对共享资源的访问,mutex只有两种…...
soular基础教程-使用指南
soular是TikLab DevOps工具链的统一帐号中心,今天来介绍如何使用 soular 配置你的组织、工作台,快速入门上手。  1. 账号管理 可以对账号信息进行多方面管理,包括分配不同的部门、用户组等,从而确保账号权限和职责…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...
循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...
如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...
从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...
搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...
人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent
安全大模型训练计划:基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标:为安全大模型创建高质量、去偏、符合伦理的训练数据集,涵盖安全相关任务(如有害内容检测、隐私保护、道德推理等)。 1.1 数据收集 描…...
