AVR2012.02.04 11:23

[AVR Essay 11] 2 x 16 Character LCD

 

 

2 x 16 Character LCD는 학습용 키트에서 가장 많이 사용되는 LCD로 알고 있다.

 

내 경험 상, 이거 안 달린 키트를 못 봤고 이 내용을 다루지 않는 마이크로 프로세서 서적을 못 봤다.

 

그리고 내 방에 가장 많이 굴러다니고 있는 LCD이기도 하다.

 

요놈은 다음과 같이 생겼다.

 

 

이 놈은 제조 회사나 백라이트 유무에 따라 많은 모델이 있고

 

모듈 형태도 조금씩 다르다.

 

그리고 여기서는 2 x 16 크기만 다루지만 1 x 8 부터 4 x 40 크기까지 다양한 크기가 있다.

 

(여기서 2 x 16 이라는 것은 2줄짜리이고 한 라인에 16 글자 표현 가능하다는 의미이다.)

 

그런데, 내부 구조는 거의 동일 하다.

 

즉, 한가지 모델만 제대로 이해하면 다른 모델은 데이터 시트만 쓱 보면 잘 사용 할 수 있다는 것이다.

 

 

일반적인 Character LCD는 다음과 같은 특징을 가지고 있다.

 

1. 5V의 단일 전원으로 동작한다.

 

2. CG (Character Generator) ROM, CG RAM, DD (Display Data) RAM을 내장하고 있다

 

3. 인터페이스가 표준화 되어 있고, 8bit 데이터 버스, 4bit 데이터 버스 중 선택 해서 제어가 가능하다.

 

4. 내부 제어 명령이 거의 표준화되어 있어서 제조회사나 모델에 상관 없이 동일한 방법으로 구동 가능하다.

 

5. LCD 모듈은 일반적인 I/O 인터페이스 반도체 소자에 비해 액세스 시간이 상당히 길어 동작이 느리다.

 

6. 영문 ASCII 문자와 일본어를 표시 가능하며 8개의 사용자 정의 문자를 만들어 사용 할 수 있다.

 

7. 자동 커서 증가, 커서 이동, 커서 디스플레이 유무를 조작 할 수 있다.

 

8. 문자를 5 x 7 (혹은 5 x 10) 도트 매트릭스 방식으로 표시한다.

 

9. DD RAM, CG RAM에 데이터를 읽고 쓸 수 있다.

 

10. 백라이트 모델이 있어 어두운 곳에서도 화면을 볼 수 있고 백라이트 광원도 다양하다.

 

 

대충 이정도로 이론은 정리하고, 내부 구조 등을 상세히 알고 싶으면 데이터 시트를 참조 해라.

 

(데이터시트 구하기 생각보다 어려웠다. 그래서 이놈 데이터 시트는 이 문서에 첨부 했다.)

 

 

외부 핀은 다음과 같은 기능을 한다.

신호명

기능

1

Vss

 전원 GND

2

Vdd

 전원 +5V

3

Vo

 화면 밝기 제어 동작 전압 (4.2 < Vdd-Vo < 4.6)

4

Rs

 Register Select (1 = data, 2 = instruction)

5

R/W

 Read/Write ( 1 = CPU <- LCD, 0 = CPU -> LCD )

6

E

 Enable signal for read/write

7

DB0

 (LSB)

8

DB1

 

9

DB2

 

10

DB3

 데이터 버스

11

DB4

 (4bit 인터페이스 모드에서는

12

DB5

  DB7~DB4를 사용)

13

DB6

 

14

DB7

 (MSB)

 

모델에 따라 15, 16은 백라이트로 사용된다.

 

백라이트는 모델마다 사용법이 다를 수 있으므로 해당 모델 데이터 시트를 구해서 참조 하기를 바란다.

 

LCD 자체 전원은 다음과 같이 준다.

 

이때 VDD와 VSS의 방향이 바뀌면 LCD 뿐만 아니라 회로 전체가 손상 입을 수 있으므로 주의 해야한다.

 

(실제로 나는 전원 잘 못 줬는데 LCD는 멀쩡한데 MCU가 나가 버렸다.)

 

여기서 가변 저항은 Vo에 들어가는 전압을 조작하기 위한 용도이기 때문에

 

아무것이나 사용해도 상관 없지만 경험상 5k옴 정도가 적합한 것 같다.

 

