GPIO——General purpose input output
声明:出现的文字图片参照b站江科大自化协课程学习,但代码为跟着手打,发博客是作为学习笔记也是仅供自我学习纪录,便于自己复习查阅,非商用也禁商用侵权联系必删!!!
一、GPIO简介
-
GPIO(General Purpose Input Output)通用输入输出口 可配置为8种输入输出模式
-
引脚电平:0V~3.3V,部分引脚可容忍5V
-
输出模式下可控制端口输出高低电平,用以驱动LED、控制蜂鸣器、模拟通信协议输出时序等
-
输入模式下可读取端口的高低电平或电压,用于读取按键输入、外接模块电平信号输入、ADC电压采集、模拟通信协议接收数据等
二、GPIO基本结构
stm32的寄存器为32位,只有低16位用到了PA或PB端口,高16位并未用到,寄存器只用于存储数据,驱动器用以增大驱动能力。
三、GPIO位结构
- 两个保护二极管分别保证电压过高或过低时引走电流,避免对内部造成损害(正常0-3.3v);
- 两个上拉和下拉输入电阻开关,两个都不接通为浮空状态,上拉电阻开关接通为上拉输入,可称作默认为高电平的输入模式,下拉同理,两个电阻都较大,属于弱上拉和弱下拉;
- TTL施密特触发器(图片可能翻译有误),可设定上限和下限两个比较阈值,在中间留有波动范围,可有效避免因信号波动造成的输出抖动现象;
- 经过施密特触发器,即可输入数据寄存器;
- 输出数据寄存器同时控制16个端口,且只能整体读写,若想对某一位操作,有三种方式。位设置/清除寄存器可对输出数据寄存器的某一端口(位)进行操作;也可以利用C语言或与指令方式;第三种就是读写STM32的“位带”区域,位带的作用与51单片机的位寻址作用差不多,是专门分配的一段地址区域,这段区域映射了RAM和外设寄存器所有的位,读写这段地址中的数据,就相当于读写映射位置的某一位;
- P-MOS和N-MOS管是一种电子开关,信号控制开关的导通和关闭,开关负责将IO口接到VDD或VSS,有推挽、开漏或关闭三种输出方式;
- 推挽输出模式下,P-MOS和N-MOS均有效,数据寄存器为1时,上管导通,下管断开,输出直接接到VDD,就是输出高电平;为0时则相反,接到VSS,就是输出低电平,这种模式下高低电平均有较强的驱动能力,所以该模式也叫强推输出模式,在此模式下STM32对IO口具有绝对控制权,高低电平都由STM32说的算。
- 开漏输出模式下,P-MOS无效,只有N-MOS工作,数据寄存器为1时,下管断开,这时输出相当于断开也就是高阻模式;为0时,下管导通,输出直接接到VSS,也就是输出低电平,这种模式只有低电平具有驱动能力,高电平没有驱动能力。该模式可作为通信协议的驱动方式,比如IIC总线的引脚就是使用的该模式,在多机通讯的情况下,可避免各个设备的相互干扰;还可用于输出5V电平信号,如在IO口上接一个上拉电阻到5V的电阻,当输出低电平时,由内部的N-MOS直接接VSS,当输出高电平时,由外部的上拉电阻拉高至5V。
-
关闭状态,当引脚配置为输入模式时,两个MOOS管都无效,也即输出关闭,端口的电平由外部信号控制。
四、GPIO工作模式(8种)
通过配置GPIO的端口配置寄存器,端口可以配置成以下8种模式:
注:输入模式下输出都是关闭的,但输出模式下输入是可以打开的,在这8种模式种,除了模拟输入这个模式会关闭数字的输入功能,其他7种所有的输入都是有效的。
1、浮空/上拉/下拉输入
2、模拟输入
3、开漏/推挽输出
4、复用开漏/推挽输出
以上就是对于GPIO的全部介绍,详细可见芯片手册第八章!!
五、实际操作
这里利用LED和蜂鸣器来进行演示,以下是硬件电路,根据不同输出模式特点可选择合适驱动方式:
1、LED闪烁
//LED闪烁代码演示
#include "stm32f10x.h" // Device header
#include "Delay.h" //延时函数头文件
//此项目涉及RCC和GPIO两个外设,前期可在Library组下打开相关文件
//了解对应外设常使用的外设库函数以及其参数定义
//结构体参数需要在函数上面先定义结构体名,如GPIO——Init的第二个参数(结构体地址)
//然后需要对结构体的每一项进行配置,右键跳转到定义,若遇到多个定义选择带member的,若是转到注释处可ctrl+f寻找下一项
int main(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;//选择推挽输出模式即Out_PP
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//GPIO_ResetBits(GPIOA,GPIO_Pin_0);//LED点亮(低电平驱动接法)
//GPIO_SetBits(GPIOA,GPIO_Pin_0);//LED熄灭
//GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);//点亮
//GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);//熄灭
while(1){
//实现LED闪烁效果,也可用其他函数实现
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);
Delay_ms(500);
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);
Delay_ms(500);
}
}
2、LED流水灯
//LED流水灯代码演示
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;//选择推挽输出模式即Out_PP
//GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 |GPIO_Pin_2 ;//利用|一次性初始化多个端口
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;//直接用All初始化所有端口
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
while(1){
//实现LED流水灯,可用循环和移位优化代码,8个等按顺序点亮
//甚至可以定义一个数组依次给第二个参数位置实现花式点灯
GPIO_Write(GPIOA,~0x0001);//低电平点亮,加取反~符号,第一个灯点亮
Delay_ms(500);
GPIO_Write(GPIOA,~0x0002);
Delay_ms(500);
GPIO_Write(GPIOA,~0x0004);
Delay_ms(500);
GPIO_Write(GPIOA,~0x0008);
Delay_ms(500);
GPIO_Write(GPIOA,~0x0010);
Delay_ms(500);
GPIO_Write(GPIOA,~0x0020);
Delay_ms(500);
GPIO_Write(GPIOA,~0x0040);
Delay_ms(500);
GPIO_Write(GPIOA,~0x0080);
Delay_ms(500);
}
}
3、蜂鸣器
原则上选择哪个端口都行,此处为PB12,但是PA15、PB3、PB4三个口默认为JTAG的调试端口,如果要作为普通端口还需要一些配置,一般不作为首选,端口输出低电平蜂鸣器响,高电平不响。
#include "stm32f10x.h" // Device header
#include "Delay.h"
//库函数的使用除了转到定义查看,也可参考ST官网库函数文档
int main(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;//选择推挽输出模式即Out_PP
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;//PB12所以配12
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);//PB12所以配GPIOB
while(1){
GPIO_ResetBits(GPIOB,GPIO_Pin_12);//响
Delay_ms(500);
GPIO_SetBits(GPIOB,GPIO_Pin_12);//不响
Delay_ms(500);
}
}
六、GPIO输入实操
1、拓展知识
(1)按键:常见的输入设备,按下导通,松手断开
按键抖动:由于按键内部使用的是机械式弹簧片来进行通断的,所以在按下和松手的瞬间会伴随有一连串的抖动,最简单的方法是加一段延时
(2)传感器模块
:传感器元件(光敏电阻/热敏电阻/红外接收管等)的电阻会随外界模拟量的变化而变化,通过与定值电阻分压即可得到模拟电压输出,再通过电压比较器进行二值化即可得到数字电压输出。电路中一般有电容的部分为滤波稳定等辅助作用,分析时可以抓准主体。
(3)蜂鸣器与传感器硬件电路
上两种接法按下按键为低电平,松手为高电平,其中上左需要上拉输入模式,上右需要配置为上拉或浮空输入模式;下两种与之相反。一般用上面两种接法。
传感器接法
如下:
2、GPIO输入实操
(1)按键控制LED
模块化编程:将硬件驱动代码放在Hardware文件夹中(同样需要在工程目录下添加同名组,在组里添加.c、.h文件时路径要与Hardware文件夹路径一致);最后还需要在魔术棒C/C++菜单下添加新路径Harware!!
Hardware里有LED.c、LED.h、key.h、key.c四个文件!!
按键模块配置的时GPIO的输入,LED模块配置的时GPIO的输出!!
//Hardware文件下LED.c驱动模块
#include "stm32f10x.h" // Device header
void LED_Init(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;//选择推挽输出模式即Out_PP
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1| GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);//配置好默认就是输出低电平
GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2);//置为高电平,LED熄灭
}
//这里用了四个函数,属于最基础的,
//可以优化比如设置两个函数参数一个用于选择哪个灯,一个用于选择熄灭还是打开
void LED1_ON(void){
GPIO_ResetBits(GPIOA,GPIO_Pin_1);
}
void LED1_OFF(void){
GPIO_SetBits(GPIOA,GPIO_Pin_1);
}
void LED1_Turn(void){//取反函数,辅助实现按下点亮再按下熄灭
if (GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_1) == 0){//该函数读取PA1端口状态
GPIO_SetBits(GPIOA,GPIO_Pin_1);//如果状态为0,则置1
}
else{
GPIO_ResetBits(GPIOA,GPIO_Pin_1);//如果状态为1,则置0,达到电平反转功能
}
}
void LED2_ON(void){
GPIO_ResetBits(GPIOA,GPIO_Pin_2);
}
void LED2_OFF(void){
GPIO_SetBits(GPIOA,GPIO_Pin_2);
}
void LED2_Turn(void){//取反函数,实现按下点亮再按下熄灭
if (GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_2) == 0){//该函数读取PA2端口状态
GPIO_SetBits(GPIOA,GPIO_Pin_2);//如果状态为0,则置1
}
else{
GPIO_ResetBits(GPIOA,GPIO_Pin_2);//如果状态为1,则置0,达到电平反转功能
}
}
//LED.h头文件
#ifndef __LED_H
#define __LED_H
void LED_Init(void);
void LED1_ON(void);
void LED1_OFF(void);
void LED1_Turn(void);
void LED2_ON(void);
void LED2_OFF(void);
void LED2_Turn(void);
#endif
//key.c按键驱动模块
#include "stm32f10x.h" // Device header
#include "Delay.h"
void Key_Init(){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;//选择上拉输入模式即IPU
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1| GPIO_Pin_11;//选择对应端口
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
uint8_t Key_GetNum(void){//uint8_t为unsigned char的别名
uint8_t KeyNum = 0;//不按默认端口1与11返回0
//下面就要用到读取GPIO端口的功能了
if (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0){//若按下端口PB1返回1
Delay_ms(20);//按下按键抖动时间
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0);//如果按键一直按下则一直卡在这里
Delay_ms(20);//松开按键抖动时间
KeyNum=1;
}
if (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11)==0){//若按下端口PB11返回2
Delay_ms(20);//按下按键抖动时间
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11)==0);//如果按键一直按下则一直卡在这里
Delay_ms(20);//松开按键抖动时间
KeyNum=2;
}
return KeyNum;
}
//key.h头文件
#ifndef __KEY_H
#define __KEY_H
void Key_Init();
uint8_t Key_GetNum(void);
#endif
//main.c主函数,这里放在了User文件夹(组)下
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "LED.h"
#include "key.h"
uint8_t KeyNum;//定义一个全局变量用于存键码的返回值,与key.c里的KeyNum不是一个变量
int main(void){
LED_Init();
Key_Init();
while(1){
KeyNum = Key_GetNum();
if (KeyNum == 1){
LED1_Turn();
}
if (KeyNum == 2){
LED2_Turn();
}
}
}
//最终效果:按下按键1,LED1点亮,再次按下熄灭;按下按键2,LED2点亮,再次按下熄灭.
(2)光敏传感器控制蜂鸣器
Hardware里有Buzzer.c、Buzzer.h、LightSensor.h、LightSensor.c四个文件!!
//Buzzer.c蜂鸣器驱动模块
//与LED.c相似,直接根据硬件电路在其lED代码上进行微改
#include "stm32f10x.h" // Device header
void Buzzer_Init(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;//选择推挽输出模式即Out_PP
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_1|GPIO_Pin_12);
}
void Buzzer_ON(void){
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
}
void Buzzer_OFF(void){
GPIO_SetBits(GPIOB,GPIO_Pin_12);
}
void Buzzer_Turn(void){
if (GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_12) == 0){
GPIO_SetBits(GPIOB,GPIO_Pin_12);
}
else{
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
}
}
//Buzzer.h头文件
#ifndef __BUZZER_H
#define __BUZZER_H
void Buzzer_Init(void);
void Buzzer_ON(void);
void Buzzer_OFF(void);
void Buzzer_Turn(void);
#endif
//LightSensor.c光敏电阻输入配置模块
//初始化与按键模块的代码相似,因为其作用相似(都为GPIO输入),可直接在key.c基础上修改
#include "stm32f10x.h" // Device header
#include "Delay.h"
void LightSensor_Init(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;//选择上拉输入模式即IPU
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;//选择对应端口
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
uint8_t LightSensor_Get(void){
return GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13);//此处相对于按键更简单
}
//LightSensor.h头文件
#ifndef __LIGHT_SENSOR_H
#define __LIGHT_SENSOR_H
void LightSensor_Init(void);
uint8_t LightSensor_Get(void);
#endif
//main.c主函数
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "Buzzer.h"
#include "LightSensor.h"
int main(void){
Buzzer_Init();
LightSensor_Init();
while(1){
if (LightSensor_Get() == 1){//遮住光敏电阻
Buzzer_ON();
}
else{
Buzzer_OFF();
}
}
}
//最终效果,遮住光敏电阻,蜂鸣器响;放开不响
以上就是对于GPIO输出/输入的学习纪录,欢迎一起交流!!