■ DA 変換
PICにはアナログ電圧を出力できるポートはありません。したがってデジタルポートに若干のハードを追加して
PICのデジタル信号をアナログ信号(電圧)に変換する必要があります。設計に際しどのDA変換を選ぶのか以下の
ようなチェック項目があります。
★ 変換速度
気温などのように秒レベルの応答でよいのか、オーディオのように数十KHz程度までの応答が必要なのか
★ アナログ電圧の変換リップル
最終的にアナログ電圧計の針を振らせるような場合は針を振らせるメカ機構の慣性や人間の目の感度など
からかなりの変換リップルは許容されます。 一方、アナログ出力を横軸を時間軸としてPCのモニタや記録紙
などに表示する場合は変換リップルは厳しく制限されます。
★ 分解能
変換すべき出力信号の分解能はいくらなのか。 案外12ビットや16ビットも必要なく4ビット程度でたる場合も
あるものです。
★ 出力信号データの種類
・ シリアルデータ
・ パラレルデータ
★ 専用のDAコンバータICを使うか否か
・ 専用DAコンバータICを外付けする
・ オペアンプや抵抗などでディスクリートで製作する。
専用のDAコンバータICを使うほうが簡単ですが、場合によっては汎用の部品を使って自分でディスクリートで設計した方がよい
場合もあります。今をときめくDAコンバータICも5年、10年すると生産中止となるかもしれません。 設計に際しては製品寿命、修理と
いったことも考慮して決める必要があります。
以下に PICの出力信号をDA変換した例を紹介します。
(1) ディスクリートのフィルタによるPWM信号の変換
(2) ディスクリートのオペアンプ加重加算回路による変換
(3) ディスクリートのはしご回路による変換(パラレル入力)
(4) ディスクリートのはしご回路による変換(シリアル入力)
(5) 専用DAコンバータICによる変換 ( LTC 1446 )
(6) 専用DAコンバータICによる変換 ( MCP 4822 )
<試作品仕様>
・RA1ピンより周波数10KHzのPWMチョッパ電圧を出力のすること。
・通流率は0〜100%まで1%ステップで直線的に変化し 周期1Hzで増加と減少を繰り返すこと
すなわち出力電圧の平均値は周期1Hzの三角波となること
・このチョッパ出力をカットオフ周波数3.1Hzの1次ローパスフィルタでリップルを除去すること。
・更にフィルタリング後の出力をバッファーアンプに接続のこと。
<試作品回路図> (→回路図のPDFファイル)
PIC16F88をつかった場合の回路図を以下に示します。
<試作品外観> 下記の写真には上記回路図にはない、また本テーマと関係のない部品も多々写っています。
<プログラム例>
#include <16f88.h> #use delay(clock=20000000) #fuses HS,NOWDT,PUT,MCLR,BROWNOUT,NOLVP,NOCPD,NOWRT,NODEBUG,CCPB3,NOPROTECT,NOFCMEN,IESO,NOIESO #use fixed_io(a_outputs=PIN_A1) // ピンRA1を出力モードに固定 → 速度アップ unsigned int i = 0; // int j ; main() { while(1) { i++; //通流率は1%ステップで変化している if(i >= 200) i = 0; // 1サイクル(周期1秒)終了したのでリセット // 1サイクルは200のステップから構成されています // すなわち 上昇モードの100ステップと下降モード100ステップで構成されています // 1ステップ変化すると通流率も1%変化します if(i < 100) //三角波 : 上昇モード PWMのduty = 0 → 100% { //通流率は1%ステップで変化している for(j = 0; j < 50; j++) // 三角波の周期を1Hzにする為に同一通流率を50回出力する { // 100μsec × 50回 × 100ステップ(通流率のステップ数)× 2回(三角波上昇、下降) // = 1000000μsec = 1000msec = 1sec (三角波の周期) output_bit(PIN_A1,1);// i + (100 - i) = 100 μsec (cnost) → 10KHz delay_us(i); // output_low(PIN_A1); delay_us(100 -i ); } } else // 三角波 : 下降モード PWMのduty = 100% → 0% { for(j = 0; j < 50; j++) // 三角波の周期を1Hzにする為に同一通流率を50回出力する { output_high(PIN_A1); //( 200 -i ) + (i - 100) = 100μsec → 10KHz delay_us(200 - i); output_low(PIN_A1); delay_us(i - 100); } } } return 0; }
<動作結果>
・ 10KHzのPWM出力に対して fc = 3.1Hz( fc = 1/(2×πR×C) = 1/(2×5.1KΩ×10μF) )という大きな
ローパスフィルタを入れた結果 1Hzの三角波にはほとんどリップルはなくなっています。目的によってはこの
方法のDA変換はとても安価で簡単なので利用できると思います。
・ アンプ出力で三角波の上部がクリップされたのは電源電圧が5VであるためLM358が飽和したためです。
DA変換とは関係ありませんがアンプ出力波形から5Vで使用したときのLM358の飽和電圧が3.8V付近にある
ことがわかります。
アンプ入力電圧(LM358の3番ピンの電圧)[2V/div] | アンプ出力電圧(LM358の1番ピンの電圧)[2V/div] | ||
0V | 0V | ||
[200msec/div] | [200msec/div] |
PWM出力電圧(PICのRA1ピンの電圧)[2V/div] | |||||
通流率約10%の場合 | 通流率約50%の場合 | 通流率約80%の場合 | |||
0V | 0V | 0V | |||
[20μsec/div] | [20μsec/div] | [20μsec/div] |
(2) ディスクリートのオペアンプ加重加算回路による変換
デジタル出力ポートに重みを付けオペアンプで加算する8ビットのDAコンバータの例を紹介します。この方式のDA
変換は、電流加算型DA変換方式と呼ばれることもあります。
加算抵抗はE24の中から抵抗2本以内でできる以下の値を使用しました。
ポート | RC7 | RC6 | RC5 | RC4 | RC3 | RC2 | RC1 | RC0 |
合計抵抗値 | 5KΩ | 10KΩ | 20KΩ | 40KΩ | 80KΩ | 160KΩ | 320KΩ | 640KΩ |
抵抗の構成 | 10KΩ //10KΩ |
10KΩ | 20KΩ | 20KΩ +20KΩ |
47KΩ +33KΩ |
150KΩ +10KΩ |
300KΩ +20KΩ |
620KΩ +20KΩ |
<試作品仕様>
・ CポートのRC7を最上位ビットMSB、RC6を第6位ビット……RC1を第1位ビット、RC0を最下位ビットLSBとして
8ビットの整数を0から255まで出力する
・ RC0の重みを1とした場合、RC1,RC2、……RC7の重みが2倍、4倍、16倍……128倍となるようなオペアンプ
加算抵抗を選択のこと
・ これらの加算抵抗を両電源のオペアンプで加算してDA変換電圧とすること
・ 整数は10msec毎に1つ変化すること。0から255まで増加した後は255から0まで減少すること。以下これを繰り
返すこと。
<試作品回路図> (→回路図のPDFファイル)
PIC18F452をつかった場合の回路図を以下に示します。
<試作品外観> 下記の写真には上記回路図にはない、また本テーマと関係のない部品が多々写っています。
<プログラム例>
#include <18F452.h> #use delay(clock=20000000) #FUSES HS,PUT,NOWDT,BROWNOUT,BORV42,NOPROTECT,NOLVP #byte port_c=0x0F82 // port_c を アドレス番号0F82のポートCに対応させる //Cポートが以下のように対応 // RC7→MSB RC6→第6ビット …… RC1→第1ビット RC0→LSB #define UpMode 0 // 増加モード #define DownMode 1 // 減少モード main() { short int Mode = UpMode; set_tris_c(0); // Cポートをすぺて出力モードに固定 while(1) { if(Mode == UpMode) // 増加モード { port_c++; //10msec毎に1ビットづつ255まで増加 delay_ms(10); if(port_c == 255)Mode = DownMode; } else //減少モード { port_c--; //10msec毎に1ビットづつ0まで減少 delay_ms(10); if(port_c == 0)Mode = UpMode; } } return 0; }
<動作結果>
オペアンプOP-07の出力電圧を下記に示します
DA変換出力電圧(オペアンプ OP - 07 6番ピンの電圧) | |||||
マクロ波形 電圧 [ 1 V/div ] | ミクロ波形 電圧 [ 50 mV/div ] | ||||
0V | |||||
0V | |||||
[2sec/div] | [20msec/div] |
(3) ディスクリートのはしご回路による変換(パラレル入力)
専用のAD、DA変換 ICの中で使われているはしご回路によるDA変換をディスクリートで構成した例を紹介します
<試作品仕様>
・ DポートのRD7を最上位ビットMSB、RD6を第6位ビット……RD1を第1位ビット、RD0を最下位ビットLSBとして
8ビットの整数を0から255まで出力する
・ 各ポートの重みづけとしてR、2Rによるはしご(Ladder)回路を構成する
・ バッファーとしてオペアンプを追加する
・ 整数は10msec毎に1つ変化すること。0から255まで増加した後は255から0まで減少すること。以下これを繰り
返すこと。
<試作品回路図> (→回路図のPDFファイル)
PIC18F452をつかった場合の回路図を以下に示します。
<試作品外観> 下記の写真には上記回路図にはない、また本テーマと関係のない部品も多々写っています。
<プログラム例> #include <18F452.h> #use delay(clock=20000000) #FUSES HS,PUT,NOWDT,BROWNOUT,BORV42,NOPROTECT,NOLVP #byte port_d=0x0F83 // port_d を アドレス番号0F83のポートDに対応させる //Dポートが以下のように対応 // RD7→MSB RD6→第6ビット …… RD1→第1ビット RD0→LSB #define UpMode 0 // 増加モード #define DownMode 1 // 減少モード main() { short int Mode = UpMode; set_tris_d(0); // Dポートをすぺて出力モードに固定 while(1) { if(Mode == UpMode) // 増加モード { port_d++; //10msec毎に1ビットづつ255まで増加 delay_ms(10); if(port_d == 255)Mode = DownMode; } else //減少モード { port_d--; //10msec毎に1ビットづつ0まで減少 delay_ms(10); if(port_d == 0)Mode = UpMode; } } return 0; }
<動作結果>
DA変換出力電圧(オペアンプ OP - 07 6番ピンの電圧) | |||
マクロ波形 電圧[1V/div] | ミクロ波形 電圧[50mV/div] | ||
0V | |||
0V | |||
[2sec/div] | [20msec/div] |
(4) ディスクリートのはしご回路による変換(シリアル入力)
8ビットのシフトレジスタIC 74HC4094をつかってシリアル入力のDA変換をディスクリートで構成した場合の
例を紹介します。4094はシリアル信号をパラレル信号に変換するときによく使われます。
DA変換後の出力電圧は同様に三角波とします。
<試作品仕様>
・ 4094のQ8を最上位ビットMSB、Q7を第6位ビット……Q1を第1位ビット、Q0を最下位ビットLSBとして
8ビットの整数を0から255まで出力する
・ 重みづけとしてR、2Rによるはしご(Ladder)回路を構成する
・ バッファーとしてオペアンプを追加する
・ 整数は10msec毎に1つ変化すること。0から255まで増加した後は255から0まで減少すること。以下これを繰り
返すこと。
<74HC4094の使い方>(詳細はマニュアルを参照してください)
4094はD型フリップフロップが8個直列に接続された構成になっています。 端子SI(Serial In)のデータが端子CK
(Clock)の入力がLowからHighに変化する毎に1ビットづつ順次内部のレジスタ(D型フリップフロップ)に読込まれて
ゆきます。 内部レジスタの値は端子ST(Standby)がHighの時はそのまま出力され、Lowの時は出力された値が保持
(ホールド) されます。端子OE(Output Enable)がHighの時はHigh、Lowの値が出力され、Lowの場合はハイインピー
ダンス状態に なります。 以上のことからPIC側からの送信は以下のようになります。
@ STをLowにする。
A CKをLowにする。
B SIにデータのMSB(第7位)を出力する。
C CKをHighにする。このタイミングでレジスタQ1にMSBの値が読込まれる
D CKをLowにする。
E SIにデータのを第6位を出力する。
F CKをHighにする。
G CKをLowにする。
H 同様に 第5位〜第0位(LSB)を出力する。
I LSBを出力後、CKをLowにしたらSTをHighにする。この時Q8〜Q1のレジスタの値が同時に出力される。
J STをLowにする。出力された値はホールドされている。次のデータがQ8〜Q1にセットされるまでSTはLowにして
おく。
74HC4094 | |
タイミングチャート | システム図 |
<試作品回路図> (→回路図のPDFファイル)
PIC18F452をつかった場合の回路図を以下に示します。
<試作品外観> 下記の写真には上記回路図にはない、また本テーマと関係のない部品も多々写っています。
<プログラム例> // DA of 74HC4094 #include <18F452.h> #use delay(clock=20000000) #FUSES HS,PUT,NOWDT,BROWNOUT,BORV42,NOPROTECT,NOLVP #define UpMode 0 // 増加モード #define DownMode 1 // 減少モード long int Output = 0; short int Mode = UpMode; void DAout(long int out) // DAout for 74HC4094 { int i =0; output_low(PIN_B2); // ST(Standby):Low → ホールド状態 output_low(PIN_B0); // CK:Low for(i=0; i<8; i++) // 送信データをMSBから順次1ビットづつ出力する { output_bit(PIN_B1,(bit_test(out,7))); //outの第7ビットをチェック 0なら0を1なら1をRB1に出力する // RB1:SI(Serial In) out = (out << 1); // 送信後1ビットだけ左にビットシフト output_high(PIN_B0); // CK:High output_low(PIN_B0); //CK:Low } output_high(PIN_B2);// ST:High → レジスタ値出力 output_low(PIN_B2); // ST:Low → 出力値ホールド } main() { set_tris_d(0xff); // Dポートをすぺて出力モードに固定 while(1) { if(Mode == UpMode) // 増加モード { Output++; //10msec毎に1ビットづつ255まで増加 delay_ms(10); if(Output == 255)Mode = DownMode; } else //減少モード { Output--; //10msec毎に1ビットづつ0まで減少 delay_ms(10); if(Output == 0)Mode = UpMode; } DAout(Output); } return 0; }
<動作結果>
DA変換出力電圧(オペアンプ OP - 07 6番ピンの電圧) | |||
マクロ波形 電圧[V/div] | ミクロ波形 電圧[50mV/div] | ||
0V | 0V | ||
[2sec/div] | [20msec/div] |
(5) 専用DAコンバータ ICによる変換( LTC 1446 )
DAコンバータICとしてリニアテクノロジーのLTC1446を使った場合の例を紹介します。
<LTC1446 諸元>
・ デュアルDAC、分解能12ビット
・ フルスケール 4.095V at 4.5V〜5.5V単一電源
・ リファレンス内蔵
・ LSB=4.095V/4095=1mV
・ 最大DNL誤差:0.5LSB
・ 5V動作(LTC1446):ICC=1000mA標準
・ 3線式カスケード可能なシリアル・インタフェース
・ SOP、PDIP パッケージで供給
<試作品仕様>
・ PICは0から4095(2の12乗 = 4096)までの整数を1つづつ変化させLTC1446に送信すること。0から4095
まで増加させたら4095から0まで減少させること。
・ 整数値の変化は1msec毎におこなうこと。
<LTC1446の使い方>(詳細はマニュアルを参照してください)
・ +5Vの単一電源で使用する場合、出力電圧Voutはほぼ以下の式の値となります。(注)
Vout = 4095 × Code/4095 [ mV ]
Code : 出力整数値(フルスケール 4095)
(注) 高精度につかう場合はVoutAとVoutB出力を差動アンプで加算してつかいます。
・ LTC1446へのデータ送信のタイミングチャートを下記に示します
@ CLK入力をLowにします
A 20nsec以上経ってから CSをLowにします。
B MSB(第11位)データをDinに出力します。
C 40nsec以上経ってからCLKをHighにします。このクロックが立ち上がったタイミングでLTC1446はデータを
読み込みます。クロックのHigh Lowの時間はそれぞれ40nsec以上必要です
D CLKをLowにします。
E 第10位のデータを出力します。
F 40nsec以上経ってからCLKをHighにします。
G 以下同様にLSBまでこれを繰り返します
H LSBを出力してCLKをLowにした後に、CSをHighにします。
尚、VoutBにも出力する場合は、VoutAのLSBを出力した後にすぐVoutBのMSBから順次同様にDinに出力
してゆきます。
<試作品回路図> (→回路図のPDFファイル)
PIC18F452をつかった場合の回路図を以下に示します。
<試作品外観> 下記の写真には上記回路図にはない、また本テーマと関係のない部品も多々写っています。
<プログラム例>
#include <18F452.h> #use delay(clock=20000000) #FUSES HS,PUT,NOWDT,BROWNOUT,BORV42,NOPROTECT,NOLVP //WDT : 72 msec = 4*18 #define UpMode 0 // 増加モード #define DownMode 1 // 減少モード long int Output; short int Mode = UpMode; void DAout(long int out){ // データのシリアル変換&送信関数 int i =0; output_low(PIN_B3); // clock low RB3:clock output_low(PIN_B5); // CS on RB5:CS for(i=0; i<12; i++){ // 送信データをMSBから順次1ビットづつ出力する output_bit(PIN_B4,(bit_test(out,11))); //outの第11ビットをチェック 0なら0を1なら1をRB4に出力する // RB4:Din out = (out << 1); // 送信後1ビットだけ左にビットシフト output_high(PIN_B3); // RB3:clock output_low(PIN_B3); } output_high(PIN_B5);// CS off } #int_timer1 void interval() // 1msec毎に割込み発生 { set_timer1(64911); // 256*256 -625 = 64911 // 0.05 usec * 4 * 8 * 625 = 1000 usec =1 msec if(Mode == UpMode) // 増加モード { Output++; //1msec毎に1ビットづつ4095まで増加 if(Output == 4095)Mode = DownMode; } else //減少モード { Output--; //1msec毎に1ビットづつ0まで減少 if(Output == 0)Mode = UpMode; } DAout(Output); } main(){ setup_timer_1(T1_INTERNAL |T1_DIV_BY_8); set_timer1(64911); enable_interrupts(INT_TIMER1); enable_interrupts(GLOBAL); while(1) // 割込みを待つ { } return 0; }
<動作結果>
出力電圧VoutA(LTC1446の5番ピンの電圧) | |||
マクロ波形 [V/div] | ミクロ波形[10mV/div] | ||
0V | |||
0V | |||
[2sec/div] | [10msec/div] |
(6) 専用DAコンバータ ICによる変換( MCP4822 )
12ビットDAコンバータICとしてマイクロチップテクノロジーのMCP4822を使った場合の例を紹介します。MCP4822は
マイクロチップテクノロジー直販通販から2.3ドル/個程度で購入することができます。
試作品仕様は(5)のLTC1446とまったく同じです。
<MCP4822 諸元>
・ デュアルDAC、分解能12ビット
・ フルスケール 4.095V at 4.5V〜5.5V単一電源
・ リファレンス内蔵
・ LSB=4.095V/4095=1mV
・ 最大DNL誤差:±0.2LSB
・ 20MHzまで可能なSPIインターフェース
・ 出力ゲインが 1倍、2倍のいずれか選択可能
・ 低消費電力用のシャットダウン機能付
・ SOP、PDIP パッケージで供給
<試作品仕様>
・ PICは0から4095(2の12乗 = 4096)までの整数を1つづつ変化させLTC1446に送信すること。0から4095
まで増加させたら4095から0まで減少させること。
・ 整数値の変化は1msec毎におこなうこと。
<MCP 4822の使い方>(詳細はマニュアルを参照してください)
下記のタイミングチャート、書き込みコマンドレジスタへの書き込み要領にしたがってパルスを送ります。MCP 4822
の場合LTC1446に較べて若干機能が多い分複雑です。
@ CS端子をHighからLowにします。
A SDI端子に VoutAのデータかVoutBのデータか設定ビットの最初に指定(出力)します。
B クロック入力SCKをLowからHighにします。このクロックの立ち上がりでMCP4822はSDIのデータを読込みます。
以下同様にSCKのクロックがLowからHighに変化した時にSDIのデータが読み込ます
C 次の1クロックのデータは無効なデータです。
D 次にゲインを1にするか2にするかのデータGAをSDIに出力します。
MCP4822の出力電圧Voutは以下の式で与えられます。
Vout = 2.048 × G × Dn / 4096 (at Vcc = 5V)
すなわち G(ゲイン)を G = 1にするのか G = 2にするのか指定する必要があります。
E MCP4822は低消費電力のためのスタンバイモードすることができます。次のシャットダウンデータSHDNをHighに
するとVoutは出力され、LowにするとVoutはハイインピーダンスになります。
F このあとは12ビットのVoutデータのMSBから順次出力します。
G LSBまで出力したら CSをLowからHighにします。
<試作品回路図> (→回路図のPDFファイル)
PIC18F452をつかった場合の回路図を以下に示します。
<試作品外観> 下記の写真には上記回路図にはない、また本テーマと関係のない部品も多々写っています。
<プログラム例>
// DA MCP4822 #include <18F452.h> #use delay(clock=20000000) #FUSES HS,PUT,NOWDT,BROWNOUT,BORV42,NOPROTECT,NOLVP //WDT : 72 msec = 4*18 #define UpMode 0 // 増加モード #define DownMode 1 // 減少モード long int Output; short int Mode = UpMode; short int Chanel = 0; // 0:Chanel A void DAout(long int out) // DAout { int i =0; output_low(PIN_B3); // clock low output_low(PIN_B5); // CS on // 以下4ビットが設定ビット output_bit(PIN_B4,Chanel);//チャンネル設定ビット 0:Chanel A 1:Chanel B // VoutA に出力 output_high(PIN_B3); // clock output_low(PIN_B3); output_bit(PIN_B4,1);// dummy; output_high(PIN_B3); // clock output_low(PIN_B3); output_bit(PIN_B4,0); // ゲイン設定ビット 1: G = 1 0: G = 2 // G = 2; V = Data/4096 * 2.048*2 [V] output_high(PIN_B3); // clock output_low(PIN_B3); output_bit(PIN_B4,1); //シャットダウン設定ビット 1: 出力イネイブル 0: シャットダウン // output enable output_high(PIN_B3); // clock output_low(PIN_B3); for(i=0; i<12; i++) // 送信データをMSBから順次1ビットづつ出力する { output_bit(PIN_B4,(bit_test(out,11))); //outの第11ビットをチェック 0なら0を1なら1をRB4に出力する // RB4:Din out = (out << 1); // 送信後1ビットだけ左にビットシフト output_high(PIN_B3); // clock output_low(PIN_B3); } output_high(PIN_B5);// CS off } #int_timer1 void interval() // 1msec毎に割込み発生 { set_timer1(64911); // 256*256 -625 = 64911 // 0.05 usec * 4 * 8 * 625 = 1000 usec =1 msec if(Mode == UpMode) // 増加モード { Output++; //1msec毎に1ビットづつ4095まで増加 if(Output == 4095)Mode = DownMode; } else //減少モード { Output--; //1msec毎に1ビットづつ0まで減少 if(Output == 0)Mode = UpMode; } DAout(Output); } main() { setup_timer_1(T1_INTERNAL |T1_DIV_BY_8); set_timer1(64911); enable_interrupts(INT_TIMER1); enable_interrupts(GLOBAL); while(1) // 割込みを待つ { } return 0; } <動作結果>
出力電圧VoutA(MCP4822の8番ピンの電圧) | |||
マクロ波形[V/div] | ミクロ波形[10mV/div] | ||
0V | 0V | ||
[2sec/div] | [10msec/div] |