你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32H7之GPIO的HAL库API

[复制链接]
STMCU小助手 发布时间:2021-10-29 23:33
17.1 初学者重要提示
1、  如何阅读HAL库源码的问题

HAL库实现的函数有复杂的,也有简单的,简单的可以直接阅读代码。复杂的代码阅读起来比较耗时间,如果再配合参考手册抠每个寄存器的配置,那就更消耗时间了。所以对于这种函数,用户仅需了解每个部分实行的功能即可,而且HAL库都做了关键注释,以说明这部分实现的功能。所以用户没有必要去抠每个配置是如何实现的,仅需知道实现了什么功能。以后工程项目有需要了解具体配置时,再看即可。

2、  学习本章节前,务必保证已经学习了第15章。

17.2 GPIO涉及到的寄存器
GPIO外设涉及到的寄存器比较少,也容易理解,推荐大家阅读GPIO源码的时候将参考手册中对应的寄存器功能做一个了解。

很多时候,我们会直接调用GPIO的寄存器进行配置,而不使用HAL进行调用,以提高执行效率,特别是中断里面执行时。

17.3 源文件stm32h7xx_hal_gpio.c
这个文件主要是实现GPIO的引脚配置,学习这个文件注意事项:

  系统上电后,引脚默认状态是模拟模式。
  所有的引脚有弱上拉和弱下拉电阻,阻值范围30-50KΩ。其中配置为模拟模式时,上拉和下拉被硬件禁止,其它的输入、输出和复用都可以配置上拉和下拉。
  在输出或者复用模式,每个引脚可以配置成推挽或者开漏,且有GPIO速度等级可配置。另外注意,不同的供电范围,实际速度等级是有些区别的。
  每个GPIO都可以配置成外部中断/事件模式,但要特别注意,引脚要配置成输入模式,在芯片的内部有个多路选择器,选择引脚与16个外部中断/事件EXTI0 - EXTI15中的那个导通。这就决定了,每个外部中断/事件只能与一个引脚导通,如果用户配置了多个引脚PA0,PB0,PC0等,那么只有一个能够与EXTI0导通。
17.3.1 函数HAL_GPIO_Init
函数原型:

  1. void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
  2. {
  3. /* 部分省略未写 */

  4.   /* 配置GPIO引脚,这些采用16个引脚的循环检测模式 */
  5.   for(position = 0; position < GPIO_NUMBER; position++)
  6.   {
  7.      /* 部分省略未写 */
  8.     if(iocurrent == ioposition)
  9.     {
  10.       /*--------------------- GPIO模式配置 ------------------------*/

  11.       /*--------------------- EXTI模式配置 ------------------------*/

  12.     }
  13.   }
  14. }
复制代码

函数描述:

此函数用于初始化GPIO,此函数主要实现如下功能:

  GPIO功能配置。
  设置EXTI功能。
函数参数:

  第1个参数用于填写使用的端口号,可以是:
  1. #define GPIOA    ((GPIO_TypeDef *) GPIOA_BASE)
  2. #define GPIOB    ((GPIO_TypeDef *) GPIOB_BASE)
  3. #define GPIOC    ((GPIO_TypeDef *) GPIOC_BASE)
  4. #define GPIOD    ((GPIO_TypeDef *) GPIOD_BASE)
  5. #define GPIOE     ((GPIO_TypeDef *) GPIOE_BASE)
  6. #define GPIOF     ((GPIO_TypeDef *) GPIOF_BASE)
  7. #define GPIOG     ((GPIO_TypeDef *) GPIOG_BASE)
  8. #define GPIOH     ((GPIO_TypeDef *) GPIOH_BASE)
  9. #define GPIOI      ((GPIO_TypeDef *) GPIOI_BASE)
  10. #define GPIOJ      ((GPIO_TypeDef *) GPIOJ_BASE)
  11. #define GPIOK     ((GPIO_TypeDef *) GPIOK_BASE)
复制代码

  第2个形参是GPIO_InitTypeDef类型的结构体变量,这个变量比较重要,要熟练掌握,定义如下:
  1. typedef struct
  2. {
  3.   uint32_t Pin;   
  4.   uint32_t Mode;
  5.   uint32_t Pull;     
  6.   uint32_t Speed;   
  7.   uint32_t Alternate;
  8. }GPIO_InitTypeDef;
复制代码

