ArduinoでMAD7991を使う

MAD7991は4チャンネルのADコンバータ(AD7991)を搭載したモジュールです。 I2Cシリアルインターフェースでコントロールすることができます。 4チャンネルの入力として使うか、3チャンネル入力とリファレンス入力として使うかを選択できます。



ADコンバータAD7991のデータシートはアナログデバイスの こちらのページからダウンロードできます。

MAD7991の接続

ArduinoとMAD7991との接続は次の回路図の通りです。 SCL, SDAはボード内で既にプルアップされています。



Raspberry Piを使う場合の接続は次の通りです。



AD7991を直接使う場合は、ピン番号が異なります。 また、I2Cの信号線、SCL, SDAは2.2kΩの抵抗でプルアップが必要です。

Arduinoのプログラム

setup()でI2Cの動作周波数は3.4MHz(ハイスピードモード)に設定します。 I2Cのアドレスはボードの取り扱い説明書右下の「仕様」にある通り0x28です。 A/D変換のデータを読み込むだめには、AD7991のレジスタ、上位4ビットで設定します。




#include <Wire.h>

#define AD7991_ADDRESS 0x28  // AD7991のI2Cアドレス (データシート参照)
#define MAX_CLOCK   3400000  // I2Cの動作周波数 340kHz
#define REG_CH0     0x10     // チャンネル0
#define REG_CH1     0x20     // チャンネル1
#define REG_CH2     0x40     // チャンネル2
#define REG_CH3     0x80     // チャンネル3

void setup() {
    // put your setup code here, to run once:
    Wire.begin();             // 接続の初期化
    Wire.setClock(MAX_CLOCK); // I2Cの動作周波数
    Serial.begin(115200);
}
                    
uint16_t readADC(uint8_t channel) {
    Wire.beginTransmission(AD7991_ADDRESS);
    Wire.write(channel);    // チャンネル選択
    Wire.endTransmission();
                    
    delay(10);  // データ準備の待機
                    
    Wire.requestFrom(AD7991_ADDRESS, 2);  // 2バイトのデータを要求
    if (Wire.available() == 2) {
        uint8_t msb = Wire.read();
        uint8_t lsb = Wire.read();
        return ((uint16_t)(msb&0x07) << 8)+lsb;  // 12ビットデータの取得
    }
    return 0;  // エラー時
}

void loop() {
    char chr[256];
    // put your main code here, to run repeatedly:
    uint16_t adcValue = readADC(REG_CH0);// チャンネル0からデータを取得
    sprintf(chr,"%04x ",adcValue);
    Serial.print(chr);
                    
    adcValue = readADC(REG_CH1);        // チャンネル1からデータを取得
    sprintf(chr,"%04x ",adcValue);
    Serial.print(chr);
                    
    adcValue = readADC(REG_CH2);        // チャンネル2からデータを取得
    sprintf(chr,"%04x ",adcValue);
    Serial.print(chr);
                    
    adcValue = readADC(REG_CH3);        // チャンネル3からデータを取得
    sprintf(chr,"%04x\n",adcValue);
    Serial.print(chr);
                    
    delay(50);  // 50ms待つ
}

Raspberry Piのプログラム


#include <wiringPiI2C.h>
#include <stdio.h>
#include <unistd.h> // usleep() のため
    
#define I2C_ADDR    0x28 // I2Cデバイスのアドレス
#define REG_CH0  0x10
#define REG_CH1  0x20
#define REG_CH2  0x40
#define REG_CH3  0x80
    
uint8_t mChSel[4]={REG_CH0, REG_CH1, REG_CH2, REG_CH3};
    
uint16_t readAD(int fd, uint8_t channel)
{
    wiringPiI2CWrite(fd, channel);
    usleep(1000); // 1ms 待機
        
    // 2バイトのデータを取得
    char data[2];
    if (read(fd, data, 2) != 2) {
        return 0;
    }
    
    // 12ビットデータに変換(MSBの下位4ビットとLSBを結合)
    uint16_t adc_value = ((data[0] & 0x0F) << 8) | data[1];
    return(adc_value);
}
    
int main() {
    int fd;
    uint16_t adc[4];
        
    // I2Cデバイスを開く
    fd = wiringPiI2CSetup(I2C_ADDR);
    if (fd == -1) {
        printf("I2Cデバイスを開けませんでした\n");
        return 1;
    }
    
    while(1){
        for(int i=0; i<4; i++){
            adc[i]=readAD(fd,mChSel[i]);
        }        
        for(int i=0; i<4; i++){
            printf("%04x ",adc[i]);
        }
        printf("\n");
        usleep(50000); // 50ms 待機
    }
    return 0;
}

上のプログラムではレジスターの読み込みにread(fd, data, 2)という関数を使っています。 最初は以下のwiringPiI2CRead()関数を使っていました。 しかし、下のプログラムではdata[0]とdata[1]が同じ値 、lsb側のdata[1]がmsb側のdata[0]と同じ、になってしまいました。

data[0] = wiringPiI2CRead(fd);
data[1] = wiringPiI2CRead(fd);

I2Cの動作クロックを設定するには、WiringPiには関数が準備されていません。 /boot/firmware/config.txtを編集します。編集の後でrebootします。

dtparam=i2c_arm=on
dtparam=i2c_arm_baudrate=340000

工作例