アセンブラのバグなのか?
【事後フォロー記事】
所長です、こんばんわ
下記の記事ですが、内容に誤りがある為、訂正します。
私が「バグバグ」と騒いでいた原因は、「関数のリエントラント(再入可能)性」の問題であり、コンパイラのバグでは無く、私のプログラミング知識不足からくる、プログラムのバグでした。
すっぽんのクリさん(コメント参照)からのアドバイス通りです。
m( __ __ )m
「リエントラント性」とは、分かり易く言うと、
割り込む前に使っていた関数(変数)が、割り込みから復帰した後に、何事も無かったかのように、正常に復帰するかどうか?
といった感じでしょうか・・・
わかりやすい例を挙げて説明します。
【悪い例】
#int_timer1
void timer1_isr(void)
{
int t;
disable_interrupts(GLOBAL);
output_high(ST_PLUG);
delay_us(100); // wait 100usec (at 8MHz)
output_low(ST_PLUG);
set_timer1(set_interval_p);
enable_interrupts(GLOBAL);
}
void main()
{
中略
delay_us(val);
中略
}
何が悪いかと言うと、main()の中でdelay_us(x)関数が使われていますが、割り込みルーチンの中でも、同じdelay_us(x)関数を使っています。
(クリさんの言葉を借りれば・・・)「内部で静的な変数を使っている場合」両方のdelay_us(x)関数は、同じ変数を使っている可能性があります。言葉の通りだとすれば、間違った使い方だということがわかると思います。
要するに、さっきまで使っていたdelay_us(x)関数内部の変数が、使っている途中で、他の場所から使われてしまうわけです。
これでは、期待通りのプログラムにはなりません。
【良い例】
#int_timer1
void timer1_isr(void)
{
int t;
disable_interrupts(GLOBAL);
output_high(ST_PLUG);
for (t=0; t<24; t++) { } // wait 100usec (at 8MHz)
output_low(ST_PLUG);
set_timer1(set_interval_p);
enable_interrupts(GLOBAL);
}
void main()
{
中略
delay_us(val);
中略
}
割り込みの部分で、 for 文を使って、delay_us(x)と同じ働きをするプログラムを作りました。
結果は、期待通りの動作となり、私が使っているCCSCのコンパイラでは、delay_us(x)は、「リエントラント性」が保障されていない事がわかりました。
割り込み関数を使う上で、
次の点に注意してプログラムを作ることにしました。
①割り込み中に、多重割り込みを防止するために、GIEピットをクリアする。
②割り込み関数は、極力簡素にする。
③リエントラント性を考慮する。
以上、事後フォローでした。
なお、回路は出来上がり、点火するところまでは、こぎつけました。
しかし、使っていたモーターから煙が噴いたので、また後戻り中です。
Speed400モーターは、起動時にあまり負荷が大きいと、20A(7.2Vで)も流れます。おそらく、タービンの回転がおかしかったときに、モーター内部の巻線が焼損したか、コミュテーター、ブラシ周辺が焼損したと思われます。
モーターは、高速回転タイプではなく、普通の380モーターでしばらくがんばります。
では・・・
------------------- 元記事 ---------------------
おおぉぉ~!
やっとプログラムのバグが直った!
バグはたくさんあるのですが、中でもどうしようも無かったのが、TimerとRAポートの割り込み制御でした。
割り込みを切っているはずなのに、切れてない。
どういうことなのか生成された(10年ぶりに)アセンブラを覗いてみると・・・
思ったようにコンパイルされていない!
コンパイルされたコードを見ると、何を意図しているのかよーわからん。
そこで、CCSCコンパイラで、インラインアセンブラを組んでみた。
(インラインアセンブラとは、C言語の記述の中に、無理やりアセンブラを組み込む方法)
CCSCのインラインアセンブラ記述はこんな感じ・・・
#asm ~ #endasm までの間がそのまま組み込まれます。
【実例】
#int_timer1
void timer1_isr(void) // for plug heater interval
{
// disable_interrupts(int_RA); ←コメントアウトした元コード
#asm
BCF 0x0B,3 ←直接書いたアセンブラコード
#endasm
set_adc_channel(0); // set A/D channel
delay_us(20);
real_current = read_adc(); // plug current check by 10bit
output_high(ST_PLUG);
delay_us(200); // Make plug heat pulse
output_low(ST_PLUG);
enable_interrupts(int_RA);
set_timer1(set_interval);
}
これがコンパイラーのバグだとすると、ちょっと複雑な事をしようとしたときは、結局アセンブラがわかんないとやっぱダメなんだなーと思った。
CCSCには他にもバグっぽいところがいくつかある。
これらのコードを使うときは、バグに気をつけよう。
delay_ms(x)
delay_us(x)
delay_cycles(x)
disable_interrupts(x)
その他にも、割り込みにからんだコードを使うときは、なるべく他の割り込みを一時停止するのが良いです。
例えば・・・
・PWMのdutyを書き換えるとき等は、書き換える前に他の割り込みを止める。
・A/Dコンバーター(変換終了)や、EEPROM(書き込み終了)なんかも割り込みが絡んでるので注意。
CCS社(日本の代理店殿へも)にちょっと一言
Webで見るとCCSCのバグって結構ある見たいね。
有料でバグだらけの癖にアップデート期限があるなんてせこいぞ!
本国(アメリカ)で買うと12ヶ月、日本で買うと30日だけ?
どうにかなんないのかねぇ
では・・・
最近のコメント