【番外編】PIC16F1938で LCD(AQM1602XA)を動かす

PICの基礎

現在RS485通信の学習中で、
2つのPIC16F1938を使った通信には
成功しています(後日ブログ化します)。

しかし、通信できているかどうかを
1秒間隔のLチカだけなので、
通信できていることをより可視化したく、
LCDでの表示させたいと思いました。

今回は秋月電子のAQM1602XA-RN-GBWという、
16文字 × 2行
のLCDをI2C通信して表示させるための
初期設定の解説の記事となります。

I2Cとは?

I2C(Inter-Integrated Circuit)は、
周辺デバイスとのシリアル通信の方式です。

同じ基板上の近距離に接続したデバイスへの通信向けで、
長距離通信には向いていません。

I2Cは、マスタ側とスレーブ側に分かれており、
マスタすべての主導権を持ちます。

1台のマスタデバイスと複数台のスレーブデバイス間を
SCLとSDAという2本の線でパーティーライン上に接続します。

マスタが送信するクロック信号SCLを基準にして、
データ信号がSDAライン上で転送されます。

個々のスレーブがアドレスを持っていて、
必要なスレーブを指定して通信できます。

PIC16F1938では、I2C通信を行うために
MSSP(Master Synchronous Serial Port)という
専用ハードウェアを使用します。

MSSPを使用することで、
Start/Stop条件の生成や、
クロック生成、データ送受信などを
ハードウェア側で処理できます。

実際のコード

I2C初期設定

PICでI2C通信を行う際の初期設定は
下記のような形となります。

c

/* I2C通信を行うための初期設定 */
void I2C_Init(void)
{
    /* RC3(SCL)とRC4(SDA)の両方とも入力*/
    TRISCbits.TRISC3 = 1;
    TRISCbits.TRISC4 = 1;
    
    SSPSTATbits.SMP = 1;   //I2Cの標準速度モード(100kHzと1MHz)
    SSPSTATbits.CKE = 0;   //SMBus仕様の入力は無効
    
    SSPCON1bits.SSPM = 0b1000;  //I2Cマスタモード、クロック = FOSC/(4 * (SSPADD+1))
    SSPADD = 19;  // クロック = FOSC/(4 * (SSPADD+1)) => 100kHz
    
    SSPCON1bits.SSPEN = 1;  //シリアルポートを有効化。SDAピンとSCLピンを設定
}

I2Cによる送信方法

上記でI2Cのマスタ側の初期設定をしました。
続いて実際のI2Cによるデータの送信のシーケンスは次の通りです。

c

void write_I2C(unsigned char data) {
    SSPCON2bits.SEN = 1;      //スタート条件を生成
    while (SSPCON2bits.SEN);  //スタート条件成立待ち
    PIR1bits.SSPIF = 0;       //MSSP割り込みフラグをクリア
    SSPBUF = data;            //送信データを書き込む
    while (!PIR1bits.SSPIF);  //送信完了待ち
    SSPCON2bits.PEN = 1;      //ストップ条件を生成
    while (SSPCON2bits.PEN);  //ストップ条件成立待ち
}

となります。
しかしこれではSSPBUFには
1byteのデータしか送信できないため、
関数をStart()、Write()、Stop()の様に分けます。
具体的には次の通りです。

c

/* スタート用関数 */
void I2C_Start(void)
{
    SSPCON2bits.SEN = 1;
    while(SSPCON2bits.SEN);
}
c

/* 書き込み用関数 */
void I2C_Write(unsigned char data)
{
    PIR1bits.SSPIF = 0;
    data = data;
    SSPBUF = data;
    while(!PIR1bits.SSPIF);
}
c

/* ストップ用関数 */
void I2C_Stop(void)
{
    SSPCON2bits.PEN = 1;
    while(SSPCON2bits.PEN);
}

この様に分けて

c

I2C_Start();
I2C_Write(a);
I2C_Write(b);
I2C_Write(c);
I2C_Stop();

とすれば、
“abc” を一括で送信することができます。

コマンドとデータ

