【LPCマイコン】ADCを使う


今回の記事はLPCを用いてA/D変換を行う方法です。
 

AD変換の基礎

AD変換とは

ところでAD変換とは一体なんでしょうか?それは、その名前の示す通りAnalogからDigitalへと情報を変換する技術です。
身の回りにある様々な情報、例えば温度や湿度、電圧など自然界に存在する物理量は全てアナログ量、すなわち連続的に変化する情報です。しかし、コンピュータは’0’と’1’のビット列からなる値しか持つことができません。したがって、デジタルなデータは全て離散的にしか変化できない情報です。
AD変換では連続量をある一定のルールに従って離散量へと変換を行います。
 
 

サンプリング定理

アナログ信号を正しくデジタル信号に変換するには、変換対象の信号に含まれる信号とサンプリング周波数の間で満たさなければならない条件が存在します。これをサンプリング定理と言います。具体的には、サンプリング周波数を\(F_s\)、対象信号に含まれる最高の周波数を\(F_{max}\)とした時、次の関係が成り立つならばサンプリング定理を満たしていると言います。
$$F_s > 2F_{max}$$
対象信号を周波数領域で表現した時、そのスペクトルは以下のようになります。
これは、周波数領域に落とし込んだ時に2倍の帯域幅を持って現れるということになります。また、\( F_s\)ごとにサンプリングしているので実際にはこのスペクトルが一つではなく周期的に現れることになります。

ということは、サンプリング定理を満たしていない時にはどうなってしまうでしょう。
実は、エイリアシングといって実際には対象信号に含まれていない信号が変換結果に現れてしまいます。これは、隣り合ったスペクトル同士が互いに干渉してしまうことが原因です。

 
逆に、サンプリング周波数をむやみやたらに高くしたところで性能が良くなるというわけでもありません。上の図を見たら、スペクトル同士がただただ離れていくだけというのはすぐにわかることでしょう。
サンプリング定理は、変換したものを再度元に戻す際に波形が復元できるための条件です。つまり、サンプリング定理さえ満たしていればサンプリング周波数は高くても低くても変わらないということになります。なので、サンプリング周波数は定理を満たした上でできる限り低めに設定することになります。
 

帯域制限

先ほど述べたサンプリング定理を確実に満たすために、対象の信号にLPFをいれてあげると良いでしょう。最も簡単なLPFはRCを用いたパッシブLPFです。これを信号源とマイコンの間に挿入してやります。
LPFのカットオフ周波数を\(f_c\)とした時、その周波数は抵抗RとキャパシタンスCを用いて以下のように表されます。
$$f_c = \frac{1}{2\pi\sqrt{RC}}[Hz]$$
この周波数を超える信号はフィルタにカットされてAD変換の対象にはなりません。
正確にはこれは理想LPFの動作であり実際のLPFでは-20[dB/dec](周波数10倍で振幅1/10)となるので少し入ってきます。
 

標本化と量子化、符号化

アナログ量からデジタル量に変換する際には、標本化→量子化→符号化という手順を踏みます。
ここでは標本化の方法についてはここでは詳しくは触れませんが、あるタイミングでの値を取り出す動作とでも言っておきましょうか。この取り出した値に対して変換をかけていきます。
AD変換で重要なのは量子化になります。量子化とは、標本値に対してある基準値と比較することで行われます。この回数が変換の分解能に影響してきます。ということはもちろん回数が多いほど分解能が細かいということになります。
比較回数をそのまま量子化ビット数\(N\)と呼びます。また、量子化する際には最大値と最小値の間で比較されます。この範囲を\(V\)とすると、分解能は
$$\frac{V}{2^N}$$
になります。
そのため、測定レンジが狭いほど分解能は細かくなります。また、信号の振幅は測定範囲めいっぱい使うようにしましょう。
この図の場合だと比較回数は3回です。なので、幅の1/8までの分解能で測定することができます。
量子化が終わったら次は符号化処理をします。符号化処理では、量子化したものをあるルールにしたがってビット列に変換します。例えばこんな感じです。

回数 1回目 2回目 3回目
基準値に対して
符号 1 1 0

この場合、比較結果が基準値よりも高ければ’1’、低ければ’0’を割り当てています。多分大抵のマイコンはこのルールで符号化を行なっていると思います。
 

