104.A/D変換機能を使う


2001/03/21 【ソフトウエア編TOPに戻る】

104−1.A/D変換とは

A/D変換とは、アナログ/デジタル変換のことです。普通コンピュータでは、1か0か、有るか無いか、といったデジタル情報しか取り扱うことができず、連続的な中間量を含むアナログデータを扱うのは苦手です。そこで、A/D変換回路なるものを使って、入力されたアナログ量を、最も近いデジタル量にあてはめて取り込みます。

AD変換の概念

アナログ量は通常、連続的な変化をしますので、変換するデジタル量のbitが多いほうが、より細かく連続的な変化を再現することができます。また同様に、より早い時間周期でデジタル量に変換していったほうが、連続した変化の再現性が高くなります。

AKI-H8/3048F CPUボードでは、入力されたアナログ量を10bitのデジタル量に変換できるので、アナログ基準電圧を0〜1023のステップで表現できることになります。アナログ基準電圧を+5Vとした場合、約4.9mV刻みで変換できます。


104−2.スキャンモードによるA/D変換

AKI-H8/3048F CPUボードには、8チャンネル分のアナログ入力ポートがあります。そして、各アナログ入力ごとにデジタル変換を行なうことができますが、さらに、4チャンネル分を常に繰り返してデジタル変換し続けてくれるスキャンモードがあります。

TekuRobo工作室では、主に複数の関節の角度を常に監視するために使用したいので、スキャンモードを使ってみたいと思います。

で、下のプログラムでは、アナログ入力AN0〜AN3の4チャンネル分をスキャンモードで取り込み、ポート4のスイッチSW1〜4を押すことによって、ポート1の8bitのLEDに、取り込んだデータを表示させます。

ソースプログラムは、ここをクリックするとダウンロードできます。(adtest1.c)

また、ハードウエアについては、電子回路編 3.AKI-H8開発キットの回路の[15]A/D入力接続コネクタおよび、[12]PORT1 LEDランプ 8bitを参照して下さい。

/**************************************************
A/Dテスト                        RIKIYA 2001.03.20 
                                         adtest1.C 
H8-3048/F 
4CHのアナログ入力をSCAN MODEでA/D変換しLED表示する。
sw1〜4で4chの入力表示を切り替える。 
/**************************************************/

#include <3048f.h>

/* メイン関数 *****************************************/

void main(void){
int ad_data;


     P1.DDR = 0xff;                         /*port1出力に設定 表示LED      */
     P4.DDR = 0x00;                         /*port4入力に設定 操作用sw1〜4 */
     P4.PCR.BYTE = 0xff;                    /*port4プルアップon               */

     AD.CSR.BYTE = 0x33;                    /* SCAN MODE / CH0〜3         */
     while(AD.CSR.BIT.ADF == 0){}           /* 4CH分の変換終了を待つ       */
     while(1){
          if(P4.DR.BIT.B4 == 0){            /* sw1の時 */
               ad_data = AD.DRA>>11;
          }
          else if(P4.DR.BIT.B5 == 0){       /* sw2の時 */
               ad_data = AD.DRB>>11;
          }
          else if(P4.DR.BIT.B6 == 0){       /* sw3の時 */
               ad_data = AD.DRC>>11;
          }
          else if(P4.DR.BIT.B7 == 0){       /* sw4の時 */
               ad_data = AD.DRD>>11;
          }
          P1.DR.BYTE = ad_data & 0x1F;      /*上位5bit分だけ表示させる     */
     }
}

【先頭に戻る】


104−3.プログラムの説明

このプログラムは、特に関数を分けたりしていません。全てmain( )関数だけで処理しています。

■3048f.hのヘッダファイルを読み込む

#include <3048f.h>

 

■変数の宣言

int ad_data;

先ず、int型のad_dataという変数を宣言しています。これは、A/D変換によって取り込んだデータを処理するための変数です。

 

■PIOの初期化

    P1.DDR = 0xff;             /*port1出力に設定 表示LED     */
    P4.DDR = 0x00;             /*port4入力に設定 操作用sw1〜4*/
    P4.PCR.BYTE = 0xff;        /*port4プルアップon              */

ここでは、ポート1をLED表示用に出力ポートとして設定し、ポート4をスイッチ入力用として入力ポートに設定しています。ポート4のプルアップは全てONの設定です。

 

■A/D変換機能の設定

    AD.CSR.BYTE = 0x33;      /* SCAN MODE / CH0〜3 */

ここではA/D変換の設定をしています。ADCSRとはADコントロール/ステータスレジスタのことで、以下の意味があります。

7 6 5 4 3 2 1 0
ADF ADIE ADST SCAN CKS CH2 CH1 CH0

 

ADF(A/Dエンドフラグ)は、A/D変換が終了したことを示すフラグです。スキャンモードでは、4チャンネル全てのA/D変換が終了した時にになります。

 

ADIE(A/Dインタラプトイネーブル)は、A/D変換の終了で割込み処理を発生させるかどうかを設定します。今回は割込みを使用しないので0にします。

 

