質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.91%

UARTの受信を文字列で出来るようにしたい

解決済

回答 5

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 490

Merrifield

score 18

UARTの受信を文字列で出来るようにしたいと考えています。4文字で送信される文字列を、受信側で一文字ずつ受け取り、それを4要素の配列に格納できるようなプログラムを考えているのですが、上手くいきません。送信側から、「R021」のように頭文字プラス三桁の数字が送られてきて、これをreceive[0] = R、receive[1] = 0、receive[1] = 2、receive[2] = 1、のように格納したいのですが、receive[0]に数字が格納されたり、ずれが起きてしまいます。下にプログラムを示すので、どこを修正すればよいのか教えていただけないでしょうか?

判定部分を修正したものを一番下に2つ示します。これも上手くいかなかったのですが、どこがおかしいのか教えていただけないでしょうか?

受信側: 開発環境HEW C言語  
送信側: visual studio2019 windows フォームアプリケーション(.NET Framework) 
マイコン R8C/33T
UART-シリアル変換モジュールはFT232RL

受信側

void main(void)
{    
    volatile int i,j,k;
    unsigned char c;
    int numericalR = 0;
    int numericalG = 0;
    int numericalB = 0;
    int ret;
    char receive[4];

    init_PWM();             //PWM初期設定(赤、青)
    timer_rb_init();        //タイマrb初期設定(緑)
    clock_initial();        //クロック初期設定
    UART_init();            //UART初期設定




    while(1)
   {


          for(k = 0; k < 4; k++)
          { 

            while( UART_get( &c ) );  // 受信するまで待つ

            ret = UART_get( &c );         // 受信文字があればcに代入

            if(ret == 1)
            {
                receive[k] = c;
                ret = 0;
            }
          }


              switch(receive[0])
              { 

                 case 'R':


                          numericalR = 100 * (receive[1] - '0') + 10 * (receive[2] - '0') + (receive[3] - '0'); //数値に変換
                          PWM_red( numericalR );  //デューティー比変更(赤)

                          break;



                case 'G':


                         numericalG =  100 * (receive[1] - '0') + 10 * (receive[2] - '0') + (receive[3] - '0'); //数値に変換
                         PWM_green( numericalG );  //デューティー比変更(緑)

                          break;




                case 'B':


                        numericalB =  100 * (receive[1] - '0') + 10 * (receive[2] - '0') + (receive[3] - '0'); //数値に変換
                        PWM_blue( numericalB );  //デューティー比変更(青)

                        break;


              }  

UART 一文字受信関数

int UART_get( char *s )
{
      int ret = 0, data;
    char c;

    if (ri_u0c1 == 1)          //受信データあり? 
    {  
         data = u0rb; 
         *s = (char)data;      //bit7~bit0を読み込む 
         ret = 1; 

         if( data & 0xf000 )   // エラーあり?
          {
               /* エラー時は再設定 */

               re_u0c1 = 0;    //受信禁止
               te_u0c1 = 0;    //送信禁止
               c = u0mr;
               u0mr &= 0xf8;   //u0mr = u0mr & 0xf8  (シリアルインタフェース無効)
               u0mr = c;       //UARTモード転送データ長8ビット???
               re_u0c1 = 1;    //受信許可
               te_u0c1 = 1;    //送信許可
               ret = -1;
            }
    }
    return ret;
}

送信側 一部

 private void trackBar1_ValueChanged(object sender, EventArgs e)
        {
            // TrackBar1の値が変更されたらラベルに表示
            label1.Text = trackBar1.Value.ToString();



            //TrackBarの値を文字列に変換(  
            StringR = "R" + trackBar1.Value.ToString("D3");



            //送信
            mySerialPort.Write(StringR);

        }

修正版

``
do
        {
              do
              {
                    do
                    {
                          do
                          {
                               while(  UART_get( &c )  );  // 受信するまで待つ 

                          }while(!((c == 'R') || (c == 'G') || (c == 'B')) ); 


                          receive[0] = c;  
                          while(  UART_get( &c )  );  // 受信するまで待つ


                    }while(!((c != 'R') && (c != 'G') && (c != 'B')));


                    receive[1] = c;
                      while(  UART_get( &c )  );  // 受信するまで待つ


              }while(!((c != 'R') && (c != 'G') && (c != 'B')));


              receive[2] = c;
              while(  UART_get( &c )  );  // 受信するまで待つ


         }while(!((c != 'R') && (c != 'G') && (c != 'B')));


         receive[3] = c;
while(1)
        {
            while(  UART_get( &a )  );  // 受信するまで待つ

            if( a != 'R'  &&  a != 'G'  &&  a != 'B' ) continue;     //aがR、G、Bのどれかでなければ


            while(  UART_get( &b )  );  // 受信するまで待つ

            if( b == 'R'  ||  b == 'G'  ||  b == 'B' ) continue;     //bがR、G、Bのどれかであれば


            while(  UART_get( &c )  );  // 受信するまで待つ


            if( c == 'R'  ||  c == 'G'  ||  c == 'B' ) continue;     //cがR、G、Bのどれかであれば


            while(  UART_get( &d )  );  // 受信するまで待つ

            if( d == 'R'  ||  d == 'G'  ||  d == 'B' ) continue;     //dがR、G、Bのどれかであれば


            break;
        }

       receive[0] = a;
            receive[1] = b;
            receive[2] = c;
            receive[3] = d;
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • fuzzball

    2020/07/16 11:11

    「受信するまで待つ」で受信した文字を捨てちゃってません?

    キャンセル

  • Merrifield

    2020/07/16 11:13

    while( UART_get( &c ) ); // 受信するまで待つ
    ret = UART_get( &c ); // 受信文字があればcに代入

    この2文のどちらか必要ないということでしょうか?

    キャンセル

  • fuzzball

    2020/07/16 11:31 編集

    入れ違いになっちゃってたのでepistemeさん(の回答)におまかせします。

    キャンセル

回答 5

+2

なんかよくわかんないコードです...

  for(k = 0; k < 4; k++)
  { 
     while( UART_get( &c ) );  // ここで受信した文字は捨てちゃうの? だったらズレるのも無理はない
     ret = UART_get( &c ); 
     if(ret == 1)
     {
        receive[k] = c;
        ret = 0;
     }
   }

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/16 11:12

    >> // ここで受信した文字は捨てちゃうの? だったらズレるのも無理はない
    これはどういう意味でしょうか?そのwhileの部分は、必要ないですか?

    キャンセル

  • 2020/07/16 11:14

    ぃゃぃゃぃゃ、受信出来たらcにその受信文字が入ってるんでしょ? それ使ってないやん。空読みしてるやん。

    キャンセル

  • 2020/07/16 11:16

    while( UART_get( &c ) );
    ret = UART_get( &c );

    一文目と二文目のcの値が違っているということですか?

    キャンセル

  • 2020/07/16 11:17 編集

    当然違うんちゃいます? UART_get を複数回呼んだんだから。

    キャンセル

+1

シリアル通信の通信の単位は1バイト、それ以上でも以下でもありません。送信側で4文字まとめて送った、などという情報は通信では伝わらず、単に文字の連続(ストリーム)としてしか扱われません。なので、単純に4文字を受信していると、なにかの拍子に1文字取りこぼしたりすると以降は'123R'として受信してしまうようになるかも知れません。

この場合であれば、

'R''G''B'のいずれかを受信するまで待ち、
受信したら次の文字を受信、これが数字であれば保持、'R''G''B'のいずれかであれば数字の受信開始に戻って、
次の文字を受信、これが数字(2文字目)であれば保持、'R''G''B'であれば数字の受信開始に戻って
次の文字を受信、これが数字(3文字目)であれば保持、'R''G''B'であれば数字の受信開始に戻って
3文字受信できたら数値に変換して以降の処理に渡す

というような構造にしたほうがよろしいかと思います。

通信は、特に手を打っていない限りは何らかのエラーが起こることを想定してプログラムを組まなければいけません。


上記の「通り」に作ったらこんなところでしょうか。まぁ、普通コレ見たらムズムズしますよね。
あと、これはRGBと数字以外の文字を受信しちゃった、という事故に対しては無防備です。

  char c;
  //'R''G''B'のいずれかを受信するまで待ち
  do {
    while (UART_get(&c))
      ;
    receive[0] = c;
  } while (!(c == 'R' || c == 'G' || c == 'B'));
  while (1) {
    //次の文字を受信
    while (UART_get(&c))
      ;
    if (isdigit(c)) {  // isdigit()が使えない環境なら適宜対応
      //これが数字であれば保持
      receive[1] = c;
    } else if (c == 'R' || c == 'G' || c == 'B') {
      //'R''G''B'のいずれかであれば数字の受信開始に戻って
      receive[0] = c;
      continue;  //事実上のgoto。この働きでwhile(1)の直後に戻る
    }
    //次の文字を受信
    while (UART_get(&c))
      ;
    if (isdigit(c)) {
      //これが数字であれば保持(2文字目)
      receive[2] = c;
    } else if (c == 'R' || c == 'G' || c == 'B') {
      //'R''G''B'のいずれかであれば数字の受信開始に戻って
      receive[0] = c;
      continue;
    }
    //次の文字を受信
    while (UART_get(&c))
      ;
    if (isdigit(c)) {
      //これが数字であれば保持(3文字目)
      receive[3] = c;
    } else if (c == 'R' || c == 'G' || c == 'B') {
      //'R''G''B'のいずれかであれば数字の受信開始に戻って
      receive[0] = c;
      continue;
    }
    break;  //ここにたどり着いたということは数字3文字受信したということ
  }

あまりムズムズしないようにしてみました。

  int n = 0;
  char c;
  int err = 0;//エラーがあると0以外になる
  do {
    while (UART_get(&c)) ; // 1文字受信
    if (c == 'R' || c == 'G' || c == 'B') {
      if(n!=0){
        err = 1;
      }
      receive[0] = c;
      n = 1;  // RGBを受信したらシーケンス開始
    } else {
      if (n > 0 && isdigit(c)) {  // isdigit()が使えない環境なら適宜数字の判断を作成
        receive[n++] = c;
      } else {  //無効な文字を受信したので全面やり直し
        err = 2;
        n = 0;
      }
    }
  } while (n < 4);

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/20 15:02 編集

    thkanaさん
    連続ですみません。下の書き方で上手く出来ました。いろいろアドバイスを頂き、ありがとうございました。
    while(1)
    {


    while( 1 )
    {
    ret = UART_get( &c );
    if( ret == 1 )
    {


    receive[i]=c;

    i++;

    if(i > 3)i=0;



    }
    }

    キャンセル

  • 2020/07/20 20:57

    それでうまくいったというのならいいのですけれど...
    どこかで例えばノイズでエラーで一文字とりこぼして
    'R001G02B003R004'
    となった時にどういう結果が得られるでしょう。
    > RGBと数字以外の文字を受信しちゃった
    というのは、
    'R001G02aB003'
    とかいうデータを受信してしまったときにどう振る舞うか、という話です。プログラムの目的によって対応はいろいろかと思いますが。
    笑ってリセットかければいい、ということならそれはそれですけど。

    キャンセル

  • 2020/07/20 22:11

    今回は、課題みたいな感じでやっていたことなので、あまり細かいことはところまで求められていないと思いますが、ノイズ等のエラーが起きたときや文字の格納がずれたときの処理までは出来ていないですね。これからのことも考えて、thkanaさんから頂いたコメントは参考にさせていただきます。

    キャンセル

0

文字列として受信したいなら(というより複数バイトのデータを読みたいなら)、終端データを決めて、それを区切りに送信するようにしましょう
文字列であれば、改行コードを送ってそれを区切りにすればいいです

#C#で送信するなら、1行送信(Writeline)すればいいだけですね

受信側では、受信文字を貯めていって、改行コードが来れば、今まで貯めた受信データを文字列としてあとの処理に回すようにすればいいです

int rxcnt=0;
char rxbff[32];

int rxstr(void)
{
  int ch=UART_get();
  if(ch==0) return 0;
  if(rxcnt>=(32-1)) rxcnt=0;
  rxbff[rxcnt]='\0';
  if(ch=='\n'){
     rxcnt=0;
     return 1;
  }
  rxbff[rxcnt++]=ch;
  rxbff[rxcnt]='\0';
  return 0;
}

int main(void)
{
  ...
  while(1){
    if(rxstr()){
       rxbffに受信文字列が入ってる
    }
    ....
}

これでわかるかな?

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/17 17:27

    まあ、受信文字を返す関数、と想定してます。
    受信文字がない場合は0を返してください。

    別の名前の関数でしたほうがよかったでしたかw

    キャンセル

  • 2020/07/17 17:31

    受信文字が0だとややこしいですよね。まあその部分は、自分で考えます。

    キャンセル

  • 2020/07/17 17:34

    まあ、あくまでもサンプルコードということで。
    動作確認もしてませんし、テキトーに修正しましょう

    キャンセル

0

※未検証

char バッファ[4];
int バッファに何文字書いたか;

int main(){
    while(1){
        受信();
        // 必要ならdelayいれたり他の処理をする
    }
}

void 受信(){
    char 受信文字;
    if(受信しようと試みたけど受信できなかった)
        return;

    if(受信文字がR,G,Bのどれかだった場合){
        バッファの先頭に受信文字を代入 // バッファ[0] = 受信文字
        return;
    }

    if(バッファが空で、受信文字がR,G,B以外だった場合){
        return;
    }

    if(バッファに何かしらデータが入っていて、受信文字が数字だった場合){
        バッファの末尾に受信文字を代入  // バッファ[バッファに何文字書いたか++] = 受信文字
        if(バッファが4文字){
            バッファの内容に応じた処理をする
            バッファをクリア // バッファに何文字書いたか = 0
        }
    }
}

ちなみに改行区切りの場合は受信()の中身がこんな感じ

if(受信しようと試みたけど受信できなかった)
    return;

if(受信文字が改行だった){
    バッファの内容に応じた処理をする // 決められたフォーマット通りに受信できているかの判定も必要
    バッファをクリア
}else{
    バッファの末尾に受信文字を追加
}

// 他にもバッファとして用意した配列の長さを超えても改行が来なかった場合の対処とかも必要

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/17 11:04

    ありがとうございます

    キャンセル

  • 2020/07/17 11:46

    バッファが空の場合というのは、バッファ[0]=0ということでしょうか?それだと、0が送られてきた場合と被りそうですが。

    キャンセル

  • 2020/07/17 12:48 編集

    そのために「バッファに何文字書いたか」(より正確に言えばバッファの先頭何文字が有効データか)という変数を置いて管理します

    キャンセル

check解決した方法

-3

これで上手くいきました。ご回答くださった皆さんありがとうございました。

 while(1)
 {


        while( 1 ) 
        {
          ret = UART_get( &c );
          if( ret == 1 )
            {


           receive[i]=c;

           i++;

           if(i > 3)i=0;



        }
    }

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.91%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る