まずすごい単純なことなんですが、データの受信などは割り込み処理で処理して受信バッファ溜め込んで、メインループで読み出して処理、 なんてことをやると思います。
で、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
{
// バッファに未読データがあったなら取得して処理
}
}
以上、つまらないメモでした。