GPIO
一、GPIO理论
由于 STM32 的外设很多,为了降低功耗,每个外设都挂在到一个对应的时钟,在芯片刚上电的时候这些 时钟都是被关闭的,如果想要外设工作,必须把相应的时钟打开。
寄存器四个位控制一个IO口。
1. GPIO(general purpose intput output)
1.GPIO:输入输出端口。简单来说就是软件可控制的引脚,STM32芯片的GPIO引脚与外部设备连接起来,从而实现与外部通讯、控制以及数据采集的功能
2.STM32F103ZET6 型号的芯片有 GPIOA、GPIOB、GPIOC 至 GPIOG 共 7 组 GPIO,芯片一共 144 个引脚,其中 GPIO 就占了一大部分,所有的 GPIO引脚都有基本的输入输出功能。
3.GPIO 引脚线路经过两个保护二极管后,向上流向“输入模式”结构,向下流向“输出模式”结构。
2. 保护二极管及上、下拉电阻
引脚的两个保护二级管可以防止引脚外部过高或过低的电压输入.
3. P-MOS 管和 N-MOS 管
(1)推挽输出
(2)开漏输出
1.一个由 P-MOS 和 N-MOS 管组成的单元电路。
2.这个结构使 GPIO 具有了“推挽输出”和“开漏输出”两种模式。
3.当引脚高低电平切换时,两个管子轮流导通,P 管负责灌电流,N 管负责拉电流,使其负载能力和开关速度都比普通的方式有很大的提高。
4.推挽输出的低电平为 0 伏,高电平为 3.3 伏。
5.开漏输出模式时,上方的 P-MOS 管完全不工作。具有“线与”特性。
6.高阻态:
若控制输出为 1 (它无法直接输出高电平) 时,则 P-MOS管和 N-MOS 管都关闭,所以引脚既不输出高电 平,也不输出低电平。
7.为正常使用时必须外部接上拉电阻。
8.推挽输出模式:
一般应用在输出电平为 0 和 3.3 伏而且需要高速切换开关状态的场合。在 STM32的应用中,除了必须用开 漏模式的场合,我们都习惯使用推挽输出模式。
9.开漏输出:
1)一般应用在 I2C、SMBUS 通讯等需要“线与”功能的总线电路中。
2)只能输出低电平,不能输出高电平。
3)如果要输出高电平,则需要外接上拉。
4)开漏输出具有“线与”功能,一个为低,全部为低,多用于I2C和SMBUS总线。
10.推挽结构指两个三极管受两路互补的信号控制,总是在一个导通的时候另外一个截止,优点开关效率效率高,电流大,驱动能力强。
4. 输出数据寄存器(GPIOx_ODR)
前面提到的双 MOS 管结构电路的输入信号,是由 GPIO“输出数据寄存器 GPIOx_ODR”提供的,因此我们通过修改输出数据寄存器的值就可以修改 GPIO 引脚的输出电平。而“置位/复位寄存器GPIOx_BSRR”可以通过修改输出数据寄存器的值从而影响电路的输出。
5. 复用功能输出
“复用”是指 STM32 的其它片上外设对 GPIO 引脚进行控制,此时 GPIO 引脚用作该外设功能的一部分,算是第二用途。
6. 输入数据寄存器
看 GPIO 结构框图的上半部分,GPIO 引脚经过内部的上、下拉电阻,可以配置成上/下拉输入,然后再连接到施密特触发器,信号经过触发器后,模拟信号转化为 0、1 的数字信号,然后存储在“输入数据寄存器 GPIOx_IDR”中,通过读取该寄存器就可以了解 GPIO 引脚的电平状态。
7.复用功能输入
与“复用功能输出”模式类似,在“复用功能输入模式”时,GPIO 引脚的信号传输到 STM32 其它片上外设,由该外设读取引脚状态。
8.模拟输入输出
1.当 GPIO 引脚用于 ADC 采集电压的输入通道时,用作“模拟输入”功能,此时信号是不经过施密特触发器的。
2.因为经过施密特触发器后信号只有 0、1 两种状态,所以 ADC 外设要采集到原始的模拟信号,信号源输入必须在施密特触发器之前。
3.当 GPIO 引脚用于 DAC 作为模拟电压输出通道时,此时作为“模拟输出”功能,DAC 的模拟信号输出就不经过双 MOS 管结构,模拟信号直接输出到引脚。
9. 八种工作模式
typedef enum
{
GPIO_Mode_AIN = 0x0, // 模拟输入
GPIO_Mode_IN_FLOATING = 0x04, // 浮空输入
GPIO_Mode_IPD = 0x28, // 下拉输入
GPIO_Mode_IPU = 0x48, // 上拉输入
GPIO_Mode_Out_OD = 0x14, // 开漏输出
GPIO_Mode_Out_PP = 0x10, // 推挽输出
GPIO_Mode_AF_OD = 0x1C, // 复用开漏输出
GPIO_Mode_AF_PP = 0x18 // 复用推挽输出
} GPIOMode_TypeDef;
(1)输入模式 (模拟/浮空/上拉/下拉)
1.在输入模式时,施密特触发器打开,输出被禁止,可通过输入数据寄存器 GPIOx_IDR 读取 I/O 状态。
2.浮空输入的电平是不确定的,完全由外部的输入决定,一般接按键的时候用的是这个模式。
3.模拟输入则用于 ADC 采集。
(2)输出模式(推挽/开漏)
1.在输出模式中,推挽模式时双 MOS 管以轮流方式工作,输出数据寄存器 GPIOx_ODR 可控制 I/O输出高低电平。
2.开漏模式时,只有 N-MOS 管工作,输出数据寄存器可控制 I/O 输出高阻态或低电平。
3.输出速度可配置,有 2MHz10MHz50MHz 的选项。
4.在输出模式时施密特触发器是打开的,即输入可用,通过输入数据寄存器 GPIOx_IDR 可读取 I/O的实际状态。
(3)复用功能 (推挽/开漏)
复用功能模式中,输出使能,输出速度可配置,可工作在开漏及推挽模式,但是输出信号源于其它外设,输出数据寄存器 GPIOx_ODR 无效。
10. 寄存器(CRL, CRH, IDR, ODR, BSRR, BRR, LCKR)
(1)端口配置低寄存器(GPIOx_CRL) (x=A…E)
1.CRL控制着端口的低8位IO。
2.主要配置输入输出模式及引脚输出速度。
(2)端口配置高寄存器(GPIOx_CRH) (x=A…E)
1.CRH控制着端口的高8位。
2.主要配置输入输出模式及引脚输出速度。
(3)端口输入数据寄存器(GPIOx_IDR) (x=A…E)
只读寄存器,保存IO口的状态
(4)端口输出数据寄存器(GPIOx_ODR) (x=A…E)
受BSRR, BRR寄存器的控制。
(5)端口位设置/清除寄存器(GPIOx_BSRR) (x=A…E)
对应控制ODR寄存器。
(6)端口位清除寄存器(GPIOx_BRR) (x=A…E)
对应控制ODR寄存器。
(7)端口配置锁定寄存器(GPIOx_LCKR) (x=A…E)
1.该寄存器用来锁定端口位的配置.
2.在规定的写入操作期间,不能改变LCKP[15:0]。当对相应的端口位执行了LOCK序列后,在下次系统复位之前将不能再更改端口位的配置。
二、库函数
1. 初始化结构体
typedef struct
{
uint16_t GPIO_Pin; //IO引脚号
GPIOSpeed_TypeDef GPIO_Speed; //IO引脚输出速度
GPIOMode_TypeDef GPIO_Mode; //IO引脚模式
}GPIO_InitTypeDef;
2. 枚举类型
(1)输出速度
typedef enum
{
GPIO_Speed_10MHz = 1,
GPIO_Speed_2MHz,
GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;
(2)输入输出模式
typedef enum
{
GPIO_Mode_AIN = 0x0, // 模拟输入
GPIO_Mode_IN_FLOATING = 0x04, // 浮空输入
GPIO_Mode_IPD = 0x28, // 下拉输入
GPIO_Mode_IPU = 0x48, // 上拉输入
GPIO_Mode_Out_OD = 0x14, // 开漏输出
GPIO_Mode_Out_PP = 0x10, // 推挽输出
GPIO_Mode_AF_OD = 0x1C, // 复用开漏输出
GPIO_Mode_AF_PP = 0x18 // 复用推挽输出
} GPIOMode_TypeDef;
3. 宏定义
//结构体成员GPIO_Pin的取值
#define GPIO_Pin_0 ((uint16_t)0x0001) /*!< Pin 0 selected */
#define GPIO_Pin_1 ((uint16_t)0x0002) /*!< Pin 1 selected */
#define GPIO_Pin_2 ((uint16_t)0x0004) /*!< Pin 2 selected */
#define GPIO_Pin_3 ((uint16_t)0x0008) /*!< Pin 3 selected */
#define GPIO_Pin_4 ((uint16_t)0x0010) /*!< Pin 4 selected */
#define GPIO_Pin_5 ((uint16_t)0x0020) /*!< Pin 5 selected */
#define GPIO_Pin_6 ((uint16_t)0x0040) /*!< Pin 6 selected */
#define GPIO_Pin_7 ((uint16_t)0x0080) /*!< Pin 7 selected */
#define GPIO_Pin_8 ((uint16_t)0x0100) /*!< Pin 8 selected */
#define GPIO_Pin_9 ((uint16_t)0x0200) /*!< Pin 9 selected */
#define GPIO_Pin_10 ((uint16_t)0x0400) /*!< Pin 10 selected */
#define GPIO_Pin_11 ((uint16_t)0x0800) /*!< Pin 11 selected */
#define GPIO_Pin_12 ((uint16_t)0x1000) /*!< Pin 12 selected */
#define GPIO_Pin_13 ((uint16_t)0x2000) /*!< Pin 13 selected */
#define GPIO_Pin_14 ((uint16_t)0x4000) /*!< Pin 14 selected */
#define GPIO_Pin_15 ((uint16_t)0x8000) /*!< Pin 15 selected */
#define GPIO_Pin_All ((uint16_t)0xFFFF) /*!< All pins selected */
4. 初始化
void LED_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct; //重新定义初始化结构体名字
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//初始化GPIOB时钟
//-------------初始化结构体的值-------------//
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
//--------把结构体的值写入初始化函数-------//
GPIO_Init(GPIOB, &GPIO_InitStruct);
}
5. main
int main(void)
{
// 来到这里的时候,系统的时钟已经被配置成72M。
LED_GPIO_Config(); //调用配置好的初始化函数
while(1)
{
GPIO_SetBits(GPIOB, GPIO_Pin_0); //调用置位功能函数
Delay(0xFFFFF);
GPIO_ResetBits(GPIOB, GPIO_Pin_0);//调用复位功能函数
Delay(0xFFFFF);
}
}