戰(zhàn):STM32與USB轉(zhuǎn)串口調(diào)試避坑指南)
簡(jiǎn)介UART串口通信的Verilog實(shí)現(xiàn)資料包面向FPGA和嵌入式系統(tǒng)學(xué)習(xí)者針對(duì)UART協(xié)議中的幀格式、波特率生成、FIFO緩沖等核心知識(shí)點(diǎn)提供了從設(shè)計(jì)、仿真到綜合的完整工程參考。壓縮包共89個(gè)文件總大小642KB內(nèi)容涵蓋Vivado工程配置.xpr、.xdc、Verilog源碼.v、仿真腳本、時(shí)序報(bào)告及輔助腳本等目錄按源文件、約束、仿真和綜合結(jié)果劃分結(jié)構(gòu)清晰。已有3005人學(xué)習(xí)使用適合需要理解異步串行通信并動(dòng)手實(shí)現(xiàn)收發(fā)模塊的初學(xué)者。資源中包含UART主控模塊、FIFO緩沖、波特率發(fā)生器和測(cè)試激勵(lì)等關(guān)鍵部分并配合Vivado工程配置可直接用于學(xué)習(xí)起始位、停止位、數(shù)據(jù)位與校驗(yàn)位的幀結(jié)構(gòu)設(shè)計(jì)以及時(shí)鐘分頻、串并轉(zhuǎn)換和ModelSim仿真驗(yàn)證流程幫助讀者完成從代碼編寫(xiě)到FPGA上板驗(yàn)證的完整鏈路。 干了十幾年嵌入式開(kāi)發(fā)和單片機(jī)相關(guān)的活如果讓我說(shuō)哪個(gè)接口最不起眼、最離不開(kāi)我會(huì)毫不猶豫地投UART一票。它沒(méi)有以太網(wǎng)那么復(fù)雜沒(méi)有USB那么高速但幾乎所有MCU、傳感器模塊、調(diào)試口、藍(lán)牙模組、GPS模塊、4G模組乃至工業(yè)設(shè)備上的RS232、RS485底層走的全是UART這套機(jī)制。很多人覺(jué)得串口通信太簡(jiǎn)單了不就是TXD接RXD、配個(gè)波特率、收發(fā)數(shù)據(jù)嗎可真到項(xiàng)目里你會(huì)發(fā)現(xiàn)波特率漂移、亂碼、丟字節(jié)、USB轉(zhuǎn)串口芯片驅(qū)動(dòng)裝不上、STM32的HAL庫(kù)中斷接收卡死、設(shè)備偶爾連不上——這些問(wèn)題每一個(gè)都?jí)蚰阏垓v半天的。這篇文章我不打算寫(xiě)教科書(shū)而是把UART串口通信從原理到實(shí)戰(zhàn)層面重新捋一遍結(jié)合常用芯片、ST官方庫(kù)、常見(jiàn)坑點(diǎn)和排查經(jīng)驗(yàn)盡量讓剛?cè)腴T(mén)的朋友少走彎路也讓有一定基礎(chǔ)的人能從里面翻出點(diǎn)新東西。如果你手里正好有STM32、ESP32這類(lèi)板子在調(diào)串口或者在用FT232、CP2102這類(lèi)USB轉(zhuǎn)串口工具這篇文章應(yīng)該能幫上忙。1. 先弄清UART到底在做什么一條線發(fā)一條線收的異步全雙工想用好串口第一步不是急著寫(xiě)代碼而是把UART的本質(zhì)嚼透。UART全稱(chēng)是Universal Asynchronous Receiver/Transmitter通用異步收發(fā)器。這里的異步是理解整個(gè)協(xié)議的關(guān)鍵——收發(fā)雙方不共享時(shí)鐘信號(hào)沒(méi)有SCLK這根線全靠約定好的波特率Baud Rate來(lái)對(duì)齊每一位的時(shí)間寬度。因此UART傳輸幀結(jié)構(gòu)非常講究??偩€在空閑時(shí)保持高電平發(fā)送數(shù)據(jù)時(shí)先拉低一個(gè)位時(shí)間作為起始位然后從最低位LSB開(kāi)始逐位發(fā)送數(shù)據(jù)位常見(jiàn)為8位最后是停止位1位、1.5位或2位通常為1位。接收端正是靠這個(gè)下降沿來(lái)識(shí)別數(shù)據(jù)要開(kāi)始了并以此作為采樣基準(zhǔn)點(diǎn)。簡(jiǎn)單說(shuō)起始位就是發(fā)令槍數(shù)據(jù)位就是運(yùn)動(dòng)員停止位就是終點(diǎn)的緩沖帶。很多人會(huì)問(wèn)既然沒(méi)有時(shí)鐘線那波特率誤差多少能忍根據(jù)我實(shí)際測(cè)試的經(jīng)驗(yàn)絕大多數(shù)UART外設(shè)在波特率誤差不超過(guò)±2%時(shí)都能穩(wěn)定通信超過(guò)±3%就有可能出現(xiàn)偶發(fā)錯(cuò)位或亂碼。以115200波特率為例每位寬度約8.68微秒2%的誤差也就約0.17微秒的漂移看似微小但在一幀10位起始位8數(shù)據(jù)位停止位的累積下就會(huì)產(chǎn)生明顯偏差。所以配置時(shí)鐘樹(shù)時(shí)如果發(fā)現(xiàn)實(shí)際波特率和目標(biāo)波特率誤差超過(guò)2%建議換一個(gè)能讓分頻結(jié)果更精確的時(shí)鐘源或調(diào)整PLL參數(shù)。再補(bǔ)充一個(gè)容易誤解的點(diǎn)UART本身只是定義了字節(jié)怎么在線上傳輸它不關(guān)心數(shù)據(jù)內(nèi)容是什么意思。你可以讓它傳ASCII字符串也可以直接傳二進(jìn)制協(xié)議幀甚至把多個(gè)字節(jié)拼成浮點(diǎn)數(shù)。真正決定通信雙方能不能聽(tīng)懂彼此的除了波特率、數(shù)據(jù)位、校驗(yàn)位、停止位這四項(xiàng)參數(shù)外還有上層應(yīng)用協(xié)議。這也是為什么調(diào)試串口時(shí)第一件事永遠(yuǎn)是核對(duì)這四項(xiàng)參數(shù)而不是一上來(lái)就懷疑硬件壞了。如果拿生活場(chǎng)景類(lèi)比UART就像是兩個(gè)人隔著很遠(yuǎn)的距離用手電筒發(fā)信號(hào)約定好一秒閃幾下波特率、先閃一下表示開(kāi)始起始位、然后按順序閃8下代表一個(gè)字母數(shù)據(jù)位、最后再閃一下表示說(shuō)完這句話停止位。兩邊都有各自獨(dú)立的手表計(jì)時(shí)不需要額外喊預(yù)備——開(kāi)始。這個(gè)比喻能幫你快速理解為什么收發(fā)雙方必須事先嚴(yán)格約定時(shí)間基準(zhǔn)。理解了這套幀結(jié)構(gòu)你會(huì)更容易明白后文里所有踩坑案例的邏輯。很多看似玄學(xué)的串口問(wèn)題歸根結(jié)底就是位時(shí)序錯(cuò)亂或電平不匹配而非代碼邏輯本身的問(wèn)題。2. 電腦怎么跟單片機(jī)聊天USB轉(zhuǎn)UART芯片的選型與驅(qū)動(dòng)避坑現(xiàn)在的筆記本基本都沒(méi)有RS232串口了所以調(diào)MCU串口時(shí)電腦和板子之間幾乎必然要經(jīng)過(guò)一顆USB轉(zhuǎn)UART橋接芯片。熱搜詞里頻繁出現(xiàn)的FT232R、FT231X、CP2102、CH340干的全是這同一件事把電腦端的USB信號(hào)轉(zhuǎn)換成UART的TXD/RXD電平信號(hào)。這顆芯片雖然不起眼卻是串口調(diào)試鏈路上最容易出幺蛾子的一環(huán)。我在不同階段用過(guò)CH340、CP2102和FT232R淺談一下各自的側(cè)重點(diǎn)芯片型號(hào)常見(jiàn)封裝驅(qū)動(dòng)兼容性典型應(yīng)用場(chǎng)景踩坑點(diǎn)CH340SOP-16Windows即插即用Linux內(nèi)核自帶開(kāi)發(fā)板、下載器、低成本產(chǎn)品部分老版本驅(qū)動(dòng)在Win10/11下藍(lán)屏或識(shí)別為未知設(shè)備CP2102QFN-28需裝驅(qū)動(dòng)Win10后較穩(wěn)定工業(yè)調(diào)試、傳感器采集板山寨芯片太多驅(qū)動(dòng)安裝失敗大概率是假芯片F(xiàn)T232RSOP-28官方驅(qū)動(dòng)非常成熟兼容性最好專(zhuān)業(yè)調(diào)試工具、量產(chǎn)測(cè)試治具價(jià)格偏高市場(chǎng)上翻新料多注意購(gòu)買(mǎi)渠道FT231XQFN-24官方驅(qū)動(dòng)性能穩(wěn)定批量產(chǎn)品的USB轉(zhuǎn)串口方案引腳間距小手工焊接麻煩驅(qū)動(dòng)這方面FT232R和FT231X的VCP驅(qū)動(dòng)Virtual COM Port在Windows、Linux、macOS下表現(xiàn)都比較省心。CP2102在Win10以上的系統(tǒng)里通常也能自動(dòng)識(shí)別但如果系統(tǒng)提示USB to UART Bridge Controller無(wú)法識(shí)別優(yōu)先考慮是不是買(mǎi)到了打磨片或者REAL芯片被替換成國(guó)產(chǎn)兼容方案的板子。CH340雖然便宜但有些劣質(zhì)電路板在USB D/D-上沒(méi)做ESD保護(hù)插拔頻繁容易導(dǎo)致電腦USB口識(shí)別異常。我個(gè)人的習(xí)慣是調(diào)試環(huán)境里常備一根FT232R方案的TTL串口線一根CP2102方案的串口小板再備幾顆CH340作為低成本替代。原因很簡(jiǎn)單FT232R驅(qū)動(dòng)的兼容性確實(shí)好在客戶現(xiàn)場(chǎng)電腦環(huán)境未知的情況下能少很多設(shè)備識(shí)別不了的麻煩。CP2102適合日常開(kāi)發(fā)成本適中性能在線。而CH340則適合做對(duì)成本敏感的量產(chǎn)產(chǎn)品。另外還有個(gè)容易忽略的電壓匹配問(wèn)題。FT232R的IO電平有3.3V和5V兩種版本引腳CP2102通常固定為3.3V邏輯電平。如果你的MCU板子是5V系統(tǒng)直接用3.3V的USB轉(zhuǎn)串口模塊接TXD/RXD電平不匹配會(huì)導(dǎo)致通信不穩(wěn)定甚至燒毀引腳。穩(wěn)妥做法是確認(rèn)模塊是否支持跳線切換電平或者加電平轉(zhuǎn)換芯片。這一點(diǎn)我在幫朋友調(diào)一塊老式51開(kāi)發(fā)板時(shí)踩過(guò)5V單片機(jī)的TXD直接接CP2102的RXD結(jié)果收發(fā)數(shù)據(jù)全是亂碼因?yàn)楦唠娖奖汇Q位了。還有一個(gè)不少人栽過(guò)的坑USB轉(zhuǎn)UART模塊上的TXD要接單片機(jī)上的RXDRXD要接單片機(jī)上的TXD。這個(gè)交叉連接是串口通信最經(jīng)典的接線方式如果兩根線直連了數(shù)據(jù)就會(huì)自己發(fā)給自己自然什么都收不到。別笑我見(jiàn)過(guò)很多新手甚至部分老手換了板子重接線時(shí)也會(huì)順手接反。3. STM32的UART到底怎么配才穩(wěn)從C8T6到HAL庫(kù)的落地經(jīng)驗(yàn)熱搜詞里STM32串口相關(guān)內(nèi)容占了很大篇幅尤其是C8T6串口通信程序和STM32CubeMX串口通信接收說(shuō)明STM32F103C8T6這塊經(jīng)典板子依然是很多人的入門(mén)主力。關(guān)于STM32的UART我建議直接把重點(diǎn)放在如何正確使用中斷接收上而不是簡(jiǎn)單用一個(gè)阻塞式HAL_UART_Transmit發(fā)送完事。先說(shuō)CubeMX配置流程這是我目前在工程上推薦的標(biāo)準(zhǔn)起手式選擇芯片型號(hào)如STM32F103C8T6在Pinout視圖里把USART1的TXPA9和RXPA10配置為異步收發(fā)模式Asynchronous。在Parameter Settings里設(shè)置波特率常用115200、數(shù)據(jù)位8、無(wú)校驗(yàn)、停止位1字長(zhǎng)Word Length選8 Bits。開(kāi)啟USART1全局中斷NVIC Settings里勾選USART1 global interrupt這是接收數(shù)據(jù)不丟字節(jié)的關(guān)鍵。生成代碼后在main.c里調(diào)用HAL_UART_Receive_IT(huart1, rx_buffer, 1)啟動(dòng)單字節(jié)中斷接收。為什么特意強(qiáng)調(diào)第4步因?yàn)镾TM32的HAL庫(kù)接收機(jī)制比較特殊每次調(diào)用HAL_UART_Receive_IT只接收指定長(zhǎng)度的數(shù)據(jù)接收完成后回調(diào)HAL_UART_RxCpltCallback。如果你想持續(xù)接收不定長(zhǎng)數(shù)據(jù)最簡(jiǎn)單的做法是每次接收完一個(gè)字節(jié)后在回調(diào)里重新調(diào)用一次HAL_UART_Receive_IT把接收緩沖區(qū)的指針往后挪一位。這種單字節(jié)中斷循環(huán)重啟的模式可以說(shuō)是串口接收的基石。我見(jiàn)過(guò)的很多新手問(wèn)題出在回調(diào)函數(shù)里做太多事。HAL_UART_RxCpltCallback是在中斷上下文里執(zhí)行的里面如果放HAL_Delay、串口打印大量日志、處理復(fù)雜協(xié)議解析輕則影響實(shí)時(shí)性重則導(dǎo)致中斷嵌套溢出甚至系統(tǒng)死機(jī)。正確做法是回調(diào)里只把數(shù)據(jù)搬進(jìn)環(huán)形緩沖區(qū)置一個(gè)標(biāo)志位真正解析協(xié)議放到主循環(huán)里做。這是從實(shí)際項(xiàng)目里得到的深刻教訓(xùn)——我曾經(jīng)在一個(gè)接收回調(diào)里直接做字符串匹配結(jié)果波特率一高就丟數(shù)據(jù)排查了很久才明白是中斷占用時(shí)間過(guò)長(zhǎng)導(dǎo)致下一個(gè)字節(jié)來(lái)的時(shí)候沒(méi)被及時(shí)接收。關(guān)于DMA接收如果你想做高波特率如921600或大流量數(shù)據(jù)傳輸建議用HAL_UART_Receive_DMA配合空閑中斷IDLE Line Interrupt?;舅悸肥荄MA持續(xù)把數(shù)據(jù)搬進(jìn)一個(gè)大緩沖區(qū)串口空閑中斷觸發(fā)時(shí)用當(dāng)前DMA計(jì)數(shù)器算出這一包數(shù)據(jù)的長(zhǎng)度。這個(gè)方案能大幅降低CPU占用是量產(chǎn)設(shè)備里比較通用的做法。配置時(shí)注意把DMA的Mode設(shè)為Circular循環(huán)模式否則緩沖區(qū)滿了之后DMA就停了。CubeMX里的配置路徑是USART1 - DMA Settings添加RX通道Mode選Circular即可。除了接收波特率精度也是STM32串口穩(wěn)定性的大問(wèn)題。HAL庫(kù)底層會(huì)根據(jù)你選擇的時(shí)鐘頻率自動(dòng)計(jì)算USARTDIV分頻系數(shù)但如果你用的外部晶振是8MHz又把系統(tǒng)時(shí)鐘超頻到72MHzHAL庫(kù)初始化時(shí)如果配置不當(dāng)USART波特率可能就會(huì)出現(xiàn)分頻取整偏差。排查方法很簡(jiǎn)單用示波器或邏輯分析儀抓一下TXD引腳上實(shí)際發(fā)送的波特率和配置的波特率對(duì)比誤差超過(guò)2%就檢查時(shí)鐘樹(shù)。另外再提一個(gè)關(guān)于C8T6的特殊點(diǎn)這塊芯片只有USART1和USART2部分封裝還有USART3引腳少很多人在設(shè)計(jì)PCB時(shí)會(huì)把串口引腳占用掉導(dǎo)致調(diào)試口不夠用。建議在設(shè)計(jì)階段就留出一個(gè)專(zhuān)門(mén)用于調(diào)試日志輸出的UART比如USART1日常打印用正式通信走另一個(gè)UART。這樣既不會(huì)讓調(diào)試信息干擾業(yè)務(wù)數(shù)據(jù)排查問(wèn)題時(shí)也能實(shí)時(shí)看日志效率翻倍。4. 應(yīng)用層才是串口通信的真正分水嶺協(xié)議設(shè)計(jì)、環(huán)形緩沖與常見(jiàn)亂象如果只是點(diǎn)對(duì)點(diǎn)傳幾個(gè)字節(jié)UART沒(méi)什么好談的。真正讓串口通信變得工程化的是應(yīng)用層協(xié)議的設(shè)計(jì)。我經(jīng)常跟人說(shuō)物理層和驅(qū)動(dòng)層決定數(shù)據(jù)能不能跑通應(yīng)用層協(xié)議決定你的代碼能不能長(zhǎng)期維護(hù)、穩(wěn)定運(yùn)行。很多連調(diào)現(xiàn)場(chǎng)兩天兩夜搞不定的bug最后查出來(lái)都跟協(xié)議設(shè)計(jì)不當(dāng)有關(guān)。先聊最常見(jiàn)的裸串口通信失敗場(chǎng)景你可能會(huì)遇到這些問(wèn)題通信偶爾失敗重發(fā)一次就成功大概率是時(shí)序競(jìng)爭(zhēng)或幀格式不嚴(yán)謹(jǐn)接收方?jīng)]有明確的幀頭幀尾判斷導(dǎo)致中間字節(jié)丟失整幀錯(cuò)亂。連續(xù)發(fā)送多個(gè)字節(jié)后出現(xiàn)最后一個(gè)字綴在下一包前面這是典型的粘包問(wèn)題因?yàn)榻邮斩藳](méi)有按協(xié)議幀切割數(shù)據(jù)流。波特率一樣但通信亂碼檢查兩邊是否都配置了相同的校驗(yàn)位、停止位、數(shù)據(jù)位。尤其是某些傳感器模組默認(rèn)是8E18數(shù)據(jù)位偶校驗(yàn)1停止位和常見(jiàn)的8N1不兼容亂碼是必然的。兩個(gè)設(shè)備地電位不共地串口通信雖然只需要TXD/RXD兩根線但收發(fā)雙方必須共地GND。如果沒(méi)有共地尤其在不同電源系統(tǒng)的設(shè)備間通信會(huì)隨機(jī)性丟包。針對(duì)粘包和幀結(jié)構(gòu)我推薦一個(gè)極簡(jiǎn)卻好用的協(xié)議模板幀頭如0xAA 0x55 長(zhǎng)度字節(jié) 命令字 數(shù)據(jù)域 校驗(yàn)字節(jié)如累加和或CRC8。解析時(shí)用狀態(tài)機(jī)逐字節(jié)處理不需要等待整包到達(dá)再統(tǒng)一解析。這樣做的好處是內(nèi)存占用小實(shí)時(shí)性強(qiáng)哪怕一幀數(shù)據(jù)被拆成兩半到達(dá)狀態(tài)機(jī)也能正確恢復(fù)。實(shí)現(xiàn)上可以維護(hù)一個(gè)簡(jiǎn)單枚舉狀態(tài)等待幀頭1 - 等待幀頭2 - 等待長(zhǎng)度 - 等待數(shù)據(jù) - 等待校驗(yàn)。接收側(cè)強(qiáng)烈建議先實(shí)現(xiàn)環(huán)形緩沖區(qū)Ring Buffer。環(huán)形緩沖區(qū)的思想是用一個(gè)定長(zhǎng)數(shù)組和兩個(gè)讀寫(xiě)指針模擬無(wú)限隊(duì)列讀指針追寫(xiě)指針寫(xiě)指針追讀指針。哪怕主循環(huán)里解析速度稍慢只要緩沖區(qū)容量足夠中斷里快速寫(xiě)入的數(shù)據(jù)也不會(huì)被覆蓋。我常用的緩沖區(qū)大小是256字節(jié)或512字節(jié)配合DMA空閑接收足夠應(yīng)付大多數(shù)傳感采集和指令交互場(chǎng)景。如果用HAL庫(kù)單字節(jié)中斷接收環(huán)形緩沖區(qū)的實(shí)現(xiàn)思路完全兼容——回調(diào)里往緩沖區(qū)寫(xiě)一個(gè)字節(jié)主循環(huán)里讀并解析。還有一個(gè)很實(shí)際的問(wèn)題調(diào)試串口時(shí)經(jīng)常需要盯著十六進(jìn)制數(shù)據(jù)看。有人只看ASCII字符串導(dǎo)致二進(jìn)制協(xié)議里的0x00、0xFF被過(guò)濾或顯示成亂碼誤判為通信異常。我建議調(diào)試時(shí)使用支持十六進(jìn)制顯示的串口工具比如稍老點(diǎn)但穩(wěn)定的SSCOM或者開(kāi)源的MobaXterm的串口會(huì)話、VOFA這類(lèi)支持波形顯示的現(xiàn)代工具。尤其當(dāng)你調(diào)試的是IMU、GPS這類(lèi)輸出二進(jìn)制數(shù)據(jù)的模塊十六進(jìn)制視圖幾乎是必須的。很多串口疑難雜癥的根源不是軟件而是干擾和走線。高頻數(shù)字信號(hào)在長(zhǎng)線上傳輸容易產(chǎn)生反射和串?dāng)_如果串口線超過(guò)30厘米且旁邊走過(guò)電機(jī)驅(qū)動(dòng)線、電源線通信穩(wěn)定性會(huì)明顯下降。工業(yè)現(xiàn)場(chǎng)的正確姿勢(shì)是使用RS485差分信號(hào)而不是TTL電平直連或者至少用屏蔽雙絞線并確保兩端地線連接可靠。如果你在調(diào)試一個(gè)電機(jī)或者開(kāi)關(guān)電源附近工作的板子串口莫名亂碼先別急著改代碼試著把串口線拿遠(yuǎn)一點(diǎn)或者改用屏蔽線問(wèn)題可能瞬間消失。5. UART、I2C、SPI、RS232這些總線到底誰(shuí)是誰(shuí)很多初學(xué)者看到USART、UART、I2C、SPI、RS232這些詞就頭大其實(shí)理解起來(lái)沒(méi)有多難關(guān)鍵是搞清楚分層邏輯。UART/USART是外設(shè)控制器RS232/RS485是電氣標(biāo)準(zhǔn)TTL是電平規(guī)范I2C和SPI則是另外兩種完全不同的同步串行總線。它們之間的關(guān)系不是競(jìng)爭(zhēng)的而是應(yīng)用場(chǎng)景不同。拿表格整理一下最直觀總線/規(guī)范時(shí)鐘/同步方式接線數(shù)量傳輸方向典型速率典型距離應(yīng)用場(chǎng)景UART (TTL)異步無(wú)時(shí)鐘線2TX/RX全雙工9600~9216001米以?xún)?nèi)MCU間通信、傳感器模塊、調(diào)試口RS232異步負(fù)邏輯電平3TX/RX/GND全雙工最高約11520015米左右工控設(shè)備、老式儀器RS485異步差分信號(hào)2A/B半雙工通常最高10Mbps可達(dá)1200米工業(yè)總線、多節(jié)點(diǎn)組網(wǎng)I2C同步SCL時(shí)鐘2SDA/SCL半雙工100k/400k/1M1米以?xún)?nèi)傳感器、EEPROM、低速外設(shè)SPI同步SCLK時(shí)鐘4MOSI/MISO/SCLK/CS全雙工可達(dá)幾十Mbps1米以?xún)?nèi)Flash、SD卡、高速ADC/DAC重點(diǎn)提醒一個(gè)常見(jiàn)誤解UART不等于RS232。RS232是早期計(jì)算機(jī)串口的電氣標(biāo)準(zhǔn)電平是負(fù)邏輯-3V到-15V表示13V到15V表示0傳輸距離比TTL電平長(zhǎng)得多但它仍然要依靠UART控制器來(lái)封裝幀格式。也就是說(shuō)RS232只是把UART產(chǎn)生的字節(jié)流翻譯成了適合長(zhǎng)線傳輸?shù)碾娖叫盘?hào)?,F(xiàn)在電腦上幾乎沒(méi)有RS232接口了但很多工控設(shè)備、老式PLC還在用這個(gè)標(biāo)準(zhǔn)所以遇到RS232設(shè)備需要RS232轉(zhuǎn)TTL模塊再進(jìn)MCU。SPI為什么能跑到幾十兆因?yàn)樗峭酵ㄐ胖鳈C(jī)隨時(shí)輸出SCLK時(shí)鐘從機(jī)跟著時(shí)鐘節(jié)奏一位一位收發(fā)不需要雙方獨(dú)立對(duì)齊時(shí)間。I2C為什么只接兩根線就能多設(shè)備通信因?yàn)樗吭O(shè)備地址尋址用開(kāi)漏結(jié)構(gòu)和上拉電阻實(shí)現(xiàn)線與邏輯。而UART沒(méi)有地址概念天然適合一對(duì)一通信想一對(duì)多就需要RS485這種物理層用地址幀擴(kuò)展或者靠上層協(xié)議自行定義尋址規(guī)則。選總線的時(shí)候我一般按這個(gè)思路判斷數(shù)據(jù)量小、距離近、設(shè)備少優(yōu)先I2C或UART數(shù)據(jù)量大、速率要求高選SPI或UART高波特率工業(yè)現(xiàn)場(chǎng)、遠(yuǎn)距離、多節(jié)點(diǎn)直接RS485要和舊設(shè)備對(duì)接老老實(shí)實(shí)用RS232電平轉(zhuǎn)換。每種方案都有它不可替代的場(chǎng)景沒(méi)有絕對(duì)的好壞之分只有合不合適。順帶說(shuō)一嘴GD32這類(lèi)國(guó)產(chǎn)MCU的UART外設(shè)和STM32在寄存器層面高度相似但細(xì)節(jié)上有差別。我調(diào)試過(guò)GD32F103系列的串口比較明顯的一個(gè)點(diǎn)是GD32的USART波特率寄存器計(jì)算方式和STM32在時(shí)鐘源不同時(shí)可能略有差異。如果你的GD32板子串口通信異常先檢查庫(kù)函數(shù)版本和時(shí)鐘樹(shù)配置其次看參考手冊(cè)里的波特率計(jì)算公式不要盲目照搬STM32的初始化代碼。6. 新平臺(tái)遷移與協(xié)議棧擴(kuò)展ESP32、Verilog及UART轉(zhuǎn)別的口如果只是停留在STM32這一種平臺(tái)UART的很多設(shè)計(jì)思路還是受限的。換個(gè)平臺(tái)往往能讓你更深刻地理解串口通信本身是平臺(tái)無(wú)關(guān)的協(xié)議剩下的只是外設(shè)寄存器怎么操作而已。ESP32的UART使用體驗(yàn)就很典型。它的UART外設(shè)功能非常豐富除了常規(guī)的發(fā)送接收還內(nèi)置了RS485模式支持、硬件流控、甚至還能直接映射到任意GPIO。ESP32的IDF里使用UART也簡(jiǎn)單先uart_config_t結(jié)構(gòu)體里配置波特率、數(shù)據(jù)位、停止位、校驗(yàn)位然后調(diào)用uart_driver_install安裝驅(qū)動(dòng)再通過(guò)uart_read_bytes和uart_write_bytes讀寫(xiě)數(shù)據(jù)。如果你在ESP32上做藍(lán)牙或WiFi透?jìng)黜?xiàng)目基本都會(huì)碰上一路UART和MCU通信——這個(gè)用法跟STM32沒(méi)本質(zhì)區(qū)別只是API風(fēng)格變了。Verilog實(shí)現(xiàn)UART是另一個(gè)經(jīng)典話題。很多學(xué)FPGA的朋友會(huì)寫(xiě)一個(gè)最簡(jiǎn)單的UART發(fā)送模塊練手核心就是一個(gè)狀態(tài)機(jī)從空閑態(tài)跳到起始位、數(shù)據(jù)位、停止位波特率通過(guò)時(shí)鐘分頻計(jì)數(shù)產(chǎn)生。熱搜詞里的UART奇偶校驗(yàn) Verilog也說(shuō)得比較明白奇偶校驗(yàn)設(shè)計(jì)思路是發(fā)送端統(tǒng)計(jì)數(shù)據(jù)位中1的個(gè)數(shù)奇校驗(yàn)要求含校驗(yàn)位在內(nèi)1的總數(shù)為奇數(shù)偶校驗(yàn)則要求為偶數(shù)接收端再統(tǒng)計(jì)一次并比對(duì)。這里有個(gè)經(jīng)驗(yàn)別一上來(lái)就寫(xiě)帶FIFO和校驗(yàn)的復(fù)雜模塊先用邏輯分析儀把最基礎(chǔ)的收發(fā)跑通再逐級(jí)加功能。FPGA調(diào)試串口比MCU麻煩的地方在于沒(méi)有現(xiàn)成的HAL庫(kù)一切時(shí)序都得自己算但好處是你能從最底層看到UART的每一位是怎么走線的對(duì)理解協(xié)議的幫助特別大。搜熱詞里還有UART轉(zhuǎn)CAN2.0電路這個(gè)方向我也想多寫(xiě)幾句。UART轉(zhuǎn)CAN不是直接拿線連而是需要一顆協(xié)議轉(zhuǎn)換芯片比如MCP2515配合TJA1050或直接使用自帶CAN控制器的MCU?;舅悸肥荕CU的UART收到數(shù)據(jù)后按自定義協(xié)議解析再通過(guò)SPI把數(shù)據(jù)寫(xiě)入MCP2515的發(fā)送緩沖區(qū)最后經(jīng)TJA1050差分收發(fā)器發(fā)到CAN總線。反向同理。這里真正的難點(diǎn)不是CAN控制器操作而是兩種協(xié)議的數(shù)據(jù)映射策略——CAN幀有ID、DLC、Data字段UART只是一串字節(jié)流兩邊的幀格式如何對(duì)應(yīng)決定了整個(gè)網(wǎng)關(guān)設(shè)計(jì)的復(fù)雜程度。如果你是在做一個(gè)串口轉(zhuǎn)CAN網(wǎng)關(guān)的項(xiàng)目建議先明確一個(gè)UART幀對(duì)應(yīng)一個(gè)CAN幀還是一個(gè)CAN幀按長(zhǎng)度拆成多個(gè)UART幀這個(gè)映射規(guī)則定了整個(gè)軟件架構(gòu)才好往下走。平臺(tái)遷移時(shí)最常見(jiàn)的坑是什么呢其實(shí)是用STM32的思維寫(xiě)別的芯片。每個(gè)芯片外設(shè)的別名、寄存器命名、中斷標(biāo)志位清除方式都不同當(dāng)你的代碼從STM32標(biāo)準(zhǔn)庫(kù)遷到HAL庫(kù)再遷到GD32庫(kù)或者ESP32 IDF時(shí)UART初始化這幾行配置看似差不多但底層的細(xì)節(jié)差異足以讓你排查一個(gè)下午。我的經(jīng)驗(yàn)是遷移前先看官方例程里的UART收發(fā)例程改平臺(tái)先從改例程開(kāi)始而不是把老代碼原樣粘貼然后滿屏報(bào)錯(cuò)。串口調(diào)試這件事技巧再多也離不開(kāi)扎實(shí)的觀察和邏輯推理。有些人不看波形不看日志一拍腦袋就說(shuō)是芯片壞了或者編譯器有問(wèn)題。其實(shí)大多數(shù)情況下拿著邏輯分析儀抓一遍T(mén)XD/RXD電平對(duì)比一下數(shù)據(jù)手冊(cè)里的時(shí)序圖問(wèn)題就水落石出了。只要把UART的幀結(jié)構(gòu)、電平標(biāo)準(zhǔn)、中斷機(jī)制和協(xié)議設(shè)計(jì)這四塊基石打牢別說(shuō)調(diào)試STM32就算是搗鼓新平臺(tái)、新芯片你也能一通百通。本文還有配套的精品資源點(diǎn)擊獲取