首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >串行编程RS485

串行编程RS485
EN

Stack Overflow用户
提问于 2012-09-21 21:11:51
回答 3查看 17.6K关注 0票数 10

我的任务是在RS485双线系统上实现ModBus协议。(实际上是三根线,A/B和GND)。不过,ModBus不是重点,而是接口上的that...simple I/O之前的一步。

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

编码应该是19200,8,n,1。但它似乎不起作用。

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

代码语言:javascript
复制
 int fd = open("/dev/ttyS3", O_RDWR | O_CTTY);
 if(fd == -1) return "Error while opening the port";

接下来,我配置端口。

代码语言:javascript
复制
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);

奇偶校验和流量控制目前没有计划,因为最终结果将连接到一个低级电路板,在那里我需要自己处理信号。此外,没有任何电线,这将允许“自由通信”。(毕竟,我不希望XON/XOFF字符限制我可以传输的字节范围)

所有这些函数都会正常运行,并设置好数据。

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

代码语言:javascript
复制
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字节的arrive...however不是从0到255的数字,而是0006等等。

当我在设置实际需要的选项之前将termios结构的所有成员设置为0时,我可以让Linuxhost工作。我猜,这是因为控制characters...however,如果我这样做,视窗主机只收到4个256个字节。

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

我如何实现读取操作:

代码语言:javascript
复制
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

需要注意的是,我确实设置了超时,但记不住确切的值。

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

代码语言:javascript
复制
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;

}

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

代码语言:javascript
复制
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的信号是长度为50微秒的恒定Up/Down (应该如此,因此设置波特率也不成问题)。

那么,我的“接收”代码中有没有什么地方我做错了呢?“select”是错误的吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-09-21 21:14:30

  1. 您是否也在Windows端设置了适当的波特率?
  2. 使用示波器检查导线上的实际数据。调试串行通信就是发明示波器的目的。差不多了。:)
票数 5
EN

Stack Overflow用户

发布于 2012-10-26 01:32:42

你的read函数很容易爆炸。如果您在缓冲区的末尾附近,并且您读取的数量超过了填充缓冲区的数量,则将复制到缓冲区的末尾,从而覆盖堆栈。

在Linux发送端,您应该查看"raw mode",例如cfmakeraw()。这样,您就不会被系统“帮助”您所困扰(就像当您发送换行符时添加CR --真的搞砸了二进制数据……)。在微软中有一种方法可以做到这一点,但我忘记了是如何做到的。

票数 0
EN

Stack Overflow用户

发布于 2013-05-15 20:51:39

在Windows端,您是否在DCB结构中设置了DCBLength字段?

代码语言:javascript
复制
dcb.DCBlength = sizeof(dcb);
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12531289

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档