IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> STM32-GPIO篇 -> 正文阅读

[嵌入式]STM32-GPIO篇

1 GPIO简介

????????GPIO是通用输入输出端口的简称,简单来说就是STM32可控制的引脚,STM32芯片的GPIO引脚与外部设备连接起来,从而实现与外部通讯、控制以及数据采集的功能。STM32芯片的GPIO被分成很多组,每组有16个引脚,所有的GPIO引脚都有基本的输入输出功能。上电复位后,GPIO默认为浮空状态,部分特殊功能引脚为特定状态。
????????最基本的输出功能是由STM32控制引脚输出高、低电平,实现开关控制,如把GPIO引脚接入LED灯,那就可以控制LED灯的亮灭,引脚接入到继电器或三极管,那就可以通过继电器或三极管控制外部大功率电路的通断。最基本的输入功能是检测外部电平,如把GPIO引脚连接到按键,通过电平高低区分按键是否被按下。

???????STM32的 GPIO模式有以下几种:
? ????????????????GPIO_Mode_AIN ---------------------- 模拟输入
? ????????????????GPIO_Mode_IN_FLOATING -------- 输入浮空
????????????????? GPIO_Mode_IPD ---------------------- 输入下拉
? ????????????????GPIO_Mode_IPU ?---------------------- 输入上拉
? ????????????????GPIO_Mode_Out_OD ---------------- 开漏输出
? ????????????????GPIO_Mode_Out_PP ---------------- 推挽式输出
? ????????????????GPIO_Mode_AF_OD ---------------- 开漏复用功能
? ????????????????GPIO_Mode_AF_PP ----------------- 推挽式复用功能

typedef enum
{ 
  GPIO_Mode_AIN = 0x0,
  GPIO_Mode_IN_FLOATING = 0x04,
  GPIO_Mode_IPD = 0x28,
  GPIO_Mode_IPU = 0x48,
  GPIO_Mode_Out_OD = 0x14,
  GPIO_Mode_Out_PP = 0x10,
  GPIO_Mode_AF_OD = 0x1C,
  GPIO_Mode_AF_PP = 0x18
} GPIOMode_TypeDef;

????????保护二极管及上、下拉电阻:?

????????引脚的两个保护二极管可以防止引脚外部过高或过低的电压输入,当引脚电压高于V_{DD-FT}时,上方的二极管导通,当引脚电压低于Vss时,下方的二极管导通,防止不正常电压引入芯片导致芯片烧毁。但是尽管如此,还是不能直接外接大功率器件,须加大功率及隔离电路驱动,防止烧坏芯片或者外接器件无法正常工作。
?

????????上下拉电阻,从它的结构我们可以看出,通过上、下拉电阻的开关配置,我们可以控制引脚默认状态的电压,开启上拉的时候引脚电压为高电平,开启下拉的时候引脚电压为低电平。也可以设置“既不上拉也不下拉模式”,我们也把这种状态称为浮空模式,配置成这个模式时,直接用电压表测量其引脚电压为1点几伏,这是个不确定的值。所以一般来说我们都会选择给引脚设置“上拉模式”或“下拉模式”使它有默认状态。STM32的内部上拉时“弱上拉”,即通过上拉输出的电流时很弱的,如要求大电流还是需要外部上拉,通过“上拉/下拉寄存器GPIOx_CRL和GPIOx_CRH”控制引脚的上、下拉及浮空模式。

????????TTL施密特触发器:

????????基本原理是当输入电压高于正向阈值电压,输出为高;当输入电压低于负向阈值电压,输出为低;信号经过触发器后,模拟信号转化为0和1的数字信号。但是,当GPIO引脚作为ADC采集电压的输入通道时,用其“模拟输入”功能,此时信号不再经过触发器进行TTL电平转换。ADC外设要采集到的原始的模拟信号。IO口信号经过触发器后,模拟信号转化为0和1的数字信号? ? 也就是高低电平? 并且是TTL电平协议? ?这也是为什么STM32是TTL电平协议的原因。

????????P-MOS管和N-MOS管:

????????GPIO引脚线路经过两个保护二极管后,向上流向“输入模式”结构,向下流向“输出模式”结构。先看输出模式部分,线路经过一个由P-MOS和N-MOS管组成的单元电路,这个结构使GPIO具有了“推挽输出”和“开漏输出”两种模式。
????????所谓的推挽输出模式,是根据这两个MOS管的工作方式来命名的。在该结构中输入高电平时,经过反向后,上方的P-MOS导通,下方的N-MOS关闭,对外输出高电平;而在该结构中输入低电平时,经过反向后,N-MOS导通,P-MOS关闭,对外输出低电平,当引脚高低电平切换时,两个管子轮流导通,P管负责灌电流,N管负责拉电流,使其负载能力和开关速度都比普通的方式由很大的提高。推挽输出的低电平为0伏,高电平为3.3V,如下图,它是推挽输出模式时的等效电路。

????????而在开漏输出模式时,上方的P-MOS完全不工作。如果我们控制输出为0,低电平,则P-MOS管完全关闭,N-MOS管导通,使输出接地,若控制输出为1(它无法直接输出高电平)时,则P-MOS和N-MOS都关闭,所以引脚既不输出高电平也不输出低电平,为高阻态。为了正常使用时必须外部接上拉电阻,参考下图中的等效电路。它具有“线与”特性,也就是说,若有很多个开漏模式引脚连接到一起时,只有当所有引脚都输出高阻态,才由上拉电阻提供高电平,此电平的电压为外部上拉电阻所接的电源的电压。若其中一个引脚为低电平,那线路就相当于短路接地,使得整条线路都为低电平,0伏。

