基于RS485的双机(客户端)通信软件-MFC(C++)实现

双端口可以是双PC机,也可以是一台PC的两个串口。
可实现2端口间通信,其中有通信协议的设置。分为主从站。

功能+协议文档

一、 硬件环境

  1. 连接方式
    RS-485,一主四从模式
    各站点间通过“USB转485”相连,将各转接器的485并联到总线上。
  2. 网络拓扑

二、 报文格式概述

  1. 报文格式
    类型 帧头 源
    地址 目的
    地址 主
    功能码 读写
    功能码 有效
    数据 CRC 帧尾
    字节数 1 1 1 2 2 2 2 1
    ASCII ‘*’ 如‘0’ 如‘1’ 如‘Ms’ 如‘Wr’ 如‘12’ 如‘12’ ‘#’

  2. 主功能码:

    1. 定时通信: 主 –》 某从 Master Slave Ms
    2. 广播同步: 主 –》 任意从 BroadCast Bc
    3. 错误检测: 主 –》 从 Error Er
      从 –》 主
    4. 网络管理: 主 轮询 从 Net Control Nc
  3. 读写功能码:

  4. 读 : Write Wr
  5. 写 : Read Rd
  6. 无 : None No

三、各功能描述

  1. 定时通信
    1.1 主站主动向从站发, 读写功能码填Wr或Rd.
    2.1 读:从站“读写功能码”回复为读,有效数据为待读数据
    写:从站“读写功能码”回复为写,有效数据为原(主向从发送的)报文。

类型 帧头 源
地址 目的
地址 主
功能码 读写
功能码 有效
数据 CRC 帧尾
字节数 1 1 1 2 2 2 2 1
ASCII ‘*’ 如‘0’ 如‘1’ 如‘Ms’ 如‘Wr’ 如‘12’ 如‘12’ ‘#’

  1. 广播同步
    类型 帧头 源
    地址 目的
    地址 主
    功能码 读写
    功能码 有效
    数据 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);
    }
}

发送程序

//----------------------------------------------------------
  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
当然,我可以为您提供一个基于RS485的客户和服务的C++代码示例。请注意,以下代码仅供参考,您可能需要根据您的具体需求进行适当的修改。 首先,让我们看一下RS485客户的代码示例: ```cpp #include <iostream> #include <cstring> #include <unistd.h> #include <fcntl.h> #include <termios.h> int main() { int fd; const char* device = "/dev/ttyUSB0"; // RS485设备路径 // 打开串口设备 fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY); if (fd == -1) { std::cout << "无法打开串口设备" << std::endl; return -1; } // 配置串口 struct termios options; tcgetattr(fd, &options); options.c_cflag = B9600 | CS8 | CLOCAL | CREAD; // 波特率9600,8个数据位,无流控制 options.c_iflag = 0; options.c_oflag = 0; options.c_lflag = 0; tcflush(fd, TCIFLUSH); tcsetattr(fd, TCSANOW, &options); // 发送数据 const char* message = "Hello, RS485!"; write(fd, message, strlen(message)); // 读取响应 char buffer[255]; ssize_t bytesRead = read(fd, buffer, sizeof(buffer)-1); if (bytesRead > 0) { buffer[bytesRead] = '\0'; std::cout << "接收到的数据:" << buffer << std::endl; } // 关闭串口设备 close(fd); return 0; } ``` 接下来,让我们看一下RS485服务的代码示例: ```cpp #include <iostream> #include <cstring> #include <unistd.h> #include <fcntl.h> #include <termios.h> int main() { int fd; const char* device = "/dev/ttyUSB0"; // RS485设备路径 // 打开串口设备 fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY); if (fd == -1) { std::cout << "无法打开串口设备" << std::endl; return -1; } // 配置串口 struct termios options; tcgetattr(fd, &options); options.c_cflag = B9600 | CS8 | CLOCAL | CREAD; // 波特率9600,8个数据位,无流控制 options.c_iflag = 0; options.c_oflag = 0; options.c_lflag = 0; tcflush(fd, TCIFLUSH); tcsetattr(fd, TCSANOW, &options); // 接收数据 char buffer[255]; ssize_t bytesRead = read(fd, buffer, sizeof(buffer)-1); if (bytesRead > 0) { buffer[bytesRead] = '\0'; std::cout << "接收到的数据:" << buffer << std::endl; // 处理数据并进行响应 const char* response = "Hello from RS485!"; write(fd, response, strlen(response)); } // 关闭串口设备 close(fd); return 0; } ``` 请注意,上述代码中的"/dev/ttyUSB0"是一个示例RS485设备路径,您需要根据您实际使用的设备来修改它。此外,代码中使用的波特率为9600,您可以根据需要进行适当调整。 希望以上示例能帮助到您!如果您有任何进一步的问题,请随时提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值