I2C接続LCDを制御する
 

 I2CインターフェイスLCD、秋月のAQM0802−RN-GBW(8文字x2行)を制御します。コントローラはST7032iを使っており、同じコントローラを使っているストロベリーリナックスのLCDでも同じように制御可能です。

(1)I2C(Inter-Integrated Circuit)通信とは
 I2CまたはIICと表示され、アイスクウエーアCと発音されているようです。フィリップ社の提唱したインターフェース方式(I 2 C バス仕様およびユーザーマニュアル)で、数10cm以内の同じ基盤上のデバイス間で、1台のマスターと1台以上のスレーブとの間をSCL(Serial Clock Line)とSDA(Serial Data Line)の2本の信号線の状態の組み合わせで行う通信方式です。


(2)I2C通信の仕様

 マスタの動作を中心にした以下の通信状態の組み合わせがあります。


1)スタートコンデション

2)ストップコンディション

3)ビット1の送信



4)ビット0の送信

5)肯定応答(ACK)

6)否定応答(NO ACK)


    1)スタートコンデション
      SCLとSDAがともにHighの状態でSDAがHighからLowに
      変化する状態を、通信を開始するStart状態になると呼ぶ

    2)ストップコンディション
      SCLがHighのとき、SDAがLowからHighに変化する状
      態を通信停止(Stop)状態と呼ぶ。

    3)ビット1の送信
      SDAがHighの状態で、SCLをL → H → L と変化
      させると1が送信される

    4)ビット0の送信
      SDAがLowの状態で、SCLをL → H → L と変化
      させると0が送信される

    5)肯定応答(ACK)
      SDA端子を入力状態にして、SCLをHighにしてSDAの状態を読み込む。
      状態がLowならば、ACK状態となる。応答確認後、SCLをLowにする。

    6)否定応答(NO ACK)
      SDA端子を入力状態にして、SCLをHighにしてSDAの状態を読み込む。
      状態がHighならば、NO ACK状態となる。応答確認後、SCLをLowにする。

    7)クロックストレッチ(Clock Stretch)
      スレーブ側がクロックLowにすると、マスタ側からの送信を待機させることができます。
      スレーブ側からの送信処理が間に合わないときなどに適用されます。


(3) I2C通信の流れ

 I2Cは、1本の信号線上に複数のデバイスを繋ぐことができます。従って、実際にデバイスと通信を行うには、スレーブ相手のアドレスを指定する必要があります。通信データのフォーマットは以下のようになっています。

 通信は全てマスタ側の主導で開始されます。まず、マスタがStart Conditionで通信開始を宣言し、それに続いて、8ビット単位で上位ビットからデータ送信を開始します。まず、アドレスを送信します。アドレスは7ビットで指定します。アドレスの後のR/Wは、

 R/W  0:Write(スレーブデバイスにマスタからデータを送信するとき)
      1:Read (スレーブデバイスからマスタに送信するとき)

そのアドレスをスレーブ側が正常受信できたらACK応答を返してきます。それを確認し、引き続き

 データ送信(8ビット) + ACK応答   

を必要回数繰り返し、マスタ側のStop Conditionで通信を終了するまで続きます。

           デ ー タ 通 信 の 流 れ
Start →

 
アドレス
(7ビット)
 
 
+R/W →
 
ACK→

 
データ送信→
(8ビット) 
 
ACK→

 
 ・・・Stop

 
 
 

(4) ST7032iコントローラを使ったLCDの仕様

 基本的なコマンドは、一般的なLCDであるSC1602と同じです。この液晶のスレーブアドレスは、011 1110(7ビット形式)固定です。このデバイスに対してはWriteしかできませんので、送信アドレスは 0111110+R/W を送信しますが、ライトオンリーデバイスですのでR/Wは0となり、8ビット形式で01111100(0x7c)になります。

    Slave Address送信


 次にControl byte を1バイト送ります。
Co RS

Coは連続データ最終コマンド 1:連続してデータを送るときに指定します。
               0:最終データの時に指定します。

RSはデータ、コマンド指定   1:データ送信の時に指定します。
               0:コマンド送信の時に指定します。

 

 従って、以下のような通信の流れになります。

アドレス送信
0x7c
 
A
C
K
コントロールバイト
0x00
 
A
C
K
コマンド
(8バイト)
 
A
C
K
コントロールバイト
0x40
 
データ
(8バイト)
 
A
C
K

・・・・・
 

