GPIO (General Purpose Input Output)通用输入输出口
可配置8种输入输出模式(8种工作模式)
部分引脚FT(five tolerate)可以输入5v的电压,但对于输出来讲最高只能是3.3v,引脚电平0---3.3v
每个GPIO外设,共有16个引脚,编号是从0-15
每个GPIO的模块内,主要包含了寄存器和驱动器
- 寄存器:特殊存储器,只负责存储数据,内核可通过APB2总线对寄存器进行读写rw(输出、读取电平的功能)
- STM32是32位的单片机,所以其内部寄存器都是32位的。由于端口(引脚)只有16位,所以只用到了低16位,高16位没有用到
- 驱动器:增强信号的驱动能力
上、下拉电阻
- 为了避免引脚悬空(漂浮在外太空,极易受到干扰)导致的输入数据不确定——上拉、下拉电阻(阻值较大、弱上拉、弱下拉)——尽量不影响正常的输入操作
- 上拉输入——默认为高电平的输入模式
- 下拉输入——默认位低电平的输入模式
施密特触发器——对输入电压进行整形
- 执行逻辑:若输入电压大于某一阈值,输出瞬间升为高电平;若输入电压小于某一阈值,输出瞬间降为低电平。只有高于上限或低于下限,输出才会变化。在上限阈值与下限阈值的波动范围之间,维持不变。
- advantage:有效避免因信号波动造成的输出抖动现象
输出数据寄存器:同时控制16个端口,且其只能整体rw,若想单独控制其中一个端口而不影响其他端口——特殊方式
- 特殊方式1——读出此寄存器,利用按位与&=、按位或|=,最后再将更改后的数据写入
- fault:麻烦、效率差
- 特殊方式2——通过设置位设置和位清除寄存器
- 特殊方式3——rwSTM32的“位带”区域(与51单片机的位寻址相似)(暂时用不到)
库函数使用的是——rw位设置和位清除寄存器的方法
mos管——电子开关 三种输出方式:推挽、开漏、关闭
1.推挽输出方式——高低电平均有较强的驱动能力——强推输出模式
STM32对IO口具有绝对的控制权
2.开漏模式——数据寄存器为1,高阻模式;为0,NMOS接入。即只有低电平具有驱动能力
- 主要用途:作为通信协议的驱动方式,例如I2C通信的引脚
- 输出5v的电平信号,用于兼容一些5v电平的设备
3.关闭状态——当引脚配置为输入模式,PMOS、NMOS均无效,即输出关闭,端口电平信号由外部信号控制
8种工作方式:
输入模式下:输出无效
原因:一个端口只能有一个输出,但可以有多个输入,所以当配置成输出模式下,内部输入也是无影响的
GPIO配置寄存器
每一个端口的模式由4位进行配置,16个端口需要64位,所以这里的配置寄存器需要2个——端口配置低寄存器、端口配置高寄存器
对多个端口同时进行位设置和位清除,保证位设置和位清除的同步性
LED两种驱动方式如何选择?
取决于IO口高低电平的驱动能力
之前介绍到:推挽输出方式——高低电平均有较强的驱动能力——强推输出模式
我们倾向于使用低电平驱动——很多单片机、芯片都使用了高电平弱驱动、低电平强驱动的规则。
蜂鸣器:
三极管驱动
操作STM32的GPIO的三个步骤:
1、使用RCC开启使能GPIO的时钟
2、使用GPIO_Init函数初始化GPIO
3、使用输出或输入函数控制GPIO口
GPIO的八种工作模式:
AIN Analog IN 模拟输入
IN FlOATING 浮空输入
IPD in pull down 下拉输入
IPU in pull up 上拉输入
Out_OD Out Open Drain 开漏输出
Out_PP out push pull 推挽输出
AF_OD Atl Open Drain 复用开漏
AF_PP
//把指定端口设置成高电平
void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);
void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);
啊啊啊,因为红光太强,所以我一直以为我的灯没有亮,别因为别人的光芒太耀眼,而看不到自己的光芒。
在PA0口不太行,我在PA2口实验成功,不知道是不是因为拓展坞的原因,明明是采用推挽输出,但是导致灯特别暗。
#include "stm32f10x.h" // Device header
int main(void)
{
RCC_AHBPeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//RCC里面的外设时钟控制函数,开启时钟
//---------------GPIO初始化--------------------------
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //点灯使用的是推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 ; //0号引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//-----------------GPIO初始化完成---------------------------------
//GPIO_ResetBits(GPIOA, GPIO_Pin_2);
//GPIO_SetBits(GPIOA, GPIO_Pin_2);
GPIO_WriteBit(GPIOA, GPIO_Pin_2, Bit_SET);
while(1)
{
}
}
哟吼,PA0口也行了。实际没行。
什么鬼啊,为什么我的程序不行,我拿up主的程序灯就会亮,运行up主的程序前,要先修改
我把up主的程序复制也可以,就是我自己程序的问题,不行了,我要去吃饭了
下图对高低电平进行操作
GPIO_WriteBit(GPIOA, GPIO_Pin_0, 0);
Delay_ms(500);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, 1);
Delay_ms(500);
产生两个错误:枚举类型中混入其他类型的变量
solution:强制类型转换
GPIO_WriteBit(GPIOA, GPIO_Pin_0,(BitAction)0);
我的代码:
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//RCC里面的外设时钟控制函数,开启时钟
//---------------GPIO初始化--------------------------
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //点灯使用的是推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 ; //0号引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//-----------------GPIO初始化完成---------------------------------
//GPIO_ResetBits(GPIOA, GPIO_Pin_2);
//GPIO_SetBits(GPIOA, GPIO_Pin_2);
while(1)
{ GPIO_ResetBits(GPIOA, GPIO_Pin_0);
Delay_ms(500);
GPIO_SetBits(GPIOA, GPIO_Pin_0);
Delay_ms(500);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET);
Delay_ms(500);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET);
Delay_ms(500);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)0);
Delay_ms(500);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)1);
Delay_ms(500);
}
}
up代码:
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
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);
while (1)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_0);
Delay_ms(500);
GPIO_SetBits(GPIOA, GPIO_Pin_0);
Delay_ms(500);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET);
Delay_ms(500);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET);
Delay_ms(500);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)0);
Delay_ms(500);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)1);
Delay_ms(500);
}
}
啊啊啊,整个人又傻了
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; // Out Open Drain 开漏输出
开漏模式——数据寄存器为1,高阻模式;为0,NMOS接入。即只有低电平具有驱动能力
我们把LED的负极接在面包板的负极上,把LED正极接在PA0口,(对应的是LED的高电平驱动)可以看到在此情形下,LED是没有闪烁的。
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //点灯使用的是推挽输出
而在推挽输出的情况下,无论LED是高电平驱动还是低电平驱动都是闪烁的。
而我又犯了错误,在LED是低电平驱动的条件下,使用开漏输出模式,结果是编译、下载,LED仍闪烁,我还又责怪不知道哪里出了问题,但是我也验证了在开漏模式下,只有低电平驱动具有驱动能力