对STM32 HAL库的一些思考(二)__weak关键字和systick

__weak关键字

有时候我们阅读HAL源码的时候会发现,很多函数前会有一个__weak的前缀,一开始我对这个前缀很好奇,想知道这到底是什么意思。在IAR for ARM中使用“go to defination”查看该关键字的时候会提示__weak defined on the command line,即在命令行中定义,得,这又回到原点了……

不过一次偶然的机会,在查找C语言相关资料时,我看到它的真实含义,该关键字用于函数前,编译器在链接函数时会优先链接没有该关键字前缀的函数,即变相的,而且是不完全的实现了C++的函数重载特性,示例:

//HAL库微秒延时函数,有__weak关键字即代表可以重载该函数
__weak HAL_Delay(__IO uint32_t delay);
//用户函数
HAL_Delay(__IO uint32_t delay);

以上两个函数,编译器会优先链接下面的函数,即为我们重定义延时函数提供了较大的便利性。


Systick定时器

了解ARM的人对Systick定时器一定不陌生,这是内核自带的定时器,不过我更喜欢称它为心跳定时器,一般在裸机开发时,由它提供整个内核的时基信号,当然也可以替换为外围的定时器,这一点可以在STM32CubeMX中得到验证,此处不赘述。

不过为什么要把systick和__weak关键字扯在一起呢?这当然是有原因的。

如果有阅读过HAL库中关于cortex部分的源码,你会发现关于心跳定时器的每一个函数前都有__weak关键字,以STM32F407为例,这部分源码在stm32f4xx_hal.h/.c和stm32f4xx_hal_cortex.h/.c中,之前网上有很多修改Tick的初始化以达到使HAL_Delay()函数可以进行微秒级延时的例子,这里不多说这种方法,只是阐述一下HAL_Delay()的延时原理。

  1. Tick的默认初始化

    __weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
    {
      /*Configure the SysTick to have interrupt in 1ms time basis*/
      HAL_SYSTICK_Config(SystemCoreClock/1000U);
    
      /*Configure the SysTick IRQ priority */
      HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U);
    
      /* Return function status */
      return HAL_OK;
    }

    经过此函数,心跳定时器被初始化为1ms的时基单元,即每毫秒进入一次Systick中断

  2. Systick中断及其回调函数

        /**
         * stm32f4_it.c
        **/
    
    void SysTick_Handler(void)
    {
          /* USER CODE BEGIN SysTick_IRQn 0 */
    
          /* USER CODE END SysTick_IRQn 0 */
          HAL_IncTick();
          HAL_SYSTICK_IRQHandler();
          /* USER CODE BEGIN SysTick_IRQn 1 */
    
          /* USER CODE END SysTick_IRQn 1 */
    }
    
    /**
     * stm32f4xx_hal.c
     * uwTick是一个全局变量,在该文件中定义
    **/
    __weak void HAL_IncTick(void)
    {
        uwTick++;
    }

    即每毫秒uwTick加一,用于计算Systick产生的节拍数

  3. 获取当前节拍

    /**
     * stm32f4xx_hal.c
     * uwTick是一个全局变量,在该文件中定义
    **/
    
    __weak uint32_t HAL_GetTick(void)
    {
        return uwTick;
    }

    返回当前节拍,如果内核正常运行,则uwTick的值即为已经经过的ms数(不考虑精确度)

  4. 延时函数

    /**
     * stm32f4xx_hal.c
     * uwTick是一个全局变量,在该文件中定义
    **/
    
    __weak void HAL_Delay(__IO uint32_t Delay)
    {
        uint32_t tickstart = HAL_GetTick();
        uint32_t wait = Delay;
    
        /* Add a period to guarantee minimum wait */
        if (wait < HAL_MAX_DELAY)
        {
            wait++;
        }
    
        while((HAL_GetTick() - tickstart) < wait)
        {
        }
    }

    我想这个函数不需要多解释,就是数节拍以确定延时。


既然了解了HAL的延时函数是如何延时的,则我们完全可以重写这些函数以实现花样延时,但是不建议直接修改库文件,有__weak在,就可以在其他文件中书写自己的函数而不是破坏库函数原有结构了。另外,修改了库函数,在使用CubeMX重新生成的时候,所做的修改会消失,这一点要慎重。

你可能感兴趣的:(STM32,C/C++,RT-Thread)