最近要在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
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/146821.html
標籤:其他
上一篇:STM32MP157系列教程連載-硬體設計篇2:STM32MP1微處理器之電源篇
下一篇:excel計算加權和
