(图源均来自与B站UP江协科大)
框图架构讲解
GPIO通俗理解就是单片机的输入输出口,51单片机称为IO口,而STM32称为GPIO口,单片机与外部电路信息交汇就是靠这个GPIO口来实现的,可见,GPIO是STM32单片机很重要的一个组成模块,单片机可以通过寄存器来读取或者写入GPIO口的电平信息,再通过内部驱动器来给GPIO口输出高低电平。大致框图如下。
在STM32中,GPIO共有(A~G)个部分,即GPIOA,GPIOB……而GPIPA又有16个引脚,分别是Pin0,Pin1………
这与51单片机是类似的,IO寄存器分为P0,P1……P0又有8个引脚,P0.0,P0.1……
这里插入一个小知识。
在学习过程中,我认为STM32单片机与51单片机最大的一个区别就是时钟开启部分,在使用51单片机时,如果想要使用某个IO口,就直接对相应寄存器操作就行了,但是STM32就不一样,你要想使用这个GPIO口,就得先开启对应GPIO的时钟,就像是唤醒这个模块一样。如果你不开启这个时钟,那么就算你操作了对应的寄存器,单片机也是不会让你使用这个GPIO模块的,这里后面写代码的时候我再详细介绍。
继续介绍GPIO,GPIO可以被设置成8种模式,分别是浮空输入,上拉输入,下拉输入,模拟输入,开漏输出,推挽输出,复用开漏输出,复用推挽输出。下面我将搭配电路图详细介绍这些模式的工作原理以及应用场景。
其中,浮空输入,上拉输入,下拉输入模式都是把GPIO口当做一个输入口来使用的,也就是说,单片机要从这个端口来接受数字信号的,这个数字信号在经过肖特基触发器整形后,得到一个模拟电压波形,再存储到输入数据寄存器中。逻辑框图走线如下图。
注意:引脚这里的保护二极管起到一个保护的作用,如果引脚的电压值高于3.3V(单片机能容忍的最大工作电压,部分特殊引脚是5V,要另外讨论),那么上方的保护二极管就会打开,是多余的电压流入到VDD中,如果引脚的电压值小于VSS,那么下方的保护二极管就会打开,引脚就会从VSS汲取电压。
那么这三种输入模式的具体区别在哪里呢?
1:浮空输入,当GPIO口模式设置为浮空输入,图中框出来的电阻都是断开的,也就是说,引脚电平处于不确定的状态。
2:上拉输入,当GPIO口模式设置为上拉输入,图中框出来的电阻开关闭合的,此时,GPIO口默认电平就会被VDD拉高,也就是说,引脚默认电平处高电平的状态。
3:下拉输入,当GPIO口模式设置为下拉输入,图中框出来的电阻开关闭合的,此时,GPIO口默认电平就会被VSS拉低,也就是说,引脚默认电平处低电平的状态。
还有一种输入模式是模拟输入模式,这种模式主要是采集外设的电压信息的,当GPIO口设置了模拟输入的模式后,引脚的数字信号就不会经过肖特基触发器了,而是直接接到STM32的ADC外设模块部分,由ADC对采集到的数字信息进一部处理,随后得到电压值。框图走线如下图。
介绍完了四种输入模式,还有四种输出模式。也就是由单片机内部产生一个信号,输出到外部电路中。
1:推挽输出,当模式为推挽输出时,由输出寄存器将数字信号引到GPIO口,此时P-MOS与N-MOS这两个MOS管均有效,当给寄存器写1时,上方的P-MOS打开,下方的N-MOS关闭,此时为高电平,并且驱动能力很强。当给寄存器写0时,上方的P-MOS关闭,下方的N-MOS打开,此时为低电平,驱动能力也很强。这个模式的特性就是高低电平均具有很强的驱动能力。(不过大部分的单片机都是高电平无驱动能力,低电平有很强的驱动能力,可以避免高低电平打架问题)
2:复用推挽输出,这种模式下,主要是单片机内部的其它模块(例如定时器模块)生成一个信号,引到GPIO口,其它部分与推挽输出模式是一样的。当我们要利用单片机输出一段PWM波到GPIO口时,我们就需要将此GPIO口设置为复用推挽输出模式。框图走线如下图。
3:开漏输出,这种模式的P-MOS是无效的,可以理解为不可以用。而N-MOS是有效的,当我们给寄存器写1时,N-MOS关闭,此时GPIO口为高电平,但是也是一个高阻态的状态,也就是说,没有驱动能力。(可以形象地理解为不能输出电流) 我们给寄存器写0时,N-MOS打开,此时GPIO口电压被VSS拉低,为低电平,但是此时驱动能力很强,总结来说,开漏模式就是高电平无驱动能力,低电平有驱动能力。
4:复用开漏输出模式,这种模式与开漏模式差不多,只是信号产生源变成了片上外设而已。
代码实现
点亮LED灯
以上就是GPIO口的大致介绍,那么用代码如何实现利用GPIO来点亮一个LED灯呢?
作为点灯大师,当然是要从点亮一个LED灯开始啦!我们就使用GPIOA的PA0引脚为例子,来实现点灯的功能吧!
上面我也提到过,要用某个模块,我们就要先开启这个模块的时钟。我们可以使用STM公司封装好的函数来实现对寄存器的操作,进而开启时钟。这里用到的函数就是
RCC_APB2PeriphClockCmd(APB2PeriphClock_GPIOA,ENABLE);
现在我们对这个函数来具体解释一下,首先因为GPIO模块是连接APB2这个总线的,所以这里函数是APB2而不是APB1,这个函数的形参有两个,第一个你要使能的具体模块也就是GPIOA,第二个参数是ENABLE,也就是使能(开启)的意思。
开启了GPIOA的时钟后,我们要对这个模块进行初始化,这里STM帮我们用结构体封装了起来,结构体类型名为GPIO_InitTypeDeF,变量名为了规范我们就设置为GPIO_InitStructure吧,所以这里我们先声明一个结构体变量,然后再对这个结构体变量赋初始值。这个结构体一共有三个成员,Mode,Pin,Speed,Mode可以设置成以上我介绍过的八种模式,那么这里我们采用推挽输出,高电平点亮的方案。Pin为引脚号,可以是0—15引脚,这里我们就采用PA0引脚吧,Speed为电平输出信号的翻转速度,是为了低功耗稳定性设置的,一般情况就设置为50MHz就好啦!最后利用GPIO_Init(GPIOA,&GPIO_InitStructure)就可以了。到这里呢,我们的开启时钟工作才算是结束。
GPIO_InitTyPeDeF GPIO_InitStructure;
GPIO_InitStructure.Mode=GPIO_Mode_Out_PP ;
GPIO_InitStructure.Pin=GPIO_Pin_0 ;
GPIO_InitStructure.Speed=GPIO_Speed_50MHz ;
GPIO_Init(GPIOA,&GPIO_InitStructure);
开启完时钟后,我们就可以使用GPIOA的PA0引脚了,这里要对这个引脚置高电平(Set),有多个函数可以选择,GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_Set);GPIO_SetBits(GPIOA,GPIO_Pin_0);这里选择哪一个都可以。
那么完整的代码就是
#include "stm32f10x.h"
int main(void)
{
RCC_APB2PeriPhClock(APB2PeriPh_GPIOA,ENABLE);//开启时钟
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;//PA0引脚
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//50MHz
GPIO_Init(GPIOA,&GPIO_InitStructure);
while(1)
{
GPIO_SetBits(GPIOA,GPIO_Pin_0);//高电平点亮LED灯
}
}
LED流水灯
这里与流水灯要用到八个引脚,所以只需要在开启时钟时,将八个引脚的时钟都开启即可(按位与“|”即可实现,GPIO_Pin_0|GPIO_Pin_1)。那为了方便起见,我们就把16个引脚的时钟都先开启。用到时再设置为高电平即可。
代码具体实现如下
#include "stm32f10x.h"
#include "Delay.h" //改文件已经事先写好,就是封装好的延时函数
int main(void)
{
int i;
RCC_APB2PeriPhClock(APB2PeriPh_GPIOA,ENABLE);//开启时钟
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_All;//PA0引脚
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//50MHz
GPIO_Init(GPIOA,&GPIO_InitStructure);
while(1)
{
for(i=0;i<9;i++)
{
GPIO_SetBits(GPIOA,GPIO_Pin_i);//高电平点亮LED灯
Delay_ms(500);
}
}
}
GPIO大致就粗浅的学习到这里了,后面的学习再陆续使用,以加深对GPIO模块的认识。