denshikobo’s blog

PICプログラミングやPCの操作で感じた日々の由無し事を綴ります

オシロジのノウンバグが無くなった

(2015.12.20)
11月のPICクラブでオシロジの開発状況を『(ほぼ)完成』と報告しましたが、実はノウンバグがいくつか残っていました。バグを一つ一つ片づけている最中に新たなバグが見つかったりして、なかなかノウンバグのリストは短くなりません。それでも着実にバグを潰し、計測データを保持する機能のバグだけが残りました。


オシロジは描画Window(<==四つある)ごとに計測データの保持と更新が指定できる仕様です。再計測しても、保持を選択したWindowの描画は前の状態を保つ筈なのですが、何故か書き換わってしまいます。orz

最初は描画ロジックのバグを疑ったのですが、調べても不具合箇所は見つかりません。
Log_Dataクラスへのポインタの切り替えも問題ありません。Log_Dataクラスのクローンはちゃんと出来ているし・・・
(<==実はここに間違いがあった)

確認作業を続けてついに、”保持する筈の計測データが書き換わっている”ということに気付きました。clone関数における、シャロー・コピーとディープ・コピーの違いについて理解しているつもりでしたが、int配列がポインタ参照であることをうっかりしていたのです。

 Log_Dataクラスのメンバ配列のclone処理を追加して、ようやく計測データの保持が機能するようになったのでした。

やれやれ

MCCを使ってサクッと開発・・・出来なかった

(2015.12.02)
X-TALオシレータの発振周波数がどうもおかしいので、手元にあった
PIC16F1705で簡単なタイマー回路を組んで調べてみることにしました。

f:id:denshikobo:20151202130552p:plain

ブレッドボード上に回路を組み、MCC(MPLAB Code Configurator)でタイマーetc.を定義したプログラムでX-TALオシレータの動作をサクッと確認する筈だったのですが、LEDチカが動きません。orz

実は、プログラムを書き始めてから回路を組み立てるまで二か月近く経って(オシロジ開発に専念していた)います。プログラムの骨格は書き上げた筈なのですが、定かではありません。

デバッガでブレーク・ポイントを設定して調べてみると、TIMER1割り込みハンドラが呼び出されていません。WATCHウィンドウでレジスタの状態を調べると、PIR1.T1IEが1でPIE1.T1IFも1になっていて、MCCが行ったタイマー設定も、割り込み設定も良さそうです。

で、INTCONを調べるとGIEもPIEも0(<==ディスエーブル)のままです。

あれ?
なんでこれが0のままなんだろう?

早速MCCのInterrupt Managerウィンドウを開いてみると・・・

f:id:denshikobo:20151202130559p:plain

はっきり書かれていました。
割り込みはユーザの責任でイネーブルにしなければならないのです。
とは言っても、そのための準備はちゃんと整っています。

f:id:denshikobo:20151202130604p:plain

二か所のコメントを外し、無事LEDがチカチカし始めました。

ちょっと躓きましたが確認プログラムが動き始めました、X-TALオシレータをとっかえひっかえして動作を確認してみると、やっぱり発振周波数が変(高かったり低かったり)です。

デバイスを購入した秋月電子に電話しました。

『X-TALオシレータの発振周波数が変なんですが・・・』
「1番ピンはどうされました?」
『えっ、NCじゃないんですか?』

あわててPDFマニュアルを開く

『ご、ごめんなさい。』
チン

10kΩでプルアップしたら、X-TALは12MHzで安定して動作するようになりました。
どうやら別のマニュアルを見ていたようです。:-p

 

Free EditionではRegister変数が使えない?

(2015.11.17)

=====================

記事の途中、SkypePhoneへのリンクが生成されていますが無視して下さい

=====================

 PIC32用のCコンパイラXC32のマニュアルの9.6項にRegister変数に関する記述があります。
9.6 VARIABLES IN REGISTERS

”最適化レベル1以上で有効”と記されていて、5.9.7項によるとFree Edition(無償版)でも最適化レベル1は設定可能です。
5.9.7 Options for Controlling Optimization
TABLE 5-11: GENERAL OPTIMIZATION OPTIONS

オシロジは、ソフトウェアによるトリガ判定処理がサンプリング・レートを制限する要因となっていて、Register変数を上手く利用すれば処理時間を短縮出来るかもしれないのです。