LPCでのAD変換

回路図

今回、LPC1549でAD変換をするにあたって使用する入力信号は可変抵抗を利用して作り出します。
この回路ではAD変換のレンジは電源電圧と同じとなっていますが、場合によってはツェナーダイオード等でそれ以下の基準電圧を作り出してやることも可能です。

あ、当然だけどこれの他にも電源とかは必要だからね?
 

モジュール

LPC1549のAD変換器は2つのモジュールを持っています。一つのモジュールには複数のアナログピンが接続されており、これを切り替えることで入力信号を選択します。
サンプリング周波数の設定や変換値の補正はこのモジュールごとに行われます。
また、AD変換をした後の結果もモジュールごとにまとまった配列に格納されています。
それぞれのモジュールは、LPC_ADC0LPC_ADC1というシンボリックリンクが設定されているので素直にこれを使用することにしましょう。
 

シーケンサ

LPCのAD変換器はシーケンサという概念を持っています。シーケンサに対してどのピンからの入力を変換するかという設定を行います。
PICやAVRと異なる点は、シーケンサには複数のピンが登録され、これらが一括で変換されるということでしょうか。(もしかしたら上位のPICとかは違うかもですが)
また、一つのモジュールには二つのシーケンサが備わっています。そのため、モジュールごとに二種類の変換設定ができるというわけです。
モジュールごとに二種類の設定ができるということは、あるピンは繰り返しで変換しながら他のピンは任意のタイミングで変換できるということです。するかどうかは別ですが。
それぞれのシーケンサはADC_SEQA_IDXADC_SEQB_IDXとしてシンボリックリンクが貼られている。
 

動作モード

このマイコンでのAD変換には複数の動作モードがあります。よく使われるのが以下の3つと言ったところでしょうか…

  • 単発変換(割り込みなし)(Single Conversion)
  • 単発変換(割り込みあり)(Single Conversion)
  • 自動リピート変換(Burst Conversion)

特に、割り込みは変換ごとに割り込むパターンシーケンス終了ごとに割り込むパターンに分けられます。が、シーケンサでまとめて変換しているのでシーケンス単位で割り込むのが一般的じゃないでしょうか。
割り込みを発生させるかどうか、というのはシーケンサの設定時に行います。Burstモードでも割り込みを行うことはできるのですが、とある記事によるとBurstと割り込みを併用すると変換速度が遅くなってしまうらしいです。僕自身で検証したわけではありませんが、この記事を信じてみることにします。
 

Input MUXの設定

LPCのGPIOはInputMUXによって接続されていますが、これの設定によってはAD変換に支障をきたしてしまう場合があります。
具体的には内部プルアップが働いている場合です。デフォルトの設定では内部プルアップが有効だったはずなので、これでは可変抵抗とプルアップ抵抗が並列に入ってしまいます。
また、デフォルトではGPIOはデジタルピンとして機能するのでこれも設定を変更して上げる必要があります。
この設定は

という関数を使用して行うことができます。このmodefuncに対してMUXの動作モードを指定します。
内部プルアップを無効にする設定はIOCON_MODE_INACTです。また、アナログモードとして動かすためにIOCON_ADMODE_ENも同時に指定してやる必要があります。
上の回路ではPIO0_13を使用しているので、全体としては

としてやります。ここで注意が必要なのが、MUXの動作モードはOR結合で指定する必要があるということです。
 

Switch Matrixの設定

こないだどっかの記事でも述べたんですけど、LPC1549にはSwitch Matrixという機能が備わっています。これは内部回路とピンの割り当てを動的に切り替えることのできる機能です。とは言ってもAD変換の機能が割り当てられているピンは決まっているのですが。これらはFixed Functionと呼ばれています
ですが、通常ではAD変換器は無効化されています。そこで機能を有効化してやる必要があります。Fixed Functionの切り替えもSwitch Matrixを介して行われます。以下の関数を使用して有効化してあげましょう。

今回はADC1_6を使用するので、

となります。
ここまできたらAD変換を行うためのピンの設定は終了です。
 

モジュールとシーケンサの設定

AD変換器を使用するにあたって、まずはモジュールへとクロックを供給してあげる必要があります。クロックの供給は

