單片機編程過程當中常常用到延時函數,最經常使用的莫過於微秒級延時delay_us( )和毫秒級delay_ms( )。編程
1.普通延時法函數
這個比較簡單,讓單片機作一些可有可無的工做來打發時間,常常用循環來實現,不過要作的比較精準仍是要下一番功夫。代碼以下,重點掌握微秒級的延時,毫秒級的延時能夠直接經過HAL庫提供的HAL_Delay()函數來延時。spa
//粗延時函數,微秒 void delay_us(u16 time) { u16 i=0; while(time--) { i=10; //本身定義 while(i--) ; } } //毫秒級的延時 void delay_ms(u16 time) { u16 i=0; while(time--) { i=12000; //本身定義 while(i--) ; } }
2.SysTick 定時器延時code
CM3 內核的處理器,內部包含了一個SysTick 定時器,SysTick 是一個24 位的倒計數定時器,當計到0 時,將從RELOAD 寄存器中自動重裝載定時初值。只要不把它在SysTick 控制及狀態寄存器中的使能位清除,就永不停息。SysTick 在STM32 的參考手冊裏面介紹的很簡單,其詳細介紹,請參閱《Cortex-M3 權威指南》。blog
這裏面也有兩種方式實現:table
a.中斷方式class
以下,定義延時時間time_delay,SysTick_Config()定義中斷時間段,在中斷中遞減time_delay,從而實現延時。變量
volatile unsigned long time_delay; // 延時時間,注意定義爲全局變量 //延時n_ms void delay_ms(volatile unsigned long nms) { //SYSTICK分頻--1ms的系統時鐘中斷 if (SysTick_Config(SystemFrequency/1000)) { while (1); } time_delay=nms;//讀取定時時間 while(time_delay); SysTick->CTRL=0x00; //關閉計數器 SysTick->VAL =0X00; //清空計數器 } //延時nus void delay_us(volatile unsigned long nus) { //SYSTICK分頻--1us的系統時鐘中斷 if (SysTick_Config(SystemFrequency/1000000)) { while (1); } time_delay=nus;//讀取定時時間 while(time_delay); SysTick->CTRL=0x00; //關閉計數器 SysTick->VAL =0X00; //清空計數器 } //在中斷中將time_delay遞減。實現延時 void SysTick_Handler(void) { if(time_delay) time_delay--; }
b.非中斷方式循環
主要仿照原子的《STM32不徹底手冊》。SYSTICK 的時鐘固定爲HCLK 時鐘的1/8,在這裏咱們選用內部時鐘源72M,因此SYSTICK的時鐘爲9M,即SYSTICK定時器以9M的頻率遞減。SysTick 主要包含CTRL、LOAD、VAL、CALIB 等4 個寄存器,定時器
SysTick->CTRL
位段 | 名稱 | 類型 | 復位值 |
描述 |
16 | COUNTFLAG | R | 0 | 若是在上次讀本寄存器後systick已爲0,則該位爲1,若 讀該位自動清零 |
2 | CLKSOURCE |
RW | 0 | 0:外部時鐘源 1:內部時鐘 |
1 | TICKINT |
RW | 0 | 0:減到0無動做;1:減到0產生systick異常請求 |
0 | ENABLE | RW | 0 | systick定時器使能位 |
SysTick-> LOAD
位段 |
名稱 |
類型 |
復位值 |
描述 |
23:0 |
RELOAD |
RW |
0 |
減到0時被從新裝載的值 |
SysTick-> VAL
位段 |
名稱 |
類型 |
復位值 |
描述 |
23:0 |
CURRENT |
RW |
0 |
讀取時返回當前倒計數的值,寫則清零,同時還會清除在systick控制及狀態寄存器中的COUNTFLAG 標誌 |
SysTick-> CALIB 不經常使用,在這裏咱們也用不到,故不介紹了。
程序以下,至關於查詢法。
//仿原子延時,不進入systic中斷 void delay_us(u32 nus) { u32 temp; SysTick->LOAD = 9*nus; SysTick->VAL=0X00;//清空計數器 SysTick->CTRL=0X01;//使能,減到零是無動做,採用外部時鐘源 do { temp=SysTick->CTRL;//讀取當前倒計數值 }while((temp&0x01)&&(!(temp&(1<<16))));//等待時間到達 SysTick->CTRL=0x00; //關閉計數器 SysTick->VAL =0X00; //清空計數器 } void delay_ms(u16 nms) { u32 temp; SysTick->LOAD = 9000*nms; SysTick->VAL=0X00;//清空計數器 SysTick->CTRL=0X01;//使能,減到零是無動做,採用外部時鐘源 do { temp=SysTick->CTRL;//讀取當前倒計數值 }while((temp&0x01)&&(!(temp&(1<<16))));//等待時間到達 SysTick->CTRL=0x00; //關閉計數器 SysTick->VAL =0X00; //清空計數器 }
三種方式各有利弊,第一種方式容易理解,但不太精準。第二種方式採用庫函數,編寫簡單,因爲中斷的存在,不利於在其餘中斷中調用此延時函數。第三種方式直接操做寄存器,看起來比較繁瑣,其實也不難,同時克服了以上兩種方式的缺點,我的感受比較好用。