最新教程下载:http://www.armbbs.cn/forum.php?mod=viewthread&tid=93255
本章教程为大家讲解GPIO(General-purpose I/Os)的API使用和注意事项。GPIO是所有外设里面较容易掌握的,但也是用到最多的。
配合第15章讲解的各种IO模式再学习本章,更容易理解透彻。
17.1 初学者重要提示
17.2 GPIO涉及到的寄存器
17.3 源文件stm32f4xx_hal_gpio.c
17.4 如何使用HAL库的GPIO驱动
17.5 总结
1、 如何阅读HAL库源码的问题
HAL库实现的函数有复杂的,也有简单的,简单的可以直接阅读代码。复杂的代码阅读起来比较耗时间,如果再配合参考手册抠每个寄存器的配置,那就更消耗时间了。所以对于这种函数,用户仅需了解每个部分实行的功能即可,而且HAL库都做了关键注释,以说明这部分实现的功能。所以用户没有必要去抠每个配置是如何实现的,仅需知道实现了什么功能。以后工程项目有需要了解具体配置时,再看即可。
2、 学习本章节前,务必保证已经学习了第15章。
GPIO外设涉及到的寄存器比较少,也容易理解,推荐大家阅读GPIO源码的时候将参考手册中对应的寄存器功能做一个了解。
很多时候,我们会直接调用GPIO的寄存器进行配置,而不使用HAL进行调用,以提高执行效率,特别是中断里面执行时。
这个文件主要是实现GPIO的引脚配置,学习这个文件注意事项:
函数原型:
void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init) { /* 部分省略未写 */ /* 配置GPIO引脚,这些采用16个引脚的循环检测模式 */ for(position = 0; position < GPIO_NUMBER; position++) { /* 部分省略未写 */ if(iocurrent == ioposition) { /*--------------------- GPIO模式配置 ------------------------*/ /*--------------------- EXTI模式配置 ------------------------*/ } } }
函数描述:
此函数用于初始化GPIO,此函数主要实现如下功能:
函数参数:
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE) #define GPIOB ((GPIO_TypeDef *) GPIOB_BASE) #define GPIOC ((GPIO_TypeDef *) GPIOC_BASE) #define GPIOD ((GPIO_TypeDef *) GPIOD_BASE) #define GPIOE ((GPIO_TypeDef *) GPIOE_BASE) #define GPIOF ((GPIO_TypeDef *) GPIOF_BASE) #define GPIOG ((GPIO_TypeDef *) GPIOG_BASE) #define GPIOH ((GPIO_TypeDef *) GPIOH_BASE) #define GPIOI ((GPIO_TypeDef *) GPIOI_BASE) #define GPIOJ ((GPIO_TypeDef *) GPIOJ_BASE) #define GPIOK ((GPIO_TypeDef *) GPIOK_BASE)
{
uint32_t Pin;
uint32_t Mode;
uint32_t Pull;
uint32_t Speed;
uint32_t Alternate;
}GPIO_InitTypeDef;
下面将结构体每个成员做个说明:
GPIO_MODE_INPUT /* 输入模式 */ GPIO_MODE_OUTPUT_PP /* 推挽输出 */ GPIO_MODE_OUTPUT_OD /* 开漏输出 */ GPIO_MODE_AF_PP /* 复用推挽 */ GPIO_MODE_AF_OD /* 复用开漏 */ GPIO_MODE_ANALOG /* 模拟模式 */ GPIO_MODE_IT_RISING /* 外部中断,上升沿触发检测 */ GPIO_MODE_IT_FALLING /* 外部中断,下降沿触发检测 */ GPIO_MODE_IT_RISING_FALLING /* 外部中断,双沿触发检测 */ GPIO_MODE_EVT_RISING /* 外部事件模式,上升沿触发检测 */ GPIO_MODE_EVT_FALLING /* 外部事件模式,下降沿触发检测 */ GPIO_MODE_EVT_RISING_FALLING /* 外部事件模式,双沿触发检测 */
GPIO_NOPULL /* 无上拉和下拉电阻 */ GPIO_PULLUP /* 带上拉电阻 */ GPIO_PULLDOWN /* 带下拉电阻 */
GPIO_SPEED_FREQ_LOW /* 低速 */ GPIO_SPEED_FREQ_MEDIUM /* 中等速度 */ GPIO_SPEED_FREQ_HIGH /* 快速 */ GPIO_SPEED_FREQ_VERY_HIGH /* 高速 */
GPIO_AF7_USART1
GPIO_AF7_USART2
GPIO_AF7_USART3
GPIO_AF7_USART6
GPIO_AF7_UART7
注意事项:
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Pin = GPIO_PIN_0; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); //这里会执行16次for查询 GPIO_InitStruct.Pin = GPIO_PIN_1; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); //这里会执行16次for查询 GPIO_InitStruct.Pin = GPIO_PIN_2; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); //这里会执行16次for查询
如果是程序运行期间的引脚状态切换,最好采用下面的方式或者直接寄存器操作:
GPIO_InitStruct.Pin = GPIO_PIN_0 |GPIO_PIN_1 | GPIO_PIN_2 ; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); //这里会执行16次for查询
使用举例:
GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */ GPIO_InitStruct.Pull = GPIO_NOPULL; /* 无上拉和下拉电阻 */ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; /* GPIO速度等级最高 */ HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
函数原型:
void HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin) { for(position = 0; position < GPIO_NUMBER; position++) { /* 部分省略未写 */ if(iocurrent == ioposition) { /*------------------------- GPIO Mode Configuration --------------------*/ /* 配置为模拟模式 */ GPIOx->MODER |= (GPIO_MODER_MODER0 << (position * 2)); /* 配置复用模式为AF0,即作为通用IO */ GPIOx->AFR[position >> 3] &= ~((uint32_t)0xF << ((uint32_t)(position & (uint32_t)0x07) * 4)) ; /* 配置到最低速度 */ GPIOx->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR0 << (position * 2)); /* 输出类型是推挽,如果IO模式被设置为模拟,此选项对其没有影响 */ GPIOx->OTYPER &= ~(GPIO_OTYPER_OT_0 << position) ; /* 无上拉和下拉电阻 */ GPIOx->PUPDR &= ~(GPIO_PUPDR_PUPDR0 << (position * 2)); /*------------------------- EXTI模式配置 --------------------*/ } } }
函数描述:
此函数用于复位IO到初始化状态,具体状态看函数原型中的注释即可。
函数参数:
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE) #define GPIOB ((GPIO_TypeDef *) GPIOB_BASE) #define GPIOC ((GPIO_TypeDef *) GPIOC_BASE) #define GPIOD ((GPIO_TypeDef *) GPIOD_BASE) #define GPIOE ((GPIO_TypeDef *) GPIOE_BASE) #define GPIOF ((GPIO_TypeDef *) GPIOF_BASE) #define GPIOG ((GPIO_TypeDef *) GPIOG_BASE) #define GPIOH ((GPIO_TypeDef *) GPIOH_BASE) #define GPIOI ((GPIO_TypeDef *) GPIOI_BASE) #define GPIOJ ((GPIO_TypeDef *) GPIOJ_BASE) #define GPIOK ((GPIO_TypeDef *) GPIOK_BASE)
使用举例:
此函数的使用比较简单,需要调用的时候直接调用即可。
函数原型:
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) { GPIO_PinState bitstatus; /* Check the parameters */ assert_param(IS_GPIO_PIN(GPIO_Pin)); if((GPIOx->IDR & GPIO_Pin) != (uint32_t)GPIO_PIN_RESET) { bitstatus = GPIO_PIN_SET; } else { bitstatus = GPIO_PIN_RESET; } return bitstatus; }
函数描述:
此函数用于读取引脚状态,通过GPIO的IDR寄存器读取。
函数参数:
使用举例:
此函数的使用比较简单,需要调用的时候直接调用即可。
函数原型:
void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState) { /* Check the parameters */ assert_param(IS_GPIO_PIN(GPIO_Pin)); assert_param(IS_GPIO_PIN_ACTION(PinState)); if(PinState != GPIO_PIN_RESET) { GPIOx->BSRRL = GPIO_Pin; } else { GPIOx->BSRRH = GPIO_Pin ; } }
函数描述:
此函数用于设置引脚输出高电平或者低电平。使用GPIO的BSRR寄存器进行设置,使用这个寄存器的好处是支持原子操作,由硬件支持的。原子操作的含义是操作过程不会被中断打断,而我们使用GPIO中另一个设置输出的寄存ODR是会被中断打断的。大家看下寄存器赋值操作对应的反汇编,是由多条汇编指令组成的。
函数参数:
使用举例:
此函数的使用比较简单,需要调用的时候直接调用即可。
函数原型:
void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) { /* Check the parameters */ assert_param(IS_GPIO_PIN(GPIO_Pin)); GPIOx->ODR ^= GPIO_Pin; }
函数描述:
此函数用于设置引脚的电平翻转,使用GPIO的ODR寄存器进行设置。
函数参数:
使用举例:
此函数的使用比较简单,需要调用的时候直接调用即可。
函数原型:
HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) { __IO uint32_t tmp = GPIO_LCKR_LCKK; assert_param(IS_GPIO_LOCK_INSTANCE(GPIOx)); assert_param(IS_GPIO_PIN(GPIO_Pin)); /* 应用IO锁的写入顺序 */ tmp |= GPIO_Pin; /* 设置 LCKx bit(s): LCKK=‘1‘ + LCK[15-0] */ GPIOx->LCKR = tmp; /* 复位 LCKx bit(s): LCKK=‘0‘ + LCK[15-0] */ GPIOx->LCKR = GPIO_Pin; /* 设置 LCKx bit(s): LCKK=‘1‘ + LCK[15-0] */ GPIOx->LCKR = tmp; /* 复位 LCKK bit*/ tmp = GPIOx->LCKR; if((GPIOx->LCKR & GPIO_LCKR_LCKK) != RESET) { return HAL_OK; } else { return HAL_ERROR; } }
函数描述:
此函数用于锁住GPIO引脚所涉及到的寄存器,这些寄存器包括GPIOx_MODER,GPIOx_OTYPER,GPIOx_OSPEEDR,GPIOx_PUPDR,GPIOx_AFRL 和 GPIOx_AFRH。
函数参数:
注意事项:
使用举例:
此函数的使用比较简单,需要调用的时候直接调用即可。
使用方法由HAL库提供(本章17.3.1小节提供的例子就是这种方式):
(1) 通过结构体GPIO_InitTypeDef的成员Mode配置输入、输出、模拟等模式。
(2) 通过结构体GPIO_InitTypeDef的成员Pull配置上拉、下拉电阻。
(3) 通过结构体GPIO_InitTypeDef的成员Speed配置GPIO速度等级。
(4) 如果选择了复用模式,那么就需要配置结构体GPIO_InitTypeDef的成员Alternate。
(5) 如果引脚功能用于ADC、DAC的话,需要配置引脚为模拟模式。
(6) 如果是用于外部中断/事件,结构体GPIO_InitTypeDef的成员Mode可以配置相应模式,相应的上升沿、下降沿或者双沿触发也可以选择。
另外注意下面三个问题:
本章节就为大家讲解这么多,建议大家将GPIO的驱动源码结合参考手册中的寄存器通读一遍,对于我们后面章节的学习大有裨益。
【STM32F407开发板用户手册】第17章 STM32F407之GPIO的HAL库API
原文:https://www.cnblogs.com/armfly/p/13299898.html