当前位置:   article > 正文

STM32之CubeMX学习笔记(5)库文件解读(以stm32f4xx_hal_gpio为例)_stm32cubemx 库文件

stm32cubemx 库文件


绪论

与51单片机对比,Stm32在使用上最显著的区别是添加了库函数,或者说叫标准库、HAL库、LL库等等。更专业一点叫做API(Application Programming Interface,应用程序接口)。这些API是开发者已经帮我们整理好的可以直接用的某些功能。

很多同学,包括我,在一开始学习stm32的时候,并没有重视这些库函数的学习。其实这些库函数的重要性,犹如公式原理之于理工科,成语诗词之于文学,各种符号之于C。它们是使用好Stm32的基础,是搭好软件系统的一块块积木。所以我们一定要对这些常用的库函数有一个熟悉的印象。

然后又到了学习的瓶颈:英语翻译、缩写意思、程序结构、实现方式等等。关于函数大括号中内容,也就是它是如何实现功能的,我觉得这个属于进阶内容,这些东西在结合数据手册将会是我们在解决一些Bug的有力工具,但这篇文章我们不主要讲这个。我们从最简单的翻译看懂,看懂翻译,知道每个函数是怎么用的,用在什么地方,这才是首先要学的,也是能很快提高代码编写效率的。其中,有很多库函数中有嵌套的结构,我也会稍微讲一下嵌套之后的函数结构。另外,这些库函数解读能力也是和大家的C语言能力挂钩的,C学的越好,理解得越深,代码就看的更加透彻。在解读代码时提高C语言水平,用更好的对C语言的理解来更透彻地解读代码。

最后,我简单说一下我现在的水平。看得懂结构,没空去对照数据手册去看位操作,所有涉及芯片内存操作的,我就基本会不看了,我相信ST的工程师,不会把内存地址写错的。但是有空还是要看看,这对提高C语言高级操作技能很有效。

我觉得把库文件当作一本小词典来对待就行,要简单地浏览一遍,要用到的时候,也要很快地去找到相应函数在词典的地方。能掌握到这种程度,我觉得对于stm32就有一个很高的应用水平了。

stm32f4xx_hal_gpio.c翻译

开头注释

/*
  @file    stm32f4xx_hal_gpio.c
  @author  MCD Application Team
  @brief   GPIO HAL module driver.
 This file provides firmware functions to manage the following 
 functionalities of the General Purpose Input/Output (GPIO) peripheral:
 + Initialization and de-initialization functions
 + IO operation functions
*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

文件:stm32f4xx_hal_gpio.c //stm32f4xx芯片的hal库关于gpio的库文件(源文件)
作者:MCD Application Team //MCD 应用团队
简要说明:GPIO HAL模块驱动。这个文件提供固件函数去管理以下事情:通用型之输入输出(GPIO)设备的功能:+初始化与恢复初始化函数、+IO操作函数。

/*
  GPIO Peripheral features
  [..] 
  Subject to the specific hardware characteristics of each I/O port listed in the datasheet, 
  each port bit of the General Purpose IO (GPIO) Ports, can be individually configured by 
  software
  in several modes:
  (+) Input mode 
  (+) Analog mode
  (+) Output mode
  (+) Alternate function mode
  (+) External interrupt/event lines

  [..]  
  During and just after reset, the alternate functions and external interrupt  
  lines are not active and the I/O ports are configured in input floating mode.
  
  [..]   
  All GPIO pins have weak internal pull-up and pull-down resistors, which can be 
  activated or not.

  [..]
  In Output or Alternate mode, each IO can be configured on open-drain or push-pull
  type and the IO speed can be selected depending on the VDD value.

  [..]  
  All ports have external interrupt/event capability. To use external interrupt 
  lines, the port must be configured in input mode. All available GPIO pins are 
  connected to the 16 external interrupt/event lines from EXTI0 to EXTI15.
  
  [..]
  The external interrupt/event controller consists of up to 23 edge detectors 
  (16 lines are connected to GPIO) for generating event/interrupt requests (each 
  input line can be independently configured to select the type (interrupt or event) 
  and the corresponding trigger event (rising or falling or both). Each line can 
  also be masked independently. 
  */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

