PIC 18F4553とI2C LCD ACM1602N1

投稿日 2013/09/26

PIC 18F4553でI2C LCDのACM1602NIを制御してみました。

 

PIC 18FシリーズではMSSP(Master Synchronous Serial Port)を搭載しています。このMSSPはシリアル・インターフェースSPI,もしくはI2Cのいずれかを選択して使えるものです。

 

今回はI2Cモードにして、LCD(キャラクタディスプレイ)を制御してみました。ほかにEEPROMやADコンバータなどI2Cで制御するものが多いようです。

 

今回はLCDが相手なので出力のみ。マイコン側がマスター、LCD側がスレーブになります。

 

LCDの説明書から配線方法、I2Cスレーブアドレス、SCLクロック周波数を読み取っておきます。

PIC_18F4553とI2C_LCD_ACM1602N1.jpg

ACM1602NIは以下のようになっています。(秋月電子添付の資料より)

 

Pin1 GND
Pin2 Vcc 3.3V
Pin3 Vo (コントラスト調整)
Pin4 SCL
Pin5 SDA
Pin6 BL+(バックライト用電源+)
Pin7 BL-(バックライト用電源-)

 

スレーブアドレスは、0b1010000です。

 

SCLのクロック周波数は100KHzです。

 

配線はLCDの説明書を見て行っておきます。

 

PIC 18F4553のI/Oポート RB0がSDA、RB1がSCLです。
SCLとSDAはプルアップしておく必要があります。今回は4.7KΩでプルアップしておきました。

 

プログラミングは以下のように行います。

 

CPUのクロック周波数を設定します。今回使用したボードは20MHzのクリスタルを搭載しているので、それを2分周して10MHzとしました。(Fosc = 10MHz)

 

18FはI/Oポートが標準でADコンバータになっているのでRA0からRA3をデジタルI/Oにしておきます。
今回はRB0とRB1しか使わないので関係ないのですが、デジタルI/Oの出力ポートにしておいたほうが安心です。

 

コンパレータを明確に殺しておきます。

 

I/OポートのAからEをすべて出力ポートにし、出力レベルをLOWレベルにしておきます。

 

そのうち、RB0とRB1だけは入力ポートにします。
I2CをイネーブルにするとRB0, RB1はオープンコレクタになるので、先に書いたプルアップ抵抗が必要です。

 

I2Cを初期化します。初期化でPICをI2Cのマスターモードにしておきます。
SCLのクロック周波数として、ボーレートレジスタとアドレスレジスタにボーレートを設定しておきます。最後にI2Cをイネーブルにします。

 

スレーブアドレスは7ビットですが、最後のWRITEモードを示す0を追加して8ビットで与えます。

 

ボーレートは、BAUD = ((Fosc / 100KHz) / 4) -1 = ((10000000 / 100000) / 4) - 1 = 24となります。

 

LCDに文字や制御コードを送信するには、

 

スタートコンディションを発行する(SEN = 1)
スタートコンディションが終わるまで待つ(SENは自動的にクリアされる)
バッファSSPBUFにスレーブアドレスをセットする。(セットするだけで自動的に送信される)
バッファが空になるまで待つ(BF)
スレーブがACK応答するまで待つ(ACKSTAT)
バッファSSPBUFにコマンドをセットする
バッファが空になるまで待つ(BF)
スレーブがACK応答するまで待つ(ACKSTAT)
バッファSSPBUF文字コードをセットする
バッファが空になるまで待つ(BF)
スレーブがACK応答するまで待つ(ACKSTAT)
ストップコンディションを発行する(PEN = 1)
ストップコンディションが終わるまで待つ(PENは自動的にクリアされる)

 

これで1文字表示終了です。(なおLCDは表示する文字の位置などをあらかじめ送っておく必要があります)

 

18F4553のデータシート(実際には18F4550のデータシート)のI2Cの説明(タイミングチャート)を見ると、バッファが空になったことを示すBFがクリアされてからACKが返るまで時間があるので、10uSのディレーを入れています。

 

ディレーはビルトインのコードを使用しています。

 

制御方法がこれがベストかどうかはわかりません。

 

