ページ 11

シリアル通信

Posted: 2015年12月04日(金) 16:29
by @picマイコン
PICマイコンとPC間でシリアル通信を試みています。

PIC18F2520 + C18環境です。
PCとPIC間でシリアル通信を行うプログラムについて。

PCより、RGBのデータが#xxyyzzという形式で送信されます。
xx: Redの8bitデータ (00~FF)
yy: Greenの8bitデータ (00~FF)
zz: Blueの8bitデータ (00~FF)

データが正しく送信されていることは確認済です。

このデータをPICマイコンで受信して、それぞれred,green,blueの変数に格納する方法はどうすれば良いのでしょうか?

送られてきたデータを色ごと(red/green/blue)に分割して格納する方法が分かりません。教えてください。

Re: シリアル通信

Posted: 2015年12月04日(金) 17:20
by みけCAT
@picマイコン さんが書きました:このデータをPICマイコンで受信して、それぞれred,green,blueの変数に格納する方法はどうすれば良いのでしょうか?
  1. 適切なプログラムを書く
  2. 書いたプログラムをコンパイルする
  3. それをマイコンに書き込む
  4. PCとマイコンを適切に接続する
  5. マイコンを起動して書き込んだプログラムを実行させる
@picマイコン さんが書きました:送られてきたデータを色ごと(red/green/blue)に分割して格納する方法が分かりません。
データを単純に受信するプログラムなどが既にあるのであれば、それを提示してください。

Re: シリアル通信

Posted: 2015年12月07日(月) 10:18
by picc
みけCAtT様

返信ありがとうございます。
以下のコードで1バイトのデータを受信することはできております。

送信側から、XXYYZZというデータを送付していますが、先頭のXのみ受信している状況です。
2点分からないことがあります。
①XXYYZZと複数バイト(6バイト)受信する方法
②6倍と受信した後、XXをRedのデータ、YYをGreenのデータ、ZZをBlueのデータとする方法

素人質問で申し訳ありませんが、教えていただけると助かります。



コード:

	char cmnd;
		while (!DataRdyUSART());      
         cmnd = getcUSART();             //受信データを読み込む

Re: シリアル通信

Posted: 2015年12月07日(月) 11:24
by みけCAT
処理系のことを知らないので予想ですが、こんな感じでしょうか?
(コンパイル・テストしていません)

コード:

int getDigit(char c) {
    if ('0' <= c && c <= '9') {
        return c - '0';
    }
    switch(c) {
    case 'a': case 'A': return 10;
    case 'b': case 'B': return 11;
    case 'c': case 'C': return 12;
    case 'd': case 'D': return 13;
    case 'e': case 'E': return 14;
    case 'f': case 'F': return 15;
    }
    return 0;
}

int getData(const char* d) {
    return getDigit(d[0]) * 16 + getDigit(d[1]);
}

/* 何かの関数の中 */
char cmnd[7] = {0};
int i;
int Red, Green, Blue;
for (i = 0; i < 6; i++) {
    while (!DataRdyUSART());
    cmnd[i] = getcUSART();
}
Red = getData(cmnd + 0);
Green = getData(cmnd + 2);
Blue = getData(cmnd + 4);

Re: シリアル通信

Posted: 2015年12月07日(月) 15:03
by picc
みけCAT様


返信ありがとうございます。
教えていただいたコードを自分なりに勉強させていただきました。

[私の理解と質問]

①文字列データ→整数化
PCから送付される、XXYYZZのデータは、16進数の文字列データとして送付されるため、
これを整数に置き換える処理と理解しました。

コード:

int getDigit(char c) {
    if ('0' <= c && c <= '9') {
        return c - '0';
    }
    switch(c) {
    case 'a': case 'A': return 10;
    case 'b': case 'B': return 11;
    case 'c': case 'C': return 12;
    case 'd': case 'D': return 13;
    case 'e': case 'E': return 14;
    case 'f': case 'F': return 15;
    }
    return 0;
}
②16進数→10進数化
①で数値に変換した16進数をここで10進数に置き換えていると理解しました。

コード:

int getData(const char* d) {
    return getDigit(d[0]) * 16 + getDigit(d[1]);
}
③ここの理解ができておりません。

◆cmnd[7]は、PCから送付されるXXYYZZを格納する配列と解釈しましたが、
送られてくるのは6文字なので、cmnd[6]でも[7]でも同じ結果でした。

