D/A変換器・A/D変換器

この節では、ディジタルシステムであるCPUボードが、 例えば2.1Vというようなアナログ値の電圧を扱うための デバイスとその扱い方をみていくことにしましょう。

I/Oポート

このボードに載っているCPUには、メモリやメモリマップドI/Oに対する アクセス以外にも、1ビットのディジタル値(0または1)に応じた 電圧(1=高い電圧3V、0=低い電圧0V)を出力するための I/Oポートという端子があります。 このEZ-USB FX2にはポートA(PA)からポートE(PE)までの5種類の I/Oポートがあり、それぞれのポートは8本(8ビット)の0または1の値を 出力、あるいは入力できるピンから成り立っています。 もちろんその出力したい値、または入力した値は、 プログラムで設定したり、読み出したりすることができます。 ここでは、ポートE(PE)を使ってみることにしましょう。

I/Oポートを使用するときには、プログラムの最初に、 各ピンを入力あるいは出力のどちらとして使用するかを 設定する必要があります。 この設定はOEE(Output Enable E)という特殊な変数(実際には メモリマップドされた1バイト変数)に値を書き込むことで行います。 具体的には、出力として使用したいピンの桁を1、 入力として使用したいピンの桁を0とした8ビットの2進数(1バイト)の 値を設定します。

演習

ポートE(PE)の0番(PE0)、PE1、PE6、PE7だけを出力、 残りのPE2, PE3, PE4, PE5を入力として使うときに OEEに書き込むべき値はいくつになるでしょうか。

I/Oポートの操作

I/Oポートの各ピンに0または1の値を出力したり、 入力ピンの値を読み取るためには、IOEという特殊な変数を用います。

例えばPE0が出力として設定されているとして、 IOEの0ビット目を1にすると、PE0の値が1(つまり高い電圧3V)となります。

逆にPE3が入力として設定されているとすると、 IOEの3ビット目を読み取ると、PE3に加えられている電圧が 高い(3V=1)か低い(0V=0)かを知ることができます。

ビット演算・シフト

このように特にI/Oポートを操作するときには、特定のビットのみを 1または0にしたり、特定のビットの値のみを知る必要が生じます。 このようなときには、 ビット演算 と呼ばれる方法が便利です。

ビット操作は、AND演算子(&)やOR演算子(|)や排他的論理和演算子(^)や NOT演算子(~)を用いて 論理演算を行います。 AND演算子(&)は、各ビットに対して論理積を計算し、 それらを並べた値を求めます。 例えば、0xaf (2進数では10101111) と0xf0 (同11110000) の 論理積「0xaf & 0xf0」を求めると、

	10101111
	   AND
	11110000
	    =
	10100000
より、結果は0xa0となります。 同様に、「|」や「^」や「~」も 各ビットに対して規定の論理演算を行います。

変数の中の特定のビットのみを操作する際には、 例えば次のような記述によって行うことができます。
変数aの0ビット目(最下位ビット)のみを1にする a = a | 0x01;
変数aの2ビット目のみを1に変更する a = a | 0x04;
変数aの4ビット目のみを0に変更する a = a & ~0x10;
変数aの6ビット目が0であるかを調べる (a & 0x40)==0

また数ビットだけ左または右にシフトするための 演算子として、「<<」と「>>」があります。 例えば「a >> 1」は、変数aの値を1ビットだけ右にシフトした 値をあらわすことになります。 したがって例えば a = 0x32; であれば、 「a >> 1」は0x19となることになります。 なお、「a >> 1」自体は、変数aの値を1ビット右にシフトした値を計算しますが、 変数aに格納されている値自体は変化させないことに注意してください。

演習

これらのビット演算・シフト演算の内容を理解しましょう。 ビット演算やシフト演算を行った結果をLEDに表示させるプログラムを 作成、実行してみると良いでしょう。

ヒント:

演習

unsigned char型変数 (8ビット) の値を調べ、 上位ビットから順に0であるか1であるかを LEDに表示するプログラムを作りましょう。

ヒント:

注意:

D/A変換器のいじり方

アナログ値の電圧を出力するための装置を D/A変換器 (Digital-to-Analog Converter)と呼びます。 このボードには、TLV5626という型番のD/A変換器が載っていますので これを扱うことにしましょう。

このD/A変換器の扱い方を知るためには、 D/A変換器TLV5626のデータシートを 読むことになります。 要点は、次のようなことになります。

p.6の図1を見ると、D/A変換器TLV5626に与えるデータは 1ビットずつ、D15からD0までの16個を、この順に SCLKに変化にあわせて与えること、そして最初に/CSを0にし、 最後に/CSを1にすること、を読み取ることができます。 (図の読み方)
p.6の図1の解説
具体的には、
  1. /CS=1, SCLK=1の状態から始める
  2. まず/CS=0とする
  3. 最初のデータ(ビット)をDINに設定し、SCLK=0とする
  4. 少し待つ
  5. SCLK=1とする
  6. 少し待つ
  7. 3.〜6.を合計16回繰り返す
  8. /CS=1とする

つまりD/A変換器TLV5626へのデータの転送は16ビット(2バイト)を 単位として行うことになります。

