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

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

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

好比在主循环中加入程序:ide

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

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

实际使用中若是延时时间为几百ms或几s,1ms的偏差并无太大影响,而遇到延时时间很是短的状况则会产生巨大影响。函数

分析HAL_Delay函数定义

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

/** * @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):spa

HAL_TickFreqTypeDef uwTickFreq = HAL_TICK_FREQ_DEFAULT;  /* 1KHz */

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

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的延时。code

重定义HAL_Delay函数

因为HAL_Delay为虚函数,用户可根据实际须要进行重定义,因此能够从新定义延时函数为以下形式,在保留原有功能的基础上消除这个偏差:xml

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)
  { 
 
  
  }
}

可是,因为系统中其余地方也会用到这个函数,因此不建议对其进行重定义
比较稳妥的作法是手动定义新函数来实现延时功能blog

附: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