가변 저항은 LCD에 표시되는 화면을 최적으로 조정하기 위해서 사용한다.

 

 

핀 4, 5, 6은 제어 포트이고 7에서 14는 데이터 I/O 포트이다.

 

이 내용은 아래 표에 설명 되어 있다.

 

기능

제어 신호

제어 명령

실행시간

(Max.)

RS

R/W

7

6

5

4

3

2

1

0

 Clear Display

0

0

0

0

0

0

0

0

0

1

1.52 ms

 Return Home

0

0

0

0

0

0

0

0

1

-

1.52 ms

 Entry mode set

0

0

0

0

0

0

0

1

I

S

37 us

 Display ON/OFF control

0

0

0

0

0

0

1

D

C

B

37 us

 Cursor or display shift

0

0

0

0

0

1

S

R

-

-

37 us

 Function set

0

0

0

0

1

D

N

F

-

-

37 us

 Set CG RAM address

0

0

0

1

CG RAM address

37 us

 Set DD RAM address

0

0

1

DD RAM address

37 us

 Read busy flag and address

0

0

B

Address Counter

0 us

Data write to CG RAM/ DD RAM

1

0

 

41 us

Data read from CG RAM/ DD RAM

1

1

 

41 us

 

① 화면을 모두 지우고 커서를 home으로 위치 시킨다.

 

② 커서만 home으로 위치 시킨다. (데이터 보존)

 

③ LCD를 read/write 할 때 DD RAM 어드레스 증가(I=1), 감소(I=0), 화면 시프트 할 것(S=1)인지 안 할 것(S= 0)인지 지정한다.

 

④ 화면 On/Off (D), 커서 On/Off (C), 커서를 깜박이게 할 것인지(B)를 지정한다.

 

⑤ 커서를 이동시킨다. (R = 1이면 오른쪽, R=0이면 왼쪽)

 

⑥ 데이터 비트 제어(8bit : D = 1, 4bit : D = 0), 화면 행 수(2행 : N = 1), 폰트 크기(F)를 지정한다.

 

⑦ CG RAM을 선택한다.

 

⑧ DD RAM을 선택한다.

 

⑨ LCD가 명령을 수행 중인지 확인한다.

 

 

제어 명령은 몰라도 대충 어떤 기능이 있는지는 알고 있어야 한다.

 

lcd 관련 라이브러리를 직접 만들 수도 있겠지만 범위가 방대하기 때문에

 

다른 사람이 제작한 라이브러리를 읽고 이해하는 정도면 충분 하다고 생각한다.

 

 

LCD를 사용하기 위해서는 간단한 초기화 과정이 필요 하다.

 

이는 사용 준비를 하기위한 절차이며

 

4 bit로 제어 할 것인지, 8 bit로 제어 할 것인지 설정 하기 위함이다.

 

 

8 bit 제어는 다음 절차를 거친다.

 

 

4 bit 제어 초기화는 다음과 같다.

 

만약 잘 안 보이면 첨부한 데이터시트를 참조 하도록 하라.

 

 

DD RAM은 표시할 문자들의 ASCII 코드가 저장되어 있는 내부 메모리이며 모두 80개의 번지가 있다.

 

그 중에서 2 x 16 CLCD는 32개만을 사용한다. 그리고 이 주소에 해당하는 위치가 실제 출력 위치이다.

 

 

문자 데이터는 데이터 시트에 잘 나와있고 그 중 일부를 발췌 했다.

 

아래와 같은 형태로 출력이 되고 데이터 값 자체는 ASCII 코드와 일치 하므로 편리하다.

 

 

이제 그러면 직접 ATmega 8535로 LCD를 구동해 보도록 하자.

 

다음과 같이 하드웨어를 구성한다.

 

(여기서 VDD, VSS, Vo는 생략 했다. 회로도에는 생략되었지만 반드시 구현해야한다.)


 

 

이 프로그램에 대한 소스는 다음과 같다. (delay.h와 delay.c는 생략 했다.)

 

형광색으로 칠한 주석 부분은 소스의 파일 명이다.

 

 

// main.c

#include <avr/io.h>
#include "delay.h"
#include "lcd.h"

int main(void)
{
   lcd_init() ;

   //                              "0123456789ABCDEF"
   lcd_display_string(0, 0, "          Hello!         ");
   lcd_display_string(0, 1, "ATmega8535 World");
 
   return 0;
}

 

 

// lcd.h

