计算机串口第二次实验报告

3.4计算机串口数据收发与测量

一、实验目的

  1. 熟练使用Linux下io函数read、write和epoll等
  2. 熟练处理流式通信数据

二、实验过程

1、向STC单片机下载下面的程序
在这里插入图片描述
下载程序后,单片机将使用1200波特率发送自身序列号,格式同上一节课(3.3节内容)相同。请记录下该序列号。

这里可以选择使用串口助手,很容易就可以得到序列号为:A0 1D D7 A0 1A 5A 79 66 41 CA 90。

2、向串口写入自己的学号,格式为

0xAA 0x55 十二位学号数字。

例如学号20220110203,应该通过串口发送以下数据

AA 55 02 00 02 02 00 01 01 01 00 02 00 03

解:很容易就可以看出来规律,也就是学号的每一位前面加上一个0,然后组成一个序列,同时最前面需要加上AA 55。

3、STC单片机接收到学号后会发送第一串密码,密码长度为4字节。请将解析出该串密码并原样发回给串口。STC单品机收到返回后会继续发送下一串密码,请继续解析出该串密码并原样发回给串口。以此往复,将收到的最后一串密码记录下来。

例如,收到以下密码串

AA 55 0A 00 00 00 00 00 00 C9 34 3F 5D

其中AA 55为前导串,0A表示起始字节是第10字节。请将密码原样发回给串口。

AA 55 C9 34 3F 5D

解:也就是输入学号之后,可以得到返回出来的对应的结果为第一个密码,然后再将第一个密码通过串口助手发送过去之后会返回第二个密码,这时则需要依次往后。使用串口助手,可以得到的次数很少,最后还是需要编写程序。

这里,直接将第5部分的解答给出:

import serial
import requests
import serial.tools.list_ports

#字符串转字符串
def b2s(data):
    return ''.join([f'{ch:0>2x}'for ch in data]).upper()

#连接设备
ser=serial.Serial(list(serial.tools.list_ports.comports()[0])[0],1200)

#判断是否连接到设备
assert(ser!=None)

#输入学号
studentID=input("学号:")

#判断学号是否正确
assert(studentID.isdigit() and len(studentID)==12)

#读入序列号
number=b2s(ser.read(13))

#断言魔数为AA55
assert(number[:4]=="AA55")

#取出序列号
number=number[4:]

assert(len(number)==11*2)
print(f'序列号:{number}')

#使用学号构造即将发送的字节数据
startData=b'\xaa\x55' +bytes([ord(ch)-ord('0') for ch in studentID])

#写入设备
ser.write(startData)

password=b'' #读到的密码
i=1 #循环次数

#开启永真循环,直到ctrl+c被按下
try:
    while True:
        #读1位,探测魔数
        t=b2s(ser.read(1))
        #第一个魔数应该是AA
        if t !="AA":
            continue

        #再读一位
        t=b2s(ser.read(1))
        #第二个魔数应该是55
        if t !="55":
            continue

        #判断魔数后可以放心读取数据
        #读入密码开始位置的索引
        index=list(ser.read(1))[0]

        #若索引大于4,则表明中间有无关字节,吸收掉无关字节
        if index-4 >0:
            ser.read(index -4)

        #读入四字节的密码
        password=ser.read(4)
        print(f'[{i}]密码:{b2s(password)}')

        #使用这次读到的密码构造数据,发送给设备以读取下一次的密码
        ser.write(b'\xaa\x55'+password)
        i+=1

运行该程序(如果出现少包就使用pip install),就可以得到对应的结果如下:
在这里插入图片描述
得到密码为6041C890。

4、将学号、序列号、最后一串密码发送到课程后台。上送命令语法为

curl “132.232.98.70:6363/checkSecret?id=学号&v=序列号&s=密码”

例如,序列号是A01D564D744A42C9363F5E,学号是202201110203,最后一串密码为51ED8D3A,那么上报结果命令为:

curl “132.232.98.70:6363/checkSecret?id=202201110203&v=A01D564D744A42C9363F5E&s=51ED8D3A”

解:根据上面得到的密码输入上述指令即可得到如下结果:
在这里插入图片描述

3.6RS485信号的测量

一、实验目的

  1. 熟练使用Linux下io函数read、write和epoll等
  2. 熟练S485串口的信号特点

二、RS485信号介绍

RS485的基本介绍:
在这里插入图片描述

RS485的基本原理:
在这里插入图片描述
我们在这里需要使用的就是这个RS485接口,使用杜邦线来接收信号和传输信号,后面就需要使用这两个接口来完成A和B板之间的通信。
在这里插入图片描述

三、实验过程