????????推挽输出模式一般应用在输出电平0和3.3伏而且需要高速切换开关状态的场合。在STM32的应用中,除了必须用开漏模式的场合,我们都习惯使用推挽输出模式。开漏输出一般应用在I2C、SMBUS通讯等需要“线与”功能的总线电路中。除此之外,还用在电平不匹配的场合,如需要输出5伏的高电平,就可以在外部接一个上拉电阻,上拉电源为5伏,并且把GPIO设置为开漏模式,当输出高阻态时,由上拉电阻和电源向外输出5伏电平,如下图所示。

????????这里需要注意的是,在查看《STM32中文参考手册V10》中的GPIO的表格时,会看到有“FT”一列,这代表着这个GPIO口时兼容3.3V和5V的;如果没有标注“FT”,就代表着不兼容5V。

2 GPIO设置模式

????????根据上面的几种模式,可以大致分为 4种模式:输入、输出、复用、模拟。先来了解一下M3和M4?I/O口总的功能结构:

2.1 输入配置

????????当I/O端口配置为输入时:
????????????????● 输出缓冲器被禁止?
????????????????● 施密特触发输入被激活
????????????????● 根据输入配置(上拉,下拉或浮动)的不同,弱上拉和下拉电阻被连接
????????????????● 出现在I/O脚上的数据在每个APB2时钟被采样到输入数据寄存器?
????????????????● 对输入数据寄存器的读访问可得到I/O状态

????????下图给出了I/O端口位的输入配置:

????????浮空输入模式:GPIO_Mode_IN_FLOATING

????????浮空输入模式下,I/O端口的电平信号直接进入输入数据寄存器。电平进入后,不经过上下拉,在触发施密特触发器后,进入输入数据寄存器,最后由CPU读取。MCU直接读取I/O口电平,I/O的电平状态是不确定的,完全由外部输入决定;如果在该引脚悬空(在无信号输入)的情况下,读取该端口的电平是不确定的。 (接用电压表测量其引脚电压为1点几伏,这是个不确定值) 以用来做KEY识别。

????????输入上拉模式:GPIO_Mode_IPU

????????IO内部接上拉电阻,此时如果IO口外部没有信号输入或者引脚悬空,IO口默认为高电平,如果I/O口输入低电平,那么引脚就为低电平,MCU读取到的就是低电平。STM32的内部上拉是"弱上拉",即通过此上拉输出的电流是很弱的,如要求大电流还是需要外部上拉。?触发时需要低电平触发,有上拉电阻存在,使得端口为高电平,达到抗干扰作用,只接受低电平!

?????????输入下拉模式:GPIO_Mode_IPD?

????????IO内部接下拉电阻,此时如果IO口外部没有信号输入或者引脚悬空,IO口默认为低电平,如果I/O口输入高电平,那么引脚就为高电平,MCU读取到的就是高电平。需要高电平触发,有下拉电阻存在,使得端口为低电平,达到抗干扰作用,只接受高电平!

? ? ? ? GPIO输入模式总结:

????????在输入模式时,施密特触发器打开,输出被禁止。数据寄存器每隔1个AHB1时钟周期更新一次,可通过数据寄存器GPIOx_IDR读取I/O状态。其中F1的AHB1的时钟如按默认配置一般为72MHz,F4的AHB1的时钟如按默认配置一般为180MHz。

2.2 输出配置

????????当I/O端口被配置为输出时:?
????????????????● 输出缓冲器被激活?
????????????????????????─ 开漏模式:输出寄存器上的’0’激活N-MOS,而输出寄存器上的’1’将端口置于高阻状态(P-MOS从不被激活)。
????????????????????????─ 推挽模式:输出寄存器上的’0’激活N-MOS,而输出寄存器上的’1’将激活P-MOS。?
????????????????● 施密特触发输入被激活
????????????????● 弱上拉和下拉电阻被禁止?
????????????????● 出现在I/O脚上的数据在每个APB2时钟被采样到输入数据寄存器?
????????????????● 在开漏模式时,对输入数据寄存器的读访问可得到I/O状态?
????????????????● 在推挽式模式时,对输出数据寄存器的读访问得到最后一次写的值。

????????下图给出了I/O端口位的输出配置:

????????开漏输出模式:GPIO_Mode_Out_OD

? ? ? ? CPU先写入输出数据寄存器中,再经过输出控制电路,最终到达端口(在此模式下,IO口也可以读取IO口电压)。在开漏输出模式时,只有N-MOS管工作,如果我们控制输出为0,低电平,则P-MOS管关闭,N-MOS管导通,使输出低电平,I/O端口的电平就是低电平,若控制输出为1时,高电平,则P-MOS管和N-MOS管都关闭,输出指令就不会起到作用,此时I/O端口的电平就不会由输出的高电平决定,而是由I/O端口外部的上拉或者下拉决定 ? 如果没有上拉或者下拉 IO口就处于悬空状态,并且此时施密特触发器是打开的,即输入可用,通过输入数据寄存器GPIOx_IDR可读取I/O的实际状态。,I/O口的电平不一定是输出的电平。
????????
优点:
????????1)输出端相当于三极管的集电极,要得到高电平需要上拉电阻才行,适合做电流型的驱动,其吸收电流的能力较强(20ma以内)
????????2)开漏是用来连接不同电平的器件,匹配电平用的,因为开漏引脚不连接外部的上拉电阻时,只能输出低电平,如果需要同时具备输出高电平的功能,则需要接上拉电阻,很好的一个优点是通过改变上拉电源的电压,便可以改变传输电平。比如加上上拉电阻就可以提供TTL/CMOS电平输出等。但其也带来上升沿的延时!
????????3)可以将多个开漏输出的Pin,连接到一条线上。通过一只上拉电阻,在不增加任何器件的情况下,形成“与逻辑”关系。这也是I2C,SMBus等总线判断总线占用状态的原理。
????????(补充:什么是“线与”?: 在一个结点(线)上, 连接一个上拉电阻到电源 VCC 或 VDD 和 n 个 NPN 或 NMOS 晶体管的集电极 C 或漏极 D, 这些晶体管的发射极 E 或源极 S 都接到地线上, 只要有一个晶体管饱和, 这个结点(线)就被拉到地线电平上. 因为这些晶体管的基极注入电流(NPN)或栅极加上高电平(NMOS),晶体管就会饱和, 所以这些基极或栅极对这个结点(线)的关系是或非NOR 逻辑. 如果这个结点后面加一个反相器, 就是或 OR 逻辑.)