このLCDの操作には、
LCDへの命令を出す “コマンド” と
LCDに表示させる “データ” を
送信するのですが、送信の仕方がそれぞれ違います。
上の送信方法を使って以下のように送信します。

コマンドの送信

void write_Command(unsigned char command)
{
    I2C_Start();
    I2C_Write(LCD_ADRS << 1);
    I2C_Write(0x00);
    I2C_Write(command);
    I2C_Stop();
}

スタート → アドレス(LCDのアドレス) → 0x00 → コマンド → ストップ
の流れです。

データの送信

void write_Data(unsigned char data)
{
    I2C_Start();
    I2C_Write(LCD_ADRS << 1);
    I2C_Write(0x40);
    I2C_Write(data);
    I2C_Stop();
}

スタート → アドレス(LCDのアドレス) → 0x40 → コマンド → ストップ
の流れです。

コマンドとデータの大きな違いは、
0x00 か 0x40
となります。

また、2行目のアドレスの書き込みで、
1bit左にシフトさせている理由は、

I2Cでは、スレーブアドレスの下位1bitが
Read/Write指定用として使用されます。

そのため、7bitアドレスである0x3Eを
1bit左シフトして送信しています。

I2C_Write(LCD_ADRS << 1);

これにより、

0x3E → 0x7C

となり、最後の1bitがWrite指定(0)になります。

LCDの初期設定

いよいよLCDを表示させるための初期設定に入ります。

仕様書に書かれている初期化の例です。
『とにかくコマンドを入れまくれ!』
と書かれています。
コードは次の様にしました。

c

void LCD_Init(void) {
    __delay_ms(100);
    write_Command(0x38);
    __delay_ms(30);
    write_Command(0x39);
    __delay_ms(30);
    write_Command(0x14);
    __delay_ms(30);
    write_Command(0x73);
    __delay_ms(30);
    write_Command(0x5C);  //※
    __delay_ms(30);
    write_Command(0x6C);
    __delay_ms(250);
    write_Command(0x38);
    __delay_ms(30);
    write_Command(0x01);
    __delay_ms(30);
    write_Command(0x0C);
    __delay_ms(30);
}

とにかくコマンドを入れまくります!

ただそれだけのことです。
一つ一つのコマンドの意味は仕様書に書かれている通りです。

そして※を付けたところですが、
よく見ていただくと送っているコマンドが仕様書と異なります。

仕様書のコマンドを打ち込んでも、
真っ黒表示になってしまい、
目的の文字を表示させることができませんでした。

このPower/Icon/Contrast Controlの部分だけ要注意です。

main関数によるLCDの表示

ようやくLCDに表示させる準備がすべて整いましたので、
世界で最も表示されている言葉を表示させたいと思います。

main関数は以下の通りです。

c

void main(void)
{
    OSCCON = 0b01110010;
    I2C_Init();
    LCD_Init();
    
    for(unsigned char i = 0; str[i] != '\0'; i++)
    {
        write_Data(str[i]);
    }
    write_Command(0xC0); //カーソルを2行目1文字目に移動
    write_Data('W');
    write_Data('o');
    write_Data('r');
    write_Data('l');
    write_Data('d');
    
    while(1)
    {
        
    }
}

このコードをPICへ書き込むと…

うまく表示させることができました!

<h2>まとめと予告</h2>

今回の記事では、
PIC16F1938とI2C接続LCDを使用し、
文字表示の基本動作を確認しました。

I2C化することで、
LCD制御に必要な配線本数を大幅に削減でき、
限られたマイコンのGPIOを有効活用できます。
また、I2C通信はセンサやEEPROMなど、
さまざまなデバイスでも使用されるため、
今後の組み込み学習において非常に重要な技術です。

一方で、I2C変換モジュールを使用すると、
LCD内部でどのような信号が動いているのかが
見えにくくなる側面もあります。

次回は、14pin接続によるLCD制御に挑戦し、

  • RS信号
  • Enable信号
  • データライン
  • 4bit通信

などを直接扱いながら、
LCDがどのように動作しているのかをより深く理解していきます。

「表示できた」で終わらせず、
内部動作まで理解できるよう、
引き続き学習を進めていきます。

コメント

タイトルとURLをコピーしました