で早速、こんなプログラムを試して見たのですが・・・
672:                 register unsigned int *ifs0 __asm__("$4");   // a0 reg.
673:                   ifs0 = (unsigned int *)(&IFS0);
674:                   
675:                   if( *ifs0&ifs0_adif_bit )
9D002900  8F828060   LW V0, -32672(GP)
9D002904  3C03BF88   LUI V1, -16504
9D002908  8C631030   LW V1, 4144(V1)
9D00290C  00431824   AND V1, V0, V1
9D002910  10600002   BEQ V1, ZERO, 0x9D00291C
9D002914  3C03BF88   LUI V1, -16504
676:                   {
677:                       *(ifs0+1) = ifs0_adif_bit;
9D002918  AC621034   SW V0, 4148(V1)   <== ここと
678:                   }


こちらはIFS0レジスタを直接読む従来の方法です
641:                   if( IFS0&ifs0_adif_bit )
9D003120  3C02BF88   LUI V0, -16504
9D003124  8C431030   LW V1, 4144(V0)
9D003128  8F828060   LW V0, -32672(GP)
9D00312C  00431824   AND V1, V0, V1
9D003130  5060001E   BEQL V1, ZERO, 0x9D0031AC
9D003134  3C02BF88   LUI V0, -16504
642:                   {
643:                        IFS0CLR = ifs0_adif_bit;
9D003138  3C03BF88   LUI V1, -16504   <==ここが
9D00313C  AC621034   SW V0, 4148(V1)  <==ちょっと違う

8ステップから7ステップに変わりましたが、Register変数の指定は無視されているように思われます。

使い方が悪いのでしょうか?
それともRegister変数を使った最適化は(Free Editionには)許されないのでしょうか?
orz

マニュアルの11.3項には、”関数パラメータはA0~A3レジスタを介して引き渡される”と記されています。
11.3 REGISTER CONVENTIONS

そこで、さらにもう一つ試して見ました。
662:                 void dummy_log(uint32_t *ifs0)
663:                 {
664:                   if( *(ifs0)&ifs0_adif_bit )
9D004CA4  8F828060   LW V0, -32672(GP)
9D004CA8  8C830000   LW V1, 0(A0)
                              <== A0(IFS0へのポインタ)を介してアクセスしている
9D004CAC  00431824   AND V1, V0, V1
9D004CB0  54600001   BNEL V1, ZERO, 0x9D004CB8
9D004CB4  AC820004   SW V0, 4(A0)
                              <== A0+4(IFSCLRへのポインタ)を介してアクセスしている
9D004CB8  03E00008   JR RA
9D004CBC  00000000   NOP
665:                   {
666:                       *(ifs0+1) = ifs0_adif_bit;
667:                   }
668:                 }

684:                   dummy_log( (uint32_t *)(&IFS0) );
9D002934  3C04BF88   LUI A0, -16504
9D002938  0F401329   JAL dummy_log
9D00293C  24841030   ADDIU A0, A0, 4144  <== IFS0へのポインタがセットされる

成功です。A0レジスタを介して引き渡されたIFS0へのポインタを使ってIFS0にアクセスしています。

僅かな違い(7ステップから5ステップ)ですが、一歩前進しました!

 

オシロジの描画に不具合が見つかった

(2015.11.16)
オシロジの開発が佳境に入りました。

オシロジの構想が(スペックはダウンしましたが)ようやく実現しつつあります。
念願のオシロジ動作(オシロ4CHとロジアナ10chの同時サンプリング)も無事達成・・・
と喜んだのも束の間、問題が見つかりました。

f:id:denshikobo:20151116171919p:plain

同じ信号を計測しているのに、オシロとロジアナの表示が微妙にずれています。orz

最初はJAVAプログラムのバグを疑ったのですが、原因となりそうな不具合は見つかりませんでした。
そこで、PICの計測処理を調べたところ、DMAの転送回数に差を生じていることが判りました。

はて?
一体何が起きているのでしょうか?

TIMERはオシロもロジアナも同じ設定になっていることを確認しました。
となると、割込フラグの検出で問題が生じているに違いありません。

アセンブル・リストを調べてみると・・・
ビンゴ~
不具合発生箇所が見つかりました。

1634:                  if( IFS0bits.T4IF )
1635:                  {
1636:                     IFS0bits.T4IF = 0;

9D003F7C  3C02BF88   LUI V0, -16504
9D003F80  8C421030   LW V0, 4144(V0)
9D003F84  7C4204C0   EXT V0, V0, 19, 1
9D003F88  10400014   BEQ V0, ZERO, 0x9D003FDC
9D003F8C  3C02BF88   LUI V0, -16504
9D003F98  8C431030   LW V1, 4144(V0)      <== IFS0を読み込んで
9D003F9C  7C039CC4   INS V1, ZERO, 19, 1  <== フラグ・ビットを0クリアして
9D003FA0  AC431030   SW V1, 4144(V0)      <== 書き戻している

これでは、読み込んでから書き戻すまでの間に変化したIFS0のフラグが消えてしまいます。XC8では問題なかった記述がXC32では使えなかったのです。

PIC32で拡張されたCLRレジスタを使って、割込フラグの操作を以下のように書き換えました。
1635:                     IFS0CLR = ifs0_t4if_bit;  // ifs0_t4if_bit = (1<<19);

9D003F9C  3C03BF88   LUI V1, -16504
9D003FA0  AC621034   SW V0, 4148(V1)

f:id:denshikobo:20151116172025p:plain

割込フラグの操作を書き換えたところオシロとロジアナの表示が一致するようになりました。上はオシロ2kHz、ロジアナ5kHzでサンプリングした結果を表示しています。JAVA側で行っているX軸のスケーリングも上手く機能しています。

パチパチパチ~

どうやら不具合対処は成功したようです。
やれやれ

 

inline指定が無視されている?

 

(2015.11.04)
PIC32MX250F128を使った最小モデルのオシロジをOscilogi410(オシロ4CH、ロジアナ10CHの意)と呼ぶことにしました。(以前はOscilogi2と呼んでいた)

PIC32MX250F128は最大500kHzでAD変換できます(スペックは1MHzだったのですがErrataで・・・)が、ソフトウェアで行うトリガ判定が間に合うかどうか微妙なところです。

そこでdisassembly listを調べてみたのですが、inline指定している関数の呼び出しにJAL(Jump and Link)命令が使われていることに気づきました。クロック・サイクルが問題になっているところにこんなコードが使われていては台無しです。

コンパイラのマニュアルを調べて、inlineを有効にするためにはコンパイラ・オプション”-finline-functios”の指定が必要であることが判りました。

で、早速試して見ると・・・

f:id:denshikobo:20151104130655p:plain

--------------------------------
warning: Compiler option (inline functions) ignored because the free XC32 C compiler does not support this feature.

visit http://www.microchip.com/MPLABXCcompilers to purchase a new MPLAB XC compiler license.
--------------------------------
”inline関数を使いたければ、有償版のXC32を買え”ということのようです。orz

inline関数を呼び出すのは以下の5箇所で、制御レジスタを操作するだけの簡単なコードです。

    if( ADC_1_INT_FlagGet() )   <==
    {
        ad_buff[0] = ADC1BUF0;  
        ADC_1_INT_FlagClear();   <==
    }
    
    if( ADC_TIMER_INT_FlagGet() ) <==
    {
       SYS_DMA_Channel1ForceStart(); <==
       ADC_TIMER_INT_FlagClear();  <==
    }

Harmonyの枠組みからは外れてしまいますが、ここだけ制御レジスタを直接操作することにします。

有償版のXC32は安い方のSTD版(これ)で凡そ$500です。

それを購うことができない貧乏人には”Harmonyの高邁な理念は高嶺の花”ということでしょうか?

デバッグの新しい技を習得した

(2015.10.29)
描画パラメータを計算するルーチンにデバッグ・ダンプ文を挿入したところ、何故か複数回呼び出されることが判りました。オシロジのJAVAプログラムは(何度も手直しを重ねたため)些か見通しが悪くなっていて、どこから呼び出しているのかはっきりしません。

Error Exceptionが発生したときに表示されるコール・スタックを指定した箇所で表示できれば、関数の呼び出し階層を確認することができます。

先達の知恵を借りるべく、ググってみると・・・
ビンゴ~

同じようなことを考えた人が見つかりました。

stackoverflow.com

いくつか回答が寄せられていましたが、その中の一つを使って見ることにしました。
new Exception("Stack trace").printStackTrace();

update_data_positionルーチンに挿入した結果がこれ(適当に編集した)です。
java.lang.Exception: Stack trace
    at Main_Frame.update_data_position(Main_Frame.java:4293)
    at Main_Frame.Display_PositionStateChanged(Main_Frame.java:4301)
    at Main_Frame.access$2600(Main_Frame.java:16)
    at Main_Frame$27.stateChanged(Main_Frame.java:624)
    at javax.swing.JSlider.fireStateChanged(JSlider.java:432)
    at javax.swing.JSlider$ModelListener.stateChanged(JSlider.java:350)
    at javax.swing.RangeModel.fireStateChanged(RangeModel.java:364)
    at javax.swing.RangeModel.setRangeProperties(RangeModel.java:302)
    at javax.swing.RangeModel.setMaximum(DefaultBoundedRangeModel.java:219)
    at javax.swing.JSlider.setMaximum(JSlider.java:619)
    at Main_Frame.update_slider(Main_Frame.java:3920)
    at Main_Frame.window_type_change(Main_Frame.java:3957)
    at Main_Frame.window_button_common(Main_Frame.java:3728)
    at Main_Frame.Window_1_ButtonActionPerformed(Main_Frame.java:3748)
(この下にjava.awt.EventDispatchThread関連の呼び出しが40行くらい続いている)

draw_right_posi:644>draw_max_posi:0
java.lang.Exception: Stack trace
    at Main_Frame.update_data_position(Main_Frame.java:4293)
    at Main_Frame.Display_PositionStateChanged(Main_Frame.java:4301)
    at Main_Frame.access$2600(Main_Frame.java:16)
    at Main_Frame$27.stateChanged(Main_Frame.java:624)
    at javax.swing.JSlider.fireStateChanged(JSlider.java:432)
    at javax.swing.JSlider$ModelListener.stateChanged(JSlider.java:350)
    at javax.swing.RangeModel.fireStateChanged(RangeModel.java:364)
    at javax.swing.RangeModel.setRangeProperties(RangeModel.java:302)
    at javax.swing.RangeModel.setMinimum(RangeModel.java:203)
    at javax.swing.JSlider.setMinimum(JSlider.java:578)
    at Main_Frame.update_slider(Main_Frame.java:3921)
    at Main_Frame.window_type_change(Main_Frame.java:3957)
    at Main_Frame.window_button_common(Main_Frame.java:3728)
    at Main_Frame.Window_1_ButtonActionPerformed(Main_Frame.java:3748)

draw_right_posi:644>draw_max_posi:0
java.lang.Exception: Stack trace
    at Main_Frame.update_data_position(Main_Frame.java:4293)
    at Main_Frame.window_button_common(Main_Frame.java:3729)
    at Main_Frame.Window_1_ButtonActionPerformed(Main_Frame.java:3748)

表示位置を操作するスライダのMin、Maxを設定したときにStateChanged関数が呼び出され、update_data_positionルーチンが呼び出されていることが判りました。

新たなフラグを設けて、StateChanged関数からの呼び出しを制御することにすれば良さそうです。

このデバッグ技は使えます!

 

BCM2835ライブラリのバグ?

(2015.10.24)
オシロジのJAVAプログラムはbcmlib_for_javaこれです)を使ってI2C通信を行っています。bcmlib_for_javaは、BCM2835ライブラリ(これ)をJAVAから呼び出すラッパーとして動作しますが、BCM2835ライブラリ(<==root権限で動作する)を直接JAVAプログラムから呼び出す訳ではありません。(子プロセスとして起動し、プロセス間通信を行っている)

オシロの計測データを読み出したとき、I2C通信に問題があることが判りました。

f:id:denshikobo:20151024174721p:plain

1024バイトずつデータを読み出す度に、おかしなことになっています。調べてみると1バイト余計に読み出していました。

この(I2C通信の)不具合が、BCM2835ライブラリのバグなのか、プロセス間の通信処理のバグなのか、はっきりしません。

プログラムを変更して、読み出すデータ数を1023バイトデータにしてみました。
for (int offset = 0; offset < OSCILLO_BUFF_SIZE;) {
//  ret = bcm2835.i2c_read(i2c_buff, 1024);
  ret = bcm2835.i2c_read(i2c_buff, 1023);
  for (int o = 0; o < 1024; o++) {
     oscillo_buff[offset++] = i2c_buff[o];
  }
}

f:id:denshikobo:20151024174712p:plain

改善しましたが、ときどき不連続な部分が現れます。1024バイト目の計測データを読み取っていないことが原因です。

I2Cを読み出し回数と転送データ数が何故一致しないのか?
ソース・コードを見ただけでは、不具合ヶ所は見つけられません。orz

そこで、次善の策として読み出しポインタを一つ戻すコマンド"RCV N"を設け、1024バイト読み出す度に、"RCV N"コマンドを送るようにしてみました。

for (int offset = 0; offset < OSCILLO_BUFF_SIZE;) {
  ret = bcm2835.i2c_read(i2c_buff, 1024);
  for (int o = 0; o < 1024; o++) {
     oscillo_buff[offset++] = i2c_buff[o];
  }
  ret = bcm2835.i2c_write("RCV N" + '\0', 6);
}

f:id:denshikobo:20151024174732p:plain


どうやら、連続した計測データを分割して読み取ることが出来るようになりました。
あくまで緊急避難的な処置ですが、とりあえずこのまま開発を続けようと思います。

オシロジが完成したところで、(I2C通信の信号をモニタしながら)あらためてデバッグします。