GPIO
GPIO:通用输入输出口;可配置8种输入输出模式
引脚电平:0V-3.3V,部分引脚可容忍5V也可认为高电平,但是对于输出而言,最大就只能输出3.3V,因为供电就只有3.3V,
能容忍5v的在以下的引脚定义下,I/O电平含FT的可以

输出模式下可控制端口输出高低电平,用以驱动LED、控制蜂鸣器、模拟通信协议输出时序等
输入模式下可读取端口的高低电平或电压,用于读取按键输入(最常用)、外接模块电平信号输入、ADC电压采集、模拟通信协议接收数据等
GPIO的基本结构:GPIO挂载在APB2上的


总共有16个引脚,一般称为PA0~PA15
GPIO分为不同的模块:用GPIOA、GPIOB来表示,主要包含了寄存器(是一段特殊的存储器,内核可以通过APB2总线对寄存器进行读写,从而完成对于的输入输出功能)和驱动器。
在模块内每一个位对于一个引脚,其中输出寄存器写1,对于的引脚就会输出高电平,写0就输出低电平,输入同理。
STM32为32位的寄存器,但是模块中的端口只有16位,所有这个寄存器只有低16位的端口
GPIO的电路图:


输入部分
引脚部分

这接入了两个保护二极管,目的是保护电路的:
上面的二极管接VDD,3.3V:如果输入电压比3.3V还要高,则上方会被导通,输入电压产生的电流会流入VDD,从而保护电路;
下面的二极管接VSS,0V:同理;
补:VDD:代表总正极,常用于现代电子
VCC:代表总正极,常用于三极管等
VSS:代表总负极,在MOS管电路中更常用。
GND:所有电路的公共回路
如果电流在0-3.3V中则两边都不接通,正常流入到电路中来

这里连接一个上拉电阻(连至VDD)和下拉电阻(连至VSS),这个开关是可以通过程序进行配置的:如果上面导通,下面断开,就是上拉输入模式(高电平模式),反之;如果两个都断开则为浮空模式;如果两者都接入,并不会同时生效,相互存在互斥,为了防止电源短路风险。
作用:为了给输入提供一个默认的输入电平,因为当输入电平时没有让引脚的输入为低电平或者是高电平时,电路也不知道它是什么状态,则会被其他外界的环境所干扰;目的是不影响正常的输入操作。
补:电路:由电子元器件(电阻、电容、晶体管等)和导线组成的闭合路径,用于传输电能或处理电信号
电压:电势差(单位:伏特 V),表示电场中两点之间的能量差,驱动电流流动。
电平:表示数字信号的状态(高电平或低电平),通常对应特定的电压范围。
电流:电荷的定向流动(单位:安培 A),由电压驱动。
电平信号:用电压表示的离散信号,通常用于数字通信。

肖特基(施密特)触发器:
对输入电压进行整形:如果是输入电压大于某一个阈值,输出就会瞬间变为高电平,反之;
例如:通常的引脚输入的模拟信号为波形模拟信号:

会通过施密特触发器对信号进行整形,中间留有流动范围,可以有效地避免因信号波动造成的是输出抖动现象
之后就可以通过输入寄存器输入数据了,我们再用程序去读取输入数据寄存器对于某一位的数据,就可以知道端口的输入电平;

是连接片上外设的一些端口:
模拟输入:连至ADC(模拟到数字转换器),因为它需要接受模拟数据量,所以接入在触发器之前的
复用功能输入:连接到其他需要读取端口的外设上的
输出部分

数字部分可以由输出寄存器和片上外设控制,两者方式通过数据选择器接到输出控制部分;
输出寄存器:
为普通IO输出,写数据寄存器的某一位就可操作对于的某个端口;同时控制低16位端口,并且只能整体读写,
所以如果想控制一个端口而不影响其他端口的话,需要特殊操作:
1.先读出这个寄存器,然后按位与&=和按位或|=的方式更改某一位。最后将更改的数据写回去;
- (主要)设置位设置/清楚寄存器(可以单独拿操作输出数据寄存器的某一位,而不影响其他位),在位设置寄存器上的对于的位上修改为1即可,不修改的位置写0。如果想对一位进行清0的操作,在位清除寄存器上对应位写1即可;
3.读写STM32的位带区域(这段地址映射了RAM和外设寄存器所有的位);

