全網最適合入門的物件導向程式設計教程:04 類和物件的 Python 實現-為自定義類新增方法(PySerial 庫接收串列埠資料)

FreakStudio發表於2024-06-29

全網最適合入門的物件導向程式設計教程: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

image

本文件主要介紹如何使用 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)

image

可以看到,我們已經給串列埠類新增了具體方法,這裡,你可能會問,那這些方法具體該怎麼實現呢?難道說要我們調作業系統的驅動函式讀取串列埠、造輪子對資料解析?當然不可能,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,官方文件說明如下:

image

常用引數含義如下:

引數名稱 含義
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

image

這裡我們建立兩個相互連線的串列埠 com11 和 com17,虛擬的串列埠需要成對建立,來指明他們的連線關係,就好比各種連線線的公投和母頭一樣。我們建立好虛擬串列埠連線以後,就可以使用它們來通訊了,我們可以選擇任意一個串列埠助手軟體來與我們寫的 python 串列埠類進行通訊。這裡,我們選擇 xcom 軟體進行測試,xcom 軟體的安裝下載教程可以點選連結: https://blog.csdn.net/qq_41573860/article/details/103796913

開啟 xcom 軟體,我們先進行相關串列埠引數的配置,具體如下圖所示:

image

此時,我們在 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")

執行結果如下:
image

接下來,我們嘗試使用 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()))

image

執行結果如下:

image

image

相關文章