コピペで超簡単!PICマイコンでI2C接続のLCD(AQM1602)を使う【PIC16F1938】

今回やること・必要物品

前回までは、PIC基礎編でした(おそらく)。主にピンのオンオフのみを使って何かを動かしていましたね。

今回は、PICに備わる機能を使っていく応用編に入っていきます。

この記事シリーズで詳しく解説していますが、同じくC言語でPICマイコンを扱うための体系的な書籍「C言語による PICプログラミング大全」が発売されています。まとまった情報が欲しい方はこちらの購入をお勧めします。かなり読みやすいですよ!

液晶ディスプレイを繋ぎ、文字を表示

今回は、以下の動画のように「液晶ディスプレイ」に文字を表示していきます。一見すごく難しそうですが、動けばいいという視点でいえば、主要関数はほぼコピペで問題ないので簡単です。

必要なもの

基礎編からプラスで必要なのは液晶ディスプレイ(LCD)のみですが、一応全て列挙します。

秋月電子のモデルとは異なりますが、よくある1602系のLCDにI2C変換を施したものも販売されているようです。こちらも基本的に使い方は同じでしょう。下にリンクを載せておきます。

PIC書き込みに必要な物品や、書き込み方法はこちら

PICを使う前準備

PICにプログラムを書き込んでみよう①~MPLAB X IDEの使い方~

LCDとは?

LCDとは、Liquid Crystal Displayの略で、そのまま日本語に直すと液体結晶ディスプレイ、つまり液晶ディスプレイです。非常に低消費電力で、PICのような小さなマイコンでも駆動することができます。

暗くても見えるバックライト付きのLCDもあります。これは若干高価なので、今回は導入としてバックライトなしのI2C接続小型キャラクタLCDモジュール(16x2行・3.3V/5V)ピッチ変換キットを使います。

このLCDとPICをI2Cという接続方法を使って、接続します。

I2Cとは?

I2Cは、Inter-Integrated Circuitの略で、いろいろな機器間の通信をたった2本の線で実現するすごい方式です(語彙不足)。

マスター(主)とスレーブ(奴隷)

I2Cには、命令を送るマスター(主)と、命令を受けて何かをするスレーブ(奴隷)となる機器があります。スレーブには番号(スレーブアドレスと呼ばれる)が付いており、基本的に

  1. マスターが、通信したいスレーブのアドレス信号を送る
  2. スレーブ「あ、私呼ばれた。命令待ち状態に入ります。」
  3. マスターが命令やデータを送る
  4. スレーブが命令に沿って何かをする(データを送る・LCDに文字を表示する等)
  5. おわり

という手順を取ります。刑務所で「おい、1番、そこを掃除しろ!」と1番に命令しているのを想像してみれば分かりますね(例えが悪いですね、すみません)。「1番」の時点で他の2番や5番などは「私じゃないな」と分かり、次の「掃除しろ」命令が聞こえても行動に移しませんよね。このような挙動を示すことから、命令を出す側をマスター、受ける側をスレーブと呼んでいるみたいです。

SCLとSDA

マスターとスレーブは、以下の図のようにSCLと呼ばれる線とSDAという線でつながっています。SCLがLOWの時にSDAがHIGHだとほにゃらら…等の挙動をして、データを送ります。しかし、どれも同じ原理で動くので、コピペしてしましましょう。今回は、そのような方式で行きます。

さらに詳しくI2Cについて知りたい方は、こちらのサイトが参考になるでしょう。I2C通信の使い方

PICとLCD間の通信

I2CにはSCLとSDAという線があることは上で説明しました。データシートを見てみましょう。

14番ピンがSCLで、15番ピンがSDAですね。これを、それぞれ繋ぎたいスレーブのSCL,SDAに繋げば、回路は完成です。

I2C接続LCDモジュールの変換基板

今回使用するLCDは、I2Cで接続するためのSCL,SDAに加え、電源用のVDDとGNDの4本のみで駆動できるようになっています。便利。

しかし、これを実現するためにキットの中に入っている変換基板があるのですが、このはんだ付けが初心者には非常に難しくなっています。細かいところへのはんだ付けの方法をググってから取り付けるといいでしょう。失敗しても吸い取れば直せますので、暖め過ぎにだけ気を付けてください。

はんだ付け前の様子



はんだ付け後の様子

この基板上のSCLとSDAを、それぞれPICのそれに繋ぎ、+Vと書かれた部分に5V、GNDは当然0Vへ繋ぎます。これで回路は完成です。

回路

完成回路は、以下の写真のとおりです。これに、PICkit3を接続して書き込みます。

PICへの書き込み方法はこちら PICにプログラムを書き込んでみよう

I2C接続ライブラリ(完全コピペ)

