6

私の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 48FINISH
  • PIC が「23456\n」を送信すると、出力は次のようになります。 Read: 6 START-14 51 52 53 54FINISH
  • PIC が「34567\n」を送信すると、出力は次のようになります。 Read: 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 設定によって最初の文字がめちゃくちゃになっています。Cutecom を実行すると、上記と同じテスト入力が正確に返されるため、termios 設定である必要があります。マニュアルページを何度も読み、入力コントロールのさまざまな設定をすべて試しましたが、何をしてもこの問題を解決することはできません。

簡単に修正するには、データを 1 文字シフトするだけですが、これは避けたいと考えています。

誰かがそのような問題を経験したことがありますか、またはそれについて何をすべきか考えていますか?

どうもありがとう。

28/3/13 素晴らしい提案オースティン。ここに興味がある人のために、2 つの出力があります。

  • 最初は私のプログラムの termios 設定です

    速度 38400 ボー; 行 0; 列 0; 行 = 0; イントラ = ; 終了 = ; 消去 = ; 殺す=; eof = ; eol = ; eol2 = ; スイッチ = ; 開始 = ; 停止 = ; サスペンド = ; rprnt = ; ワーセ = ; 次の = ; フラッシュ = ; 分 = 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 -ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0 -isig icanon -iexten -echo -echo -echok -echonl -noflsh -xcase -tostop -echort -echoctl -echoke

  • そして、キュートコムが使用する設定

    速度 38400 ボー; 行 0; 列 0; 行 = 0; intr = ^C; 終了 = ^\; 消去 = ^?; 殺す = ^U; eof = ^D; eol = ; eol2 = ; スイッチ = ; 開始 = ^Q; 停止 = ^S; Susp = ^Z; rprnt = ^R; ウェラセ = ^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; イントラ = ^?; 終了 = ^\; 消去 = ^H; 殺す = ^U; eof = ^D; eol = ; eol2 = ; スイッチ = ; 開始 = ^Q; 停止 = ^S; Susp = ^Z; rprnt = ^R; ウェラセ = ^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
    }
    

何ができるのか、ここからどこに持っていくべきなのか、私は完全に迷っています。

4

2 に答える 2

2

コードをすばやくスキャンしても、明らかに問題があることはわかりません。unsigned char buf[]8 ビット値を使用する予定がある場合は、への移行を検討することをお勧めします。

Cutecom で動作するプログラムがあるので、termios 設定を参照して独自のプログラムをデバッグできます。

で Cutecom を実行している状態で/dev/ttyUSB0、別の端末で次のコマンドを実行して、tty 設定をダンプします。

stty -a -F /dev/ttyUSB0

プログラムを実行するときに同じことを行い、2 つの構成の違いを探します。プログラムの端末設定を、Cutecom で報告されたものと正確に一致するように設定してみてください。

アップデート:

termios 設定を修正しても問題が解決されないため、さらにいくつか試してみてください。どこかにタイミングの問題があると思います。Cutecom コンソールで入力するときは、一度に 1 文字ずつデバイスに送信し、文字間隔は数ミリ秒です。プログラムを使用する場合、コマンドを入力した後、文字の完全なバッファが送信されます。文字は、ドライバーが許可する限りの速さで連続して送信されます。PIC プログラムがデータ ストリームのタイミングを処理できないか、たとえば 1 つではなく 2 つのストップ ビットを予期して、奇妙なリターン コードが返される可能性があります。

おそらく、開始するのに最適な場所は、ソースに戻ることです。オシロスコープまたはロジック アナライザーを入手して、PIC から送信されたデータが実際に正しいことを確認します。スタート ビットとストップ ビットを考慮して、ビット レベルの波形を理解する必要があります。Cutecom とプログラムの波形を比較します。ロジック アナライザを使用する場合は、使用するクロックがボー レートの高い倍数であることを確認してください。たとえば、32 乗数。

デバッグするもう 1 つの方法は、 を使用straceして、ドライバーによって返された文字が実際には正しくないことを確認することであり、それはプログラムの問題ではありません。を使用straceすると、プログラムの生の読み取り/書き込みと、カーネルから返されたものを確認できます。strace -o ~/tmp/strace_output.txt -ttt -xx your_programプログラムの実行中にすべてのシステム コールをダンプするために使用します。場合によっては、プログラムをトレースするプロセスだけで、タイミング エラーが表示されるほど遅くなることがあります。読み取り/書き込みのタイミングをstraceCutecom と比較できます。write()テストのために、文字列を送信するが各文字間で少し遅延する独自の関数を追加できます。

于 2013-03-28T02:37:36.147 に答える
0

私はついにそれを解決しました。これはそれを修正したものです:

  • (無効) 書き込み (tty_fd、buf2、1);

問題は修正されましたが、なぜそれが何をしていたのかについては 100% 確信が持てません。問題は、マイクロ コントローラーの書き込みに \n を追加する私のプログラムでした。Cutecom と私のプログラムの両方で strace を実行したところ、私のプログラムは「1\n」と書き込みますが、Cutecom は「1」のみと書き込みます。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 ボックスとマイクロコントローラーのインターフェースを開始したい人にとっては、上記のコードが参考になるかもしれません。

于 2013-03-31T01:24:01.843 に答える