数字系统实验:倒车雷达

M3M4M5M6:(本人的本职负责为M5M6,兼职负责M3M4)
一、个人模块部分的不同方案分析:
M5M6所负责的是A级实验的上位机软件模块,要求负责控制命令发送、采样数据接收、数据处理、数据图形化显示、数据文件保存等等。相当于要我们写一个功能完备的串口助手出来,并且相对于之前在第八次实验老师给的串口助手更加高级,需要增添图形显示界面。我从开始到最终结束,从网上找到了十几个大神所给的代码文件,有Python的有VS的,后来发现完全没法用,太高级,所以干脆自己写,又写了四五个版本,最终得到一个能用的。可以比较完备的显示的。
在这里插入图片描述
网上代码
在这里插入图片描述
自己写的
由于我们选用Python语言来设计上位机的软件功能模块和UI交互模块,这里通过学习从网上找来的代码,发现可以使用Python中的serial库来设计串口通信功能模块,通过pyqt库或者tkinter库来设计UI交互界面,pygragh库可以进行图形绘制,这么多的网上代码无怪乎如此。所以我尝试了很多。首先尝试从serial库入手,先尝试建立起串口通信,这就是串口1文件夹,发现实际上串口通信在上位机很简单,只需要调用read和write函数就可以;于是就开始设计图像绘制,这就是串口2文件夹,发现可以使用pygragh库进行图形的实时绘制;然后开始设计UI交互界面,这里很麻烦,我尝试了两种使用pyqt库来设计ui界面的方法,但由于pyqt库太过复杂,两种都失败了,就是串口3和串口3’文件夹;没办法尝试使用tkinter库来设计UI界面,成功了,这就是串口4文件夹。如下:
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
M3M4模块是负责本次A级实验的串口通信模块的下位机电路工程设计,是本次实验的难点之一,要求配合上位机模块实现解析控制命令、驱动采样模块、封装并上传数据等等。最难的是如何与上位机约定好协议实现数据的传输。这个在网上是找不到现有的工程的,也没有能够作为借鉴的资料文件,真正的无中生有。从开始到结束,我和我的同伴总共写了十一个版本,其中小的修改不计其数,最终才得到了一个能用的串口通信模块,并整合到了整个实验工程模块中。
在这里插入图片描述
我和我的同伴走的是两条路,我写的是串口工程四个文件夹,他写的是串口通信七个文件夹,希望两路并进来实现这个模块。我所走的是通过增设一个字节的标志位来传递几个字节的数据,这个数据的字节数可以自己进行设定。由于一开始不知道小板子的通信是如何进行的,还是按照之前我们给的小板子进行设计,设计了一个并行通信的工程,当然是失败了,不过还是为测试上位机软件做出了贡献。然后开始在实验板上进行设计,却发现怎么都不成功。最气人的是即便在电脑上功能仿真成功了,在板子上还是不成功,用串口助手接收不到数据。
在这里插入图片描述在这里插入图片描述
这几个文件夹先后攻克了分频问题(分频错误)、标志位无法识别问题(时钟不匹配导致标志位无法正确识别)、数据传输问题(没有设计好传输协议)、数据封装问题(之前一直不知道需要对数据进行封装),最终回归到串口工程文件夹,写出了可以正常运行、正常数据传输的串口工程。
然后整合到最终结果上,设计完整的电路工程,又写了九个,最终得到了好的结果,也算是苦尽甘来、水到渠成了,毕竟,过程再坎坷,磕磕绊绊也无所谓,把所有的坑踩遍,最后也能到达终点,而且收获也最多。