I2C接続ですが、海外の方が非常に有用で簡潔なライブラリを作ってくれています。これをそのままコピペすれば、I2C接続ができます。

参考にした海外のサイト


    void I2C_Master_Init(const unsigned long c)
    {
      SSPCON1 = 0b00101000;
      SSPCON2 = 0;
      SSPADD = (_XTAL_FREQ/(4*c))-1;
      SSPSTAT = 0b00000000 ;    // 標準速度モードに設定する(100kHz)
    }

    void I2C_Master_Wait()
    {
      while ((SSPSTAT & 0x04) || (SSPCON2 & 0x1F));
    }

    void I2C_Master_Start()
    {
      I2C_Master_Wait();
      SEN = 1;
    }

    void I2C_Master_RepeatedStart()
    {
      I2C_Master_Wait();
      RSEN = 1;
    }

    void I2C_Master_Stop()
    {
      I2C_Master_Wait();
      PEN = 1;
    }

    void I2C_Master_Write(unsigned d)
    {
      I2C_Master_Wait();
      SSPBUF = d;
    }
  

LCD接続テンプレート

こちらは、私が構成したLCDを動かすための関数群です。これもコピペでいいでしょう。実は、下の関数はI2CLCDの説明書に書いてあるものをそのままPIC用にしただけです(特にLCD_init()はほぼ同じ)。

説明書にある初期化、表示例


void writeData(char t_data){
  I2C_Master_Start();
  I2C_Master_Write(LCD_ADD);
  I2C_Master_Write(0x40);
  I2C_Master_Write(t_data);
  I2C_Master_Stop();
  __delay_ms(10);
}
void writeCommand(char t_command){
  I2C_Master_Start();
  I2C_Master_Write(LCD_ADD);
  I2C_Master_Write(0x00);
  I2C_Master_Write(t_command);
  I2C_Master_Stop();
  __delay_ms(10);
}
void LCD_Init(){
I2C_Master_Init(100000);
__delay_ms(400);
writeCommand(0x38);
__delay_ms(20);
writeCommand(0x39);
__delay_ms(20);
writeCommand(0x14);
__delay_ms(20);
writeCommand(0x73);
__delay_ms(20);
writeCommand(0x52);
__delay_ms(20);
writeCommand(0x6C);
__delay_ms(250);
writeCommand(0x38);
__delay_ms(20);
writeCommand(0x01);
__delay_ms(20);
writeCommand(0x0C);
__delay_ms(20);
}

void LCD_str(char *c) {
unsigned char i,wk;
for (i=0 ; ; i++) {
  wk = c[i];
  if  (wk == 0x00) {break;}
  writeData(wk);
}

}

  

完成プログラム

文字を表示させるには、上記で作成したLCD_str()に配列を渡すだけで実現できます。LCDに「Hello,world!」と表示させたいときは、


  char moji[] = "Hello,world!";
  LCD_str(moji);

とすれば終わりです。

main部分

main部分は、LCDの初期化をして、while文の中で”Hello, PIC world Wak-tech”を繰り返すようにしてみました。以下のようになります。


  int main(void){
    PICinit();      //PICを初期化
    LCD_Init();
    writeCommand(0x01); //画面をクリア
    __delay_ms(20);
    writeCommand(0x02); //ホームへカーソル移動
    __delay_ms(2); // LCD側の処理待ち

    while(1){
        writeCommand(0x02);   //ホームへカーソル移動
        LCD_str(moji);
        writeCommand(0x40+0x80); //2列目へ移動
        __delay_ms(200);
        LCD_str(moji2);
        __delay_ms(1000);
        writeCommand(0x01); //画面をクリア
        __delay_ms(200);
    }
    return 0;
  }
  

途中、writeCommand()がたくさん出てきてますね。これは、LCDの機能を呼び出す関数(コマンド)です。次回色々なコマンドを解説します。特に、writeCommand(0x01)はLCDの画面を全てまっさらにするコマンドだということは知っておいて損はないです。

全体コード(そのままコピペで動く)

