20x4 LCD/OLED

文字単位の表示デバイスについて

128x64 OLEDもいいのですが、表示内容を更新するためには8kドットものフルグラフィックスのリフレッシュ操作が必要なため、100kbps程度のI2Cインターフェースでは処理的に重くなります。

今回は、20x4 キャラクタタイプの表示デバイスを使用して、この課題を解決します。

表示コントローラーは一般的にキャラクター液晶で広く採用されているHD44780またはその互換LCDCです。

WINSTAR社 20x4キャラクタモジュール

http://www.winstar.com.tw/ja/products/oled-module/oled-character-display/oled-20x4.html

PSoCとの接続インタフェースについて

ただこのままだと、8bit/4bitパラレル接続によるLCD制御のための本数が多く、GPIOの少ないPSoCでは不向きです。そこで、I2C-LCD変換のデバイスを使用します。

SainSMART社のI2C->LCD変換基板を使用します。

販売ページより引用

こちらの製品は基板上にある「 A0~A2のジャンパー」の状態でI2Cアドレスを変更できるようになっています。

例えばI2Cアドレスを0x25にするには半固定抵抗脇のA1にジャンパー(0Ω抵抗等)を実装する事で端子間をショートします。

自分で加工するのが面倒な方は・・・

液晶とコントローラがアッセンブリ済みのもの

https://www.sainsmart.com/sainsmart-iic-i2c-twi-serial-2004-20x4-lcd-module-shield-for-arduino-uno-mega-r3-white.html

こちらをご利用ください。

液晶タイプを使用する際の注意点

有機ELタイプでもLCDタイプでもソフト的な制御方法は一緒ですが、ハード的な使いこなしがやや異なります。

「LED」と書かれたジャンパーピンはLCDのバックライトをON/OFFするためのもので、有機ELの場合は関係ありません。

同様に半固定ボリュームは液晶のコントラストを調整するためのものなのですが、本来5V系のLCDジュールを3.3V系で強引に点灯させた場合、

コントラストが最大となる半固定ボリュームが0Vの位置でも十分ではなく、表示が薄くて殆ど読めません。

そこでコントラスト調整用の信号電圧をマイナス側へ振る改造をする事で十分なコントラストを得ることができます。

20x4 LCD

//-----------------------------------------------------------------------------------------------------------------

// LCD2004 Control (OLED 20x4)

//-----------------------------------------------------------------------------------------------------------------

#define LCD_ADDR_20x4 0x25 // LCD2004 20x4

#define LCD_CONTRAST 30

// bit7:1=continue, bit7:0=not continue

// bit6:0=Command, bit6=1=Data

#define LCD_RS_CMD 0x00

#define LCD_RS_DATA 0x40

#define LCD_CMD_CLEAR 0x01

#define LCD_CMD_HOME 0x03

#define LCD2004_BL 0x08

#define LCD2004_EN 0x04

#define LCD2004_RW 0x02

#define LCD2004_RS 0x01

uint8_t LCD_ADDR = LCD_ADDR_20x4;

uint8_t LCD24004_BACKLIGHT_STATUS = 0x08;

//-------------------------------------------

// Physical Layer Code

//-------------------------------------------

uint8_t LCD_reg_read(uint8_t addr)

{

uint8_t value = 0;

uint8_t status;

status = I2C_1_MasterSendStart(LCD_ADDR, I2C_1_WRITE_XFER_MODE);

status = I2C_1_MasterWriteByte(addr);

status = I2C_1_MasterSendStop();

status = I2C_1_MasterSendStart(LCD_ADDR, I2C_1_READ_XFER_MODE);

value = I2C_1_MasterReadByte(I2C_1_ACK_DATA);

status = I2C_1_MasterSendStop();


return value;

}

uint8_t LCD_reg_write(uint8_t addr, uint8_t data)