LCDの初期化はマニアルに記載されているとおりで、以下のようになります。
     100μs待ち
        ↓
       0x38 ・・・ファンクションセット 8ビット21ライン Normal mode
        ↓
     30μs待ち
        ↓
       0x39 ・・・ファンクションセット 8ビット21ライン Exteded mode
        ↓
     30μs待ち
        ↓
       0x14 ・・・Internal CSC Frequency 183Hz Bias 1/5
        ↓
     30μs待ち
        ↓
       0x35 ・・・コントラストセット
        ↓
     30μs待ち
        ↓
       0x56 ・・・Power Icon Contrast control
        ↓
     30μs待ち
        ↓
       0x6c ・・・Follow control
        ↓
     30μs待ち
        ↓
       0x38 ・・・Set normal mode
        ↓
     30μs待ち
        ↓
       0x30 ・・・Display ON
        ↓
     30μs待ち
        ↓
       0x01 ・・・Clear Display
        ↓
     10ms待ち


(5)書き込みの基本関数

 int I2C_write(unsigned char data)
 {
    int i;
    int ack;
    I2C_SDA_OUT;     ・・・ SDA端子に指定したP1_0を出力に設定する
    for(i=0;i<8;i++){   ・・・ 1バイトのデータを1ビット毎に8回の
                    クロックと共に送信
        if(data & 0x80){ ・・・ 最上位ビットから順にビットを確認
          I2C_S_SDA; ・・・ ビットが1ならSDA端子の値を1にする
        }
        else{
          I2C_C_SDA;    ・・・ ビットが0ならSDA端子の値を0にする
        }
        delay();      ・・・ 送信時間待ち
        I2C_H_CLK();   ・・・ クロックをHighにした後
        I2C_L_CLK();   ・・・ クロックをLowにすることで1ビット送信される
        data<<=1;    ・・・ 送信対象ビット上位側に1ビットシフトする
    }             ・・・ 以上の動作を8回繰り返すことによって1バイト送信される
    I2C_SDA_IN;       ・・・ SDA端子に指定したp1_0を入力に設定する
    I2C_H_CLK();       ・・・ CLK端子をHighにしたのち
    I2C_L_CLK();       ・・・ Lowにするとack確認状態になる
    ack=I2C_SDA_data;    ・・・ ack信号を受信する
    I2C_SDA_OUT;      ・・・ SDA端子p1_0を出力にする
    delay();        ・・・ 時間待ち
    return ack;        ・・・ ack信号を返す
 }

(6)読み込みの基本関数

  戻り値 :読み込んだ8ビットデータ

 static unsigned char I2C_read(void)
 {
   int i;
   unsigned char data;
   data = 0;
   I2C_SDA_IN;    ・・・ SDA端子に指定したp1_0を入力に設定する
   for(i=0;i<8;i++){
     data<<=1;    ・・・ 最上位ビットから入力していく
     I2C_S_SCL;     ・・・ I2Cクロック端子p1_1を1にする
     delay();
     data |=I2C_SDA_data;・・・データポートp1_0の値をdataに入れる
     I2C_C_SCL;     ・・・ I2Cクロック端子p1_1を0にすることで1ビット受信
     delay();     ・・・ 8回繰り返すことによって1バイト受信する
   }
   I2C_S_SCL;       ・・・ stopコンディション
   delay();
   I2C_S_SDA;
   delay();       ・・・ 通信停止完了する
   I2C_C_SDA;
   delay();
   I2C_C_SCL;       ・・・ 次の通信準備へ遷移
   delay();
   return data;      ・・・ 受信データを返す
 }

(7) プログラムの作成

 本プログラムでは、SCL(シリアルクロックライン)をp1_1、SDA(シリアルデータライン)をp1_0に指定しています。M12AではI2Cはハード的には実装されていませんので、ソフトでI2Cを実装しています。従って、端子定義を変更するだけで、自由に他のポートに変更することが可能です。


1)メインプログラム M12_I2C_LCD.c

  #include ”sfr_r8m12a.h”; /* R8C/M12A SFRの定義ファイル */
  #include ”i2clcd_ctrl.h”;

  unsigned int i;

  void main(void)
  {

    init();
    I2C_LCD_init(); // LCD初期化
    I2C_SDA_OUT; // SDAピン出力に設定
    I2C_LCD_cursor(1,0); // 1行目0より表示
    I2C_LCD_str("アキツキLCD"); // 表示文字列出力
    wait_ms(1000);
    I2C_LCD_cursor(2,0); // 2行目0より表示
    I2C_LCD_str("テスト");
    wait_ms(1000);
    /* I2C_LCD_clear(); // LCDクリヤーの後は最低でも2ms以上ウエイト*/
    while(1);
  }

  void init(void)
  {
    int i;

    /**** 高速オンチップオシレータで動作する場合 ****/
    prc0 = 1; // プロテクトレジスタ解除
    hocoe = 1; // 高速オンチップオシレータ発振開始
    for(i=0;i<50;i++); //発振安定まで待つ
    hscksel =1; // 高速オンチップオシレータ選択ビットを1にする
    scksel =1; // システム基準クロックを高速にする
    prc0 = 0; // プロテクトレジスタ禁止
    pd1 =0xff; // ポートディレクションレジスタ1 全て出力ポート設定
    p1 =0x00;
  //pur4 =0b11100100;
  //pd4 =0x04; //p4_2(TXD0)出力p4_6(RXD0)入力
  }


