繁体   English   中英

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

[英]Linux termios modifying first character after serial port read()

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

  • 微控制器(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 55结束
  • 当PIC发送“ 45678 \\ n”时,输出为:读取:6 START-12 53 54 55 56FINISH
  • 当PIC发送“ 56789 \\ n”时,输出为:读取:6 START-12 54 55 56 57FINISH

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

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

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

非常感谢。

2013年3月28日奥斯汀的好建议。 对于那些感兴趣的人,这里有两个输出:

  • 首先是我程序中的termios设置

    速度38400波特; 第0行; 列0; 行= 0; intr =; 退出=; 擦除=; 杀=; eof =; eol =; eol2 =; swtch =; 开始=; 停止=; susp =; rprnt =; werase =; 下一个=; 冲洗=; min = 0; 时间= 0; -parenb -parodd cs8 -hupcl -cstopb cread clocal -crtscts ignbrk -brkint ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon ixoff -iuclc -ixany -imaxbel -iutf8 -opost -olcuc -ocrnonlon -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 cread 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 cread 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转储所有系统调用。 有时,仅在编制程序的过程中,它的速度就会降低,足以显示时序错误。 你可以用比较的读/写操作的时间strace Cutecom的。 仅出于测试目的,您可以添加自己的write()函数,该函数发送一个字符串,但每个字符之间的延迟很小。

我终于解决了。 这是固定的原因:

  • (void)写(tty_fd,buf2,1);

问题是固定的,但不能100%地确定其原因。 问题是我的程序在微控制器写操作后附加了\\ n。 当我同时对Cutecom和我的程序进行跟踪时,我发现Cutecom只写“ 1”,而我的程序写“ 1 \\ n”。 我只是觉得不够彻底,因为在使用Cutecom发送字符时,您需要在用户提示中键入例如1,然后按Enter。 在PIC方面,我的程序如下所示:

while(1)
{
    WATCHDOG();

    if(flag == 1)
    {
        char *start = str2;
        RS485_TXEN1();
        indicator = UART1_getch(); //get character sent from PC
        switch(indicator)
        {
            case '1' :                       
                    UART1_putstr("00000\n");
                    DAC_Write( DAC_CH_2, 4095);
                    break;
            case '2' :                      
                    UART1_putstr("23456\n");
                    DAC_Write( DAC_CH_2, 0);
                    break;
            case '3' :
                    UART1_putstr("34567\n");
                    break;
            case '4' :                       
                    UART1_putstr("45678\n");
                    break;
            case '\n' :
                    UART1_putch('\n');
                    break;
            default  :         
                    UART1_putstr("56789\n");                  
                    break;
        }
        RS485_RXEN1();
        flag = 0;
    }
} 

当字符到达UART RX时,将产生一个中断。 我在此中断服务程序中设置“标志”,然后在main()中服务接收到的命令。 不知道第一个字符是如何被修改的,但是由于“ case'\\ n':”,看起来好像发生了一些覆盖或写入中断。

最终进行了简单的修复,甚至还学习了一些有关使用Linux系统和调试的宝贵经验。 感谢所有提供建议的人。 对于任何想开始与Linux盒子和微控制器接口的人,上面的代码可能对您入门很有帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM