嵌入式_串口助手__可以显示图表、进行FFT
缘于博客,出于需要查看串口数据变化情况的需求,又没有找到合适的串口助手,于是临时使用WPF搭建一个串口助手。也因此界面简单,由于使用WPF,相比Qt性能上确实有些堪忧。虽然界面和功能远远不及其它串口助手,不过由于项目比较简单,可以更加定制化地开发,也能满足一些特殊需求,适合练手。本项目工程已经上传到github和gitcode了,功能和界面比较简单,可以当成一个小框架自己继续开发,或者练手。代码里
一、简介
缘于博客_FYAW智能显示控制仪表的简单使用_串口通信-CSDN博客,出于需要查看串口数据变化情况的需求,又没有找到合适的串口助手,于是临时使用WPF搭建一个串口助手。也因此界面简单,由于使用WPF,相比Qt性能上确实有些堪忧。虽然界面和功能远远不及其它串口助手,不过由于项目比较简单,可以更加定制化地开发,也能满足一些特殊需求,适合练手。
本项目工程已经上传到github和gitcode了,功能和界面比较简单,可以当成一个小框架自己继续开发,或者练手。代码里面有不少注释,命名也还比较规范,采用MVVM模式,对初学者应该也比较友好。由于本人不一定有时间更新或者修复(Readme还没怎么写),所以Issue或者私信什么的不一定能及时处理,还请见谅。当然,Fork或者Clone再自行解决也是不错的(MIT)
github:ichliebedich-DaCapo/ZQcom (github.com)
gitcode:ZQcom - GitCode
为了能直接使用,打算发布一个beta版意思意思,真正算得上特色的或许只有图表这个功能了。原本打算做一个显示变量的IDE插件,但串口调试的方法显然更加通用。
二、基本使用
1,说明
还没有做工具栏什么的,基础功能就这些。编码方案、CRC计算、更复杂的数据处理或者运行某些脚本等功能后续可能会考虑做。
目前进行小段时间小频次的收发数据和图像显示是没有问题的,但由于我的需求源自于智能仪表显示仪的通信(10Hz一次收发),所以对大量数据接收与显示还有些不足甚至闪退,体验可能欠佳,后面会尝试慢慢解决。
2,处理数据框和图表的使用
第一步自然是选择串口,如果在打开程序后才插上线,那么可以点击一下刷新。然后就是选择波特率、数据位等,选择完成后就可以打开串口了,这个不要忘。
成功打开串口后就可以收发数据了,可以开启16进制发送或显示,也可以选择定时发送,右边是定时发送周期(单位是ms)。
下面共有三个窗口,我分为上下两大部分,上面是日志框,下面两个小框是数据处理框。其中左半边是截取框,用于截取收到的数据,当截取长度设为-1时,表示不截取(此时起始位置失效)。右半部分是转换框,把截取的数据进行某种转换,这里出于我个人的需求,仅仅是把左边接收到的8位16进制字符串(需要开启16进制显示)或普通字符串转为浮点表达。
当然,只有勾选了【处理数据】,才能使用数据处理框。至于时间戳,我只添加了日志框的,数据处理框我个人觉得没必要所以就没添加。日志保存和打开日志目录是可以正常使用的,【保存日志】时,如果勾选了处理数据复选框,那么就会连同下面的数据处理框的内容也一并保存。
由于转换过的数据是可以打印在右边的图表框上的,所以进行了一些数据检查,如果数据不合法,那么就会弹出警告。但有时由于发送数据过快,容易出现一些黏连现象,所以添加了【强制处理】,其实就是忽略没有不合法的数据,只打印“无法转换”在框中,并不会把数据传到右侧框(或许只传入0更能体现出这个数据是不合法的)
如果只是想接收单片机发来的浮点数据或者整型数据打印在图表上,那么就不要开启16进制显示和发送,这样进行处理时只是将普通字符串转为浮点数(相当于什么都没做),记得在长度框输入-1(没有做保存上次记录的持久化存储功能)。然后勾选图表框上的【开启图表】,默认起始索引是0,与数组对应。旁边的测试按钮其实啥用也没有
下面是接收单片机发送的正弦波数据,由于数据处理框和图表框我是放在处理数据这一个函数的,所以它俩是同步的,图表框会阻塞数据处理框。由于还能达到我个人的需求也就没有把它俩进行异步处理。但数据接收框和它俩是异步的,确保接收数据不会被延误。
所以发送大量数据后,你会看到日志框已经停下来了,但图表框和数据处理框可能还在工作。
点击FFT后可以进行FFT处理,再点击一次就会取消FFT。禁用动画这个功能,从观感上有时候会更流畅,有时候会更卡。
3,工程下载
可以从github的发行地方下载
如果打不开,可以到gitcode(镜像)上下载
可能需要安装.Net8.0运行时环境,如果你没有安装的话。
下载 .NET 8.0 Desktop Runtime (v8.0.11) - Windows x64 Installer
2024.11.25
优化了接收数据和处理数据,添加了【待处理】显示,可以知道处理数据过程与接收数据过程差多少数据量(队列,异步)。
修复了关闭串口会卡死的现象。修复了清屏时数据处理框仍在打处理数据。
接收数据并处理数据的极限速度是0.016s,即1s发62.5个数据。即使是0.015s也会出现异常,这个异常是由浮点转换函数抛出的异常导致的,目前的解决方案是关闭【处理数据】,问题还正在解决。关闭时间戳、禁用动画或者不使用处理数据会流畅很多。
略微修改了界面的样式,标题栏采用自定义形式,所以暂时还无法缩放界面(可以拖拽)
添加了一个数据打印功能,由于LiveCharts实在有些卡,于是此处采用ScottPlot。点击数据打印按钮后,可以选择当前Log目录下的文件,选择后会显示一张图表,可以自由缩放拖拽。不过图表初始显示时,不一定能看到你的数据,需要自己翻找。
后续可能考虑将主界面的图表组件改为ScottPlot,但现在制约处理数据的过程是字符串黏连现象。
为了方便自行测试串口助手,于是使用了com0com开了虚拟串口,这个功能如果想要正常使用,需要设置两个名称,选择两个没有被使用的串口就性,然后使能缓冲区,最后点击右下角的Apply
下面是发送串口数据的Python脚本
import asyncio import math import random import serial import serial_asyncio class VirtualSerialServer(asyncio.Protocol): def __init__(self, baudrate=115200, interval=0.1, frequency=1.0, amplitude=1.0, offset=0.0): self.transport = None self.baudrate = baudrate self.interval = interval self.frequency = frequency # 正弦波频率(Hz) self.amplitude = amplitude # 正弦波振幅 self.offset = offset # 正弦波偏移量 self.time = 0.0 # 当前时间 def connection_made(self, transport): self.transport = transport print(f'Virtual Serial Port connected with baudrate {self.baudrate}') asyncio.create_task(self.send_data_periodically()) # def data_received(self, data): # print(f'Received: {data.decode()}') # # 回显接收到的数据 # self.transport.write(data) async def send_data_periodically(self): while True: # 生成100以内的随机整数 value = random.randint(0, 100) # 将整数转换为字符串格式 data_to_send = f'{value}\n'.encode('utf-8') self.transport.write(data_to_send) # 打印数据 # print(f'Sent: {value}') await asyncio.sleep(self.interval) async def main(): # 固定波特率和时间间隔 baudrate = 115200 interval = 0.016 # 发送间隔(秒) # 使用实际的虚拟串口号,例如 'COM15' port = 'COM15' loop = asyncio.get_running_loop() try: server = await serial_asyncio.create_serial_connection( loop, lambda: VirtualSerialServer(baudrate, interval), url=port, baudrate=baudrate) except serial.serialutil.SerialException as e: print(f"Error opening serial port {port}: {e}") return await asyncio.Event().wait() # 保持程序运行 if __name__ == '__main__': try: asyncio.run(main()) except KeyboardInterrupt: print('程序已终止。')
持久化存储选项后续会做
三、工程框架
这个工程采用的是MVVM模式,可以看到相应目录结构。由于实际开发上觉得有些麻烦,虽然视图和数据分离这一部分做得还比较好,但我一般不怎么实用Model,而是直接把变量都一股脑放在ViewModel里。
窗口的启动从App.xaml转移到了App.xaml.cs里
Views
界面完全使用数据绑定、绑定事件什么的,不会直接在ViewModel里调用组件
ViewModels
MainViewModel里没有写任何逻辑,仅仅是添加串口和图表的视图模型
在SerialPortViewModel里,我实现了与串口相关的绝大部分逻辑,另一小部分在Services里的SerialPortService里。而把数据显示在图表上,使用的是事件聚合器,为的是解耦
在ChartViewModel里,Model与ViewModel分离做得比上面好,管理图表框相关的所有逻辑
Models
主要是ChartModel,为了能显示多条曲线,用的是Series,不过性能上怎么样不好说(嵌入式后遗症)
更多推荐
所有评论(0)