I2C接続のLCDに表示する

LCD出力サンプルプログラム確認
今まで測定した温度センサーや加速度センサーの値をLCDに表示してみます。今回、確認するのは、‘ I2C接続の小型LCD搭載ボード(5V版)’  です。

SWITCH SCIENCEという会社の商品で、同社のホームページから、使い方に関するブログへのリンクがあります。基本的にはこのブログで紹介されているプログラムを活用させて頂きたいと思います。(リンク先:使い方①使い方②
このブログでは、Arduino UNOの端子にLCD端子を直結しています。記事によると、SDAピンとA4ピン、SCLピンとA5ピンは内部接続されており、ArduinoのA2端子を5V、A3端子をGNDとして使っているとのことです。(#RESET(ArduinoのA1端子)は未処理)
実際に同じ接続方法でブログの使い方②のプログラムを実行し動作を確認出来ました。

今回作成プログラムによるLCD出力
このLCD出力プログラムを利用し、前に確認した温度センサーと加速度センサーの値をLCDに表示してみます。先ずは実際に今回の確認結果を次の画像で確認できます。2行あるLCDの上段に温度、下段に加速度(Z方向)を表示しています。
温度は大きな変化はありませんが、Z方向の加速度センサー値は平面設置(傾斜0度):約1G,傾斜45度:約0.7G、直角設置(傾斜90度):約0G   程度の値が出力されています。

LCD配線
ADT7410温度センサー確認時の配線に追加しています。I2C接続は電源を除き、2本のシリアル通信線でバス接続できることがメリットですので、温度センサー、加速度センサーのシリアル通信用配線(SDA,SCL)を延長して使用します。尚、シリアル通信用のプルアップ抵抗は今回、10KΩに変更しています。

 

LCD出力サンプルプログラム確認
プログラムコードを示します。加速度センサー(MMA8451)のプログラムに温度センサーとLCD出力のサンプルプログラムを組み入れています。自分でわかりやすい様に変数名の変更、ArduinoとLCDを直結する為のサンプルプログラム部分をコメントアウトなどしていますが、大きくは変更していません。
LCD出力のサンプルプログラムは文字列、もしくは整数しか扱っていませんでしたので、行番248,249の下記コードによって、取得したセンサー値を文字列に変換する方法の調査が必要でした。

#include <Wire.h>

// ★ AQM0802A 定数・変数
// https://www.switch-science.com/catalog/1407/
// SWITCH-SCIENCE : SSCI-014076
#define DVC_AQM0802A 0x3e                       // LCD(AQM0802A)アドレス(固定)
byte contrast = 20;                             // コントラスト(0~63)
char msg0[8]; char t[8];
char msg1[8]; char a[8];

// MMA8451 定数・変数
#define DVC_MMA8451 0x1D                        // 加速度センサ(MMA8451)アドレス(固定)
#define GRAV_E       (9.80665F)                 // 標準重力加速度

uint16_t x_r , y_r , z_r ;                      // XYZ 14bitデータ
long int x_v , y_v , z_v ;                      // XYZ 数字
float x_g , y_g , z_g ;                         // XYZ 角加速度
float z_sts ;

// ☆ ADT7410 定数・変数
#define DVC_ADT7410 0x48                        // 温度センサ(ADT7410)アドレス(外部設定)
uint16_t g_md = 0x40 ; 
uint16_t g_rs = 0x00 ;
float r_t ;

// ★ LCD(AQM0802A)初期化
void LCD_INIT(){
    lcd_cmd(0b00111000);                        // function set
    lcd_cmd(0b00111001);                        // function set
    lcd_cmd(0b00000100);                        // EntryModeSet
    lcd_cmd(0b00010100);                        // interval osc
    lcd_cmd(0b01110000 | (contrast & 0xF));         // contrast Low
    lcd_cmd(0b01011100 | ((contrast >> 4) & 0x3));  // contast High/icon/power
    lcd_cmd(0b01101100);                        // follower control
    delay(200);
    lcd_cmd(0b00111000);                        // function set
    lcd_cmd(0b00001100);                        // Display On
    lcd_cmd(0b00000001);                        // Clear Display
    delay(2);
}

// ★ LCD(AQM0802A)コマンド書き込み(I2C)
void lcd_cmd(byte x) {
    Wire.beginTransmission(DVC_AQM0802A);
    Wire.write(0b00000000);                     // CO = 0,RS = 0
    Wire.write(x);
    Wire.endTransmission();
}

// ★ 連続文字
void lcd_contdata(byte x) {
  Wire.write(0b11000000);                       // CO = 1, RS = 1
  Wire.write(x);
}

// ★ 最終文字
void lcd_lastdata(byte x) {
  Wire.write(0b01000000);                       // CO = 0, RS = 1
  Wire.write(x);
}
 
// ★ 文字の表示
void lcd_printStr(const char *s) {
  Wire.beginTransmission(DVC_AQM0802A);
  while (*s) {
    if (*(s + 1)) {
      lcd_contdata(*s);
    } else {
      lcd_lastdata(*s);
    }
    s++;
  }
  Wire.endTransmission();
}
 
// ★ 表示位置の指定
void lcd_setCursor(byte x, byte y) {
  lcd_cmd(0x80 | (y * 0x40 + x));
}

// ★ 数字表示
void lcd_printInt(int num){
  char int2str[10];
  sprintf(int2str,"%d",num);
  lcd_printStr(int2str);  
}

// ☆ 温度センサ-(ADT7410)初期化
void ADT7410_INIT(){
  Wire.beginTransmission(DVC_ADT7410);         // 接続開始
  Wire.write(0x03);                            // Configurationアドレス指定
  Wire.write(g_md | g_rs);                     // 操作モードと分解能指定
  Wire.endTransmission();                      // 接続終了
}

// ☆ 温度センサ-(ADT7410)データ読み取り
void rd_dat_ADT7410(void) {
    uint16_t ui_val ; long int in_val ; float fl_tmp ;

    Wire.beginTransmission(DVC_ADT7410);       // 接続開始
    Wire.write(0x00);                          // 読込開始アドレス指定
    Wire.endTransmission(false);               // 送信完了(接続維持)
    
    Wire.requestFrom(DVC_ADT7410 , 2);         // 2バイト要求
    ui_val = (uint16_t)Wire.read() << 8 ;      // データの読み出し(上位)
    ui_val |= Wire.read();                     // データの読み出し(下位)

    if (g_rs == 0x80){                         // 測定レンジによる処理選択
        in_val = (long int)ui_val;
        if(ui_val & 0x8000) {                  // 符号判定
            in_val = in_val - 65536 ;
        }
        fl_tmp = (float)in_val / 128.0 ;
        
    }else{
        ui_val >>= 3 ;
        in_val = (long int)ui_val;
        if(ui_val & 0x1000) {                  // 符号判定
            in_val = in_val - 8192 ;
        }
        fl_tmp = (float)in_val / 16.0 ;
    }

    r_t = fl_tmp ;
    
    Serial.print("温度 : ");
    Serial.print(fl_tmp, 5);
    // Serial.print(" , ");
    // Serial.print(ui_val);
    
    if (g_rs == 0x80){
          Serial.println("   (16bit)");
    }else{
          Serial.println("   (13bit)");
    }
}

// ①通信開始、デバイス初期化
bool DVC_INIT(uint8_t i2caddr){
    Wire.begin();                               // 接続開始
  
    wrt_reg(0x2B, 0x40);                        // DEVICE RESET ENABLED
    while (read_reg(0x2B) & 0x40);              // デバイスリセット待ち
    wrt_reg(0x0E , 0x01);                       // RANGE 設定
  
    wrt_reg(0x2B, 0x02);                        // PowerMode( 0x00:normal,0x01:Low Noise Low Power,0x02:High Resolution, 0x03:Low Power)
    wrt_reg(0x2D, 0x01);                        // INT_EN_DRDY
    wrt_reg(0x2E, 0x01);                        // INT_CFG_DRDY

    wrt_reg(0x11, 0x40);                        // PL_EN
    wrt_reg(0x2A, 0x01 | 0x04);                 // ACTIVE , LNOISE
    return true;   
}

// ②指定レジスタにデータ書き込み
void wrt_reg(uint8_t reg, uint8_t value) {
    Wire.beginTransmission(DVC_MMA8451);        // デバイス指定、通信開始
    Wire.write((uint8_t)reg);                   // レジスタ指定
    Wire.write((uint8_t)(value));               // データ書込
    Wire.endTransmission();                     // 送信完了
}

// ③指定レジスタからデータを読み出し
uint8_t read_reg(uint8_t reg) {
    Wire.beginTransmission(DVC_MMA8451);        // 送信処理開始
    Wire.write(reg);                            // レジスタ指定
    Wire.endTransmission(false);                // 送信完了(コネクション維持)
    Wire.requestFrom(DVC_MMA8451, 1);           // 1byteデータに要求

    if (! Wire.available()) return -1;          // データ有無判定
    return (Wire.read());                       // 1byteデータ  
}

// ④データ取得、変換
void read_data(void) {
    Wire.beginTransmission(DVC_MMA8451);        // 送信処理開始
    Wire.write(0x01);                           // アドレス指定
    Wire.endTransmission(false);                // 送信完了(コネクション維持)
    Wire.requestFrom(DVC_MMA8451, 6);           // 6byteデータ要求
                                                //
    // データ取得、変換
    x_r = Wire.read() ; x_r <<= 8 ; x_r |= Wire.read() ; x_r >>= 2;
    y_r = Wire.read() ; y_r <<= 8 ; y_r |= Wire.read() ; y_r >>= 2;
    z_r = Wire.read() ; z_r <<= 8 ; z_r |= Wire.read() ; z_r >>= 2;

    x_v = (long int)x_r ; y_v = (long int)y_r ; z_v = (long int)z_r ;    

    // マイナス値変換
    if(x_r & 0x2000) x_v = x_v - 16384 ;        
    if(y_r & 0x2000) y_v = y_v - 16384 ;
    if(z_r & 0x2000) z_v = z_v - 16384 ;
 
    // スケールレンジを取得
    uint8_t range = read_reg(0x0E) & 0x03;
    uint16_t div_p = 1;

    // 加速度変換係数(測定レンジにより異なる)
    if (range == 0x02) div_p = 1024 ; 
    if (range == 0x01) div_p = 2048 ;
    if (range == 0x00) div_p = 4096 ;
    x_g = (float)x_v * GRAV_E / div_p ; 
    y_g = (float)y_v * GRAV_E / div_p ; 
    z_g = (float)z_v * GRAV_E / div_p ;

    //姿勢判定(Z方向)
    //参考:http://www.robotsfx.com/robot/TriAxisSen.html
    z_sts = 0.7071 * GRAV_E ;
    if(z_g > z_sts){
        Serial.print("▲▲▲▲▲ ( ");
    }else if(z_g  > -1 * z_sts){
        Serial.print("◇◇◇◇◇ ( ");
    }else{
        Serial.print("▼▼▼▼▼ ( ");
    }
    Serial.print(z_g) ; Serial.print(" (判定値:± ") ;  Serial.print(z_sts) ; Serial.println("))") ;
}

// ⑤最初に1回だけ行う処理
void setup() {
    Serial.begin(9600);
    // デバイスアドレス指定し、初期設定
    if(!DVC_INIT(DVC_MMA8451))  {
        Serial.println("-----");
        while (1);
    }
                                                  
    uint8_t reg1 = read_reg(0x2A);              // 元設定確認
    wrt_reg(0x2A, 0x00);                        // StandByMode
    wrt_reg(0x0E, 0x00 & 0x3);                  // Set Range( 0x00:2g , 0x01:4g , 0x02:8g , 0x03:reserved )
    wrt_reg(0x2A, reg1 | 0x01);                 // ActiveMode

    LCD_INIT();                                 // ★ LCD初期化
    ADT7410_INIT();                             // ☆ 温度センサ-(ADT7410)初期化
}

// ⑥繰り返し処理(センサーを読んで、シリアル通信出力)
void loop() {
    read_data();                                // XYZ軸データ取得

    // XYZ取得値出力 ・ 角加速度データ出力(m/s^2)
    Serial.print("X:\t"); Serial.print(x_v) ; Serial.print("\t,   X m/s^2:\t"); Serial.println(x_g);
    Serial.print("Y:\t"); Serial.print(y_v) ; Serial.print("\t,   Y m/s^2:\t"); Serial.println(y_g);
    Serial.print("Z:\t"); Serial.print(z_v) ; Serial.print("\t,   Z m/s^2:\t"); Serial.println(z_g);

    rd_dat_ADT7410();                           // ☆ 温度センサ-(ADT7410)データ取得

    // ★ 測定結果をLCDに表示 ★
    sprintf(msg0, "T: %s", dtostrf(r_t,4,2,t)); // Z軸方向の加速度を文字列変換、表示用フォーマット変換
    sprintf(msg1, "a: %s", dtostrf(z_g,4,2,a)); // Z軸方向の加速度を文字列変換、表示用フォーマット変換
    
    lcd_cmd(0b00000001);                        // Clear Display
    lcd_setCursor(0, 0);                        // 表示座標設定
    lcd_printStr(msg0);                         // 表示内容(文字)
    
    // static int count = 0;
    lcd_setCursor(0, 1);                        // 表示座標設定(2行目最左)
    lcd_printStr(msg1);                         // 表示内容(文字)
    // lcd_printInt(count);                     // 表示内容(数字)
    // count++;
    
    delay(800);
}

以上で、 I2Cの同一シリアルバス上にある3つのデバイスを同時に制御できることを確認出来ました。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です