嵌入式学习笔记3.基于寄存器方式控制GPIO
1. 模式寄存器 (GPIOx->MODER)
模式寄存器一共有32位,每2位一组,对应着1个不同的1/0引脚。这些位通过软件写人,用于配置1/0引脚的模式:
- 00:输入模式(复位值)
- 01:通用输出模式
- 10:复用功能模式
- 11:模拟模式
eg:向GPIOA_MODER寄存器的bit1和bitO写人01,表示将引脚PA0设置为输出模式;向GPI0C_MODER寄存器的bit31和bit30写人11,表示将引脚PC15设置为模拟模式。
1. 模式寄存器 (GPIOx->MODER) MODER 寄存器用于配置引脚是输入、输出还是复用功能。每个引脚由两位控制。 配置为输入模式:00 配置为通用输出模式:01 配置为复用功能模式:10 配置为模拟模式:11 操作方法:先清零,再置位。// 示例:将 PA5 配置为通用输出模式 (01) // 1. 清零 PA5 对应的位 (第10位和第11位) GPIOA->MODER &= ~(0x03 << (5 * 2)); // 2. 设置 PA5 为通用输出模式 (01) GPIOA->MODER |= (0x01 << (5 * 2));// 示例:将 PA6 配置为输入模式 (00) // 只需清零即可 GPIOA->MODER &= ~(0x03 << (6 * 2));
2. 输出类型寄存器 (GPIOx->OTYPER)
输出类型寄存器(GPIOx_OTYPER)
该寄存器用于控制GPIO端口各引脚的输出类型,该寄存器在引脚配置为输出模式后才有效,其他工作模式下无效。
输出类型寄存器一共有32位,如表6-3所示。高16位保留,低16位的每1位对应一个不同的1/0引脚。这些位通过软件写人,用于配置1/0引脚的输出类型:
- 0:推挽输出(复位值)
- 1:开漏输出
例如:向GPIOB_OSPEEDR寄存器的bit1写人1,表示将引脚PB1设置为开漏输出模式。
2. 输出类型寄存器 (GPIOx->OTYPER) OTYPER 寄存器用于配置输出引脚是推挽(Push-Pull)还是开漏(Open-Drain)。每个引脚由一位控制。 推挽输出:0 (输出高低电平) 开漏输出:1 (只能拉低电平,需要外部上拉电阻才能输出高电平) 操作方法:通常与 MODER 配合使用。// 示例:将 PA5 配置为推挽输出 GPIOA->OTYPER &= ~(0x01 << 5); // 清零第5位// 示例:将 PA7 配置为开漏输出 GPIOA->OTYPER |= (0x01 << 7); // 置位第7位
3. 输出速度寄存器 (GPIOx->OSPEEDR)
- 00:低速(复位值)
- 01:中速
- 10:高速
- 11:超高速
例如:向GPI0D_MODER寄存器的bi15和bit14写人11,表示将引脚PD7的输出速度设置为超高速。
3. 输出速度寄存器 (GPIOx->OSPEEDR) OSPEEDR 寄存器用于配置输出引脚的切换速度。每个引脚由两位控制。 低速:00 中速:01 高速:10 超高速:11 (部分引脚支持) 操作方法:先清零,再置位。 c 运行 // 示例:将 PA5 配置为高速输出 (10) GPIOA->OSPEEDR &= ~(0x03 << (5 * 2)); // 清零 GPIOA->OSPEEDR |= (0x02 << (5 * 2)); // 设置为 10
4. 上下拉寄存器 (GPIOx->PUPDR)
- 00:既无上拉也无下拉(复位值)
- 01:上拉
- 10:下拉
- 11:保留
例如:向GPIOE_PUPDR寄存器的bit17和bit16写入01,表示使能引脚PE8的上拉电阻。
4. 上下拉寄存器 (GPIOx->PUPDR) PUPDR 寄存器用于配置引脚的上拉或下拉电阻。每个引脚由两位控制。无上拉 / 下拉:00 上拉:01 下拉:10 操作方法:先清零,再置位。 // 示例:将 PA6 (输入引脚) 配置为上拉 GPIOA->PUPDR &= ~(0x03 << (6 * 2)); // 清零 GPIOA->PUPDR |= (0x01 << (6 * 2)); // 设置为 01// 示例:将 PA5 (输出引脚) 配置为无上拉/下拉 GPIOA->PUPDR &= ~(0x03 << (5 * 2)); // 只需清零
5. 输入数据寄存器 (GPIOx->IDR)
- 0:对应引脚输入低电平
- 1:对应引脚输入高电平
例如:如果读到GPIOA_IDR寄存器的bit7为1,表示引脚PA7输人高电平。
注意:输入数据寄存器是只读模式,不能通过软件写人。当引脚设置为输出模式或复用模式时,也可以通过输入数据寄存器读取引脚的电平状态。
5. 输入数据寄存器 (GPIOx->IDR) IDR 寄存器用于读取引脚的当前电平状态。它是只读寄存器。 操作方法:使用按位与(&)操作来读取特定引脚。// 示例:读取 PA6 的电平状态 uint32_t pin_state = GPIOA->IDR & (0x01 << 6);if (pin_state) {// PA6 为高电平 } else {// PA6 为低电平 }
6. 输出数据寄存器 (GPIOx->ODR)
- 0:对应引脚输出低电平
- 1:对应引脚输出高电平
例如:向GPIOA_ODR寄存器的bi5写人1,表示引脚PA5输出高电平。
6. 输出数据寄存器 (GPIOx->ODR) 用于控制输出引脚的电平。 ODR:直接读写整个端口的输出状态。读改写(Read-Modify-Write)操作可能引发竞态条件。操作方法:// 使用 ODR 控制 PA5 GPIOA->ODR |= (0x01 << 5); // PA5 输出高电平 (Set) GPIOA->ODR &= ~(0x01 << 5); // PA5 输出低电平 (Reset)// HAL库也提供了直接操作BSRR的宏 __HAL_GPIO_SET_PIN(GPIOA, GPIO_PIN_5); __HAL_GPIO_RESET_PIN(GPIOA, GPIO_PIN_5);
- 高16位用于控制对应的I0引脚输出低电平:写入1对应引脚输出低电平,写人 0没有任何作用。
- 低16位用于控制对应的1/0引脚输出高电平:写入1对应引脚输出高电平,写人 0没有任何作用。
和GPIOx_ODR寄存器相比,使用GPIOx_BSRR寄存器控制引I脚有两个优势:
①GPIOx_BSRR寄存器的操作是原子操作,不会被中断所打断。
②在控制多个引脚同时输出高/低电平时更加方便。
7.置位 / 复位寄存器 (GPIOx->BSRR) BSRR:推荐使用。高 16 位用于复位(置 0),低 16 位用于置位(置 1)。操作是原子的,不会有竞态条件。 操作方法:// 使用 BSRR 控制 PA5 (更安全、更高效) #define SET_PIN5 (0x01 << 5) #define RESET_PIN5 (0x01 << (5 + 16))GPIOA->BSRR = SET_PIN5; // PA5 输出高电平 GPIOA->BSRR = RESET_PIN5; // PA5 输出低电平
7. 复用功能寄存器 (GPIOx->AFRL, GPIOx->AFRH)
AFRL
(用于引脚 0-7) 和 AFRH
(用于引脚 8-15) 寄存器用于选择复用功能的具体外设。每个引脚由四位控制。
操作方法:需要查阅芯片数据手册(Datasheet)来确定外设对应的 AF 编号。
// 示例:将 PA9 配置为 USART1_TX (假设 AF 编号为 7)
// PA9 在 AFRH 寄存器中,对应位 [15:12]
// 1. 清零 PA9 对应的 4 个位
GPIOA->AFRH &= ~(0x0F << ((9 - 8) * 4));
// 2. 设置 AF 编号为 7 (0111)
GPIOA->AFRH |= (0x07 << ((9 - 8) * 4));
8. 配置锁存寄存器 (GPIOx->LCKR)
LCKR
寄存器用于锁定引脚的配置,防止意外修改。一旦锁定,只有在下次复位后才能重新配置。
操作方法:遵循特定的 “锁定序列”。
// 示例:锁定 PA5 的配置
// 1. 写 LCKR 寄存器,设置 LCKK 位和要锁定的引脚位
GPIOA->LCKR = (GPIO_PIN_5 | GPIO_LCKK_Msk);
// 2. 再次写 LCKR 寄存器,同样设置 LCKK 位和引脚位
GPIOA->LCKR = (GPIO_PIN_5 | GPIO_LCKK_Msk);
// 3. 第三次读 LCKR 寄存器
volatile uint32_t temp = GPIOA->LCKR;
// 4. 第四次读 LCKR 寄存器。如果 LCKK 位为 1,则锁定成功。
if ((GPIOA->LCKR & GPIO_LCKK_Msk) == GPIO_LCKK_Msk) {// PA5 配置已成功锁定!
}
总结:HAL 库函数 vs. 直接寄存器操作
功能 (Function) | HAL 库函数 (HAL Library Function) | 直接寄存器操作 (Direct Register Access) |
---|---|---|
初始化配置 | HAL_GPIO_Init() | GPIOx->MODER , OTYPER , OSPEEDR , PUPDR , AFRL/H |
读取输入 | HAL_GPIO_ReadPin() | GPIOx->IDR & GPIO_PIN_x |
写输出 | HAL_GPIO_WritePin() | GPIOx->ODR = ... 或 GPIOx->BSRR = ... |
翻转输出 | HAL_GPIO_TogglePin() | GPIOx->ODR ^= GPIO_PIN_x 或 GPIOx->BSRR = ... |
锁定配置 | HAL_GPIO_LockPin() | GPIOx->LCKR (遵循锁定序列) |