全網最適合入門的物件導向程式設計教程:04 類和物件的 Python 實現-為自定義類新增方法(PySerial 庫接收串列埠資料)
摘要:
本文我們主要講解了如何為自定義類新增方法,pyseria 庫的基本使用(串列埠資料收發、serial.Serial 類的屬性和方法),VSPDPro 虛擬串列埠軟體使用方法等,並使用自定義的串列埠類和 PC 主機進行串列埠資料收發。
往期推薦:
學嵌入式的你,還不會物件導向??!
全網最適合入門的物件導向程式設計教程:00 物件導向設計方法導論
全網最適合入門的物件導向程式設計教程:01 物件導向程式設計的基本概念
全網最適合入門的物件導向程式設計教程:02 類和物件的 Python 實現-使用 Python 建立類
全網最適合入門的物件導向程式設計教程:03 類和物件的 Python 實現-為自定義類新增屬性
更多精彩內容可看:
給你的 Python 加加速:一文速通 Python 平行計算
一文搞懂 CM3 微控制器除錯原理
肝了半個月,嵌入式技術棧大彙總出爐
電子計算機類比賽的“武林秘籍”
一個MicroPython的開源專案集錦:awesome-micropython,包含各個方面的Micropython工具庫
文件和程式碼獲取
可訪問如下連結進行對文件下載:
https://github.com/leezisheng/Doc
本文件主要介紹如何使用 Python 進行物件導向程式設計,需要讀者對 Python 語法和微控制器開發具有基本瞭解。相比其他講解 Python 物件導向程式設計的部落格或書籍而言,本文件更加詳細、側重於嵌入式上位機應用,以上位機和下位機的常見串列埠資料收發、資料處理、動態圖繪製等為應用例項,同時使用 Sourcetrail 程式碼軟體對程式碼進行視覺化閱讀便於讀者理解。
相關示例程式碼獲取連結如下:https://github.com/leezisheng/Python-OOP-Demo
正文
可以看到上一小節,我們為 SerialClass 類新增了串列埠裝置名、波特率、資料位等串列埠相關的屬性,但是注意到物件導向程式設計的重點在於不同物件之間的互動。我們感興趣的是,觸發某些行為可以使屬性發生變化或與外界產生互動。在類中定義函式就相當於定義類的方法,回想一下我們在微控制器上使用串列埠時往往進行開啟串列埠、傳送資料、接收資料和關閉串列埠等操作,在 PC 端串列埠操作與微控制器上類似。這裡我們先為類新增方法,具體實現先省略,程式碼如下:
class SerialClass:
_# 注意:特殊方法“__init__”前後分別有兩個下劃線!!!_
def __init__(self,port,baudrate,bytesize,parity,stopbits):
self.devport = port
self.devbaudrate = baudrate
self.devbytesize = bytesize
self.devparity = parity
self.devstopbits = stopbits
_# 開啟串列埠_
def OpenSerial(self):
_# __TODO:__開啟串列埠方法待完成_
pass
_# 關閉串列埠_
def CloseSerial(self):
_# __TODO:__開啟串列埠方法待完成_
pass
_# 串列埠讀取_
def ReadSerial(self):
_# __TODO:__串列埠讀取方法待完成_
pass
_# 串列埠寫入_
def WriteSerial(self):
_# __TODO:__串列埠寫入方法待完成_
pass
這時,我們可以利用 dir(obj)方法獲得類的物件例項的所有屬性和方法名,dir(obj)返回一個 list。我們使用 for 迴圈列印,程式碼如下:
_# serdev是SerialClass類的一個例項化物件_
for item in dir(serdev):
print(item)
可以看到,我們已經給串列埠類新增了具體方法,這裡,你可能會問,那這些方法具體該怎麼實現呢?難道說要我們調作業系統的驅動函式讀取串列埠、造輪子對資料解析?當然不可能,Python 強大的第三方庫什麼都有,這不,它來了:
pyserial,一個實用的串列埠通訊 python 庫
pySerial 是 Python 中用於操作串列埠的第三方模組,它支援 Windows、Linux、OSX、BSD 等多個平臺。如果要使用 pySerial 模組,首先必須保證 Python 版本高於 Python 2.7 或者 Python 3.4。另外,如果你是用的是 Windows 系統,那必須使用 Win7 及以上的版本。
pySerial 的安裝很簡單,只需要執行一條命令:pip install pyserial。安裝完成後,只需要在 Python 程式碼中使用** import serial **語句匯入該模組即可。
pySerial 中主要的類就是 class serial.Serial,官方文件說明如下:
常用引數含義如下:
引數名稱 | 含義 |
---|---|
port | 定義裝置名稱 |
baudrate | 波特率:波特率是指串列埠通訊中每秒鐘傳輸的符號數,單位是波特(baud)。它決定了資料傳輸的速度和效率。在串列埠通訊中,收發雙方必須使用相同的波特率才能正常通訊。典型值為:9600, 19200, 38400, 57600, 115200。 |
bytesize | 資料位:資料位是指在每個資料包中傳輸的實際資料位數。它表示每個資料包中攜帶的有效資訊量。常見的資料位長度有 5 位、7 位和 8 位。典型值為: FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS。 |
parity | 設定校驗位,奇偶校驗是一種錯誤檢測方式,用於檢查資料傳輸過程中是否出現錯誤,可選擇偶校驗或奇校驗。典型值為:PARITY_NONE, PARITY_EVEN, PARITY_ODD PARITY_MARK, PARITY_SPACE。 |
stopbits | 停止位是指在資料包的末尾新增的一個額外的位,用於標識一個資料包的結束。停止位的長度可以是 1 位、1.5 位或 2 位。典型值為: STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO。 |
timeout | 設定讀取超時值(以秒為單位):可選引數為-0、None、x。 |
write_timeout | 設定寫入超時值(以秒為單位):可選引數為-0、None、x。timeout = None:永遠等待/直到收到請求的位元組數。timeout = 0:非阻塞模式,任何情況下立即返回,返回零個或多個,最多可達請求的位元組數。 |
serial.Serial 類常用方法如下:
方法名稱 | 作用 |
---|---|
serial.open() | 開啟串列埠 |
serial.close() | 關閉串列埠 |
serial.isOpen() | 判斷串列埠是否開啟 |
serial.write(data) | 寫串列埠資料 data |
serial.read(size) | 讀串列埠 size 個資料 |
serial.flushInput() | 清除輸入緩衝區資料 |
serial.flushOutput() | 中止當前輸出並清除輸出緩衝區資料 |
serial.inWaiting() | 判斷當前接收的資料 |
serial.readline() | 讀一行資料,以/n 結束,要是沒有/n 就一直讀,阻塞 |
接下來看我們的示例程式碼:
class SerialClass:
def __init__(self,devport,devbaudrate,devbytesize,devparity,devstopbits):
_# 直接傳入serial.Serial()類_
self.dev = serial.Serial()
self.dev.port = devport
self.dev.baudrate = devbaudrate
self.dev.bytesize = devbytesize
self.dev.parity = devparity
self.dev.stopbits = devstopbits
def OpenSerial(self):
self.dev.open()
def CloseSerial(self):
self.dev.close()
def ReadSerial(self):
_# 非阻塞方式按行讀取_
data = self.dev.readline()
_# 收到為二進位制資料,用utf-8編碼將二進位制資料解碼為unicode字串_
_# 字串轉為int型別_
data = int(data.decode('utf-8', 'replace'))
return data
def WriteSerial(self,write_data):
_# 非阻塞方式寫入_
self.dev.write(write_data.encode())
_# write的輸入引數必須是bytes 格式,_
_# 字串資料需要encode()函式將其編碼為二進位制資料_
_# \r\n表示換行回車_
self.dev.write('\r\n'.encode())
這裡可以看到,為方便講解,我們直接呼叫 pyserial 庫中函式完成串列埠的開啟關閉和讀寫等功能:
- 在初始化函式中,我們定義 self.dev 屬性為串列埠裝置,用其接收串列埠物件,相當於 self.dev 就是 serial.Serial 類的例項物件,其後用其他入參給 self.dev 設定串列埠通訊引數,完成串列埠裝置物件的建立;
- 在開啟串列埠和關閉串列埠函式中,我們使用 self.dev.open()、self.dev.close()語句,相當於直接呼叫 serial.open()和 serial.close()方法;
- 重點就在收發函式中,需要特別注意的有兩點:
① 串列埠接收到的是二進位制資料,如果接收到的 data 全是英文,就需要用 utf-8 編碼將二進位制資料解碼為 unicode 字串。如果 data 裡包含中文,則最好以 gb18030 編碼將二進位制資料解碼為 unicode 字串。同時,有時候由於帶中文,或者由於串列埠的傳輸線纜出現接觸不良等原因,會產生錯誤或者亂碼,如果直接解碼,就會報錯,為了能夠順暢的解碼串列埠列印,避免這種情況傳送,decode 的引數里加上“replace”即可。它實現的作用是,如果解碼的過程中遇到錯誤,會自動以問號?代替解碼失敗的字元。
② 在串列埠傳送中,pyserial 的文件註明了,write 的輸入引數必須是 bytes 格式的(也就是二進位制資料),python3 裡對字串和二進位制資料流有明確的區分,文字總是 unicode 編碼儲存的,由 str 型別表示。二進位制資料則由 bytes 型別表示,所以字串資料需要 encode()函式將其編碼為二進位制資料,然後才可以順利傳送。
串列埠類的方法已然寫好,接下來如果進行測試呢?我們需要買一個微控制器和 usb 轉 ttl 模組連線電腦,進行串列埠類的方法測試?(當然不是,開個玩笑)
在寫與微控制器通訊的上位機軟體時,如果使用微控制器的串列埠來實際除錯,那麼我們至少還需要一個 USB 轉串列埠,這樣才能讓微控制器和電腦串列埠通訊,接著我們還需要在微控制器上執行程式和串列埠相關的程式,以便我們知道資料傳輸的狀態,這無疑加大的開發的難度。這裡,我們使用虛擬串列埠軟體即可,虛擬串列埠軟體是一種模擬物理序列介面的軟體,它完全複製了硬體 COM 介面的功能,並且將被作業系統和序列應用程式識別為真實埠。現實生活中,虛擬串列埠用處很多。比如:你的應用程式檢測序列輸入資料的時候,方便除錯。還比如:多個有應用程式之間使用串列埠通訊。
這裡我們使用 VSPDProv6.9 軟體,建立虛擬串列埠,軟體下載安裝教程點選連結:https://www.xue51.com/soft/9349.html
這裡我們建立兩個相互連線的串列埠 com11 和 com17,虛擬的串列埠需要成對建立,來指明他們的連線關係,就好比各種連線線的公投和母頭一樣。我們建立好虛擬串列埠連線以後,就可以使用它們來通訊了,我們可以選擇任意一個串列埠助手軟體來與我們寫的 python 串列埠類進行通訊。這裡,我們選擇 xcom 軟體進行測試,xcom 軟體的安裝下載教程可以點選連結: https://blog.csdn.net/qq_41573860/article/details/103796913
開啟 xcom 軟體,我們先進行相關串列埠引數的配置,具體如下圖所示:
此時,我們在 Python 程式中,建立串列埠裝置例項,連線 com17,程式碼如下:
_# 生成串列埠類的例項_
serdev = SerialClass(devport = "COM17",
devbaudrate = 115200,
devbytesize = serial.EIGHTBITS, _# 資料位長度為8位_
devparity = serial.PARITY_NONE, _# 無奇偶校驗_
devstopbits = serial.STOPBITS_ONE _# 1位停止位_
)
我們首先嚐試使用 Python 完成串列埠傳送功能,這裡我們定義了一個 count 計數變數,初始值為 0,每次傳送完成後遞增,迴圈 100 次後關閉串列埠,程式碼如下:
serdev.OpenSerial()
count = 0
while True:
# 必須使用全域性變數,不然每次迴圈都是一個新的count
# 關於原因可以自行檢視變數的生存期和作用域相關知識點
count = count + 1
serdev.WriteSerial(str(count))
if(count == 100):
break
serdev.CloseSerial()
print("device close")
執行結果如下:
接下來,我們嘗試使用 Python 完成串列埠接收功能,我們在 xcom 中定時傳送,在 Python 中輪詢接收並列印接收的資料,這裡我們匯入 time 庫,列印接收時間。time 庫是 Python 中處理時間的標準庫,是最基礎的時間處理庫。time 庫主要用於計時和獲取系統時間,time.ctime()函式用於獲取當前世界統一時間,形式為“星期-月份-當月號-時-分-秒-年份”。
示例程式碼和配置如下:
import time
while True:
data = serdev.ReadSerial()
print("serdev recieve %d"%data)
print("recieve time:"+str(time.ctime()))
執行結果如下: