I2C LCD ACM1602NIの制御

投稿日 2013/06/22

I2CのキャラクタLCD ACM1602NIをPICで制御してみました。

 

I2C LCDはGPIOのピンをSCL, SDAに割り当てて、プログラムでレベルをコントロールすれば、簡易的に使用することができますが、今回はI2C制御のCODECやEEPROM, ADCなどと共存できるよう、PICの周辺機能の一つであるI2Cを使用して正式(?)な方法で制御してみました。

I2C_LCD_3.jpg

ACM1602NI 後述のプログラムを実行した結果


 

結論から言うと、簡単に制御できますが、LCDがACKを返すタイミングがあるようで、少し調整が必要。それが分かるまで数時間格闘しました。

 

使用したPICは、dsPIC33FJ64GP802ですが、I2Cをサポートしていればなんでも構いません。

 

OSCはFRCを使用して、PLLでFoscを80MHzとしました。Fcyはその1/2で40MHzです。

 

PICにもよりますが、今回使用したPICでは、I2CのSCL, SDA信号をSCL1. SDA1とASCL1, ASDA1の2つから選べるようになっていますので、_FPOR(ALTI2C_OFF)でSCL1, SDA1を使うように設定しています。

 

I2Cのクロック速度は100KHzとします。(ACM1602NIは100KHzがMAXです)

 

SCLとSDAのプルアップは5.6KΩで行いました。電源は3.3Vです。

 

ACM1602NIのスレーブアドレスは0xA0です。

 

ICSPは都合でPGED2を使用しています。勿論PGED1でも構いません。

I2C_LCD_2.jpg

回路図 電源は省略 ICSPとLCDのみ


 

手順:

 

①I2CBRDレジスタでクロック速度を100KHzに設定します。

 

②I2C1CONのENビットをセットして、I2Cをイネーブルにします。
I2C1CONレジスタの他のビットはすべて0ですので、とくに設定は要りません。
アドレスは7bit長になります。

 

③イネーブルの状態で、I2C1CONのSENビットをセットするとスタートコンディションが発行されます。

 

④スレーブアドレス、コマンド、データの順でアクノーリッジ(ACK)を確認しながら送信します。

 

⑤送信し終わったらI2C1CONのPENビットをセットして、ストップコンディションを発行します。

I2C_signal.jpg

I2C_LCD_IOctl(0x00, x30)を実行したときのSCL, SDAの波形
ACKの後のSDA highは10uSディレイ

 

I2Cはスタートコンディション(SCLがHighのときに,マスタがSDAをlowにする)以降、9個のSCLクロックで1フレームとなります。

 

最初のフレームで、LCDのスレーブアドレス 0xA0 (0b10100000) を送っています。9回目のクロックで、スレーブ側がSDAをlowにすればアクノリッジ(ACK)です。スレーブ側がlowにしない(つまりhigh)場合なノットアクノリッジ(NACK)となります。

 

このLCDの場合、2つ目のフレームはLCDのコマンド(0x00)です。0x00の場合、次の3つ目のフレームはLCDの制御コマンドとなります。0x80の場合、文字コードとなります。

 

実験の結果、各フレームの送信の間に10uSほどディレイを入れる必要があります。これを入れないとNACKが返ってしまいます。これがわかるまでちょっと時間がかかりました。

 

今回はDSPフィルターの製作に際してI2CのCODEC WM8510と同居する環境でも実験しました。
以下はLCDの制御のみにしたプログラムです。

 

#include "p33FJ64GP802.h"

 

_FGS(GWRP_OFF & GCP_OFF);
_FOSCSEL(FNOSC_FRC);
_FOSC(FCKSM_CSECMD & OSCIOFNC_ON & POSCMD_NONE);
_FWDT(FWDTEN_OFF);
_FPOR(ALTI2C_OFF);
_FICD(JTAGEN_OFF & ICS_PGD2);

 

#define I2C_TRISSCL _TRISB8
#define I2C_TRISSDA _TRISB9
#define LCD_I2C_ADDR 0b10100000 //A0
#define I2CBAUD 396 //100KHz

 

unsigned int TimerInterruptCount = 0;

 

void Delay_ms (unsigned int delay)
{

TimerInterruptCount = 0;
PR1 = 0x9C40;     // (1ms / 25ns) = 40,000 = 0x9C40
IPC0bits.T1IP = 4;
IEC0bits.T1IE = 1;
T1CONbits.TON = 1;

while (TimerInterruptCount < delay);
T1CONbits.TON = 0;

}

 

void Delay_us (unsigned int delay)
{

TimerInterruptCount = 0;

PR1 = 0x0028; // (1us / 25ns) = 40 = 0x0028
IPC0bits.T1IP = 4;
IEC0bits.T1IE = 1;

T1CONbits.TON = 1;

while (TimerInterruptCount < delay);
T1CONbits.TON = 0;

}

 

void __attribute__((__interrupt__, no_auto_psv)) _T1Interrupt()
{

TimerInterruptCount++;
IFS0bits.T1IF = 0;

}

 

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

 

int i2c_lcd_IOCtl(unsigned char command, unsigned char data)
{
I2C1CONbits.SEN = 1;     // start condition
while(I2C1CONbits.SEN == 1);

I2C1TRN = LCD_I2C_ADDR;   // Address of the I2C LCD
while(I2C1STATbits.TRSTAT);
if (I2C1STATbits.ACKSTAT)  // ACK?
{

I2C1CONbits.PEN = 1; // NACK
while(I2C1CONbits.PEN); // Send the stop condition
return(-1);

}
Delay_us(10);
I2C1TRN = command;
while(I2C1STATbits.TRSTAT);
if (I2C1STATbits.ACKSTAT)
{

I2C1CONbits.PEN = 1;
while(I2C1CONbits.PEN);
return(-1);

}
Delay_us(10);
I2C1TRN = data;
while(I2C1STATbits.TRSTAT);
if (I2C1STATbits.ACKSTAT)
{

I2C1CONbits.PEN = 1;
while(I2C1CONbits.PEN);
return(-1);

}
Delay_us(10);
I2C1CONbits.PEN = 1;
while(I2C1CONbits.PEN);
return(1);
}

 

void lcd_init(void)
{

Delay_ms(10);
i2c_lcd_IOCtl(0x00, 0x30);
Delay_ms(1);
i2c_lcd_IOCtl(0x00, 0x30);
Delay_us(100);
i2c_lcd_IOCtl(0x00, 0x30);
Delay_us(1);
i2c_lcd_IOCtl(0x00, 0x38);
Delay_us(500);
i2c_lcd_IOCtl(0x00, 0x08);
Delay_us(500);
i2c_lcd_IOCtl(0x00, 0x01);
Delay_us(500);
i2c_lcd_IOCtl(0x00, 0x06);
Delay_us(500);
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++);

}

 

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++);

}

 

int main(void)
{

PLLFBD=41;
CLKDIVbits.PLLPOST=0;
CLKDIVbits.PLLPRE=0;
OSCTUN=0;

__builtin_write_OSCCONH(0x01);
__builtin_write_OSCCONL(0x01);
while (OSCCONbits.COSC != 0b01);
while(!OSCCONbits.LOCK);

I2C1BRG = I2CBAUD; // 100KHz
I2C_TRISSCL = 1;
I2C_TRISSDA = 1;
I2C1CONbits.I2CEN = 1;

lcd_init();
lcd_write(0, 0, "1234567890!#$%&'" );
lcd_write(0, 1, "ABCDEFGHIJKLMNOP" );

while(1);

}



 

(JF1VRR)