◆for分は、1バイトずつデータを受信してくるので、これを6文字分(6バイト)受信するプログラムと理解しました。

◆最後の+0, +2 +4が全く理解できていません。(素人で申し訳ありません。)
 教えていただけると助かります。

コード:

char cmnd[7] = {0};
int i;
int Red, Green, Blue;
for (i = 0; i < 6; i++) {
 	while (!DataRdyUSART());
   	cmnd[i] = getcUSART();
	}

Red= getData(cmnd + 0);
Geen = getData(cmnd + 2);
Blue= getData(cmnd + 4);

[確認結果]
PC側からデータを送付して、LCDにRed/Green/Blueの結果を表示させましたが、次の結果になってしまいます。

PC側からのデータ送信 FFFFFFの時、
Red/Green/Blue = 15/255/255

PC側からのデータ送信 00FFFFの時、
Red/Green/Blue = 0/15/255

PC側からのデータ送信 FF00FFの時、
Red/Green/Blue = 15/240/15

PC側からのデータ送信 FFFF00の時、
Red/Green/Blue = 15/255/240

なにか、初歩的なミスをしているのでしょうか。

Re: シリアル通信

Posted: 2015年12月07日(月) 16:36
by みけCAT
picc さんが書きました:◆cmnd[7]は、PCから送付されるXXYYZZを格納する配列と解釈しましたが、
送られてくるのは6文字なので、cmnd[6]でも[7]でも同じ結果でした。
文字列(null-terminated string)として利用されるかもしれないと思い念のため最後にヌル文字を置きました。
picc さんが書きました:◆for分は、1バイトずつデータを受信してくるので、これを6文字分(6バイト)受信するプログラムと理解しました。
forではなくforでしょう。
picc さんが書きました:◆最後の+0, +2 +4が全く理解できていません。
comm + 2は&comm[2]と同じ意味になります。
読み込んだ文字列の途中から渡しています。
picc さんが書きました:[確認結果]
PC側からデータを送付して、LCDにRed/Green/Blueの結果を表示させましたが、次の結果になってしまいます。

PC側からのデータ送信 FFFFFFの時、
Red/Green/Blue = 15/255/255

PC側からのデータ送信 00FFFFの時、
Red/Green/Blue = 0/15/255

PC側からのデータ送信 FF00FFの時、
Red/Green/Blue = 15/240/15

PC側からのデータ送信 FFFF00の時、
Red/Green/Blue = 15/255/240

なにか、初歩的なミスをしているのでしょうか。
データがずれているようです。
本当にFFFFFFのようなデータを入力したのですか?
最初に書いてある#FFFFFFのようなデータではないのですか?

Re: シリアル通信

Posted: 2015年12月07日(月) 18:07
by picc
みけCAT様

返信ありがとうございます。
次のコードに書き換えたことで、正しくRGBの値をLCDに表示させることができました。
ご報告と質問がございます。

[ご報告]

cmnd[7]配列にPCからのデータが格納されると思いますが、

次のコードでできたということは、
cmnd[7] = { ??, X1, X2, Y1,Y2, Z1,Z2} というように先頭に何か文字が入ってしまっているのだと思います。

Red=X1X2
Green=Y1Y2
Blue=Z1Z2 となっています。

コード:

for (i = 0; i < 7; i++) {
 	while (!DataRdyUSART());
   	cmnd[i] = getcUSART();
	}

Red= getData(cmnd + 1);
Green = getData(cmnd + 3);
Blue= getData(cmnd + 5);


次のコードの場合は、
cmnd[7] = { ??, X1, X2, Y1,Y2, Z1} となっており、

Red=0X1
Green=X2Y1
Blue=Y2Z1  となってしまっていました。
つまり先頭(??)に0が1文字入ってしまっているようです。(送信側のプログラムを確認してみます。)

コード:

for (i = 0; i < 6; i++) {
 	while (!DataRdyUSART());
   	cmnd[i] = getcUSART();
	}

Red= getData(cmnd + 0);
Green = getData(cmnd + 2);
Blue= getData(cmnd + 4);


[ご質問]
ここまででPCからのデータをLCDに表示させることはできたのですが、データの受信が一度しかできません。
次のコードでプログラムを記述していますが、一度PCからデータを送信すると、次のデータを受け付けてくれません。

