<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應用 > STM下純軟件實現(xiàn)精確定時

          STM下純軟件實現(xiàn)精確定時

          作者: 時間:2016-11-21 來源:網(wǎng)絡(luò) 收藏
          先上測試代碼(普通版)
          #include "stm32f10x.h"
          #include "core_cm3.h"
          __asm int Dellayus(u32 usec)
          {
          ALIGN
          PUSH.W {r1} //2時鐘周期
          MOV r1,#18 //1時鐘周期
          MUL r0,r1 //1時鐘周期
          SUB r0,#3 //1時鐘周期
          loop
          SUBS r0,#1 //1時鐘周期
          BNE loop //如果跳轉(zhuǎn)則為3個周期,不跳則只有1個周期
          POP {r1} //2時鐘周期
          BX lr //3個時鐘周期
          //總共所用周期為(usec*4)-4,此處減4主要用于抵消調(diào)用此函數(shù)的消耗時鐘周期(傳參1時鐘,BLX跳轉(zhuǎn)3時鐘)
          }
          int main(void)
          {
          while(1)
          {
          //運行到此處時32.93us,第1379時鐘周期
          Dellayus(100);
          //運行到此處時132.93us,第8579時鐘周期,運行上條指令共花8579-1379=7200時鐘周期
          //因系統(tǒng)時鐘為72MHz,所以每72時鐘周期所花時間為1微妙,上條指令所花時間
          //等于7200 ÷ 72 = 100微妙,延時完全準確
          Dellayus(100);
          //運行到此處時232.93us,第15779時鐘周期,同樣驗證上面的結(jié)論
          }
          }
          調(diào)試:
          第一個100微妙延時函數(shù)運行前

          第一個100微妙延時函數(shù)運行后

          所花系統(tǒng)時鐘周期8579-1379=7200個系統(tǒng)時鐘,時間精確為0.00013293-0.0003293=0.0001秒,
          也就是100微秒。延時函數(shù)通過軟件模擬以及硬件調(diào)試同樣精確,如果延時參數(shù)大于255微秒,調(diào)用
          時會多耗兩個時鐘周期(0.028微秒)用于從代碼段加載常數(shù)??梢孕薷拇a修正誤差。最大定時不能
          超過(232-1) ÷ 18 ≈ 238.6秒。
          以上方法可以節(jié)約系統(tǒng)定時器資源,避免和其他代碼爭用定時器。通常用于微秒級延時。
          版主的建議非常好,是該增加時鐘參數(shù)以適應不同的時鐘頻率,改了一下代碼,目前軟件仿真測試沒問題,等晚上再用JLink
          測試一下(理論上應該沒問題,已經(jīng)考慮了閃存預取優(yōu)化)。
          #include "stm32f10x.h"
          #include "core_cm3.h"
          __asm void Dellayus(u32 usec,u8 freq) //freq參數(shù)為系統(tǒng)時鐘頻率(SYSCLK),單位MHz,且必須能被4整除,且必須大 于等于16,常用的
          { //有24,36,48,56,72,如果一定要使用8MHz則必須滿足延時時間大于2微秒,但freq一定不要
          //小于8MHz,否則函數(shù)將出現(xiàn)混亂。條件為 ((usec >= 1) && (freq >=16)) || ((usec >= 2) && (freq >= 8))
          ALIGN
          LSR r1,r1,#2 //1時鐘周期,除以單次循環(huán)所用的時鐘數(shù)(4個)即得到延時1微妙所需的循環(huán)數(shù)
          MUL r0,r1 //1時鐘周期
          SUB r0,#3 //1時鐘周期
          NOP //用于匹配延時周期以及使loop循環(huán)處指令在8字節(jié)邊界對齊,提高精度(因為指令預取單元一次預取8字節(jié)指令)
          NOP //所以循環(huán)時都不用再從閃存內(nèi)取指令,避免閃存延時影響延時精度
          loop
          SUBS r0,#1 //1時鐘周期
          BNE loop //延時循環(huán),每次4個時鐘周期,最后一次只需兩個時鐘周期(如果跳轉(zhuǎn)則為3個周期,不跳則只有1個周期)
          NOP
          BX lr //3個時鐘周期
          //本函數(shù)內(nèi)總共所用周期為usec*(freq/4)-2 +9,調(diào)用此函數(shù)的消耗5個時鐘周期(傳參2時鐘,BLX跳轉(zhuǎn)3時鐘)
          } //函數(shù)最低耗時11個時鐘周期,上面usec*(freq/4) - 2為循環(huán)代碼的耗時(此處減2是因為最后一次循環(huán)BNE沒有跳轉(zhuǎn),只消耗1個時鐘比跳轉(zhuǎn)的3個時鐘節(jié)約2個所以減去2才是最終循環(huán)的耗時),9就是其它代碼的耗時
          //比如頻率為8MHz時延時2微秒所需延時周期數(shù)為2*8=16個時鐘,將值帶入上面的公式即為 2 * (8 / 4) - 2 + 9 =11 即為函數(shù)體耗時,再加上5個周期的調(diào)用開銷最終消耗16個時鐘周期,同時這也是最低延時周期數(shù)。
          int main(void)
          {
          while(1)
          {
          //運行到此處時32.93us,第1379時鐘周期
          Dellayus(100,72);
          //運行到此處時132.93us,第8579時鐘周期,運行上條指令共花8579-1379=7200時鐘周期
          //因系統(tǒng)時鐘為72MHz,所以每72時鐘周期所花時間為1微妙,上條指令所花時間
          //等于7200 ÷ 72 = 100微妙,延時完全準確
          Dellayus(100,72);
          //運行到此處時232.93us,第15779時鐘周期,同樣驗證上面的結(jié)論
          }
          }
          示例代碼見附件:
          附件1:軟件延時測試.rar (文件大小: 21 KB 下載次數(shù):218次)

          附件2:軟件延時測試增強版.rar (文件大小: 22 KB 下載次數(shù):309次)
          1、頂,花了不少功夫!
          不過在不同時鐘條件下,應該就又要修改代碼了吧?
          比如36M?8M?
          所以建議加入一個時鐘參數(shù),這樣比較通用一點.
          2、謝謝樓主!,我軟件測試延時的時候并沒有出現(xiàn)當參數(shù)大于255微妙的時候會多消耗2個時鐘周期的情況,一樣很準啊。還有我用J-LINK仿真SW模式,單步,那個SEC沒有變化,不知道是怎么回事。還有一個問題:為什么返回值要用int不太懂匯編
          3、需要將J-LINK設(shè)為JTAG模式才能讀出SEC值,另外返回值用int當時主要是想以后增加函數(shù)功能
          4、這個建議非常好,馬上上個增強版
          5、呵呵,還不如用C的
          voidDelay_us(unsignedintt)
          {
          inti;

          for(i=0;i<144*t;i++){
          ;
          }
          }
          比上面匯編的要準:同時1000個循環(huán),
          匯編用時為:100.844ms
          C及時:100.139ms
          6、搞個-O2優(yōu)化試試,還準不準.
          另外換個時鐘頻率試試,還準不準?
          7、
          //加個編譯項就行了.
          #define OSC (8) //定義為8M
          #define OSC_D ((OSC*144)/8)
          #pragma O3
          void Delay_us(unsigned int t)
          {
          int i;
          for(i=0; i
          ;
          }
          }
          //測試
          //運行時間為100.014ms
          呵呵,純?yōu)橛螒蛑?在這種CPU,延時最好是用原子的方法了.
          8、用原子的戰(zhàn)艦開發(fā)板測試,用樓主這種方法,得到的延時總理理論時間的1.5倍,比如要延遲1秒,實際總是1.5秒。
          懷疑CPU運行在48M,但是我設(shè)置的的確是72M,是不是哪里設(shè)置有問題?
          9、要完全精確的話還得提前關(guān)掉中斷哦……
          不過一般也不需要這么精確的延時,獻上我的,不占用定時器資源哦:

          #defineRT_TICK_PER_SECOND100
          voidhw_tsc_init() //TSC-timestampcounter
          {
          unsignedintcnts;

          cnts=(unsignedint)9000000/RT_TICK_PER_SECOND;

          SysTick->CTRL&=0xfffffffb;//bit2清空,選擇外部時鐘,也即HCLK/8
          SysTick->LOAD=cnts-1;
          SysTick->VAL=0;//writetoclean
          SysTick->CTRL=0x01;//開始倒數(shù)不產(chǎn)生中斷
          }

          voidhw_tick_start()
          {
          unsignedintcnts;

          cnts=(unsignedint)9000000/RT_TICK_PER_SECOND;

          SysTick->CTRL&=0xfffffffb;//bit2清空,選擇外部時鐘,也即HCLK/8
          SysTick->LOAD=cnts-1;
          SysTick->CTRL=0x01;//開始倒數(shù)不產(chǎn)生中斷

          SysTick->CTRL=0x03; //啟動systick并產(chǎn)生中斷
          }
          voidhw_delay_us(unsignedintus)
          {
          unsignedintstart,target,cur;

          while(us>500) //防止us超過systick周期。(假設(shè)systick周期超過500us)
          {
          hw_delay_us(500);
          us-=500;
          }

          start=SysTick->VAL;
          target=(start-us*9);

          if(start
          {
          target+=(9000000/RT_TICK_PER_SECOND);
          do
          {
          cur=SysTick->VAL;
          }while(cur<=start||cur>target);//target不能等于start,否則死循環(huán)。也即us不能等于systick周期
          }
          else//target<=start
          {
          do
          {
          cur=SysTick->VAL;
          }while(target<=start); //假設(shè)target為一個很小的值,則cur有可能在循環(huán)周期內(nèi)溢出而大于target,故須加上cur<=start的條件
          }
          }

          voidhw_delay_ms(unsignedintms)
          {
          while(ms--)
          {
          hw_delay_us(500);
          hw_delay_us(500);
          }
          }
          10、我不用軟件延時的...
          我一直用systick延時。
          11、樓主給力,不過不推薦用樓主這種笨辦法,樓主大可以移植ucos,移植成功后,你想定多久有多久,要多精確有多精確。
          同時,我覺得用定時器實現(xiàn)精確延時比這個方法靠譜得多。
          12、ucos的定時是以系統(tǒng)時基為單位的,比如OS_CFG_TICK_RATE_HZ設(shè)為100時就只能精確到10ms,也就是說高于10ms并且步進為10ms(50ms,500ms能較精確,但55ms就不精確了)可以比較精確定時,那我說延時10us如何用ucos的定時器定時呢?
          13、你可以增大ucos的時鐘節(jié)拍數(shù),然后用OSTimeTick()來實現(xiàn)定時
          14、STM32的定時器多的是,基本都用不完吧



          關(guān)鍵詞: STM純軟件精確定

          評論


          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();