6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

I2CセンサデバイスのArduino IDEからMATLAB Suport Package for Arduino Hardwareへの移植のコツ

Last updated at Posted at 2020-12-27

1.はじめに

MATLABでArduinoをプログラムするには、MATLAB Support Package for Arduino Hardwareを用いるわけですが、新しいI2Cデバイスを動かしてみたい、と思っても、ほとんどの場合は、サンプルプログラムはありません。一方でArduino IDE用のサンプルプログラムはあったりします。

本記事では、今年のEXPO LTで使いました圧力センサDPS368を例にとり、Arduino IDEのI2Cを含むプログラムをMATLAB用に移植する場合のコツをまとめました。
全体のプログラムはMATLAB CentralのFile exchangeにもアップロードしてあります。

2.最近のセンサデバイスについて

古いセンサデバイスは単機能で、温度なら温度、圧力なら圧力というように1種類のデータしか測定できないものが多く、また、精度などのパラメータも変換できないことが多かったです。
それに対して最近は、1つのデバイスで複数種類のデータを出力でき、また、条件に合わせて調整が可能になっています。
そのため、センサデバイスはセンサとインターフェースだけでなく、マイコンを内蔵したようなものになってきています。
測定条件は内部のレジスタに書き込む形になっています。

3.温度&圧力センサDPS368に関して

ここで取り上げます温度と圧力を測定できるインフィニオン社のDPS368も複数のレジスタを持ち、ここにデータを書き込むことで、測定条件を設定します。
下の表がレジスタマップです(Infineonのデータシートから引用) 。
レジスターマップ.JPG

Arduino Unoとの接続は下記のようになります。今回はDPS368 Pressure Shield2GO用評価ボードを用いました(Digi-keyから買えます)。DPS368に5V系の電源・信号を接続すると壊れるので、電源は3.3V、信号線はレベル変換器を介して接続します。
DPS368接続図.png

4.動作の流れの概略

動作の流れを説明します。
0)レジスタのアドレスとか、あらかじめ決まっている初期値の定義
1)センサとArduinoの通信を確立する
2)センサ固有の値を取得する
3)センサにパラメータを書き込む
4)データ要求と取得
5)通信の終了

5.Arduino IDEにおける構文とMATLABへの翻訳

5-1.値を1つだけ書き込む

リセットなどで用います。
Arduino IDEでは

Wire.beginTransmission(DPS368_address);
    Wire.write(reset);
Wire.endTransmission();

MATLABに翻訳すると、

write(DPS368,Reset,'uint8');% リセット

5-2.レジスタに値を書き込む

Wire.beginTransmission(DPS368_address);
    Wire.write(MEAS_CFG); // 0x08
    Wire.write(0xC0);
Wire.endTransmission();

MATLABに翻訳すると、

writeRegister(DPS368,MEAS_CFG,hex2dec('C0'),'uint8');

5-3.データを1つだけ読み出す

Wire.beginTransmission(DPS368_address);
    Wire.write(MEAS_CFG); //0x08
    Wire.endTransmission(false);
    Wire.requestFrom(DPS368_address,1);
Flag = Wire.read();

MATLABに翻訳すると、

write(DPS368,MEAS_CFG,'uint8');% 
Flag=read(DPS368,1,'uint8');

5-4.データを複数個(m個)読み出す

for ( int i = 0 ; i < m ; i++ ) {
   Wire.beginTransmission(DPS368_address);
        Wire.write(COEF + i);
   Wire.endTransmission();
   Wire.requestFrom(DPS368_address,1);
        readbuffer[i] = Wire.read();
}

MATLABに翻訳すると、

for t=0:m-1
    write(DPS368,COEF+t,'uint8');% 係数を要求
    readbuffer(t+1)= read(DPS368,1,'uint8');% データ取込み
end

もしくは、あまりデータ数が多くない場合には(例えば3個)、

Wire.requestFrom(DPS368_address,3);
    readbuffer[0] = Wire.read(); 
    readbuffer[1] = Wire.read(); 
    readbuffer[2] = Wire.read();

MATLABに翻訳すると、

data=read(DPS368,3,'uint8');%dataは1x3の配列になる

となります。

6.動作の全体

ということで、Arduino IDEとMATLABをまとめて示します。

まず、Arduino IDEのコードから。

//0)レジスタのアドレスとか、あらかじめ決まっている初期値の定義
# include <Wire.h>
# define DPS368_address 0x77
# define PSR_B0 0x02
# define PSR_B1 0x01
# define PSR_B2 0x00
# define TMP_B0 0x05
# define TMP_B1 0x04
# define TMP_B2 0x03
 
