GPIO
1. 基本定义和概念
IO复用:同一个IO引脚具备多个不同的功能;
IO重映射:当出现功能引脚冲突时,讲功能移动到到备用引脚上;
通用和复用的区别:通用表示直接控制,复用表示其他功能模块托管(片上外设)使用;
通用输入输出(GPIO,General Purpose Input/Output): 具有八种各种模式。分别是:
(1)输入浮空(Input floating)
(2)输入上拉(Input pull-up)
(3)输入下拉(Input-pull-down)
(4)模拟输入(Analog)
(5)通用开漏输出(Output open-drain)
(6)通用推挽式输出(Output push-pull)
(7)推挽式复用功能(Alternate function push-pull)
(8)开漏复用功能(Alternate function open-drain)
输出模式下:控制端口输出高低电平,用于驱动LED,蜂鸣器等,大功率器件借助驱动芯片间接控制。
输入模式下:读取端口的高低电平,用于读取按键,外接模拟信号的输入,ADC电压采集,模拟通信协议接收数据等。
IO输出最大速度:电压上升和下降的时间限制了IO的输出速度(极限状态下电平稳定时间太短)。考虑到低功耗,实际使用通常取满足要求最小通信速度。限制电压在VSS~VDD范围内。
GPIO每位的具体电路结构:
2.开漏输出和推挽输出
2.1 推挽输出和开漏输出的区别
一句话概括:推挽支持高低电平输出,开漏仅有低电平外加高阻态。同时开漏模式的上方的P-MOS始终关闭。
补充:https://www.cnblogs.com/yangyang13/p/18060487
如何控制P-MOS与N-MOS见随笔:
2.2 推挽输出和开漏输出的选择
使用推挽输出:
- 驱动能力要求较高;
- 高信号传输;
- 不需要共用信号线(如果有设备输出0,有设备输出1就会形成短路);
使用开漏输出:
- 多个设备共用信号线,线与;
- 不同电压系统之间的接口(通过上拉电阻改变电平);
- 需要外部上拉电阻来确定逻辑高电平;
2.3 输入上拉、输入下拉和输入浮空的区别
一句话概括:当引脚浮空时,上拉电阻提供默认高电压,下拉电阻提供默认低电压。浮空则处于不确定状态。
3 输入模式
3.1保护二极管的作用
当I/O引脚电压大于VDD时,如上图位于I/O引脚与VDD之间形成导通,同理,当电压低于VSS时,下二极管导通,形成钳位。
3.2 输入是如何判断高低电平的
通过施密特触发器(图中的肖特基是翻译错误)对信号进行波形整形,然后输入数据寄存器中。
补充:模拟输入时关闭施密特触发器,信号直接输入到片上外设。
4. GPIO寄存器
每个GPIO端口有7个相关的寄存器:
-
2个32位配置寄存器(GPIOx_CRL,GPIOx_CRH),可读可写,Port Configuration Register Low/High,端口配置低/高寄存器, 用于控制IO端口输入输出模式,CRL控制低八位引脚,CRH控制高八位引脚,每位引脚由MODE和CNF控制,共4bit位。其中MODE控制是输出还是输入以及工作速度,CNF控制是否上下拉或浮空及模拟输入。
-
2个32位数据寄存器(GPIOx_IDR和GPIOx_ODR),输入只能读,输出可读可写,Port Input/Output Data Register,端口输入/输出寄存器, 控制IO端口具体输入和输出的值(0/1)。均只有低16位使用,高16位保留。
-
1个32位置位/复位寄存器(GPIOx_BSRR),只能写,Port bit set/reset register, 端口位设置/清除寄存器,可以设置ODR具体某位的值,低16位从0~15,置1时设置0~15端口输出1,高16位从16~32,置1时设置0~15端口输出0。
-
1个16位复位寄存器(GPIOx_BRR),只能写,端口位清除寄存器,可以将ODR具体某位的值复位为0,只有低16位使用,高16位保留。
-
1个32位锁定寄存器(GPIOx_LCKR),可读可写,端口配置锁定寄存器,可以锁定寄存器的工作模式让其不被修改。使用方法比较特殊如下图
I/O端口位的基本结构(辅助理解寄存器功能):
5. 实践验证
5.1 寄存器形式
实现逻辑:
查找对应寄存器地址,改变其值到对应模式。以GPIOA_Pin_0为例,
-
配置时钟
在存储映像的寄存器组起始地址进行查询
查询可以看到,IO外设是位于APB2总线上,APB2ENR地址偏移量是0x18
所以 APB2ENR地址为:0x4002 1000 + 0x18 ,因为GPIOA位于第二位,所以启动GPIOA时钟代码为: *(uint32_t *)(0x40021000 + 0x18) = 4;
-
配置IO输出模式寄存器
查询对应寄存器组基地址
查看对应偏移地址和寄存器参数功能
参考上图,配置PA0为通用推挽输出代码为:*(uint32_t *)(0x4001 0800 + 0x00) = 3;
-
配置输出寄存器
参考上图,配置PA0为输出0代码为:*(uint32_t *)(0x4001 0800 + 0xch) = 0xfffe; -
优化,在"stm32f10x.h"头文件中已经定义了相关寄存器和结构体,可以直接调用。
如下:#define PERIPH_BASE ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */ #define AHBPERIPH_BASE (PERIPH_BASE + 0x20000) #define RCC_BASE (AHBPERIPH_BASE + 0x1000) #define RCC ((RCC_TypeDef *) RCC_BASE) typedef struct { __IO uint32_t CR; __IO uint32_t CFGR; __IO uint32_t CIR; __IO uint32_t APB2RSTR; __IO uint32_t APB1RSTR; __IO uint32_t AHBENR; __IO uint32_t APB2ENR; __IO uint32_t APB1ENR; __IO uint32_t BDCR; __IO uint32_t CSR; #ifdef STM32F10X_CL __IO uint32_t AHBRSTR; __IO uint32_t CFGR2; #endif /* STM32F10X_CL */ #if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL) uint32_t RESERVED0; __IO uint32_t CFGR2; #endif /* STM32F10X_LD_VL || STM32F10X_MD_VL || STM32F10X_HD_VL */ } RCC_TypeDef; // 结构体内排序与芯片手册寄存器位置偏移量相对应,利用了结构体内地址连续的特点 // 代码可优化为 RCC->APB2ENR = 4; GPIOA->CRL = 3; GPIOA->ODR = 0xfffe; // 位操作优化 RCC->APB2ENR |= 0x1 << 2; GPIOA->CRL &= ~(0x1 << 3); GPIOA->CRL &= ~(0x1 << 2); GPIOA->CRL |= 0x1 << 1; GPIOA->CRL |= 0x1 << 0; GPIOA->ODR &= ~(0x1 << 0); // "stm32f10x.h"头文件中也定义了对应寄存器的控制,再次优化 #define RCC_APB2ENR_IOPAEN ((uint32_t)0x00000004) /*!< I/O port A clock enable */ #define GPIO_CRL_CNF0_0 ((uint32_t)0x00000004) /*!< Bit 0 */ #define GPIO_CRL_CNF0_1 ((uint32_t)0x00000008) /*!< Bit 1 */ #define GPIO_CRL_MODE0_0 ((uint32_t)0x00000001) /*!< Bit 0 */ #define GPIO_CRL_MODE0_1 ((uint32_t)0x00000002) /*!< Bit 1 */ // 最终代码 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; GPIOA->CRL &= ~GPIO_CRL_CNF0_1; GPIOA->CRL &= ~GPIO_CRL_CNF0_0; GPIOA->CRL |= GPIO_CRL_MODE0_1; GPIOA->CRL |= GPIO_CRL_MODE0_0; GPIOA->ODR &= ~GPIO_ODR_ODR0;
5.2 库函数形式
实现逻辑:
使用cubeMX配置引脚状态,然后再通过keil在工程中调用Hal库函数实现想要的现象
- cubeMX配置关键界面
- 配置完后生成工程,我们可以发现,工程中会自动初始时钟配置和GPIO配置
- 在初始化函数中会配置时钟和GPIO
- 主函数中利用HAL_GPIO_WritePin函数进行IO端口的控制,请对应传入参数入如下值