二、所选方案和实现:
M5M6模块上面说我写了5个文件夹分别对串口通信、图形绘制、UI交互三个方面进行了实现,接下来需要进行整合。一开始尝试以串口通信为主进行整合,却发现没有办法实现UI界面,后来尝试以UI交互界面为主进行整合,发现很难将图像绘制和UI界面进行整合,最终决定摒弃之前的绘图方式,即放弃使用pygragh库进行图形的实时绘制,而是采用tkinter库中的画布函数来实现直接在UI界面中实现图形的实时绘制,并通过定义全局变量来实现对串口通信数据的保留和保持,最终实现了上位机软件,具体如下:
单单为了实现整合,我就写了十几个文件,有复杂的有简单的,复杂的可以到三四个文件来构成一个软件,简单的却只有十几行用来测试能否正确绘图,最终的tkin窗口2.0和tkin窗口2.1都是成功结果,而2.1相对于2.0增加了一些花里胡哨的音色、音量调节的功能,在后面将会提到。
这里首先使用2.0版本的进行整体的设计和实验,设计了开机关机按钮和“串口号”、“波特率”、“平均值”三个标签用来表示当前程序的状态。设计了右侧的部分进行动态图形绘制。
具体代码如下:

# encoding: utf-8
import tkinter
import serial
import serial.tools.list_ports
import random
import sys
import time
import easygui as g
import numpy as np
from tkinter import Tk,StringVar
from pyico import *
ser=0
pq=0
N=200
M=1
Q=0
tmp=0
ftmp=0
ii=0
r=''
rr=""
star='0'
end='0'
tt=0
L=[]
def com():
    port_list=list(serial.tools.list_ports.comports())
    if len(port_list)==0:
        g.msgbox('警告','找不到串口','拜拜了您嘞')
        return '0'
        # print('找不到串口')
    else:
        for i in range(0,len(port_list)):
            print(port_list[i])
        ss=str(port_list[0])
    return ss[0:4]
def a():
    global ii,rr,tmp,Q,ftmp,end,pq,tt,r,L
    # num=ser.inWaiting()
    # while num!=1:
    #     print(num)
    #     ss=ser.read(16)
    #     num=ser.inWaiting()
    ser.flushInput()
    num=ser.inWaiting()
    # print(num)
    data0=bytes([0x2a])
    data1=bytes([0x30])
    # data2=bytes([0x2a])
    # data3=bytes([0x00])
    # print(data0)
    # print(data1)
    ser.write(data0)
    ser.write(data1)
    num=ser.inWaiting()
    # print(num)
    # while num:
    #     num=ser.inWaiting()
    #     print(num)
    #     if num==4:
    #         ser.write(data2)
    #         ser.write(data3)
    # print(data0)
    # print(data1)
    # print(num)
    if num>0:
        # print(num)#查看当前有多少数据在缓存区
        s0=ser.read(2)
        ser.flushInput()
        # s0=ser.read(1)
        # print(s0[0],s0[1])
        # print(s1)
        if s0[0]==0:#修改点
            # print("zhi",s0[0],s0[1])
            ftmp=tmp
            # tmp=s[0]*256+s[1]
            tmp=s0[1]
            # print(tmp)
            if(len(L)==10):
                del L[0]
                L.append(tmp)
            else:
                L.append(tmp)
            if pq==0:
                ftmp=tmp
            pq=pq+1
            count=np.bincount(L)
            tmp=np.argmax(count)
            print(tmp)
            # print(pq)
            oupu.write(str(M*Q)+':'+str(tmp)+'\n')
            Q=Q+1
            win.create_line(245,ftmp*0.8,255,tmp*0.8)
            for ie in range(0,pq+1):
                win.move(ie,-6,0)
            top.update()#刷新
        # print("zhi")
        # ftmp=tmp
        # # tmp=s[0]*256+s[1]
        # tmp=s0[0]
        # print(tmp)
        # if(len(L)==10):
        #     del L[0]
        #     L.append(tmp)
        # else:
        #     L.append(tmp)
        # if pq==0:
        #     ftmp=tmp
        # pq=pq+1
        # # print(pq)
        # oupu.write(str(M*Q)+':'+str(tmp)+'\n')
        # Q=Q+1
        # win.create_line(240,ftmp+20,260,tmp+20)
