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的内容,可以参考这里:

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

 

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

参考资料:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值