下面将结构体每个成员做个说明:

  成员Pin用于配置选择的引脚,范围GPIO_PIN_0到GPIO_PIN_15,额外还可以选择GPIO_PIN_All和GPIO_PIN_MASK。
  成员Mode可以选择:
  1. GPIO_MODE_INPUT             /* 输入模式  */
  2. GPIO_MODE_OUTPUT_PP        /* 推挽输出  */
  3. GPIO_MODE_OUTPUT_OD       /* 开漏输出  */
  4. GPIO_MODE_AF_PP             /* 复用推挽  */
  5. GPIO_MODE_AF_OD            /* 复用开漏  */

  6. GPIO_MODE_ANALOG          /* 模拟模式  */
  7. GPIO_MODE_IT_RISING         /* 外部中断,上升沿触发检测 */
  8. GPIO_MODE_IT_FALLING       /* 外部中断,下降沿触发检测 */
  9. GPIO_MODE_IT_RISING_FALLING    /* 外部中断,双沿触发检测   */

  10. GPIO_MODE_EVT_RISING           /* 外部事件模式,上升沿触发检测  */
  11. GPIO_MODE_EVT_FALLING          /* 外部事件模式,下降沿触发检测  */
  12. GPIO_MODE_EVT_RISING_FALLING  /* 外部事件模式,双沿触发检测 */
复制代码

  成员Pull用于配置上拉下拉电阻:
  1. GPIO_NOPULL         /* 无上拉和下拉电阻 */
  2. GPIO_PULLUP          /* 带上拉电阻  */
  3. GPIO_PULLDOWN    /* 带下拉电阻  */
  4.   成员Speed用于配置GPIO速度等级,有下面四种可选:
  5. GPIO_SPEED_FREQ_LOW          /* 低速 */
  6. GPIO_SPEED_FREQ_MEDIUM      /* 中等速度 */
  7. GPIO_SPEED_FREQ_HIGH         /* 快速 */
  8. GPIO_SPEED_FREQ_VERY_HIGH   /* 高速  */
复制代码

  成员Alternate用于配置引脚复用,可选择的复用方式在文件stm32h7xx_hal_gpio_ex.h里面进行了定义,比如串口复用:
  1. GPIO_AF7_USART1   
  2. GPIO_AF7_USART2   
  3. GPIO_AF7_USART3      
  4. GPIO_AF7_USART6   
  5. GPIO_AF7_UART7
复制代码

注意事项:

与F1,F4系列的标准库不同,H7的HAL库已经没有单独的EXTI外部中断设置文件,是将其整合到此函数里面了。
函数HAL_GPIO_Init对引脚的初始化是把同组16个引脚for循环检测了一遍,效率稍低。所以不推荐下面这种初始化:
  1. GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;                  
  2. GPIO_InitStruct.Pull = GPIO_NOPULL;              
  3. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;  
  4. GPIO_InitStruct.Pin = GPIO_PIN_0;
  5. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);   //这里会执行16次for查询

  6. GPIO_InitStruct.Pin = GPIO_PIN_1;
  7. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);   //这里会执行16次for查询

  8. GPIO_InitStruct.Pin = GPIO_PIN_2;
  9. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);  //这里会执行16次for查询
复制代码

如果是程序运行期间的引脚状态切换,最好采用下面的方式或者直接寄存器操作:

  1. GPIO_InitStruct.Pin = GPIO_PIN_0 |GPIO_PIN_1 | GPIO_PIN_2 ;
  2. GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;                  
  3. GPIO_InitStruct.Pull = GPIO_NOPULL;              
  4. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;  

  5. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);  //这里会执行16次for查询
复制代码

使用举例:

  1. GPIO_InitTypeDef  GPIO_InitStruct;

  2. GPIO_InitStruct.Pin = GPIO_PIN_0;
  3. GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;      /* 推挽输出 */         
  4. GPIO_InitStruct.Pull = GPIO_NOPULL;                /* 无上拉和下拉电阻 */
  5. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; /* GPIO速度等级最高 */

  6. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);  
复制代码

17.3.2 函数HAL_GPIO_DeInit
函数原型:

  1. void HAL_GPIO_DeInit(GPIO_TypeDef  *GPIOx, uint32_t GPIO_Pin)
  2. {  
  3.   for(position = 0; position < GPIO_NUMBER; position++)
  4.   {
  5.      /* 部分省略未写 */
  6.     if(iocurrent == ioposition)
  7.     {
  8.       /*------------------------- GPIO Mode Configuration --------------------*/
  9.       /* 配置为模拟模式 */
  10.       GPIOx->MODER |= (GPIO_MODER_MODER0 << (position * 2));

  11.       /* 配置复用模式为AF0,即作为通用IO */
  12.       GPIOx->AFR[position >> 3] &= ~((uint32_t)0xF << ((uint32_t)(position & (uint32_t)0x07) * 4)) ;

  13.       /* 配置到最低速度 */
  14.       GPIOx->OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR0 << (position * 2));

  15.       /* 输出类型是推挽,如果IO模式被设置为模拟,此选项对其没有影响 */
  16.       GPIOx->OTYPER  &= ~(GPIO_OTYPER_OT_0 << position) ;

  17.       /* 无上拉和下拉电阻 */
  18.       GPIOx->PUPDR &= ~(GPIO_PUPDR_PUPDR0 << (position * 2));

  19.       /*------------------------- EXTI模式配置 --------------------*/

  20.     }
  21.   }
  22. }