GPIO外围设备功能
[…]
根据数据表中列出的每个I/O端口的特定硬件特性,通用IO(GPIO)端口的每个端口位可以通过软件单独配置。在以下的几个模式:
(+)输入模式
(+)模拟模式
(+)输出模式
(+)代替功能模式(复用模式)
(+)外部中断/事件线
[…]
在复位期间和复位后,交替功能和外部中断线未激活,I/O端口配置为输入浮动模式。
[…]
所有GPIO引脚都有微弱的内部上拉电阻和下拉电阻,可以激活也可以不激活。
[…]
在输出或备用模式下,每个IO可以配置为开漏或推挽型,IO速度可以根据VDD值进行选择。
[…]
所有端口都具有外部中断/事件功能。要使用外部中断线,端口必须配置为输入模式。所有可用的GPIO引脚都连接到EXTI0到EXTI15的16条外部中断/事件线。
[…]
外部中断/事件控制器由多达23个边缘检测器(16条线连接到GPIO)组成,用于生成事件/中断请求,每个输入线可以独立配置,以选择类型(中断或事件)和相应的触发事件(上升或下降或两者)。也可以单独屏蔽每条线。

/*
                       ##### How to use this driver #####
  ==============================================================================  
  [..]
    (#) Enable the GPIO AHB clock using the following function: __HAL_RCC_GPIOx_CLK_ENABLE(). 

    (#) Configure the GPIO pin(s) using HAL_GPIO_Init().
        (++) Configure the IO mode using "Mode" member from GPIO_InitTypeDef structure
        (++) Activate Pull-up, Pull-down resistor using "Pull" member from GPIO_InitTypeDef 
             structure.
        (++) In case of Output or alternate function mode selection: the speed is 
             configured through "Speed" member from GPIO_InitTypeDef structure.
        (++) In alternate mode is selection, the alternate function connected to the IO
             is configured through "Alternate" member from GPIO_InitTypeDef structure.
        (++) Analog mode is required when a pin is to be used as ADC channel 
             or DAC output.
        (++) In case of external interrupt/event selection the "Mode" member from 
             GPIO_InitTypeDef structure select the type (interrupt or event) and 
             the corresponding trigger event (rising or falling or both).

    (#) In case of external interrupt/event mode selection, configure NVIC IRQ priority 
        mapped to the EXTI line using HAL_NVIC_SetPriority() and enable it using
        HAL_NVIC_EnableIRQ().
         
    (#) To get the level of a pin configured in input mode use HAL_GPIO_ReadPin().
            
    (#) To set/reset the level of a pin configured in output mode use 
        HAL_GPIO_WritePin()/HAL_GPIO_TogglePin().
    
    (#) To lock pin configuration until next reset use HAL_GPIO_LockPin().

                 
    (#) During and just after reset, the alternate functions are not 
        active and the GPIO pins are configured in input floating mode (except JTAG
        pins).
  
    (#) The LSE oscillator pins OSC32_IN and OSC32_OUT can be used as general purpose 
        (PC14 and PC15, respectively) when the LSE oscillator is off. The LSE has 
        priority over the GPIO function.
  
    (#) The HSE oscillator pins OSC_IN/OSC_OUT can be used as 
        general purpose PH0 and PH1, respectively, when the HSE oscillator is off. 
        The HSE has priority over the GPIO function.
  
  @endverbatim
  ******************************************************************************
  */ 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

