双端口可以是双PC机,也可以是一台PC的两个串口。
可实现2端口间通信,其中有通信协议的设置。分为主从站。
功能+协议文档
一、 硬件环境
- 连接方式
RS-485,一主四从模式
各站点间通过“USB转485”相连,将各转接器的485并联到总线上。 - 网络拓扑
二、 报文格式概述
报文格式
类型 帧头 源
地址 目的
地址 主
功能码 读写
功能码 有效
数据 CRC 帧尾
字节数 1 1 1 2 2 2 2 1
ASCII ‘*’ 如‘0’ 如‘1’ 如‘Ms’ 如‘Wr’ 如‘12’ 如‘12’ ‘#’主功能码:
- 定时通信: 主 –》 某从 Master Slave Ms
- 广播同步: 主 –》 任意从 BroadCast Bc
- 错误检测: 主 –》 从 Error Er
从 –》 主 - 网络管理: 主 轮询 从 Net Control Nc
读写功能码:
- 读 : Write Wr
- 写 : Read Rd
- 无 : None No
三、各功能描述
- 定时通信
1.1 主站主动向从站发, 读写功能码填Wr或Rd.
2.1 读:从站“读写功能码”回复为读,有效数据为待读数据
写:从站“读写功能码”回复为写,有效数据为原(主向从发送的)报文。
类型 帧头 源
地址 目的
地址 主
功能码 读写
功能码 有效
数据 CRC 帧尾
字节数 1 1 1 2 2 2 2 1
ASCII ‘*’ 如‘0’ 如‘1’ 如‘Ms’ 如‘Wr’ 如‘12’ 如‘12’ ‘#’
- 广播同步
类型 帧头 源
地址 目的
地址 主
功能码 读写
功能码 有效
数据 CRC 帧尾
字节数 1 1 1 2 2 8 2 1
ASCII ‘*’ ‘0’ ‘F’ ‘Br’ ‘No’ “12:00:00” 如‘12’ ‘#’
因所有从站均接收,故目的地址为’F’.
不需要读写功能码。
有效数据为时间(时:分:秒)
3. 错误检测
类型 帧头 源
地址 目的
地址 主
功能码 读写
功能码 有效
数据 CRC 帧尾
字节数 1 1 1 2 2 2 2 1
ASCII ‘*’ 如‘1’ 如‘0’ ‘Er’ 无 无 无 无
上图为错误帧”*10Er#”, 主功能码为错误功能码, 有效数据设为默认的“%%”。
以主-》从为例,当从收到CRC不一致时,向主发送此错误帧,主会重传原报文。
4. 网络管理
类型 帧头 源
地址 目的
地址 主
功能码 读写
功能码 有效
数据 CRC 帧尾
字节数 1 1 1 2 2 2 2 1
ASCII ‘*’ 如‘0’ 如‘1’ ‘Nc’ 如‘Wr’ 如‘12’ 如‘12’ ‘#’
4.1 掉线检测:
主轮询,某从若3次未回复
4.2 上线检测:
主轮询(冗余), 若某从第一次回复,认为上线。假设目前为1主4从, 但主会冗余轮询1-9共9个从站,故1-9间任意从站上线均会认为上线。
从站若在线,回复相同报文。
5. 数据记录
存为txt文件, 绘制历史曲线, 可选择调取任意站点、时间的数据。
固定节点4为监控节点, 不断监控总线上的所有数据。
6. 闭环控制
以控制炉温100度为例:
传感器(节点1)测得90度,将90度发送给控制器(节点2) 节点2算得100-90 = 10度偏差, 将”10度偏差对应的控制动作“送给执行器(节点3) 执行器(节点3)收到后,使炉温由90度上升到100度 节点4一直在记录总线数据。
2个节点的简化如下:
以控制炉温100度为例:
传感器(节点1)测得90度,将90度发送给控制器(节点2) 节点2算得100-90 = 10度偏差, 将”10度偏差对应的控制动作“送给执行器(节点3) 执行器(节点3)收到后,使炉温由90度上升到100度 节点4一直在记录总线数据。
接收程序
//----------------------------------------------------------
// 串口控件
void CTestDlg::OnCommMscomm1()
{
// TODO: 在此处添加消息处理程序代码
if (m_mscom.get_CommEvent() == 2)
{
char str[1024] = { 0 };
long k;
VARIANT InputData = m_mscom.get_Input();
COleSafeArray fs;
fs = InputData;
for (k = 0; k < fs.GetOneDimSize(); k++)
fs.GetElement(&k, str + k);
m_EditReceive += str; // 显示到接收框内
// 根据报头报尾区分报文,并计算CRC
int start, end;
int crc = 0;
for (start = 0; start < strlen(str); ++start) // 找start
{
if (str[start] == '*')
break;
}
for (end = start; end < strlen(str); ++end) // 找end
{
if (str[end] == '#')
break;
}
for (int i = start; i < end - 3; ++i)
{
crc += str[i];
}
crc = crc % 100;
if (crc != str[end - 2] * 10 + str[end - 1]) // 如果crc不相等的话
{
WrongFlag = true; // 主向从发送固定的错误帧为*10Er#
}
// str里有很多段报文,这只是截取了一段
UpdateData(false);
}
}
发送程序
//----------------------------------------------------------