我是靠谱客的博主 怡然咖啡,最近开发中收集的这篇文章主要介绍STM32F4中的有关SysTick和延时函数的理解(HAL库),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

说到单片机的延时函数,对于很多人来说并不陌生,在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行完成,如下

 /* Update the SystemCoreClock global variable */
SystemCoreClock = HAL_RCC_GetSysClockFreq() >> AHBPrescTable[(RCC->CFGR & RCC_CFGR_HPRE)>> RCC_CFGR_HPRE_Pos];
/* Configure the source of time base considering new system clocks settings */
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。

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

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

void DelayUs(uint32_t micros)
{
/* multiply micro with multipliter */
micros = multiplier * micros - 10;
/* 4 cycles for one loop */
while(micros--);
}
void DelayMs(uint32_t mills)
{
/* multiply mills with multipliter */
mills = multiplier * mills * 1000 - 10;
/* 4 cycles for one loop */
while(mills--);
}

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

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

#include "delay.h"
#include "stm32f4xx_hal.h"
//初始化延迟函数
//当使用OS的时候,此函数会初始化OS的时钟节拍
//SYSTICK的时钟需要通过时钟树配置,可选为HCLK系统时钟,也可8分频HCLK_DIV8系统时钟
//SYSCLK:系统时钟频率HCLK
static uint8_t
fac_us=0;
//us延时倍乘
void delay_init(void)
{
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); //SysTick频率为HCLK
fac_us=(uint32_t)HAL_RCC_GetHCLKFreq()/1000000;	//不论是否使用OS,fac_us都需要使用
}
//延时nus
//nus为要延时的us数.
//nus:0~190887435(最大值即2^32/fac_us@fac_us=22.5)
void delay_us(uint32_t nus)
{
uint32_t ticks;
uint32_t told,tnow,tcnt=0;
uint32_t reload=SysTick->LOAD;
//LOAD的值
ticks=nus*fac_us;
//需要的节拍数
told=SysTick->VAL;
//刚进入时的计数器值
while(1)
{
tnow=SysTick->VAL;
if(tnow!=told)
{
if(tnow<told)tcnt+=told-tnow;	//这里注意一下SYSTICK是一个递减的计数器就可以了.
else tcnt+=reload-tnow+told;
told=tnow;
if(tcnt>=ticks)break;
//时间超过/等于要延迟的时间,则退出.
}
};
}
//延时nms
//nms:要延时的ms数
void delay_ms(uint16_t nms)
{
uint32_t i;
for(i=0;i<nms;i++) delay_us(1000);
}

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

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

 

4.dwt_stm32_delay.c/h(HAL库)

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

  • [野火]一种Cortex-M内核中的精确延时方法-ns级别-DWT
  • https://www.youtube.com/watch?v=iq6j1hGTVR8
预期实测
10us11.81us
100us102.06us

 

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

参考资料:

  • STM32固件库使用手册的中文翻译版.pdf
  • https://forum.mianbaoban.cn/topic/61447_1_1.html
  • https://blog.csdn.net/qq_17351161/article/details/89216473
  • https://stm32f4-discovery.net/2014/09/precise-delay-counter/
  • https://www.cnblogs.com/ollie-lin/p/10057884.html

最后

以上就是怡然咖啡为你收集整理的STM32F4中的有关SysTick和延时函数的理解(HAL库)的全部内容,希望文章能够帮你解决STM32F4中的有关SysTick和延时函数的理解(HAL库)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(71)

评论列表共有 0 条评论

立即
投稿
返回
顶部