串行编程RS485

ATa*_*lor 10 c c++ serial-communication rs485 baud-rate

我的任务是通过RS485 2线系统实现ModBus协议.(实际上它是三根线,A/B和GND).虽然ModBus不是重点,但是之前的步骤......界面上的简单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";
Run Code Online (Sandbox Code Playgroud)

接下来,我配置端口.

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);
Run Code Online (Sandbox Code Playgroud)

奇偶校验和流量控制目前尚未规划,因为最终结果将连接到低级别的电路板,我需要自己处理信号.此外,没有任何电线,这将允许"不受约束的通信".(毕竟我不希望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);
Run Code Online (Sandbox Code Playgroud)

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

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

同样,所有这些功能都完美运行.

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

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

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

当我在设置我真正想要的选项之前将termios结构的所有成员设置为0时,我可以让Linux主机工作.我猜,这是因为控制字符...但是如果我这样做,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
Run Code Online (Sandbox Code Playgroud)

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

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

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;
Run Code Online (Sandbox Code Playgroud)

}

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

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;
Run Code Online (Sandbox Code Playgroud)

}

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

0x55的信号在50微秒的长度上是一个常数Up/Down(应该如此,所以设置波特率也没问题).

那么我的'接收'代码中有什么东西我做错了吗?'选择'错了吗?

unw*_*ind 5

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

  • @ATaylor我也不是,我认为自己是一个99%的软件人.但我仍然认为示波器是嵌入式编程的必备工具. (3认同)
  • @ATaylor在进行此类与硬件相关的编程时,您真的非常需要使用示波器。因为现在你不知道是Linux端、Windows端还是硬件出了问题。如果您可以测量离开 Linux 机器的具有正确波特率的正确 UART 数据信号,则问题出在接收器中,否则问题出在发送器或硬件中。你不能因为 Linux 版本可以“与自己对话”而假设它是正确的,因为这样你就可以将波特率和数据格式设置为任何值,它仍然可以工作。 (2认同)