????????推挽输出模式:GPIO_Mode_Out_PP

????????CPU先写入输出数据寄存器中,再经过输出控制电路,最终到达端口(在此模式下,IO口也可以读取IO口电压),与开漏输出在于其在经过输出控制电路后面的MOS管不同!在推挽输出模式时,N-MOS管和P-MOS管都工作,如果我们控制输出为0,低电平,则P-MOS管关闭,N-MOS管导通,使输出低电平,I/O端口的电平就是低电平,若控制输出为1 高电平,则P-MOS管导通N-MOS管关闭,使输出高电平,I/O端口的电平就是高电平, ?外部上拉和下拉的作用是控制在没有输出时IO口电平。此时施密特触发器是打开的,即输入可用,通过输入数据寄存器GPIOx_IDR可读取I/O的实际状态。I/O口的电平一定是输出的电平。
????????
优点:
????????推挽电路是两个参数相同的三极管或MOSFET,以推挽方式存在于电路中,各负责正负半周的波形放大任务,电路工作时,两只对称的功率开关管每次只有一个导通,所以导通损耗小、效率高。输出既可以向负载灌电流,也可以从负载抽取电流。推拉式输出级既提高电路的负载能力,又提高开关速度。

????????GPIO输出模式总结:

????????在输出模式中,输出使能,推挽模式时以双MOS管的方式工作,输出数据寄存器GPIOx_ODR可控制I/O输出高低点评。开漏模式时,只有N-MOS工作,输出数据寄存器可控制I/O输出高阻态或低电平。输出速度可配置,有2MHz\25MHz\50MHz的选项。此处的输出速度即I/O支持的高低电平状态最高切换频率,支持的频率越高,功耗越大,如果功耗要求不严格,把速度设置成最大即可。
????????此时施密特触发器时打开的,即输入可用,通过输入数据寄存器GPIOx_IDR可读取I/O的实际状态。
????????用于输出模式时,可使用上拉、下拉或悬空模式。但此时由于输出模式时引脚电平会收到ODR寄存器的影响,而ODR寄存器对应引脚的位为0,即引脚初始化后默认输出低电平,所以在这种情况下,上拉只能起到小幅提高输出电流能力,但不会影响引脚的默认状态。

2.3 复用功能配置

????????当I/O端口被配置为复用功能时:
????????????????● 在开漏或推挽式配置中,输出缓冲器被打开?
????????????????● 内置外设的信号驱动输出缓冲器(复用功能输出)?
????????????????● 施密特触发输入被激活?
????????????????● 弱上拉和下拉电阻被禁止?
????????????????● 在每个APB2时钟周期,出现在I/O脚上的数据被采样到输入数据寄存器?
????????????????● 开漏模式时,读输入数据寄存器时可得到I/O口状态?
????????????????● 在推挽模式时,读输出数据寄存器时可得到最后一次写的值

????????下图示出了I/O端口位的复用功能配置。一组复用功能I/O寄存器允许用户把一些复用功能重新映象到不同的引脚。

????????开漏复用输出模式:GPIO_Mode_AF_OD
????????复用与不是复用的区别在于———非复用输出是cpu控制,复用功能则是从复用功能输出口上接受输出信号,可以是从外设接受输入信号输出。GPIO复用为其他外设,输出数据寄存器GPIOx_ODR无效;? 输出的高低电平的来源于其它外设,施密特触发器打开,输入可用,通过输入数据寄存器可获取I/O实际状态? ? 除了输出信号的来源改变 其他与开漏输出功能相同。

????????推挽复用输出模式:GPIO_Mode_AF_PP

????????复用功能与之前类似,输出信号来源来自复用功能输出口。与开漏复用输出在于其在经过输出控制电路后面的MOS管不同!

????????GPIO复用功能总结:

????????复用功能模式中,输出使能,输出速度可配置,可工作在开漏及推挽模式,但是输出信号源于其它外设,输出数据寄存器GPIOx_ODR无效;输入可用,通过输入数据寄存器可获取I/O实际状态,但一般直接用外设的寄存器来获取该数据信号。
????????用于复用功能时,可使用上拉、下拉或者浮空模式。同输出模式,在这种情况下,初始化后引脚默认输出低电平,上拉只起到小幅提高输出电流能力,但不会影响引脚的默认状态。

2.4 模拟输入配置

?????????当I/O端口被配置为模拟输入配置时:?
????????????????● 输出缓冲器被禁止
????????????????● 禁止施密特触发输入,实现了每个模拟I/O引脚上的零消耗。施密特触发输出值被强置为’0’
????????????????● 弱上拉和下拉电阻被禁止
????????????????● 读取输入数据寄存器时数值为’0’

?????????下图示出了I/O端口位的高阻抗模拟输入配置:

????????模拟输入通道:GPIO_Mode_AIN

