學習單片機,STM32好長時間了,也做了一些項目,一直想利用上位機實現電腦與單片機之間的傳輸數據,利用串口助手是最直接的通信方式,但串口助手不適合做上位機,如果會寫串口助手的軟件,基本能寫自己寫一個上位機,歷時一個月,終於寫出一個簡易的串口助手,筆者自己寫的軟件,有些Bug,分享出來,僅供參考,歡迎大家一起學習與討論。
目標:利用Python語言寫一個簡易的串口助手
環境:Win10, python語言, PyQt5, Pycharm
思路:先做出主體,慢慢優化與添加其他的功能,在串口助手設計中主體是獲取串口ID號,設置波特率,發送數據,接收數據,添加界面佈局,信號觸發等操作。
一、設置串口ID號
建議製作第一版串口助手,可以先選定固定串口和波特率進行設計,在筆者設計時,固定一個串口爲COM4,波特率爲9600設計,設置參考代碼如下:
self.t = serial.Serial('com4', 9600)
port = self.t.portstr
print(port)
這樣就簡單將串口設置爲打開串口時爲COM4,波特率爲9600,COM4是筆者所用的電腦端口。後續將端口和波特率變爲可選擇
二、發送數據和接收數據
發送數據類型簡單可分爲字符串發送和十六進制發送,發送數據方式是輸入發送,在本文中先實現字符串發送,後續再實現十六進制發送。同理,接收數據也可先實現字符串接收,後續再實現十六進制發送。參考代碼如下:
import serial
import time
import threading
class SerialPort:
message = ''
def __init__(self, port, buand):
super(SerialPort, self).__init__()
self.port = serial.Serial(port, buand)
self.port.close()
if not self.port.isOpen():
self.port.open()
def port_open(self):
if not self.port.isOpen():
self.port.open() #打開串口
def port_close(self):
self.port.close()
def send_data(self):
data = input("請輸入要發送的數據(非中文)並同時接收數據: ")
n = self.port.write(data.encode()) #發送數據
print(n)
def read_data(self): #接收數據
while True:
self.message = self.port.readline() #接收數據
print(self.message)
serialPort = "COM4" # 串口
baudRate = 9600 # 波特率
if __name__ == '__main__':
mSerial = SerialPort(serialPort, baudRate)
t1 = threading.Thread(target=mSerial.read_data)
t1.start()
while True:
time.sleep(1)
mSerial.send_data()
三、加入界面佈局
利用PyQt5庫搭建界面,在界面佈局中使用網格佈局管理QGridLayout,代碼如下:
def initUI(self):
grid = QGridLayout()
self.portname = QLabel("端口號") #添加控件
self.datanumber = QLabel("發送數據位數:")
self.datasender = QLabel("發送數據:")
self.datareview = QLabel("接收數據:")
self.button = QPushButton("發送")
self.open_button = QPushButton("打開")
self.portnameEdit = QLineEdit()
self.datanumberEdit = QLineEdit()
self.datasenderEdit = QLineEdit()
self.datareviewEdit = QLineEdit()
grid.addWidget(self.portname, 1, 0) #添加控件的位置
grid.addWidget(self.portnameEdit, 1, 1)
grid.addWidget(self.datanumber, 2, 0)
grid.addWidget(self.datanumberEdit, 2, 1)
grid.addWidget(self.datasender, 3, 0,)
grid.addWidget(self.datasenderEdit, 3, 1, 1, 6)
grid.addWidget(self.datareview, 4, 0)
grid.addWidget(self.datareviewEdit, 4, 1, 1, 6)
grid.addWidget(self.button, 5, 3)
grid.addWidget(self.open_button, 5, 1)
self.setLayout(grid) #佈局
self.button.clicked.connect(self.Cosender) #關聯"發送"按鈕與信號槽
self.open_button.clicked.connect(self.Check_serial) #關聯“打開”按鈕與信號槽
self.setGeometry(300,300,200,200) #設置窗體大小
self.setWindowTitle("串口助手")
四、加入觸發信號
在上面的佈局管理中,加入了“打開”和“發送”兩個按鈕。需要給兩個按鈕加入信號槽。編寫好信號槽代碼後再關聯按鈕與信號槽。信號槽代碼如下:
def messageUI(self):
'''提示串口打開狀況信息'''
QMessageBox.critical(self, "錯誤", "串口打開失敗,請選擇正確的串口") #彈出提示信息
def Check_serial(self):
'''檢測串口是否被打開'''
try:
self.t = serial.Serial('com4', 9600) #打開串口COM4
port = self.t.portstr #返回但端口號
self.portnameEdit.setText(port) #顯示在界面上
self.flag_open=1
except serial.serialutil.SerialException: #打開失敗,輸出提示信息
self.messageUI() #提示信息
def Cosender(self):
'''“發送”按鈕,信號槽'''
if self.flag_open==1: #串口被打開
self.str_input = self.datasenderEdit.text() #返回上面的發送文字
n = self.t.write((self.str_input+'\n').encode())
self.datanumberEdit.setText(str(n-1)) #寫入數據位數框
self.datasenderEdit.setText(str(self.str_input)) #寫入發送框
time.sleep(1) # sleep() 與 inWaiting() 最好配對使用
num = self.t.inWaiting() #獲取接收到的數據長度
if num:
self.receivemessage = self.t.read(num) #讀取接收數據
print(self.receivemessage)
self.datareviewEdit.setText(str(self.receivemessage)[2:-1]) #寫入接收框
else: #串口打開失敗
self.messageUI()
關聯按鈕與信號槽的代碼如下:
self.button.clicked.connect(self.Cosender) #關聯"發送"按鈕與信號槽
self.open_button.clicked.connect(self.Check_serial) #關聯“打開”按鈕與信號槽
這樣製作一個基本的簡易串口助手完成。如有不足之處請多多指教。效果如圖1所示。後面在開發加入了串口,波特率可供選擇性,發送和接收數據有字符串和十六進制等設計,效果如圖2所示,有興趣的讀者可以自行下載源代。源代碼鏈接:
https://download.csdn.net/download/scx2006114/11579725
圖1 圖2
參考資料鏈接:
1.發送和接收數據:https://blog.csdn.net/xiaoeleis/article/details/81484872
2.頁面佈局:https://blog.csdn.net/zhulove86/article/details/52563298
完整代碼如下:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QLineEdit, \
QGridLayout, QLabel, QMessageBox, QComboBox, \
QCheckBox
import serial
import time
import serial.tools.list_ports
class CO2UI(QWidget):
def __init__(self):
super(CO2UI, self).__init__()
self.initUI()
global flag_open #標誌位,判斷串口是否打開
self.flag_open = 0
def initUI(self):
grid = QGridLayout()
self.portname = QLabel("端口號")
self.datanumber = QLabel("發送數據位數:")
self.datasender = QLabel("發送數據:")
self.datareview = QLabel("接收數據:")
self.button = QPushButton("發送")
self.open_button = QPushButton("打開")
self.portnameEdit = QLineEdit()
self.datanumberEdit = QLineEdit()
self.datasenderEdit = QLineEdit()
self.datareviewEdit = QLineEdit()
grid.addWidget(self.portname, 1, 0)
grid.addWidget(self.portnameEdit, 1, 1)
grid.addWidget(self.datanumber, 2, 0)
grid.addWidget(self.datanumberEdit, 2, 1)
grid.addWidget(self.datasender, 3, 0,)
grid.addWidget(self.datasenderEdit, 3, 1, 1, 6)
grid.addWidget(self.datareview, 4, 0)
grid.addWidget(self.datareviewEdit, 4, 1, 1, 6)
grid.addWidget(self.button, 5, 3)
grid.addWidget(self.open_button, 5, 1)
self.setLayout(grid)
self.button.clicked.connect(self.Cosender)
self.open_button.clicked.connect(self.Check_serial)
self.setGeometry(300,300,200,200)
self.setWindowTitle("C02上位機")
def messageUI(self):
'''提示信息'''
QMessageBox.critical(self, " ", "串口打開失敗,請選擇正確的串口")
def Check_serial(self):
'''檢測串口是否被打開'''
try:
self.t = serial.Serial('com4', 9600) #打開串口COM4
port = self.t.portstr #返回但端口號
self.portnameEdit.setText(port) #顯示在界面上
self.flag_open=1
except serial.serialutil.SerialException: #打開失敗,輸出提示信息
self.messageUI() #提示信息
def Cosender(self):
if self.flag_open==1:
if self.flag_open == 1: # 串口被打開
self.str_input = self.datasenderEdit.text() # 返回上面的發送文字
n = self.t.write((self.str_input + '\n').encode())
self.datanumberEdit.setText(str(n - 1)) # 寫入數據位數框
self.datasenderEdit.setText(str(self.str_input)) # 寫入發送框
time.sleep(1) # sleep() 與 inWaiting() 最好配對使用
num = self.t.inWaiting() # 獲取接收到的數據長度
if num:
self.receivemessage = self.t.read(num) # 讀取接收數據
print(self.receivemessage)
self.datareviewEdit.setText(str(self.receivemessage)[2:-3]) # 寫入接收框
else:
self.messageUI()
if __name__ == "__main__":
app = QApplication(sys.argv)
test = CO2UI()
test.show()
sys.exit(app.exec_())