两个Mos管,上面是P-MOS,下面N-MOS
MOS管就是一种电子开关,信号来控制开关的导通或关闭,开关负责将IO口接到VDD或者VSS。
推挽、开漏、关闭三种输出方式:
1.推挽
P-MOS和N-MOS均有效,数据寄存器为1时,上管导通,下官断开,输出直接接到VDD,输出高电平,反之;这种模式,高低电平均有较强的驱动能力,所以推挽模式也叫强输出模式。在此模式下STM32对IO口有着绝对的控制权。
2.开漏
P-MOS无效,N-MOS有效,数据寄存器为1时,下官断开,这是输出相当于断开,属于高阻模式;为0时,则是输出低电平;这种模式下只有低电平有驱动能力,作为通讯协议的模式。同时还可以用于输出5V电平信号
3.关闭
P-MOS和N-MOS均无效:当引脚配置为输入模式的时候,端口的电平由外部设备进行控制;
GPIO的8种工作模式:通过配置端口的配置寄存器


1.

电路结构基本一样,区别在与上拉电阻和下拉电阻的连接。在浮空输入模式下,端口一定要接一个连续的驱动源,从而不出现浮空的状态。


VDD 3.3V端口和VDD_FT 容忍5V端口
2.

ADC模数转换器的专属:

只有这个模式会关闭数字的输入功能
3.

区别是在高电平下的状态

可以发现输出情况下,输入是存在的,因为一个端口只有一个输出,可以有多个输入
4.


更多内容参考STM32F10xxx参考手册(中文)-第8章
GPIO的寄存器
每个端口的模式由4位进行配置

由端口配置低寄存器和端口配置高寄存器,具体配置在参考手册中
端口输入数据寄存器:
,只用低16位
端口输出数据寄存器
,同理
端口位设置/清除寄存器
端口位清除寄存器:为了与端口位设置/清除寄存器进行区别,此寄存器只进行位清除
端口位设置/清除寄存器,对端口的配置进行锁定,防止被更改
GPIO的外部设备和电路
LED和蜂鸣器:
LED:
发光二级管,正向通电点亮,反向通电不亮,
长脚为正极,内部里较小的是正极。
STM32的GPIO口驱动LED的电路
低电平驱动的电路

LED正极接入3.3V,负极通过一个限流电阻接到PA0上,当PA0输出低电平时,LED两端会产生电压差就会形成正向导通的电流,这样LED就点亮了。当PA0输出高电平时,因为LED两端都是3.3V电压,不会形成电流;限流电阻:防止LED电流过大而烧毁和调整LED的亮度;
补:此时的电压差解释:本质电压差:电路中两点之间的电压之差(即电势差);当PA0为低电平时,电压差=V正极−V负极=3.3V−0V=3.3V(不考虑电阻),故灯亮;当PA0为高电平时,电压差=V正极−V负极=3.3V−3.3V=0V,故灯灭
高电平驱动的电路

LED负极接入GND,正极通过一个限流电阻接到PA0上,反之;
至于这两种接入的选择,需靠考虑输入输出的驱动器,正如我们的STM32的GPIO的推挽模式下,低电平和高电平都具有较高的驱动能力,所以都可以使用,而对单片机或部分芯片中,都使用了高电平弱驱动,低电平高驱动的方法。
有源蜂鸣器:
内部自带振荡源,将正负极接上直流电压即可持续发声,频率固定。
电路中用了个三极管开关进行驱动,在VCC和GND上分别接上正负极的供电,中间的引脚2接入低电平,蜂鸣器就会响,高电平停止。
无源蜂鸣器:内部不带振荡源,需要控制器提供振荡脉冲才可发声,调整提供振荡脉冲的频率,可发出不同频率的声音
三极管可防止IO驱动使STM32负担过重:左边的是基极,带箭头的是发射极,剩下的是集电极
PNP三极管的驱动电路

电流方向:电流从发射极(E)流向集电极(C),基极(B)控制导通
基极给低电平,三极管则会导通,通过3.3V和GND可以给蜂鸣器提供驱动电流
基极给高电平,三极管停止,蜂鸣器没有电流
NPN三极管的驱动电路

电流方向:电流从集电极(C)流向发射极(E),基极(B)控制导通。
驱动逻辑与上分之
注:PNP的三极管最好接在上边,NPN的三极管最好接在下边:"上边"通常指靠近电源(VCC)的一侧 ,"下边"指靠近地(GND)的一侧。
因为三级管的通断,是需要在发射极和基极产生一定的开启电压
面包板


当把元件的引脚插入面包板的孔里后,面包板内部的金属爪就会夹住引脚。
如果需要供电,就从上下的孔位,用跳线引出来即可;
如果中间部分的面包板,没有连接需要用跳线进行连接

点亮LED电路

