STM32使用HAL库自带延时函数HAL_Delay时产生1ms误差

2020/10/02 10:49
阅读数 2K

最近要在stm32f103上写一个pwm编解码程序,要对pwm脉宽进行精确计时,无意间发现使用HAL库自带延时函数产生的延时存在+1ms的误差,即:

HAL_Delay(x);
实际延时时间为(x+1)ms

比如在主循环中加入程序:

		HAL_Delay(1);
		HAL_GPIO_TogglePin(LED_GPIO_Port, GPIO_PIN_13);

烧录程序后使用示波器观察方波波形:
在这里插入图片描述
可以看到方波周期为4ms,相邻跳变之间的时间差为2ms,存在+1ms的误差

实际使用中如果延时时间为几百ms或几s,1ms的误差并没有太大影响,而遇到延时时间非常短的情况则会产生巨大影响。

分析HAL_Delay函数定义

观察HAL_Delay函数在stm32f1xx_hal.c中的定义:

/**
  * @brief This function provides minimum delay (in milliseconds) based
  *        on variable incremented.
  * @note In the default implementation , SysTick timer is the source of time base.
  *       It is used to generate interrupts at regular time intervals where uwTick
  *       is incremented.
  * @note This function is declared as __weak to be overwritten in case of other
  *       implementations in user file.
  * @param Delay specifies the delay time length, in milliseconds.
  * @retval None
  */
__weak void HAL_Delay(uint32_t Delay)
{
   
   
  uint32_t tickstart = HAL_GetTick();
  uint32_t wait = Delay;

  /* Add a freq to guarantee minimum wait */
  if (wait < HAL_MAX_DELAY)
  {
   
   
    wait += (uint32_t)(uwTickFreq);
  }

  while ((HAL_GetTick() - tickstart) < wait)
  {
   
   
  }
}

基本思路是在进入函数时读取当前的tick值(以ms形式储存至tickstart变量),之后为了满足最低延时要求给wait变量+uwTickFreq,最后不断查询tick值,直到当前tick值大于wait变量,退出函数。
查看全局变量uwTickFreq的定义,其数值为systick时钟的默认频率(1khz):

HAL_TickFreqTypeDef uwTickFreq = HAL_TICK_FREQ_DEFAULT;  /* 1KHz */

HAL_TICK_FREQ_DEFAULT=1U(即无符号整型1):

typedef enum
{
   
   
  HAL_TICK_FREQ_10HZ         = 100U,
  HAL_TICK_FREQ_100HZ        = 10U,
  HAL_TICK_FREQ_1KHZ         = 1U,
  HAL_TICK_FREQ_DEFAULT      = HAL_TICK_FREQ_1KHZ
} HAL_TickFreqTypeDef;

可以发现,HAL库函数为了防止无意义延时(即0ms延时)的产生,在HAL_Delay函数传入参数之后会对参数加1。 如果使用HAL库默认延时函数进行延时,实际延时时间将会比预期时间多1ms。换句话说,HAL_Delay函数至少会产生1ms的延时。

重定义HAL_Delay函数

由于HAL_Delay为虚函数,用户可根据实际需要进行重定义,所以可以重新定义延时函数为如下形式,在保留原有功能的基础上消除这个误差:

void HAL_Delay(uint32_t Delay)
{
   
   
  uint32_t tickstart = HAL_GetTick();
  uint32_t wait = Delay;

  if (wait == 0)
  {
   
   
	wait += 1U;
  }
  
  while ((HAL_GetTick() - tickstart) < wait)
  {
   
   
  }
}

但是,由于系统中其他地方也会用到这个函数,所以不建议对其进行重定义
比较稳妥的做法是手动定义新函数来实现延时功能

附:us延时函数

void Delay_us(int16_t nus) 
{
   
   
  int32_t temp; 
  SysTick->LOAD = nus*9; //72MHz
  SysTick->VAL=0X00;
  SysTick->CTRL=0X01;
  do 
  {
   
    
    temp=SysTick->CTRL;
  }
  while((temp&0x01)&&(!(temp&(1<<16))));
     
  SysTick->CTRL=0x00; 
  SysTick->VAL =0X00; 
}

欢迎批评指正!如果你有更好的方法或我的文章存在错误请留言告诉我! XD

展开阅读全文
pwm
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部