本来还想着做多机的,但老师已经打分了就算了。为了压缩到6页删减了很多内容,将就看吧
基于485总线的评分系统实验报告 实验目标:通过本案例加深理解RS485通信方式,实现上位机的主控制器与所有的下位机进行通信。
B级任务 实验要求:使用两块STC板分别下载上一节所提供的.hex文件,搭建485双机通信电路,在linux中编程实现打分程序代码。
实验资源:Windows10电脑,Ubuntu18.04,单片机,杜邦线 实验步骤:-
阅读程序系统流程框图,明确双机通信的功能需求。
-
熟悉上一节中模拟MODBUS协议的数据包结构,相关功能码及附加数据定义
-
回顾485总线数据收发实验,搭建双机通信电路。
-
PC端串口设置如下:串口波特率:9600 数据位:8位 校验位:无 停止位:
5.所编写的PC端程序应参考上一节中的通信协议完成一次完整的评分过程:
-
需要包含串口的设置
-
主节点发起从机检测过程:发送指定从机编号正常检测数据包,判断回应查询数据包是否符合上一节中的通信协议。
-
主机获取从机评分过程:发送指定从机评分相关数据包,判断回应查询数据包是否符合上一节中的通信协议。
-
主机发起结束评分的过程,若复位成功,STC从机上第1位和第8位LED 灯会熄灭。
-
展示串口相关信息,展示检测到的从机编号和从机的评分等。
1.定义了几个本次实验要用到的函数:
string format(const vector<uchar> &data); //数据处理为字符串
int check(serial &pipe, int addr); //设备检测
int get_score(serial &pipe, int addr); //获取分数
void reset(serial &pipe); //从机复位
2.接下来是主函数,首先从单片机读入数据,波特率设为9600(题目已给出)
serial pipe("/dev/ttyUSB0", B9600);
3.接着根据题目的要求根据用户输入来选择想要的功能。第一个是根据单片机的编号查找对应的分数,第二个是将从机复位,第三个是退出程序,通过设置循环来重复用户的选择。具体代码如下:
while(1){
cout<<"--------------------基于RS485总线的评分系统-------------------------"<<endl;
cout<<"输入说明:\n1. 输入合法从机地址查询分数\n2. 输入-2将从机复位\n3. 输入-1退出程序\n请输入指令:";
cin>>addr;
if(addr == -1) break;
if(addr == -2) {
reset(pipe);
continue;
}
check_ret = check(pipe, addr);
if(check_ret == 1){
cout<<"设备检测正常"<<endl;
sleep(1);
if(!(score = get_score(pipe, addr)))
cout<<"获取分数失败"<<endl;
else
cout<<"分数:"<<score<<endl;
}
else if(check_ret == 0){
cout<<"地址错误,请输入地址重试"<<endl;
continue;
}
else{
cout<<"数据传输错误,请重启从机"<<endl;
}
//reset(pipe);
cout<<"从机已复位,输入-1退出"<<endl;
}
可以看到,输入-1时退出循环,然后return 0
退出程序。输入-2时调用reset
函数使从机复位。输入正确的从机编号后就会输出对应的成绩,主要是通过调用check
函数来检测从机编号是否正确,调用get_score
来获得成绩,下面具体再分析一下这三个函数的实现思路。
4.reset
函数完整代码:
void reset(serial &pipe){
vector<uchar> code = {0x5a, 0x00, 0x01,0x00,0x5b};
//cout <<"reset:"<< format(code) << endl;
/* 发送数据包 */
for(int i=0;i<600;i++) {
pipe.myWrite(code);
}
return;
}
参数值是serial串口的数据,根据实验指导可以知道,当主机要发送复位信号给从机时,其序列号应为5a + 广播地址00 + 复位功能码01 + 00 +校验字节
,所以实现思路就是定义一个字符串序列号,然后通过主机不断向从机发送数据包,最终实现从机复位。
5.check
函数完整代码:
int check(serial &pipe,int addr){
int check_code = 117 + addr, ret; //校验码为累加和
vector<uchar> code = {0x5a, 0x08, 0x13};
vector<uchar> rec;
code.insert(code.begin()+1, (uchar)addr); //插入从机地址
code.push_back((uchar)check_code); //插入校验码
//cout << format(code) << endl;
/* 写入并接收回应数据包 */
pipe.myWrite(code);
sleep(1);
rec = pipe.myRead(5);
if(format(rec) == format(code)) //检验接收数据包是否与发送相同,相同则从机地址正确
ret = 1;
else{
if(rec[3] == 0x6f) ret = 0;
else ret = -1;
}
return ret;
}
函数的参数为串口数据和从机地址。主机发送进行数据检测的序列号为5a + 从机地址 + 检测功能码08 + 13 + 校验码
。实现思路就是将数据检测序列号写入,然后根据收到的数据包来判断从机地址是否正确。如果返回1说明地址正确,返回0说明地址错误,返回-1说明数据错误。
6.get_score
函数完整代码:
int get_score(serial &pipe, int addr){
int check_code = 93 + addr, ret; //校验码为累加和
vector<uchar> code = {0x5a, 0x00, 0x03};
vector<uchar> rec;
code.insert(code.begin()+3, (uchar)addr); //插入从机地址
code.push_back((uchar)check_code); //插入校验码
//cout << format(data) << endl;
/* 写入并接收回应数据包 */
pipe.myWrite(code);
sleep(1);
rec = pipe.myRead(5);
if(rec[3] == 0x6f) ret = -1; //检验是否错误
else {
ret = (int)rec[3]; //转为数字
if(ret < 0 || ret > 100) ret =-2; //检测数字是否合法
}
return ret;
}
函数的参数为串口数据和从机地址。主机发送进行分数获取序列号为5a + 00 + 读取功能码03 + 从机地址 + 校验码
。实现思路和上一个函数一样,将数据检测序列号写入,然后根据收到的数据包来获得分数,同时要对分数的合法性进行判断。如果正确则返回正确分数,返回-1说明从机未准备好,返回-2说明从机错误。
7.format
函数完整代码如下:
string format(const vector<uchar> &data) {
std::string str(2 * data.size() + 1, '\x00');
for (int i = 0; i < data.size(); i++) {
sprintf(&str[i * 2], "%02X", data[i]);
}
return str;
}
函数的功能主要是将数据处理为字符串,在check
函数中使用
这里设置下位机的编号为2号,分数为66分
打开程序,运行后输入从机编号2可得成绩,输入-2可复位从机,输入-1可退出程序
可以看到,结果正确,当输入正确的从机地址2时,输出了分数66。当输入了-2时,从机就会复位,效果如第一张图所示,单片机上的第一个和第八个灯熄灭。当输入-1时,直接退出程序。
- 人文:认识到交流的重要性。很多不懂的问题通过和同学交流都得到了有效解决。同时遇到问题时可以向老师虚心求问。
- 知识:掌握了基于485总线的评分系统的工作原理和具体实现。学会了如何获取串口数据,并利用串口数据进行一些判断。还学会了如何使用c++来实现基于485总线的评分系统的双机评分。
- 技能:能够较快的编写出c++代码,同时对于单片机的使用和串口助手软件的使用更加得心应手,对于单片机的一些按键能够简单使用。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)