2)LCD制御関数 i2Clcd_ctrl_.c

  #include "sfr_r8m12a.h" /* R8C/M12A SFRの定義ファイル */
  #include "i2clcd_ctrl.h"

  /*液晶初期化関数*/
  void I2C_LCD_init(void)
  {
    wait_ms(100);
    I2C_LCD_cmd(0x38); // Function set 8bit 21Line Normal mode
    wait_us(30);
    I2C_LCD_cmd(0x39); // Function set 8bit 21Line Extended mode
    wait_us(30);
    I2C_LCD_cmd(0x14); // Internal OSC Frequency 183Hz BIAS 1/5
    wait_us(30);
    I2C_LCD_cmd(0x35); // Contrast set 0x70:2.7V 〜 0x30:3.0v 0x35
    wait_us(30);
    I2C_LCD_cmd(0x56); // Power Icon Contrast control
    wait_us(30);
    I2C_LCD_cmd(0x6c); // Follow control 3.3v
    wait_ms(400);
    I2C_LCD_cmd(0x38); // Set normal mode
    wait_us(30);
    I2C_LCD_cmd(0x0c); // Display ON
    wait_us(30);
    I2C_LCD_cmd(0x01); // Clear Display
    wait_ms(10);
  }

  /+タイマ本体 us単位のwait関数*/
  void wait_us(unsigned long usec)
  {
    usec = (usec * CLOCK) / 45000;
    while(usec --);{}
  }

  /* タイマ本体 ms単位のwait関数 */
  void wait_ms(unsigned long msec)
  {
    unsigned int i;
    for(i=0;i<msec;i++){
        wait_us(1000);
    }
  }

  /* 6usの遅れ関数 */
  void delay(void)
  {
    unsigned char i;
    for(i=0;i<5;i++){}
  }

  /* 開始条件 */
  void I2C_start(void)
  {
    I2C_SDA_OUT;
    I2C_H_CLK();
    I2C_S_SDA;
    delay();
    I2C_C_SDA;
    delay();
    I2C_L_CLK();
    delay();
  }

  /* 終了条件 */
  void I2C_stop(void)
  {
    I2C_SDA_OUT;
    I2C_L_CLK();
    I2C_C_SDA;
    delay();
    I2C_H_CLK();
    delay();
    I2C_S_SDA;
    delay();
  }

  /* クロックをHighにする */
  void I2C_H_CLK(void)
  {
    I2C_S_SCL;
    delay();
  }

  /* クロックをLowにする */
  void I2C_L_CLK(void)
  {
    I2C_C_SCL;
    delay();
  }

  /* ACKNOWLEDGE */
  void ack(void)
  {
    I2C_SDA_IN;
    I2C_S_SCL;
    delay();
    I2C_C_SCL;
    delay();
    I2C_SDA_OUT;
  }

  /* 書き込みの基本関数 */
  /* 引数 data 8ビットデータ */
  /* 戻り値 ack */
  int I2C_write(unsigned char data)
  {
    int i;
    int ack;
    I2C_SDA_OUT;
    for(i=0;i<8;i++){ // 1ビト毎にクロックと共に送信
        if(data & 0x80){
            I2C_S_SDA;
        }
        else{
            I2C_C_SDA;
        }
        delay();
        I2C_H_CLK();
        I2C_L_CLK();
        data<<=1;
    }
    I2C_SDA_IN;
    I2C_H_CLK();
    I2C_L_CLK();
    ack=I2C_SDA_data;
    I2C_SDA_OUT;
    delay();
    return ack;
  }

  /* 読み込み基本関数 */
  /* 引数 なし */
  /* 戻り値 読み込んだ8ビットデータ */
  static unsigned char I2C_read(void) 
  {
    int i;
    unsigned char data;

    data=0;
    I2C_SDA_IN;
    for(i=0;i<8;i++){
        data<<=1;
        I2C_S_SCL;
        delay();
        data |=(I2C_SDA_data);
        I2C_C_SCL;
        delay();
    }
    I2C_S_SCL;
    delay();
    I2C_S_SDA;
    delay();
    I2C_C_SDA;
    delay();
    I2C_C_SCL;
    delay();
    return data;
  }

  /* 液晶へのコマンド出力関数 */
  void I2C_LCD_cmd(unsigned char cmd)
  {
    I2C_start(); // スタートコンディ゙ション
    I2C_write(AD); // アドレス指定
    // C0+RS+000000 C0=1:連続で通信可 C0=0:stopコンディションで終了
    // RS=0:次のバイトでコントロールコード送信可能
    // I2C_write(0x80); // 次はコマンド出力を指定 連続
    I2C_write(0x00); // 次はコマンド出力を指定 連続ではない
    I2C_write(cmd); // コマンド送信出力
    I2C_stop(); // ストップコンディション
    wait_us(50);
    // delay();
  }

  /* 液晶への1文字出力関数 */
  void I2C_LCD_data(unsigned char data)
  {
    I2C_start(); // スタートコンディ゙ション
    I2C_write(AD); // アドレス指定
    // C0+RS+000000 C0=1:連続で通信可 C0=0:stopコンディションで終了
    // RS=1:次のバイトでデータ出力を指定
    // I2C_write(0xc0); // 次はデータ出力を指定 連続
    I2C_write(0x40); // 次はデータ出力を指定 連続ではない
    I2C_write(data); // 1文字データ出力
    I2C_stop(); // ストップコンディション
    wait_us(50);
    // delay();
  }

  /* 液晶への文字列表示関数 */
  void I2C_LCD_str(char* ptr)
  {
    while(*ptr !=0){
      I2C_LCD_data(*ptr++);
    }
  }

  /* 液晶画面のクリャー関数 */
  void I2C_LCD_clear(void)
  {
    I2C_LCD_cmd(0x01);
  }

  /* 液晶のカーソル位置設定関数 */
  void I2C_LCD_cursor(unsigned char line,unsigned char cursor)
  {
    if(line == 1){
        I2C_LCD_cmd(0x80 | cursor);
    }
    else{
        I2C_LCD_cmd(0xc0 | cursor);
    }
  }