def kaishi():
    global star,end
    star='start'
    print('kaishi')
    # ass=[0,1,0,1,0,1,0,1]
    data0=bytes([0x2a])
    data1=bytes([0x00])
    # print(data0)
    # print(data1)
    ser.write(data0)
    ser.write(data1)
    # data=bytes([0xE8])
    while end!='end':
        a()
    oupu.close()
def jieshu():
    global end
    print('jieshu')
    end='end'
    # ass=[1,1,1,1,1,1,1,1]
    data0=bytes([0x2a])
    data1=bytes([0xaa])
    # print(data0)
    # print(data1)
    ser.write(data0)
    ser.write(data1)
    top.quit()
def junzhi():
    global  L
    df=0
    for i in L:
        df=df+i
        ttmp=str(df/len(L))
    # ttmp='123456'
    Label4=tkinter.Label(top,text="平均值:"+ttmp,fg='black')#bg='light gray',
    Label4.place(relx=0.1,rely=0.3)
ssss=com()
if ssss!='0':
    top=tkinter.Tk()
    top.title('波形显示串口助手')#标题
    top.resizable(0,0)#大小可调性
    top.geometry('500x300')#大小
    bote=9600
    oupu=open('output1.txt','w')
    ser = serial.Serial(ssss, bote)
    Label1=tkinter.Label(top,text=("串口号:"+ssss),fg='black')#bg='light gray',
    Label1.place(relx=0.1,rely=0.1)
    Label2=tkinter.Label(top,text="波特率:"+str(bote),fg='black')#bg='light gray',
    Label2.place(relx=0.1,rely=0.2)
    Label3=tkinter.Label(top,text="平均值:",fg='black')#bg='light gray',
    Label3.place(relx=0.1,rely=0.3)
    button1=tkinter.Button(top,text="开机",command=kaishi,bg='light gray',fg='black')
    button1.place(relx=0.1,rely=0.45,width=80,height=40)
    button2=tkinter.Button(top,text="关机",command=jieshu,bg='light gray',fg='black')
    button2.place(relx=0.1,rely=0.6,width=80,height=40)
    button3=tkinter.Button(top,text="均值",command=junzhi,bg='light gray',fg='black')
    button3.place(relx=0.1,rely=0.75,width=80,height=40)
    win=tkinter.Canvas(top,bg='light gray')
    win.place(relx=0.35,rely=0.1,width=310,height=240)
    top.update()#刷新
    top.mainloop()

其中包含了大量的注释掉的程序语句,因为在我将整个程序的整体框架确定下来之后,对这个程序调试了很多遍,最终得到的代码行数实际上只有100行左右,但功能却能完整的实现。
M3M4首先对单个的串口工程进行实验,最终选定的方案是设置标记位为“01010100”,然后在串口工程中先搜索接受的上位机数据中有没有“010”的三位二进制数,如果找到就再搜索有没有“00”的二进制数,如果找到则说明找到了标志位,“00”之后的八位二进制数就是上位机传给下位机的真正数据。然后上传给上位机的时候,一开始没有注意到需要对数据进行封装,还是尝试着先发八个二进制“00000000”作为标志位,再跟上八位二进制的数据,但怎么也传不上去,后来在科三的同学的帮助下,知道了需要对数据进行封装,每八位数据需要在最前面加0,在最后面加1,将原本的8位数转换为10位数后才可以让芯片进行识别,将中间的8位数据传送给上位机,最终得到的串口工程如下:
在这里插入图片描述
fenpin模块负责将输入的24Mhz的输入信号转换为9600hz的时钟信号,用来控制数据的传输。jieshou模块负责接收从上位机发送的数据,并产生相应的kj开机控制信号和load数据上传控制信号。chuansong模块负责接收外界给的八位二进制数据,进行封装之后在load和kj控制信号的控制下传递给上位机,这里只截取了三个元件中的部分主要代码,因为截取全部代码太占据空间了,而且所截取的代码是功能的主要实现部分,已经可以体现如何实现功能模块。
具体模块如下:
在这里插入图片描述在这里插入图片描述在这里插入图片描述