# define MEAS_CFG 0x08
# define PRS_CFG 0x06
# define TMP_CFG 0x07
# define CFG_REG 0x09
# define Kt 2088960.0 // 128times Compensation Scale Factors
# define Kp 2088960.0 // 128times Compensation Scale Factors
# define RESET 0x0C
 
# define COEF 0x10 //0x10 - 0x21
byte readbuffer[30];
int16_t c0,c1,c01,c11,c20,c21,c30;
int32_t c00,c10;
int32_t dum,dum1,dum2;
byte Flag;
 
void setup() {
  delay(1000);//これはあまり意味はないかも

// 1)センサとArduinoの通信を確立する
    Serial.begin(9600);
    Wire.begin();
    Wire.beginTransmission(DPS368_address);
        Wire.write(RESET);
    Wire.endTransmission();
    delay(100);

// 2)センサ固有の値を取得する
    for ( int i = 0 ; i < 19 ; i++ ) {
       Wire.beginTransmission(DPS368_address);
            Wire.write(COEF + i);
       Wire.endTransmission();
       Wire.requestFrom(DPS368_address,1);
            readbuffer[i] = Wire.read();
    }
    
        c0 = (readbuffer[0] << 8 | readbuffer[1] & 0xf0 ) >> 4; 
        c0 = -(c0 & 0b100000000000) | (c0 & 0b011111111111);
        c1 = (readbuffer[1] & 0x0f ) << 8 | readbuffer[2] ; 
        c1 = -(c1 & 0b100000000000) | (c1 & 0b011111111111);
          dum=(int32_t)readbuffer[3];
        c00 = (dum*4096 + readbuffer[4]*16 + (readbuffer[5]/16 ));

        if(c00>(int32_t)524287)
        {
          c00=c00-(int32_t)(1048576);
        }
         dum=(int32_t)(readbuffer[5] & 0x0f);
         dum2=(int32_t)(readbuffer[6]);
        c10 = (int32_t)((dum <<16) +  (dum2 << 8) + readbuffer[7]);
        if(c10>(int32_t)(2^19-1))
        {
          c10=c10-(int32_t)(1048576);
        }
        c01 = readbuffer[8] << 8 | readbuffer[9];
        c01 = -(c01 & 0b1000000000000000) | (c01 & 0b0111111111111111);
        c11 = readbuffer[10] << 8 | readbuffer[11];
        c11 = -(c11 & 0b1000000000000000) | (c11 & 0b0111111111111111);
        c20 = readbuffer[12] << 8 | readbuffer[13];
        c20 = -(c20 & 0b1000000000000000) | (c20 & 0b0111111111111111);
        c21 = readbuffer[14] << 8 | readbuffer[15];
        c21 = -(c21 & 0b1000000000000000) | (c21 & 0b0111111111111111);
        c30 = readbuffer[16] << 8 | readbuffer[17];
        c30 = -(c30 & 0b1000000000000000) | (c30 & 0b0111111111111111);

// 3)センサにパラメータを書き込む
    Wire.beginTransmission(DPS368_address);
        Wire.write(MEAS_CFG); // 0x08
        Wire.write(0xC0);
    Wire.endTransmission();

    Wire.beginTransmission(DPS368_address);
        Wire.write(RESET); // 0x0C
        Wire.write(0x80);
    Wire.endTransmission();

    Wire.beginTransmission(DPS368_address);
        Wire.write(TMP_CFG); // 0x07
        Wire.write(0xA7);
    Wire.endTransmission();
 
    Wire.beginTransmission(DPS368_address);
        Wire.write(PRS_CFG); // 0x06
        Wire.write(0x23);
    Wire.endTransmission();
 
    Wire.beginTransmission(DPS368_address);
        Wire.write(CFG_REG); // 0x09
        Wire.write(0x0C);
    Wire.endTransmission();
}
 
//4)データ要求と取得

