I2C接続LCD

  1. I2C接続LCD


    1. I2C

       I2Cインタフェースについては、別項を参照してください。このインタフェースは2本のバスラインで複数の回路を接続できます。

    2. LCD回路

       wsNAKさんのボードを利用しています。小型の液晶文字表示を行います。英数字と記号が表示可能です。他に3個のLED表示とスイッチ入力が可能です。LCD液晶の詳細は、こちらを参照してください。


       wsNAKさんの回路図を参照させていただきます。LCDは4bit接続で、Aポートの下位4ビットを利用しています。
       LCDのバックライトは、PA3 で制御できますが、PA3は液晶への文字表示にも利用していますから、文字表示したら再設定する必要があります(文字を送るたびにバックライトがちらつくことになります)。バックライトの制御は、基板のジャンパーで固定にすることもできます。

    3. コマンド

      <lcdda> はI2Cアドレスで、0x74です。次がコマンド種別で、1 は文字位置、2は文字列の指定です。続けて、文字列を送り、最後に 0 を送ります。各行の文字列は最大16文字です。文字位置は水平位置が1から16、yが1または2です。xは文字番号、yは行数になります。
       コマンド種別 3 でLCDの文字をクリアします。5 はバックライトのオン/オフ、6 はLED への表示を行います。コマンドが可変長になるため、最後に 0 が必要です。
      文字位置指定:<lcdda><1><x><y>  ;x=1..16 y=1,2
      文字列表示 :<lcdda><2><char_1>..<char_n><0>
      クリア     :<lcdda><3><0>
      バックライト:<lcdda><5><2/1><0>
      LED表示:<lcdda><6><led_Pat+1><0>

  2. プログラム


    1. 接続試験用マスタソース

      I2C接続LCDの試験用プログラムです。最初に "hello" と"stand by"を表示します。バックライトの点滅、LED の表示、などのテストも行います。(注:コマンド設計にバグがあり、コマンドとスレーブプログラムを変更しました。プログラムテストはこのマスターソースでは実行していませんから不具合があるかもしれません)

      //////////////////////////////////////////////////
      // I2Cマスターテストプログラム      //  
      //LCD制御
      //I2Cデバイスアドレスは0x74
      //コマンド 1:文字列、2:文字列、3:クリア
      
      //ポートB、A0 をLED表示(動作確認用)
      
      #include <16f88.h>
      
      #fuses INTRC_IO,NOWDT,NOPROTECT,NOLVP,NOBROWNOUT
      #use delay(clock = 4000000)           
      #use i2c(MASTER,slow,sda=PIN_B1,scl=PIN_B4)
      
      #use fast_io(B)
      #use fast_io(A)
      
      //メイン関数/////////////////////////////////////////
      #define lcddva 0x74
      #define Leddva 0x64
      #define seg6dva 0x62
      
      #include <string.h>
      
      int i,val;
      int tm,num;
      int loopcnt;
      char moji1[]="Hello";
      char moji2[]="stand by";
      
      void main(){
      
          val=0;
          tm=0;
          loopcnt=0;
          //set_tris_c(0x99);          //RB 7-4:IN 3-0:OUT
           
          output_float(PIN_B1);       //I2C pin float
          output_float(PIN_B4);     
          //Cポートに出力する場合、set_tris_c()が必要 
          set_tris_a(0xF0);          //RB 7-4:IN 3-0:OUT
          set_tris_b(0xED);   
          delay_ms(100); //wait for Client up  
             
          while(1){
      
          output_bit(PIN_A3,tm);//動作確認
          tm = ~tm;
                          
          i2c_start();       
         i2c_write(lcddva);
         i2c_write(1);//番地コマンド 
         i2c_write(1);
         i2c_write(2);//行
         i2c_write(0);
         i2c_stop();
         delay_ms(1);
      
         i2c_start();
         i2c_write(lcddva);
         i2c_write(2);//文字            
         for(i=0;i<=strlen(moji1);i++){
              i2c_write(moji1[i] );
           }
         i2c_stop();
         delay_ms(1);
      
         i2c_start();       
         i2c_write(lcddva);
         i2c_write(1);
         i2c_write(1);
         i2c_write(1);//1行 
         i2c_write(0);
         i2c_stop();
         delay_ms(1);
      
         i2c_start();       
         i2c_write(lcddva);      
         i2c_write(2);//文字               
         for(i=0;i<=strlen(moji2);i++){
              i2c_write(moji2[i] );
           }
         i2c_stop();
                                                      
         loopcnt++;                        
         delay_ms(500);
      
       }         
      }

    2. I2C接続スレーブソース

       割り込みで、0 がクルまで chbuf[] に受信し、0 を受信したら、main 側で、コマンド種別に対応する処理を行います。
      (バグ発見:バックライトの設定で 0 を受信すると、次の文字は受信していません。コマンド種別1,2 以外は、固定長としコマンドの最後の0は不要とするよう変更する必要があります。)
      //I2C Slave
      //Display to LCD
      //SD1602:16chara * 2
      
      //LCD data:DB7 RA3,DB6 RA2,DB5 RA1,DB4 RA0
      //LCD E:RB3,RW:RB7,RS:RB6,
      //CN2: GND,+5V,SCL,SDA
      //CN1:VPP,VDD,GND,PDA,PCL
      //LED1:RA0,LED2:RA1,LED3:RA2,BackLight:RA3,COMON:RA4
      //SW1:RA1,SW2:RA2,SW3:RA3 COMMON RB0
      
      //setcursor :<lcdda><1><x+1><y> x=1-10 y=1,2
      //string:<2><char_1>..<char_n><0>
      //clear lcd:<lcdda><3><0>
      //send command(No effect):<lcdda><4><command><para><0>
      //backlight :<lcdda><5><2/1><0>
      //set led  :<lcdda><6><led_Pat+1><0>
      //wait after command send
      
      //問題
      //LCDのみ出力してもLEDが点滅する
      //2行目のデータが1行目に表示される
      
      #include <16F88.h>
      #include <stdlib.h>
      
      #fuses HS,NOWDT,NOLVP,NOBROWNOUT
      #use delay(clock = 4000000)      
      #use I2c(slave,sda=PIN_B1,scl=PIN_B4,address=0x74,FORCE_HW)//address must be EvenNumber
      
      #use fast_io(B)
      #use fast_io(A)
      
      #define LineMax 16
      
      //LCD connection
      #byte db = 5  // PORTA address
      #define Amode 0xE0 //Aport I/O 
      #define rs PIN_B6 //chip select
      #define rw PIN_B7 //read/write
      #define stb PIN_B3 //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;
      int alt;
      char chbuf[17]={2,'h','e','l','l','o','\0'};
      int backLight=2;
      int ledPat=0;
      int ddcm;
      
      #INT_SSP
      void ssp_interupt (){
          
         state=i2c_isr_state();
         //output_high(PIN_A3);//backLight on
         
         if(state>=0x80){//addrs match with master read         
                 
                set_tris_a(0x07);
                output_low(PIN_B0);     
                i2c_write((input_a()>>1) & 0x7);//return sw value
                set_tris_a(0xE0);
                
          }
          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 (){
       
            set_tris_b(0x36);    
            set_tris_a(0xE0);
            
            cp=0;       
            alt=0;
            //lpcnt=0;
                 
            output_float(PIN_B4);  //I2C pin float
            output_float(PIN_B1);  //I2C pin float
            
            set_tris_b(0x36);
            output_low(stb);
            output_low(rs);
            
            lcd_init();//set 4bit mode
            lcd_clear();
      
            lcd_cmd(0xC0);
            lcd_data("stand by");
            lcd_cmd(0x80);
            
      
            enable_interrupts(GLOBAL);      //全ての割り込みを許可
            enable_interrupts(INT_SSP);     //SSP割り込みを許可
      
            //delay_ms(1000);
            set_tris_a(0xE0);
      
            done=1;
           output_bit(PIN_A4,1);
      
            while (1) {
                                                                  
              if(done){
                 switch(chbuf[0]){
                    case 1:ddcm=0x80 + chbuf[1]-1;//firstLine
                            if (chbuf[2]==2) ddcm += 0x40;//second line
                            lcd_cmd(ddcm);
                            break;
      
                    case 2 :outchbuf(1);//string
                            break;
      
                    case 3:lcd_cmd(0x1);//clear
                            break;
                    
                    case 4://lcd_cmd(chbuf[1]);
                            //outchbuf(2);
                            break;
      
                    case 5:backLight=chbuf[1]-1;
                           break;
      
                    case 6:ledPat=chbuf[1]-1;//LED
                           break;
                     
                                      
                    }
                 done=0;
                 }
      
          
              output_bit(PIN_A0,bit_test(ledPat,0));//LED
              output_bit(PIN_A1,bit_test(ledPat,1));
              output_bit(PIN_A2,bit_test(ledPat,2));
              output_bit(PIN_A4,0);
              //delay_ms(10);
           output_bit(PIN_A4,1);
                  
              output_bit(PIN_A3,bit_test(backLight,0));//backLight
               
          //lpcnt++;
              //delay_ms(50);
              
         }     
      }
      
      void outchbuf(int i){
       while(chbuf[i] != 0){
              lcd_data(chbuf[i]);
              i++;
           //delay_us(10);
       }
      }       
      
      //lcd command out for init
      void lcd_incmd(int cmd){
              db = cmd;       //mode command
              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 | 0x10;    //set upper data
              output_low(rw); //set write
              output_high(rs);        //set rs high
              output_high(stb);       //strobe
              output_low(stb);
      
              db = (asci & 0xF) | 0x10;               //set lower data
              output_high(stb);       //strobe
              output_low(stb);        
              //delay_us(200);//busy check しないとき200必要
              while(bit_test(lcd_status(),7));//check busy
      }
      
      //read status
      int lcd_status(){
              
              int high,low;
      
              output_bit(PIN_A4,1);
      
              set_tris_a(Amode | 0x0F);       //set input mode
              output_low(rs);
              output_high(rw);
                              //read mode
              output_high(stb);       
              high=db & 0xF;                  //input A ,read upper   
              output_low(stb);
      
              //delay_us(10);
              output_high(stb); 
              low=db & 0xF;                   //inputA ,read lower
              output_low(stb);
              
              set_tris_a(Amode);
              
              return(high<<4 | (low));        //return status
      }
      
      //send command
      void lcd_cmd(int cmd){
              //set upper data to PORTA & set PA4=0(LED common)
       
              output_low(rw);   //set write
              output_low(rs);   //set rs low
              output_high(stb);  //strobe
           db = cmd>>4 | 0x10;
              output_low(stb);;
      
              output_high(stb);  //strobe
              db=(cmd & 0xF) | 0x10;  
              output_low(stb);
          delay_us(5);
           //delay_ms(2);
              
              while(bit_test(lcd_status(),7));
      }
      
      
      //  clear display clear function
      void lcd_clear(){
              lcd_cmd(1);  //initialize command
           delay_ms(10);
           lcd_cmd(2);
           delay_ms(2);
              //delay_ms(2);
          //while(bit_test(lcd_status(),7));
      }