オシロジのノウンバグが無くなった
(2015.12.20)
11月のPICクラブでオシロジの開発状況を『(ほぼ)完成』と報告しましたが、実はノウンバグがいくつか残っていました。バグを一つ一つ片づけている最中に新たなバグが見つかったりして、なかなかノウンバグのリストは短くなりません。それでも着実にバグを潰し、計測データを保持する機能のバグだけが残りました。
オシロジは描画Window(<==四つある)ごとに計測データの保持と更新が指定できる仕様です。再計測しても、保持を選択したWindowの描画は前の状態を保つ筈なのですが、何故か書き換わってしまいます。orz
最初は描画ロジックのバグを疑ったのですが、調べても不具合箇所は見つかりません。
Log_Dataクラスへのポインタの切り替えも問題ありません。Log_Dataクラスのクローンはちゃんと出来ているし・・・
(<==実はここに間違いがあった)
確認作業を続けてついに、”保持する筈の計測データが書き換わっている”ということに気付きました。clone関数における、シャロー・コピーとディープ・コピーの違いについて理解しているつもりでしたが、int配列がポインタ参照であることをうっかりしていたのです。
Log_Dataクラスのメンバ配列のclone処理を追加して、ようやく計測データの保持が機能するようになったのでした。
やれやれ
MCCを使ってサクッと開発・・・出来なかった
(2015.12.02)
X-TALオシレータの発振周波数がどうもおかしいので、手元にあった
PIC16F1705で簡単なタイマー回路を組んで調べてみることにしました。
ブレッドボード上に回路を組み、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ウィンドウを開いてみると・・・
はっきり書かれていました。
割り込みはユーザの責任でイネーブルにしなければならないのです。
とは言っても、そのための準備はちゃんと整っています。
二か所のコメントを外し、無事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の同時サンプリング)も無事達成・・・
と喜んだのも束の間、問題が見つかりました。
同じ信号を計測しているのに、オシロとロジアナの表示が微妙にずれています。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)
割込フラグの操作を書き換えたところオシロとロジアナの表示が一致するようになりました。上はオシロ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”の指定が必要であることが判りました。
で、早速試して見ると・・・
--------------------------------
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が発生したときに表示されるコール・スタックを指定した箇所で表示できれば、関数の呼び出し階層を確認することができます。
先達の知恵を借りるべく、ググってみると・・・
ビンゴ~
同じようなことを考えた人が見つかりました。
いくつか回答が寄せられていましたが、その中の一つを使って見ることにしました。
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通信に問題があることが判りました。
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];
}
}
改善しましたが、ときどき不連続な部分が現れます。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);
}
どうやら、連続した計測データを分割して読み取ることが出来るようになりました。
あくまで緊急避難的な処置ですが、とりあえずこのまま開発を続けようと思います。
オシロジが完成したところで、(I2C通信の信号をモニタしながら)あらためてデバッグします。