概述
说到单片机的延时函数,对于很多人来说并不陌生,在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 开发板的实测结果如下
预期 | 实测 |
10us | 15.19us |
100us | 105.31us |
4.dwt_stm32_delay.c/h(HAL库)
关于DWT的内容,可以参考这里:
- [野火]一种Cortex-M内核中的精确延时方法-ns级别-DWT
- https://www.youtube.com/watch?v=iq6j1hGTVR8
预期 | 实测 |
10us | 11.81us |
100us | 102.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库)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复