目录
简介:
下面都是理论知识,可以稍微看一看,
GPIO的基本结构:
GPIO位结构:
电路图说明:
保护二极管:IO 引脚上下两边两个二极管用于防止引脚外部过高、过低的电压输入。当引脚电压高于 VDD 时,上方的二极管导通;当引脚电压低于VSS时,下方的二极管导通,防止不正常电压引入芯片导致芯片烧毁。但是尽管如此,还是不能直接外接大功率器件,须加大功率及隔离电路驱动,防止烧坏芯片或者外接器件无法正常工作。
P-MOS管和N-MOS管:由P-MOS管和N-MOS管组成的单元电路使得GPIO具有“推挽输出”和“开漏输出”的模式。这里的电路会在下面很详细地分析到。
TTL肖特基触发器:信号经过触发器后,模拟信号转化为0和1的数字信号。但是,当GPIO引脚作为ADC采集电压的输入通道时,用其“模拟输入”功能,此时信号不再经过触发器进行TTL电平转换。ADC外设要采集到的原始的模拟信号。
下面介绍一下我们需要的LED,蜂鸣器:
GPIO输出的八种模式
GPIO的模式:
STM32的GPIO工作方式
GPIO支持4种输入模式:
浮空输入(GPIO_Mode_IN_FLOATING)
上拉输入(GPIO_Mode_IPU)
下拉输入(GPIO_Mode_IPD)
模拟输入(GPIO_Mode_AIN)
GPIO支持4种输出模式:
开漏输出(GPIO_Mode_Out_OD)
开漏复用输出(GPIO_Mode_AF_OD)
推挽输出(GPIO_Mode_Out_PP)
推挽复用输出(GPIO_Mode_AF_PP)
同时,GPIO 还支持三种最大翻转速度(2MHz、10MHz、50MHz)。每个I/O口可以自由编程,但 I/O 口寄存器必须按32位字访问。
需要注意,在查看《STM32中文参考手册V10》中的GPIO的表格时,会看到有“FT”一列,这代表着这个GPIO口是兼容3.3V和5V的;如果没有标注“FT”,就代表着不兼容5V。
浮空输入模式
浮空输入模式下,I/O端口的电平信号直接进入输入数据寄存器。也就是说,I/O的电平状态是不确定的,完全由外部输入决定;如果在该引脚悬空(在无信号输入)的情况下,读取该端口的电平是不确定的。
上拉输入模式
上拉输入模式下,I/O 端口的电平信号直接进入输入数据寄存器。但是在 I/O 端口悬空(在无信号输入)的情况下,输入端的电平可以保持在高电平;并且在 I/O 端口输入为低电平的时候,输入端的电平也还是低电平。
下拉输入模式
下拉输入模式下,I/O 端口的电平信号直接进入输入数据寄存器。但是在 I/O 端口悬空(在无信号输入)的情况下,输入端的电平可以保持在低电平;并且在 I/O 端口输入为高电平的时候,输入端的电平也还是高电平。
模拟输入模式:
模拟输入可以说是ADC模数转换器的专属配置。
模拟输入模式下,I/O 端口的模拟信号(电压信号,而非电平信号)直接模拟输入到片上外设模块,比如 ADC 模块等等。
开漏输出模式:(PMOS无效,就是开漏输出,)
开漏输出模式下,通过设置位设置/清除寄存器或者输出数据寄存器的值,途经 N-MOS 管,最终输出到I/O端口。这里要注意 N-MOS 管,当设置输出的值为高电平的时候,N-MOS 管处于关闭状态,此时I/O端口的电平就不会由输出的高低电平决定,而是由 I/O 端口外部的上拉或者下拉决定;当设置输出的值为低电平的时候,N-MOS 管处于开启状态,此时 I/O 端口的电平就是低电平。同时,I/O 端口的电平也可以通过输入电路进行读取;注意,I/O 端口的电平不一定是输出的电平。
开漏复用输出模式 (P-MOS和N-MOS都有效)
开漏复用输出模式,与开漏输出模式很是类似。只是输出的高低电平的来源,不是让CPU直接写输出数据寄存器,取而代之利用片上外设模块的复用功能输出来决定的。
推挽输出模式
推挽输出模式下,通过设置位设置/清除寄存器或者输出数据寄存器的值,途经 P-MOS 管和 N-MOS 管,最终输出到 I/O 端口。这里要注意 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 直接写输出数据寄存器,取而代之利用片上外设模块的复用功能输出来决定的。
根据八种模式在keil5上配置好相应的代码
现在的很多单片机在GPIO配置的时候,除了配置输入输出类型、速度以外,还需要配置一下模式,即GPIO_Mode。以STM32为例,有输入浮空、输入上拉、输入下拉、模拟输入、开漏输出、推挽式输出、 推挽式复用功能、开漏复用功能。 下面我们举一种例子来讲一下如何配置,
也为我们点亮LED灯做准备。
第一步:选择时钟寄存器,
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//配置寄存器的时钟使能 //自己可以在keil5上跳转到他的函数定义,看看他要什么参数,我这里使用的是GPIOA,你也可以用 //其他的,第一个参数变成RCC_APB2Periph_GPIOB,就是使用B寄存器
上面配置好了就可以为寄存器输入时钟,当然,时钟也有很多个,我这里选择的是RCC这个,
第二步:
建立一个数组:为啥要建立数组呢?要配置输入输出类型、速度,还有上面一直介绍的八种模式。
存放不同类型的变量C语言是用数组存放的。
GPIO_InitTypeDef GPIO_InitStructure; //定义一个结构体结构体有下面三个参数
第三部:设定好我们要的输出类型吗,速度,模式。
下面就是八种模式存放在函数的,以枚举类型赋给了相应的值,这样我们就不需要直接给相应的寄存器复制,直接调用这个函数即可:对应的英文如下
浮空输入(GPIO_Mode_IN_FLOATING)
上拉输入(GPIO_Mode_IPU)
下拉输入(GPIO_Mode_IPD)
模拟输入(GPIO_Mode_AIN)
开漏输出(GPIO_Mode_Out_OD)
开漏复用输出(GPIO_Mode_AF_OD)
推挽输出(GPIO_Mode_Out_PP)
推挽复用输出(GPIO_Mode_AF_PP)
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; //选择什么模式,上述八种模式的一种 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0; //我用A0这个I/O口 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //速度选择50Mhz GPIO_Init(GPIOA,&GPIO_InitStructure);//初始化
OK,这样的话,我们就已经选择了GPIO的模式,速度,和哪一个引脚。已经拥有了I/O口的使用权。上面这些需要的参数都是可以跳转到函数定义去看看它需要啥的,直接复制粘贴即可
最后就是操作寄存器给0或1就可以输出和读入了,当然,我们还是直接操作相应的函数给他1和0.
GPIO的相关寄存器
下面GPIO配置寄存器,每一个端口的模式由四位进行配置。16个端口需要64位,(基本很少直接配置寄存器,库函数已经配置好了,我们直接了解库函数的使用,直接调用即可)
所以配置寄存器有两个,一个是端口配置低寄存器,一个是端口配置高寄存器。
端口数据寄存器:
低16位对应16个引脚,高16位没有使用。
端口输出寄存器:
低16位是进行设置的,高16位是进行清除的。
与上面寄存器高16位是一样的功能。为了方便操作设置的,如果你想单一的进行位设置或者清除。多个端口同时进行设置和位清除,使用8.2.5寄存器就OK了,这样可以保证位设置和位清除的同步性,
可以对端口的配置进行锁定,防止意外更改,
下面是八种模式,函数已经把我们定义好了。我们依次讲一下。
1.GPIO的输出
1.1LED闪烁
如图所示连接面包板。我使用的是A0这个GPIO,可能图看不太清楚。
代码如下:(基于库函数的工程已经建好的情况下)
这里主要使用main.c函数
main.c
当然,置1还是置0的函数也有很多个
下面四个,具体参数可以转到相应的函数定义去看看它具体要啥。
#include "stm32f10x.h" //32的头文件 #include "Delay.h" //延时的文件 int main() { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//配置寄存器的时钟使能 //自己可以在keil5上跳转到他的函数定义,看看他要什么参数,我这里使用的是GPIOA,你也可以用 //其他的,第一个参数变成RCC_APB2Periph_GPIOB,就是使用B寄存器 GPIO_InitTypeDef GPIO_InitStructure; //定义一个结构体结构体有下面三个参数 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//选择什么模式,上述八种模式的一种 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStructure); // GPIO_SetBits(GPIOA,GPIO_Pin_0);//选择A0这个I/O口,置1操作 // GPIO_ResetBits(GPIOA,GPIO_Pin_0);//选择A0这个I/O口,置0操作 while(1) { GPIO_ResetBits(GPIOA,GPIO_Pin_0);//选择A0这个I/O口,置0操作 Delay_ms(100); GPIO_SetBits(GPIOA,GPIO_Pin_0);//选择A0这个I/O口,置1操作 Delay_ms(100); } }
Delay函数可以自己找一个,江科大就有,在工程里面建一个文件夹,放在工程里面。最后拿进来就OK了,当然还要很多注意操作事项,我就不展示了。
怎么点亮看自己连接的引脚图。0点亮还是1点亮,具体由自己决定。
我上面连接的电路图是二极管长接正,短接A0口,所以,只有A0口接0就会亮。所以我是0点亮。当然看你的模式是啥,推挽输出的话:二极管长接A0口,短接负极,好像也是可以点亮的,跟它的工作方式有关,自己可以试试。但是开漏输出不行,说明开漏输出没有高电平驱动能力的
1.2LED流水灯
有了上面LED闪烁的例子,其实LED流水灯也很容易。
只不过我们多要操作7个I/O口:初始化部分麻烦了点。
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;对引脚的初始化可以这样,为什么呢?
我们右击下图所示,转到定义。
通过这幅图可以看到,pin0对应的数据是0x01,pin1为0x02........
转为16进制是不是 0000 0000 0000 0001(pin0)
0000 0000 0000 0010 (pin1)........
按位或就是 0000 0000 0000 0011 这样就把两个端口全部选上了。
当然最下面还有个GPIO_pin_All,就是把所有引脚全部选上了。
其实时钟控制的那一项,也是可以通过按位或的操作来进行多个选择的,同理,右键
GPIO_SetBits的参数也是可以设置多个引脚的。
OK,有了上述知识,我就点亮四个灯,来实现流水灯,插入哪一个引脚看自己的选择,我选择
A0,1,2,3.这四个引脚。代码如下:
#include "stm32f10x.h" //32的头文件 #include "Delay.h" //延时的文件 int main() { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//配置寄存器的时钟使能 //自己可以在keil5上跳转到他的函数定义,看看他要什么参数,我这里使用的是GPIOA,你也可以用 //其他的,第一个参数变成RCC_APB2Periph_GPIOB,就是使用B寄存器 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_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStructure); // GPIO_SetBits(GPIOA,GPIO_Pin_0);//选择A0这个I/O口,置1操作 // GPIO_ResetBits(GPIOA,GPIO_Pin_0);//选择A0这个I/O口,置0操作 while(1) { GPIO_ResetBits(GPIOA,GPIO_Pin_0);//选择A0这个I/O口,置0操作 Delay_ms(500); GPIO_SetBits(GPIOA,GPIO_Pin_0);//选择A0这个I/O口,置1操作 Delay_ms(500); GPIO_ResetBits(GPIOA,GPIO_Pin_1); Delay_ms(500); GPIO_SetBits(GPIOA,GPIO_Pin_1);//选择A0这个I/O口,置1操作 Delay_ms(500); GPIO_ResetBits(GPIOA,GPIO_Pin_2);//选择A0这个I/O口,置0操作 Delay_ms(500); GPIO_SetBits(GPIOA,GPIO_Pin_2);//选择A0这个I/O口,置1操作 Delay_ms(500); GPIO_ResetBits(GPIOA,GPIO_Pin_3);//选择A0这个I/O口,置0操作 Delay_ms(500); GPIO_SetBits(GPIOA,GPIO_Pin_3);//选择A0这个I/O口,置1操作 Delay_ms(500); } }
当然,也可以使用 GPIO_Write函数。
GPIO_Write(GPIOA,~0x0001);//0000 0000 0000 0001 Delay_ms(100); GPIO_Write(GPIOA,~0x0002);//0000 0000 0000 0010 Delay_ms(100); GPIO_Write(GPIOA,~0x0004);//0000 0000 0000 0100 Delay_ms(100); GPIO_Write(GPIOA,~0x0008);//0000 0000 0000 1000 Delay_ms(100);
1.3:蜂鸣器
这一部分留给自己操作吧,应该也是很简单的,跟上面的代码差不多。
.