#ifndef _LCD_H_
#define _LCD_H_

 

#include <avr/io.h>
#include "delay.h"

 

// LCD 사용 포트를 지정한다.
#define LCD_DCTRL (DDRC)
#define LCD_DDATA (DDRD)
#define LCD_CTRL (PORTC)
#define LCD_DATA (PORTD)

#define LCD_RS   (PC5)
#define LCD_RW   (PC6)
#define LCD_E   (PC7)
#define LCD_DB0  (PD0)
#define LCD_DB1  (PD1)
#define LCD_DB2  (PD2)
#define LCD_DB3  (PD3)
#define LCD_DB4  (PD4)
#define LCD_DB5  (PD5)
#define LCD_DB6  (PD6)
#define LCD_DB7  (PD7)

 

void lcd_init(void); // LCD 초기화
void lcd_control(uint8_t data) ; // 기능 제어
void lcd_write_char(uint8_t data) ; // CG/DD RAM 데이터 쓰기
void lcd_position (uint8_t x, uint8_t y) ; // 커서 위치 지정
void lcd_display_string(uint8_t x, uint8_t y, char *string) ; // 문자열 출력

 

#endif

 

 

// lcd.c

#include "lcd.h"

void lcd_init(void) 

   LCD_DDATA = 0xFF;

   LCD_DCTRL = 0xFF;
   LCD_DATA = 0;
   LCD_CTRL = 0;

   delay_ms(1);

 

   lcd_control(0x38);  // Function Set (8bit, 2line, 5 x 7 dot 
   lcd_control(0x0C);  // Display ON, Cursor OFF
   lcd_control(0x06);  // Entry Mode Set (increment, not shift)
   lcd_control(0x01);  // Clear Display   
}

 

void lcd_control(uint8_t data) 
{
   LCD_CTRL = LCD_CTRL & ~_BV(LCD_RW);
   LCD_CTRL = LCD_CTRL & ~_BV(LCD_RS);
   LCD_DATA = data;
   LCD_CTRL = LCD_CTRL | _BV(LCD_E);
   delay_ms(2);
   LCD_CTRL = LCD_CTRL & ~_BV(LCD_E);
}


void lcd_write_char(uint8_t data) 
{
   LCD_CTRL = LCD_CTRL & ~_BV(LCD_RW);

   LCD_CTRL = LCD_CTRL | _BV(LCD_RS);
   LCD_DATA = data;
   LCD_CTRL = LCD_CTRL | _BV(LCD_E);
   delay_ms(2);
   LCD_CTRL = LCD_CTRL & ~_BV(LCD_E);
}


void lcd_position(uint8_t x, uint8_t y) 
{
   uint8_t location=0;


   if(y > 0x01) 
      y = 0x01; 
   if(x > 0x0f)

      x = 0x0f;
 

   if(y == 0)

      location = x + 0x80;

   else

      location = x + 0xC0; 
   lcd_control(location); 
}


void lcd_display_string(uint8_t x, uint8_t y, char *string) 
{
   lcd_position(x,y);

   while(*string != '\0')
   {
      lcd_write_char(*string);
      string++;
   }
}


 

대충 어떤 것을 출력 할 것인지는 감이 올 것이다.

 

lcd.h에서 #define 된 부분에 괄호속에 값을 바꾸면 손쉽게 제어 포트를 바꿀 수 있다.

 

그리고 명령 중에 PC에서 프로그래밍 할 때 못 본 내용이 있다.

 

_BV() 인데.... 이는 sft_defs.h에

 

#define _BV(bit) (1 << (bit))

 

과 같이 정의 되어 있다.

 

나머지는 표준 C 언어로 되어 있기 때문에 생략 한다.

 

명령 코드 표과 참조해서 보면 쉽게 이해가 될 것이다.

 

또, LCD의 많은 기능 중에서 문자 출력만 구현 했는데,

 

더 원하는 기능이 있다면 함수를 추가해서 사용 할 수 있다.

 

그러나 당장 사용하지 않는 기능을 함수로 추가 하는 것은 별로 좋지 않다.

 

AVR은 성능이나 용량이 PC에 비해 형편없이 딸리기 때문에 어느 정도 최적화를 고려 해야 한다.

 

 

결과는 다음과 같다.

 

 

 

PS...

 

2007년 06월 26일 4bit 제어 라이브러리도 추가하였다.

 

Posted by 콩알은