LCDの初期化や制御方法は一般的な方法に従っています。

 

ブログの文字数制限のため、コメントや分かるところは省略していますのでコンパイルエラーになったら補ってください。

 

main.c
#include <xc.h>
#include <p18f4553.h>

 

#pragma config FOSC = HS
#pragma config CPUDIV = 1
#pragma config MCLRE = ON

 

#define _XTAL_FREQ 10000000

 

void init(void){

ADCON1 = 0b00001111;
CMCON = 0b00000111;
TRISA = 0;
TRISB = 0b00000011;
TRISC = 0;
TRISD = 0;
TRISE = 0;
LATA = 0;
LATB = 0;
LATC = 0;
LATD = 0;
LATE = 0;

}

 

void main(void){

init();

i2c_init();
lcd_init();
lcd_clear();
lcd_write(0, 0, "JF1VRR" );
lcd_write(0, 1, "PIC18F4553" );

while(1);

}

 

i2c_lcd.c
#include <xc.h>
#include <p18f4553.h>

 

#define I2CBAUD 24
#define LCD_I2C_ADDR 0b10100000
#define _XTAL_FREQ 10000000

 

const int lcd_adrbase[2] = { 0x80, 0xC0 };

 

void Delay_ms(int cnt){

int i;

for(i = 0; i < cnt; i++) __delay_ms(1);

}

 

void i2c_init(void){

SSPCON1bits.SSPEN = 0;
SSPCON1bits.SSPM = 8;
SSPSTATbits.SMP = 1;
SSPSTATbits.CKE = 0;
SPBRG = I2CBAUD;
SSPADD = I2CBAUD;

SSPCON1bits.SSPEN = 1;

}

 

int i2c_lcd_IOCtl(unsigned char command, unsigned char data){
SSPCON2bits.SEN = 1;
while(SSPCON2bits.SEN);
SSPBUF = LCD_I2C_ADDR;
while(SSPSTATbits.BF);
__delay_us(10);
if(SSPCON2bits.ACKSTAT){

SSPCON2bits.PEN = 1;
while(SSPCON2bits.PEN);

return(-1);
}
SSPBUF = command;
while(SSPSTATbits.BF);
__delay_us(10);
if(SSPCON2bits.ACKSTAT){

SSPCON2bits.PEN = 1;
while(SSPCON2bits.PEN);

return(-1);
}
SSPBUF = data;
while(SSPSTATbits.BF);
__delay_us(10);
if (SSPCON2bits.ACKSTAT){
SSPCON2bits.PEN = 1;

while(SSPCON2bits.PEN);
return(-1);

}
SSPCON2bits.PEN = 1;
while(SSPCON2bits.PEN);
return(1);
}

 

void lcd_init(void){

Delay_ms(100);
i2c_lcd_IOCtl(0x00, 0x30);
__delay_ms(5);
i2c_lcd_IOCtl(0x00, 0x30);
__delay_ms(1);
i2c_lcd_IOCtl(0x00, 0x30);
__delay_ms(1);
i2c_lcd_IOCtl(0x00, 0x38);
__delay_ms(1);
i2c_lcd_IOCtl(0x00, 0x08);
__delay_ms(1);
i2c_lcd_IOCtl(0x00, 0x01);
__delay_ms(1);
i2c_lcd_IOCtl(0x00, 0x06);
__delay_ms(1);
i2c_lcd_IOCtl(0x00, 0x0C);

}

 

void lcd_position(unsigned char xpos, unsigned char ypos){

i2c_lcd_IOCtl(0x00, lcd_adrbase[ypos] + xpos );

}

 

void lcd_write(unsigned char xpos, unsigned char ypos, const unsigned char* ptr){
lcd_position(xpos, ypos);
while(*ptr != 0x00){

i2c_lcd_IOCtl(0x80, *ptr);
ptr++;

}
}

 

void lcd_clear(void){

i2c_lcd_IOCtl(0x00, 0x01);
__delay_ms(10);

}

 

void lcd_str(const unsigned char* ptr){

while(*ptr != 0) i2c_lcd_IOCtl(0x80, *ptr++);

}

 

(JF1VRR)