そして送るべきデータは、 次の2組の16ビット(2バイト)となることが記述されています。

(この動作はD/A変換器の設計者が決めた「仕様」です。) ここで「出力値」とは、D/A変換器から出力したい電圧を示す数値で、 電圧と一対一の関係があります。

この「出力値」を、上位4ビットと下位4ビットにわけ、 D/A変換器に送ることになるわけですが、 例えば値0x12を送る場合には、2回目の転送で 次のような2バイトを送ることになります。

演習

出力値0x12をD/A変換器に送るとき、 送るべき2バイトのデータが上記のようになることを理解しましょう。 また、出力値0x34を送る時は、 どのような2バイトを送ることになるのかを考えましょう。

ヒント:

D/A変換器への値の転送プログラム

D/A変換器を操作する、次のような3つの関数があった仮定をしましょう。 この3つの関数を使うと、D/A変換器から出力値d(d=0x00〜0xff)の電圧を出力する ためには、次のようなプログラムを記述すればよいことになります。 (わかりやすいように日本語でコメントを書いてありますが、 実際のエディタでは日本語は文字化けします。 コメントを英語にしたものもあります。 )
void DACwrite(unsigned char d)
{
  /* 内部参照電圧を設定するおまじない */
  CS_DAC(1); CK(1); /* 初期状態 */
  CS_DAC(0);
  DACwrite_byte(0xd0); DACwrite_byte(0x02);
  CS_DAC(1);

  /* 出力値dに対応する電圧を発生させる */
  CS_DAC(0);
  DACwrite_byte(0xc0 | (d >> 4));
  DACwrite_byte(d << 4);
  CS_DAC(1);
}
(コメントを英語にしたコピー用)

なお「>>」は、指定したビット分だけ右にシフトする演算子で、 例えば"0x53 >> 4"は0x05となります。 同様に「<<」は、指定したビット分だけ左にシフトする演算子です。

演習

このプログラムの動作を解読して、 D/A変換器から出力値dに対応した出力電圧が得られることを理解しましょう。

ヒント:


D/A変換器の操作

先ほどのI/Oポート操作とビット演算を用いると、 D/A変換器TLV5626を操作するための、最低限の関数を 記述することができます。 D/A変換器TLV5626は、最終的には、SCLK, DIN, /CSという3本の信号線で 制御されますが、 これらは、次のような対応で、ポートEの各ピンに接続されています。
CPUとD/A変換器の接続
そこで、これらの値を0(低い電圧0V)または1(高い電圧3V)とするための 関数を次のように記述しておくと便利でしょう。
/* CK(d): D/A, A/D変換器のSCLKをd (d=0 or 1)に設定する関数 */
void CK(char d)    {if (d == 1) IOE |= 0x01; else IOE &= ~0x01;}

/* CS_DAC(d): D/A変換器の/CSをd (d=0 or 1)に設定する関数 */
void CS_DAC(char d){if (d == 1) IOE |= 0x04; else IOE &= ~0x04;}

/* DO_DAC(d): D/A変換器のDINをd (d=0 or 1)に設定する関数 */
void DO_DAC(char d){if (d == 1) IOE |= 0x08; else IOE &= ~0x08;}
(コメントを英語にしたコピー用)

演習

これらの関数・ビット演算子などを活用し、 D/A変換器に出力値dに対応した電圧を出力するための 関数void DACwrite(unsigned char d) が動作するようにプログラムを記述しましょう。 具体的には、 関数void DACwrite_byte(unsigned char d) の中身を作成します。 CK(), CS_DAC(), DO_DAC()は前述の通りです。

関数DACwrite()に適当な出力値を与え、 出力値に応じて電圧が変化することを確認しましょう。 D/A変換器から出力される電圧は、ボード上の「Aout」端子に現れます。 テスターを用いて、 こことGND(グランド=基準電圧)との間の電圧を計測し、 数値に応じて電圧値が変化することを確認しましょう。 テスター接続用ケーブルを使用すると便利です。

ヒント:

注意:

(第1日目はこのあたりまでを目安とするとよいでしょう。 もっと先まで進んでおくと第2日目が楽になります。)

演習

D/A変換器に出力する電圧をスイッチで制御でき、 またそのときの出力値dを7セグメントLEDに数値として表示するプログラムを記述し、 実行させてみましょう。 出力値と電圧の関係を記録し、グラフにしましょう。 出力値を電圧に変換する計算式を求めましょう。

ヒント:

A/D変換器

