本内容是基于学习江协科技STM32之后,和韦东山视频中的GPIO内容整理而得。
1. GPIO
1.1 GPIO简介
- GPIO是通用输入输出口
- 可配置为8种输入输出模式
- 输入:浮空输入(IN_FLOATING)、模拟输入(AIN)、上拉输入(IPU)、下拉输入(IPD)
- 输出:开漏输出(Out_OD)、推挽输出(Out_PP)、复用开漏输出(AF_OD)、复用推挽输出(AF_PP)
- 引脚电平:0V~3.3V,部分引脚可容忍5V(标注"FT")
- 输出模式下可控制端口输出高低电平,用以驱动LED、控制蜂鸣器、模拟通信协议输出时序等
- 输入模式下可读取端口的高低电平或电压,用于读取按键输入、外接模块电平信号输入、ADC电压采集、模拟通信协议接收数据等。
1.2 GPIO基本结构
- APB2总线
在STM32中,所有GPIO都是挂载在APB2外设总线上的,GPIO外设的名称是按照GPIOA、GPIOB、GPIOC等来命名的。
每个GPIO外设,总共有16个引脚,编号从0到15。 - 寄存器
每个GPIO包括寄存器和驱动器。寄存器是一段特殊的存储器,内核可以通过APB2总线对寄存器进行读写,可以完成输出电平和读取电平的功能。寄存器的每一位对应一个引脚,其中输出寄存器写1,对应的引脚就会输出高电平;写0,就输出低电平。
输入寄存器读取为1,就证明对应的端口目前是高电平,读取为0,就是低电平。
因为STM32是32位的单片机,所以STM32内部的寄存器都是32位的。
但这个端口只有16位的,所以这个寄存器只有低16位对应的有端口,高16位没有用到。 - 驱动器
驱动器是增加信号的驱动能力的。
寄存器只负责存储数据,如果要进行点灯这样的操作的话,需要驱动器来负责增大驱动能力。
1.3 GPIO端口结构
- I/O引脚部分
- 首先IO引脚,接了两个保护二极管,是对输入电压进行限幅的。上面的二极管接VDD=3.3V,下面接VSS=0V。如果输入电压比3.3V还要高,那上方二极管导通,输入电压产生的电流就会直接流入VDD而不会流入内部电路,这样就可以避免过高的电压对内部这些电路产生伤害。
- 如果输入电压比0V还要低,这个电压是相对于VSS的电压,所以是可以有负电压的,那这时下方这个二极管就会导通,电流会从VSS直接流出去,而不会从内部电路汲取电流,也是可以保护内部电路的。
- 如果输入电压在0~3.3V之间,那两个二极管均不会导通,这时二极管对电路没有影响,这就是保护二极管的用途。
- 开关
- 上拉电阻至VDD,下拉电阻至VSS,这个开关是可以通过程序进行配置的。
- 如果上面导通、下面断开,就是上拉输入模式;
- 如果下面导通、上面断开,就是下拉输入模式;
- 如果两个都断开,就是浮空输入模式。
- 上拉和下拉其实是为了给输入提供一个默认的输入电平的,因为对应一个数字的端口,输入不是高电平就是低电平,那如果引脚啥都不接,那到底算高电平还是低电平呢?如果输入啥都不接,这时输入就会处于一种浮空的状态,引脚的输入电平极易受外界干扰而改变,为了避免引脚悬空而导致输入数据不确定,需要在这里加上上拉或下拉电阻。如果接入上拉电阻,当引脚悬空时,还有上拉电阻来保证引脚的高电平,所以上拉输入又可以称作是默认为高电平的输入模式。下拉就是默认为低电平的输入方式。
- 当模拟输入时,上拉、下拉或浮空都无效。
- 电压范围
- 1:0.42*(3.3-1)+1=1.546V~3.6V,电压在该范围时,读寄存器读到1,表示高电平。对于具有FT的,1.546V~5.xV,到5V上
- 0:-0.3V~0.28*(3.3-2)+0.8=1.164V,电压在该范围时,读寄存器读到0,表示低电平
- 施密特触发器
- 施密特触发器是对输入电压进行整形的。如果输入电压大于某一阈值,输出就会瞬间升为高电平;输入电压小于某一阈值,输出就会瞬间降为低电平。
- 接下来经过施密特触发器整形的波形就可以直接写入输入数据寄存器了,再用程序读取输入数据寄存器对应的某一位数据,就可以知道端口的输入电平了。
- 至片上外设
- 上面两路线路就是连接到片上外设的一些端口,模拟输入是连接到ADC上的,因为ADC需要接收模拟量。
- 另一个是复用功能输入,是连接到其他需要读取端口的外设上的,比如串口的输入引脚等。这根线接收的是数字量,所以在施密特触发器后面。
- 输出部分:
- 可以由输出数据寄存器或片上外设控制,两种控制方式通过数据选择器接到了输出控制。
- 如果选择通过输出数据寄存器进行控制,就是普通的IO口输出。写这个数据寄存器的某一位就可以操作对应的某个端口了。
- 左边还有个位设置/清除寄存器,可以用来单独操作输出数据寄存器的某一位,而不影响其他位。因为这个输出数据寄存器同时控制16个端口,并且寄存器只能整体读写,如果想控制其中某一个端口而不影响其他端口的话,就需要一些特殊的操作方式。
- 第一种方式是先读出这个寄存器,然后用按位与和按位或的方式更改某一位,最后再将更改的数据写回去,在C语言中就是&=和|=的操作。
- 第二种是通过设置这个位设置和位清除寄存器。如果要对某一位进行置1的操作,在位设置寄存器的对应位写1即可,剩下不需要操作的位写0,这样它内部就会有电路,自动将输出数据寄存器中对应位置为1,而剩下写0 的位保持不变。这样就保证了只操作其中某一位而不影响其他位,并且这是一步到位的操作。如果想对某一位进行清0操作,就在位清除寄存器的对应位写1即可,这样内部电路就会把这一位清0了。
- 第三种操作方式,就是读写STM32中的"位带"区域,这个位带的作用就跟51单片机的位寻址的作用差不多的。在STM32中,专门分配的有一段地址区域,这段地址映射了RAM和外设寄存器所有的位,读写这段地址中的数据,就相当于读写所映射位置的某一位。
- 推挽、开漏、或关闭(在网上看到说输出控制里是有个取反操作的)
- 输出控制之后就接到了MOS管,上面是P-MOS,下面是N-MOS,这个MOS管是一种电子开关,信号来控制开关的导通和关闭,开关负责将IO口接到VDD或者VSS,在这里可以选择推挽、开漏或关闭三种输出方式。
- 在推挽输出模式下,P-MOS和N-MOS均有效。数据寄存器为1时,上管导通,下管断开,输出直接接到VDD,就是输出高电平;数据寄存器为0时,上管断开,下管导通,输出直接接到VSS,就是输出低电平,这种模式下,高低电平均有较强的驱动能力,所以推挽输出模式也可以叫强推输出模式。在推挽输出模式下,STM32对IO口具有绝对的控制权,高低电平都由STM32决定。
- 在开漏输出模式下,P-MOS是无效的,只有N-MOS在工作,数据寄存器为1时,下管断开,这时输出相当于断开,也就是高阻模式;数据寄存器为0时,下管导通,输出直接接到VSS,也就是输出低电平。这种模式下,只有低电平有驱动能力,高电平是没有驱动能力的。开漏模式可以作为通信协议的驱动方式,比如I2C通信的引脚就是使用的开漏模式,在多机通信的情况下,这个模式可以避免各个设备的相互干扰,另外开漏模式还可以用于输出5V的电平信号。
双向通信:
- IC1想发data:
- 会输出1:即PMOS不起作用,引脚处于悬空状态,由外部上拉电阻起作用,
- 可读该引脚的状态:
- 0:IC2驱动pin,该引脚被占用
- 1:IC2未驱动,可使用该引脚
- 当读取引脚的状态为1时,IC1驱动data为0;
- 当引脚配置为输入模式的时候,这两个MOS管都无效,也就是输出关闭,端口的电平由外部信号来控制。
操作GPIO引脚:
(1)设置方向
(2)设置模式:
- 输入:上拉、下拉
- 输出:推挽、开漏
(3)设置速率
(4)
- 输入:读值
- 输出:写值
1.4 GPIO模式
模式名称 | 性质 | 特征 |
---|---|---|
浮空输入 | 数字输入 | 可读取引脚电平,若引脚悬空,则电平不确定 |
上拉输入 | 数字输入 | 可读取引脚电平,内部连接上拉电阻,悬空时默认高电平 |
下拉输入 | 数字输入 | 可读取引脚电平,内部连接下拉电阻,悬空时默认低电平 |
模拟输入 | 模拟输入 | GPIO无效,引脚直接接入内部ADC |
开漏输出 | 数字输出 | 可输出引脚电平,高电平为高阻态,低电平接VSS |
推挽输出 | 数字输出 | 可输出引脚电平,高电平接VDD,低电平接VSS |
复用开漏输出 | 数字输出 | 由片上外设控制,高电平为高阻态,低电平接VSS |
复用推挽输出 | 数字输出 | 由片上外设控制,高电平接VDD,低电平接VSS |
1.4.1 浮空/上拉/下拉输入
根据开关的工作不工作、上拉还是下拉,可以配置为浮空、上拉、下拉输入模式,
1.4.2 模拟输入
1.4.3 开漏/推挽输出
注:此时输入是可用,通过输入数据寄存器GPIOx_IDR可读取I/O的实际状态。I/O口的电平是输出的电平。
1.4.4 复用开漏/推挽输出
1.5 外设GPIO配置
参考STM32F10参考书册
2. C语言
2.1 C语言数据类型
关键字 | 位数 | 表示范围 | stdint关键字 | ST关键字 |
---|---|---|---|---|
char | 8 | -128 ~ 127 | int8_t | s8 |
unsigned char | 8 | 0 ~ 255 | uint8_t | u8 |
short | 16 | -32768 ~ 32767 | int16_t | s16 |
unsigned short | 16 | 0 ~ 65535 | uint16_t | u16 |
int | 32 | -2147483648 ~ 2147483647 | int32_t | s32 |
unsigned int | 32 | 0 ~ 4294967295 | uint32_t | u32 |
long | 32 | -2147483648 ~ 2147483647 | ||
unsigned long | 32 | 0 ~ 4294967295 | ||
long long | 64 | -(2^64)/2 ~ (2^64)/2-1 | int64_t | |
unsigned long long | 64 | 0 ~ (2^64)-1 | uint64_t | |
float | 32 | -3.4e38 ~ 3.4e38 | ||
double | 64 | -1.7e308 ~ 1.7e308 |