复制代码

函数描述:

此函数用于复位IO到初始化状态,具体状态看函数原型中的注释即可。

函数参数:

  第1个参数用于填写使用的端口号,可以是:
  1. #define GPIOA    ((GPIO_TypeDef *) GPIOA_BASE)
  2. #define GPIOB    ((GPIO_TypeDef *) GPIOB_BASE)
  3. #define GPIOC    ((GPIO_TypeDef *) GPIOC_BASE)
  4. #define GPIOD    ((GPIO_TypeDef *) GPIOD_BASE)
  5. #define GPIOE     ((GPIO_TypeDef *) GPIOE_BASE)
  6. #define GPIOF     ((GPIO_TypeDef *) GPIOF_BASE)
  7. #define GPIOG     ((GPIO_TypeDef *) GPIOG_BASE)
  8. #define GPIOH     ((GPIO_TypeDef *) GPIOH_BASE)
  9. #define GPIOI      ((GPIO_TypeDef *) GPIOI_BASE)
  10. #define GPIOJ      ((GPIO_TypeDef *) GPIOJ_BASE)
  11. #define GPIOK     ((GPIO_TypeDef *) GPIOK_BASE)
复制代码

  第2个参数是配置选择的引脚,范围GPIO_PIN_0到GPIO_PIN_15。
使用举例:

此函数的使用比较简单,需要调用的时候直接调用即可。

17.3.3 函数HAL_GPIO_ReadPin
函数原型:

  1. GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
  2. {
  3.   GPIO_PinState bitstatus;

  4.   /* Check the parameters */
  5.   assert_param(IS_GPIO_PIN(GPIO_Pin));

  6.   if((GPIOx->IDR & GPIO_Pin) != (uint32_t)GPIO_PIN_RESET)
  7.   {
  8.     bitstatus = GPIO_PIN_SET;
  9.   }
  10.   else
  11.   {
  12.     bitstatus = GPIO_PIN_RESET;
  13.   }
  14.   return bitstatus;
  15. }
复制代码

函数描述:

此函数用于读取引脚状态,通过GPIO的IDR寄存器读取。

函数参数:

  第1个参数用于填写使用的端口号,从GPIOA到GPIAK。
  第2个参数是配置选择的引脚,范围GPIO_PIN_0到GPIO_PIN_15。
使用举例:

此函数的使用比较简单,需要调用的时候直接调用即可。

17.3.4 函数HAL_GPIO_WritePin
函数原型:

  1. void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
  2. {
  3.   /* Check the parameters */
  4.   assert_param(IS_GPIO_PIN(GPIO_Pin));
  5.   assert_param(IS_GPIO_PIN_ACTION(PinState));

  6.   if(PinState != GPIO_PIN_RESET)
  7.   {
  8.     GPIOx->BSRRL = GPIO_Pin;
  9.   }
  10.   else
  11.   {
  12.     GPIOx->BSRRH = GPIO_Pin ;
  13.   }
  14. }
复制代码

函数描述:

此函数用于设置引脚输出高电平或者低电平。使用GPIO的BSRR寄存器进行设置,使用这个寄存器的好处是支持原子操作,由硬件支持的。原子操作的含义是操作过程不会被中断打断,而我们使用GPIO中另一个设置输出的寄存ODR是会被中断打断的。大家看下寄存器赋值操作对应的反汇编,是由多条汇编指令组成的。

函数参数:

  第1个参数用于填写使用的端口号,从GPIOA到GPIAK。
  第2个参数是配置选择的引脚,范围GPIO_PIN_0到GPIO_PIN_15。
  第3个参数用于设置引脚输出高电平还是低电平,GPIO_PIN_RESET表示低电平,GPIO_PIN_SET表示高电平。
使用举例:

此函数的使用比较简单,需要调用的时候直接调用即可。

17.3.5 函数HAL_GPIO_TogglePin
函数原型:

  1. void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
  2. {
  3.   /* Check the parameters */
  4.   assert_param(IS_GPIO_PIN(GPIO_Pin));

  5.   GPIOx->ODR ^= GPIO_Pin;
  6. }
