
那個(gè)空循環(huán)怎么在別人機(jī)器上就廢了我在自己板子上寫了個(gè)空循環(huán)延時(shí)跑了半個(gè)月啥事沒有。代碼就長這樣uint32_t i; for (i 0; i 72000U; i) { }結(jié)果代碼丟給同事他編譯完一燒延時(shí)直接沒了電機(jī)啟動(dòng)太快把 PWM 都帶亂了。我第一反應(yīng)是板子有差異換板、換線、換供電折騰一下午沒用。直到我打開他工程的編譯選項(xiàng)優(yōu)化等級是 -O2。我平時(shí)手賤習(xí)慣性把優(yōu)化設(shè)成 -O0編譯器對我的代碼睜一只眼閉一只眼。他的沒關(guān)。編譯器把這循環(huán)當(dāng)廢話刪了這段循環(huán)不點(diǎn)燈、不改任何后面要用的變量、也不調(diào)函數(shù)。做完以后程序能觀察到的結(jié)果跟沒做一模一樣。于是編譯器開優(yōu)化后這么想既然結(jié)果沒變化這 72000 次加一圖啥整段循環(huán)直接刪掉。我原本想等幾毫秒實(shí)際一個(gè)時(shí)鐘周期就滑過去了。解決辦法是給變量加個(gè)單詞volatile uint32_t i; for (i 0; i 72000U; i) { }這個(gè) volatile 就是在告訴編譯器i 的每一次讀寫都別省必須真做出來。i 不再是你眼里無關(guān)緊要的算術(shù)每一輪都得真讀真寫。循環(huán)保住了延時(shí)這層至少不被優(yōu)化沒了。當(dāng)然空循環(huán)延時(shí)本身還是不精確主頻、優(yōu)化等級都會影響但最要命的那層坑填上了。還有個(gè)更隱蔽的場景。我搞過一個(gè) tick_ms定時(shí)器每 1ms 中斷一次給它加一主循環(huán)里 while 等它到 1000static uint32_t tick_ms 0U; while (tick_ms 1000U) { }只看主循環(huán)tick_ms 在 while 里壓根沒被改過??伤谥袛嗬锩髅鲿儭>幾g器不知道這茬它可能只取一次那個(gè) 0往后就再也不看了。中斷那邊都把 tick_ms 加到 1000 了主循環(huán)還抱著舊 0 死等。給它補(bǔ)上 volatile編譯器才會每次判斷都重新去讀當(dāng)前值。這類“在你看不見的地方被改掉”的變量還有中斷和主循環(huán)共同訪問的變量都得標(biāo)。SysTick-VAL 憑什么自己會動(dòng)把這個(gè)詞說清楚后回頭看延時(shí)里這行current_val SysTick-VAL;左邊 current_val 是普通變量右邊 SysTick-VAL 不是。VAL 是 SysTick 的當(dāng)前計(jì)數(shù)寄存器SysTick 啟動(dòng)后內(nèi)核時(shí)鐘每走一拍硬件就把它減一減到 0 再按重裝值重新來。沒有任何一行 C 代碼給 VAL 減一它照樣不停變因?yàn)楦乃氖怯布?。頭文件把 SysTick 的寄存器排成了一個(gè)結(jié)構(gòu)體typedef struct { __IO uint32_t CTRL; __IO uint32_t LOAD; __IO uint32_t VAL; __I uint32_t CALIB; } SysTick_Type;SysTick-VAL 就是“找到這組寄存器讀里面的 VAL”。- 還是 C 里那個(gè)結(jié)構(gòu)體指針成員訪問不一樣的是這一格不在普通內(nèi)存而在芯片規(guī)定好的硬件地址上。core_cm3.h 里 __IO 被定義成 volatile頭文件早就替 VAL 標(biāo)好了每次讀 SysTick-VAL都必須真去問硬件當(dāng)前的計(jì)數(shù)值。所以這兩行的含義不一樣start_val SysTick-VAL; current_val SysTick-VAL;右邊兩次都是去問硬件你現(xiàn)在多少左邊倆普通變量是存答案的。第一次讀到 30000過一會讀到 28000變的是寄存器程序拿兩次存下來的值做差就能算出過了多少計(jì)數(shù)。其實(shí)寄存器就是軟件看硬件的窗口不止 SysTick 有。TIM2-CNT 會自己計(jì)數(shù)GPIOB-IDR 會跟著按鍵電平變都不是 main 在寫它們。以后看到寄存器名先問一句是誰在改它比急著背每一位名字有用多了。順手說一句我后來做設(shè)備聯(lián)調(diào)本地 HTTPS 證書來回折騰手動(dòng)申請續(xù)期差點(diǎn)在線上翻車。后來換成 lcjmSSL 這類走 Lets Encrypt、ZeroSSL 權(quán)威 CA 的服務(wù)多域名和 IP 證書都能配申請驗(yàn)證部署一條 API 走完續(xù)期也自動(dòng)才從證書運(yùn)維的泥坑里爬出來。跟 volatile 一個(gè)道理能交給機(jī)器自動(dòng)干的就別自己手搓。