一、串行通信簡介
定義:串行通信是一種使用串行數據流來傳送數據的通信協議,相區別於“並行通信”;串行通信的特點是使用一根電線完成發送數據,同時使用另一根電線完成接受數據。
上面提到“串行通信”只是一種協議,那麼對這個協議的實現分別有:RS232標準、RS485標準。這就意味着在開發這些協議對應的“上位機軟件”的時候,可以同一個電腦編程“串口通信編程庫”。
二、串口參數配置
-
端口 port : [com1、com2、等]
指使用哪個串行端口通信。 -
波特率 Baud Rate : [2400、4800、9600、19200 等]
這是一個衡量符號傳輸速率的參數。指的是信號被調製以後在單位時間內的變化,即單位時間內載波參數變化的次數,如每秒鐘傳送240個字符,而每個字符格式包含10位(1個起始位,1個停止位,8個數據位),這時的波特率爲240Bd,比特率爲10位*240個/秒=2400bps。高波特率常常用於放置的很近的儀器間的通信 -
數據位 Data Bits : [5、6、7、8]
這是衡量通信中實際數據位的參數。當計算機發送一個信息包,實際的數據往往不會是8位的,標準的值是6、7和8位。 -
停止位 Stop Bits : [1、1.5、2]
用於表示單個包的最後一位。典型的值爲1,1.5和2位。由於數據是在傳輸線上定時的,並且每一個設備有其自己的時鐘,很可能在通信中兩臺設備間出現了小小的不同步。因此停止位不僅僅是表示傳輸的結束,並且提供計算機校正時鐘同步的機會。串口通信首先是設置波特率,由此決定每位數據在線上維持的時間。
以傳輸1bit所需要的時間作爲一個單位時間。
1位停止位表示 停止信號在線上維持一個單位時間。
2位停止位表示 停止信號在線上維持兩個單位時間。 -
奇偶校驗位 Parity : [Odd、Even、NONE、Mark、Space]
奇偶校驗是對數據傳輸正確性的一種校驗方法。在數據傳輸前附加一位奇校驗位,用來表示傳輸的數據中"1"的個數是奇數還是偶數,爲奇數時,校驗位置爲"0",否則置爲"1",用以保持數據的奇偶性不變。
Odd:奇校驗
Even:偶校驗
NONE:無校驗
Mark:校驗位始終爲1(不常用)
Space:校驗位始終爲0(不常用)
三、硬件相關知識
-
單工、半雙工、全雙工
單工數據傳輸只支持數據在一個方向上傳輸。
半雙工數據傳輸允許數據在兩個方向上傳輸,但是在某一時刻只允許數據在一個方向上傳輸。
全雙工數據通信允許數據同時在兩個方向上傳輸。 -
RS232、RS485 各自的特點
RS232:
接口的信號電平值較高
傳輸速率有侷限(距離越遠傳輸越慢)
傳輸距離有限,最多隻能通信幾十米。
通信的時候只能兩點之間進行通信,不能夠實現多機聯網通信
全雙工數據通信
RS485:
採用差分信號
通信速率快,最大傳輸速度可以達到 10Mb/s 以上
傳輸距離最遠可以達到 1200 米左右
可以在總線上進行聯網實現多機通信
半雙工通信 -
下位機與上位機連線方式
方式一
方式二
方式三
方式四
四、硬件芯片實現串口編程
一般MCU自帶串口功能,只需配置對應的“寄存器”或使用對應的庫函數,即可實現串口功能開發。
注意:上位機和下位機所使用的串口參數要一致,否則無法完成通信。比如硬件用波特率9600,那麼上位機軟件也要使用波特率爲9600。
五、軟件實現串口通信的相關知識
在各種計算機系統平臺下,以及各種上位機開發平臺上都提供了串口庫函數,通過調用庫函數即可完成串口通信開發。
然後使用軟件界面開發函數開發出“交互界面”和“數據處理邏輯”即可;
1. 阻塞模式、非阻塞模式
在開發上位串口中,在初始化串口的時候,可以選擇是“阻塞模式”或“非阻塞模式”,這個模式選擇隻影響串口接收函數:
- 阻塞模式:調用接收函數後,接收函數阻止程序繼續運行,直到收到串口數據
- 非阻塞模式:調用接收函數之後,函數判斷串口接收緩存是否有數據,有則返回數據,沒有則之間返回0,即,無論收到或沒有收到數據,接收函數都不阻塞程序繼續運行。
2. 結合各自特點,該選哪個阻塞模式?
答案是看你的需求,如果你所需功能只是,定期看一看有沒有收到數據(無論收到數據或沒有收到數據都有功能意義),那麼根據你這個需求你應該使用“非阻塞模式”。而大多數功能需求情況是,只有收到數據的時候纔有功能意義(在沒有收到數據的時候,不需要做任何處理)。因此你應該使用“阻塞模式”。
- “阻塞模式”單線程編程示例(使用僞代碼):
//使用C編程的僞代碼
int main() {
inti_uart();//初始化串口
send("hello hardware!");
delay(100);
while(true) {
char buffer[10]; //緩存
receive(buffer, 10);//阻塞式讀取串口數據
process(buffer, 10);//處理收到的數據。只要收到數據後,才執行次行以及其後代碼
}
return 0;
}
- “阻塞模式”多線程編程示例(使用僞代碼):
//使用C++編程的僞代碼
static void thread_function() //子線程函數,在這個線程中專門循環接收,並處理數據
{
while(true) {
char buffer[10]; //緩存
receive(buffer, 10);//阻塞式讀取串口數據
process(buffer, 10);//處理收到的數據。只要收到數據後,才執行次行以及其後代碼
}
}
int main() {
inti_uart();
start_thread(thread_function);//開啓線程
while(ture) { //主線程:一直給硬件發送“hello hardware!”
send("hello hardware!");
delay(100);
}
return 0;
}
3. 上位機實現查找串口設備的原理
-
在上位開發中實現連接下位機設備的時候,經常需要“查找設備”的功能。以方便用戶連接下位機設備。
那麼在使用串口通信的時候怎麼實現呢?
在開發平臺上一般會有提供這個“串口設備列舉函數”,調用這個系統函數會返回可用的串口設備號。這是完成了查找設備的基礎。使用這個功能可以給用戶返回一個設備列表,然後讓用戶選擇連接哪個。 -
如果更進一步,要實現查找設備並自動連接呢?
由於在查找出來的多個串口設備中只有一個纔是真正的你要連的設備,怎樣區別出這個設備呢,這就需要上位機和下位機之間建立一套“自家設備識別協議”。簡單實現如下:
- 握手協議(相當於上位機與下位機自定義私有協議[API]):
上位機給下位機通過串口發送“who are you?(可以是任意自定義字符串)”
自家下位機收到這個字符串後給上位機發送“I’m your budy!(可以是任意自定義字符串)” - 握手協議使用方法:
上位機軟件從查找到的串口設備列表中選一個設備,然後打開設備並按照“握手協議”發送數據,同時監聽(讀取串口接收數據)這個設備返回的數據。如果返回的數據與“握手協議”所規定的相匹配,則可確定這個設備就是自家的設備。如果不匹配或根本沒有收到任何數據(監聽超時)則不是自家的設備。依次對上位機查找到的串口設備做這個判斷過程,即可從“查找到的串口設備中”過濾出來“自家的串口設備”
六、上位機軟件串口通信測試方法
既然開發串口設備相關的上位機軟件,那麼在開發軟件中避免不了連接上硬件測試。那麼肯定會遇到以下實際問題:
- 怎樣確認所寫的串口代碼是否可以與硬件建立通信?
- 調試的時候想要看一下下位機收到的具體字符串(包括二進制數據),從而判斷邏輯處理是否正確?
- 想要實時查看發送給串口的字符與二進制數據?從而判斷軟件執行進度與過程。
- 以上這些問題在下位機開發者幫助的情況下可解決。但是當對方忙或者其他原因導致沒法幫助的時候,你怎麼辦?
以上這些問題是你在開發串口上位機軟件過程中是實實在在的問題。
解決這個問題最方便以及實際的方法是使用“虛擬下位機”。
- 使用硬件設備虛擬設備模擬下位機:
- 方法一:
如果你使用的臺式電腦主機後面有“RS232”插口(針),那麼找到串口的Rx針和Rx針,把兩者用電線連接起來即可。
連起來的結果是,你上位機軟件給下位機發送過去的字符串會立即返回來,上位機就收到了自己剛剛發送出去的數據。雖然有侷限性,但是在其他方式不方便的時候,也不失爲一種手段。 - 方法二:
使用一個usb轉串口線連接到電腦的usb口。然後同樣短接轉接頭的Rx和Tx接頭。即可實現和方法一同樣的效果。
- 使用軟件虛擬下位:
- 在windows操作系統下:
爲了在解決以上問題的時候有更好的使用體驗,可以使用“VSPD虛擬串口軟件”。下載並安裝好這個軟件後。使用這個軟件建立2個虛擬串口端口,然後把這兩個虛擬串口使用軟件的“連接”功能連接起來。在下載任意一個“串口小助手軟件”,使用“串口小助手軟件”來假裝是下位機硬件設備。打開“串口小助手軟件”並連接一個剛剛虛擬好的任意一個串口端口。然後打開自己的上位機軟件連接剩餘的一個虛擬串口端口。這樣的話你上位機軟件發送給下位機的所有字符串與數據都會在“串口小助手軟件”中實時顯示。並且可以通過“串口小助手軟件”給自己的軟件發送數據,用於模擬下位機硬件給上位機軟件反饋的數據。 - 在Linux/MacOS操作系統下:
在非windows系統下沒有類似VSPD的串口虛擬軟件,但是在網上可以找到一個“虛擬串口python腳本”。不知道是哪位大牛寫的,總之非常實用。代碼很簡短,附在下方(注意,在網絡上發現有的版本是二進制的,且其中代碼有好幾千行,懷疑是假的或是帶有木馬的,本人對python不是特別瞭解。總之這裏所附的代碼是乾淨的,可以通過自己讀代碼來確定)
文件名字:linux_virtual_serials.py
使用方法,在命令行裏面執行:python linux_virtual_serials.py
使用環境:python
#! /usr/bin/env python
#coding=utf-8
import pty
import os
import select
def mkpty():
# 打開僞終端
master1, slave = pty.openpty()
slaveName1 = os.ttyname(slave)
master2, slave = pty.openpty()
slaveName2 = os.ttyname(slave)
print '\nslave device names: ', slaveName1, slaveName2
return master1, master2
if __name__ == "__main__":
master1, master2 = mkpty()
while True:
rl, wl, el = select.select([master1,master2], [], [], 1)
for master in rl:
data = os.read(master, 128)
print "read %d data." % len(data)
if master==master1:
os.write(master2, data)
else:
os.write(master1, data)