一、GPIO是什么
GPIO(General Purpose Input Output) 通用输入输出口,也就是俗称的IO口,他的引脚电平是0V~3.3V,部分引脚可容忍5V(这里的5V意思是可以接收5V的电压,但是输出最多是3.3V),想要知道哪些引脚是可以容忍5V的时候,可以参考STM32手册的引脚定义,里面带FT的就是可以容忍5V的。
输出模式下GPIO可以控制输出高或低的电平,所有对于高低电平能够实现的功能都可以通过这个来控制,如果控制的是功率比较大的设备,只需要再加入驱动电路即可,输出模式还可以用于输出模拟通讯协议。
输入模式下可以读取端口的高低电平或电压,用于读取按键输入等各种模块或协议的输入
二、GPIO的基本结构
在STM32中,所有的GPIO都是挂载在APB2外设总线上的,其中GPIO外设的名称是按照GPIOA,GPIOB等等这样来命名的,每个GPIO外设总共有16的引脚,编号是从0到15,GPIOA的第0号引脚,我们一般就用PA0来命名。在每个GPIO模块内,主要包含了寄存器和驱动器这些东西,寄存器就是一段特殊的存储器,内核可以通过APB2总线对寄存器进行读写,从而完成输出电平和读取电平的功能。其中输出寄存器写1,对应的引脚就会输出高电平,写0就输出低电平,输入寄存器读取为1,就证明对应的端口目前是高电平,读取为0就是低电平。STM32里的寄存器都是32位的,但是这里的端口只有16位,所以这里的寄存器只有低16位有对应端口。驱动器的作用是增加信号的驱动能力,寄存器只负责存储数据,如果要对外面的设备进行一些操作还是需要驱动器来负责增大驱动能力。
三、GPIO具体电路结构
如上图,左边的部分是寄存器,中间的部分是驱动器,右边是对应的具体IO引脚。上面一的一半是输入部分,下面是输出部分。
这个部分是输入部分IO引脚处的保护二极管,此处二级管的作用是对输入电压进行限幅,这里可以看到在二极管的上端接着Vdd,也就是3.3V的电压,这个时候如果输入电压比3.3V还要高,上方的二极管就会导通。
这是下方输出部分的二极管,此时Vss是0V,这里的作用是防止IO口电压为负时使二极管导通,就不会从内部电路汲取电流,保护内部电路。
这是输入部分的与IO口连接的地方,此处连接了一个上拉电阻和一个下拉电阻,上拉电阻至Vdd,下拉电阻至Vss,这个开关是可以通过程序进行配置的。上面导通,下面断开,就是上拉输入模式;下面导通,上面断开,就是下拉输入模式;两个都断开,就是浮空输入模式。这里的上拉与下拉表示的是在引脚悬空时引脚对应的电平,上拉表示悬空时引脚为高电平,下拉表示悬空时引脚为低电平。
沿着导线接下去将会遇到这个施密特触发器(图中为肖特基触发器),这个触发器的作用是对输入电压进行整形。如果输入电压大于阈值上限,输出就会瞬间成为高电平,如果低于阈值下限,输出就会瞬间降为低电平,这是处理数字信号在实际应用中可能产生的各种失真的情况(这里要注意的是上限与下限是不相等的,这样就不会出现信号在高低电平频繁波动的情况)
接下去经过触发器整形后的波形将会直接写入输入数据寄存器,我们再通过程序读取输入寄存器对应某一位的数据,得到端口的输入电平。
输入片上外设的部分分为模拟输入和复用功能输入(是数字输入),模拟输入一般给ADC等所以要提供模拟量,导线接在施密特触发器前面;复用功能输入一般连接到其他需要读取端口的外设上,接收的是数字信号,导线接在施密特触发器后面。
输出部分可以由输出数据寄存器或片上外设控制,如果选择通过输出数据寄存器控制,写数据寄存器的某一位就可以操作对应的端口了,寄存器只能够整体读写,所以要想实现只改变一位数据而不影响其他数据,便有了下图,位设置/清除寄存器用来实现刚刚的操作。(实际上有两种方式能够实现单独改变一个数据,一个是通过按位与或的方式,一个是通过该寄存器,但是第一种方法速度会比第二种慢很多)
上面是P-MOS,下面是N-MOS,MOS管是一种电子开关,由信号来控制开关的导通和关闭,开关负责将IO口接到VDD或VSS,这里也可以通过开关选择推挽,开漏或关闭三种输出方式。
选择推挽输出模式时,P-MOS和N-MOS均有效,数据寄存器为1就上管导通,下管断开,输出直接接到VDD,输出高电平;数据寄存器为0就上管断开,下管导通,输出直接接到VSS,输出低电平。这种模式下高低电平均有较强的驱动能力,所以推挽输出模式也可以叫强推输出模式,这种模式下STM32对IO口有绝对的控制权。
选择开漏输出模式时,P-MOS是无效的,只有N-MOS在工作,数据寄存器为1就把下管断开,这时输出就相当于断开了,成为高阻模式;数据寄存器为0就把下管导通,输出直接接到VSS,输出低电平。这个模式只有低电平有驱动能力,高电平是没有驱动能力的,一般用于作为通信协议的驱动方式,在多机通信的情况下这个模式可以避免各个设备相互干扰。开漏模式也可以用于输出5V的电平信号,只需在IO口外接一个上拉电阻到5V的电源,当输出低电平时,由内部的N-MOS直接接VSS,当输出高电平时,由外部的上拉电阻达到5V。这样就可以输出5V电平信号,用于兼容一些5V的电平设备。
选择关闭输出模式时,两个MOS管都无效,输出关闭,端口电平由外部信号控制。
通过上面的各种不同的模块,我们就可以把端口配置成8种不同的模式,只有在模拟输入时,单片机会关闭数字的输入功能。
四、GPIO输出实例
1、点灯
1. #include "stm32f10x.h" // Device header 2. #include "Delay.h" 3. int main(void) 4. { 5. //RCC初始化操作 6. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); 7. 8. //GPIO具体引脚初始化操作 9. GPIO_InitTypeDef GPIO_InitStructure; 10. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出 11. //GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;//开漏输出 12. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; 13. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 14. GPIO_Init(GPIOA,&GPIO_InitStructure); 15. 16. //GPIO_SetBits(GPIOA,GPIO_Pin_0);//拉高引脚输出电平 17. //GPIO_ResetBits(GPIOA,GPIO_Pin_0);//拉低引脚输出电平 18. //GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);//拉低引脚输出电平 19. //GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);//拉高引脚输出电平 20. 21. int temp = 0; 22. // temp = 0; 23. // GPIO_WriteBit(GPIOA,GPIO_Pin_0,(BitAction)temp);//用0 1代表高低电平时,前面加上BitAction强转,不然会warning 24. // Delay_ms(100); 25. // temp = 1; 26. // GPIO_WriteBit(GPIOA,GPIO_Pin_0,(BitAction)temp); 27. // Delay_ms(100); 28. 29. while(1) 30. { 31. // temp = 0; 32. // GPIO_WriteBit(GPIOA,GPIO_Pin_0,temp); 33. // Delay_ms(100); 34. // temp = 1; 35. // GPIO_WriteBit(GPIOA,GPIO_Pin_0,(BitAction)temp); 36. // Delay_ms(100); 37. GPIO_Write(GPIOA,~0x0001); 38. Delay_ms(100); 39. // GPIO_Write(GPIOA,~0x0000); 40. // Delay_ms(100); 41. } 42. }
2、流水灯
1. #include "stm32f10x.h" // Device header 2. #include "Delay.h" 3. int main(void) 4. { 5. //RCC初始化操作 6. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//如果是选定的几种引脚,则 RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB 7. 8. //GPIO具体引脚初始化操作 9. GPIO_InitTypeDef GPIO_InitStructure; 10. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出 11. //GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;//开漏输出 12. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;//如果是选定的几个引脚,则 GPIO_Pin_0 | GPIO_Pin_1 13. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 14. GPIO_Init(GPIOA,&GPIO_InitStructure); 15. 16. //GPIO_SetBits(GPIOA,GPIO_Pin_0);//拉高引脚输出电平 //如果是选定的几个引脚,则 GPIO_Pin_0 | GPIO_Pin_1 17. //GPIO_ResetBits(GPIOA,GPIO_Pin_0);//拉低引脚输出电平 18. //GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);//拉低引脚输出电平 19. //GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);//拉高引脚输出电平 20. 21. while(1) 22. { 23. GPIO_Write(GPIOA,~0x0001); 24. Delay_ms(100); 25. GPIO_Write(GPIOA,~0x0002); 26. Delay_ms(100); 27. GPIO_Write(GPIOA,~0x0004); 28. Delay_ms(100); 29. GPIO_Write(GPIOA,~0x0008); 30. Delay_ms(100); 31. GPIO_Write(GPIOA,~0x0010); 32. Delay_ms(100); 33. GPIO_Write(GPIOA,~0x0020); 34. Delay_ms(100); 35. GPIO_Write(GPIOA,~0x0040); 36. Delay_ms(100); 37. GPIO_Write(GPIOA,~0x0080); 38. Delay_ms(100); 39. 40. } 41. }
3、读取输入电平
if (GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_12) == 0)