指南)
做嵌入式這幾年我拿示波器看的最多的信號就是TTL串口。你可能每天都在用串口調(diào)試助手收發(fā)數(shù)據(jù)但真的把探頭夾到TX引腳上看過線上波形的人其實不多。芯片之間怎么靠一根導(dǎo)線完成通信答案全都藏在那串高低電平的跳變里。這篇文章就從最基礎(chǔ)的TTL電平講起帶你把示波器設(shè)置好親手抓一幀串口數(shù)據(jù)再把波形“翻譯”回十六進制。不管你剛?cè)腴T還是老手希望你看完都能對串口通信多一層更底層的理解??赐赀@篇文章你會明白線上那些看似雜亂的高低電平其實每一跳都在傳遞一個bit也會掌握一套用示波器排查串口問題的通用方法以后無論是調(diào)自己的板子還是分析別人的設(shè)備都多了一把趁手的工具。1. 先搞清楚TTL串口這條線上跑的是什么信號很多初學(xué)者把串口當(dāng)成一個黑盒調(diào)用庫函數(shù)數(shù)據(jù)就出去了另一端就收到了。但中間到底怎么走的很少有人想過。其實揭開這個黑盒并不難線上只有兩種電壓狀態(tài)在來回跳所謂通信就是收發(fā)雙方對這些電壓跳變進行約定好的“解讀”。1.1 TTL電平不是“玄學(xué)”是電壓的兩種狀態(tài)TTL串口線上只有兩種狀態(tài)高電平和低電平。邏輯1對應(yīng)高電平邏輯0對應(yīng)低電平。至于高是多少、低是多少取決于你用的電平標(biāo)準(zhǔn)。嚴(yán)格來說TTLTransistor-Transistor Logic是用雙極型晶體管實現(xiàn)的邏輯家族它定義的高電平閾值是2V以上低電平閾值是0.8V以下。而現(xiàn)代嵌入式設(shè)備里絕大多數(shù)芯片用的是CMOS工藝輸出高電平接近電源電壓輸出低電平接近0V幾乎沒有中間地帶。我們平時掛在嘴邊的“TTL串口”其實混用了TTL和CMOS兩套標(biāo)準(zhǔn)習(xí)慣上把所有0~3.3V或0~5V的串口信號都統(tǒng)稱為TTL。很多新人第一次聽到“TTL電平”和“CMOS電平”的區(qū)別會懵這里可以簡單記TTL是電流型邏輯閾值寬CMOS是電壓型邏輯輸出更接近電源軌。這里面有個容易踩坑的點同樣是3.3V系統(tǒng)不同芯片的電平閾值不一樣。CMOS輸入的高電平閾值大約是0.7倍電源電壓也就是3.3V系統(tǒng)大概在2.31V以上才算高低電平閾值大約是0.3倍電源電壓0.99V以下才算低。所以判斷信號是否有效不能只看波形幅度大不大還得看接收端的閾值和耐壓范圍。我拿示波器測過不少串口波形最常見的組合是3.3V高電平、0V低電平也有5V的工業(yè)板上還會遇到1.8V電平的情況。測量的時候一定要先確認(rèn)系統(tǒng)電壓再去判斷波形是否“正常”否則容易把正確的信號誤判成異常。1.2 一幀數(shù)據(jù)怎么排起始位、數(shù)據(jù)位、停止位UARTUniversal Asynchronous Receiver/Transmitter通用異步收發(fā)器是TTL串口通信最常見的協(xié)議形式。它叫“異步”是因為發(fā)送方和接收方之間沒有專門的時鐘線雙方靠約定好的波特率來對齊每一位。協(xié)議規(guī)定空閑時TX引腳保持高電平。發(fā)送一幀數(shù)據(jù)時先拉低一個bit的時間這叫起始位接著按低位先發(fā)的順序發(fā)送5到8個數(shù)據(jù)位然后是可選的校驗位最后拉高一個或兩個bit的時間這是停止位。整個幀結(jié)構(gòu)拆開看就是起始位“開嗓”數(shù)據(jù)位“說話”停止位“閉嘴”。為什么要有起始位因為線上空閑是高電平從高電平跳變到低電平的這個下降沿就是一個明確的“我要開始發(fā)數(shù)據(jù)了”的信號。接收方看到這個下降沿就按照約定的波特率開始計時一個bit一個bit地采數(shù)據(jù)。這就好比兩人約好了暗號對方一抬手你就知道他要說第一句話了。停止位的作用則是保證兩個幀之間至少有一段高電平時間。如果一幀的最后一個數(shù)據(jù)位恰好是低電平?jīng)]有停止位緩沖下一幀的起始位就會跟它連在一起接收方就沒法區(qū)分幀邊界了。所以不要小看這個“多余的”高電平它讓通信過程有了清晰的呼吸節(jié)奏。1.3 波特率決定“說話”的語速波特率就是每秒傳輸多少個bit單位是bps。9600bps意味著每個bit占據(jù)約104.2微秒115200bps每個bit約8.68微秒1Mbps則是1微秒。通信雙方必須使用一致的波特率否則就會亂碼。這跟兩個人打電話一個道理一個語速飛快一個慢慢聽怎么都對不上。嵌入式里常見的波特率有9600、19200、38400、57600、115200以及更高一些的230400、460800、921600。一般來說調(diào)試信息用115200比較多一些低速傳感器模塊還在用9600。實際示波器上怎么看波特率非常簡單測量一個數(shù)據(jù)位的寬度取倒數(shù)就是波特率。比如測到位寬約8.68微秒那波特率就是1/8.68us約等于115200。這個技巧在分析未知設(shè)備時非常管用后面我會演示怎么操作。常用波特率與位時間的對應(yīng)關(guān)系波特率位時間一幀大概耗時10bit9600104.17 us1.04 ms1920052.08 us520.8 us3840026.04 us260.4 us5760017.36 us173.6 us1152008.68 us86.8 us2304004.34 us43.4 us9216001.09 us10.9 us2. 示波器準(zhǔn)備接線和設(shè)置決定能不能看到波形示波器不是拿來就能直接看的接線和幾個關(guān)鍵設(shè)置搞錯了你可能會看到一條毫無意義的直線或者波形在屏幕上到處亂飄。準(zhǔn)備階段多花兩分鐘后面抓波形就省心得多。2.1 探頭怎么接地線別省x1/x10別搞錯第一步當(dāng)然是把探頭接到示波器的通道1。探頭標(biāo)配的鱷魚夾地線一定要夾到被測板的GND上而且連線越短越好。串口信號是相對于地的電壓信號不接地線示波器就是懸浮測量波形會亂飄幅度也不對根本沒法判斷。探頭上有x1/x10的開關(guān)日常測串口建議用x10檔。x10檔探頭輸入阻抗更高、對被測電路影響更小。但要注意示波器通道菜單里的探頭衰減系數(shù)也要選到x10不然顯示出來的幅度會是真實值的十分之一新手容易在這個地方看懵以為信號幅度不夠。還有一點容易被忽略x1檔雖然在低速下也能用但它的帶寬通常只有幾MHz而且寄生電容比較大會影響被測信號。測串口這樣的低頻信號時x1不至于不能看但養(yǎng)成用x10的習(xí)慣更穩(wěn)妥以后測高速信號也能直接上手。注意測串口這類數(shù)字信號建議用x10檔并確保通道菜單里的探頭衰減系數(shù)與探頭開關(guān)位置一致。2.2 觸發(fā)怎么設(shè)抓住下降沿一幀就跑不掉示波器的“觸發(fā)”功能是讓波形穩(wěn)定顯示的關(guān)鍵。觸發(fā)條件可以簡單理解成告訴示波器“等出現(xiàn)我指定的電平變化你才開始畫波形”。串口空閑是高電平幀從下降沿開始。所以最適合串口的觸發(fā)方式是下降沿觸發(fā)。觸發(fā)電平一般設(shè)在信號幅度的中間位置3.3V系統(tǒng)大約1.6到1.7V5V系統(tǒng)大約2.5V。這個位置比較穩(wěn)既能避開噪聲又不會錯過真正的下降沿。觸發(fā)模式建議用Normal或者Single。如果用Auto模式示波器在等待觸發(fā)的同時會周期性刷新屏幕可能把波形刷得亂七八糟尤其低速串口會顯得很不穩(wěn)定。用Normal模式時只有滿足觸發(fā)條件才會刷新一次波形用Single模式抓到一次就停住適合分析單幀數(shù)據(jù)。我抓串口幀的習(xí)慣是先把觸發(fā)模式設(shè)成Normal調(diào)好觸發(fā)電平再按一下Single然后去操作設(shè)備發(fā)送數(shù)據(jù)屏幕就定格一幀完整波形。這樣抓到的幀非常干凈適合仔細逐位分析。2.3 時基和采樣率讓波形既完整又清晰時基s/div決定屏幕上每一格代表多長時間。串口一幀一般是10個bit起始位1個數(shù)據(jù)位8個停止位1個所以一幀的總時間約等于10倍的位時間。以9600波特率為例一幀約1.04ms示波器一般顯示10格如果時基設(shè)為200us/div正好能顯示2ms可以裝下差不多兩幀既能看到完整的幀又不會因為顯示太多導(dǎo)致波形太密。115200波特率的一幀約86.8us時基設(shè)20us/div或50us/div比較合適。采樣率方面現(xiàn)代數(shù)字示波器動輒1GSa/s測串口綽綽有余。但要注意的是采樣率越高相同存儲深度下能記錄的時間越短。好在單幀串口數(shù)據(jù)很短用最高采樣率也存得下。只有當(dāng)你需要觀察長時間內(nèi)的多幀交互時才需要考慮降采樣率換取更長的記錄窗口。如果你的示波器帶“Decode”解碼功能花幾分鐘把串口參數(shù)配好它甚至能在屏幕上直接標(biāo)出每個字節(jié)的數(shù)值極大提升效率。沒有解碼功能也沒關(guān)系手動數(shù)波形照樣能讀懂后面我會演示。3. 實際抓波形從示波器屏幕讀出一幀報文理論講完該動手了。這里我以一個最普通的測試環(huán)境為例完整走一遍操作流程。你跟著做一遍比看十遍概念都有用。3.1 搭一個二十分鐘能搞定的測試環(huán)境你需要的東西一臺示波器、一個USB轉(zhuǎn)TTL模塊CH340、CP2102、FT232都行、幾根杜邦線以及電腦上任意一款串口調(diào)試助手。把USB轉(zhuǎn)TTL模塊插到電腦上驅(qū)動裝好之后設(shè)備管理器里會出現(xiàn)一個COM口。用杜邦線把模塊的TX引腳引出來同時引一條GND方便夾探頭。串口調(diào)試助手里設(shè)置好波特率我建議先用9600因為位時間寬波形在屏幕上看得清楚初學(xué)者容易上手。發(fā)送內(nèi)容填“55”注意要以十六進制發(fā)送這樣線上跑的就是一個字節(jié)0x55。如果沒有USB轉(zhuǎn)TTL模塊用STM32、ESP32這類開發(fā)板也一樣寫一個最簡單的循環(huán)發(fā)送0x55的代碼就行。關(guān)鍵是要保證線上真的有數(shù)據(jù)在跑示波器才有東西可看。我遇到過有人說“我代碼發(fā)數(shù)據(jù)了示波器沒波形”結(jié)果查了半天發(fā)現(xiàn)是代碼根本沒跑到發(fā)送那行這種低級錯誤大家都會犯先用示波器確認(rèn)信號是否存在永遠比猜代碼靠譜。3.2 看波形先從“高電平空閑”和“下降沿起始”入手接好線探頭夾在模塊TX引腳上地線夾GND。先別急著發(fā)數(shù)據(jù)示波器在Auto模式下應(yīng)該能看到一條穩(wěn)定的高電平線這就是空閑狀態(tài)。如果看到的是低電平那可能夾錯引腳了如果看到的是亂跳的波形先檢查地線有沒有夾穩(wěn)。然后設(shè)置下降沿觸發(fā)觸發(fā)模式切到Normal觸發(fā)電平調(diào)到大約1.65V。按一下Single鍵示波器進入等待觸發(fā)狀態(tài)。這時候去串口助手點發(fā)送0x55屏幕會“啪”地定格一幀波形。屏幕上你會看到一個從高到低的下降沿緊接著一串高低電平最后回到高電平。這就是完整的一幀TTL串口波形。別急著高興先數(shù)一數(shù)從下降沿開始每隔一個bit時間波形就變化一次按照9600波特率也就是每隔104微秒左右變一次。一幀里應(yīng)該有10個bit的變化最后那段高電平是停止位。3.3 逐位解碼0x55的波形在示波器上長什么樣0x55是十六進制對應(yīng)二進制0b01010101數(shù)據(jù)位8個bit分別是0、1、0、1、0、1、0、1。因為UART是低位先發(fā)所以實際發(fā)送順序是1、0、1、0、1、0、1、0。把起始位和停止位算進去完整幀就是低起始位→ 高 → 低 → 高 → 低 → 高 → 低 → 高 → 低 → 高停止位。換句話說用示波器看0x55的波形最明顯的特征就是一串整齊的高低交替方波像梳子齒一樣非常好認(rèn)。很多老工程師拿0x55當(dāng)測試數(shù)據(jù)就是因為它的波形規(guī)律一看就知道鏈路是否正常。用光標(biāo)量這個方波每個高電平或低電平的寬度在9600波特率下應(yīng)該是104微秒左右。如果量出來是這個數(shù)說明你的發(fā)送端確實工作在9600波特率線上跑的就是標(biāo)準(zhǔn)UART幀。如果發(fā)0x00波形會變成起始位低電平然后8個連續(xù)低電平的數(shù)據(jù)位最后停止位高電平。你會看到一個很長很寬的低電平段大約占9個bit的寬度。如果發(fā)0xFF則是起始位低一下之后立刻回到高電平并一路高到幀結(jié)束。這兩種波形都容易讓新手迷惑但記住一點永遠按幀結(jié)構(gòu)去數(shù)位不要憑直覺看“這一段是低還是高”。4. 調(diào)試中繞不開的坑常見問題與排查思路我做嵌入式這些年遇到過各種千奇百怪的串口問題很多現(xiàn)象在代碼層面看毫無頭緒但一旦把示波器接上去原因立刻水落石出。下面這些問題都是實操中反復(fù)出現(xiàn)的我按“現(xiàn)象—原因—排查”的方式列出來方便你對照?,F(xiàn)象可能原因排查方法串口助手收到亂碼波特率不一致量位寬反推實際波特率串口助手收到亂碼數(shù)據(jù)位/校驗位設(shè)置不一致核對兩端的幀參數(shù)配置示波器看不到波形探頭地線沒接將地線夾到GND示波器看不到波形TX/RX接錯引腳確認(rèn)夾在發(fā)送端TX上波形噪聲大、邊沿毛刺多地線夾太長或接觸不良換短地線或加彈簧地線波形幅度明顯偏小探頭x10衰減未匹配檢查通道菜單衰減系數(shù)兩設(shè)備通信失敗TX接TX、RX接RX交叉接線TX對RX兩設(shè)備通信失敗未共地兩邊GND必須連在一起4.1 波形解出來全是亂碼先別怪芯片亂碼是最常見的串口問題。串口助手顯示一串“???”或者亂符號時第一反應(yīng)應(yīng)該是波特率對不對數(shù)據(jù)位、停止位、校驗位的配置雙方是否一致如果波特率不對波形上每個bit的寬度和接收方采樣的時間點對不上采樣點落在錯誤的位置上自然解出錯誤數(shù)據(jù)。這時候用示波器量一下位寬就能實錘比如你以為雙方都配了9600實際量出來位寬是52微秒那實際波特率其實是19200。把波特率改過來通信立刻恢復(fù)。還有一種情況是校驗位搞錯。有的設(shè)備開了偶校驗有的沒開導(dǎo)致接收方每個字節(jié)少讀一位數(shù)據(jù)位結(jié)果所有數(shù)據(jù)全部錯位。在示波器上能看到幀的總長度不對但更直接的排查辦法是檢查兩端的串口參數(shù)配置表把數(shù)據(jù)位、校驗位、停止位逐項對齊。4.2 電平對不上3.3V和5V之間的“翻譯”TTL串口最麻煩的坑就是電平不匹配。3.3V單片機去接5V的模塊或者反過來都可能出問題。從3.3V輸出到5V輸入如果5V端是TTL電平高電平閾值2V那么3.3V的高電平夠用但如果是5V CMOS電平高電平閾值大約3.5V3.3V就觸發(fā)不了高電平通信必然失敗。更危險的是某些5V器件的IO引腳不允許輸入超過VCC的電平3.3V直接懟上去問題不大但如果5V輸出接到3.3V輸入長期使用可能損壞引腳。從5V輸出到3.3V輸入5V高電平超過很多3.3V器件的耐壓范圍。雖然現(xiàn)在不少MCU的IO引腳標(biāo)注“5V tolerant”也就是容忍5V輸入但最好還是不要賭尤其是量產(chǎn)項目可靠性要求高的話電平轉(zhuǎn)換是必須的。最簡單的做法是加電平轉(zhuǎn)換芯片比如TXB0108、PCA9306這類雙向電平轉(zhuǎn)換芯片幾塊錢一顆接線也簡單或者用兩個MOS管搭雙向電平轉(zhuǎn)換電路原理也不復(fù)雜。示波器在這里的作用是把兩邊的實際電平量出來確認(rèn)究竟是“邏輯無法識別”還是“電平損壞器件”。我處理過不止一次“明明接了電平轉(zhuǎn)換但還是亂碼”的工單結(jié)果查到最后是電平轉(zhuǎn)換芯片的地沒有接導(dǎo)致參考電位不對所有輸出都異常。示波器一量就發(fā)現(xiàn)電平轉(zhuǎn)換輸出電壓只有一半這種問題靠猜是猜不出來的。4.3 設(shè)備之間通信失敗但示波器波形正常有時候波形看起來完全正常幅度夠、波特率對、幀結(jié)構(gòu)也標(biāo)準(zhǔn)但兩臺設(shè)備就是通信不上。這種情況十有八九是接線問題。最常見的是TX接TX、RX接RX。串口要交叉連接一端的TX必須接另一端的RX。很多人接成“同名相連”數(shù)據(jù)發(fā)出去對方根本收不到。第二個高頻問題是沒有共地雖然RS232那種標(biāo)準(zhǔn)可以做到信號隔離但TTL串口是電壓信號必須兩邊共用一個參考地不然波形會飄、信號不可靠。示波器只能看到一根線上的信號沒法直接判斷“另一頭是否真的收到”。但當(dāng)波形正常而通信失敗時優(yōu)先檢查交叉接線和共地這兩件事。我也見過因為杜邦線接觸不良導(dǎo)致的偶發(fā)丟數(shù)據(jù)示波器上偶爾能捕捉到毛刺這種時候可以輕輕撥動線材再抓一次波形看看波形是否穩(wěn)定。4.4 實測技巧從波形反推波特率、判斷TX/RX拿到一個未知設(shè)備怎么快速判斷它的串口參數(shù)示波器是最快的工具。先把探頭放在某個疑似信號的引腳上用Auto模式看波形。如果看到空閑高電平、偶爾有下降沿的方波這大概率是TX。如果一直高電平可能是RX也可能是TX當(dāng)前沒有數(shù)據(jù)發(fā)送需要觸發(fā)抓一次數(shù)據(jù)發(fā)送過程才能區(qū)分。看到波形之后用光標(biāo)量最短的那個高電平或低電平的寬度取倒數(shù)基本就是波特率。比如量到8.68微秒波特率就是115200。然后再量一幀的總時間除以bit數(shù)就能反推出數(shù)據(jù)位、停止位的配置。這套方法我在調(diào)試一些沒資料的舊設(shè)備時用過很多次比翻手冊還快。不過要提醒一句有些設(shè)備的TX線上還串了電阻或接了其他負(fù)載波形幅度可能不是標(biāo)準(zhǔn)滿幅。比如3.3V系統(tǒng)上只有2V左右的擺動這不一定代表邏輯錯誤可能只是線上帶了負(fù)載。判斷時要結(jié)合接收端的閾值來看不要一看幅度低就以為電平標(biāo)準(zhǔn)不對。5. 示波器看串口的進階玩法基礎(chǔ)會了之后示波器看串口還能干不少有意思的事。這些玩法不一定每天用但關(guān)鍵時刻非常頂事。5.1 “偷聽”兩臺設(shè)備之間的對話嵌入式系統(tǒng)里主控和傳感器模塊之間一般都有通信線。當(dāng)你懷疑某個模塊的數(shù)據(jù)有問題時不必去改代碼打斷通信直接把示波器探頭夾到通信線上用下降沿觸發(fā)、Single模式就能抓到一幀真實的交互波形。示波器探頭是高阻輸入對電路本身影響很小。串口這種幾十kbps到幾Mbps的信號探頭的寄生電容影響基本可以忽略。抓下波形之后可以用手動解碼也可以用示波器自帶的解碼功能直接看協(xié)議內(nèi)容。我在排查一個傳感器偶爾返回錯誤數(shù)據(jù)的問題時就是用這個方法抓到了異常幀有些幀的停止位之后多了半個bit的低電平明顯不符合標(biāo)準(zhǔn)UART幀結(jié)構(gòu)。后來定位到是傳感器固件在波特率切換時存在bug。這種偶發(fā)問題靠串口助手日志很難發(fā)現(xiàn)但波形上一眼就看出來了因為波形不會說謊。5.2 拆機調(diào)試場景先看波形再動手很多嵌入式設(shè)備比如路由器、機頂盒、各種智能硬件PCB上都會留調(diào)試串口的焊盤或針座常見四針或五針標(biāo)注GND、TX、RX、VCC。想接上這類接口做調(diào)試或者刷機第一件事不是盲目飛線而是用示波器確認(rèn)每個引腳的身份。通電后先用示波器DC檔測各引腳電壓GND是0VVCC是供電電壓TX空閑時應(yīng)為高電平具體是3.3V還是1.8V取決于系統(tǒng)設(shè)計RX則取決于對端設(shè)備是否在發(fā)送。再用示波器在疑似TX的引腳上抓信號如果能在設(shè)備啟動時看到一串波形基本就能確認(rèn)TX。確認(rèn)好引腳之后拿USB轉(zhuǎn)TTL模塊的TX去接設(shè)備的RX模塊的RX去接設(shè)備的TX兩邊地線共地打開串口終端就能看到設(shè)備啟動日志。如果設(shè)備支持還可以在bootloader階段中斷啟動流程進入刷機或維護模式。整個過程里示波器的作用是讓你少走彎路不至于盲接燒錯線。我見過有人在沒確認(rèn)引腳的情況下亂接結(jié)果把模塊的5V灌到設(shè)備TX上板子當(dāng)場冒煙這種事真的犯不上。這里多提一句如果是舊款筆記本電腦直接就有串口DB9接口但現(xiàn)在的電腦基本都沒有了所以USB轉(zhuǎn)TTL模塊幾乎是嵌入式調(diào)試的標(biāo)配。選模塊時盡量挑帶FT232或CP2102芯片的驅(qū)動兼容性好不容易掉線。5.3 擴展串口數(shù)據(jù)接入虛擬機的思路如果你經(jīng)常在Windows宿主機上寫嵌入式代碼應(yīng)該遇到過這種需求讓虛擬機里的Linux能訪問Windows上的USB轉(zhuǎn)TTL串口。思路其實不復(fù)雜在VMware這類虛擬機軟件里把宿主機上的串口設(shè)備映射給虛擬機。具體做法是在虛擬機設(shè)置中添加一個串口選擇“使用物理串口”或“命名管道”把Windows的COM口指向虛擬機里的/dev/ttyS0或類似設(shè)備。這樣就省去了在Windows和Linux之間來回切換串口助手的麻煩可以直接在Linux下用minicom或者寫Python腳本讀寫串口。不過這類配置跟具體虛擬機軟件版本關(guān)系很大不同版本菜單名稱略有差異。我的建議是先在Windows端確認(rèn)COM口工作正常再用示波器確認(rèn)線上有數(shù)據(jù)在跑最后再去配置虛擬機映射。這樣即便虛擬機里看不到數(shù)據(jù)也能快速定位是虛擬機映射問題還是底層通信問題不會抓瞎。寫到這里其實想說的核心就一句話串口通信并不神秘它的一切行為都在那根線上的電平跳變里。我一開始學(xué)嵌入式的時候也只會在串口助手里看十六進制直到第一次用示波器看到0x55那串整齊的方波才真正理解“異步”“起始位”“波特率”這些詞在物理上究竟意味著什么。建議你手邊有示波器的話找塊開發(fā)板接上USB轉(zhuǎn)TTL隨便發(fā)幾個字節(jié)親眼看一看波形。看完之后你會發(fā)現(xiàn)以后再遇到串口通信問題心里會踏實很多——因為你已經(jīng)親眼見過芯片之間“說話”的樣子了。