zoukankan      html  css  js  c++  java
  • 【STM32H7教程】第17章 STM32H7之GPIO的HAL库API

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980

    第17章       STM32H7之GPIO的HAL库API

    本章教程为大家讲解GPIO(General-purpose I/Os)的API使用和注意事项。GPIO是所有外设里面较容易掌握的,但也是用到最多的。

    配合第15章讲解的各种IO模式再学习本章,更容易理解透彻。

    17.1 初学者重要提示

    17.2 GPIO涉及到的寄存器

    17.3 源文件stm32h7xx_hal_gpio.c

    17.4 如何使用HAL库的GPIO驱动

    17.5 总结

    17.1 初学者重要提示

    1、  如何阅读HAL库源码的问题

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

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

    17.2 GPIO涉及到的寄存器

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

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

    17.3 源文件stm32h7xx_hal_gpio.c

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

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

    17.3.1 函数HAL_GPIO_Init

    函数原型:

    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,此函数主要实现如下功能:

    •   GPIO功能配置。
    •   设置EXTI功能。

    函数参数:

    •   第1个参数用于填写使用的端口号,可以是:
    #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)
    •   第2个形参是GPIO_InitTypeDef类型的结构体变量,这个变量比较重要,要熟练掌握,定义如下:
    typedef struct
    {
      uint32_t Pin;    
      uint32_t Mode; 
      uint32_t Pull;     
      uint32_t Speed;   
      uint32_t Alternate; 
    }GPIO_InitTypeDef;

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

    •   成员Pin用于配置选择的引脚,范围GPIO_PIN_0到GPIO_PIN_15,额外还可以选择GPIO_PIN_All和GPIO_PIN_MASK。
    •   成员Mode可以选择:
    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  /* 外部事件模式,双沿触发检测 */
    •   成员Pull用于配置上拉下拉电阻:
    GPIO_NOPULL         /* 无上拉和下拉电阻 */
    GPIO_PULLUP          /* 带上拉电阻  */
    GPIO_PULLDOWN    /* 带下拉电阻  */
    •   成员Speed用于配置GPIO速度等级,有下面四种可选:
    GPIO_SPEED_FREQ_LOW          /* 低速 */
    GPIO_SPEED_FREQ_MEDIUM      /* 中等速度 */
    GPIO_SPEED_FREQ_HIGH         /* 快速 */
    GPIO_SPEED_FREQ_VERY_HIGH   /* 高速  */
    •   成员Alternate用于配置引脚复用,可选择的复用方式在文件stm32h7xx_hal_gpio_ex.h里面进行了定义,比如串口复用:
    GPIO_AF7_USART1   
    GPIO_AF7_USART2    
    GPIO_AF7_USART3       
    GPIO_AF7_USART6    
    GPIO_AF7_UART7 

    注意事项:

    1. 与F1,F4系列的标准库不同,H7的HAL库已经没有单独的EXTI外部中断设置文件,是将其整合到此函数里面了。
    2. 函数HAL_GPIO_Init对引脚的初始化是把同组16个引脚for循环检测了一遍,效率稍低。所以不推荐下面这种初始化:
    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); 

    17.3.2 函数HAL_GPIO_DeInit

    函数原型:

    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到初始化状态,具体状态看函数原型中的注释即可。

    函数参数:

    •   第1个参数用于填写使用的端口号,可以是:
    #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)
    •   第2个参数是配置选择的引脚,范围GPIO_PIN_0到GPIO_PIN_15。

    使用举例:

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

    17.3.3 函数HAL_GPIO_ReadPin

    函数原型:

    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寄存器读取。

    函数参数:

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

    使用举例:

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

    17.3.4 函数HAL_GPIO_WritePin

    函数原型:

    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是会被中断打断的。大家看下寄存器赋值操作对应的反汇编,是由多条汇编指令组成的。

    函数参数:

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

    使用举例:

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

    17.3.5 函数HAL_GPIO_TogglePin

    函数原型:

    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寄存器进行设置。

    函数参数:

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

    使用举例:

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

    17.3.6 函数HAL_GPIO_LockPin

    函数原型:

    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。

    函数参数:

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

    注意事项:

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

    使用举例:

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

    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。

    另外注意下面三个问题:

    1.   系统上电复位后,GPIO默认是模拟模式,除了JTAG相关引脚。
    2.   关闭LSE的话,用到的两个引脚OSC32_IN和OSC32_OUT(分别是PC14,PC15)可以用在通用IO,如果开启了,就不能再做GPIO。
    3.   关闭HSE的话,用到的两个引脚OSC_IN和OSC_OUT(分别是PH0,PH1)可以用在通用IO,如果开启了,就不能再做GPIO。

    17.5 总结

    本章节就为大家讲解这么多,建议大家将GPIO的驱动源码结合参考手册中的寄存器通读一遍,对于我们后面章节的学习大有裨益。

  • 相关阅读:
    几种常用的曲线
    0188. Best Time to Buy and Sell Stock IV (H)
    0074. Search a 2D Matrix (M)
    0189. Rotate Array (E)
    0148. Sort List (M)
    0859. Buddy Strings (E)
    0316. Remove Duplicate Letters (M)
    0452. Minimum Number of Arrows to Burst Balloons (M)
    0449. Serialize and Deserialize BST (M)
    0704. Binary Search (E)
  • 原文地址:https://www.cnblogs.com/armfly/p/10831770.html
Copyright © 2011-2022 走看看