? ? ? ? 当GPIO用于模拟功能时,引脚的上、下拉电阻是不起作用的,输入没有上拉下拉电阻,这个时候即使配置了上拉或下拉模式,也不会影响到模拟信号的输入输出,且输入的是电压而非电平(电平只有高低之分,电压则是一个连续值),该输入方式可用于AD转换接受模拟信号。当GPIO引脚用于ADC采集电压的输入通道时,用作"模拟输入"功能,此时信号不经过施密特触发器,直接直接进入ADC模块,并且输入数据寄存器为空 ,CPU不能在输入数据寄存器上读到引脚状态。
????????除了 ADC 和 DAC 要将 IO 配置为模拟通道之外其他外设功能一律 要配置为复用功能模式。

???????GPIO模拟输入功能总结:

????????模拟输入输出模式中,双MOS管结构被关闭,施密特触发器停用,上/下拉也被禁止,其他外设通过模拟通道进行输入输出。

???????IO口总结:
????????????????在STM32中选用IO模式总结:
????????????????(1) 浮空输入_IN_FLOATING ——浮空输入,可以做KEY识别
????????????????(2)带上拉输入_IPU——IO内部上拉电阻输入
????????????????(3)带下拉输入_IPD—— IO内部下拉电阻输入
????????????????(4) 模拟输入_AIN ——应用ADC模拟输入,或者低功耗下省电
????????????????(5)开漏输出_OUT_OD ——IO输出0接GND,IO输出1,悬空,需要外接上拉电阻,才能实现输出高电平。当输出为1时,IO口的状态由上拉电阻拉高电平,但由于是开漏输出模式,这样IO口也就可以由外部电路改变为低电平或不变。可以读IO输入电平变化,实现STM32的IO双向功能
????????????????(6)推挽输出_OUT_PP ——IO输出0-接GND, IO输出1 -接VCC,读输入值是未知的
????????????????(7)复用功能的推挽输出_AF_PP ——片内外设功能(I2C的SCL,SDA)
????????????????(8)复用功能的开漏输出_AF_OD——片内外设功能(TX1,MOSI,MISO.SCK.SS)
????????????????正点原子对常用输入输出方式的作用的总结:
????????????????(1)作为普通GPIO 输入:根据需要配置该引脚为浮空输入、带弱上拉输入或带弱下拉输入,同时不要使能该引脚对应的所有复用功能模块。
????????????????(2)作为普通GPIO 输出:根据需要配置该引脚为推挽输出或开漏输出,同时不要使能该引脚对应的所有复用功能模块。
????????????????(3)作为普通模拟输入:配置该引脚为模拟输入模式,同时不要使能该引脚对应的所有复用功能模块。
????????????????(4)作为内置外设的输入:根据需要配置该引脚为浮空输入、带弱上拉输入或带弱下拉输入,同时使能该引脚对应的某个复用功能模块。
????????????????(5)作为内置外设的输出:根据需要配置该引脚为复用推挽输出或复用开漏输出,同时使能该引脚对应的所有复用功能模块。

3?外设 I/O配置模式选择

????????高级定时器TIM1/TIM8:

?????????通用定时器TIM2/3/4/5:

?????????USART:

?????????SPI:

?????????I2S:

?????????I2C接口:

????????BxCAN:

?????????USB:

?????????全速USB OTG引脚配置:

?????????SDIO:

?????????ADC/DAC:

?????????FSMC:

?????????其它I/O功能:

?4 F1寄存器配置

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

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

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

????????????????看GPIO结构框图的上半部分,它时GPIO引脚经过上、下拉电阻后引入的,它连接到施密特触发器,信号经过触发器后,模拟信号转化为0、1的数字信号,然后存储再“输出数据寄存器GPIOx_IDR”中,通过读取该寄存器就可以了解GPIO引脚的电平状态。

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

????????????????前面提到的双MOS管结构电路的输入信号,是由GPIO“端口输出数据寄存器“GPIOx_ODR”提供的,因此我们通过修改输出数据寄存器的值就可以修改GPIO引脚的输出电平。

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

????????????????”端口位设置/清除寄存器GPIOx_BSRR“可以通过修改输出数据寄存器的值从而影响电路的输出。

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

????????????????”端口位清除寄存器GPIOx_BRR“可以通过修改输出数据寄存器的值从而影响电路的输出。

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

? 5 F4寄存器配置

????????端口模式寄存器(GPIOx_MODER)

????????端口输出类型寄存器(GPIOx_OTYPER)??

????????端口输出速度寄存器(GPIOx_OSPEEDR)

????????端口上拉/下拉寄存器(GPIOx_PUPDR)

????????端口输入数据寄存器(GPIOx_IDR)?

????????端口输出数据寄存器(GPIOx_ODR)

????????端口置位/复位寄存器(GPIOx_BSRR)?

????????端口配置锁存寄存器(GPIOx_LCKR?)

????????复位功能寄存器(低位GPIOx_AFRL & GPIOx_AFRH)?

6 GPIO的初始化(F1)

????????我们以初始化LED为例

6.1 定义一个 GPIO_InitTypeDef 类型的结构体

????????GPIO_InitTypeDef GPIO_InitStructure;? ?/*定义一个 GPIO_InitTypeDef 类型的结构体*/

????????一共有3个参数

typedef struct
{
  uint16_t GPIO_Pin;             /*!< Specifies the GPIO pins to be configured.
                                      This parameter can be any value of @ref GPIO_pins_define */
  GPIOSpeed_TypeDef GPIO_Speed;  /*!< Specifies the speed for the selected pins.
                                      This parameter can be a value of @ref GPIOSpeed_TypeDef */
  GPIOMode_TypeDef GPIO_Mode;    /*!< Specifies the operating mode for the selected pins.
                                      This parameter can be a value of @ref GPIOMode_TypeDef */
}GPIO_InitTypeDef;