アナログ値の電圧を、ディジタル値に変換し、コンピュータに 取り込むための装置をA/D変換器 (Analog-to-Digital Converter)と呼びます。 このボードには、TLC548という型番のA/D変換器が載っていますので これを扱うことにしましょう。 このA/D変換器は、 与えられたアナログ電圧を1バイト (8bit) のディジタル値に変換することができます。 このA/D変換器の扱い方を知るためには、 A/D変換器TLC548のデータシートを 読むことになりますが、要点は以下のとおりです。 p.3の図を見ると、A/D変換器TLC548に与えるデータは、 /CS=1, CLOCK=0の状態から始めて、 まず/CS=0とした後にクロックCLOCKを0→1→0の変化を8回与え、 その後/CS=1とします。 そしてしばらく待ち、 再び/CS=0とした後でCLOCKの0→1→0の変化を8回与えながら、 A/D変換器から出力されてくるDATAOUTを1ビットずつ読み取り、 最後に/CS=1とすることになります。 このA/D変換器がディジタル値に変換した値は1バイト(8ビット)の 数値で、DATAOUTに最上位ビット(B7)から最下位ビット(B0)の順に 現れることがわかります。
p.3の図の解説
これらの3つの信号線は、ポートE(PE)に以下のように接続されています。 CLOCKが接続されているPE0にはD/A変換器のSCLKも接続されているので、 PE0によってD/A変換器とA/D変換器のクロックが同時に変化します。 このうちDATAOUTが接続されているPE4だけは、 A/D変換器から出力される値を読み取る必要がありますから、 「入力」に設定する必要がありますが、 その他のPE0, PE1, PE2, PE3は「出力」と設定することにしましょう。

なお/CS=0の期間にCLOCKを8回与える箇所が2回ありますが、 この1回目でアナログ電圧をディジタル値に変換し、 その結果を2回目に読み出しているためです。 そのため必ず2回必要です。

演習

PEの各ピンの入出力を上記のように設定するためには、 OEEにどのような値を設定すればよいでしょうか。

A/D変換器の操作

上記のことをふまえ、D/A変換器およびA/D変換器を操作するために 最低限必要な5本の信号線の値を0(低い電圧0V)または1(高い電圧3V)に 設定したり、A/D変換器から出力される値を読み取るための 関数を、次のように定義しておくことにしましょう。
/* CK(d): D/A, A/D変換器のSCLKをd (d=0 or 1)に設定する関数 */
void CK(char d)    {if (d == 1) IOE |= 0x01; else IOE &= ~0x01;}

/* CS_DAC(d): D/A変換器の/CSをd (d=0 or 1)に設定する関数 */
void CS_DAC(char d){if (d == 1) IOE |= 0x04; else IOE &= ~0x04;}

/* DO_DAC(d): D/A変換器のDINをd (d=0 or 1)に設定する関数 */
void DO_DAC(char d){if (d == 1) IOE |= 0x08; else IOE &= ~0x08;}

/* ここからが追加分 */
/* CS_ADC(d): A/D変換器の/CSをd (d=0 or 1)に設定する関数 */
void CS_ADC(char d){if (d == 1) IOE |= 0x02; else IOE &= ~0x02;}

/* DI_ADC(): A/D変換器のDATAOUTを読み取る関数 */
char DI_ADC(){if ((IOE & 0x10) == 0) return(0); else return(1);}
(コメントを英語にしたコピー用)

CS_ADC(d)とDI_ADC()が追加されています。 残りの関数はD/A変換とまったく同じです。

演習

これらの関数・ビット演算子などを活用し、 A/D変換器に与えられているアナログ電圧を 1バイトのディジタル値に変換した値を返す関数 unsigned char ADCread(void)を記述しましょう。 続いて、A/D変換器の入力に、先ほどのD/A変換器の出力を与え、 D/A変換器から適当なアナログ電圧を出力し、それをA/D変換器で 取り込んで、その値を7セグメントLEDなどに表示させましょう。 アナログ電圧とA/D変換結果を記録して、これらの対応関係を調べましょう。 グラフを作成し、 ADCread()の結果をアナログ電圧に変換する式を求めます。

なおこのとき、D/A変換器の出力(Aout)とA/D変換器の入力(Ain)を 接続することになりますが、 この接続では テスター接続用ケーブル を用います。 テスター接続用ケーブルは、AoutとAinを接続し、 かつ、テスターでAoutの電圧を測定できるようになっています。

ヒント:

注意:

インバータの入出力電圧特性の計測

A/D変換器・D/A変換器を活用し、ディジタル論理ICである SN7404の入出力特性を計測してみましょう。

SN7404はインバータ(否定ゲート)が6個入ったICです。

このボードのVDDを電源電圧(+5V)、GNDをグランド(0V)に接続し、 INに与える電圧を変えたときにOUTに現れる電圧の 関係を調べてみることにします。

インバータでは、入力が0(低い電圧)のときには出力が1(高い電圧)、 入力が1(高い電圧)のときには出力が0(低い電圧)となるはずですので、 おおまかには次のようなグラフとなると考えられます (図の横軸はかなりいい加減です)。
インバータの入出力特性
そこでD/A変換器の出力である「Aout」をインバータのINに、 またインバータの出力をA/D変換器の入力である「Ain」に、 ケーブルを用いて接続します。

演習

D/A変換器に出力する電圧をスイッチによって制御し、 D/A変換器に出力した電圧と A/D変換器で読み取った電圧を7セグメントLEDに表示する プログラムを作りましょう。 このプログラムを用いて、インバータSN7404の入出力特性を計測してみましょう。 マイコンとインバータSN7404は、フラットケーブルで接続します。

ヒント:

注意:


Index Prev: マイコンのプログラミング Next: 自動測定