□PIC(18F26K22)とxc8で書いたプログラム(XORWF)が挙動不審

まずすごい単純なことなんですが、データの受信などは割り込み処理で処理して受信バッファ溜め込んで、メインループで読み出して処理、 なんてことをやると思います。

で、18F26K22というPICでそんなプログラムを書いていたのですが、どう にも挙動不審になって、うんうんうなってしまいましたので、その記録 です。

プログラムとしては二つのグローバル変数があって、

unsigned int serial_rcvptr;
unsigned int serial_readptr;

// メインループ
while(1)
{
	// シリアルバッファに未読データがあるなら
	if (serial_rcvptr != serial_readptr)
	{
		// バッファに未読データがあったなら取得して処理
	}
}

みたいな感じで、受信割り込み処理では「serial_rcvptr」が加算され ていき、バッファから未読データを取得したら「serial_readptr」も 加算されていく(実際にはバッファサイズでマスクしてループですが) という処理です。

これが、全ての未読データを受信して

「serial_rcvptr == serial_readptr」

例えばserial_rcvptrも3、serial_readptrも3とかの状態なのにもかか わらず、なぜか

// シリアルバッファに未読データがあるなら
if (serial_rcvptr != serial_readptr)

で「違うよ」(=未読データがあるよ)という判定で{…}を処理してし まい、あれよあれよという挙動不審になっていた。(まぁここまで追い 詰めるにもだいぶ時間がかかりましたが)

で、致し方なくxc8が生成したアセンブラコードをみてみることにして みました。

1.if (serial_rcvptr != serial_readptr) {…} だと

941:                // メインループ
942:                while(1)
  1BB4    D6BA     BRA 0x192a
943:                {
944:                 // シリアルバッファに未読データがあるなら
945:                 if (serial_rcvptr != serial_readptr)
  192A    5055     MOVF 0x55, W, ACCESS  ; 0x55の値をWにコピー
  192C    0100     MOVLB 0   ; バンク切り替え
  192E    0100     MOVLB 0   ; バンク切り替え
  1930    19B8     XORWF 0xb8, W, BANKED ; 0xb8とW(0x55)の値をXORしてWに
  1932    B4D8     BTFSC 0xfd8, 0x2, ACCESS ; 0xfd8(=STATUS)<2>(=Z)が0なら次をNOP
  1934    D001     BRA 0x1938   ; …つまり1なら0x1938に飛んで…
  1936    D001     BRA 0x193a   ; 等しくないので{…}を処理するために0x1938に飛ぶ
  1938    D0CA     BRA 0x1ace   ; {…}の外(0x1ace)に飛ぶ
946:                 {
947:                  // 受信バッファから一文字取得
948:                  rcv_data = rcv_serialdata();
  193A    ECE9     CALL 0x31d2, 0
  193C    F018     NOP
  193E    0100     MOVLB 0
  1940    0100     MOVLB 0
  1942    6F9D     MOVWF 0x9d, BANKED

(;の後ろのコメントは自分で足しました)

ふむふむ、別におかしくないじゃないですか。

http://jyosou-robot.livedoor.biz/pdfs/PIC_18_ASM.pdf

どうもXORの処理にともなうZフラグの値がそもそもおかしいんじゃね?

国内ではすぐに出てこなかったけど、海外サイトでは同様に「わかんねー」 という書き込みがあって、変数をvolatile宣言しろとかありましたけど、 ちなみにこの二つのグローバル変数をvolatileにしてもアセンブラコード もなんら変化は無く、さてどうしたものか?

でもXOR(A ^ B)はなんかおかしいよということが散見されたので、ならば ということで以下のようにソースを変えてみました。

2. if (serial_rcvptr != serial_readptr) {…} を
    if (serial_rcvptr == serial_readptr) {} else {…}にしてみたら

941:                // メインループ
942:                while(1)
  0FA0    D682     BRA 0xca6
943:                {
944:                 // シリアルバッファに未読データがあるなら
945:               ///  if (serial_rcvptr != serial_readptr)
946:               ///  {
947:               ///  ↑この書き方だときちんと判定してくれない、PICかxc8のバグなのか?(´Д`)ハァ… 2013.07.11 T.Kabu
948:                 if (serial_rcvptr == serial_readptr)
  0CA6    0100     MOVLB 0   ; バンク切り替え
  0CA8    0100     MOVLB 0   ; バンク切り替え
  0CAA    51B8     MOVF 0xb8, W, BANKED  ; 0xb8の値をWにコピー
  0CAC    6255     CPFSEQ 0x55, ACCESS  ; 0x55とW(0xb8)が同じなら次をNOPに
  0CAE    D001     BRA 0xcb2   ; …つまり同じじゃないなら0xcb2に飛んで…
  0CB0    D001     BRA 0xcb4   ; 同じなので{…}を処理するために0xcb4に飛ぶ
  0CB2    D002     BRA 0xcb8   ; さらに0xcb8(else{…})に飛ぶ
  0CB4    D102     BRA 0xeba   ; まぁすることは無いので0xebaに飛ぶ
949:                 {
950:                 }
  0CB6    D101     BRA 0xeba   ; このコード無意味
951:                 else
952:                 {
953:               send_strdata("--- SERIAL Avaival? ---");
  0CB8    0EFF     MOVLW 0xff   ; さー未読データの処理をしましょう!!

(;の後ろのコメントは自分で足しました)

これならZフラグ関係ないし動きますね。

たぶんなんかの条件で、XORWFのPIC内部の処理がおかしいくてZフラグが 正しく設定されないんでしょうね。

なので、PICのプログラムで下記のような一件無駄な処理をみかけても、 実は深い意味があるということを忘れてはいけないってことでよろしくです。(自分自身も!!)

unsigned int serial_rcvptr;
unsigned int serial_readptr;

// メインループ
while(1)
{
	// シリアルバッファに未読データがないなら
	if (serial_rcvptr == serial_readptr)
	{
		// なにもしない
	}
	else
	{
		// バッファに未読データがあったなら取得して処理
	}
}

以上、つまらないメモでした。

自宅用GVC、デモ開始

OSCのあとしばらくご無沙汰していましたが、自宅用のGVCを作成していました。

DSC_0206

本日より「http://livecam.pgw.jp/」にてGVCのデモンストレーションの公開を始めましたのでごらんいただければと思います。

つきなみですが、温度・湿度・大気圧のリアルタイム表示と、クリックすることでリモートでスイッチのON/OFFをすることが出来るというものです。

最低限の機能のみを実現しましたが、雰囲気はつかんでいただけると思います。

このシステムの構成図については、後日掲載したいと思いますのでお待ちください。