一、STM32GPIO概念及分类
- GPIO:通用输入输出的英文缩写,可以通过软件控制它的输入和输出
- STM32的GPIO引脚和外部设备进行连接就可以实现通信、数据采集等功能
- 最简单的控制就是LED的控制了
- 通过软件操控GPIO输出高低电平,GPIO也可以作为输入来使用
- STM32F103ZET6引脚图
- GPIO引脚分类
- 电源引脚:VSS,VDD,VSSA,VREF,VDDA
- 晶振引脚:PC14,PC15(32.768k的晶振引脚),OCS(外部高速晶振的输入)
- 复位引脚:NRST
- 下载引脚:PA13,PA14,PA15,PB3,PB4(用于JTAG和SWD的下载,也可作为普的GPIO引脚功能)
- BOOT引脚:BOOT0,PB2(BOOT1)引脚共用
- GPIO引脚:其余引脚为GPIO引脚,PC14,PC15引脚也可以作为GPIO引脚使用
- PA0-PA15,PB0-PB15等,16个引脚为一组
- 如何查找引脚功能
- 数据手册
- 原理图
- 引脚附加说明
- S:电源管脚
- I:仅输入
- I/O:可输入可输出
- FT:附加说明,可以耐5V
- B:BOOT引脚
- RST:复位引脚
二、GPIO的结构框图
- 只有了解了结构框图,才会对GPIO的各种应用模式非常清楚
- 最右侧的I/O端口,就是我们可见的引脚
- I/O左侧的部分为芯片内部封装的东西
- 保护二极管: 防止引脚外部电平过高引入,I/O口电压高,VDD电压低,就会形成回路,吸收电压。外部电压输入低于VSS时,下面的二极管也会起到保护作用,吸收低电压
- 当I/O口连接大功率器件的时候,必须给引脚外部增加驱动电路,例如:当直接连接电机时,电机会不转或者烧毁芯片。
- 所以STM32这个芯片只适用于控制,而不适用于驱动
- 对于输入驱动电路的上下拉电阻
- 使能上拉电阻,那么VDD就作为输出,默认高电平。
- 使能下拉电阻,VSS作为输出,输出一个低电平。这是对于输出模式。 给上拉和下拉开关都断开,都不使能,这种状态称为一个浮空模式。配置成该模式,引脚的电平无法确定,用万用表测量,该模式下的引脚电压只有1点多伏,还可能会不停的跳变,一般不推荐使用这种模式
- 上拉电阻驱动能力较弱,即弱上拉,输出电流较小
- 输出驱动电路
- 输出控制输出高电平的时候,P-MOS导通,N-MOS截止,对外输出就是一个高电平3.3V
- 输出控制输出低电平的时候,P-MOS截止,N-MOS导通,对外输出就是一个低电平0V
- 信号经过施密特触发器的时候,模拟信号变为数字信号(0,1)
- 当GPIO引脚用于ADC采集电压,输入通道的时候,作为模拟输入功能,不经过施密特触发器。
- 施密特触发器:施密特就是为了防止在某一个临界电平的情况出现各种情况的抖动出现,为了稳定我们的输出而设计的。
- 施密特触发器采用电位触发方式,其状态由输入信号电位维持;对于负向递减和正向递增两种不同变化方向的输入信号,施密特触发器有不同的阈值电压。
三、GPIO的工作模式
- 输入模式: 浮空输入、上拉输入、下拉输入、模拟输入
- 输出模式: 开漏输出、复用开漏输出、推挽输出、复用推挽输出
浮空输入模式
- 浮空输入模式下,上拉、下拉处于关闭状态(即不使用开关),经过施密特触发器,I/O端口的电平信号进入输入数据寄存器。也就是说,I/O的电平状态是不确定的,完全由外部输入决定;如果在该引脚悬空(在无信号输入)的情况下,读取该端口的电平是不确定的。
- 通常用于按键、IIC、USART。
上拉输入模式
- 默认情况下输入引脚数据为1,高电平。
- 上拉输入模式下,在上拉开关处使能,上拉电阻连接到VDD,输入信号进入,上拉出会给一个默认的电平3.3V,经过施密特触发器进入输入数据寄存器。读取输入数据寄存器就可以获得外部信号的值。
- 如果外部没有输入,外部信号为上拉电阻VDD。如果外部输入低,就可以获取一个低。
下拉输入模式
- 默认情况下输入引脚为0,低电平。
- 下拉输入模式下,在下拉开关处使能,下拉电阻连接到VSS,输入信号进入,下拉出会给一个默认的电平0V,经过施密特触发器进入输入数据寄存器。读取输入数据寄存器就可以获得外部信号的值。
- 但是在I/O端口悬空(在无信号输入)的情况下,输入端的电平保持在低电平;并且在I/O端口输入为高电平的时候,输入端的电平也是高电平。
模拟输入模式
- 不经过施密特触发器,得到模拟信号,通常用于ADC的外设
- 该模式下,配置上下拉电阻没有作用,不影响外部的模拟信号的采集
开漏输出模式
- 上面部分处于不工作的状态,施密特触发器处于开启状态,所以在输出的时候,它也可以读取输入数据寄存器的值
- 无论输出高低电平,P-MOS管始终处于关闭状态,只依靠N-MOS管来工作
- 输出高电平,N-MOS管处于关闭状态,输出是一个高阻态,由外部上拉电阻输出高电平
- 输出低电平,N-MOS管处于开启状态,此时I/O端口的电平就是低电平。
- 输出的时候,电平也流入到了施密特触发器,可读取输入数据寄存器的值
复用开漏输出模式
- 开漏复用输出模式,与开漏输出模式很是类似。只是输出的高低电平的来源,不是让CPU直接写输出数据寄存器,取而代之利用片上外设模块的复用功能输出来决定的。
推挽输出模式
- P-MOS管、N-MOS管都工作,不断切换。要实现输出控制,可以对输出数据寄存器进行写,也可对位设置/清除寄存器进行写,比如对它的字位写1,就会控制I/O输出高电平,清除位写1,就会输出低电平
- 这里要注意P-MOS管和N-MOS管,当设置输出的值为高电平的时候,P-MOS管处于开启状态,N-MOS管处于关闭状态,此时I/O端口的电平就由P-MOS管决定:高电平;
- 当设置输出的值为低电平的时候,P-MOS管处于关闭状态,N-MOS管处于开启状态,此时I/O端口的电平就由N-MOS管决定:低电平。
- 同时,I/O端口的电平也可以通过输入电路进行读取;注意,此时I/O端口的电平一定是输出的电平。
- 推挽输出模式可以使负载能力和开关速度大大提高
复用推挽输出模式
- 推挽复用输出模式,与推挽输出模式很是类似。只是输出的高低电平的来源,不是让CPU直接写输出数据寄存器,取而代之利用片上外设模块的复用功能输出来决定的。
注意
- 在输出模式的时候,也要对管脚配置一个上拉或下拉,让管脚处于一个稳定的状态
四、硬件设计
LED硬件电路
- 使用两个LED指示灯,DS0,DS1
- DS0发光二极管的阳极连接一个限流电阻到3.3V,要让指示灯点亮,那么阴极需要一个低电平,所以可以通过GPIO输出低电平让它点亮。同理,要让它熄灭只需要输出高电平。
五、软件设计
- 由图知是GPIOB的第五个引脚,就需要找到外设总线GPIOB的地址
#define PERIPH_BASE ((unsigned int)0x40000000) #define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000) #define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00) #define GPIOB_CRL *(unsigned int*)(GPIOB_BASE+0x00) #define GPIOB_CRH *(unsigned int*)(GPIOB_BASE+0x04) #define GPIOB_IDR *(unsigned int*)(GPIOB_BASE+0x08) #define GPIOB_ODR *(unsigned int*)(GPIOB_BASE+0x0C) #define GPIOB_BSRR *(unsigned int*)(GPIOB_BASE+0x10) #define GPIOB_BRR *(unsigned int*)(GPIOB_BASE+0x14) #define GPIOB_LCKR *(unsigned int*)(GPIOB_BASE+0x18)
步骤
- 先将地址封装到头文件中
- 对端口的时钟使能,需要找到时钟的地址,从存储器映射图中去找
- 找到RCC:在AHB高速总线上(从DMA1就是AHB的高速总线)
- DMA1:0x40020000,偏移量20000
- RCC:0x40021000,偏移量21000
- AHB的外设基地址相对于我们的外设总线基地址的偏移量是20000,所以AHB总线的基地址可以写为:
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)
RCC外设基地址相对于我们AHB总线偏移量1000,所以RCC总线的基地址为:#define RCC_BASE (AHBPERIPH_BASE + 0x1000)
- GPIOB是挂接在APB2总线的,需要知道RCC_GPIOB是哪一个位是能的,需要查阅中文参考手册
- 看到偏移地址是0x18,使能控制地址是RCC外设总线的基地址 + 偏移地址
define RCC_APB2ENR (RCC_BASE + 0x18)
- 但是编译器编译之后并不能识别它是地址,所以需要强制转换成指针类型,在最外面再加入一个指针符号,就可以操作它里面的值
#define RCC_APB2ENR *(unsigned int*)(RCC_BASE + 0x18)
- 看到偏移地址是0x18,使能控制地址是RCC外设总线的基地址 + 偏移地址
- 接下来要进行控制,在main函数里面,要让PB5输出一个低电平,点亮指示灯。所以要对PB5进行输出。
- 第一步:打开它的时钟,需要先找到它的使能位
0是关闭,1是开启,所以要写1:
RCC_APB2ENR |= 1 << 3
,左移3位就可以移动到IOPB寄存器位,从而给他赋值1,从而使能时钟。 使用或运算是因为可以不干扰到其它的位 - 第二步:配置推挽输出模式
寄存器分为端口配置低寄存器和端口配置高寄存器,因为是PB5,所以是前8位,使用端口配置低寄存器 要控制寄存器的CNF和MOD,所以一个引脚要占据4个位,因为要对第5个管脚进行控制,所以要MADE5、CNF5,所以*5,移动20位
GPIOB_CRL &= ~(0x0F << (4 * 5))
,这个是清0,0x0F
即0000 1111
,将4个1左移20位,到了CNF5、MODE5,都将他们配置成1,再取反他们就变为0,然后再&,就相当于都清0 接下来要进行配置通用推挽输出,CNF5配置为00,MODE5配置为11,即0011 = 0x03 = 3。GPIOB_CRL |= 3 << (4 * 5)
- 第三步,进行管脚输出控制
可以通过输出数据寄存器或者位设置/清除寄存器进行控制
因为是PB5,所以对第5个引脚进行控制,要输出低电平,所以要写0,所以给BR5写0
GPIO_BSRR = 1 << (16 + 5)
16:高16位的控制,5是第5个引脚效果: 灯常亮
Code:程序占用Flash的大小,180就代表180个字节 RO-data:程序定义的一些常量,存放在Flash当中,Code和RO-deta累加起来,就是代表程序的Flash占用量 RW-data和ZI-data代表RAM的占用容量 占用这么多的原因,因为启动文件有堆栈的定义
- 第一步:打开它的时钟,需要先找到它的使能位