« 2008年10月 | トップページ | 2009年1月 »

2008年12月 7日 (日)

アセンブラのバグなのか?

【事後フォロー記事】
所長です、こんばんわ

下記の記事ですが、内容に誤りがある為、訂正します。
私が「バグバグ」と騒いでいた原因は、「関数のリエントラント(再入可能)性」の問題であり、コンパイラのバグでは無く、私のプログラミング知識不足からくる、プログラムのバグでした。
すっぽんのクリさん(コメント参照)からのアドバイス通りです。
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日だけ?
どうにかなんないのかねぇ

では・・・

| | コメント (4)

2008年12月 5日 (金)

バグとの戦い

いやー、久々のブログ更新
皆様、お待たせしましたって、実は進んでないんですがね・・・
あまりに進まないんで、なんで進んでないのかを報告したいとおもいますね。
といっても、時間がかかってるだけで、のろまのカメのごとく進んでますよ(!)

【進まない理由その1】
新回路で、回路のバグ修正と格闘。
同一基板上で、あれもこれも一緒に動かす場合、モーターのノイズなどが悪さをします。
このノイズ対策が不足しており、電解コンデンサの追加や、
パターン引き回し変更を余儀なくされました。
又、プラグヒート用のPchFETの内部抵抗の高さで、どうしてもまともに電流が流れず、NchのFETに変更。
且つ、FETがあっちっちなので、FETは3個並列に!
Img_1429自作ECUは、なんだか
オブジェ見たいになっています。
これはこれで「作品(?)」かも


【進まない理由その2】
ハードルの高いプログラミングと格闘

前回、モータードライバーとして機能していた2つの8ピンのPICを
新回路では、1つ削減しました。1つのPIC(PIC12F683)をこれ以上使えないくらいフルに使ってます。各ポートの割り当ては、こんな感じ・・・
PIN_A0   Starter Plug Current Feed Back
PIN_A1   Starter Plug(自作PWM)
PIN_A2   Kerosin pump (内部PWM)
PIN_A3   Clock from MAIN
PIN_A4   Data from MAIN
PIN_A5   GUS Valve(自作PWM)

プラグヒート電流のフィードバック制御とかで、技量も無いのに難しい事をしてたので、ここでも時間かかってます。
ちなみに、MAIN側 CPUとの通信(片側通行ですが)は、SPIもどきの独自プロトコルにしてみました。(って言うとかっこいいけど、大したことはしてません)

【進まない理由その3】
CCS社製コンパイラのバグと格闘
有料のコンパイラなんですけど、時々バグってます。

いつもつかまるバグは、「Delay_xx()」ですかね。
入れる数字の大きさによって、割り込みが利かなくなります。
今、どうしようも無く困ってるバグは、「勝手に変数が変わるバグ」です。
if(valve_pwm_last != valve_pwm)
{
  中略
}
2つの変数を比較してるだけなのに、別のことをすると
勝手にValve_pwmの変数が変わってしまう・・・
変数の定義をintからlongにしてみたり、
if文の定義を無理やり変えてみたりしてますが、
なかなか直りません。(どーしよう・・・困った)
このバグが無くなれば、一段落するんですが・・・

ちなみに、PICの書き込みには、CCS社純正のICD-U40 を使ってますがこれまた使いにくImg_1430 い・・・
コンパイラからの直接書き込みは、超時間がかかるので、結局、単独でプログラマを起動し、コンパイルするたびに別の書き込みプログラムを使って書き込んでます。
そもそも、8pinや、18pinのPICでは、どうしても空きポートがなくなってしまうので、写真の様な、2段重ねROMソケット+切り替えスイッチを自作して使ってます。


【おまけ】
最近1週間で、どんな人がBlogに訪問してるのか、訪れる人の検索ワードを覗いてみました・・・
Blog_access
やはり上位を占めるのは「ジェットエンジン」ですが、PIC系の検索ワードも多いですね。(あんまり役に立ってなくてゴメンナサイ)

いつも上位に組み込むのは、mikrocですかねぇ。
PICのコンパイラですが、あまりにバグが多いので、私はやめました。皆さん困っているんでしょうね。

FETプラグヒーターを自作しようという人も多いようです。

「ジェットエンジン起動」の人は、自作を目指しているんでしょうか?
検索ワードを見てると、あーみんな考えることは同じなんだなぁ~とか思ってしまいます。

累計アクセス数:59119
一日平均:84.34
結構思ったよりもアクセスしてもらっているようなので、がんばらねば・・・

今週は、風邪を引いて、思考能力はメロメロですが、
バグとの格闘を続けますかぁねぇ・・・

| | コメント (4)

« 2008年10月 | トップページ | 2009年1月 »