上記のすべての関数を組み込んだコードです。これをコピペして書き込めば、動画のように文字が表示されるはずです。


    // PIC16F1938 Configuration Bit Settings

    // 'C' source line config statements

    // CONFIG1
    #pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
    #pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
    #pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
    #pragma config MCLRE = OFF      // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
    #pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
    #pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
    #pragma config BOREN = OFF      // Brown-out Reset Enable (Brown-out Reset disabled)
    #pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
    #pragma config IESO = OFF       // Internal/External Switchover (Internal/External Switchover mode is disabled)
    #pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled)

    // CONFIG2
    #pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
    #pragma config VCAPEN = OFF     // Voltage Regulator Capacitor Enable (All VCAP pin functionality is disabled)
    #pragma config PLLEN = ON// PLL Enable (4x PLL disabled)
    #pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
    #pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
    #pragma config LVP = ON         // Low-Voltage Programming Enable (Low-voltage programming enabled)

    // #pragma config statements should precede project file includes.
    // Use project enums instead of #define for ON and OFF.

    #include <xc.h>
    #include <pic16f1938.h>
    #define _XTAL_FREQ 32000000
    #define LCD_ADD 0x7C

    char moji[] = "Hello, PIC World!";
    char moji2[] = "Wak-tech";
    void I2C_Master_Init(const unsigned long c)
    {
      SSPCON1 = 0b00101000;
      SSPCON2 = 0;
      SSPADD = (_XTAL_FREQ/(4*c))-1;
      SSPSTAT = 0b00000000 ;    // 標準速度モードに設定する(100kHz)
    }

    void I2C_Master_Wait()
    {
      while ((SSPSTAT & 0x04) || (SSPCON2 & 0x1F));
    }

    void I2C_Master_Start()
    {
      I2C_Master_Wait();
      SEN = 1;
    }

    void I2C_Master_RepeatedStart()
    {
      I2C_Master_Wait();
      RSEN = 1;
    }

    void I2C_Master_Stop()
    {
      I2C_Master_Wait();
      PEN = 1;
    }

    void I2C_Master_Write(unsigned d)
    {
      I2C_Master_Wait();
      SSPBUF = d;
    }
    void writeData(char t_data){
        I2C_Master_Start();
        I2C_Master_Write(LCD_ADD);
        I2C_Master_Write(0x40);
        I2C_Master_Write(t_data);
        I2C_Master_Stop();
        __delay_ms(10);
    }
    void writeCommand(char t_command){
        I2C_Master_Start();
        I2C_Master_Write(LCD_ADD);
        I2C_Master_Write(0x00);
        I2C_Master_Write(t_command);
        I2C_Master_Stop();
        __delay_ms(10);
    }
    void PICinit(){
      OSCCON = 0b01110000;
      ANSELA = 0b00000000;
      ANSELB = 0b00000000;
      TRISA  = 0b00000000;
      TRISB  = 0b00000000;
      TRISC  = 0b00011000;
      PORTA  = 0b00000000;    //2進数で書いた場合
      PORTB  = 0x00;          //16進数で書いた場合
    }
    void LCD_Init(){            //LCDの初期化
      I2C_Master_Init(100000);
      __delay_ms(400);
      writeCommand(0x38);
      __delay_ms(20);
      writeCommand(0x39);
      __delay_ms(20);
      writeCommand(0x14);
      __delay_ms(20);
      writeCommand(0x73);
      __delay_ms(20);
      writeCommand(0x52);
      __delay_ms(20);
      writeCommand(0x6C);
      __delay_ms(250);
      writeCommand(0x38);
      __delay_ms(20);
      writeCommand(0x01);
      __delay_ms(20);
      writeCommand(0x0C);
      __delay_ms(20);
    }

    void LCD_str(char *c) {     //LCDに配列の文字を表示
      unsigned char i,wk;
      for (i=0 ; ; i++) {
        wk = c[i];
        if  (wk == 0x00) {break;}
        writeData(wk);
      }
    }

    int main(void){
      PICinit();      //PICを初期化
      LCD_Init();
      writeCommand(0x01); //画面をクリア
      __delay_ms(20);
      writeCommand(0x02); //ホームへカーソル移動
      __delay_ms(2); // LCD側の処理待ち

      while(1){
          writeCommand(0x02);   //ホームへカーソル移動
          LCD_str(moji);
          writeCommand(0x40+0x80); //2列目へ移動
          __delay_ms(200);
          LCD_str(moji2);
          __delay_ms(1000);
          writeCommand(0x01); //画面をクリア
          __delay_ms(200);
      }
      return 0;
    }
  

次回

次回は、今回解説しなかったLCDのcommandについて解説します。例えば、文字を一文字ずつ右にずらす等の機能が備わっていて、それをPIC側から使う、ということをしていきます。お楽しみに!

次回>LCD(AQM1602)のコマンドを使って、文字を移動させてみた【PIC16F1938】

[amazon_long]

