I2C接続LCD

  1. 目的、仕様


    1. 目的

      英数カナを16文字、2行まで表示できる液晶モジュール(LCD:Liquid Crystal Display)を利用して、文字表示を行います。ここでは、よく利用されるサンテイク社 SC1602 を紹介します。

    2. LCD仕様

       SC1602は1行16文字で2行まで表示可能です。表示可能な文字は英数字と半角カナで、コードはJISに従います。文字カーソル位置を指定して指定した位置に文字を表示することも可能です。
       SC1602BSLB ではバックライトの点灯も可能です(実験で使用するLCD(SC1602BS*B)にはバックライトはありません)。


    3. 構成

       LCDに文字を表示するには、文字コードを液晶モジュール(以下LCD)に送り付けます。LCD側で受け取った文字コードから内蔵の文字パターン ROM を参照し、5*7 のビットパターンを作成し、表示します。
       2行の表示が可能ですが、2行目のアドレスは先頭が40hとなります。1行目の最後のアドレス0Fhとは連続していません。注意して下さい。

       
    4. LCDの信号線

       LCDは以下の信号で接続します。VOはコントラストを設定するアナログ信号で、0Vのとき最大コントラストになります。
       データ線は4本または8本で使用します。4本の場合、下位と上位の2回に分割してデータを送ります。RSは文字コードとコマンドを区別し、R/W は読み/書きを設定します。E でデータの同期をとります。
      ピン 信号名 意味
      VCC 5V
      GND GND
      VO コントラスト
      RS 1:制御、0:データ
      R/W 1で読み、0で書き込み
      E(stb) データ同期用パルス
      7−10 BD0-DB3 下位データ
      11−14 BD4-BD7 上位データ
      ピン番号は、LCDの左側の接続端子の番号で、左下が1、その右が2ピンです。その上の左が3、右が4ピンです。

    5. コマンド

       SC1602のコマンドや初期化法についてはこちらを参照してください。

  2. 接続回路


    1. 接続回路

      LCDの上位4ビットのデータを16F873のBポートの下位4ビットに、LCDの制御信号をAポートの下位3ビットに接続します。Voはコントラスト調整用です。
      また、I2C用のSDAとSCLをブレッドボードの左下のI2Cバス端子に、プログラム書き込み用のICSPをブレッドボード左上に接続します。


    2. 回路製作

       LCDの上位4ビットのデータ信号4本(11,12,13,14ピン)、制御信号3本(4,5,6ピン)、電源、コントラスト3本(1,2,3)をヘッダピンに接続します。ヘッダピンをBポート、Aポート、電源に接続すれば完成です。
      写真には、SCL、SDAの接続はありません。


  3. プログラム


    1. コマンド

      コマンドはWSNAK版とどうようです。<lcdda> はI2Cアドレスで、ここでは0x74とします。次がコマンド種別で、1 は文字位置、2 は文字列の指定です。文字位置は最初に行内の文字位置(列番号:1-16)、次に行番号(1,2)を送ります。
       文字列コマンドの場合、文字列を送り、最後に 0 を送ります。各行の文字列は最大16文字です。文字位置は水平位置が1から16、yが1または2です。xは文字番号、yは行数になります。
       コマンド種別 3 でLCDの文字をクリアします。5 はバックライトのオン/オフ、6 はLED への表示を行います。コマンドが可変長になるため、最後に 0 が必要です。
      I2Cのマスタに対しては、use I2C で no_stretch を指定するほうが安全です。
      #use I2c(slave,sda=PIN_C4,scl=PIN_C3,address=0x74,no_stretch,FORCE_HW)
      文字位置指定:<lcdda><1><x><y><0>  ;x=1..16 y=1,2
      文字列表示 :<lcdda><2><char_1>..<char_n><0>
      クリア     :<lcdda><3><0>

    2. ソース

      以下はソースです。受信用バッファと表示用バッファを分離すれば、マスタ側の遅延が少なくなります。
      //I2C Slave
      //Display to LCD
      //SD1602:16chara * 2
      //LCD data:DB7:14> RB3,DB6:13> RB2,DB5:12> RB1,DB4:11> RB0
      //LCD E:6>RA0,RW:5>RA1,RS:4>RA2,
      
      //send address:<lcdda><1><x><y><0>
      //send string :<lcdda><2><char_1>..<char_n><0>
      //clear lcd:<lcdda><3><0>
      //send command:<lcdda><4><command><para><0>
      
      
      #include <16F873A.h>
      #include <stdlib.h>
      
      #fuses HS,NOWDT,NOLVP,NOBROWNOUT
      #use delay(clock = 20000000)      
      //#use I2c(slave,sda=PIN_C4,scl=PIN_C3,address=0x74,no_stretch,FORCE_HW)//address must be EvenNumber
      #use I2c(slave,sda=PIN_C4,scl=PIN_C3,address=0x74,no_stretch,FORCE_HW)
      #define LineMax 16
      
      //LCD connection
      //#byte db = 6  // PORTB address
      #define Amode 0xF0 //Aport I/O 
      #define rs PIN_A2 //chip select
      #define rw PIN_A1 //read/write
      #define stb PIN_A0 //E strobe
      
      void outchbuf(int i);
      
      void lcd_init();//-------- initialize
      void lcd_ready();//------- busy check
      void lcd_cmd(cmd);//------ send command
      void lcd_data(string);//-- display string
      void lcd_clear(); //------ clear display
      int lcd_status(); //
      
      int ch2;
      int state,done;
      int cp,lpcnt;
      int alt;
      int ddcm;
      char chbuf[17]={2,'w','a','t','i','n','g','\0'};
      
      
      #INT_SSP
      void ssp_interupt (){
      
          output_toggle(PIN_C7);
         state=i2c_isr_state();
         //output_high(PIN_A3);//backLight on
         
         if(state>=0x80){//addrs match with master read         
                        
          }
         else if(state >0){//adrs match with master write
                ch2=i2c_read();
                 chbuf[cp++]=ch2;
                         
                 if(cp > LineMax) cp--;
                  
                 if (ch2=='\0') {
                         //output_toggle(PIN_A3);//check
                         cp=0;
                         done=1;
                 }       
          }   
      }
      
      void main (){
       
            delay_ms(200);
            cp=0;       
            alt=0;
            done=0;
            lpcnt=0;
      
            
            output_low(stb);
            output_low(rs);
            
            lcd_init();//set 4bit mode
            lcd_clear();
      
            enable_interrupts(INT_SSP);     //SSP割り込みを許可
            enable_interrupts(GLOBAL);      //全ての割り込みを許可
      
            lcd_cmd(0x80);//1 line
            lcd_data("helloo");
            
            lcd_cmd(0xC0);//2 line
            lcd_data("wait");
      
            while (1) { 
      
            //lcd_cmd(0x80);//1 line
           //lcd_data("hello");
                                                          
           if(done){
                 switch(chbuf[0]){
                    case 1:ddcm=0x80 + chbuf[1]-1;//firstLine
                           if (chbuf[2]==2) ddcm += 0x40;
                            lcd_cmd(ddcm);//行アドレス
                            break;
      
                    case 2 ://文字列
                            outchbuf(1);
                            break;
      
                    case 3:lcd_cmd(0x1);//clear
                            break;
                    
                    case 4:lcd_cmd(chbuf[1]);
                            outchbuf(2);
                            break;
                     
                       //done=0;              
                    }
                 } //if   
            //lpcnt++;
            done=0;
            delay_ms(10);
              
          }    
      }
      
      void outchbuf(int i){
       while(chbuf[i] != 0){
              lcd_data(chbuf[i]);
              i++;
       }
      }       
      
      //lcd command out for init
      void lcd_incmd(int cmd){
              //db = cmd;                     //mode command
        output_b(cmd);
              output_low(rw); //set write
              output_low(rs); //set rs low
              output_high(stb);       //strobe
              output_low(stb);
              delay_us(100);
      }
      
      //initialize LCD
      //set 4bit mode
      void lcd_init(){
              
              delay_ms(15);
              lcd_incmd(0x30);                        //8bit mode set
      
              delay_ms(5);
              lcd_incmd(0x30);                        //8bit mode set
              
              delay_ms(5);
              lcd_incmd(0x30);                        //8bit mode set
      
              delay_ms(5);
              lcd_incmd(0x20);                        //4bit mode set
              
              delay_ms(5);
              lcd_cmd(0x2C);          //DL=0 4bit mode
              lcd_cmd(0x08);          //display off C=D=B=0
              lcd_cmd(0x0C);          //display on C=D=1 B=0
              lcd_cmd(0x06);          //entry I/D=1 S=0
              
              //delay_ms(2);
         while(bit_test(lcd_status(),7));
      }
      
      
      //send data to LCD
      void lcd_data(int asci){
              //db = asci>>4 ;                //set upper data
        output_b(asci>>4);
              output_low(rw);         //set write
              output_high(rs);        //set rs high
              output_high(stb);       //strobe
              output_low(stb);
              
              //db = asci;            //set lower data
        output_b(asci);
              output_high(stb);       //strobe
              output_low(stb);
              
              //delay_us(50);
              while(bit_test(lcd_status(),7));//check busy
      }
      
      //read status
      int lcd_status(){
              
              int high,low;
      
              set_tris_b(Amode | 0x0F);       //set input mode
              output_low(rs);
              output_high(rw);//read mode
              output_high(stb);       
              //high=db & 0xF;//input A ,read upper
        high=input_b()&0XF;   
              output_low(stb);
      
              delay_us(10);
              output_high(stb); 
              //low=db & 0xF;         //inputA ,read lower
        low=input_b()&0XF;
              output_low(stb);
              
              set_tris_b(Amode);
              
              return(high<<4 | (low));        //return status
      }
      
      //send command
      void lcd_cmd(int cmd){
              
              //db = cmd>>4 ;  //set upper data & set PA4=0
        output_b(cmd>>4 );
              output_low(rw);   //set write
              output_low(rs);   //set rs low
              output_high(stb);       //strobe
              output_low(stb);
      
              //db = cmd;       //set lower data
        output_b(cmd );
              output_high(stb);  //strobe
              output_low(stb);
      
              while(bit_test(lcd_status(),7));
      }
      
      
      //  clear display clear function
      void lcd_clear(){
              lcd_cmd(1);  //initialize command
              //delay_ms(10);
         lcd_cmd(2);
          
        while(bit_test(lcd_status(),7));
      }

    3. マスタプログラム

      参考のためマスタのテストプログラムをのせます。タイマー1の割り込みで約1秒単位でtmを計数します。表示はmain()で200mS単位で行います。基本クロックが88の内部クロックのため、正確ではありません。
       LCDで数字を表示する場合、桁数指定をして文字数を合わせないと、前の表示が残ってしまいます。また、intは符号なし1バイト整数ですから、表示フォーマットは %u になります。2バイト整数の場合 %ld です。コマンド間には数m秒の遅延が必要です。
       一般に main() の初期化部ではクライアントの立ち上がる時間を待ち合わせる必要があります。LCD では 300mS程度が必要で、100mS では初期表示画面でフリーズすることがあります。
      //////////////////////////////////////////////////
      // I2Cマスターテストプログラム      //  
      //LCD制御
      //I2Cデバイスアドレスは0x74
      
      //コマンド 
      //send address:<lcdda><1><x><y><0>
      //send string :<lcdda><2><char_1>..<char_n><0>
      //clear lcd:<lcdda><3><0>
      //send command:<lcdda><4><command><para><0>
      
      //マスタの回路は、C4,C3をI2C接続
      //ポートB、A0 をLED表示(動作確認用)
      
      #include <16f873a.h>
      #include <stdlib.h>
      
      //#include <string.h>
      
      #fuses HS,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT
      #use delay(clock = 20000000)      
      #use i2c(MASTER,sda=PIN_C4,scl=PIN_C3,FORCE_HW)
      
      //メイン関数/////////////////////////////////////////
      #define lcddva 0x74
      
      
      int i,val;
      int tm,num;
      int32 loopcnt;
      char moji1[]="Hai:";
      char moji2[]="num :";
      char numbuf[6];
      char lbuf[15];
      
      void main(){
      
          val=0;
          tm=0;
          loopcnt=0;
          //set_tris_c(0x99);          //RB 7-4:IN 3-0:OUT
           
          output_float(PIN_C3);       //I2C pin float
          output_float(PIN_C4);       //I2C pin float 
          //Cポートに出力する場合、set_tris_c()が必要 
              
          delay_ms(300); //wait for Client up  
            i2c_start();      //clear
            i2c_write(lcddva);
                i2c_write(3);
                i2c_write(0);
           i2c_stop();
           delay_ms(50);
             
          while(1){
      
           output_toggle(PIN_A0);//動作確認
      
           i2c_start();      //clear
           i2c_write(lcddva);
               i2c_write(3);
               i2c_write(0);
           i2c_stop();
           delay_ms(100);
      
      //-----------------------------------
          itoa(loopcnt,10,numbuf);
          strcpy(lbuf,moji1);
          strcat(lbuf,numbuf);
                               
                i2c_start(); 
              i2c_write(lcddva);
              i2c_write(1);//コマンド             
          i2c_write(4);//x
          i2c_write(1);//y
          i2c_write(0);
              i2c_stop();
              delay_ms(10);
      
          itoa(loopcnt,10,numbuf);
          strcpy(lbuf,moji2);
          strcat(lbuf,numbuf);
      
          i2c_start(); 
              i2c_write(lcddva);
          i2c_write(2 );              
              for(i=0;i<=strlen(lbuf);i++){
                i2c_write(lbuf[i] );
                //delay_ms(1);
              }
          //i2c_write(0);
              i2c_stop();
              delay_ms(20);
        
              //------------------------------------
               i2c_start(); 
              i2c_write(lcddva);
              i2c_write(1);//コマンド             
          i2c_write(2);//x
          i2c_write(2);//y
          i2c_write(0);;
              i2c_stop();
          delay_ms(10);
      
          i2c_start(); 
              i2c_write(lcddva);
          i2c_write(2 );              
              for(i=0;i<=strlen(lbuf);i++){
                i2c_write(lbuf[i] );
                //delay_ms(1);
              }
              i2c_stop();
              delay_ms(20);
                              
              //------------------------------------
               i2c_start(); 
              i2c_write(lcddva);
              i2c_write(1);//コマンド             
          i2c_write(12);//x
          i2c_write(2);//y
          i2c_write(0);;
              i2c_stop();
          delay_ms(10);
      
          i2c_start(); 
              i2c_write(lcddva);
          i2c_write(2 );              
              for(i=0;i<=strlen(moji1);i++){
                i2c_write(moji1[i] );
          }
              i2c_stop();
              delay_ms(10);
                                                                      
               loopcnt++;                                
           delay_ms(400);
      
               }
               
      }