GPIO
简介
GPIO(General Purpose Input Output)通用输入输出口是微控制器(MCU)必备的片上外设,可以实现微控制器与外部设备的数字交换。
STM32F103系列的芯片最多可以提供112个多功能双向IO引脚,但是显然我们的STM32F103C8T6没有那么多IO口,直接把核心板拿起来看看就可以知道。具体可以查看一下引脚定义表(来自b站江科大自化协):
虽然没有112个那么多,但是一般情况下也是够我们用的。
STM32的一部分功能是有指定某些引脚的,所以我们再设计线路的时候应该优先把有特殊功能的引脚先分配给那些特殊功能。
例如USART的TX和RX口分别分配到了GPIOA的9号口和GPIOA的10号口。那么如果我们的项目中涉及到了USART1,我们就应该先把这两个口给USART留着。如果这两个引脚我们就是没办法留下来,那我们也还有最后一手,那就是引脚重映射。
表格的最后一项有说明某些引脚可以重定义为哪些功能,例如GPIOA的9号和10号引脚我们没法为USART1留住,我们还可以将GPIOB的6号和7号引脚重映射为USART1(同步异步收发器)的TX和RX,关于这个,后面会演示。
命名
STM32的GPIO引脚命名分为两部分,第一部分是端口号,第二部分是引脚号。
端口号就是GPIOx,x为大写字母,可以从上面引脚定义表看出,一般我们用的就是GPIOA或者是GPIOB,GPIOC的话仅有3个引脚,并且都有各种特殊的使命。
引脚号从0~15一共16个,也就是我上面说的x号引脚。
工作模式
STM32芯片的IO引脚一共有8种工作模式,输入输出各占4个。
不同模式的不同结构我就不细说了,一共有三点原因。
第一点是我自己都迷迷糊糊懵懵懂懂的就不误人子弟了。
第二点是就算不了解物理结构也不妨碍我们使用。
第三点参考第一点。
输入模式
浮空输入GPIO_Mode_IN_FLOATING
默认情况为高阻态,一般用在数据传输的时候,例如SPI。
给低电平则读出逻辑0,给高电平则读出逻辑1,什么也不给的话就不确定了。
上拉输入GPIO_Mode_IPU
默认情况下为高电平,也就是给低电平的时候读出逻辑0,给高电平或者是什么也不给的话就读出逻辑1。
下拉输入GPIO_Mode_IPD
默认情况下为低电平,也就是给高电平的时候读出逻辑1,给低电平或者是什么也不给的话就读出逻辑10。
模拟输入GPIO_Mode_AIN
将芯片引脚模拟信号引到内部的模数转换器ADC中。用到ADC的时候用到这个。
输出模式
开漏输出GPIO_Mode_Out_OD
利用外部电路的驱动能力,减少IC内部的驱动。一般来说,如果要求输出电流大,或者是外部电平不匹配的时候,我们就选择开漏输出。
开漏复用输出GPIO_Mode_AF_OD
在开漏输出的条件下,当GPIO口被用作第二功能的时候(并非作为同样IO口),我们选择开漏复用输出。
推挽输出GPIO_Mode_Out_PP
导通损耗小,效率高,既能提高负载能力又能提高开关速度。简单来说,纪要输出高电平又要输出低电平的情况下,我们就选择推挽输出。
推挽复用输出GPIO_Mode_AF_PP
同上,在推挽输出的条件下,当GPIO口被用作第二功能的时候(并非作为同样IO口),我们选择推挽复用输出。
GPIO输出速度
如果GPIO为输出模式,那么我们可以选择输出的速度,固件库给我们提供了三种选择,分别为2MHz,10MHz,50MHz。
一般推荐GPIO输出速度为输出信号的5~10倍。例如我们使用USART时,撑死也就115200的波特率,2MHz就够用了。
使用I2C的话,如果使用了400000b/s的速率,那么就可以选用10MHz。
使用SPI的话,或许就要使用50MHz了。
总之就是输出的信号速率越大,我们给的输出速度就越大,如果实在拿不准的话直接50MHz也是可以的。
如果只是点灯,点数码管,让蜂鸣器叫个响之类的不用传输信号的,那么给个2MHz就可以了,减小了噪声也更加省电。
固件库函数
注:红色字体为固定的函数名,绿色字体为推荐的参数
GPIO初始化
GPIO_Init(GPIOA,&itd)
第一个参数填入需要初始化的GPIO端口号,STM32F103C8T6中仅能使用GPIOA和GPIOB。
第二个参数为类型为GPIO_InitStruct类型变量的地址,通过给该类型变量的成员赋值来配置GPIO的初始化。
GPIO_Pin用于指定要初始化的引脚号。
GPIO_Speed用于指定输出的频率,如上文所说一共有三种选择,如果我们仅仅是点灯的话使用2MHz就行,GPIO_Speed_2MHz。
GPIO_Mode用于指定GPIO口的模式,具体可选的参数请自行去库函数文件里寻找(注释里有提醒),我们点灯的话使用推挽输出,GPIO_Mode_Out_PP。
设置输出的电平
能够设置输出电平的函数很多,我这里仅列出几个常用的,具体可以参考库函数文件。
GPIO_WriteBit(GPIOA,GPIO_Pin_0,1)
指定GPIO的端口以及引脚来输出指定的电平。
参数一指定端口。
参数二指定引脚。
参数三指定输出的电平,类型为BitAction,实际上是枚举类型,我们填入0(低电平)或是1(高电平)即可。
如果嫌这样一个个引脚指定太麻烦了,我们也有更方便的函数。
GPIO_Write(GPIOA,0x00)
参数一指定端口。
参数二填入一个16位的数,这个数可以指定整个端口的16个引脚的输出电平,16位刚好对应了每个端口的16个引脚。
获取输入的电平
这两个函数的参数和上面设置输出电平的函数类似,相信大家看看着俩函数的名字也可以知道怎么使用它们来读取电平,就不再赘述了。
流水灯示例
#include "stm32f10x.h" // Device header
#include "Delay.h"int main(void){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef itd;itd.GPIO_Mode=GPIO_Mode_Out_PP; //使用推挽输出itd.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2; //可以用|来一次初始化多个引脚itd.GPIO_Speed=GPIO_Speed_2MHz; //点个灯,2MHz足矣GPIO_Init(GPIOA,&itd);GPIO_Write(GPIOA,0x00);while(1){GPIO_WriteBit(GPIOA,GPIO_Pin_0,1);Delay_ms(500);GPIO_WriteBit(GPIOA,GPIO_Pin_0,0);GPIO_WriteBit(GPIOA,GPIO_Pin_1,1);Delay_ms(500);GPIO_WriteBit(GPIOA,GPIO_Pin_1,0);GPIO_WriteBit(GPIOA,GPIO_Pin_2,1);Delay_ms(500);GPIO_WriteBit(GPIOA,GPIO_Pin_2,0);}
}
GPIO重映射
来填一下文章开头的坑,如果GPIOA的9号和10号引脚我都另外有用了,但是同时我还需要使用USART1,那么根据引脚定义表我们可以将GPIOA的9号引脚和10号引脚的USART1_TX和USART1_RX的功能重映射到GPIOB的6号和7号引脚。
我们只需要在原本的使用USART1的代码上做一些改变。
第一步是修改GPIO的初始化,将原本初始化GPIOA的9号和10号引脚的配置改为GPIOB的6号和7号引脚。
第二步是加上打开AFIO外设时钟的代码:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
AFIO是专门负责功能引脚重映射的。所以我们需要将其的外设时钟打开。
第三步就是开始重映射:
GPIO_PinRemapConfig(GPIO_Remap_USART1,ENABLE);
这个函数的第一个参数是选择重映射的方式,第二个参数是ENABLE或是DISABLE,要开启重映射就选择ENABLE,开启后要关闭就选择DISABLE。
选择重映射的方式,这个是需要查看参考手册的,在第116页。查询过后我们还需要跑到库函数文件里去查找对应的参数选项,还是挺麻烦的,所以这类有特殊功能的引脚我们尽量先分配给这个特殊功能。
参考
STM32F10xxx参考手册(中文)
b站江科大自化协
《ARM Cortex-M3 嵌入式原理及应用(基于STM32F103微控制器)》