ADST(A/Dスタート)は、A/D変換の開始/停止を設定します。0で停止、1で開始です。今回は始から1に設定してしまいます。

 

SCAN(スキャンモード)は、スキャンモードとしてA/D変換をするか、指定チャンネルを単一モードでA/D変換するかを指定します。今回はスキャンモードなので1に設定します。

 

CKS(クロックセレクト)は、A/D変換時間の設定を行ないます。0で266ステート、1で134ステートの時間になるそうです。私の勉強不足で、このステートが具体的にどれくらいの時間になるのか、いまいち良く分かりません。すみません。初期値が0なので、今回は0にしておきます。

 

CH2,1,0には、以下の意味があります。

グループ選択 チャンネル選択 説 明
CH2 CH1 CH0 単一モード スキャンモード
0 0 0 AN0 AN0
0 1 AN1 AN0,1
1 0 AN2 AN0,1,2
1 1 AN3 AN0,1,2,3
1 0 0 AN4 AN4
0 1 AN5 AN4,5
1 0 AN6 AN4,5,6
1 1 AN7 AN4,5,6,7

今回はAN0,1,2,3の4チャンネル分をスキャンするため、011となります。

 

これらの結果をADCSRに当てはめると以下のようになります。

7 6 5 4 3 2 1 0
ADF ADIE ADST SCAN CKS CH2 CH1 CH0
0 0 1 1 0 0 1 1

これを16進数にすると33hになるので、AD.CSR.BYTE = 0x33; ということになります。

 

■一発目のA/D変換終了を待つ

while(AD.CSR.BIT.ADF == 0){} /* 4CH分の変換終了を待つ */

A/D変換が開始されてから、一番初めに4チャンネル全てのデータが出揃うまで待っています。上記の命令文は、「CSRレジスタのADFビットが0(A/D変換中)の間は、待っていなさい。」という意味になります。

 

■押されたスイッチに相当するチャンネルのデータを取得する

         if(P4.DR.BIT.B4 == 0){            /* sw1の時 */
               ad_data = AD.DRA>>11;
          }

P4.DR.BIT.B4 == 0 は、「sw1が押されている時」という条件文で、この条件が満たされる場合に、ad_data = AD.DRA>>11; を実行します。AD.DRAとは、AチャンネルのA/D変換データが収められているレジスタで、以下の16bitで構成されます。

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
AD9 AD8 AD7 AD6 AD5 AD4 AD3 AD2 AD1 AD0 - - - - - -

レジスタは16bitですが、A/D変換されたデジタルデータは10bitなので、下位6bitは無効なデータとなり、上位10bitに必要なデータが格納されます。

>>11 とはbit演算子で、指定のbit数だけ右(下位)にシフトしなさいという意味です。この場合、AD.DRAのデータを11bit下位にシフトし、ad_dataという変数に入れなさいという意味になります。上のAD.DRAレジスタの内容を11bit分だけ下位にシフトすると、以下のようになります。

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
- - - - - - - - - - - AD9 AD8 AD7 AD6 AD5

つまり、ad_data変数には、AD.DRAレジスタの上位5bit分だけが下位側に格納されます。

なぜこのようなことをするかと言うと、10bitの精度(0〜1023)のデータを扱うのは大変だし、そんなに細かな精度が必要ないからです。とりあえずは5bit分(0〜31)の32段階もあれば十分かなあ、といったところで決めています。

以降のelse if文も、違うチャンネルで同様の処理をおこなっています。今回のスキャンモードでは、AD.DRA , AD.DRB , AD.DRC , AD.DRD の4チャンネル分について、A/D変換データを取り出している様子がお分かり頂けると思います。

 

■取り出したA/D変換データをLEDに表示させる

         P1.DR.BYTE = ad_data & 0x1F; /*上位5bit分だけ表示させる */

ad_dataに1Fh(0001 1111)ANDしています。これは、下位5bit分だけをちゃんと取り出し、上位3bit分にマスクを掛ける処理です。>>11によってシフトされたあと、変なゴミなどが上位bitに混じってしまってもいいようにしています。実際はゴミが混じることは無いと思いますが、気持ち悪いので強制的に0にしています。

で、P1.DR.BYTEに上位3bitをマスクしたad_dataを出力し、LED表示させています。

【先頭に戻る】


104−4.実際の動作

スイッチ チャンネル
SW1 AN0
SW2 AN1
SW3 AN2
SW4 AN3

という対応で、各チャンネルのA/D変換データを8bit分のLEDに表示させることが出来ます。スイッチを押してすぐ手を離すと、押された時点でのデータだけを表示し続けますが、スイッチを押したままアナログ電圧(ボリューム)を変化させると、変化の様子がbit表示されます。

実験の様子 左の写真は、ボリュームを回しきって、アナログ基準電圧である5V分をめい一杯入力した時のLED表示です。スイッチを押しながらボリュームを回すと、データが順に増減する様子が良く分かります。

ちなみに、今回使用するスイッチは、写真上のディップスイッチではなく、写真外右上に位置する押しボタンスイッチなので、間違えないようにして下さい。

 

【ソフトウエア編TOPに戻る】


【表紙に戻る】