jjzjj

c++ - 串行编程 RS485

coder 2023-11-15 原文

我的任务是在 RS485 2 线系统上实现 ModBus 协议(protocol)。 (实际上是三根线,A/B 和 GND)。 ModBus 不是重点,而是之前的步骤……通过接口(interface)进行简单的 I/O。

我正在使用 FTDI USB-RS485 转换器将 Linux 主机(不可互换)连接到 Windows 主机(可与另一台 Linux 主机互换,但我想避免这种情况)

编码应该是 19200, 8, n, 1。 但这似乎行不通。

我手边没有确切的代码,但在 Linux 上我这样做:

 int fd = open("/dev/ttyS3", O_RDWR | O_CTTY);
 if(fd == -1) return "Error while opening the port";

接下来,我配置端口。

struct termios tty;

tcgetattr(fd, &tty);

cfsetispeed(&tty, B19200);
cfsetospeed(&tty, B19200);

tty.c_cflag  = CS8;              //Empties the cflags and sets the character width.
tty.c_cflag |= (CLOCAL | CREAD); //Sets 'recommended' options.

tty.c_lflag  = 0;
tty.c_iflag  = 0;
tty.c_oflag  = 0;

tcgetattr(fd, TCSANOW, &tty);

Parity 和 Flow Control 目前没有计划,因为最终结果将连接到低级板,我需要自己处理信号。此外,没有任何电线,这将允许“不受约束的通信”。 (毕竟我不希望 XON/XOFF 字符限制我可以传输的字节范围)

所有这些功能都正常运行并设置了数据。

在 Windows 上,我这样打开串口:

DCB SP;
HANDLE hSerial = CreateFile("COM6", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if(hSerial == INVALID_HANDLE_VALUE) return "Error while opening the port";
GetCommState(hSerial, &SP);

奇偶校验以及流控制被禁用。字节大小设置为 8。

编辑: 既然有人问过,这是我在 Windows 上的波特率代码(来自内存) SP.DCBlength= sizeof(SP); SP.BaudRate = 19200; SP.Parity = NOPARITY; SP.StopBits = ONESTOPBIT; SetCommState(hSerial, &SP);

同样,所有这些功能都可以完美运行。

现在,对于让我很头疼的测试用例。

在 Linux 主机上,我创建了一个 256 字节大小的字节缓冲区。 这个缓冲区填充了 0-255 的字符值...然后通过网络发送 write。 与此同时,另一端正在等待'ReadFile'数据的到来。

使用此配置,对于“其他 Linux 主机”以及 Windows 主机,都会到达 256 字节...但是它不是 0-255 之间的数字,而是 00 06 等。

当我在设置我真正想要的选项之前将 termios 结构的所有成员设置为 0 时,我可以让 Linuxhost 工作。我猜,这是因为控制字符......但是如果我这样做,Windows 主机要么只接收 256 个字节中的 4 个。

正如我所说,不幸的是我手头没有代码。如果有人知道我可以从什么角度解决这个问题,我将不胜感激。一旦我再次访问它,我将发布更多代码。

我是如何实现读取操作的:

DWORD nBytes = 0;
char Buffer[256], *ptr = Buffer;
int Rem = 256;

while(Rem) {
    ReadFile(hSerial, ptr, Rem, &nBytes, 0);
    Rem -= nBytes;
    ptr += nBytes;
}

//Evaluate Buffer

请注意,我确实设置了超时,但不记得确切的值。

编辑:因为我现在可以再次访问我的工作地点,所以这里是实际(当前)代码。

const char *InitCOM(const char *TTY) {
    struct termios tty;
    hSerial = open(TTY, O_RDWR | O_NOCTTY | O_NDELAY);
    if(hSerial == -1) return "Opening of the port failed";
    fcntl(hSerial, F_SETFL, 0);
    if(tcgetattr(hSerial, &tty) != 0) return "Getting the parameters failed.";
    if(cfsetispeed(&tty, B19200) != 0 || cfsetospeed(&tty, B19200) != 0) return "Setting the baud rate failed.";


    //CFlags
    //Note: I am full aware, that there's an '=', and that it makes the '&=' obsolete, but they're in there for the sake of completeness.
    tty.c_cflag  = (tty.c_cflag & ~CSIZE) | CS8;    //8-bit characters
    tty.c_cflag |= (CLOCAL | CREAD);und erlaubt 'Lesen'.
    tty.c_cflag &= ~(PARENB | PARODD);          
    tty.c_cflag &= ~CSTOPB;
    tty.c_cflag &= ~CRTSCTS;                    

    //Input Flags
    tty.c_iflag     &= ~IGNBRK;             
    tty.c_iflag &= ~(IXON | IXOFF | IXANY);         

    //Local Flags
    tty.c_lflag  = 0;                   

    //Output Flags
    tty.c_oflag  = 0;

    //Control-Characters
    tty.c_cc[VMIN]   = 0;
    tty.c_cc[VTIME]  = 5;
    if(tcsetattr(hSerial, TCSAFLUSH, &tty) != 0) return "Setting the new parameters failed";
return NULL;

至于实际的发送/接收代码:

int main(int argc, char* argv[]) {
    #if defined FOR_PC
        const char *err = InitCOM("/dev/ttyUSB0");
    #else
        const char *err = InitCOM("/dev/ttyS3");
    #endif

    if(err) printf("Error while initalizing: %s ErrNum: %d\n", err, errno);
    else {
    /*unsigned char C[256];    //Original code with the array
    int nBytes;
    #ifdef FOR_PC
        int Rem = 256, ReqCount = 0;
        unsigned char *ptr = C;
        while(Rem > 0) {    
            fd_set fds;
            FD_ZERO(&fds);
            FD_SET(hSerial, &fds);
            select(hSerial+1, &fds, NULL, NULL, NULL);
            nBytes = read(hSerial, ptr, Rem);
            if(nBytes > 0) {
                Rem -= nBytes;
                ptr += nBytes;
                ++ReqCount;
            }
        }
        printf("Number of received Bytes: %d in %d sends.\n\n", 256 - Rem, ReqCount);
        for(int i = 0; i < 256; ++i) {
            printf("%02X ", C[i]);
            if((i%32) == 31) printf("\n");
        }
    #else
        for(int i = 0; i < 256; ++i) C[i] = i;
        nBytes = write(hSerial, C, 256);
        printf("\nWritten Bytes: %d\n", nBytes);
    #endif*/

    //Single-Byte Code
    unsigned char C = 0x55;
    #ifdef FOR_PC
        while(true) {   //Keeps listening
            fd_set fds;
            FD_ZERO(&fds);
            FD_SET(hSerial, &fds);
            select(hSerial+1, &fds, NULL, NULL, NULL);
            read(hSerial, &C, 1);
            printf("Received value 0x%02X\n", C);
        }
    #else
        write(hSerial, &C, 1);  //Sends one byte
    #endif
    close(hSerial);
}
return 0;

至于示波器:我已经通过发送测试了两个方向。两人的工作都令人钦佩。

0x55 的信号是一个恒定的 Up/Down,长度为 50 微秒(应该如此,因此设置波特率也没有问题)。

那么我的“接收”代码中有什么地方做错了吗? “选择”有误吗?

最佳答案

  1. 您是否也在 Windows 端设置了正确的波特率?
  2. 使用示波器检查电线上的实际数据。调试串行通信是发明示波器的目的。几乎。 :)

关于c++ - 串行编程 RS485,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12531289/

有关c++ - 串行编程 RS485的更多相关文章

  1. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  2. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  3. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  4. 网络编程套接字 - 2

    网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识

  5. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  6. ruby - 我正在学习编程并选择了 Ruby。我应该升级到 Ruby 1.9 吗? - 2

    我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or

  7. ruby - 如何以编程方式删除实例上的 "singleton information"以使其编码(marshal)? - 2

    我创建了一个由于“在运行时执行的单例元类定义”而无法编码的对象(这段代码的描述是否正确?)。这是通过以下代码执行的:#defineclassXthatmyusesingletonclassmetaprogrammingfeatures#throughcallofmethod:break_marshalling!classXdefbreak_marshalling!meta_class=class我该怎么做才能使对象编码正确?是否可以从对象instance_of_x的classX中“移除”单例组件?我真的需要一个建议,因为我们的一些对象需要通过Marshal.dump序列化机制进行缓存。

  8. arrays - Ruby 数组 += vs 推送 - 2

    我有一个数组数组,想将元素附加到子数组。+=做我想做的,但我想了解为什么push不做。我期望的行为(并与+=一起工作):b=Array.new(3,[])b[0]+=["apple"]b[1]+=["orange"]b[2]+=["frog"]b=>[["苹果"],["橙子"],["Frog"]]通过推送,我将推送的元素附加到每个子数组(为什么?):a=Array.new(3,[])a[0].push("apple")a[1].push("orange")a[2].push("frog")a=>[[“苹果”、“橙子”、“Frog”]、[“苹果”、“橙子”、“Frog”]、[“苹果”、“

  9. Ruby 元编程问题 - 2

    我正在查看Ruby日志记录库Logging.logger方法并从sourceatgithub提出问题与这段代码有关:logger=::Logging::Logger.new(name)logger.add_appendersappenderlogger.additive=falseclass我知道类 最佳答案 这实际上删除了方法(当它实际被执行时)。这是确保close不会被调用两次的保障措施。看起来好像有嵌套的“class 关于Ruby元编程问题,我们在StackOverflow上找到一

  10. ruby - Paperclip:以编程方式分配图像并设置其名称 - 2

    使用Paperclip,我想从这样的URL抓取图像:require'open-uri'user.photo=open(url)问题是我最后得到一个像“open-uri20110915-4852-1o7k5uw”这样的文件名。有什么方法可以更改user.photo上的文件名?作为一个额外的变化,Paperclip将我的文件存储在S3上,所以如果我可以在初始分配中设置我想要的文件名就更好了,这样图像就会上传到正确的S3key。像这样:user.photo=open(url),:filename=>URI.parse(url).path 最佳答案

随机推荐