操作GPIO,需要使用三个步骤:
- 使用RCC开启GPIO的时钟
- 使用GPIO_Init函数初始化GPIO
- 使用输出或输入的函数控制GPIO口
从库函数中找到RCC和GPIO两个外设相关的函数库:
stm32f10x_rcc和stm32f10x_gpio,通过查看其头文件。来找到我们需要的库函数
stm32f10x_rcc:
void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState);//RCC AHB外设时钟控制
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState); //RCC APB2外设时钟控制
void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState); //RCC APB1外设时钟控制
跳到对应的函数里,查看调用信息:

注释上有对此函数的相关说明,以及相关参数:

stm32f10x_gpio.h:
void GPIO_DeInit(GPIO_TypeDef* GPIOx); //所指定的GPIO外设就会被复位
void GPIO_AFIODeInit(void); //可以复位AFIO外设
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);//用结构体的参数来初始化GPIO口,需要手动配置一个结构体变量并赋值。
void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct); //把结构体变量赋值为一个默认值
//以下四个为GPIO的读取函数
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);
uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);
//以下四个为GPIO的写入函数:
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); //可以指定的端口设置为高电平
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); //可以指定的端口设置为低电平
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal); //根据三个参数的值,来设定此端口的电平
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal); //可以同时对16个端口进行写入操作
接线图:
LED闪烁

#include "stm32f10x.h" // Device header
#include "Delay.h"
int main(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//定义结构体变量
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//对其成员工作模式进行配置:推挽模式
//GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_OD;//开漏输出模式
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;//GPIO的引脚位置配置为我们接入的A0引脚
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//默认为50mhz
GPIO_Init(GPIOA,&GPIO_InitStructure);//注意此函数调用的是结构体指针,所以使用此函数时需要传递地址
//输出函数
//GPIO_ResetBits(GPIOA,GPIO_Pin_0);//向PA0输出低电压,灯亮(默认是低电平)
GPIO_SetBits(GPIOA,GPIO_Pin_0);//反之
//GPIO_WriteBit(GPIOA,GPIO_Pin_0, Bit_SET);
//实现LED闪烁,新建System文件夹,相关配置之后,引入延时文件Delay
while(1){
//GPIO_SetBits(GPIOA,GPIO_Pin_0);
//Delay_ms(500);
//GPIO_ResetBits(GPIOA,GPIO_Pin_0);
//Delay_ms(500);
}
}
LED流水灯

#include "stm32f10x.h" // Device header
#include "Delay.h"
int main(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
//GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0 |GPIO_Pin_1|GPIO_Pin_2 |GPIO_Pin_3|GPIO_Pin_4 |GPIO_Pin_5|GPIO_Pin_6 |GPIO_Pin_7;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_All;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_0);
while(1){
/*使用GPIO_Write,同时设置GPIOA所有引脚的高低电平,实现LED流水灯,Write直接写入OCD寄存器中*/
GPIO_Write(GPIOA, ~0x0001); //0000 0000 0000 0001,PA0引脚为低电平,其他引脚均为高电平,注意数据有按位取反
Delay_ms(100); //延时100ms
GPIO_Write(GPIOA, ~0x0002); //0000 0000 0000 0010,PA1引脚为低电平,其他引脚均为高电平
Delay_ms(100); //延时100ms
GPIO_Write(GPIOA, ~0x0004); //0000 0000 0000 0100,PA2引脚为低电平,其他引脚均为高电平
Delay_ms(100); //延时100ms
GPIO_Write(GPIOA, ~0x0008); //0000 0000 0000 1000,PA3引脚为低电平,其他引脚均为高电平
Delay_ms(100); //延时100ms
GPIO_Write(GPIOA, ~0x0010); //0000 0000 0001 0000,PA4引脚为低电平,其他引脚均为高电平
Delay_ms(100); //延时100ms
GPIO_Write(GPIOA, ~0x0020); //0000 0000 0010 0000,PA5引脚为低电平,其他引脚均为高电平
Delay_ms(100); //延时100ms
GPIO_Write(GPIOA, ~0x0040); //0000 0000 0100 0000,PA6引脚为低电平,其他引脚均为高电平
Delay_ms(100); //延时100ms
GPIO_Write(GPIOA, ~0x0080); //0000 0000 1000 0000,PA7引脚为低电平,其他引脚均为高电平
Delay_ms(100); //延时100ms
}
}
蜂鸣器

#include "stm32f10x.h" // Device header
#include "Delay.h"
int main(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
//GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0 |GPIO_Pin_1|GPIO_Pin_2 |GPIO_Pin_3|GPIO_Pin_4 |GPIO_Pin_5|GPIO_Pin_6 |GPIO_Pin_7;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_12);
while(1){
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
Delay_ms(100);
GPIO_SetBits(GPIOB,GPIO_Pin_12);
Delay_ms(100);
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
Delay_ms(100);
GPIO_SetBits(GPIOB,GPIO_Pin_12);
Delay_ms(700);
}
}