3)ヘッダーファイル i2clcd_ctrl.h

  #define I2C_S_SCL p1_1=1 // I2Cクロック p1_1を1にする
  #define I2C_C_SCL p1_1=0 // I2Cクロック p1_1を0にする
  #define I2C_S_SDA p1_0=1 // I2Cデータ p1_0を1にする
  #define I2C_C_SDA p1_0=0 // I2Cデータ p1_0を0にする
  #define I2C_SDA_IN pd1_0=0 // p1_0を入力ポートに設定
  #define I2C_SDA_OUT pd1_0=1 // p1_0を出力ポートに設定
  #define I2C_SDA_data p1_0 // データポートをp1_0に設定
  #define AD 0x7c // アドレス 0B 0111 110+0(c/w)
  #define CLOCK 20000
  /*======================================*/
  /* プロトタイプ宣言 */
  /*======================================*/
  void init(void);
  void wait_us(unsigned long usec); // us単位のタイマ
  void wait_ms(unsigned long msec); // ms単位のタイマ
  void delay(void); // 6usの遅延関数
  void I2C_start(void); // I2C開始
  void I2C_stop(void); // I2Cストップ
  void ack(void); // ACKNOWLEDGE信号受信
  void I2C_H_CLK(void); // クロックHigh
  void I2C_L_CLK(void); // クロックLow
  void I2C_LCD_init(void); // lcd初期化関数
  int I2C_write(unsigned char data); // 書き込みの基本関数
  static unsigned char I2C_read(void); // 読み込みの基本関数
  void I2C_LCD_cmd(unsigned char cmd); // 液晶へのコマンド出力関数
  void I2C_LCD_data(unsigned char data);// 液晶への1文字出力関数
  void I2C_LCD_str(char* ptr); // 液晶への文字列表示関数
  void I2C_LCD_clear(void); // 液晶のクリャー関数

  // カーソル位置設定関数 line(行数):1 or 2 cursor(左端からの位置):0〜f
  void I2C_LCD_cursor(unsigned char line,unsigned char cursor;






基 盤 半 田 面 配 線 図

VR:可変抵抗器は10kΩ



秋月LCD付属基盤半田面

秋月LCD AQMC802-RN-GBW 実行画面

ST7032iを使っている他のI2C-LCDも表示できます

◎参考文献、参考サイト

 CQ出版社、中尾 司「マイコンの1線2線3線インターフェイス活用入門」

R8C/10 グループ汎用ポートによる I2C-BUS 制御プログラム

プロジェクトファイルのダウンロード


メニューに戻る