6.2?开启 LED 相关的 GPIO 外设时钟

????????RCC_AHB1PeriphClockCmd ( RCC_AHB1Periph_GPIOB, ENABLE);? ? ?/*开启 AHB1时钟*/

????????相对应的外设功能所使用的时钟在stm32f10x_rcc.h 中即可查看到,下面是部分时钟:

//APB2_peripheral
#define RCC_APB2Periph_AFIO              ((uint32_t)0x00000001)
#define RCC_APB2Periph_GPIOA             ((uint32_t)0x00000004)
#define RCC_APB2Periph_GPIOB             ((uint32_t)0x00000008)
#define RCC_APB2Periph_GPIOC             ((uint32_t)0x00000010)
#define RCC_APB2Periph_GPIOD             ((uint32_t)0x00000020)
#define RCC_APB2Periph_GPIOE             ((uint32_t)0x00000040)
#define RCC_APB2Periph_GPIOF             ((uint32_t)0x00000080)
#define RCC_APB2Periph_GPIOG             ((uint32_t)0x00000100)
#define RCC_APB2Periph_ADC1              ((uint32_t)0x00000200)
#define RCC_APB2Periph_ADC2              ((uint32_t)0x00000400)
#define RCC_APB2Periph_TIM1              ((uint32_t)0x00000800)
#define RCC_APB2Periph_SPI1              ((uint32_t)0x00001000)
#define RCC_APB2Periph_TIM8              ((uint32_t)0x00002000)
#define RCC_APB2Periph_USART1            ((uint32_t)0x00004000)
#define RCC_APB2Periph_ADC3              ((uint32_t)0x00008000)
#define RCC_APB2Periph_TIM15             ((uint32_t)0x00010000)
#define RCC_APB2Periph_TIM16             ((uint32_t)0x00020000)
#define RCC_APB2Periph_TIM17             ((uint32_t)0x00040000)
#define RCC_APB2Periph_TIM9              ((uint32_t)0x00080000)
#define RCC_APB2Periph_TIM10             ((uint32_t)0x00100000)
#define RCC_APB2Periph_TIM11             ((uint32_t)0x00200000)

6.3 选择要控制的 GPIO 引脚

????????GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 ;?/*选择Pin9引脚*/

????????可选引脚为0-15 一组IO口有16个引脚

#define GPIO_Pin_0                 ((uint16_t)0x0001)  /* Pin 0 selected */
#define GPIO_Pin_1                 ((uint16_t)0x0002)  /* Pin 1 selected */
#define GPIO_Pin_2                 ((uint16_t)0x0004)  /* Pin 2 selected */
#define GPIO_Pin_3                 ((uint16_t)0x0008)  /* Pin 3 selected */
#define GPIO_Pin_4                 ((uint16_t)0x0010)  /* Pin 4 selected */
#define GPIO_Pin_5                 ((uint16_t)0x0020)  /* Pin 5 selected */
#define GPIO_Pin_6                 ((uint16_t)0x0040)  /* Pin 6 selected */
#define GPIO_Pin_7                 ((uint16_t)0x0080)  /* Pin 7 selected */
#define GPIO_Pin_8                 ((uint16_t)0x0100)  /* Pin 8 selected */
#define GPIO_Pin_9                 ((uint16_t)0x0200)  /* Pin 9 selected */
#define GPIO_Pin_10                ((uint16_t)0x0400)  /* Pin 10 selected */
#define GPIO_Pin_11                ((uint16_t)0x0800)  /* Pin 11 selected */
#define GPIO_Pin_12                ((uint16_t)0x1000)  /* Pin 12 selected */
#define GPIO_Pin_13                ((uint16_t)0x2000)  /* Pin 13 selected */
#define GPIO_Pin_14                ((uint16_t)0x4000)  /* Pin 14 selected */
#define GPIO_Pin_15                ((uint16_t)0x8000)  /* Pin 15 selected */

6.4 设置所选引脚的模式

????????GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;?/*设定为推挽输出模式*/

????????引脚的模式共有八种,分别为模拟输入,输入浮空,输入下拉,输入上拉,开漏输出,推挽式输出,开漏复用功能,推挽式复用功能

typedef enum
{ GPIO_Mode_AIN = 0x0,
  GPIO_Mode_IN_FLOATING = 0x04,
  GPIO_Mode_IPD = 0x28,
  GPIO_Mode_IPU = 0x48,
  GPIO_Mode_Out_OD = 0x14,
  GPIO_Mode_Out_PP = 0x10,
  GPIO_Mode_AF_OD = 0x1C,
  GPIO_Mode_AF_PP = 0x18
}GPIOMode_TypeDef;

6.5? 设定所选管脚的速度

????????GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;? /*设定速度为50MHz?? 高速模式*/

