現在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がどのように動作しているのかをより深く理解していきます。
「表示できた」で終わらせず、
内部動作まで理解できるよう、
引き続き学習を進めていきます。


コメント