編程指南)
1. 項目概述Itead Studio的1602 LCD Keypad Shield如果你玩過Arduino大概率見過或者用過那種帶一塊藍(lán)色背光屏幕和幾個彩色按鍵的小擴展板。沒錯我說的就是Itead Studio出品的1602 LCD Keypad Shield。這玩意兒在Arduino社區(qū)里堪稱“國民級”配件價格便宜功能直觀幾乎是每個新手在點亮LED、玩轉(zhuǎn)舵機之后第一個會接觸到的顯示與交互模塊。它本質(zhì)上是一個“盾板”Shield直接插在Arduino Uno、Mega等主流開發(fā)板上無需額外飛線就能立刻獲得一個16字符x2行的液晶顯示屏外加一個5向搖桿或獨立按鍵和幾個功能鍵。我手頭這塊板子已經(jīng)跟了我好幾年從最初的學(xué)習(xí)項目到后來的一些快速原型驗證它都立下了汗馬功勞。它的核心價值在于“集成”與“簡化”。對于初學(xué)者它免去了連接1602 LCD所需的繁瑣布線至少需要6根IO線和電位器調(diào)節(jié)對比度的麻煩對于有經(jīng)驗的開發(fā)者它提供了一個快速實現(xiàn)人機交互HMI的現(xiàn)成方案讓你能把精力集中在核心邏輯上而不是底層驅(qū)動上。今天我就來徹底拆解一下這塊經(jīng)典盾板從硬件原理、庫驅(qū)動、到實戰(zhàn)編程和那些官方手冊里不會寫的“坑”希望能幫你把它玩得更透。2. 硬件深度解析與接口定義2.1 核心組件拆解這塊盾板的結(jié)構(gòu)非常清晰主要包含三大功能模塊1602字符型液晶顯示器LCD這是板子的視覺核心。1602代表其顯示容量為16列2行每個字符是一個5x8的點陣。它使用標(biāo)準(zhǔn)的HD44780或兼容芯片作為控制器。板載了一個藍(lán)色的LED背光通過一個限流電阻連接到電源通??梢酝ㄟ^一個跳線帽或焊點來選擇是常亮還是由Arduino的某個引腳控制。按鍵矩陣Keypad板子右側(cè)集成了6個按鍵。最常見的設(shè)計是一個模擬搖桿上下左右選擇和1個獨立的選擇鍵SELECT。也有版本是5個獨立的輕觸按鍵。其原理是電阻分壓式模擬按鍵讀取這是本板設(shè)計的一個巧思也是容易出問題的地方后面會詳細(xì)說。接口與擴展作為Shield它完美兼容Arduino Uno的引腳布局。所有與Arduino通信的引腳數(shù)據(jù)線、控制線、背光控制、按鍵讀取都通過排針直接連接到下方的Arduino主控板上。同時它通常會將Arduino的模擬輸入口A0用于讀取按鍵并將數(shù)字引腳D4-D7、D8-D10具體引腳分配因版本略有不同用于驅(qū)動LCD。更重要的是它會把Arduino的其余數(shù)字和模擬引腳通過排針引出方便你同時連接其他傳感器如DHT11溫濕度傳感器接D2超聲波模塊接D11、D12等這是它作為“擴展板”而非“獨占式模塊”的優(yōu)勢。2.2 引腳連接原理與沖突規(guī)避這是使用這塊盾板時必須首先搞清楚的問題否則你的其他外設(shè)可能會失靈。以最常見的版本為例其引腳占用情況如下LCD數(shù)據(jù)線占用數(shù)字引腳D4, D5, D6, D7。這是4位數(shù)據(jù)總線模式相比8位模式節(jié)省了4個IO口。LCD控制線占用數(shù)字引腳D8 (RS) D9 (Enable) D10 (背光控制)。有些版本背光控制可能用D3或其他引腳需要查看板子背面絲印或原理圖。按鍵讀取占用模擬引腳A0。所有按鍵通過一組電阻網(wǎng)絡(luò)連接到A0按下不同按鍵會在A0產(chǎn)生不同的分壓值A(chǔ)rduino通過ADC讀取這個電壓來判別是哪個鍵被按下。注意這里有一個經(jīng)典的沖突點。D10引腳通常被Arduino的SPI庫用于片選SS。如果你同時要使用SD卡模塊、無線NRF24L01模塊等基于SPI的設(shè)備就會發(fā)生沖突。解決方案通常有兩種一是修改LCD庫的引腳定義如果支持二是使用軟SPISoftware SPI來驅(qū)動你的SPI設(shè)備避開D10、D11、D12、D13這一組硬件SPI引腳。2.3 按鍵電阻網(wǎng)絡(luò)原理為什么一個模擬口A0能讀6個鍵我們來看一下它的典型電路簡化。假設(shè)Vcc5V按鍵未按下時A0通過一個上拉電阻如10K接到Vcc讀值為10235V。當(dāng)按下某個鍵比如“右”鍵時該按鍵會將A0引腳通過一個特定阻值的電阻如R1連接到GND。此時A0的電壓由10K上拉電阻和R1電阻分壓決定。ADC讀取這個電壓值并映射到一個數(shù)值范圍例如0-1023。設(shè)計時為每個按鍵選擇不同的R1阻值例如330歐 650歐 1K等使得按下不同按鍵時A0的電壓值落在幾個不重疊的區(qū)間內(nèi)。代碼中只需要判斷ADC值落在哪個區(qū)間就知道哪個鍵被按下了。常見ADC值區(qū)間參考因板子批次、電阻精度、供電電壓略有浮動需校準(zhǔn)無按鍵按下 1000 選擇鍵(SELECT): 600 - 800 左鍵(LEFT): 400 - 600 右鍵(RIGHT): 200 - 400 上鍵(UP): 50 - 150 下鍵(DOWN): 0 - 50實操心得這個分壓電路對供電電壓非常敏感。使用USB供電和外部電源供電讀值可能會有幾十個點的偏差。因此強烈建議在項目初始化時或者每次上電后先做一個簡單的按鍵校準(zhǔn)在串口監(jiān)視器中打印出A0的原始ADC值然后依次按下每個鍵記錄下其典型值范圍并據(jù)此更新你代碼中的判斷閾值。這是確保按鍵響應(yīng)穩(wěn)定的關(guān)鍵一步。3. 軟件驅(qū)動與庫的選擇3.1 經(jīng)典之選LiquidCrystal庫Arduino IDE內(nèi)置了LiquidCrystal庫這是驅(qū)動HD44780兼容屏幕的官方標(biāo)準(zhǔn)庫。對于這塊盾板你需要使用其4位數(shù)據(jù)線模式的構(gòu)造函數(shù)。由于引腳定義已經(jīng)是“事實標(biāo)準(zhǔn)”社區(qū)里直接為這塊盾板封裝了一個更簡單的庫但理解原始庫的用法依然很重要。使用內(nèi)置LiquidCrystal庫的初始化代碼如下#include LiquidCrystal.h // 初始化參數(shù)依次為RS, Enable, D4, D5, D6, D7 LiquidCrystal lcd(8, 9, 4, 5, 6, 7); void setup() { lcd.begin(16, 2); // 初始化16列2行 lcd.print(Hello, World!); // 打印文字 }背光控制則需要你手動操作對應(yīng)的引腳例如D10pinMode(10, OUTPUT); digitalWrite(10, HIGH); // 點亮背光 // digitalWrite(10, LOW); // 關(guān)閉背光3.2 專為盾板優(yōu)化LiquidCrystal_I2C不是直接兼容庫這里有個常見的誤區(qū)。很多人看到1602 LCD就想到LiquidCrystal_I2C庫但那是給通過I2C轉(zhuǎn)接板驅(qū)動的LCD用的。我們這塊盾板是直接并口驅(qū)動所以不能用I2C庫。不過得益于它的流行網(wǎng)上有很多針對此盾板的“一鍵安裝”庫例如直接搜索“Arduino LCD Keypad Shield Library”你會找到一些將LCD驅(qū)動和按鍵讀取打包好的庫。這些庫的優(yōu)勢是開箱即用提供了像readButton()這樣的簡便函數(shù)直接返回按鍵編號。我的建議對于初學(xué)者可以直接使用這些整合庫快速上手。但對于想深入理解原理或需要更精細(xì)控制的項目我推薦從標(biāo)準(zhǔn)LiquidCrystal庫和手動讀取A0模擬值開始。這樣你能完全掌控流程也方便調(diào)試和解決一些怪異問題。3.3 按鍵消抖與狀態(tài)機直接讀取A0的ADC值并做區(qū)間判斷會遇到經(jīng)典的“按鍵抖動”問題。機械觸點閉合瞬間會產(chǎn)生一系列毛刺導(dǎo)致ADC值在短時間內(nèi)劇烈波動可能造成一次按下被誤判為多次按下。解決方案軟件消抖。最簡單的做法是在檢測到按鍵值進入有效區(qū)間后延遲20-50毫秒再次讀取如果仍然在相同區(qū)間則確認(rèn)為有效按鍵。更健壯的方法是使用狀態(tài)機這是處理按鍵的工業(yè)級做法。下面是一個簡單的四狀態(tài)IDLE, PRESS_DETECT, PRESSED, RELEASE按鍵狀態(tài)機示例片段enum ButtonState { IDLE, PRESS_DETECT, PRESSED, RELEASE }; ButtonState btnState IDLE; int lastStableADC 1023; unsigned long debounceTime 0; const int DEBOUNCE_DELAY 50; // 消抖延時50ms int getADCKey() { int adc analogRead(A0); // 根據(jù)你的校準(zhǔn)閾值返回按鍵編碼例如1SELECT, 2LEFT... if (adc 1000) return 0; // 無按鍵 else if (adc 600) return 1; // ... 其他判斷 } void updateButtonFSM() { int currentKey getADCKey(); unsigned long now millis(); switch (btnState) { case IDLE: if (currentKey ! 0) { lastStableADC currentKey; debounceTime now; btnState PRESS_DETECT; } break; case PRESS_DETECT: if (now - debounceTime DEBOUNCE_DELAY) { if (getADCKey() lastStableADC) { btnState PRESSED; // 觸發(fā)按鍵按下事件 onKeyPressed(lastStableADC); } else { btnState IDLE; // 抖動忽略 } } break; case PRESSED: if (getADCKey() 0) { // 按鍵釋放 btnState RELEASE; debounceTime now; } break; case RELEASE: if (now - debounceTime DEBOUNCE_DELAY) { if (getADCKey() 0) { btnState IDLE; // 觸發(fā)按鍵釋放事件如果需要 onKeyReleased(lastStableADC); } } break; } }使用狀態(tài)機后按鍵響應(yīng)會非常干凈和可靠特別適合用于菜單導(dǎo)航等場景。4. 實戰(zhàn)項目一構(gòu)建一個多功能環(huán)境監(jiān)測站現(xiàn)在我們把這塊盾板用起來做一個簡單的桌面環(huán)境監(jiān)測站顯示溫濕度、光照強度并可以通過按鍵切換顯示頁面或設(shè)置報警閾值。4.1 硬件連接與物料清單主控Arduino Uno顯示與交互Itead 1602 LCD Keypad Shield已插在Uno上傳感器DHT11溫濕度傳感器 - 連接至盾板引出的數(shù)字引腳 D2GY-30 BH1750光照強度傳感器I2C接口 - 連接至盾板引出的模擬引腳 A4 (SDA), A5 (SCL)供電USB線或外部7-12V電源適配器。接線注意DHT11的數(shù)據(jù)線接D2電源和地可以接到盾板上的5V和GND排針。BH1750是I2C設(shè)備A4和A5在Arduino Uno上就是專用的SDA和SCL引腳直接連接即可。這里完美體現(xiàn)了盾板的擴展性核心的LCD和按鍵不占用額外的IO我們依然有豐富的引腳連接其他傳感器。4.2 代碼架構(gòu)與實現(xiàn)這個項目需要多個庫協(xié)同工作LiquidCrystal或?qū)S枚馨鍘煊糜隍?qū)動LCD。DHT sensor library用于讀取DHT11。Wire.h和BH1750庫需自行安裝用于讀取光照。核心邏輯設(shè)計狀態(tài)頁面管理定義幾個顯示頁面如“主頁溫濕度光照”、“歷史最高/最低”、“設(shè)置閾值”。按鍵驅(qū)動頁面切換使用左/右鍵切換頁面上/下鍵在設(shè)置頁面調(diào)整數(shù)值選擇鍵確認(rèn)。定時采樣使用millis()進行非阻塞定時例如每2秒讀取一次傳感器避免使用delay()阻塞按鍵響應(yīng)。關(guān)鍵代碼片段 - 頁面切換與顯示#include LiquidCrystal.h #include DHT.h #include BH1750.h #include Wire.h LiquidCrystal lcd(8, 9, 4, 5, 6, 7); DHT dht(2, DHT11); BH1750 lightMeter; enum DisplayPage { MAIN_PAGE, STATS_PAGE, SETTINGS_PAGE }; DisplayPage currentPage MAIN_PAGE; float temp, humi, lux; unsigned long lastSensorRead 0; const long SENSOR_INTERVAL 2000; void setup() { lcd.begin(16, 2); dht.begin(); Wire.begin(); lightMeter.begin(); pinMode(10, OUTPUT); digitalWrite(10, HIGH); // 打開背光 lcd.print(Env Station Init); delay(1000); lcd.clear(); } void loop() { unsigned long now millis(); // 非阻塞式傳感器讀取 if (now - lastSensorRead SENSOR_INTERVAL) { readSensors(); lastSensorRead now; } // 更新按鍵狀態(tài)機 updateButtonFSM(); // 根據(jù)當(dāng)前頁面刷新顯示 refreshDisplay(); } void readSensors() { humi dht.readHumidity(); temp dht.readTemperature(); lux lightMeter.readLightLevel(); // 添加錯誤檢查略 } void refreshDisplay() { lcd.clear(); lcd.setCursor(0, 0); switch(currentPage) { case MAIN_PAGE: lcd.print(T:); lcd.print(temp, 1); lcd.print(C H:); lcd.print(humi, 0); lcd.print(%); lcd.setCursor(0, 1); lcd.print(Lux:); lcd.print(lux, 0); break; case STATS_PAGE: // 顯示統(tǒng)計信息... break; case SETTINGS_PAGE: // 顯示設(shè)置界面... break; } } // 在按鍵狀態(tài)機的PRESSED事件中調(diào)用 void onKeyPressed(int key) { switch(key) { case 2: // LEFT currentPage (DisplayPage)((currentPage 2) % 3); // 循環(huán)左翻頁 break; case 3: // RIGHT currentPage (DisplayPage)((currentPage 1) % 3); // 循環(huán)右翻頁 break; // 處理UP, DOWN, SELECT... } refreshDisplay(); // 立即刷新顯示 }這個框架清晰地分離了數(shù)據(jù)采集、用戶輸入和界面渲染是構(gòu)建更復(fù)雜菜單系統(tǒng)的基礎(chǔ)。5. 實戰(zhàn)項目二制作一個可編程定時器/倒數(shù)計數(shù)器這是一個更能體現(xiàn)按鍵交互價值的項目。我們將制作一個可以通過按鍵設(shè)置時分秒然后開始倒計時時間到后通過蜂鳴器報警的裝置。5.1 功能設(shè)計與硬件添加核心功能待機界面顯示當(dāng)前時間或“Ready”。按下“SELECT”鍵進入設(shè)置模式通過“UP”/“DOWN”調(diào)整光標(biāo)選中的數(shù)字時、分、秒通過“LEFT”/“RIGHT”移動光標(biāo)。再次按下“SELECT”確認(rèn)并開始倒計時。倒計時過程中屏幕實時顯示剩余時間。倒計時結(jié)束蜂鳴器鳴響屏幕閃爍提示。新增硬件一個有源蜂鳴器連接盾板引出的數(shù)字引腳 D3通過一個三極管或小功率MOS管驅(qū)動或者直接接在IO口和GND之間注意電流不要超過Arduino引腳極限通常加一個220歐電阻限流。5.2 狀態(tài)機與菜單邏輯的進階應(yīng)用這個項目的狀態(tài)比環(huán)境監(jiān)測站更復(fù)雜。我們需要一個更精細(xì)的狀態(tài)機enum TimerState { STATE_IDLE, STATE_SET_HOUR, STATE_SET_MINUTE, STATE_SET_SECOND, STATE_COUNTING, STATE_ALARM }; TimerState timerState STATE_IDLE; int setHour 0, setMinute 0, setSecond 10; // 默認(rèn)10秒 int remainingTimeInSeconds 0; int cursorPos 0; // 0:時, 1:分, 2:秒在STATE_IDLE下按下SELECT進入設(shè)置模式初始光標(biāo)在“時”上STATE_SET_HOUR。此時UP/DOWN鍵用于增減數(shù)值LEFT/RIGHT鍵用于在時、分、秒之間移動光標(biāo)切換STATE_SET_HOURSTATE_SET_MINUTESTATE_SET_SECOND。再次按下SELECT計算總秒數(shù)remainingTimeInSeconds進入STATE_COUNTING倒計時狀態(tài)。倒計時的實現(xiàn)同樣使用millis()非阻塞方式。記錄進入STATE_COUNTING的時刻startCountMillis和總時長remainingTimeInSeconds。每次循環(huán)計算已過去的時間更新remainingTimeInSeconds并在LCD上格式化為“HH:MM:SS”顯示。當(dāng)remainingTimeInSeconds 0時切換到STATE_ALARM狀態(tài)。顯示刷新優(yōu)化在倒計時狀態(tài)下不需要每輪循環(huán)都清屏重繪lcd.clear()會導(dǎo)致屏幕閃爍??梢灾桓伦兓臄?shù)字部分。例如將時間字符串“12:34:56”分成多個部分只重寫需要更新的字符位置。void updateCountingDisplay(int totalSeconds) { int h totalSeconds / 3600; int m (totalSeconds % 3600) / 60; int s totalSeconds % 60; // 只在數(shù)字變化時更新對應(yīng)位置避免全局清屏閃爍 static int lastH -1, lastM -1, lastS -1; if (h ! lastH) { lcd.setCursor(0, 0); lcd.print(h 10 ? 0 : ); lcd.print(h); lastH h; } // ... 類似地更新分鐘和秒的顯示 }5.3 蜂鳴器報警與交互反饋在STATE_ALARM狀態(tài)下控制蜂鳴器引腳以一定頻率例如1Hz閃爍鳴叫同時讓LCD背光引腳10或整個屏幕內(nèi)容閃爍。unsigned long lastBeepTime 0; const long BEEP_INTERVAL 500; // 500ms if (timerState STATE_ALARM) { unsigned long now millis(); if (now - lastBeepTime BEEP_INTERVAL) { digitalWrite(BUZZER_PIN, !digitalRead(BUZZER_PIN)); // 翻轉(zhuǎn)蜂鳴器狀態(tài) digitalWrite(10, !digitalRead(10)); // 翻轉(zhuǎn)背光狀態(tài)實現(xiàn)閃爍 lastBeepTime now; } // 任何按鍵按下可以停止報警回到IDLE狀態(tài) if (keyPressed) { digitalWrite(BUZZER_PIN, LOW); digitalWrite(10, HIGH); timerState STATE_IDLE; refreshIdleDisplay(); } }這個項目綜合運用了狀態(tài)機、非阻塞定時、局部顯示刷新和外部設(shè)備控制是一個非常好的綜合性練習(xí)。6. 常見問題、調(diào)試技巧與性能優(yōu)化6.1 LCD顯示亂碼、對比度不佳或只有一排方塊癥狀屏幕亮起但顯示亂碼或者只顯示一行黑色方塊。排查對比度問題1602 LCD通常需要一個可調(diào)電阻電位器來調(diào)節(jié)對比度Vo引腳電壓。這塊盾板已經(jīng)集成了一個貼片電位器通常是一個藍(lán)色的小方塊用十字螺絲刀可以調(diào)節(jié)。這是最常見的原因。上電后慢慢旋轉(zhuǎn)這個電位器直到字符清晰顯示。初始化時序確保在setup()中l(wèi)cd.begin(16,2)被正確執(zhí)行。有時電源不穩(wěn)會導(dǎo)致初始化失敗。嘗試在begin()前加一個短延時delay(100)。引腳定義錯誤再次確認(rèn)LiquidCrystal lcd(rs, en, d4, d5, d6, d7)構(gòu)造函數(shù)中的引腳號與你盾板的實際連接完全一致。不同廠商的盾板可能有細(xì)微差別。電源問題確保Arduino供電充足。當(dāng)背光點亮?xí)r電流消耗較大劣質(zhì)USB線或電源可能導(dǎo)致電壓跌落影響LCD控制器工作。嘗試斷開背光將控制背光的引腳設(shè)為LOW看是否顯示正常。6.2 按鍵反應(yīng)不靈、串鍵或長按無法識別癥狀按下鍵沒反應(yīng)或者按一個鍵卻觸發(fā)了其他鍵的功能或者無法區(qū)分短按和長按。排查與解決ADC閾值校準(zhǔn)如前所述這是首要步驟。在setup()里用Serial.begin(9600)和Serial.println(analogRead(A0))打印原始值分別按下每個鍵記錄穩(wěn)定的數(shù)值范圍更新你的判斷閾值。電源噪聲模擬輸入對噪聲敏感。確保A0引腳附近沒有高頻數(shù)字信號干擾??梢栽贏0和GND之間加一個0.1uF的瓷片電容濾波。消抖算法務(wù)必實現(xiàn)軟件消抖。簡單的延時消抖或前述的狀態(tài)機都能極大改善體驗。實現(xiàn)長按在狀態(tài)機的PRESSED狀態(tài)中加入一個計時器。如果按鍵保持按下的時間超過某個閾值如1秒則觸發(fā)“長按”事件同時重置一個標(biāo)志位避免在長按期間重復(fù)觸發(fā)短按事件。6.3 與其他庫或外設(shè)的沖突癥狀當(dāng)接入SPI或特定數(shù)字引腳設(shè)備時LCD不顯示或按鍵失靈。解決方案SPI沖突D10, D11, D12, D13這是最經(jīng)典的沖突。如果必須使用硬件SPI設(shè)備可以嘗試修改LCD的引腳定義將RS、Enable、背光控制移到其他空閑引腳如D2, D3, A1等并修改庫的初始化代碼如果庫支持或自己重新實現(xiàn)一個精簡的LCD驅(qū)動。另一種更簡單的方法是為SPI設(shè)備使用軟件SPISoftware SPI指定另外一組空閑引腳來模擬SPI時鐘和數(shù)據(jù)線。PWM沖突D9, D10等引腳也是PWM輸出引腳。如果你用這些引腳同時控制LCD和輸出PWM可能會互相干擾。通常LCD控制是數(shù)字開關(guān)信號影響不大但最好避免。中斷沖突D2, D3是外部中斷引腳。如果你將傳感器接在這兩個引腳并啟用了中斷要確保中斷服務(wù)程序ISR執(zhí)行時間極短否則會影響LCD的時序可能導(dǎo)致顯示異常。一個變通的辦法是將傳感器接到其他支持中斷的引腳在某些型號上所有數(shù)字引腳都可通過attachInterrupt(digitalPinToInterrupt(pin), ISR, mode)方式使用中斷。6.4 顯示內(nèi)容刷新慢或閃爍癥狀更新屏幕文字時感覺有延遲或明顯的閃爍感。優(yōu)化技巧避免頻繁lcd.clear()clear()操作耗時較長。如果只是更新部分內(nèi)容如數(shù)字優(yōu)先使用lcd.setCursor()定位然后直接print新內(nèi)容覆蓋舊內(nèi)容。如果新舊內(nèi)容長度不同可以用空格 覆蓋殘留字符。使用lcd.noDisplay()和lcd.display()在需要大量更新屏幕時如整頁切換可以先noDisplay()關(guān)閉顯示更新所有內(nèi)容后再display()打開這樣用戶看不到中間的更新過程體驗更流暢。優(yōu)化字符串處理避免在loop()中頻繁使用String類進行字符串拼接這容易產(chǎn)生內(nèi)存碎片。對于固定格式的顯示可以先用sprintf或snprintf格式化到字符數(shù)組緩沖區(qū)然后一次性用lcd.print輸出。這塊Itead Studio的1602 LCD Keypad Shield雖然其硬件在今天看來已經(jīng)非常簡單甚至有些“古老”但它所涉及的技術(shù)點——并口通信、模擬信號讀取、狀態(tài)機、非阻塞編程、資源沖突解決——卻是嵌入式交互設(shè)計的基石。把它玩透了你再面對更復(fù)雜的TFT觸摸屏或者矩陣鍵盤時會發(fā)現(xiàn)很多原理是相通的。最后一個小建議不妨試著拋開現(xiàn)成的庫只用digitalWrite、digitalRead和analogRead這些最基礎(chǔ)的函數(shù)從頭實現(xiàn)一遍LCD的4位模式初始化和字符發(fā)送以及按鍵的ADC讀取和狀態(tài)判斷。這個過程會讓你對底層硬件通信有更深刻的理解這份理解是任何高級庫都無法替代的。