三、验证方法:
M5M6模块基于pycharm软件进行设计,总共调用了Python语言的tkinter库、serial库、random库、sys库、time库、easygui库、numpy库、pyico库八个库,具体框架为通过tkinter库来设计UI交互界面,通过tkinter库中的canvas库设计画布,通过move函数实现动态绘图,通过serial库实现串口读写操作,通过easygui库实现警告信息,通过numpy库实现数值稳定,通过random库实现功能测试。
首先要验证整个软件的模块逻辑是否正确。本软件的逻辑为:首先通过com()函数自动查找当前可用于通信的串口,如果没有将会弹出一个easygui的弹窗,否则将会弹出软件的UI操作界面。界面有开机按钮、关机按钮和取平均值按钮,当选择开机按钮后将会给予下位机开机指令,并进入循环,持续向下位机发送请求数据的指令,并接收下位机发送的数据,经过取众数处理后从最近的10个中取出众数(以此来提高软件的抗干扰能力),并绘制图像显示在右侧;选择关机按钮后将会向下位机发送关机指令,并自动关闭弹窗;当选择取均值按钮后将会自动计算最近10个数的平均值,并显示在界面指定位置。界面指定位置会显示当前的串口和预置的波特率(9600)。
在测试方面基于小板子,首先测试串口是否正常打开,然后通过random函数向板子发送一个上位机随机生成的数值,传给下位机后下位机进行显示,并原封不动的传给上位机,在上位机的数值显示部分进行图形的绘制,这里先注释掉了取平均值的功能,并选择了115200的波特率用来简单测试。最终得到的结果如下:
在这里插入图片描述在这里插入图片描述
在小板子上初步实现了之后,开始思考波特率的问题。由于下位机模块的数据读取是在1Mhz,而板子给定的时钟是24Mhz,经过与下位机模块负责人讨论后确定使用9600波特率,即一秒钟传送9600位,这样只需要将原本的24Mhz除以2500就可以得到9600hz的时钟信号。经过在给定的实验板上进行测试,发现正确,至此,上位机需要做的事情已经基本完成。
M3M4这里首先对所编写工程的逻辑性进行验证分析。首先初始状态kj开机控制信号的寄存器为‘0’,load装载控制信号的寄存器为‘0’,表示此时没有开机,也没有进行装载操作,不会向上位机发送特定数据(注意,还是会发送无意义的数据),而此时上位机也处于待机状态,不会接受特定数据(串口会持续发送默认的“11111111”),然后上位机给予开机信号,接收模块接收到后控制kj开机控制信号寄存器为‘1’,表示开机,整个工程开始工作。当上位机发送数据传送控制信号的时候,接收模块接收到后控制load装载控制信号寄存器为‘1’,表示开始装载,传送模块将外部输入的八位数据存储到寄存器中,并进行封装,然后将标记位也进行封装,最终得到一个二十位的二进制数据,传送给芯片,并最终传送到电脑,展示在串口助手上。
然后进行功能仿真,并最终放到整个工程中进行验证,发现结果正确,可以在上位机接收到数据,并展示在图像上。
这里需要考虑时钟信号的问题,上位机的波特率限定为9600,传入的时钟频率为24Mhz,所以需要除以2500才能得到正确的时钟信号,这里只需要写一个计数器,要求每个时钟上升沿进行计数,当记录到1250的时候进行清零,然后将输出信号取反就可以得到9600hz的时钟信号。
然后由于需要在整个电路上进行验证,所以需要添加到整个的工程文件上,即封装为bsf元件,然后与整个电路进行串联,最终得到的电路和运行结果如下:
在这里插入图片描述
在这里插入图片描述
四、下一步工作:
M5M6下一步可以在现有的基础上加一些花里胡哨的东西,来使UI界面更友好,即可以改变背景图片,改变UI界面的格式,增加音量音色调节按钮等等,期望实现的结果如下:
在这里插入图片描述
当前 1
在这里插入图片描述
期望 1
增加“小、中、大”的音量调节按钮和“1,、2、3、4”的音色调节按钮,最好可以和蜂鸣器模块负责的同学一起设计几首歌放入mif文件来进行显示。
M3M4模块通过测试发现虽然已经实现了串口通信,完成了A级实验,但当前工程的抗干扰能力不强,很容易受到机械噪音的影响导致绘制出来的图像有偏差。所以下一步工作可以尝试着从提高信号稳定性、增强通信稳定性、修改传递方式的传递数据、提高数据接收、数据读取、数据采集灵敏度等方面进行优化。
甚至可以通过上位机来输入数据实现一个电子琴的效果,当然,这是后话。