typedef enum
{ 
  GPIO_Speed_10MHz = 1,
  GPIO_Speed_2MHz, 
  GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;

6.6?初始化GPIO

????????GPIO_Init(GPIOB, &GPIO_InitStructure);? ? //初始化所设置的引脚

????????GPIO_Init() 是官方配置的初始化函数??第一个参数是GPIOX 第二个参数是结构体所对应GPIO各种参数的配置

7 GPIO的初始化(F4)

????????我们以初始化LED为例

7.1 定义一个 GPIO_InitTypeDef 类型的结构体

????????GPIO_InitTypeDef GPIO_InitStructure;? ?/*定义一个 GPIO_InitTypeDef 类型的结构体*/

????????一共有5个参数

typedef struct
{
  uint32_t GPIO_Pin;              /*!< Specifies the GPIO pins to be configured.
                                       This parameter can be any value of @ref GPIO_pins_define */
  GPIOMode_TypeDef GPIO_Mode;     /*!< Specifies the operating mode for the selected pins.
                                       This parameter can be a value of @ref GPIOMode_TypeDef */
  GPIOSpeed_TypeDef GPIO_Speed;   /*!< Specifies the speed for the selected pins.
                                       This parameter can be a value of @ref GPIOSpeed_TypeDef */
  GPIOOType_TypeDef GPIO_OType;   /*!< Specifies the operating output type for the selected pins.
                                       This parameter can be a value of @ref GPIOOType_TypeDef */
  GPIOPuPd_TypeDef GPIO_PuPd;     /*!< Specifies the operating Pull-up/Pull down for the selected pins.
                                       This parameter can be a value of @ref GPIOPuPd_TypeDef */
}GPIO_InitTypeDef;

7.2?开启 LED 相关的 GPIO 外设时钟

????????RCC_AHB1PeriphClockCmd ( RCC_AHB1Periph_GPIOB, ENABLE);? ? ?/*开启 AHB1时钟*/

????????Q:为什么要设置时钟?
? ? ? ? 任何外设都需要时钟,51单片机,stm32,430等等,因为寄存器是由D触发器组成的,往触发器里面写东西,前提条件是有时钟输入。stm32是低功耗,他将所有的门都默认设置为disable(不使能),在你需要用哪个门的时候,开哪个门就可以,也就是说用到什么外设,只要打开对应外设的时钟就可以, ? 其他的没用到的可以还是disable(不使能),这样耗能就会减少。
????????Q:为什么 STM32 要有多个时钟源呢?
? ? ? ? 因为首先STM32本身非常复杂,外设非常的多,但是并不是所有外设都需要系统时钟这么高的频率, 比如看门狗以及 RTC 只需要几十k的时钟即可。同一个电路,时钟越快功耗越大,同时抗电磁干扰能力也会越弱,所以对于较为复杂的 MCU 一般都是采取多时钟源的方法来解决这些问题。

????????相对应的外设功能所使用的时钟在stm32f4xx_rcc.h 中即可查看到,下面是部分时钟:

//RCC_AHB1_Peripherals
#define RCC_AHB1Periph_GPIOA             ((uint32_t)0x00000001)
#define RCC_AHB1Periph_GPIOB             ((uint32_t)0x00000002)
#define RCC_AHB1Periph_GPIOC             ((uint32_t)0x00000004)
#define RCC_AHB1Periph_GPIOD             ((uint32_t)0x00000008)
#define RCC_AHB1Periph_GPIOE             ((uint32_t)0x00000010)
#define RCC_AHB1Periph_GPIOF             ((uint32_t)0x00000020)
#define RCC_AHB1Periph_GPIOG             ((uint32_t)0x00000040)
#define RCC_AHB1Periph_GPIOH             ((uint32_t)0x00000080)
#define RCC_AHB1Periph_GPIOI             ((uint32_t)0x00000100) 
#define RCC_AHB1Periph_GPIOJ             ((uint32_t)0x00000200)
#define RCC_AHB1Periph_GPIOK             ((uint32_t)0x00000400)
#define RCC_AHB1Periph_CRC               ((uint32_t)0x00001000)
#define RCC_AHB1Periph_FLITF             ((uint32_t)0x00008000)
#define RCC_AHB1Periph_SRAM1             ((uint32_t)0x00010000)
#define RCC_AHB1Periph_SRAM2             ((uint32_t)0x00020000)
#define RCC_AHB1Periph_BKPSRAM           ((uint32_t)0x00040000)
#define RCC_AHB1Periph_SRAM3             ((uint32_t)0x00080000)
#define RCC_AHB1Periph_CCMDATARAMEN      ((uint32_t)0x00100000)
#define RCC_AHB1Periph_DMA1              ((uint32_t)0x00200000)
#define RCC_AHB1Periph_DMA2              ((uint32_t)0x00400000)
#define RCC_AHB1Periph_DMA2D             ((uint32_t)0x00800000)
#define RCC_AHB1Periph_ETH_MAC           ((uint32_t)0x02000000)
#define RCC_AHB1Periph_ETH_MAC_Tx        ((uint32_t)0x04000000)
#define RCC_AHB1Periph_ETH_MAC_Rx        ((uint32_t)0x08000000)
#define RCC_AHB1Periph_ETH_MAC_PTP       ((uint32_t)0x10000000)
#define RCC_AHB1Periph_OTG_HS            ((uint32_t)0x20000000)
#define RCC_AHB1Periph_OTG_HS_ULPI       ((uint32_t)0x40000000)

#define IS_RCC_AHB1_CLOCK_PERIPH(PERIPH) ((((PERIPH) & 0x810BE800) == 0x00) && ((PERIPH) != 0x00))
#define IS_RCC_AHB1_RESET_PERIPH(PERIPH) ((((PERIPH) & 0xDD1FE800) == 0x00) && ((PERIPH) != 0x00))
#define IS_RCC_AHB1_LPMODE_PERIPH(PERIPH) ((((PERIPH) & 0x81106800) == 0x00) && ((PERIPH) != 0x00))

7.3 选择要控制的 GPIO 引脚

????????GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;??/*选择Pin9引脚*/

????????可选引脚为0-15 一组IO口有16个引脚

#define GPIO_Pin_0                 ((uint16_t)0x0001)  /* Pin 0 selected */
#define GPIO_Pin_1                 ((uint16_t)0x0002)  /* Pin 1 selected */
#define GPIO_Pin_2                 ((uint16_t)0x0004)  /* Pin 2 selected */
#define GPIO_Pin_3                 ((uint16_t)0x0008)  /* Pin 3 selected */
#define GPIO_Pin_4                 ((uint16_t)0x0010)  /* Pin 4 selected */
#define GPIO_Pin_5                 ((uint16_t)0x0020)  /* Pin 5 selected */
#define GPIO_Pin_6                 ((uint16_t)0x0040)  /* Pin 6 selected */
#define GPIO_Pin_7                 ((uint16_t)0x0080)  /* Pin 7 selected */
#define GPIO_Pin_8                 ((uint16_t)0x0100)  /* Pin 8 selected */
#define GPIO_Pin_9                 ((uint16_t)0x0200)  /* Pin 9 selected */
#define GPIO_Pin_10                ((uint16_t)0x0400)  /* Pin 10 selected */
#define GPIO_Pin_11                ((uint16_t)0x0800)  /* Pin 11 selected */
#define GPIO_Pin_12                ((uint16_t)0x1000)  /* Pin 12 selected */
#define GPIO_Pin_13                ((uint16_t)0x2000)  /* Pin 13 selected */
#define GPIO_Pin_14                ((uint16_t)0x4000)  /* Pin 14 selected */
#define GPIO_Pin_15                ((uint16_t)0x8000)  /* Pin 15 selected */
#define GPIO_Pin_All               ((uint16_t)0xFFFF)  /* All pins selected */

7.4 设置所选引脚的模式

????????GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;? ?/*设定为输出模式*/

????????引脚的模式共有四种,分别为输入,输出,复用,和模拟模式

typedef enum
{ 
  GPIO_Mode_IN   = 0x00, /*!< GPIO Input Mode */
  GPIO_Mode_OUT  = 0x01, /*!< GPIO Output Mode */
  GPIO_Mode_AF   = 0x02, /*!< GPIO Alternate function Mode */
  GPIO_Mode_AN   = 0x03  /*!< GPIO Analog Mode */
}GPIOMode_TypeDef;

7.5 设定所选引脚的输出类型

????????GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;?/*设置引脚的输出类型为推挽输出*/

????????输出模式有两种:推挽输出和开漏输出? ?

typedef enum
{ 
  GPIO_OType_PP = 0x00,
  GPIO_OType_OD = 0x01
}GPIOOType_TypeDef;

????????只有输出模式才需要配置,输入模式下不需要配置

7.6??设定所选管脚的速度

????????GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//设定速度为100MHz? 高速模式

typedef enum
{ 
  GPIO_Low_Speed     = 0x00, /*!< Low speed    */
  GPIO_Medium_Speed  = 0x01, /*!< Medium speed */
  GPIO_Fast_Speed    = 0x02, /*!< Fast speed   */
  GPIO_High_Speed    = 0x03  /*!< High speed   */
}GPIOSpeed_TypeDef;

/* Add legacy definition */
#define  GPIO_Speed_2MHz    GPIO_Low_Speed    
#define  GPIO_Speed_25MHz   GPIO_Medium_Speed 
#define  GPIO_Speed_50MHz   GPIO_Fast_Speed 
#define  GPIO_Speed_100MHz  GPIO_High_Speed  

7.7?设定所选管脚的上拉与下拉

????????GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;?/*设置引脚为上拉模式*/

????????可设置为:上拉,下拉,与浮空

typedef enum
{ 
  GPIO_PuPd_NOPULL = 0x00,
  GPIO_PuPd_UP     = 0x01,
  GPIO_PuPd_DOWN   = 0x02
}GPIOPuPd_TypeDef;

7.8 初始化GPIO

????????GPIO_Init(GPIOB, &GPIO_InitStructure);? ? //初始化所设置的引脚

????????GPIO_Init() 是官方配置的初始化函数??第一个参数是GPIOX 第二个参数是结构体所对应GPIO各种参数的配置

8 stm32 GPIO速率?

8.1?GPIO 引脚速度???

????????GPIO 引脚输出速度有:GPIO_Speed_2MHz(10MHz,50MHz,100MHz) ?
????????官方一点的解释:GPIO口的
驱动电路响应速度,不是输出信号的速度。输出信号的速度与程序有关,通过选择速度来选择不同的驱动电路,降低功耗控制噪声。又称输出驱动电路的响应速度:(芯片内部在I/O口的输出部分安排了多个响应速度不同的输出驱动电路,用户可以根据自己的需要选择合适的驱动电路,通过选择速度来选择不同的输出驱动模块,达到最佳的噪声控制和降低功耗的目的。)
????????可理解为: 输出驱动电路的带宽:即一个驱动电路可以不失真地通过信号的最大频率。(如果一个信号的频率超过了驱动电路的响应速度,就有可能信号失真。) 例如如果信号频率为10MHz,而你配置了2MHz的带宽,则10MHz的方波很可能就变成了正弦波。就好比是公路的设计时速,汽车速度低于设计时速时,可以平稳地运行,如果超过设计时速就会颠簸,甚至翻车。
????????关键是:GPIO的引脚速度跟应用相匹配,速度配置越高,输出驱动电路的带宽越大,
噪声也越大,功耗越大,速度配置越低,输出驱动电路的带宽越小,噪声也越小,功耗越小。使用合适的驱动器可以降低功耗和噪声。我们选择的只是不同的输出驱动电路,而电路在设计好了后它本身的带宽也就确定了,也就是说这个速率(带宽)与系统时钟无关。

????????比如:高频的驱动电路,噪声也高,当不需要高的输出频率时,请选用低频驱动电路,这样非常有利于提高系统的EMI性能。当然如果要输出较高频率的信号,但却选用了较低频率的驱动模块,很可能会得到失真的输出信号。关键是GPIO的引脚速度跟应用匹配(推荐10倍以上)。
????????比如:
? ? ? ? ? ? ? ? 1)USART串口,若最大波特率只需115.2k,那用2M的速度就够了,既省电也噪声小。
? ? ? ? ? ? ? ? 2)I2C接口,若使用400k波特率,若想把余量留大些,可以选用10M的GPIO引脚速度。
? ? ? ? ? ? ? ? 3)SPI接口,若使用18M或9M波特率,需要选用50M的GPIO的引脚速度。

