トップ «前の日記(2008年09月06日) 最新 次の日記(2008年09月10日)» 編集

のろのろのろ雑記


2008年09月08日 [ColdFire] ColdFire×P2P地震情報(7) "33.3 Hzの壁"を超える

_ [ColdFire] ColdFire×P2P地震情報(7) "33.3 Hzの壁"を超える

 Interface 9月号付録基板を使い,P2P地震情報のサービスを作ってしまう勝手な企画です.過去の記事は,タイトルのColdFireカテゴリからどうぞ.

前回のまとめ

  • 計測震度とフィルタとの対応を見ました.
  • 静止状態は震度3相当でした…

"33.3 Hzの壁"

 静止状態で震度3になってしまったということは,すなわち震度4以上でないと働かないということであって,それを震度計・地震計とは呼べないということです.

 何とかしてノイジーな状態を解消しなければなりません.ひとまず,"オーバーサンプリング"を考えてみます.オーバーサンプリングの効果は私も詳しく知りませんが,とりあえず震度3という表示にビックリしたので何でもやってみます.

loop_axis(){
 char t1; int i,cr;
 t1=CreateTimer(0,30000,0);
 InitAd(0x70);
 for(i=0;i<1000;i++){
  GetAd(4);GetAd(5);GetAd(6);
 }
 cr=GetTimerCount(t1);
 PrNum(cr);PrStr("\r\n");
 CreateTimer(t1,0,0);
}
Ax_Timer::loop_axis
26999

Ax_Timer::loop_axis
27000

 上記のコードは,3軸を1000回サンプリングした時の所要時間を計測するものです.タイマは10 ms単位ですから, "(30000 - RESULT) / 100" と計算すれば所要秒数が分かります.

 計算してみると,ちょうど30 s掛かっていることが分かります.サンプリングのためにGetAd関数を3軸×1000回=3000回呼び出していますから,「もしかして,GetAd関数はキッチリ10 ms掛かるようになっているんじゃないか?」,つまり3軸取得時には『33.3 Hzの壁』があるのではないか,という疑問が出てきます.

"33.3 Hzの壁"を超える

 この壁を超えます.新適当マイコン電子工作研究所あたりを見ていると,どうやらメモリマップドI/Oのようです.ということは,A/Dコンバータ(ADC)とてメモリアクセスで簡単に出来るはず…

 リファレンスマニュアルを調べると,"Chapter 28 Analog-to-Digital Converter (ADC)",493ページ以降に書かれています.

  • 8チャネルの入力と,2つのA/Dコンバータ
    • AN0〜3はコンバータAに,AN4〜7はコンバータBに接続
  • サンプリングするチャネルを指定し,シーケンシャルまたはパラレルサンプリング
    • 1回のサンプリング命令で最大8サンプル(チャネル)をサンプリングしデータを格納
  • 2チャネルの差分値,オフセットなどの設定が可能

 GetAd関数からは想像もつかない作りになっています(?).この辺りを自由自在に操れると,SilentCでもまだまだ頑張れる気がしてきます.

 実際にこれを操作する方法も,リファレンスマニュアルに載っています.ここでは簡単に示します.

  1. ADLST1とADLST2の設定(28.4.4/500ページ).リザルトレジスタが8サンプル分用意されているので,どのリザルトレジスタにどのチャネルからのサンプリングデータを注ぎ込むか指定する.
    • パラレルサンプリング実行時にはチャネル指定に制限があるので注意する.
  2. 必要であれば,ADOFSnでオフセットを設定(28.4.11/509ページ).
  3. 必要であれば,CTRL1でサンプリング方法を指定.
  4. CTRL1を設定してサンプリングし,ADRSLTnを読み取ることの繰り返し.
    • ADRSLTnはシフトされていることに注意する.

 これを使って全チャネル(AN0〜7)を読み取ってみましょう.

adc(){
  int i;
  char *ctr; ctr=0x40190000; //ADC CTRL1
  int *dat; dat=0x40190012; //ADC ADRSLT
  InitAd(0x70);
  #stop 0
  for(;;) {
    if(Getc(0)!=0)break;
    ctr[0]=32; //サンプリング
    for(i=0;i<8;i++){
      PrNum(dat[i]>>3);PrStr(", "); //AN0-AN7
    }
  PrStr("\r\n");
  SystemSleep();
}
2548, 2352, 2256, 2211, 1921, 2238, 2771, 2275,
2550, 2335, 2158, 2138, 1923, 2238, 2789, 2195,
2544, 2306, 1972, 1990, 1924, 2250, 2802, 2272,
2548, 2304, 2029, 2181, 1930, 2249, 2801, 2470,
2551, 2305, 2288, 2388, 1910, 2210, 2790, 2306,

OK

 AN4,5,6がX,Y,Z軸です.ちゃんと読み取れました.サンプリングにはある程度時間が掛かりますが,SilentCがコードを読む間に終わっているようなのでウェイトは入れません.さて,これでどれだけ早くなるでしょうか.

loop_axis(){
  char t1, *k; int *a,i,cr;
  k=0x40190000; a=0x40190012;
  t1=CreateTimer(0,30000,0);
  InitAd(0x70);
  for(i=0;i<1000;i++){
    k[0]=32;
    cr=a[4] >> 3;cr=a[5] >> 3;cr=a[6] >> 3;
  }
  cr=GetTimerCount(t1);
  PrNum(cr);PrStr("\r\n");
  CreateTimer(t1,0,0);
}
Ax_Timer::loop_axis
29710

Ax_Timer::loop_axis
29705

 ビットシフトして値を出すところまで行いました.およそ3 s,GetAd関数を呼ぶ時よりも10倍早くなっています! 移動平均などの計算時間があるのでストレートに10倍のサンプリング周波数は難しいですが,GetAd関数での待ち時間がなくなるのは大きいです.

参考