{

uint8_t status;

status = I2C_1_MasterSendStart(LCD_ADDR, I2C_1_WRITE_XFER_MODE);

status = I2C_1_MasterWriteByte(addr);

status = I2C_1_MasterWriteByte(data);

status = I2C_1_MasterSendStop();

return status;

}

void LCD_reg_IS_write(uint8_t addr, uint8_t data)

{

uint8_t status;

status = I2C_1_MasterSendStart(LCD_ADDR, I2C_1_WRITE_XFER_MODE);

status = I2C_1_MasterWriteByte(0x39);

status = I2C_1_MasterWriteByte(addr);

status = I2C_1_MasterWriteByte(data);

status = I2C_1_MasterSendStop();

}

//-------------------------------------------

// Interface Layer Code

//-------------------------------------------

uint8_t LCD2004_reg_write(uint8_t data)

{

uint8_t status;

status = I2C_1_MasterSendStart(LCD_ADDR, I2C_1_WRITE_XFER_MODE);

status = I2C_1_MasterWriteByte(data);

status = I2C_1_MasterSendStop();

return status;

}

void LCD2004_LCDpulseEnableNeg(unsigned char _data){

LCD2004_reg_write(_data | LCD2004_EN); // En high

LCD2004_reg_write(_data & ~LCD2004_EN); // En low

}

void LCD2004_LCDwrite4bits(uint8_t nibEnRsMode) {

LCD2004_reg_write(nibEnRsMode & ~LCD2004_RW);

LCD2004_LCDpulseEnableNeg(nibEnRsMode & ~LCD2004_RW);

}

void LCD2004_send( uint8_t value, uint8_t RsMode )

{

uint8_t highnib = value & 0xF0;

uint8_t lownib = value << 4;

lownib &= 0xF0;

LCD2004_LCDwrite4bits((highnib) | LCD2004_EN | RsMode | LCD24004_BACKLIGHT_STATUS);

LCD2004_LCDwrite4bits((lownib ) | LCD2004_EN | RsMode | LCD24004_BACKLIGHT_STATUS);

}

void LCD2004_write_rs_command( uint8_t cmd)

{

LCD2004_send( cmd, 0 );

}

void LCD2004_write_data( uint8_t cmd)

{

LCD2004_send( cmd, LCD2004_RS );

}

#define LCD2004_INIT (((0b00000000 | LCD2004_EN | LCD24004_BACKLIGHT_STATUS) & ~LCD2004_RS) & ~LCD2004_RW) // Used to set all the O/Ps on the PCF8574 to initialise the LCD

#define LCD2004_8BIT_INIT 0b00110000 // Used to initialize the interface at the LCD

#define LCD2004_4BIT_INIT 0b00100000 // Used to initialize the interface at the LCD

#define LCD2004_CLEAR_DISPLAY 0x01 // Mode : Clears display

#define LCD2004_RETURN_HOME 0x02 // Mode : Returns cursor to home posn.

// Entry Mode Set

#define LCD2004_ENTRY_MODE_SET 0x04 // Mode : Entry Mode Set, Sets the cursor move dir and specs whether or not to shift the display

#define LCD2004_INCREMENT 0x02 // Sub Mode of ENTRY_MODE_SET : Increment DDRAM (I/D), Entry Left

#define LCD2004_DECREMENT 0x00 // Sub Mode of ENTRY_MODE_SET : Decrement DDRAM (I/D), Entry Right

#define LCD2004_SHIFT_ON 0x01 // Sub Mode of ENTRY_MODE_SET : Shift On (S), Shift Display when byte written. Display Shift

#define LCD2004_SHIFT_OFF 0x00 // Sub Mode of ENTRY_MODE_SET : Shift Off (S), Don't shift display when byte written. Cursor Move

// Display Function

#define LCD2004_DISPLAY_ON_OFF 0x08 // Mode : Display On/Off, Sets on/off of all display, Cursor on/off, Cursor Blink on/off

