こんにちは、sol です。
以前、「CS+ for CC のデバッグ・ツールについて」という質問で、一度お世話になりました。
最近、マイコンを触り始めたものです。
普段は、CS+ for CC で RL78/G14 の R5F104LE(64pin)、アプリケーション(CC-RL)でプロジェクトを作成して、シミュレータで動作確認を行っています。
現在は、こちら「https://toragi.cqpub.co.jp/tabid/864/Default.aspx」の「絵解き マイコンCプログラミング教科書」というので勉強しています。
こちらにはサンプルコードが載っており、今はそれを読み解く作業を行っております。
(もしダウンロードしてくださるのであれば、
ダウンロード・データ→関連ダウンロードデータはこちら
という部分からダウンロードできますが、1.2GB もあります。
その中の、02_ボード動作確認用プログラム > C-First_TEST です。
該当のプログラムは、RL78IOlib.c の366行目からになります。)
その中の、AD変換を行っている部分について質問があります。
// AD変換割り込み処理void ad_conv(void) { static int adsel = 0; static char test = 0; if (test) { test--; //1回目の測定結果を廃棄 return; } adout[adsel] = (adout_sum[adsel] + (ADCR >> 6)) / 2; adout_sum[adsel] = adout[adsel]; adsel++; if (adsel < 8) { ADS = adsel; } else if (adsel < 10) { ADS = adsel + 10; } else if (adsel < 12) { ADS = adsel + 118; test = 2; //1回目の測定結果の廃棄を指示(1回目どころじゃなくて、1回目と2回目を廃棄していると思います) } else { adsel = 0; ADS = adsel; ADMK = 1; ADCS = 0; //AD変換停止 return; } ADIF = 0;}
この ad_conv という関数がAD変換完了後に割り込まれる関数です。
一通り adsel == 11 まで行った後は adsel == 12 の時にAD変換が停止されますが、15kHz 低速オンチップ・オシレータ・クロックをクロックとするインターバルタイマによって 133[us] 間隔で割り込んでいる iv_timer という関数で、またすぐにAD変換が最初から開始されることになります。
ある程度時間を置くと(このプログラムでは rl78iolib_ini 関数内で wait(1000) つまり 約1秒待機しているようですが)adout[番号] にAD変換の結果が入ることになるのですが、その計算方法が独特だと思いました。
データシートに書いてあるように、ADCR(16bit)の上位10bitがAD変換の結果となるので、ADCR >> 6 は分かります。
しかし、adout と adout_sum(初期値は0)に分けているのが最初はよくわかりませんでした。
さらに、/ 2 はなぜあるのだろうかと疑問でしたが、上述したようなことが分かってきた結果、意味が分かりました。
AD変換が停止されるまでのワンループを1回とカウントして、n回目に得られたADCRの値を a_(n) とすると、
n 回目で adout に記録される値は、
a_(n) / 2 + a_(n-1) / 4 + a_(n-2) / 8 + a_(n-3) / 16 + ... + a_(1) / (2^n)
になる(誤差はおいておいて)ということが分かりました。
この値は、ADCR の値が一定であればその値に近づく(無限に行えば収束する)ため、なるほどと思いました。
長々と読んでいただいてありがとうございました。
そこで質問です。
上述のように手間をかけて計算しているのは恐らく、ノイズによる影響を抑えるためではないか、と考えているのですが、その考えは正しいでしょうか。
また、AD変換において上述のように計算を行うのは一般的、よく使われる手法なのでしょうか。
ご回答、よろしくお願いします。
チョコです。 これはIIR(無限インパルス応答)でしょう。移動平均とともにノイズ対策で使います。私はよく4回分の移動平均で、初期値は0ではなく、最初の変換結果を埋めていったものを使います。 C-First で勉強されているのですか。このプログラミングは、基本的にArduino互換の使い方です。RL78では、殆ど使われてこなかった方法です。 Arduinoは基本的にシングルタスクでのプログラミングです。プログラミングは簡単ですが、リアルタイム制御には少し難があります。それに対して、RL78では所謂ベアメタル型のプログラミングが中心です。複数のリソース(ハードウェアで準備された割り込み)をトリガにして必要な処理を行うことで、効率的な処理が可能です(RTOSを用いたマルチタスクよりは自由度は低いですが、限られたリソースの範囲でよりリアルタイムの処理が可能です)。 少し話がずれますが、RL78IOlib.cのA/Dの処理には問題があります。A/Dの初期化処理のiniadconv関数で、ADCEN = 1;の前にADM0を設定(しかも無意味な設定ですが)しています。また、ADCE = 1;の直後にADCS = 1;としていますが、これはおかしな処理です(G14のマニュアルでは、「注2. ソフトウエア・トリガ・モード時およびハードウエア・トリガ・ノーウエイト・モード時,A/D電圧コンパレータはADCS ビットとADCEビットで動作制御され,動作開始から安定するまでに,1 μsかかります。このため,ADCEビットに1を設定してから1 μs以上経過したあとに,ADCSビットに1を設定することで,最初の変換データより有効となります。1 μs以上ウエイトしないでADCSビットに1を設定した場合は,最初の変換データを無視してください。」と記載されています)。これを守っていないので、A/D変換の1回目を読み飛ばす処理が必須になっています。A/D変換の起動はiv_timerで行うので、初期化で変換を起動する必要はないはずです。 また、iv_timerは133us間隔であり、A/D変換は1チャネルで38usかかります。つまり、4チャネル目の変換中にiv_timerが起動して変換開始をトリガしているのは???です。 言い出すと、際限がないので、ここまでにしておきます。 以上
ありがとうございます。 ちゃんと見た上でやってはいるのですが、14.7.1 のソフトウェア・トリガ・モード設定を見てやってしまったので、一回目の変換を捨てるというのが抜けていました。