精講串口通信協(xié)議,小白少走彎路的東西
精講串口通信協(xié)議,小白少走彎路的東西

串口通信協(xié)議,基本上是每個單片機開發(fā)人員必會的協(xié)議,是所有通信協(xié)議里面最最常用的。但是很多人并沒有去研究過細節(jié),這篇文章干貨滿滿,將帶你深入串口協(xié)議的內(nèi)部。
1、串口協(xié)議簡介
什么是串口通信?
簡而言之,串口通信就是通過一根導線(TX,只討論發(fā)送,當然一定還包括GND),將需要的數(shù)據(jù)按bit流傳送給接收端,既然是通信,參與的雙方當然要制定數(shù)據(jù)的傳輸方式以及規(guī)范。
隨便打開一個串口助手都可以看到,如果需要使用串口就需要配置端口、波特率、數(shù)據(jù)位、停止位、校驗位、流控這些參數(shù)。
如果接收端和發(fā)送端的設(shè)置不同,就可能會導致數(shù)據(jù)異常。
端口:這個就不用解釋了,使用串口芯片安裝驅(qū)動后就可以在電腦上看到端口號了。
現(xiàn)在,我們從電平信號角度分析一下波特率、數(shù)據(jù)位、停止位、校驗位、流控(由于用的比較少,暫不分析)的作用。
2、電平信號分析
打開串口工具,分別調(diào)整如下參數(shù)
①調(diào)整參數(shù)9600/8/N/1,發(fā)送十六進制數(shù)據(jù)55 AA
②調(diào)整參數(shù)115200/8/N/1發(fā)送十六進制數(shù)據(jù)55 AA
③調(diào)整參數(shù)9600/7/N/1發(fā)送十六進制數(shù)據(jù)55 AA
④調(diào)整參數(shù)9600/8/O/1發(fā)送十六進制數(shù)據(jù)55 BA
⑤調(diào)整參數(shù)9600/8/E/1發(fā)送十六進制數(shù)據(jù)55 BA
⑥調(diào)整參數(shù)9600/8/N/2發(fā)送十六進制數(shù)據(jù)55 AA
其中B代表起始位,E代表停止位,很明顯看出是先傳輸?shù)?/span>低bit,再傳輸?shù)?/span>高bit。
根據(jù)①②分析,調(diào)整波特率并不會影響整體波形形狀,只有bit的持續(xù)時長不同,9600波特率時的一個bit大概占104us,115200波特率大概是9us,這是可以算出來的9600:1000ms/9600 = 104.1us,115200:1000/115200=8.6us。分析可知波特率越大,傳輸速度越快。
根據(jù)①③分析,調(diào)整數(shù)據(jù)位后,55本來應(yīng)該是01010101但是數(shù)據(jù)位調(diào)整到7之后,55變成了1010101,AA本來是10101010變成了0101010,分析可知錯誤的數(shù)據(jù)位會將傳輸?shù)淖止?jié)從高位開始截斷,可能導致數(shù)據(jù)丟失。一般來說除非特殊需求,否則不會調(diào)整數(shù)據(jù)位參數(shù)。
根據(jù)①④⑤分析,將校驗位調(diào)整至奇校驗(ODD)后,傳輸的bit多了一個奇校驗位,就是說如果本來的8個bit的bit位按位加起來是偶數(shù),則在最高位需要補一位1,否則補0,55傳輸變成了 (1)01010101,BA變成(0)10111010,同樣偶校驗(EVEN)、MASK校驗(校驗位始終為1)、SPACE校驗(校驗位始終為0)是相同的道理。
根據(jù)①⑥分析,將停止位設(shè)置為2后,傳輸?shù)慕Y(jié)束位(圖片中的E)變成之前的2倍,分析可知,設(shè)置停止位,可改變數(shù)據(jù)傳輸完成后的停止位bit持續(xù)的時間。
3、為什么需要起始位和停止位?
波特率、數(shù)據(jù)位、校驗位很好理解,都是為了實現(xiàn)不同的業(yè)務(wù)需求,但是起始位和停止位存在的意義是什么呢?
起始位:平時這根導線上是高電平,如果接收方檢測到低電平了,說明要開始接受數(shù)據(jù)了,是發(fā)送方通知接收方開始接受的一種方式。(不考慮流控)
停止位:發(fā)送方通知接收方發(fā)送完成的一種方式。發(fā)送方發(fā)送完一個字節(jié)后,“暫停一會”再繼續(xù)發(fā)送下一個字節(jié)。
這時帥氣的小伙伴就要問了,起始位存在的意義可以理解,但是為什么需要停止位呢?不是提前都約定好了傳輸8個bit,接收方不能就接受到8個bit后認為一個字節(jié)傳輸結(jié)束就OK了嗎?
能思考到這一步的小伙伴已經(jīng)很棒了。如果我們是設(shè)計師,我們從設(shè)計的角度設(shè)計一下接收方接受數(shù)據(jù)的偽代碼
//接收端接收數(shù)據(jù)線程
while(1) {
//接收到起始位下降沿電平
if((isGetStartFlag == 0) && getDownEdge()) {
Delayus(52);
//檢測起始位低電平
if(isLowPin()) {
isGetStartFlag = 1;
getBitIndex = 0;
}
}
//已經(jīng)接收到起始位
if(isGetStartFlag) {
Delayus(104);
if(getHighOrLow() == high) {
//接收到1bit
}else {
//接收到0bit
}
getBitIndex++;
if(getBitIndex == 8) {
//字節(jié)接收完成
isGetStartFlag = 0;
getBitIndex = 0;
}
}
}
按照這種偽代碼邏輯,如果不存在停止位,會產(chǎn)生兩個問題。
1、可能無法檢查下降沿
如果上一個字節(jié)最后一次傳輸是0,而下一個字節(jié)的起始位也是0,那么下一個字節(jié)的起始位就檢測不到下降沿,無法觸發(fā)下一個字節(jié)的傳輸,就會丟失數(shù)據(jù)。這時帥氣的小伙伴又要問了,那我不檢查下降沿,只檢查低電平不行嗎?直接把getDownEdge換成isLowPin函數(shù)不就可以了嗎,確實可以,這樣又會引入第二個問題。
2、時鐘同步問題
假設(shè)A跟B說,10分鐘后你提醒我喝水,然后B就看著自己的表,10分鐘后提醒A,這時A一看表,才過去9分鐘。這種場景現(xiàn)實也會出現(xiàn),造成的原因是A和B的表不同步或者有誤差這個誤差的引入就導致了A和B所指的“10分鐘”不相同,在我們這個串口的場景里,就是發(fā)送方和接收方約定了9600波特率,也就相當于約定了104.1us傳輸一個字節(jié),按照上面的偽代碼,幾個采集點應(yīng)該是
但是由于誤差,可能導致采集點偏移為
這兩個圖能大致表達時鐘不同步,導致接收設(shè)備的采集點后移,如果這時引入了停止位,第二個BYTE開始時,由于還是從下降沿開始采集,所以會重置上一個BYTE引入的誤差。
4、總結(jié)
這篇干貨基本把串口協(xié)議分析透徹了,大家在分析協(xié)議的時候,最好是從設(shè)計的角度去思考這樣設(shè)計的作用,如果有什么分析不對的地方,歡迎大家指正。