#define LCD2004_DISPLAY_ON 0x04 // Sub Mode of DISPLAY_ON_OFF : Puts display on (D)

#define LCD2004_DISPLAY_OFF 0x00 // Sub Mode of DISPLAY_ON_OFF : Puts display off (D)

#define LCD2004_CURSOR_ON 0x02 // Sub Mode of DISPLAY_ON_OFF : Puts cursor on (C)

#define LCD2004_CURSOR_OFF 0x00 // Sub Mode of DISPLAY_ON_OFF : Puts cursor off (C)

#define LCD2004_BLINKING_ON 0x01 // Sub Mode of DISPLAY_ON_OFF : Blinking cursor (B)

#define LCD2004_BLINKING_OFF 0x00 // Sub Mode of DISPLAY_ON_OFF : Solid cursor (B)

// Display Control

#define LCD2004_MV_CUR_SHIFT_DISPLAY 0x10 // Mode : Move the cursor and shifts the display

#define LCD2004_DISPLAY_SHIFT 0x08 // Sub Mode of CURSOR_SHFT_DIS : Display shifts after char print (SC)

#define LCD2004_CURSOR_SHIFT 0x00 // Sub Mode of CURSOR_SHFT_DIS : Cursor shifts after char print (SC)

#define LCD2004_SHIFT_RIGHT 0x04 // Sub Mode of CURSOR_SHFT_DIS : Cursor or Display shifts to right (RL)

#define LCD2004_SHIFT_LEFT 0x00 // Sub Mode of CURSOR_SHFT_DIS : Cursor or Display shifts to left (RL)

// Function Set

#define LCD2004_FUNCTION_SET 0x20 // Mode : Set the type of interface that the display will use

#define LCD2004_INTF8BITS 0x10 // Sub Mode of FUNCTION_SET : Select 8 bit interface (DL)

#define LCD2004_INTF4BITS 0x00 // Sub Mode of FUNCTION_SET : Select 4 bit interface (DL)

#define LCD2004_TWO_LINES 0x08 // Sub Mode of FUNCTION_SET : Selects two char line display (N)

#define LCD2004_ONE_LINE 0x00 // Sub Mode of FUNCTION_SET : Selects one char line display (N)

#define LCD2004_FONT_5_10 0x04 // Sub Mode of FUNCTION_SET : Selects 5 x 10 Dot Matrix Font (F)

#define LCD2004_FONT_5_7 0x00 // Sub Mode of FUNCTION_SET : Selects 5 x 7 Dot Matrix Font (F)

#define OLED_FONT_JAP 0x00

#define OLED_FONT_EU1 0x01

#define OLED_FONT_RUS 0x02

#define OLED_FONT_EU2 0x03

#define LCD2004_CG_RAM_ADDRESS 0x40 // Mode : Enables the setting of the Char Gen (CG) Ram Address, to be or'ed with require address

#define LCD2004_CG_RAM_ADDRESS_MASK 0b00111111 // Used to mask off the lower 6 bits of valid CG Ram Addresses

#define LCD2004_DD_RAM_ADDRESS 0x80 // Mode : Enables the setting of the Display Data (DD) Ram Address, to be or'ed with require address

#define LCD2004_DD_RAM_ADDRESS_MASK 0b01111111 // Used to mask off the lower 6 bits of valid DD Ram Addresses

uint8_t lcd_initialized = 0;

uint8_t lcd_max_y = 0;

uint8_t lcd_max_x = 0;

void LCD_init(void)

