当前位置:   article > 正文

STM32F4中的有关SysTick和延时函数的理解(HAL库)_hal库systick延迟函数

hal库systick延迟函数

说到单片机的延时函数,对于很多人来说并不陌生,在F4Cube Hal库中不就是HAL_Delay()函数而已么,但是实际上,简单的HAL_Delay函数的实现原理,并不是那么容易看明白。

HAL库中SystemCoreClock的变化过程如下表所示(最终的值取决于在CubeMX中的时钟树配置中的系统时钟SYSCLK

函数【所在文件】SystemCoreClock
SystemInit【startup_stm32f407xx.s】16,000,000

SystemClock_Config【main.c】

HAL_RCC_ClockConfig 【stm32f4xx_hal_rcc.c】

168,000,000(SYSCLK)

最终SystemCoreClock的更新值在stm32f4xx_hal_rcc.c的第708-712行完成,如下

  1. /* Update the SystemCoreClock global variable */
  2. SystemCoreClock = HAL_RCC_GetSysClockFreq() >> AHBPrescTable[(RCC->CFGR & RCC_CFGR_HPRE)>> RCC_CFGR_HPRE_Pos];
  3. /* Configure the source of time base considering new system clocks settings */
  4. HAL_InitTick (TICK_INT_PRIORITY);

STM32F407的时钟树配置如下图:

 

1.HAL_Delay函数

要明白HAL_Delay函数的原理,就不得先去理解SysTick的概念

Cortex -M内核的系列处理器,内部包含了一个SysTick (System tick)定时器,SysTick 是一个24 位的向下计数(倒计)定时器,它的时钟可以是系统时钟HCLK,也可以是对HCLK分频后的时钟。

当计到0 时,将从RELOAD 寄存器中自动重装载定时初值。在倒计时完成时,就会产生一个中断,中断服务函数为SysTick_Handler。当SysTick的频率和RELOAD重装载值(计数值)保持不变,中断就会以固定的时间间隔发生,此时如果在中断服务函数在对其内部预设的某一变量进行累加(或递减),就可以产生延时效果。只要不把它在SysTick 控制及状态寄存器中的使能位清除,就永不停息。

HAL_Delay函数就是利用的SysTick的上述特点

2.采用while()或for循环函数实现延时

详细分析:https://stm32f4-discovery.net/2014/09/precise-delay-counter/

要使用while等循环函数进行延时,就得要知道while一次会执行多长时间,也就是消耗多少个MCU Clock Cycle,对于F4来说,经过测试,一次while(variable--)为4个tick,因此对于毫秒级的延时来说,如果采用16Mhz的SysTick,则16tick为1us,4次简单的while循环为1us。

  1. uint32_t multiplier;
  2. void Delay_Init(void)
  3. {
  4. /* while loop takes 4 cycles */
  5. /* for 1 us delay, we need to divide with 4M */
  6. multiplier = HAL_RCC_GetHCLKFreq() / 4000000;
  7. }
  8. }

最后一行的意思是1us延时,需要的while运行次数(System clock / (1MHz * 4 Tick))。经过测试,在while中的运算与函数调用会消耗10个tick,因此可以在最终延时结果中减去者10个tick就可以降低误差

  1. void DelayUs(uint32_t micros)
  2. {
  3. /* multiply micro with multipliter */
  4. micros = multiplier * micros - 10;
  5. /* 4 cycles for one loop */
  6. while(micros--);
  7. }
  8. void DelayMs(uint32_t mills)
  9. {
  10. /* multiply mills with multipliter */
  11. mills = multiplier * mills * 1000 - 10;
  12. /* 4 cycles for one loop */
  13. while(mills--);
  14. }

3.非中断形式的delay(对SysTick的CTRL进行查询,实现延时)

参考正点原子delay函数,改写为HAL库适用

  1. #include "delay.h"
  2. #include "stm32f4xx_hal.h"
  3. //初始化延迟函数
  4. //当使用OS的时候,此函数会初始化OS的时钟节拍
  5. //SYSTICK的时钟需要通过时钟树配置,可选为HCLK系统时钟,也可8分频HCLK_DIV8系统时钟
  6. //SYSCLK:系统时钟频率HCLK
  7. static uint8_t fac_us=0; //us延时倍乘
  8. void delay_init(void)
  9. {
  10. HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); //SysTick频率为HCLK
  11. fac_us=(uint32_t)HAL_RCC_GetHCLKFreq()/1000000; //不论是否使用OS,fac_us都需要使用
  12. }
  13. //延时nus
  14. //nus为要延时的us数.
  15. //nus:0~190887435(最大值即2^32/fac_us@fac_us=22.5)
  16. void delay_us(uint32_t nus)
  17. {
  18. uint32_t ticks;
  19. uint32_t told,tnow,tcnt=0;
  20. uint32_t reload=SysTick->LOAD; //LOAD的值
  21. ticks=nus*fac_us; //需要的节拍数
  22. told=SysTick->VAL; //刚进入时的计数器值
  23. while(1)
  24. {
  25. tnow=SysTick->VAL;
  26. if(tnow!=told)
  27. {
  28. if(tnow<told)tcnt+=told-tnow; //这里注意一下SYSTICK是一个递减的计数器就可以了.
  29. else tcnt+=reload-tnow+told;
  30. told=tnow;
  31. if(tcnt>=ticks)break; //时间超过/等于要延迟的时间,则退出.
  32. }
  33. };
  34. }
  35. //延时nms
  36. //nms:要延时的ms数
  37. void delay_ms(uint16_t nms)
  38. {
  39. uint32_t i;
  40. for(i=0;i<nms;i++) delay_us(1000);
  41. }

在STM32F4Discovery 开发板的实测结果如下

预期实测
10us15.19us
100us105.31us

 

4.dwt_stm32_delay.c/h(HAL库)

关于DWT的内容,可以参考这里:

预期实测
10us11.81us
100us102.06us

 

DWT延时函数源码下载地址:https://controllerstech.com/create-1-microsecond-delay-stm32/

参考资料:

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

闽ICP备14008679号