8.2?GPIO的翻转速度??

????????GPIO的翻转速度指:输入/输出寄存器的0 ,1 值反映到外部引脚(APB2上)高低电平的速度。F1手册上指出GPIO最大翻转速度可达18MHz。

????????通过简单的程序测试,用示波器观察到的翻转时间:? 是综合的时间,包括取指令的时间、指令执行的时间、指令执行后信号传递到寄存器的时间(这其中可能经过很多环节,比如AHB、APB、总线仲裁等),最后才是信号从寄存器传输到引脚所经历的时间。??

????????如:有上拉电阻,其阻值越大,RC延时越大,即逻辑电平转换的速度越慢,功耗越大。

8.3?GPIO的输出速度

????????GPIO 输出速度:与程序有关,(程序中写的多久输出一个信号)。

9 stm32端口重映射

????????重映射:为了使不同器件封装的外设 IO 功能数量达到最优,可以把一些复用功能重新映射到其他一些引脚上,目的为了让设计工程师可以更好地安排引脚的走向和功能,在 STM32 中引入了外设引脚重映射的概念,即一个外设的引脚除了具有默认的端口外,还可以通过设置重映射寄存器的方式,把这个外设的引脚映射到其它的端口。简单的讲就是把管脚的外设功能映射到另一个管脚去使用,但是不是可以随便映射的,根据手册是否可以映射。