{

uint32_t retval;

LCD_ADDR = LCD_ADDR_20x4;

lcd_max_y = 4;

lcd_max_x = 20;

retval = LCD_reg_write(LCD_RS_CMD, 0x39);

if (LCD_ADDR == LCD_ADDR_20x4) {

// init LCD2004

//Notes

//Repeated procedures for an 4-bit bus interface

//Noise causing transfer mismatch between the four upper and lower bits can be corrected by a reset triggered by consecutively writing a “0000” instruction five times. The next transfer starts from the lower four bits and then first i nstruction “Function set” can be executed normally.

//Please insert the synchronization function in the head of procedures. The repeated procedures are show as follows

LCD2004_LCDwrite4bits(0x00);

LCD2004_LCDwrite4bits(0x00);

LCD2004_LCDwrite4bits(0x00);

LCD2004_LCDwrite4bits(0x00);

LCD2004_LCDwrite4bits(0x00);

LCD24004_BACKLIGHT_STATUS = 0;

LCD2004_reg_write(LCD2004_INIT);

Delay(100);

LCD24004_BACKLIGHT_STATUS = LCD2004_BL;

LCD2004_reg_write(LCD2004_INIT);

LCD2004_LCDwrite4bits(0x30);

Delay(4);

LCD2004_LCDwrite4bits(0x30);

Delay(1);

LCD2004_LCDwrite4bits(0x30);

Delay(1);

LCD2004_LCDwrite4bits(0x20);

Delay(1);

LCD2004_write_rs_command(LCD2004_FUNCTION_SET | LCD2004_INTF4BITS | LCD2004_TWO_LINES | LCD2004_FONT_5_7);

Delay(1);

LCD2004_write_rs_command(LCD2004_DISPLAY_ON_OFF | LCD2004_DISPLAY_OFF | LCD2004_CURSOR_OFF | LCD2004_BLINKING_OFF);

Delay(1);

LCD2004_write_rs_command(LCD2004_DISPLAY_ON_OFF | LCD2004_DISPLAY_ON);

Delay(1);

LCD2004_write_rs_command(LCD2004_ENTRY_MODE_SET | LCD2004_INCREMENT | LCD2004_SHIFT_OFF);

Delay(1);

LCD2004_write_rs_command(LCD2004_MV_CUR_SHIFT_DISPLAY | LCD2004_DISPLAY_SHIFT | LCD2004_SHIFT_LEFT);

Delay(1);

LCD2004_write_rs_command(LCD2004_RETURN_HOME);

Delay(1);

} else {

LCD24004_BACKLIGHT_STATUS = 0;

LCD2004_reg_write(LCD2004_INIT);

Delay(1000000);

LCD2004_reg_write(LCD2004_INIT);

LCD2004_LCDwrite4bits(0x30);

Delay(5);

LCD2004_LCDwrite4bits(0x30);

Delay(1);

LCD2004_LCDwrite4bits(0x30);

Delay(1);

LCD2004_LCDwrite4bits(0x20);

Delay(1);

LCD2004_write_rs_command(LCD2004_FUNCTION_SET | LCD2004_INTF4BITS | LCD2004_TWO_LINES | OLED_FONT_EU2);

Delay(1);

LCD2004_write_rs_command(LCD2004_DISPLAY_ON_OFF | LCD2004_DISPLAY_OFF | LCD2004_CURSOR_OFF | LCD2004_BLINKING_OFF);

Delay(1);

LCD2004_write_rs_command(LCD2004_DISPLAY_ON_OFF | LCD2004_DISPLAY_ON);

Delay(1);

LCD2004_write_rs_command(LCD2004_ENTRY_MODE_SET | LCD2004_INCREMENT | LCD2004_SHIFT_OFF);

Delay(1);

LCD2004_write_rs_command(LCD2004_MV_CUR_SHIFT_DISPLAY | LCD2004_DISPLAY_SHIFT | LCD2004_SHIFT_LEFT);

Delay(1);

LCD2004_write_rs_command(LCD2004_RETURN_HOME);

Delay(1);

}

lcd_initialized = 1;

}

// contrast : 6bit , 0-63, 0x00 - 0x3F

void LCD_contrast(unsigned char cont)

