位元拍打(bit-banging)
/ BIT-bang-ing /
假設你要用手電筒打一段摩斯密碼,但沒有自動閃爍器——所以你手動把燈開開關關,靠在心裡數數讓每次閃爍維持正確的時長。位元拍打就是軟體版的這件事:在程式碼裡手動切換一般的輸出接腳,一個位元接一個位元地產生一種通訊協定,由你的程式控制確切的時序,而非交給專用的硬體週邊去做。
多數 MCU 為常見的序列協定備有硬體週邊——序列用的 UART、SPI 控制器、I2C 控制器。這些週邊在矽片裡做完工作:你把一個位元組交給它們,它們就以正確的速度與波形把位元在接腳上時控輸出,而你的 CPU 去做別的事。位元拍打是你「不」用這類週邊時所做的:你拿一般的通用輸入輸出接腳,寫程式碼親手驅動它們走完協定。例如要拍打 SPI,你的程式碼會手動把資料腳設成每個位元的值,然後把時鐘腳脈衝拉高再拉低,對全部八個位元如此,並以短延遲為脈衝計時。整段期間 CPU 都忙著做這件事。人們位元拍打有幾個誠實的理由:晶片缺少你所需協定的硬體週邊;所有硬體週邊都已被佔用;你必須用的接腳沒接到任何週邊;或你需要一種沒有任何週邊實作的不尋常或非標準協定。
它之所以重要,是作為一種給你完全彈性的後備——你能在幾乎任何接腳上講幾乎任何協定——也作為一種讓協定的位元變得可見的教學工具。誠實的取捨很顯著:位元拍打綁住 CPU(拍打位元時它幾乎無法做別的,而硬體週邊在背景運作)、它通常較慢且時序較不精確(拍打途中到來的中斷可能拉長一個脈衝、破壞訊號,所以人們常在關鍵的拍打期間關閉中斷,這損害即時延遲),而且更耗電。經驗法則誠實又簡單:有合適的硬體週邊時就用它;只在不得已時才位元拍打。在一個好端端的週邊正閒置時卻預設動用位元拍打,是常見的初學者低效。
// 在一般 GPIO 接腳上親手拍打一個 SPI 位元組(模式 0): for (int i = 7; i >= 0; i--) { write_pin(MOSI, (byte >> i) & 1u); // 呈現這個位元 write_pin(SCLK, 1); short_delay(); // 時鐘拉高(從端取樣) write_pin(SCLK, 0); short_delay(); // 時鐘拉低 } // CPU 在此完全被佔用;硬體 SPI 區塊會在背景做同樣的事, // 同時讓 CPU 執行其他程式碼。
在軟體中驅動每個位元與時鐘邊緣。硬體週邊會在背景把同一個位元組時控輸出,騰出 CPU。
位元拍打綁住 CPU、較慢,且時序脆弱(拍打途中的中斷可能破壞訊號)。有合適的硬體週邊時就用它;只在你真的不得已時才位元拍打。