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に表示させるプログラムを
作成、実行してみると良いでしょう。
ヒント:
- 理解できていればプログラムを組む必要はありません
- プログラムを組んで確認する場合には、例えば、
- 変数
a
にある値 (例えば0x40
) を代入
- 変数
a
の値をLEDに表示
- 1秒待つ (EZUSB_Delay)
- 変数
a
の0ビット目のみを1にする
- 変数
a
の値をLEDに表示
- 1秒待つ
- 変数
a
の2ビット目のみを1にする
- 変数
a
の値をLEDに表示
- 1秒待つ
- 変数
a
の値を1ビット左にシフトする
- 変数
a
の値をLEDに表示
- 1秒待つ
のようなプログラムを作ってみましょう
演習
unsigned char型変数 (8ビット) の値を調べ、
上位ビットから順に0であるか1であるかを
LEDに表示するプログラムを作りましょう。
ヒント:
- 以下のようなプログラムを作ります
- 変数の最上位ビットが1ならば右端のLEDが点灯、0ならば消灯
- 1秒待つ
- 次のビットが1ならば右端のLEDが点灯、0ならば消灯
- 1秒待つ
- 以下、同様に繰り返す
数値に応じて右端のLEDが点滅するプログラムになります。
- LEDの操作
注意:
D/A変換器のいじり方
アナログ値の電圧を出力するための装置を
D/A変換器 (Digital-to-Analog Converter)と呼びます。
このボードには、TLV5626という型番のD/A変換器が載っていますので
これを扱うことにしましょう。
このD/A変換器の扱い方を知るためには、
D/A変換器TLV5626のデータシートを
読むことになります。
要点は、次のようなことになります。
- SCLK、DIN、/CSという3本の信号線で制御される
- これらを、p.6のFigure1(図1)のように変化させて与える
p.6の図1を見ると、D/A変換器TLV5626に与えるデータは
1ビットずつ、D15からD0までの16個を、この順に
SCLKに変化にあわせて与えること、そして最初に/CSを0にし、
最後に/CSを1にすること、を読み取ることができます。
(図の読み方)
具体的には、
- /CS=1, SCLK=1の状態から始める
- まず/CS=0とする
- 最初のデータ(ビット)をDINに設定し、SCLK=0とする
- 少し待つ
- SCLK=1とする
- 少し待つ
- 3.〜6.を合計16回繰り返す
- /CS=1とする
つまりD/A変換器TLV5626へのデータの転送は16ビット(2バイト)を
単位として行うことになります。
そして送るべきデータは、
次の2組の16ビット(2バイト)となることが記述されています。
- 0xd0 と 0x02 の組 (動作:内部参照電圧を設定する)
- [0xc0と出力値の上位4ビットを下位4ビットにシフトした値の論理和] と [出力値の下位4ビットを上位4ビットにシフトした値] の組
(この動作はD/A変換器の設計者が決めた「仕様」です。)
ここで「出力値」とは、D/A変換器から出力したい電圧を示す数値で、
電圧と一対一の関係があります。
この「出力値」を、上位4ビットと下位4ビットにわけ、
D/A変換器に送ることになるわけですが、
例えば値0x12を送る場合には、2回目の転送で
次のような2バイトを送ることになります。
演習
出力値0x12をD/A変換器に送るとき、
送るべき2バイトのデータが上記のようになることを理解しましょう。
また、出力値0x34を送る時は、
どのような2バイトを送ることになるのかを考えましょう。
ヒント:
- 前半は「理解する」だけです
- 出力値が、どのように2バイトのデータに埋め込まれているかを考えます
- 1バイト目の上位4ビット (16進数の上位桁) 「0xc」は
おまじないと考えてかまいません
何故「0xc」になるのかを考える必要はありません。
D/A変換器への値の転送プログラム
D/A変換器を操作する、次のような3つの関数があった仮定をしましょう。
- CS_DAC(x) : D/A変換器の/CSの値をx(x=0 or 1)に設定する
- CK(x) : D/A, A/D変換器のSCLKの値をx(x=0 or 1)に設定する
- DACwrite_byte(x) : SCLKとDINを操作し、D/A変換器に1バイトの値xを
転送する
この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に対応した出力電圧が得られることを理解しましょう。
ヒント:
先ほどのI/Oポート操作とビット演算を用いると、
D/A変換器TLV5626を操作するための、最低限の関数を
記述することができます。
D/A変換器TLV5626は、最終的には、SCLK, DIN, /CSという3本の信号線で
制御されますが、
これらは、次のような対応で、ポートEの各ピンに接続されています。
- SCLK : PE0
- /CS : PE2
- DIN : PE3
そこで、これらの値を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(グランド=基準電圧)との間の電圧を計測し、
数値に応じて電圧値が変化することを確認しましょう。
テスター接続用ケーブルを使用すると便利です。
ヒント:
注意:
- プログラムに日本語は書けません
テキストのプログラムには、
わかりやすいように日本語でコメントが書いてあります。
そのままエディタに貼り付けると、文字化けします。
コメントを英語にしたコピー用
もあります。
- C言語におけるビット操作の
注意
- CK(), CS_DAC(),
DO_DAC()において、
引数 d をどのように扱っているのかに注意しましょう
if()の条件をよく見ましょう。
(第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のデータシートを
読むことになりますが、要点は以下のとおりです。
- CLOCK、DATAOUT、/CSという3本の信号線で制御される
- これらを、p.3の図のように変化させて与え、DATAOUTを読み取る
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)の順に
現れることがわかります。
これらの3つの信号線は、ポートE(PE)に以下のように接続されています。
- CLOCK : PE0
- /CS : PE1
- DATAOUT : PE4
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の電圧を測定できるようになっています。
ヒント:
- D/A変換のプログラムに追加していくと良いでしょう
関数unsigned char ADCread(void)を作ります。
8bitのディジタル値を求める関数です。
- ADCread()が求めた8bitのディジタル値 (0〜255)
を表示するプログラムにします
- 押しボタンスイッチでD/A変換器の出力値 (8bitのディジタル値) を変更する
プログラムを作ると良いでしょう
例えばD/A変換器の出力値が、
スイッチSW0を押すと1ずつ増加、SW1を押すと10ずつ増加、
SW2を押すと1ずつ減少、SW3を押すと10ずつ減少する、など
- A/D変換器とD/A変換器の電圧値を同時には表示できないので、
- A/D変換器の電圧値だけ表示する
(D/A変換器のアナログ出力電圧をテスターで読み取る)
- スイッチで表示を切替える
例えば、スイッチを押さない時はD/A変換器、
スイッチSW7を押した時はA/D変換器の電圧値を表示する
のようなプログラムを作ると良いでしょう
- 整数値を表示するプログラムを思い出そう
- スイッチを押せば数字が増加するプログラム
を思い出そう
- 引数を持たない関数
- べき乗
注意:
- 記録するのは関数「ADCread()」の戻り値とアナログ電圧の関係です
D/A変換器への設定値ではありません。
- A/D変換器とD/A変換器の特性は異なります
同じ特性・変換式になった場合は、何らかの間違いがあります。
インバータの入出力電圧特性の計測
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は、フラットケーブルで接続します。
ヒント:
- A/D変換のプログラムに追加していくと良いでしょう
スイッチで電圧値を変更するプログラムを作っていた場合には、
電圧をボルト表示する部分だけを追加すれば済みます
- 7セグメントLEDは4桁しかないので、
- 2桁をD/A変換器に出力した電圧、2桁をA/D変換器で読み取った電圧
- 4桁で電圧を表示、押しボタンスイッチでD/AとA/Dを切り替え
のいずれかのプログラムにすると良いでしょう
8bitのディジタル値から実際の電圧 (ボルト単位) に換算し、
電圧を整数部1桁、小数点以下1桁で表示します。
- 整数値を表示するプログラム
を思い出そう
2桁表示の場合は、4桁を2桁×2個に直せば良い。
- 最初は、テスターを接続して動作確認すると良いでしょう
D/A, A/D, テスターがほぼ同じ電圧を表示するようにします。
注意:
- 実験用CPUボードのCコンパイラでは実数型 (floatとdouble) を
使えません
- 実定数も使えません
1.0などの小数点を含む定数、1e-10などの指数を含む定数は
使えません
- 例えば、小数点以下1桁の実数値を扱いたい場合には、
10倍して整数演算すると良いでしょう
100倍や1000倍でもかまいません。
- 整数型のビット数や
代入できる数値の限界
に注意しましょう
例えば、約3ボルトを超えるとおかしくなる…というのは、
この問題である可能性があります。
- 接続ケーブルの配線
接続する向き (VDD・GNDどうしが接続されているか、など)、
ケーブルがつながっている箇所などに注意します。
Index
Prev: マイコンのプログラミング
Next: 自動測定