{

//LCD2004_write_rs_command( 0x38);

//LCD2004_write_rs_command( 0x39);

//LCD2004_write_rs_command( 0x70 | (cont & 0x0F));

//LCD2004_write_rs_command( 0x5C | ((cont>> 4) & 0x03));

//LCD2004_write_rs_command(0x6c);

//Delay(2000);

}

void LCD_set_contrast(unsigned char stbry, unsigned char oled)

{

if (LCD_ADDR==LCD_ADDR_STBRY) {

LCD_contrast(stbry);

} else {

LCD_contrast(oled);

}

}

void LCD_clear(void)

{

if (LCD_ADDR!=LCD_ADDR_20x4) {

LCD_reg_write(LCD_RS_CMD, LCD_CMD_CLEAR);

Delay(1);

LCD_reg_write(LCD_RS_CMD, LCD_CMD_HOME);

Delay(1);

} else {

LCD2004_write_rs_command( LCD_CMD_CLEAR);

Delay(1);

LCD2004_write_rs_command( LCD_CMD_HOME);

Delay(1);

}

}

void LCD_setcursor(unsigned char col, unsigned char row)

{

unsigned char offset_0[] = {0x00, 0x40};

unsigned char offset_1[] = {0x00, 0x20};

unsigned char offset_2[] = {0x00, 0x40, 0x14, 0x54};

if(LCD_ADDR == LCD_ADDR_STBRY) {

LCD_reg_write(LCD_RS_CMD, 0x80 | (col + offset_0[row]));

} else if(LCD_ADDR == LCD_ADDR_AKIZU_0 || LCD_ADDR == LCD_ADDR_AKIZU_1 ){

LCD_reg_write(LCD_RS_CMD, 0x80 | (col + offset_1[row]));

} else {

LCD2004_write_rs_command( 0x80 | (col + offset_2[row]));

}

}

void LCD_putc(unsigned char c)

{

if (LCD_ADDR!=LCD_ADDR_20x4) {

LCD_reg_write(LCD_RS_DATA, c);

} else {

LCD2004_write_data( c);

}

}

void LCD_puts(char *str)

{

int i;

for (i=0; i< lcd_max_x; i++) {

if (str[i]== 0x00) {

break;

} else {

LCD_putc((unsigned char)str[i]);

}

}

}

//-------------------------------------------

// API

//-------------------------------------------

uint8_t lcd_buffer[80]; // 4*20

uint8_t lcd_x, lcd_y;

void lcd_xy(uint8_t x, uint8_t y)

{

if (x>(lcd_max_x-1)) x = 0;

if (y>(lcd_max_y-1)) y = 0;

lcd_x = x;

lcd_y = y;

}

void lcd_clr(void)

{

int i;

lcd_x = 0; lcd_y = 0;

for (i=0;i<80;i++) {

lcd_buffer[i] = 0x20;

}

}

void lcd_putc(char c)

{

int pos;

pos = lcd_x + lcd_y*16;

lcd_buffer[pos] = c;

}

void lcd_puts(char *str)

{

int pos;

int i;

pos = lcd_x + lcd_y*lcd_max_x;

for (i=0;i<lcd_max_x;i++) {

if (str[i]==0x00) {

break;

} else {

lcd_buffer[pos] = str[i];

pos++;

lcd_x++;

}

}

}

void lcd_refresh(uint8_t y)

{

int i;

if (lcd_initialized == 0)

return;

if (LCD_ADDR!=LCD_ADDR_20x4) {

if (y>1)

return;

}

LCD_setcursor(0, y);

for (i=y*lcd_max_x; i<(y+1)*lcd_max_x;i++) {

if (LCD_ADDR!=LCD_ADDR_20x4) {

LCD_reg_write(LCD_RS_DATA, lcd_buffer[i]);

} else {

LCD2004_write_data(lcd_buffer[i]);

}

}

}

void lcd_update(void)

{

int i;

for (i=0;i<4;i++)

lcd_refresh(i);

}