本次实验需要两个单片机,一个下载A程序,另一个下载B程序,两个之间的传输信号,从而得到最后的结果。
1、为自己单片机电路板(后面称为A板)下载新的hex文件。
这里我们首先需要测得A板的波特率,这里我们按照下图接入接口连接示波器,按照上一节的方法来测波特率:
请添加图片描述
得到的方波:请添加图片描述
可以看到一格的大小为20us,我们可以算得波特率为57600。注意这里测波特率,我们需要使用single,然后按一下k3才能得到信号。
这里,我们需要将A板和B板的波特率调整至相同,接下来就需要一组的成员下载B板程序。
同时,这里测波特率也并不相同,这里我们需要调整B板的波特率,所以我们需要用到下图中的摇杆:
在这里插入图片描述
按照上图相同的接法,连接好B板与示波器。与上面的一样,使用signle,同时摇动摇杆,直至B板和A板的波特率相同,即下图:
请添加图片描述
发现,两者相同,接下来我们就可以得到A板的序列号了。

连接好A板和B板,如下图:
请添加图片描述
这里直接使用串口助手,从B板读取A板的序列号。本来是需要用示波器读出A板的序列号的,但是这里读出的序列号不正确。所以,我们使用串口助手来读取序列号。

然后按下A板上的K3按键,电路板将通过RS485向外发出一组序列号信息。该信号序列格式为:
0xAA 0x55 4字节序列号
打开B板的串口即可以收到A板的序列号,结果如下图:
请添加图片描述
根据,串口助手我们可以得到A板的序列号为43 22 4B E6,接下来就可以进行第三步了。(第二步就是调试B板的波特率使其与A板的波特率相同)。
3、使用在计算上的程序,调用write函数接口向串口发送读密码命令。读密码命令格式为:
0xAA 0x55 A板序列号 12字节学号
注意,发送时所使用的波特率应该与A板通信波特率一致。命令通过B板转换为RS485信号发送到A板。
A板会回应密码,回应格式为:
0xAA 0x55 4字节密码
回应密码会经过B板转发到计算机上。请调用read函数接口从串口读取该密码。

这里,我们需要输入的序列如下:0xAA,0x55,0x43,0x22,0x4B,0xE6,0x02,0x00,0x02,0x00,0x00,0x08,0x00,0x01,0x00,0x02,0x00,0x06
所以,我们编写的代码如下:

#include <stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include "com.h"
#include "com.c"
unsigned char func(unsigned char a);
int main(void)
{
	unsigned char tmp[1]= {0}; //用于存放读取出来的数据的缓冲区
	int rl; //读取数据的长度(单位:字节)
	int i;
 
	fd = openSerial("/dev/ttyUSB0"); //打开串口,ttyUSB0是串口文件
	
	if(fd < 0)
	{
		printf("open com fail!\n");
		return 0;
	}
	
	EpollInit(fd); //初始化终端事件触发函数epoll,设置要监听的事件及相关参数等
	
	unsigned char buf[]={0xAA,0x55,0x43,0x22,0x4B,0xE6,0x02,0x00,0x02,0x00,0x00,0x08,0x00,0x01,0x00,0x02,0x00,0x06};
	write(fd,buf,18);
      while(1)
	{
	write(fd,buf,18);
		bzero(tmp,sizeof(tmp)); //把tmp对应的内存块的前sizeof(tmp)个字节置零
		
		rl = ComRead(tmp,13);//读取13个字节放到缓存
         
		//打印读到的数据
		printf("read_len = %d\n", rl);
		tmp[rl] = '\0';
		for(i = 0; i < rl; i++)
			printf(" %02x", tmp[i]);
		printf("\n\n");
	
	
	}
	
	close(epid);
	close(fd);
	
	return 0;
}

注意,这里记得更改com.c的波特率,这里使用的代码是在前几节中的代码直接更改的。可以跑出来的结果为:
请添加图片描述
所以,该板的密码为26 e0 41 4b,接下来按照题目中所给指令输入即可。

4、请将A板序列号、学号和密码通过curl命令发送到课程后台系统。
上送命令语法为
curl “132.232.98.70:6363/check485?id=学号&v=序列号&s=密码”

所以,这里我需要传送的指令为
curl “132.232.98.70:6363/check485?id=202008010206&v=43224BE6&s=26e0414b”
得到如下的结果:
在这里插入图片描述
说明此实验成功。

3.7RS485总线数据收发

一、实验目的

  1. 熟练使用Linux下io函数read、write和epoll等
  2. 熟练处理流式通信数据
  3. 理解485总线的冲突问题

二、预备知识

RS485总线有两条线信号线,能够传输一个逻辑信号。计算机标准的UART串口有RX、TX收发两条线,因此能够同时进行数据的接收和发送。而RS485只有一个逻辑信号,因此同一时刻只能有一个主体进行数据发送(因此叫做半双工通信串口)。

单片机板上使用了一颗MAX485芯片作为标准UART接口向485接口转换的接口芯片。请同学们学习该芯片介绍和各引脚的功能。