复制代码

函数描述:

此函数用于设置引脚的电平翻转,使用GPIO的ODR寄存器进行设置。

函数参数:

  第1个参数用于填写使用的端口号,从GPIOA到GPIAK。
  第2个参数是配置选择的引脚,范围GPIO_PIN_0到GPIO_PIN_15。
使用举例:

此函数的使用比较简单,需要调用的时候直接调用即可。

17.3.6 函数HAL_GPIO_LockPin
函数原型:

  1. HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
  2. {
  3.   __IO uint32_t tmp = GPIO_LCKR_LCKK;

  4.   assert_param(IS_GPIO_LOCK_INSTANCE(GPIOx));
  5.   assert_param(IS_GPIO_PIN(GPIO_Pin));

  6.   /* 应用IO锁的写入顺序 */
  7.   tmp |= GPIO_Pin;
  8.   /* 设置 LCKx bit(s): LCKK='1' + LCK[15-0] */
  9.   GPIOx->LCKR = tmp;
  10.   /* 复位 LCKx bit(s): LCKK='0' + LCK[15-0] */
  11.   GPIOx->LCKR = GPIO_Pin;
  12.   /* 设置 LCKx bit(s): LCKK='1' + LCK[15-0] */
  13.   GPIOx->LCKR = tmp;
  14.   /* 复位 LCKK bit*/
  15.   tmp = GPIOx->LCKR;

  16. if((GPIOx->LCKR & GPIO_LCKR_LCKK) != RESET)
  17.   {
  18.     return HAL_OK;
  19.   }
  20.   else
  21.   {
  22.     return HAL_ERROR;
  23.   }
  24. }
复制代码

函数描述:

此函数用于锁住GPIO引脚所涉及到的寄存器,这些寄存器包括GPIOx_MODER,GPIOx_OTYPER,GPIOx_OSPEEDR,GPIOx_PUPDR,GPIOx_AFRL 和 GPIOx_AFRH。

函数参数:

  第1个参数用于填写使用的端口号,从GPIOA到GPIAK。
  第2个参数是配置选择的引脚,范围GPIO_PIN_0到GPIO_PIN_15。
注意事项:

此函数是锁住用户设置的引脚所对应的寄存器某些位,并不是把整个寄存器都锁住了。
一旦锁住后,就不能再修改,只有复位后才可以重新配置。
使用举例:

此函数的使用比较简单,需要调用的时候直接调用即可。

17.4 如何使用HAL库的GPIO驱动
使用方法由HAL库提供(本章17.3.1小节提供的例子就是这种方式):

  第1步:使能GPIO所在总线的AHB时钟,__HAL_RCC_GPIOx_CLK_ENABLE()。

  第2步:通过函数HAL_GPIO_Init()配置GPIO。

(1)    通过结构体GPIO_InitTypeDef的成员Mode配置输入、输出、模拟等模式。

(2)    通过结构体GPIO_InitTypeDef的成员Pull配置上拉、下拉电阻。

(3)    通过结构体GPIO_InitTypeDef的成员Speed配置GPIO速度等级。

(4)    如果选择了复用模式,那么就需要配置结构体GPIO_InitTypeDef的成员Alternate。

(5)    如果引脚功能用于ADC、DAC的话,需要配置引脚为模拟模式。

(6)    如果是用于外部中断/事件,结构体GPIO_InitTypeDef的成员Mode可以配置相应模式,相应的上升沿、下降沿或者双沿触发也可以选择。

  第3步:如果配置了外部中断/事件,可以通过函数HAL_NVIC_SetPriority设置优先级,然后调用函数HAL_NVIC_EnableIRQ使能此中断。

  第4步:输入模式读取引脚状态可以使用函数HAL_GPIO_ReadPin。

  第5步:输出模式设置引脚状态可以调用函数HAL_GPIO_WritePin()和HAL_GPIO_TogglePin。



另外注意下面三个问题:

  系统上电复位后,GPIO默认是模拟模式,除了JTAG相关引脚。
  关闭LSE的话,用到的两个引脚OSC32_IN和OSC32_OUT(分别是PC14,PC15)可以用在通用IO,如果开启了,就不能再做GPIO。
  关闭HSE的话,用到的两个引脚OSC_IN和OSC_OUT(分别是PH0,PH1)可以用在通用IO,如果开启了,就不能再做GPIO。
17.5 总结
本章节就为大家讲解这么多,建议大家将GPIO的驱动源码结合参考手册中的寄存器通读一遍,对于我们后面章节的学习大有裨益。


收藏 评论0 发布时间:2021-10-29 23:33

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版