void loop() {
//温度データ取得
    Wire.beginTransmission(DPS368_address);
        Wire.write(MEAS_CFG); //0x08
        Wire.write(0xC2);
    Wire.endTransmission();
    delay(100);
 
    Wire.beginTransmission(DPS368_address);
        Wire.write(TMP_B2);
    Wire.endTransmission(false);
    delay(100);
 
    Wire.requestFrom(DPS368_address,3);
        readbuffer[0] = Wire.read(); 
        readbuffer[1] = Wire.read(); 
        readbuffer[2] = Wire.read();
        dum = (int32_t)readbuffer[0];
        dum1 = (int32_t)readbuffer[1];
        dum2 = (int32_t)readbuffer[2];
     int32_t Temp0 = 65536*dum + 256*dum1 + dum2; 
     dum = (int32_t)(2^23-1);
     dum1 = (int32_t)(2^24);

     if(Temp0>dum)
     {
       Temp0 = Temp0 - dum1;
     }
    // Serial.print("Temp0= ");Serial.println(Temp0);
    float Temp = c0 * 0.5 + c1 * Temp0 / Kt;
    Serial.print("Temp ");
    Serial.println(Temp);

//圧力データ取得

    Wire.beginTransmission(DPS368_address);
        Wire.write(TMP_CFG); // 0x07
        Wire.write(0x87);
    Wire.endTransmission();

    Wire.beginTransmission(DPS368_address);
        Wire.write(PRS_CFG); // 0x06
        Wire.write(0x07);
    Wire.endTransmission();

    Wire.beginTransmission(DPS368_address);
        Wire.write(CFG_REG); // 0x09
        Wire.write(0x0C);
    Wire.endTransmission();

    Wire.beginTransmission(DPS368_address);
        Wire.write(MEAS_CFG); //0x08
        Wire.write(0xC1);
    Wire.endTransmission();
    delay(100);

    Flag=0;

    do{
        Wire.beginTransmission(DPS368_address);
          Wire.write(MEAS_CFG); //0x08
         Wire.write(0xC1);
        Wire.endTransmission();
        delay(100);
        Wire.beginTransmission(DPS368_address);
          Wire.write(MEAS_CFG); //0x08
        Wire.endTransmission(false);
        Wire.requestFrom(DPS368_address,1);
        Flag = Wire.read();
        Flag = Flag & 0x10;
        delay(100);
    } while (Flag <1);
        
    delay(100);
 
    Wire.beginTransmission(DPS368_address);
        Wire.write(PSR_B2);
    Wire.endTransmission(false);
    delay(100);
 
    Wire.requestFrom(DPS368_address,3);
        readbuffer[0] = Wire.read(); 
        readbuffer[1] = Wire.read(); 
        readbuffer[2] = Wire.read();

        dum=(int32_t)readbuffer[0];
        dum1=(int32_t)readbuffer[1];
        dum2=(int32_t)readbuffer[2];
     int32_t P0 = 65536*dum + 256*dum1 + dum2; 
     dum=(int32_t)(8388607);
     dum1=(int32_t)(16777216);
     if(P0>dum)
     {
       P0=P0-dum1;
     }

    float Praw = P0 / Kp;
    
    float P = c00 + Praw * (c10 + Praw * (c20 + Praw * c30)) + (Temp0 / Kt) * c01 + (Temp0 / Kt) * Praw * (c11 + Praw * c21);
    Serial.print("P ");Serial.println(P);
    Serial.println(" ");
    delay(100);
}

// 5)通信の終了は無限ループなのでありません。

ポイントとしては、圧力の測定完了を確認してからデータを取得しないとゼロしか出てこないところかと思います。測定完了までループを回して待ちます。
次にMATLABのコードです。

% 0)レジスタのアドレスとか、あらかじめ決まっている初期値の定義
Reset=0x0c;
% PSR_B0=0x02;
%PSR_B1=0x01;
PSR_B2=0x00;
% TMP_B0=0x05;
% TMP_B1=0x04;
TMP_B2=0x03;
 
MEAS_CFG=0x08;
PRS_CFG=0x06;
TMP_CFG=0x07;
CFG_REG=0x09;
Kt=2088960.0;% 128times Compensation Scale Factors
Kp=2088960.0;% 128times Compensation Scale Factors

COEF=0x10; %0x10 - 0x21

readbuffer=zeros(1,30);


% 1)センサとArduinoの通信を確立する
a=arduino('COM9','Uno','Libraries','I2C');%COM番号、ボード名は適宜変更
DPS368=device(a,'I2CAddress','0x77');% I2Cデバイスをつくる

%%% Reset %%%
write(DPS368,Reset,'uint8');% リセット

pause(0.1);

% 2)センサ固有の値を取得する
for t=0:18
    write(DPS368,COEF+t,'uint8');% 係数を要求
    readbuffer(t+1)= read(DPS368,1,'uint8');% データ取込み
end

% 係数C0,C1,C00,C10,C01,C11,C20,C21,C30を計算
C0Lo=fix(readbuffer(2)/16);
C0=readbuffer(1)*16+C0Lo;
if C0> (2^11-1)
    C0=C0-2^12;