によって行われます。
今回、使用するモジュールはADC1なので、この関数を使用して

としてあげましょう。flagsはクロックと非同期にしたり、量子化ビット数を10bitにしたりするためのものなので今回は使用しません。
 
次に、サンプリング周波数の設定です。サンプリング周波数は50[MHz]を超えない範囲で任意に設定することができます。これの設定には以下の関数を使ってあげましょう。

例えば、10[kHz]にする場合には以下のようにします。

 
モジュールに対する設定はこれで終了です。次にシーケンサの設定をしましょう。
シーケンサの設定は

によって行われます。
まず、アナログ信号の入力ピンを登録してあげましょう。ピン指定は、ADC_SEQ_CTRL_CHANSEL(ch)マクロを使用して行うとわかりやすいのではないだろうか。
今回使用するピンはなんども述べるがADC1_6なので、

となる。シーケンサはAを使用している。
次に、動作モードを指定しよう。例をいくつかあげるのでそれぞれで試してみて欲しい。
単発で変換し、割り込みを使用しない場合は動作モードの指定は特に必要ない。割り込みを使用する場合は次のようにする。

Burstモードを使用するには以下のようにする。

シーケンサの設定は以上。あとはシーケンサを有効化してやるだけです。それには以下の関数を使用しましょう。

今回はADC1を使用しているので具体的にはこうしてやれば良い。

ここまできたらあとは変換と値の読み出しだけですね。
 

割り込み処理

ADCの割り込みハンドラは色々なものが定義されていますが、今回は使用しているモジュールがADC1、シーケンサがAなのでハンドラは以下のものが使用されます。

なので、実行したい処理はこの中で行うことにしましょう。
 
割り込んだらまずはフラグを沈めなければいけません。フラグを削除するには以下の関数を使用してあげます。

こいつに対して、シーケンサAの割り込みフラグのマスクであるADC_FLAGS_SEQA_INT_MASKを指定してあげればいいわけですね。
つまりこういうことです。

はい、これで割り込みフラグを消すことができました。
他に割り込み中でやることとすれば値を取得することぐらいじゃないでしょうか。その方法については後述してあるのでそっちを参照してもらえると。
 

変換・読み出し

変換を開始するには、シーケンサをスタートしてあげるだけです。ただし、単発モードとBurstモードとでスタート方法が異なるので注意を。
まず、単発モードでは以下の関数を使用します。

Burstモードではこれです。

これらの関数のうち、必要な方をLPC_ADC1ADC_SEQA_IDXを指定して呼び出してあげましょう。
この関数を呼び出すことで変換はスタートします。
 
値の読み出しは以下の関数を用いて行われます。

ここで、indexはモジュール内のピン番号です。
また、データレジスタのうち全てが変換結果のデータではなく、そのほかのデータも混じっています。なのでビットシフトしたり他のビットを沈めたりしなければいけないのですが、そのためのADC_DR_RESULT (n)というマクロがきちんと用意されています。これも併用すると、以下のように値を読み出すことができます。

 
ここで読み出しのタイミングですが、通常は変換結果が有効化どうかを確認する方法をとります。無効である場合にはまだ変換が完了していないので待機します。もちろんそのビットを取り出すためのマクロもADC_DR_DONE(n)として用意されているのでそれを使用しましょう。
変換されるまで待機するコードは以下のようになります。

ただ、Burstモードの時はこの方法はあまり適切でないように思います。というのも、必ずしも変換サイクルと読み出しサイクルが一致するとは限らないからです。値を読みださなくても新たな変換結果はレジスタに格納されます。ただし、データオーバーランが発生してしまうことには注意です。なのでBurstモードの時には読み出しのタイミングはあまり気にしないことにしましょう。(最初の呼び出しだけは気にした方がいいかも?)
 
と、ここまでできればあとは自分の好きなようにコードを書いてみてください。
 

最後に

長々と書いてしまいましたがやってることは本当に単純です。多分adc_15xx.(hpp | cpp)を見ればわかるんじゃないかなぁと。。。
 
次回は多分UARTに関する記事を書くかと思うのでどうぞご期待ください。いや、期待しないでください。

あわせて読みたい

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です