OK02 課程構建於 OK01 課程的基礎上,通過不停地開啟和關閉 OK 或 ACT LED 指示燈來實現閃爍。假設你已經有了 作業系統的程式碼,它將是這一節課的基礎。
等待是作業系統開發中非常有用的部分。作業系統經常發現自己無事可做,以及必須要延遲。在這個例子中,我們希望通過等待,讓 LED 燈開啟、關閉的閃爍可以看到。如果你只是開啟和關閉它,你將看到這個視覺效果,因為計算機每秒種可以開啟和關閉它好幾千次(LCTT 譯註:視覺暫留效應會使你難以發覺它的閃爍)。在後面的課程中,我們將看到精確的等待,但是現在,我們只要簡單地去消耗時間就足夠了。
mov r2,#0x3F0000wait1$:sub r2,#1cmp r2,#0bne wait1$
sub reg,#val
從暫存器reg
中的值上減去數位val
cmp reg,#val
將暫存器中的值與數位val
進行比較。如果最後的比較結果是不相等,那麼執行字尾了
ne
的b
命令。
上面是一個很常見的產生延遲的程式碼片段,由於每個樹莓派基本上是相同的,所以產生的延遲大致也是相同的。它的工作原理是,使用一個 mov
命令將值 3F000016 推入到暫存器 r2
中,然後將這個值減 1,直到這個值減到 0 為止。在這裡使用了三個新命令 sub
、 cmp
和 bne
。
sub
是減法命令,它只是簡單地從第一個引數中的值減去第二個引數中的值。
cmp
是個很有趣的命令。它將第一個引數與第二個引數進行比較,然後將比較結果記錄到一個稱為當前處理器狀態暫存器的專用暫存器中。你其實不用擔心它,它記住的只是兩個數誰大或誰小,或是相等而已。1
bne
其實是一個偽裝的分支命令。在 ARM 組合語言家族中,任何指令都可以有條件地執行。這意味著如果上一個比較結果是某個確定的結果,那個指令才會執行。這是個非常有意思的技巧,我們在後面將大量使用到它,但在本案例中,我們在 b
命令後面的 ne
字尾意思是 “只有在上一個比較的結果是值不相等,才去執行該分支”。ne
字尾可以使用在任何命令上,其它幾個(總共 16 個)條件也是如此,比如 eq
表示等於,而 lt
表示小於。
上一節講我提到過,通過將 GPIO 地址偏移量設定為 28(即:str r1,[r0,#28]
)而不是 40 即可實現 LED 的關閉。因此,你需要去修改課程 OK01 的程式碼,在開啟 LED 後,執行等待程式碼,然後再關閉 LED,再次執行等待程式碼,並包含一個回到開始位置的分支。注意,不需要重新啟用 GPIO 的 16 號針腳的輸出功能,這個操作只需要做一次就可以了。如果你想更高效,我建議你複用 r1
暫存器的值。所有課程都一樣,你可以在 下載頁面 找到所有的解決方案。需要注意的是,必須保證你的所有標籤都是唯一的。當你寫了 wait1$:
你其它行上的標籤就不能再使用 wait1$
了。
在我的樹莓派上,它大約是每秒閃兩次。通過改變我們所設定的 r2
暫存器中的值,可以很輕鬆地修改它。但是,不幸的是,我不能夠精確地預測它的執行速度。如果你的樹莓派未按預期正常工作,請檢視我們的故障排除頁面,如果它正常工作,恭喜你。
在這個課程中,我們學習了另外兩個組合命令:sub
和 cmp
,同時學習了 ARM 中如何實現有條件執行。
在下一個課程,課程 3:OK03 中我們將學習如何編寫程式碼,以及建立一些程式碼複用的標準,並且如果需要的話,可能會使用 C 或 C++ 來寫程式碼。
如果你點了這個連結,說明你一定想知道它的具體內容。CPSR 是一個由許多獨立的位元位組成的 32 位元暫存器。它有一個位用於表示正數、零和負數。當一個 cmp
指令執行後,它從第一個引數上減去第二個引數,然後用這個位記下它的結果是正數、零還是負數。如果是零意味著它們相等(a-b=0
暗示著 a=b
)如果為正數意味著 a 大於 b(a-b>0
暗示著 a>b
),如果為負數意味著小於。還有其它比較指令,但 cmp
指令最直觀。 ?