「コピペで超簡単!PICマイコンでI2C接続のLCD(AQM1602)を使う【PIC16F1938】」に15件のコメントがあります

  1. ピンバック: Wak-tech » PIC16F1938で「ようこそジャパリパークへ」を演奏してみた

  2. ピンバック: Wak-tech » LCD(AQM1602)のコマンドを使って、文字を移動させてみた【PIC16F1938】

  3. ピンバック: Wak-tech » 21円の温度計を作る

  4. ピンバック: Wak-tech » PICマイコンに無理やり素数を計算させてみた

  5. ピンバック: Wak-tech » 素数の音を聴く【PICマイコン】

  6. ピンバック: Wak-tech » タイマー割り込みを使って時計を作る【PIC16F1938 TMR1】

  7. ピンバック: Wak-tech » PICでタッチセンサを使ってみる(mTouch)

  8. すばらしいですね。本当にコピー&ペーストでできました。
    アマゾンで次の二個を購入しましたが、PICマイコン駆動のネット資料が少なく断念。
    ・EasyWordMall 1602 LCD ブラック IIC/I2C/TWI/SPI シリアル インタフェース ボード モジュール
    ・KKHMF DC 5V 1602 LCD ディスプレイモジュール 16×2キャラクタ LCDブルーブラックライト
    こちらのページを参考にLCDを買いなおし、あっさり出来たので拍子抜けしてしまいました。
    可能でしたら、上記モジュールとLCDユニットのサンプルも作っていただけるとありがたいです。

  9. ありがとうございました。

    1938が無かったので、手持ちのPIC16F1936で組んでみたのですが、最初、20番ピンにVDDを繋いだ時は、うまくいかず・・接触不良かと思い、20番ピンのVDD抜き差していたところ 21番ピンに VDDが接触した際 HELLO PIC WORLD WAK TECH と表示されました。

    こんなにあっさりとできるとは 思ってもみなかったので、小躍りして喜びました。
    ありがとうございました。

    VDD ピンの20番、21番については、何かの間違いだろうと 手持ちの2個の 1936にプログラムを入れてみましたが、やはり同じで、21番ピンに電源を入れないと動きません。
    19番のVSSは、接続してもしなくても変わりません。

    ブレッドボードがおかしいのか??と思い、新品のブレッドボードでも組んでみましたが、一緒の状況です。
    2台のブレッドボードで 1936のVDDが21番で 現在 「HELLO PIC WORLD WAK TECH」と表示中です。

    ご学業のかたわら、こんなページを作って頂いて 本当に頭が下がる思いです。
    ありがとうございました。ご本業でのご活躍もお祈りします・・・

    と いいつつも・・・ もし・・・・・上記の不可思議な症状・・心当たりがあれば ご教授頂ければ 幸いです。

    この状況の写真を 私のブログに載せておきますので、もし、お暇な折でも ご覧になって、ヒントでも頂ければ幸いです。

    1. 田崎様
      この度は弊サイトをご覧いただきありがとうございました!サイトも拝見させていただきました。無事動くようになり記事を書いた甲斐がありました。
      その状態で動くのは非常に不可解ですね…しかし写真を見る限り確かに21番ピンにVDDが接続されてますね。

      ソースコード中の
      #include の部分を
      #include に変更して試してみてもらえないでしょうか?

  10. 初めまして。I2CでLCDを制御したいと思い、いろいろ検索してヒットしました。
    PIC18F2620とXC8 1.45で開発しています。

    コピペでいけるか?と思ってやってみましたが、どうも下記関数で無限ループに入っていて、処理待ちになってしまいます。

    void I2C_Master_Wait()
    {
    while ((SSPSTAT & 0x04) ||(SSPCON2 & 0x1A)); //PENとSENが・・・
    }

    サイトでは SSPCON2 & 0x1F でしたが、どうもPENとSENがうまく0にならず、SSPCON2 & 0x1Aだとループに入らずに進んでくれます(LCDには表示されませんが・・・)
    I2C自体初めてで、「LCDが表示されない」という現象だけでどこから手を付けていいかも分かりません。
    お時間があるときにご教授ください。宜しくお願い致します。

  11. わかりやすい記事ありがとうございます。
    コピー&ペーストでしてみるのですが、

    1938.c:54:: warning: (520) function “_I2C_Master_RepeatedStart” is never called

    と出ます。
    そのまま書き込んでも 液晶に何も表示されません。
    ご教授いただけると幸いです。
    お手数お掛けして申し訳ありませんが、宜しくお願い致します。

    1. コメントありがとうございます!
      そのメッセージは「_I2C_Master_RepeatedStart」がプログラム中で使用されていないだけで、問題ありません。
      記事中に記述し忘れましたが、「プルアップ抵抗」をSCLとSDLにつけてあげると通信が安定します。https://www.macnica.co.jp/business/semiconductor/articles/analog_devices/127585/
      を参考にしてください。
      それでも駄目なら、またご連絡お願いしますm(_ _)m

      1. 10kオームのプルアップ抵抗をつけることで表示されました。
        ありがとうございます。
        大変助かります。

コメントする

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