如下图所示,单片机串口数据发送脚与MAX485的DI脚相连接。在发送数据前,单片机会将DE脚置为高电平,此时A、B引脚信号为所发送的单片机串口数据。
在这里插入图片描述
如果多个单片机将DE脚置为高电平,则A、B信号线同时被多个单片机板的MAX485芯片驱动,则会造成数据收发错误甚至损坏芯片。因此,多个单片机对485总线操作的时间控制至关重要。

三、实验过程

与上述实验相同,现在自己的电脑上下载A程序,在同伴的电脑上下载B程序。这里不需要观察波特率了,波特率固定为了1200。

下载程序后,A板单片机将使用1200波特率约每300ms发送一次自身序列号,格式为
0xAA 0x55 4字节序列号

这里使用示波器可以读出板子的序列号,主要这里的示波器显示的是二进制数,需要转化成16进制的数,同时注意板子不仅有起始位,也有停止位。读出的序列号如下图:
在这里插入图片描述
2、通过B板串口向RS485总线写入自己的学号,格式为

0xAA 0x55 十二位学号数字。

例如学号20220110203,应该通过串口发送以下数据

AA 55 02 00 02 02 00 01 01 01 00 02 00 03

这里可以通过串口助手来查看输出密码的格式,就是中间的填充字符是什么,是多少个。提供了下面代码实现的思路。

3、A板单片机接收到学号后会每300m发送第一串密码,密码长度为4字节。请将解析出该串密码并在150ms内原样发回给串口(否则会造成总线的冲突)。STC单品机收到返回后会继续发送下一串密码,请继续解析出该串密码并在150ms内原样发回给串口。以此往复,将收到的最后一串密码记录下来。

例如,收到以下密码串

AA 55 00 00 00 00 00 C9 34 3F 5D

其中AA 55为前导串,最后4个字节是密码。请将密码原样发回给串口。

AA 55 C9 34 3F 5D

4、将学号、序列号、最后一串密码发送到课程后台。上送命令语法为

curl “132.232.98.70:6363/check485Secret?id=学号&v=序列号&s=密码”

例如,序列号是2C9363F5E,学号是202201110203,最后一串密码为51ED8D3A,那么上报结果命令为:

curl “132.232.98.70:6363/check485Secret?id=202201110203&v=2C9363F5E&s=51ED8D3A”

我们这里将3和4小问合到一起。
我们在这里使用python,当我们使用python的时候,我们其实就不需要进行第二步了。python的功能强大,我们可以直接使用AA55的前四位来当作密码就可以了,不需要知道中间有多少位填充符,同时我们直接调用包内函数就可以自动调交到服务器上面,十分省事。

import serial
import requests
import serial.tools.list_ports

# 字节转字符串


def b2s(data):
    return ''.join([f'{ch:0>2x}' for ch in data]).upper()


# 连接设备
ser = serial.Serial(list(serial.tools.list_ports.comports()[0])[0], 1200)
# 判断是否连接到设备
assert(ser != None)

# 输入学号
studentID = input("学号:")
# 判断学号是否正确
assert(studentID.isdigit() and len(studentID) == 12)

# 读入序列号
number = b2s(ser.read(6))
# 断言魔数为AA55
assert(number[:4] == "AA55")

# 取出序列号
number = number[4:]
# 判断序列号长度是否正确
assert(len(number) == 4 * 2)
print(f'序列号:{number}')

# 使用学号构造即将发送的字节数据
startData = b'\xaa\x55' + bytes([ord(ch) - ord('0') for ch in studentID])
# 写入数据
ser.write(startData)

password = b''  # 读到的密码
i = 1   # 记录循环次数

# 开启永真循环,直到Ctrl+C被按下
try:
    while True:
        data = ser.read_all()
        sdata = b2s(data)
        if data != b'' and sdata[:4] == "AA55":
            password = data[-4:]
            if len(password) == 4:
                print(f'[{i}]密码:{b2s(password)}')

                # 使用这次读到的密码构造数据,发送给设备以读取下一次的密码
                ser.write(b'\xaa\x55' + password)
                i += 1

# 当Ctr+C被按下时执行,建议在读到的密码不再变化时按下,大概在256次循环之后
except KeyboardInterrupt:
    print(f'[结束]最后一串密码为:{b2s(password)}')
    print(f'[正在提交]http://132.232.98.70:6363/checkSecret?id={studentID}&v={number}&s={b2s(password)}')

    # 提交实验结果至服务器
    r = requests.get(f"http://132.232.98.70:6363/checkSecret?id={studentID}&v={number}&s={b2s(password)}")

    # 判断提交是否成功
    assert(r.status_code == 200 and r.text.isdigit())
    print(f'[提交成功]学号:{studentID} 密码循环次数:{r.text}')

最后的实验结果为:
在这里插入图片描述
到达了密码的最大次数,至此实验全部完成。

心得和体会

感觉对串口读写的编程实现有了更多的理解,同时磨练了我的心智,坚定了我的信念。

  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值