????????重映射技术的需求背景:
? ? ? ? 1)I/O的复用:GPIO和内置外设共用引出管脚
????????2)I/O的重映射:复用功能(AFIO)从不同的GPIO管脚引出
????????3)方便了PCB的设计,减少了信号交叉干扰
????????4)分时复用某些外设,虚拟地增加了端口数目
????????5)为了使不同器件封装的外设IO功能数量达到最优,可以把一些复用功能重新映射到其他一些引脚上。STM32中有很多内置外设的输入输出引脚都具有重映射(remap)的功能。

????????以串口1为例:

????????上图中的,Remap对应的I/O就是可以重映射到的I/O,Default就是该I/O默认可复用的功能。从上图中可以看出 串口1 可以重映射到 PB6和PB7引脚,也就是说如果PA9和PA10引脚不好用的时候,或者已经被占用了;那么可以用PB6和PB7来实现串口1的功能。

?9.1 AFIO重映射的操作步骤

? ? ? ? ?1)使能被重新映射到的I/O端口时钟
? ? ? ? ?2)使能被重新映射的外设时钟
? ? ? ? ?3)使能AFIO功能的时钟(勿忘)
? ? ? ? ?4)进行重映射

????????注意第3步,使能AFIO功能时钟,为什么需要使能这个时钟 & 什么时候需要使能这个时钟, 可以参见下面这个回答

????????那么,问题来了!AFIO 是什么?AFIO 时钟什么时候需要开启?  
   我们从《STM32中文参考手册_V10》中找到:对寄存器 AFIO_EVCR、AFIO_MAPR 和 AFIO_EXTICRX 进行读写操作前,应当首先打开 AFIO 的时钟(设置 APB2 外设时钟使能寄存器 RCC_APB2ENR)。
   也就是说:当你需要配置 AFIO 这些寄存器的时候,就需要把 RCC_APB2ENR 寄存器的 AFIO 位置‘1’打开 AFIO 时钟。  
   跟 AFIO 相关的寄存器有:
   1、 事件控制寄存器(AFIO_EVCR)
   2、 复用重映射和调试I/O 配置寄存器(AFIO_MAPR)
   3、 外部中断配置寄存器1(AFIO_EXTICR1)
   4、 外部中断配置寄存器2(AFIO_EXTICR2)
   5、 外部中断配置寄存器3(AFIO_EXTICR3)
   6、 外部中断配置寄存器4(AFIO_EXTICR4)
   看看这些寄存器的定义,我们就明白,这些寄存器是用于“事件控制”、“重映射”、“调试IO配置”、“外部中断”的。例如 AFIO_EXTICRX 用于选择 EXTIx 外部中断的输入源。
   总结:当我们需要配置这些 AFIO 寄存器的时候,就需要打开 RCC_APB2ENR 寄存器的 AFIO 时钟,而不是用到引脚复用功能的时候打开。

?9.2 部分重映射 & 完全重映射

????????部分重映射:功能外设的部分引脚重新映射,还有一部分引脚是原来的默认引脚。
????????完全重映射:功能外设的所有引脚都重新映射。

?????????引脚重映射配置过程(串口3为例):
????????????????1)使能GPIO时钟(重映射后的IO);
????????????????2)使能功能外设时钟(例如串口3);
????????????????3)使能AFIO时钟。重映射必须使能AFIO时钟:
????????????????4)开启重映射。

  GPIO_PinRemapConfig(GPIO_FullRemap_USART3, ENABLE);
  //根据第一个参数,来确定是部分重映射还是全部重映射

????????以串口3为例,如果想部分映射:GPIO_PinRemapConfig()的第一个参数取值应该是:?GPIO_PartialRemap_USART3,如果是完全映射就是:GPIO_FullRemap_USART3

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-09-19 08:09:02  更:2021-09-19 08:10:36 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 2:55:58-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码