####如何使用这些驱动####
[…]
(#)启用GPIO AHB clock需要使用以下的函数:__HAL_RCC_GPIOx_CLK_ENABLE()。

(#)配置GPIO引脚需要使用HAL_GPIO_Init()。
(++)使用GPIO_InitTypeDef结构中的“mode”成员配置IO模式。
(++)使用GPIO_InitTypeDef结构中的“Pull”成员激活上拉、下拉电阻。
(++)如果选择输出或备用功能模式:速度通过GPIO_InitTypeDef结构中的“速度”成员配置。
(++)当引脚用作ADC通道或DAC输出时,需要模拟模式。
(++)在外部中断/事件选择的情况下,GPIO_InitTypeDef结构中的“Mode”成员选择类型(中断或事件)和相应的触发事件(上升或下降或两者)。

(#)在选择外部中断/事件模式的情况下,使用HAL_NVIC_SetPriority()配置映射到EXTI行的NVIC IRQ优先级,并使用HAL_NVIC_EnableIRQ()启用它。

(#)使用HAL_GPIO_ReadPin()获取输入模式中配置的管脚级别。

(#)要设置/重置输出模式中配置的管脚级别,请使用HAL_GPIO_WritePin()/HAL_GPIO_TogglePin()。

(#)要锁定管脚配置直到下次重置,请使用HAL_GPIO_LockPin()。

(#)复位期间和复位后,备用功能不激活,GPIO引脚配置为输入浮动模式(JTAG引脚除外)。

(#)LSE振荡器关闭时,可以将LSE振荡器引脚OSC32\uIN和OSC32\OUT用作通用(分别为PC14和PC15)。LSE优先于GPIO功能。

(#)当HSE振荡器关闭时,HSE振荡器引脚OSC_IN/OSC_OUT可分别用作通用PH0和PH1。HSE优先于GPIO功能。

结束解释

头文件声明

/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"

/** @addtogroup STM32F4xx_HAL_Driver
  * @{
  */

/** @defgroup GPIO GPIO
  * @brief GPIO HAL module driver
  * @{
  */

#ifdef HAL_GPIO_MODULE_ENABLED

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/** @addtogroup GPIO_Private_Constants GPIO Private Constants
  * @{
  */

#define GPIO_NUMBER           16U
/**
  * @}
  */
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/* Exported functions --------------------------------------------------------*/
/** @defgroup GPIO_Exported_Functions GPIO Exported Functions
  * @{
  */

/** @defgroup GPIO_Exported_Functions_Group1 Initialization and de-initialization functions
  *  @brief    Initialization and Configuration functions
  *
@verbatim 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

头文件包含、硬件软开关检测 以及 私人定义

各种功能函数

===============================================================================
              ##### Initialization and de-initialization functions #####
 ===============================================================================
  [..]
    This section provides functions allowing to initialize and de-initialize the GPIOs
    to be ready for use.
 
@endverbatim
  * @{
  */


/**
  * @brief  Initializes the GPIOx peripheral according to the specified parameters in the GPIO_Init.
  * @param  GPIOx where x can be (A..K) to select the GPIO peripheral for STM32F429X device or
  *                      x can be (A..I) to select the GPIO peripheral for STM32F40XX and STM32F427X devices.
  * @param  GPIO_Init pointer to a GPIO_InitTypeDef structure that contains
  *         the configuration information for the specified GPIO peripheral.
  * @retval None
  */
void HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init)
{
  uint32_t position;
  uint32_t ioposition = 0x00U;
  uint32_t iocurrent = 0x00U;
  uint32_t temp = 0x00U;

  /* Check the parameters */
  assert_param(IS_GPIO_ALL_INSTANCE(GPIOx));
  assert_param(IS_GPIO_PIN(GPIO_Init->Pin));
  assert_param(IS_GPIO_MODE(GPIO_Init->Mode));

  /* Configure the port pins */
  for(position = 0U; position < GPIO_NUMBER; position++)
  {
    /* Get the IO position */
    ioposition = 0x01U << position;
    /* Get the current IO position */
    iocurrent = (uint32_t)(GPIO_Init->Pin) & ioposition;

    if(iocurrent == ioposition)
    {
      /*--------------------- GPIO Mode Configuration ------------------------*/
      /* In case of Output or Alternate function mode selection */
      if(((GPIO_Init->Mode & GPIO_MODE) == MODE_OUTPUT) || \
          (GPIO_Init->Mode & GPIO_MODE) == MODE_AF)
      {
        /* Check the Speed parameter */
        assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
        /* Configure the IO Speed */
        temp = GPIOx->OSPEEDR; 
        temp &= ~(GPIO_OSPEEDER_OSPEEDR0 << (position * 2U));
        temp |= (GPIO_Init->Speed << (position * 2U));
        GPIOx->OSPEEDR = temp;

        /* Configure the IO Output Type */
        temp = GPIOx->OTYPER;
        temp &= ~(GPIO_OTYPER_OT_0 << position) ;
        temp |= (((GPIO_Init->Mode & OUTPUT_TYPE) >> OUTPUT_TYPE_Pos) << position);
        GPIOx->OTYPER = temp;
       }

      if((GPIO_Init->Mode & GPIO_MODE) != MODE_ANALOG)
      {
        /* Check the parameters */
        assert_param(IS_GPIO_PULL(GPIO_Init->Pull));
        
        /* Activate the Pull-up or Pull down resistor for the current IO */
        temp = GPIOx->PUPDR;
        temp &= ~(GPIO_PUPDR_PUPDR0 << (position * 2U));
        temp |= ((GPIO_Init->Pull) << (position * 2U));
        GPIOx->PUPDR = temp;
      }

      /* In case of Alternate function mode selection */
      if((GPIO_Init->Mode & GPIO_MODE) == MODE_AF)
      {
        /* Check the Alternate function parameter */
        assert_param(IS_GPIO_AF(GPIO_Init->Alternate));
        /* Configure Alternate function mapped with the current IO */
        temp = GPIOx->AFR[position >> 3U];
        temp &= ~(0xFU << ((uint32_t)(position & 0x07U) * 4U)) ;
        temp |= ((uint32_t)(GPIO_Init->Alternate) << (((uint32_t)position & 0x07U) * 4U));
        GPIOx->AFR[position >> 3U] = temp;
      }

      /* Configure IO Direction mode (Input, Output, Alternate or Analog) */
      temp = GPIOx->MODER;
      temp &= ~(GPIO_MODER_MODER0 << (position * 2U));
      temp |= ((GPIO_Init->Mode & GPIO_MODE) << (position * 2U));
      GPIOx->MODER = temp;

      /*--------------------- EXTI Mode Configuration ------------------------*/
      /* Configure the External Interrupt or event for the current IO */
      if((GPIO_Init->Mode & EXTI_MODE) != 0x00U)
      {
        /* Enable SYSCFG Clock */
        __HAL_RCC_SYSCFG_CLK_ENABLE();

        temp = SYSCFG->EXTICR[position >> 2U];
        temp &= ~(0x0FU << (4U * (position & 0x03U)));
        temp |= ((uint32_t)(GPIO_GET_INDEX(GPIOx)) << (4U * (position & 0x03U)));
        SYSCFG->EXTICR[position >> 2U] = temp;

        /* Clear Rising Falling edge configuration */
        temp = EXTI->RTSR;
        temp &= ~((uint32_t)iocurrent);
        if((GPIO_Init->Mode & TRIGGER_RISING) != 0x00U)
        {
          temp |= iocurrent;
        }
        EXTI->RTSR = temp;

        temp = EXTI->FTSR;
        temp &= ~((uint32_t)iocurrent);
        if((GPIO_Init->Mode & TRIGGER_FALLING) != 0x00U)
        {
          temp |= iocurrent;
        }
        EXTI->FTSR = temp;

        temp = EXTI->EMR;
        temp &= ~((uint32_t)iocurrent);
        if((GPIO_Init->Mode & EXTI_EVT) != 0x00U)
        {
          temp |= iocurrent;
        }
        EXTI->EMR = temp;

        /* Clear EXTI line configuration */
        temp = EXTI->IMR;
        temp &= ~((uint32_t)iocurrent);
        if((GPIO_Init->Mode & EXTI_IT) != 0x00U)
        {
          temp |= iocurrent;
        }
        EXTI->IMR = temp;
      }
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141

GPIO初始化函数
输入参数(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)

之后的函数就不一一罗列了,在函数中的操作都是一些比较基层的位带操作,和严谨的逻辑判断来实现函数名对应的功能。不仅GPIO库函数如此,其余库都一样,只是实现的功能不同罢了。不同的功能需要根据硬件,协议等规定来进行编程。之后我会总结一些基于硬件的函数详解,或者某些通信协议进行解释,来作为学习的一部分。

之后的函数
GPIO取消初始化函数
void HAL_GPIO_DeInit(GPIO_TypeDef* GPIOx, uint32_t GPIO_Pin)
GPIO读引脚电平值
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
GPIO写引脚电平值
void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
GPIO翻转引脚电平值
void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
GPIO锁住引脚电平值(不能修改)
HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
GPIO外部中断调用函数(在其中调用EXTI_Callback)
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
GPIO外部中断回馈函数(弱声明,在其中写中断处理代码)
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)

头文件嵌套逻辑

细心的朋友会发现,这个stm32f4xx_hal_gpio.c文件的头文件是stm32f4xx_hal.h,并不是stm32f4xx_hal_gpio.h,怎么会少了个gpio呢。一般写C Source文件都会有配对的头文件,也就是.c和.h配成一队出现,c文件中的函数在头文件中都要声明一遍,以便于主文件调用。

其实问题就在stm32f4xx_hal.h之中,为了做成集成度很高的库函数文件,就需要将功能按从软件高层到硬件底层进行分层,如果把功能写在一个层layer中,就会因为更新功能,使功能全部挤在某一层,这样交叉调用的时候就难免会出问题,这就是屎山代码的由来。

为了轻量化每一层的代码,需要把一些更高层或者更底层的功能移走,让他们合并或自成一层。这样虽然层数比较多,但是每一层都是可读的。就像写一本书,要分章分节分段,如果写的更多,就应该分为上册和下册,不能把所有内容都写到一段里面。

我们转到stm32f4xx_hal.h,可以发现里面的代码都是一些关于芯片选择的,选择完芯片就可以根据芯片选择cortex-M4的各个功能,以适配各个芯片。我们用到的是普通芯片,好像没有什么需要设置的。

stm32f4xx_hal.h中还包含了一个头文件,叫stm32f4xx_hal_conf.h。看到conf就很开心了,它是configuration的缩写,是配置的意思。那其中一定有我们要找的秘密。转到其中,我们看见了一些被注释掉的功能开关,之后是一些被启用的开关,而这些开关启用的模块就是我们在Cubemx中启用的这些功能。
在这里插入图片描述
我们先记住HAL_GPIO_MODULE_ENABLED这个开关,然后往下找就找到了stm32f4xx_hal_gpio.h,在这里包含了这个头文件,虽然九曲十八弯,但这些文件把Cubemx自动选择模块的功能实现了,而且把顶层和底层代码分开,没有因为这个自动选择功能使代码变成屎山。
在这里插入图片描述

总结

1.英文水平的提高可以显著提高看代码的效率。不要看见大段英文就头大,细细翻译会发现每句话都是有用的。
2.库文件中的函数是用来实现不同功能的,具体的功能可以翻译函数名,再看不懂可以看代码周围的注释,或者查库文件相关的手册。
3.头文件的嵌套实现了Cubemx自动选择模块启用的功能,将.c和.h文件分开到两个不同地方,避免硬加代码造成不必要的混乱。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/219909
推荐阅读
相关标签
  

闽ICP备14008679号