<center id="qkqgy"><optgroup id="qkqgy"></optgroup></center>
  • <menu id="qkqgy"></menu>
    <nav id="qkqgy"></nav>
    <xmp id="qkqgy"><nav id="qkqgy"></nav>
  • <xmp id="qkqgy"><menu id="qkqgy"></menu>
    <menu id="qkqgy"><menu id="qkqgy"></menu></menu>
    <tt id="qkqgy"><tt id="qkqgy"></tt></tt>

  • <>STM32 HAL us delay(微秒延時)的指令延時實現方式及優化

    STM32的HAL庫,直接提供了1ms延時的實現函數HAL_Delay()。其原理是系統在上電后時鐘配置階段,配置了1ms產生一次中斷,然后對一個32位寄存器uwTick逐次加1。HAL_Delay(x)函數執行時,會讀取當前的uwTick值,并循環讀取不斷增加的uwTick值,直到uwTick增加了x后退出循環。

    要實現us級延時,可以從中斷方式進行,如修改系統時鐘中斷配置,將系統時鐘1ms中斷改為1us中斷,也可以用一個TIM定時器產生1us中斷來計數實現1us級延時。但如果系統的業務時序比較緊張,太頻繁的中斷可能引入某些不良時序風險。在這種情況下,采用指令延時實現1us級延時是一種方式,但是需要注意,指令延時不是時鐘延時,并非一個指令延時對應一個時鐘延時,通常一個指令周期需要多個時鐘周期實現,因此系統時鐘的配置頻率不同,也會影響一個指令周期的實現時間。采用循環語句進行指令延時的實現時,循環語句里的循環與否判斷部分,也會產生指令周期的執行延時,需要綜合考慮進去。因此,采用指令延時實現更精確1us級別延時需要進行特殊的設計。本代碼采用的初步測定–優化–應用的方式:

    <>us級延時設計
    __IO float usDelayBase; void PY_usDelayTest(void) { __IO uint32_t firstms,
    secondms; __IO uint32_t counter = 0; firstms = HAL_GetTick()+1; secondms =
    firstms+1; while(uwTick!=firstms) ; while(uwTick!=secondms) counter++;
    usDelayBase= ((float)counter)/1000; } void PY_Delay_us_t(uint32_t Delay) { __IO
    uint32_t delayReg; __IO uint32_t usNum = (uint32_t)(Delay*usDelayBase); delayReg
    = 0; while(delayReg!=usNum) delayReg++; }

    上面的設計,實現了基本的us級延時函數的設計,分為系數測定函數和延時函數,系數測定函數測定1ms內特定語句的執行次數。其中HAL_GetTick()就是讀取uwTick值并作為返回值的函數,和直接調用uwTick一樣。


    其中,循環執行的語句包含uwTick的讀取,secondms的讀取,一次不等于的比較,一次counter的加法。退出循環后的counter對應延時1ms時間要執行這種指令類型的次數。usDelayBase則是對應延時1us時間要要執行這種指令類型的次數,暫時以浮點形式表現。

    在延時實現函數里,將要延時的us數乘以usDelayBase,得到要執行的特定類型指令的次數。然后執行特定類型指令形式的延時,即下面的方式:

    這樣,就實現了1us級別的延時。

    <>us級延時設計優化

    實際上,上面1us延時的實現,還存在一點小的偏差,可以通過下面的函數設計和執行,對usDelayBase進行進一步校準優化。
    void PY_usDelayOptimize(void) { __IO uint32_t firstms, secondms; __IO float coe
    = 1.0; firstms = HAL_GetTick(); PY_Delay_us_t(1000000) ; secondms = HAL_GetTick(
    ); coe = ((float)1000)/(secondms-firstms); usDelayBase = coe*usDelayBase; }

    上述校正原理是,采用us延時函數延時1000000即1秒,那么對應的系統時鐘的1ms延時數理論值是1000,而存在us延時函數偏差累積時,得到的不是1000,這個時候可以產生偏差校正系數coe,從而用coe*usDelayBase得到矯正后的usDelayBase。

    <>us延時函數優化

    上述已實現的us延時函數,對1us的延時,已很接近1us,但并非100%等于1us,因此如延時數比較大,如30分鐘20秒100毫秒50微秒的延時,就會產生一定的累積時間偏差。對于us級精度又要實現長時間延時,用下面的優化函數,原理是將大于等于1ms的部分,用系統時鐘的1ms延時函數代替實現,將小于1ms的微秒部分,用微秒延時函數實現。
    void PY_Delay_us(uint32_t Delay) { __IO uint32_t delayReg; __IO uint32_t msNum
    = Delay/1000; __IO uint32_t usNum = (uint32_t)((Delay%1000)*usDelayBase); if(
    msNum>0) HAL_Delay(msNum); delayReg = 0; while(delayReg!=usNum) delayReg++; }
    這樣,就保證了長延時和短延時都具有良好的us精度。

    另外,HAL庫的HAL_Delay()如果沒有調整中斷優先級,不能用在各中斷的中斷處理函數中,在沒有長延時us級高精度要求情況下,可以把本文中的PY_Delay_us_t()函數用在任意場合包括各中斷處理函數中,如PY_Delay_us_t(1000)等同于HAL_Delay(1)的非中斷方式實現。

    <>使用方式

    * 先定義上述的全局變量usDelayBase和4個函數。
    * 在main函數進入while循環之前,執行 PY_usDelayTest(); 和 PY_usDelayOptimize();
    * 在需要進行us延時的時候,執行PY_Delay_us(x)或 PY_Delay_us_t(x);
    其中x為要延時的微秒數。如執行PY_Delay_us(12) ; 為延時12微秒;PY_Delay_us(1020) ; 為延時1毫秒20微秒。
    <>GPIO驅動注意事項

    在利用延時進行GPIO驅動時(譬如定時進行GPIO輸出翻轉),需要注意GPIO的驅動延時,也即MCU從開始到執行完GPIO的管腳輸出驅動有一定延時(幾us),在這個延時后才會繼續執行后面的代碼。因此在采用TIM定時器中斷和本方案指令延時驅動GPIO翻轉時,就需要注意時序的區別,如果設置成像定時器中斷里驅動GPIO翻轉一樣的延時,則采用指令延時的真實延時會加長,而解決辦法就是相應的減少指令延時的時間,圖示如下:

    以STM32G030F6P6為例,主頻設置為64MHz,驅動100us延時GPIO翻轉的波形為:
    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0); PY_Delay_us_t(100);

    驅動500us延時GPIO翻轉的波形為:
    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0); PY_Delay_us_t(500);

    可以看出有一個大約8us的GPIO輸出延時,如果想要輸出15us的翻轉延時,則需要設置延時為15-8=7us:
    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0); PY_Delay_us_t(7);
    波形驗證:

    <>易用性及擴展性

    * 初始階段執行 PY_usDelayTest() 和
    PY_usDelayOptimize()是為了測得浮點參數值usDelayBase。實際上,如果要節約初始階段的執行時間,可以單獨用一個同樣工程環境的測試工程,測試出usDelayBase,然后在正式工程中將usDelayBase定義為常量,則使用PY_Delay_us(x)或
    PY_Delay_us_t(x)前不再產生初始階段的時間占用,不需要定義和調用PY_usDelayTest()和 PY_usDelayOptimize()。
    PY_Delay_us_t(x)的方式也應用于UCOS, FREE-RTOS,
    RT-THREAD等嵌入式操作系統的微秒延時實現,不受操作系統接管時鐘系統的影響。為了避免操作系統調度打斷延時過程,還可用__disable_irq()與__enable_irq()來保護延時過程PY_Delay_us_t(x)的執行精度。
    * 在主頻高的情況下,指令延時的規格精度可以繼續升級,譬如半微秒級(semi-us)延時的函數設計就可以如下代碼實現: __IO float
    semiusDelayBase; void PY_semiusDelayTest(void) { __IO uint32_t firstms, secondms
    ; __IO uint32_t counter = 0; firstms = HAL_GetTick()+1; secondms = firstms+1;
    while(uwTick!=firstms) ; while(uwTick!=secondms) counter++; semiusDelayBase = ((
    float)counter)/2000; } void PY_Delay_semius_t(uint32_t Delay) { __IO uint32_t
    delayReg; __IO uint32_t semiusNum = (uint32_t)(Delay*semiusDelayBase); delayReg
    = 0; while(delayReg!=semiusNum) delayReg++; } void PY_semiusDelayOptimize(void)
    { __IO uint32_t firstms, secondms; __IO float coe = 1.0; firstms = HAL_GetTick()
    ; PY_Delay_semius_t(2000000) ; secondms = HAL_GetTick(); coe = ((float)1000)/(
    secondms-firstms); semiusDelayBase = coe*semiusDelayBase; } void PY_Delay_semius
    (uint32_t Delay) { __IO uint32_t delayReg; __IO uint32_t msNum = Delay/2000;
    __IOuint32_t semiusNum = (uint32_t)((Delay%2000)*semiusDelayBase); if(msNum>0)
    HAL_Delay(msNum); delayReg = 0; while(delayReg!=semiusNum) delayReg++; }
    使用方式一致,通過執行 PY_semiusDelayTest(); 和
    PY_semiusDelayOptimize();獲得浮點參數值semiusDelayBase,就可以通過PY_Delay_semius(x)或
    PY_Delay_semius_t(x)調用半微秒精度延時,如PY_Delay_semius_t(3)為延遲1.5微秒。

    –End–

    技術
    下載桌面版
    GitHub
    百度網盤(提取碼:draw)
    Gitee
    云服務器優惠
    阿里云優惠券
    騰訊云優惠券
    華為云優惠券
    站點信息
    問題反饋
    郵箱:ixiaoyang8@qq.com
    QQ群:766591547
    關注微信
    巨胸美乳无码人妻视频