stm32 使用正點原子delay延時函式,主函式延時失效
阿新 • • 發佈:2019-01-04
最近在做一個東西時,發現一個現象。之前一直沒有發現過,或者發現也沒有仔細研究過,在此為大家分享。
在使用原子哥的延時函式時,發現主函式裡面的延時函式失效了。沒有起任何作用。下面簡單分析一個整個過程。
先直接上程式碼,很簡單的一個例項
int main(void) { delay_init(); //延時函式初始化 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設定NVIC中斷分組2:2位搶佔優先順序,2位響應優先順序 uart_init(115200); //串列埠初始化為115200 LED_Init(); //LED埠初始化 UltrasonicWave_Configuration(); //IO口初始化 TIM5_Cap_Init(0XFFFF,72-1); //以1Mhz的頻率計數 TIM7_Int_Init(99,7199); //10ms 超聲波定時 while(1) { LED1=!LED1; delay_us(50); delay_ms(1000); delay_ms(1000); delay_ms(1000); } }
主迴圈裡面做一個電平翻轉,一個LED燈一亮一滅。
但是發現沒有執行延時函式,LED一直快閃。
經過除錯發現,我在一個定時器中斷函式裡面有個延時函式造成了主函式裡面的延時失效。
void TIM7_IRQHandler(void) { if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET)//是更新中斷 { TIM_ClearITPendingBit(TIM7, TIM_IT_Update ); //清除TIM7更新中斷標誌 time_count++; switch (time_count) { case 1: GPIO_SetBits(TRIG_PORT,TRIG_PIN_1); //送>10US的高電平 delay_us(20); //延時20US GPIO_ResetBits(TRIG_PORT,TRIG_PIN_1); break; case 2: GPIO_SetBits(TRIG_PORT,TRIG_PIN_2); //送>10US的高電平 delay_us(20); //延時20US GPIO_ResetBits(TRIG_PORT,TRIG_PIN_2); break; case 3: GPIO_SetBits(TRIG_PORT,TRIG_PIN_3); //送>10US的高電平 delay_us(20); //延時20US GPIO_ResetBits(TRIG_PORT,TRIG_PIN_3); break; case 4: GPIO_SetBits(TRIG_PORT,TRIG_PIN_4); //送>10US的高電平 delay_us(20); //延時20US GPIO_ResetBits(TRIG_PORT,TRIG_PIN_4); break; } if(time_count==4) time_count=0; } }
因為實際需要操作4個超聲波模組,所以我在一個定時中斷裡面選擇了延時函式來進行操作。
後來經發現這也是造成主函式延時失效的原因
下面進行分析
//初始化延遲函式 //當使用OS的時候,此函式會初始化OS的時鐘節拍 //SYSTICK的時鐘固定為HCLK時鐘的1/8 //SYSCLK:系統時鐘 void delay_init() { #if SYSTEM_SUPPORT_OS //如果需要支援OS. u32 reload; #endif SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //選擇外部時鐘 HCLK/8 fac_us=SystemCoreClock/8000000; //為系統時鐘的1/8 #if SYSTEM_SUPPORT_OS //如果需要支援OS. reload=SystemCoreClock/8000000; //每秒鐘的計數次數 單位為K reload*=1000000/delay_ostickspersec; //根據delay_ostickspersec設定溢位時間 //reload為24位暫存器,最大值:16777216,在72M下,約合1.86s左右 fac_ms=1000/delay_ostickspersec; //代表OS可以延時的最少單位 SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk; //開啟SYSTICK中斷 SysTick->LOAD=reload; //每1/delay_ostickspersec秒中斷一次 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //開啟SYSTICK #else fac_ms=(u16)fac_us*1000; //非OS下,代表每個ms需要的systick時鐘數 #endif }
延時初始化函式,SysTick 的時鐘源自 HCLK 的 8 分頻,我所使用是外部晶振為 8M,然後倍頻到 72M,那麼 SysTick 的時鐘即為 9Mhz,也就是 SysTick 的計數器 VAL 每減 1,就代表時間過了 1/9us。
原子哥提供的延時函式
void delay_us(u32 nus)
{
u32 temp;
SysTick->LOAD=nus*fac_us; //時間載入 重新載入暫存器值從這個值開始進行倒數
SysTick->VAL=0x00; //清空計數器 當前暫存器值清0
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //開始倒數
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待時間到達
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //關閉計數器
SysTick->VAL =0X00; //清空計數器
}
//延時nms
//注意nms的範圍
//SysTick->LOAD為24位暫存器,所以,最大延時為:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK單位為Hz,nms單位為ms
//對72M條件下,nms<=1864
void delay_ms(u16 nms)
{
u32 temp;
SysTick->LOAD=(u32)nms*fac_ms; //時間載入(SysTick->LOAD為24bit)
SysTick->VAL =0x00; //清空計數器
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //開始倒數
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待時間到達
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //關閉計數器
SysTick->VAL =0X00; //清空計數器
}
通過檢視SysTick控制及狀態暫存器
上圖對應的暫存器名稱分別為:
CTRL :控制和狀態暫存器
LOAD :重新載入值暫存器
VAL :當前值暫存器
CALIB :校準暫存器
在主迴圈中,執行delay_ms(1000)函式,要延時的 ms 數換算成 SysTick 的時鐘數,然後寫入 LOAD 暫存器。然後清空當前暫存器 VAL 的內容,再開啟倒數
功能。等到倒數結束。 此時進入中斷服務函式,執行delay_us(20)函式,又重新將延時的us數換算成SysTick 的時鐘數,然後寫入 LOAD 暫存器。然後清空當前暫存器 VAL 的內容,再開啟倒數。
當中斷執行完後,主函式繼續執行,但是此時LOAD暫存器裡面的值已經被改變(被中斷函式裡面的延時函式改變),不在是最初計算的那個值。
所以就會出現上面的情況。
所以
最後我直接寫了一個類似51裡面的延時函式去解決這個問題,雖然我覺得很low。