end

C1=(readbuffer(2)-C0Lo*16)*2^8+readbuffer(3);
if C1> (2^11-1)
    C1=C1-2^12;
end

C00Lo=fix(readbuffer(6)/16);
C00=readbuffer(4)*2^12+readbuffer(5)*2^4+C00Lo;
if C00> (2^19-1)
    C00=C00-2^20;
end

C10=(readbuffer(6)-C00Lo*16)*2^16+readbuffer(7)*2^8+readbuffer(8);
if C10> (2^19-1)
    C10=C10-2^20;
end

C01=readbuffer(9)*2^8+readbuffer(10);
if C01> (2^15-1)
    C01=C01-2^16;
end

C11=readbuffer(11)*2^8+readbuffer(12);
if C11> (2^15-1)
    C11=C11-2^16;
end

C20=readbuffer(13)*2^8+readbuffer(14);
if C20> (2^15-1)
    C20=C20-2^16;
end

C21=readbuffer(15)*2^8+readbuffer(16);
if C21> (2^15-1)
    C21=C21-2^16;
end

C30=readbuffer(17)*2^8+readbuffer(18);
if C30> (2^15-1)
    C30=C30-2^16;
end

% 3)センサにパラメータを書き込む
writeRegister(DPS368,MEAS_CFG,hex2dec('C0'),'uint8');% Standby Mode

writeRegister(DPS368,Reset,hex2dec('80'),'uint8');% FIFO flush

writeRegister(DPS368,TMP_CFG,hex2dec('A7'),'uint8');
% MEMSのセンサを使用、4samples/sec, 128oversampling

writeRegister(DPS368,PRS_CFG,hex2dec('23'),'uint8');% 4samples/sec, 8oversampling(仮仕様)


writeRegister(DPS368,CFG_REG,hex2dec('0C'),'uint8');% P-SHIFT, T-SHIFT enable

% 4)データ要求と取得
%%%%%%%%%%%%%%%%ここからループ%%%%%%%%%%%%%

for s=1:10 % 取り込み回数は10回

    %%%温度測定
    writeRegister(DPS368,MEAS_CFG,hex2dec('C2'),'uint8');% 温度測定モード
    pause(0.1);
    write(DPS368,TMP_B2,'uint8');% 
    pause(0.1);
    data=read(DPS368,3,'uint8');
    Temp0=data(1)*2^16+data(2)*2^8+data(3);
    if Temp0> (2^23-1)
        Temp0=Temp0-2^24;
    end
    Temp=C0*0.5 + C1*Temp0/Kt;
    
    disp(['Temp= ', num2str(Temp)]);

    %%%圧力測定
    writeRegister(DPS368,TMP_CFG,hex2dec('87'),'uint8');
    writeRegister(DPS368,PRS_CFG,hex2dec('07'),'uint8');%128oversampling
    writeRegister(DPS368,CFG_REG,hex2dec('0C'),'uint8');
    writeRegister(DPS368,MEAS_CFG,hex2dec('C1'),'uint8');% 圧力測定
    pause(0.1);

    Flag=0;
    while Flag<1 %圧力測定完了を待つループ
        writeRegister(DPS368,MEAS_CFG,hex2dec('C1'),'uint8');
        pause(0.1);
        write(DPS368,MEAS_CFG,'uint8');% 
        Flag=read(DPS368,1,'uint8');
        Flag=bitand(Flag,0x10);
        pause(0.1)
    end

    pause(0.1);
    write(DPS368,PSR_B2,'uint8');% 
    pause(0.1);
    data=read(DPS368,3,'uint8');
    Pres0=data(1)*2^16+data(2)*2^8+data(3);
    if Pres0> (2^23-1)
        Pres0=Pres0-2^24;
    end
    Praw=Pres0 /Kp;
    Pres= C00 + Praw * (C10 + Praw * (C20 + Praw * C30)) + (Temp0 / Kt) * C01 + (Temp0 / Kt) * Praw * (C11 + Praw * C21);
    pause(0.1);
    disp(['Pres= ', num2str(Pres)]);

end

% 5)通信の終了
clear DPS368 a;

ArduinoのC言語(もどき?)をMATLABに移植していて思ったけど、MATLABって"do~while"がないですね。
あと、Arduino IDEでI2Cを記述する際の"Wireなんちゃら"に較べて、行数は確実に減ります(が、とにかくサンプルプログラムがほとんどないところが問題)。

ということで。
おつかれさまでした。

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?