五、心得体会
本次设计总结:
这次的大综合实验很考验人,我和我的搭档俞阳博因为之前有学习过Python语言所以选择了上位机软件设计M5M6模块。实际上选择的时候心里也没有底气,因为要我们设计软件,真的太难了,但别人更没有基础,所以还是去做了。一开始还抱着从网上找现成代码进行修改的想法,所以找了很多,不得不说csdn还是真的很厉害的,竟然找到了很多,但要么是没有串口通信模块,要么是没办法绘图,所以到最后实际上还是自己设计,不过从网上得到了很多的知识,对Python库的理解与应用也强了很多。到最后设计的串口助手是我自己真真正正一个人完成的,没有任何网上的代码,包括串口搜索、串口通信、动态绘图,都是我自己原创的想法。总的来说,Python语言进步了很多。
然后由于下位机通信模块和上位机软件模块连接非常紧密,而且下位机通信模块比较难,所以我就参与了下位机通信模块的设计,而且采用了和原本同伴并不同的方法,后来证实我的方法是正确的。而且这一部分告诉了我很多,如何进行通信,如何进行封装,为何要进行封装,以及如何进行上传下放,为我小学期打下了很好的基础。
学期总结:
这门实验课让我进步良多,也让我花费了很多时间,单说最后一个大综合实验,整个实验从头到尾做了两个多周,从5月6号开始创建了综合实验的文件夹开始,一直没停过,从早上八点到晚上十点整天泡在实验室,饿了就叫外卖,直到要睡觉了才回宿舍,说真的我都想打地铺住在实验室算了。这门实验课很好玩,能做很多真真正正的事情,感觉自己学习的东西真的有用,能够用在实际生活中,能够做一些实实在在的事情,也让我对我的小学期充满了期待。
这门实验课我的参与度很高,我也是真的喜欢这个实验,才会愿意花这么多时间去做。而且,说真的,我很喜欢实验室的环境,特别是五一假期的时候。五一假期我哪里也没有去,四天全都泡在了实验室里,没有一直在敲代码,而是写一写别的事情,也觉得自己的效率很高,实验室的环境真的很喜欢,也很适合我,那个五一假期也是我最充实的一个假期。
在实验室里能让我感觉得平静、积极,也能让我更专注于我目前正在做的事情,不管是敲代码还是记笔记,都是如此。现在很庆幸当初将自己的指纹录入在了系统里面,或许以后实验室可以作为一个我的自习的地方。
我早就从学长学姐那里听说了,小学期也是在工训,也是给一块板子让我们自己做一个工程出来,而且是随便做,想做什么做什么,上一届就有学长学姐做了密码锁、游戏机什么的,我自认为比不上上一届的学长学姐,但我也有我自己的想法。既然已经可以实现串口通信,那么我对制作一个密码锁已经充满了信心,所以我想先做一个密码锁来保底,能够完成小学期,然后自己再开始自己的研究,我真正想做的是一个机械手,用我的电脑进行控制,或者更进一步,我在我的手上带一个手套,然后通过测距将信息传递给下位机,下位机来做出相似的动作。这个很难,但如果只是简单的完成实验作业,就谈不上研究了。

发布了275 篇原创文章 · 获赞 160 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_40851744/article/details/104820298