期待するのは、PCからデータを送ると、その都度値をLCDに表示させる動作です。
while(1)の中で常にPCからのデータを受信しているつもりなのですが…何か間違えているのでしょうか。

コード:

while(1){
		mode = PORTD & 0x70;
		switch(mode){
		case 0x50:
			manual_mode();	 
		default:
			break;
        }
		
	}
		CloseUSART();


int manual_mode(void)
{
		char cmnd[7] = {0};
		int i;
		int Red, Green, Blue;

		for (i = 0; i < 6; i++) {
    	while (!DataRdyUSART());
    	cmnd[i] = getcUSART();
		}

		Red= getData(cmnd + 0);
		Green = getData(cmnd + 2);
		Blue= getData(cmnd + 4);
}



Re: シリアル通信

Posted: 2015年12月09日(水) 08:07
by picc
みけCAT様に教えていただいたコードでPCからのデータ受信はできるようになりましたが、次の問題があります。


①PC側から送信できるのは、2回まで。
1回目に"FF0000"というデータを送信すると、LCDの表示は、
RED GREEN BLUE
100% 0% 0% と正しく受信できているのですが、
2回目に同じデータ"FF0000"を送信すると、LCDの表示は、
RED GREEN BLUE
 0% 100% 0% とデータがずれてしまいます。
3回目以降は、送信しても、何も変化しません。

原因が分からず困っております。
目的の動作は、PC側からデータが送られてくるたびに、LCDの表示が変化することです。

コード:

	while(1){
		for (i = 0; i < 6; i++) {
    	while (!DataRdyUSART());
    	cmnd[i] = getcUSART();
		}
		
		//cmnd[]からR/G/B各データを取り出し %変換
		vol_R= getData(&cmnd[0])*100/255;		//vol_R= getData(cmnd + 0);
		vol_G= getData(&cmnd[2])*100/255;		//vol_G = getData(cmnd + 2);
		vol_B= getData(&cmnd[4])*100/255;		//vol_B= getData(cmnd + 4);

		
		lcd_cmd(0x80);			//1行目へ移動
		lcd_printf(RGB);	
        lcd_cmd(0xC0);			//2行目へ移動
        sprintf(&str[0],(const far rom char *)"%3d%%  %3d%%  %3d%%",vol_R,vol_G,vol_B); // %f(浮動少数)は未サポート
        lcd_printf(&str[0]);	//vol_R vol_G vol_B表示 
		delay_ms(100); 			//0.1秒待ち
	}
}


int getDigit(char c) {
    if ('0' <= c && c <= '9') {
        return c - '0';
    }
    switch(c) {
    case 'a': case 'A': return 10;
    case 'b': case 'B': return 11;
    case 'c': case 'C': return 12;
    case 'd': case 'D': return 13;
    case 'e': case 'E': return 14;
    case 'f': case 'F': return 15;
    }
    return 0;
}

int getData(const char* d) {
    return getDigit(d[0]) * 16 + getDigit(d[1]);
}




Re: シリアル通信

Posted: 2015年12月09日(水) 09:34
by みけCAT
LCDなどできちんとforループを抜けることができているか、できていないな、何文字目で詰まっているかを調べてみてください。

Re: シリアル通信

Posted: 2015年12月09日(水) 17:48
by can110
まずはみけCAT様のおっしゃる通り、現状確認が先決ですが
送信側PCからは、確実に6バイト単位でデータが送信されていますか?
Tera Termやハイパーターミナルでは、たしかデフォルト動作ではデータ末尾にCR(\r)1バイトが付加されます。
もしかして以下のような状況になっているかもしれません。
以下の"r"はCR(\r)です。

コード:

受信   -> 色
FF0000 -> FF0000 1回目のデータ。赤
rFF000 -> 0FF000 1回目の残りCRと2回目のデータ。ほぼ緑
0rFF00 -> 00FF00 2回目の残りと3回目のデータ。緑
00r    ->        3回目の残り。受信待ち状態

Re: シリアル通信

Posted: 2015年12月10日(木) 09:16
by picc
みけCAT様、can110様

ありがとうございます。
送信側プログラム(vb)を見直したところ、CR+LFを付加して送信していました。
そのため、受信バイト数を8バイトにしたところ、目的の動作を行うことができました。

本当にありがとうございました。次は、マイコン同士の送受信にチャレンジしてみます。