c - Linux termios在串行端口read()之后修改第一个字符

标签 c linux serial-port termios

我的termios设置是使用read()修改从串行端口读取的第一个字符。我有一个与Linux盒子通讯的微 Controller 。微 Controller 响应从linux机器发送的命令。设置如下:

  • 微 Controller (PIC24F)RS485端口<-> RS485转USB转换器<-> Ubuntu PC。

  • 当我运行类似Cutecom的终端程序时,一切都会按计划进行。我向PIC发送了一个命令字符,但是得到了响应,但是当我使用命令行程序时,第一个字符已被修改。这是我的代码:
    #include <string.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <termios.h>
    
    #define DEVICE "/dev/ttyUSB0"
    #define SPEED B38400 
    
    int main()
    {
        struct termios tio; //to hold serial port settings
        struct termios stdio; //so we can accept user input
        struct termios old_stdio; //save the current port settings
        int tty_fd; //file descriptor for serial port
        int res, n, res2, read1, wri;
        char buf[255];
        char buf2[255]; 
    
        //save the current port settings
        tcgetattr(STDOUT_FILENO,&old_stdio); 
    
        //setup serial port settings
        bzero(&tio, sizeof(tio));
        tio.c_iflag = 0;
        tio.c_iflag = IGNPAR | IGNBRK | IXOFF;
        tio.c_oflag = 0;
        tio.c_cflag = CS8 | CREAD | CLOCAL; //8n1 see termios.h 
        tio.c_lflag = ICANON;
    
        //open the serial port
        tty_fd=open(DEVICE, O_RDWR | O_NOCTTY); 
    
        //set the serial port speed to SPEED
        cfsetospeed(&tio,SPEED); 
    
        //apply to the serial port the settings made above
        tcsetattr(tty_fd,TCSANOW,&tio); 
    
        for(n = 5; n > 0; n--)
        {
        printf("Please enter a command: ");
        (void)fgets(buf2, 255, stdin);
        (void)write(tty_fd, buf2, strlen(buf2));                   
        printf("Ok. Waiting for reply.");
        res = read(tty_fd, buf, 255);       
        printf("Read:%d START%d %d %d %d %dFINISH\n",res,buf[0],buf[1],buf[2],buf[3],
        buf[4]);              
        }
    
        //close the serial port 
        close(tty_fd); 
    
        //restore the original port settings
        tcsetattr(STDOUT_FILENO,TCSANOW,&old_stdio); 
    
        return EXIT_SUCCESS; 
    }
    

    这是我得到的结果的一个例子。
  • 当PIC发送“00000\n”时,输出为:读取:6 START-16 48 48 48 48 FINISH
  • 当PIC发送“23456\n”时,输出为:读取:6 START-14 51 52 53 54FINISH
  • 当PIC发送“34567\n”时,输出为:读取:6 START-14 52 53 54 55FINISH
  • 当PIC发送“45678\n”时,输出为:读取:6 START-12 53 54 55 56FINISH
  • 当PIC发送“56789\n”时,输出为:读取:6 START-12 54 55 56 57FINISH

  • 由于某些原因,第一个字符被某些termios设置弄乱了。它必须是termios设置,因为当我运行Cutecom时,将完全返回上述相同的测试输入。我已经一遍又一遍地阅读了手册页,尝试对输入控件进行所有不同的设置,但是无论我做什么都不能动摇这个问题。

    为了轻松解决问题,我只能将数据跨1个字符移动,但要避免这样做。

    有没有人遇到过这样的问题,或者不知道该怎么办?

    非常感谢。

    13年3月28日
    奥斯丁的好建议。对于那些感兴趣的人,这里有两个输出:
  • 首先是我程序中的termios设置

    速度38400波特;第0行;第0列;行= 0;
    intr =;退出=;擦除=;杀死=; eof =;
    eol =; eol2 =; swtch =;开始=;停止=;
    susp =; rprnt =; werase =;下一个=;
    冲洗=;最小值= 0;时间= 0;
    -parenb -parodd cs8 -hupcl -cstopb读取clocal -crtscts
    ignbrk -brkint ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon ixoff
    -iuclc -ixany -imaxbel -iutf8
    -opost -olcuc -ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
    -isig icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt
    -echoctl -echoke
  • 和cutecom使用的设置

    速度38400波特;第0行;第0列;行= 0;
    intr = ^ C;退出= ^\;擦除= ^ ?;杀= ^ U; eof = ^ D; eol =;
    eol2 =; swtch =;开始= ^ Q;停止= ^ S; susp = ^ Z; rprnt = ^ R;
    werase = ^ W; lnext = ^ V;冲洗= ^ O;最小值= 60;时间= 1;
    -parenb -parodd cs8 hupcl -cstopb读取clocal -crtscts
    ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff
    -iuclc -ixany -imaxbel -iutf8
    -opost -olcuc -ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
    -isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt
    -echoctl -echoke

  • 我仍在进行所有操作,并且在取得进展时会更新该帖子。

    29/3/13
    仍然有同样的问题。我什至找到了Cutecom的源代码,并遵循了它们使用的termios设置。问题仍然存在。该第一个字符已损坏!!!
  • 这是我程序中的Termios设置。由于某些原因,无法设置刷新。

    速度38400波特;第0行;第0列;行= 0;
    intr = ^ ?;退出= ^\;擦除= ^ H;杀= ^ U; eof = ^ D; eol =;
    eol2 =; swtch =;开始= ^ Q;停止= ^ S; susp = ^ Z; rprnt = ^ R;
    werase = ^ W; lnext = ^ V;冲洗=;最小值= 60;时间= 1;
    -parenb -parodd cs8 hupcl -cstopb读取clocal -crtscts
    ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff
    -iuclc -ixany -imaxbel -iutf8
    -opost -olcuc -ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
    -isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt
    -echoctl -echoke
  • 和我的新代码:
    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <termios.h>
    #include <sys/ioctl.h>
    
    #define DEVICE "/dev/ttyUSB0"
    #define SPEED B38400 
    
    int main()
    {
    struct termios tio; //to hold serial port settings
    struct termios stdio; //so we can accept user input
        struct termios old_stdio; //save the current port settings
        int tty_fd; //file descriptor for serial port
        int retval, res, n, res2, read1, wri;
        char buf[255];
        char buf2[255]; 
    
    
        tty_fd = open(DEVICE, O_RDWR | O_NDELAY);
        if(tty_fd < 0)
        {
            perror(DEVICE);
            exit(-1);
        }
        printf("Init 1 complete.\n");
    
        tcflush(tty_fd, TCIOFLUSH);
    
        int f = fcntl(tty_fd, F_GETFL, 0);
        fcntl(tty_fd, F_SETFL, f & ~O_NDELAY);
    
        retval = tcgetattr(tty_fd, &old_stdio);
        if(retval != 0)
        {
            perror(DEVICE);
            exit(-1);
        }
        printf("Init 2 complete.\n");
    
        struct termios newtio;
        retval = tcgetattr(tty_fd, &newtio);
        if(retval != 0)
        {
            perror(DEVICE);
            exit(-1);
        }
        printf("Init 3 complete.\n");
    
        cfsetospeed(&newtio, SPEED);
        cfsetispeed(&newtio, SPEED);
    
        newtio.c_cflag = (newtio.c_cflag & ~CSIZE) | CS8;
        newtio.c_cflag |= CLOCAL | CREAD;
        newtio.c_cflag &= ~(PARENB | PARODD);
        newtio.c_cflag &= ~CRTSCTS;
        newtio.c_cflag &= ~CSTOPB;
    
        newtio.c_iflag = IGNBRK;
        newtio.c_iflag &= ~(IXON | IXOFF | IXANY);
    
        newtio.c_lflag = 0;
    
        newtio.c_oflag = 0;
    
        newtio.c_cc[VTIME] = 1;
        newtio.c_cc[VMIN] = 60;
        newtio.c_cc[VINTR] = 127; 
        newtio.c_cc[VQUIT] = 28;
        newtio.c_cc[VERASE] = 8;
        newtio.c_cc[VKILL] =  21;
        newtio.c_cc[VEOF] = 4;
        newtio.c_cc[VSTOP] = 19;
        newtio.c_cc[VSTART] = 17;
        newtio.c_cc[VSUSP] = 26;
        newtio.c_cc[VREPRINT] = 18;
        newtio.c_cc[VFLSH] = 15;
        newtio.c_cc[VWERASE] = 23;
        newtio.c_cc[VLNEXT] = 22;
    
    
        retval = tcsetattr(tty_fd, TCSANOW, &newtio);
        if(retval != 0)
        {
            perror(DEVICE);
            exit(-1);
        }
        printf("Init 4 complete.\n");
    
        int mcs = 0;
        ioctl(tty_fd, TIOCMGET, &mcs);
        mcs |= TIOCM_RTS;
        ioctl(tty_fd, TIOCMSET, &mcs);
    
        retval = tcgetattr(tty_fd, &newtio);
        if(retval != 0)
        {
            perror(DEVICE);
            exit(-1);
        }
        printf("Init 5 complete.\n");
    
        newtio.c_cflag &= ~CRTSCTS;
    
        retval = tcsetattr(tty_fd, TCSANOW, &newtio);
        if(retval != 0)
        {
            perror(DEVICE);
            exit(-1);
        }
        printf("Init 6 complete.\n");
    
    
        for(n = 5; n > 0; n--)
        {
        printf("Please enter a command: ");
        (void)fgets(buf2, 255, stdin);
        (void)write(tty_fd, buf2, strlen(buf2));
        printf("Ok. Waiting for reply\n");
        res = read(tty_fd, buf, 255);       
        printf("Read:%d START%d %d %d %d %dFINISH\n",res,buf[0],buf[1],buf[2], buf[3],
        buf[4]);              
        }
    
        //restore the original port settings
        tcsetattr(tty_fd, TCSANOW, &old_stdio); 
    
        close(tty_fd);
    
        return EXIT_SUCCESS; //return all good
    }
    

  • 我完全不知道该怎么办,或者我应该从这里拿走它。

    最佳答案

    快速浏览您的代码,我看不出任何明显错误的地方。如果您希望使用8位值,则可能要考虑移至unsigned char buf[]

    由于您在Cutecom中有一个可运行的程序,因此您可以使用它们的termios设置作为调试您自己的程序的引用。

    当Cutecom在/dev/ttyUSB0上运行时,在另一个终端上运行以下命令以转储tty设置:

    stty -a -F /dev/ttyUSB0
    

    运行程序时执行相同的操作,并查找两种配置之间的差异。尝试在程序中设置终端设置,使其与为Cutecom报告的设置完全匹配。

    更新:

    由于修复termios设置仍无法解决问题,因此,请尝试以下其他操作。我可能会猜测某个地方存在计时问题。在Cutecom控制台上打字时,您一次要向设备发送一个字符,每个字符之间的间隔为毫秒。使用程序时,输入命令后将发送完整的字符缓冲区,并且字符将以驱动程序允许的最快速度背对背发送。也许您的PIC程序无法处理数据流的时序,或者期望例如两个停止位而不是一个停止位,从而导致一些奇怪的返回码。

    最好的起点可能是从源头开始。掌握示波器或逻辑分析仪,并验证PIC发送的数据实际上是正确的。您将必须了解位电平的波形,并考虑起始位和停止位。比较Cutecom和您的程序的波形。如果使用逻辑分析仪,请确保使用的时钟是波特率的高倍数。例如32乘法器。

    调试的另一种方法是使用strace来验证驱动程序返回的字符实际上是不正确的,并且这不是程序的问题。使用strace,您将能够看到程序的原始读/写以及内核返回的内容。在程序运行时,使用strace -o ~/tmp/strace_output.txt -ttt -xx your_program转储所有系统调用。有时,仅在编制程序的过程中,它的运行速度会降低,足以显示时序错误。您可以将读取/写入的时间与Cutecom的strace进行比较。仅出于测试目的,您可以添加自己的write()函数,该函数发送一个字符串,但在每个字符之间延迟一小段时间。

    关于c - Linux termios在串行端口read()之后修改第一个字符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15672751/

    相关文章:

    linux - "One of Hyper-V components is not running",Windows Server 2012 中的 Linux?

    c++ - C++ 中的串行端口 (RS -232) 连接

    c - IN 在 Windows 设备驱动函数中是什么意思?

    c - Mac 上的 scanf() 问题

    c - 了解 fork() 和 wait() 如何协同工作

    linux - 如何在 menuconfig 中反向查找 linux 内核配置选项

    linux - Linux下串口通信ttyUSBX

    serial-port - 可靠的全双工串行通信

    c - libspotify 在调用 sp_session_create 后因 segFault 而崩溃

    c++ - Eclipse Cygwin,在 PATH 中找不到程序 "\xlC"