用python写一个简单的带波形的串口上位机
在做嵌入式开发的过程中上位机是经常用到的,那上位机是什么呢?简单的说我们设计的一个嵌入式设备可能是比较底层的一个信息采集设备(也叫下位机),比如带显示的温度传感器模块,它用于去感知环境温度并进行显示,由于它的计算能力有限,所以它通常将自己采集的数据发送给上层的设备,这个上层设备可以叫做上位机。上位机通常有较强的数据处理能力和控制下位机的能力,因此上位机可以与下位机通信,并提供更强的处理功能。
本期设计的上位机的功能是从下位机接收温度数据并进行实时的波形显示。因此主要有两块设计:一个是通过串口接收数据;另一个是把接收的数据进行绘图显示。下面就直接看看几部分代码吧!
首先是本设计需要用到的模块
import serial #串口模块
import tkinter #GUI模块
import threading #多线程模块
import matplotlib.pyplot as plt #2D绘图库
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
from queue import Queue #队列的数据结构
下面是串口的数据接收代码,本设计中上位机只需要接收代码,不需要发送代码,因此只需要用到Serial的read函数。需要注意的是,在设计上下位机通信时,需要明确通信数据的格式或者使用某种协议,否则你将不知道如何复原接收的数据,也不知道数据的含义。本设计中发送的是浮点数据,每一次发送浮点数据的一位,且是用ASCII字符的形式发送,然后使用字符’a’表示一次数据发送完毕。
ser=serial.Serial("COM4", 115200, timeout=5) #打开串口并设置串口号COM4, 串口波特率115200, 阻塞等待的时间5S
....
while(1):
rcv = ser.read(1) #等待读取一个字节
if len(rcv) >= 1: #如果读到一个字节,则执行if里面的语句
print(rcv) #打印接收到的数据
if ord(rcv) - ord('0') >=0 and ord(rcv) - ord('0') <= 9: #判断 数据是否是数字
temp += str(ord(rcv) - ord('0')) # 保存在字符串里面
print('temp:')
print (temp)
continue
elif ord(rcv) - ord('.') == 0: #判断是否是小数点
temp += '.' # 保存在字符串里面
elif ord(rcv) - ord('a') == 0: #判断是否是结束标志a
res = float(temp) #将接收的数据转换成浮点数
print(res)
cnt += 1
if data_q.qsize() == 50:
xx = data_q.get() #从队列中取走一个数字
data_q.put(res) #放入接收到的新数据到队列尾
temp = ''
print ('queue size is : %d' % data_q.qsize()) #打印队列的大小
else: #错误判断
print ('rcv err!!!!')
if is_ending == 1: # cnt > 10 or
break;
.....
ser.close()#退出时关闭串口
串口的部分是一个循环实现,每当读到数据时便进行一次处理,将新接收的数据放入缓存队列,在队列每一次更新后就会更新波形。
考虑到后面的扩展,波形的显示这里单独开一个线程处理。GUI的部分主要是设置了波形串口的属性以及放置了一个退出窗口的按钮。
def my_gui():
global f, f_plot, canvs
top = tkinter.Tk() # 初始化界面
top.geometry("670x700+30+30") #设置界面的大小
top.title("tkinter curve") #设置界面的title
f = Figure(figsize=(6.4, 4.6), dpi=100) #设置波形界面的大小
f_plot = f.add_subplot(111) #在画布中增加轴域
f_plot.grid() #设置界面的网格外观
canvs = FigureCanvasTkAgg(f, top) #tkinter 渲染
canvs.get_tk_widget().place(x=15, y=15) #设置曲线的位置
button = tkinter.Button(text = "Click and Quit",
command = lambda:close_win(top))
button.pack()
# 进入消息循环
top.mainloop()
下面就贴一下全部代码:
import serial #串口模块
import tkinter #GUI模块
import threading #多线程模块
import matplotlib.pyplot as plt #2D绘图库
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
from queue import Queue #队列的数据结构
is_ending = 0
def close_win(win_handle):
global is_ending
is_ending += 1
print('window closed..%d' %is_ending)
win_handle.destroy()
def my_gui():
global f, f_plot, canvs
top = tkinter.Tk() # 初始化界面
top.geometry("670x700+30+30") #设置界面的大小
top.title("tkinter curve") #设置界面的title
f = Figure(figsize=(6.4, 4.6), dpi=100) #设置波形界面的大小
f_plot = f.add_subplot(111) #在画布中增加轴域
f_plot.grid() #设置界面的网格外观
canvs = FigureCanvasTkAgg(f, top) #tkinter 渲染
canvs.get_tk_widget().place(x=15, y=15) #设置曲线的位置
button = tkinter.Button(text = "Click and Quit",
command = lambda:close_win(top))
button.pack()
# 进入消息循环
top.mainloop()
try:
global f, f_plot, canvs
# 打开串口,并得到串口对象
gui_thread = threading.Thread(target=my_gui) #创建GUI显示界面的线程
gui_thread.start()
ser=serial.Serial("COM4", 115200, timeout=5) #打开串口并设置串口号COM4, 串口波特率115200, 阻塞等待的时间5S
print("打开串口 ")
cnt = 0
data_q = Queue(50) # 创建一个队列对象
for i in range(50):
data_q.put(0) # 添加元素
temp = ''
while(1):
res = 0.0
rcv = ser.read(1) #等待读取一个字节
if len(rcv) >= 1: #如果读到一个字节,则执行if里面的语句
print(rcv) #打印接收到的数据
if ord(rcv) - ord('0') >=0 and ord(rcv) - ord('0') <= 9: #判断 数据是否是数字
temp += str(ord(rcv) - ord('0')) # 保存在字符串里面
print('temp:')
print (temp)
continue
elif ord(rcv) - ord('.') == 0: #判断是否是小数点
temp += '.' # 保存在字符串里面
elif ord(rcv) - ord('a') == 0: #判断是否是结束标志a
res = float(temp) #将接收的数据转换成浮点数
print(res)
cnt += 1
if data_q.qsize() == 50:
xx = data_q.get() #从队列中取走一个数字
data_q.put(res) #放入接收到的新数据到队列尾
temp = ''
print ('queue size is : %d' % data_q.qsize()) #打印队列的大小
else: #错误判断
print ('rcv err!!!!')
if is_ending == 1: # cnt > 10 or
break;
x = range(0, 50) # 设定显示的x轴
y = data_q.queue # 设定显示的y轴
print ('data_q:')
print(data_q.queue)
print ('y:')
print(y)
f_plot.clear()
f_plot.plot(x, y) #生成图像
f_plot.grid()
canvs.draw() #绘制曲线图
#result=ser.write("hello guoguo".encode("gbk"))
ser.close()#关闭串口
print ('ending serial, please close GUI window')
gui_thread.join() #等待gUI界面关闭
except Exception as e:
print("---异常---:",e)
下面展示一下GUI界面和cmd窗口收到的来自下位机的数据:
需要的源文件和程序的小伙伴可以关注公众号【阿目分享嵌入式】,赞赏任意文章2¥,然后后台私信我哦!!
ending~~