ArduinoとDAコンバータでアナログ出力する回路を作りたかったのですが、分かりやすい解説がなく意外と苦労したので今後のために方法をまとめておきます。
(間違いが無いよう十分注意しておりますが、データシートなどの公式文書もあわせてご確認ください)
DAコンバータを選択した理由
LEDの輝度調整程度であれば、PWM信号で疑似アナログ出力を出すこともできますが、 今回は他のマイコンボードのADポートに信号を入力するため、DAコンバータICを使用することにしました。 また、DAコンバータICは比較的メジャーな、MICROCHIP社のMCP4922を使用しました。 MCP4922は2回路入りで、秋月電子通商の通販サイトで購入できます。
回路設計
基本的にはArduinoの端子と直接接続するだけですが、ノイズ対策でバイパスコンデンサを付けることが推奨されています。 データシートの20ページに図がありますので、その通りに取り付けします。
ピン接続
MCP4922は全部で14本のピンがありますが、下記のように接続します。
VDD: 電源端子です。Arduinoの5Vピンと接続します。
CS: この端子をLOWにしたときのみSPI信号を受け付けます。
ArduinoのSSピン、または、デジタル出力ピンに接続します。
SCK: SPIのクロック端子です。ArduinoのSCKピンと接続します。
SDI: SPIのデータ入力端子です。ArduinoのMOSIピンと接続します。
VOUTA: アナログ出力端子Aです。アナログ電圧の出力先と接続します。
VOUTB: アナログ出力端子Bです。アナログ電圧の出力先と接続します。
VREFA: アナログ出力Aの基準電圧です。今回はArduinoの5Vピンと接続します。
VREFA: アナログ出力Bの基準電圧です。今回はArduinoの5Vピンと接続します。
AVSS: ArduinoのGNDと接続します。
SHDN: 低消費電力のシャットダウンモードを使用するためのピンです。
この機能を使わない場合は、Arduinoの5Vピンと接続しておきます。
LDAC: アナログ出力のタイミングをコントロールするピンです。
この機能を使用する場合は、Arduinoのデジタル出力ピンに接続しておきます。
この機能を使用しない場合はGNDに接続しておきます
NC: 使用しないピンです。どこにも接続しない状態にしておくようです。
今回はArduinoとの接続を下図のようにしています。
MCP4922のSPIデータ仕様
MCP4922に送信するデータは16ビットです。bit15からbit12までが出力先などを設定するデータで、bit11からbit0までが出力するアナログ値を設定するデータです。
bit15 : 出力の設定先の選択。0であればVOUTAへの出力設定。1であればVOUTBへの出力設定。
bit14: バッファを使用するかどうかの設定。(よくわからないでのバッファしない設定:0にする。)
bit13 : ゲインの設定。0であれば、5Vを11bit(2048分割)で出力。1であれば5Vを12bit(4096分割)で出力。
(特に理由がなければ分解能の細かい1でよいかと思います。ただし、以下の例では0の場合で解説します。)
bit12 : シャットダウンモードを使用するかどうかの選択です。
今回はシャットダウンモードを使用使用しないので1にします。
bit11-0 : 出力するアナログ電圧の指定です。(0-4095の間で指定します)
例)
スケッチ
AnalogOutput_MCP4922という関数に、アナログ出力値(0-4095)と、VOUTA、VOUTBのどちらから出力するか(’A’、’B’)を
渡すとその通りに出力してくれるという内容のスケッチサンプルです。
loop関数の中で、VOUTAから5Vを出力させています。
#include <SPI.h>
const int SlaveSelect = 7;
const int LDAC = 8;
void setup() {
pinMode(SlaveSelect,OUTPUT) ;
pinMode(LDAC,OUTPUT) ;
pinMode(SS,OUTPUT) ; //SSはSPIのスレーブ選択ピンです。(*1)
// SPIの初期化処理を行う(*2)
SPI.begin() ;
SPI.setBitOrder(MSBFIRST) ;
SPI.setClockDivider(SPI_CLOCK_DIV8) ; // クロック(CLK)をシステムクロックの1/8で使用(16MHz/8)
SPI.setDataMode(SPI_MODE0) ;
}
//MCP4922からアナログ出力する関数。v_setはアナログ出力値。select_pinはVOUTAかVOUTBのどちらから出力するかを指定。
void AnalogOutput_MCP4922(int v_set, char select_pin){
//v_setは0から4095の間で指定する
//select_pinはAかBで選択する
int spi_command;
if (select_pin == ‘A’){
spi_command = 0x10; //VOUTAから出力する場合は、0x10とv_setを組み合わせてSPIデータを作る(*3)
} else if (select_pin == ‘B’){
spi_command = 0x90; //VOUTAから出力する場合は、0x90とv_setを組み合わせてSPIデータを作る(*3)
}
digitalWrite(LDAC,HIGH) ; //アナログ出力を停止する
digitalWrite(SlaveSelect,LOW) ; //SPI信号を受け付けられる状態にする
SPI.transfer((v_set >> 8) | spi_command) ; //出力信号の上位1バイト分を作成する(*4)
SPI.transfer(v_set & 0xFF) ; //出力信号の下位1バイト分を作成する(*5)
digitalWrite(SlaveSelect,HIGH) ; //SPI信号を受け付けらない状態にする
digitalWrite(LDAC,LOW) ; //アナログ出力する
}
void loop() {
//AnalogOutput_MCP4922(4095, ‘A’); //VOUTAから5Vのアナログ出力する
}
解説
サンプルスケッチの中で、つまづきやすいところの(*)印をつけています。
ここでは(*)の個所を解説していきます。
(*1)今回はSPIのスレーブセレクトにD7端子を使用しています。Arduinoではスレーブセレクト用のピン(SS)がありますが、
SSピンを使用しない場合でも、SSピンを出力設定しておく必要があります。
SSピンを出力設定しない場合、SPIの機能がスリーブしてしまい使えなくなるようです。
(*2)SPIの設定は、いくつかパラメータがありますが、MCP4922との接続の場合は
サンプルスケッチのように記載すれば問題ありません。
(*3)まず最初にArduinoでのSPI送信について説明します。
SPIでのデータ送信は、SPI.transfer()で行いますが、送信できるデータは1回に1バイト分だけです。
MCP4922は2バイト(16ビット)のデータを送信する必要がありますので、2回に分けて送信する必要があります。
1回目にbit15-bit8を送信し、次にbit7-bit0を送信します。1回目に送信するデータは、出力先などの設定データと
アナログ出力値の一部を含んでいます。2回目に送信するデータは、アナログ出力値の一部です。
※SPI.transfer16()という2バイト送信できるコマンドもあるようですが、1部のCPU(SAMコア)しか対応していないようなので
Zero、Dueでしか使いえないコマンドのようです。(参考 https://mag.switch-science.com/2015/12/18/arduino-ide-1-6-7/)
例)
スケッチの(*3)の個所では、MCP4922のVOUTAか、VOUTBのどちらからアナログ出力するかを選んでいます。
1回目に送信するデータのうち、アナログ出力値の部分を0とすると下のようになります。
VOUTAへ出力する場合は、「00010000」となり、16進数で表すと、0x10になります。
同様に、VOUTBへ出力する場合は、「100010000」であり、16進数で、0x90となります。
(*4)では、(*3)のデータと、アナログ出力値のデータを組み合わせて1回目に送信するデータを作成しています。
1回目に送信するデータのうち、bit11からbit8はアナログ出力値の一部です。
アナログ出力値は、v_setに格納されている12ビットのデータです。このデータからbit11からbit8に相当する個所を下記のように取り出します。
例 アナログ出力値が5V(111111111111)をVOUTBから出力する場合。
①v_setを8ビット分右シフトする。
v_setに対し、 ビットシフト演算子(>>)を使用して右シフトします。(>>8で8ビット右シフトするという意味になります)
v_setを8ビット右シフトする
②上記データを(*3)のデータと論理和で組み合わせる。
(*5)は2回目に送信するデータを作成しています。
v_setに格納されているアナログ出力値から、bit7からbit0分のデータを抜き出します。
v_setと「11111111」の論理積を行う
このように、データを2回に分けて送信することで、MCP4922の設定を行います。