ATmega128이나 ARM을 이용하여 작품을 만드는 졸작시즌이 드디어 다가왔습니다! ㅎㅎ

전국의 공대생분들께선 벌써부터 인터넷에 자료를 긁어모으시면서 이리저리 캡스톤을 구상하고 계실텐데요~

오늘은 가장 많이들 사용하시는 CLCD (문자표시용 LCD모듈)에 대해서 정리해보겠습니다! 

저 까짓게..정리라고 하니..우습군요...ㅎㅎ...ㅠㅠ..ㅋㅋ 암튼..필요한분들께 사막이 오아시스가 되길 바라며~ 장황한 설명을 시작하겠습니다! ^^  (70%이해와 실전사용을 목표로 설명하겠습니다.)



1. LCD모듈의 구조


LCD모듈은 문자표시용과 그래픽표시용으로 나누어 집니다. 그중에서 지금 다룰것은 문자표시용입니다.

주로 범용으로 많이 쓰시는 CLCD는 16문자 2라인입니다. (이것은 가로16칸, 세로2칸에 글자를표시할수있다는걸 의미합니다)


먼저 아래의 구조도를 봐주세요~




왼쪽에 5V, RS,RW,E, D0~D7,VL, 0V는 외부핀을 의미합니다. 사각형라인 밖은 외부핀입니다. 당연히 사각형 안은 내부의 데이터흐름을 나타내고 있습니다. 우리는 이 구조도만 이해하면 앞으로 프로그램코딩과 하드웨어 설계가 가능할 것입니다!


하드웨어적인 부연설명을 하자면, LCD모듈은 보통 1~16핀으로 구성되어있고요 위 그림의 왼쪽의  5V, RS,RW,E, D0~D7,VL, 0V이 이 외부핀을 의미하는 것입니다. 실제적으로 LCD모듈이 사용자가 의도하는 어떤 동작을 하기원한다면 MCU를 통해 연결된 이 16핀의 몇개핀에 타이밍에 맞춰서 High Voltage를 흘려주어야 하는거지요.  여기에 대한 구체적인 예가 아래의 2) 데이터 레지스터 DR part에 나와있습니다. 아무래도 글로 설명하기엔 힘듭니다만, 아래의

http://blog.naver.com/meelong0?Redirect=Log&logNo=140104604266

한방인생님의 블로그에 있는 캐릭터LCD사용법을 참조해주세요 ^^;..그림과 함께 아주 알기쉽게 설명되어있습니다.

그런다음, 다시 오셔서 소스를 보시면 이해가 그나마 되실 거에요~


모듈의 구조는 크게 3가지로 나누어 집니다.

 

1) 인스트럭션 레지스터 IR (명령어 레지스터)

 ▶ LCD모듈의 설정 및 동작이 이루어지도록 하는 레지스터입니다. 이것을 통하여 우리는 LCD모듈 화면에 문자를 쓰기도 읽기도 그   

     리고 화면 클리어 등 명령을 줄 수있습니다.


2) 데이터 레지스터 DR

 ▶ 값을 쓰는 곳입니다. AVR을 통해서 원하는 값을 전달하면 (8핀이니깐 AVR에서 Char형 변수로 전달하면 1클럭에 가겠군요^^)

     이곳 데이터 레지스터가 버퍼역할 하여 값을 임시적으로 가지고 있어줍니다. 모든 명령과 디스플레이를 원하는 문자는 이곳을 

     거쳐서 지나간다고 생각하세요 ^^


    //아래의 예제는 ATmega를 기본적으로 사용하실 수 있는분이 아니시라면 나머지 글을 다 읽으시고 보시는게 좋을거 같습니다.

    예시) 구체적인 데이터의 흐름으로 설명하겠습니다.

             AVR에서 LCD화면에 'A'라는 알파벳을 디스플레이하기를 원한다고 가정하겠습니다. 그리고

             AVR의 I/O핀중 PORTA(0~7)가 LCD모듈의 D0~7핀에 모두 연결되어있다고 가정하겠습니다. 

         

 DDRA = 0xFF; //PORTA를 모두 출력으로 설정

             Char value = 'A' ; //value라는 캐릭터형(8bit)변수에 문자 'A'를 대입하라.

             //아래의 ↓소스코드가 실행되기전 IR(명령레지스터)에 LCD에 문자를 뿌려주는 설정을 합니다.

             PORTA = value;  //PORTA에 value값을 전달하라.



또한, 사용자는 위와같은 소스를 작성했다고 하겠습니다.

(사실 데이터 레지스터를 사용하기 위해서는 RS,RW,E 에 대한 신호도 인가해야하지만 나중에 아래에서 설명할것이 때문에

넘어가겠습니다.)

저 코드에서 중요한 부분은 PORTA에 값을 그대로 넣어주었다는 것입니다. 즉 하드웨어적으로 포트A와 LCD모듈의 데이터핀8핀이 1:1로 대응하기에 저런식으로 코드를 작성하는 것입니다 (4핀으로도 제어가 가능합니다. 차후 설명)

            LCD모듈내부에는 AVR에서 'A'라는 문자형변수를 입력했지만 자동적으로 아스키코드로 변환되도록 설계가 되어있기때문             

            에 사용자는 문자를 아스키코드로 변환하여 모듈로 실어 보내지 않아도 됩니다^^


           [흐름] LCD모듈 데이터핀과 연결된 AVR I/O핀에 출력되길 원하는 값을 씁니다. 

                     (RS=1 : 데이터레지스터사용, R/W=0 : 쓰기, E=1 : Enable신호)


                     LCD모듈의 데이터핀은 해당값을 가지고 있어줍니다.

                     LCD모듈의 명령어핀과 연결된 핀에 LCD화면에 해당문자를 출력하라고 명령합니다.

                     LCD모듈은 데이터핀에 있는 값을 화면에 뿌려줍니다.

                     AVR로 LCD모듈에게 중지명령을 내립니다. (RS=1, R/W=1, E=0)

 

           이렇게 위와 같은 형태로 동작을 합니다. 차후 설계된 하드웨어와 LCD구조도를 예제소스와 함께 해석하겠습니다.


3)  DDRAM 과 CGRAM


 DDRAM : 만약 구현하는 모듈이 20(열)X2(행)의 LCD라면 DDRAM은 아래와 같은 주소값을 가지고 있습니다.

              그리고 80X8비트 또는 80문자의 용량을 가지고있습니다. 사용자가 출력할 문자들을 가지고 있을 곳입니다.

              그러나 RAM이기 때문에 전원을 껐다키면 다 삭제되겠지요



CGROM : 8비트 문자패턴을 저장하고 있습니다. 208개의 문자 도트패턴, 문자코드 0x31~0xFF 까지는 아스키코드와 일치합니다

              아스키코드의 번호와 일치하기때문에 위에 설명한 예시에서 처럼 소프트웨어적으로 아스키변환함수를 쓰지않고도 문자를 

              쉽게 출력할수가 있습니다.


CGRAM : 사용자가 직접 문자도트패턴을 만들어 넣을수있는 공간입니다. 한글폰트도 노가다를 좀 하시면 만드실수 있습니다.




2. LCD모듈의 PIN




따라서 위의 핀의 설명대로 ATmega128에 적용하면 아래의 회로도와 같습니다.




3. LCD모듈의 타이밍도


               [읽기 동작 타이밍]


             


              [쓰기 동작 타이밍]



타이밍도를 분석해보면 쓰기와 읽기의 차이는 결국 R/W신호의 High , Low라는것을 알수 있습니다.

그리고 LCD모듈을 컨트롤하기위해서는 반드시 RS, R/W , E 핀제어가 필요하다는것도 알수있구요..

구체적인 타이밍은 아래의 표에 정리되었습니다. 그리고 타이밍도따른 프로그램코딩은 아래의 예제에서 한꺼번에 다루겠습니다.




4. LCD모듈의 레지스터



IR(명령 레지스터)-----------------------------

LCD 화면 클리어, 커서시프트, 문자표시 ON/OFF 등 LCD의 제어에 필요한 명령을 저장.

표시데이터 RAM(DDRAM)의 위치 주소와 문자 발생기 RAM(CGRAM)의 위치를 지정하기 위한 주소 정보를 저장. 


DR(데이터 레지스터)--------------------------

DR 레지스터에 데이터를 쓰면 LCD의 내부적인 동작에 의해서 IR에 의해 지정된 DDRAM 또는  CGRAM의 주소로 전달.

DR 레지스터의 데이터를 읽으면, IR에 의해 지정된 DDRAM 또는 CGRAM의 주소 데이터가 마이크로컨트롤러로 전달. 



[레지스터의 선택]

 RS

R/W 

동작수행

0

0

 IR을 선택하여 제어 명령 쓰기(디스플레이 클리어 등)

0

1

 DB7로부터 비지플래그를 읽기/주소 카운터의 내용을 DB0~DB6으로부터 읽기

 1

 0

 DR을 선택하여 데이터 값을 쓰기(DR 에서 DDRAM 또는 CG RAM로)

 1

 1

 DR을 선택하여 데이터 값을 읽기(DDRAM 또는 CGRAM에서 DR로)

우리가 주로 사용하게 될 레지스터의 선택은 아래의 3,4항목입니다.

RS & R/W 핀에 High 와 Low신호를 인가하게 되고 Enable pin에 High를 주게되면 그때부터 CLCD는 쓰기 모드로 동작합니다. 반대로

RS & R/W 핀에 High 와 High신호를 인가하게 되고 Enable pin에 High를 주게되면 그때부터 CLCD는 읽기 모드로 동작합니다.

따라서 하드웨어 설계시 CLCD핀에 연결한 AVR I/O핀으로 제어를 하게 됩니다 ^^ 그래서 시스템설계자는 반드시 자신이 디자인한 하드웨어를 완벽히 이해하고서 프로그래밍을 하게되겠죠??


덧붙여서 비지플래그도 설명하고 넘어가겠습니다.


[ 비지 플래그] 

연속적으로 LCD 모듈에 제어명령이 입력될 때 LCD모듈이 이 명령을 처리 할 수 있는가를 나타내는 상태 표시 플래그입니다.

LCD모듈은 읽기나 쓰기중일때 다음명령을 바로 처리할수없기 때문에 이전 명령어를 처리할 일정한 시간을 기다려줘야합니다.

말그대로 괜히 Busy라고 붙은게 아니겠죠? ㅎ 코딩하실때 이 값을 읽어서 LCD의 상태를 확인하고 명령을 처리하시면 최상의 시간으로 LCD모듈을 제어하실수 있습니다 ^^ 귀찮으시면 대충 딜레이 조금만 주시고 넘기셔도 무방합니다~


RS=0,          일 때 출력되며, 이 때 BF 플래그는 데이터버스의 DB7로 출력.

BF = 0 : LCD 모듈로 다음명령을 쓸 수 있음.

BF = 1 : LCD 컨트롤러(HD44780U)는 현재 IR로 입력된 명령어를 처리하고 있는 상태로서, 다음 제어 명령을 쓸 수 없는 상태. 


[주소카운터] : LCD 디스플레이창은 아래와 같이 하나의 셀마다 주소값을 가지고있습니다.


위의 경우는 20문자 X 2라인 경우인데 흔히사용하시는 16 X 2라인도 표의 16번째 라인까지만 있다고 생각하시면 동일합니다! 


주소카운터는 DDRAM과 CGRAM의 주소를 지정하는데 사용되며 주소 카운터의 값은 IR레지스터에 기록됩니다.

또한 주소정보는 IR레지스터에서 내부의 주소 카운터로 자동으로 전송되어 해당 주소 카운터의 값으로 세트됩니다.

그리고 DDRAM  또는 CGRAM의 선택은 LCD명령에 의해서 결정이 되며 DDRAM 또는 CGRAM에 데이터를 써 넣으면 주소 카운터는 모드에 따라서 자동으로 1씩 증가하거나 또는 1씩 감소합니다. 나중에 예제를 보시면 이해가 되실겁니다.


[문자발생기 CGROM] : CLCD는 기본적으로 아스키코드와 일치하는 값으로 8비트 문자패턴을 가지고있습니다.

아래의 그림은 CGROM에 저장된 문자패턴입니다. 




[문자발생기 DDRAM] : 위의 기본저장된 문자패턴말고도 CLCD는 사용자가 원하는 문자패턴을 새롭게 만들어 저장하여 사용할수가 있습니다. 약 8비트로 문자로 80문자정도의 용량을 가지고 있습니다.




DDRAM은 사용자가 프로그램에 의해서 원하는 문자 패턴을 만들고자 할 때 사용하는 RAM영역으로써
5Χ8도트의 문자 패턴을 만들 경우, 최대 8종류의 패턴 생성이 가능하며, 5Χ10도트의 경우, 4종류의 문자 패턴 생성 가능합니다.

[새로운 문자를 만드는 방법]


설계된 비트 패턴은 CGRAM 주소로 지정되고, 주소는 6비트를 사용합니다.
5Χ8도트일 경우에 한 문자당 8바이트를 사용되어 문자는 8개로 제한됩니다.

위 그림의 예시를 봐주세요.

문자코드의 비트 4~7이 0일대 선택 (3번 비트는 무효비트)
‘R’문자의 설계- CG 패턴내의 주소 ‘0b000000’∼'0b000111'에 저장- 저장된 문자 코드는 0x00 또는 0x08에 의해 선택- CGRAM으로의 데이터 쓰기/읽기 명령과 함께 사용됨. 


[컨트롤(제어) 명령 모음] : CLCD를 제어하기 위한 명령들입니다. 코드에 해당하는 AVR의 핀에 1,0 신호를 주게 되면 해당되는 명령이 실행이 됩니다.




몇가지를 예로 들어서 설명을 드린다면,

화면클리어를 하고싶다면

RS : 0 R/W : 0 각 핀에 0을 주고 DB7~DB0 핀에 0x01값만 써넣게 되면 CLCD의 화면의 내용은 클리어됩니다.


[CLCD와 AVR간의 인터페이스 회로]



만약 위의 회로대로 하드웨어을 구성하시고 아래의 예제소스 코드를 AVR에 주입하시면 TEST라는 문자가 출력될것입니다.



[예제 소스코드] : 인터넷에 있는 소스코드를 조금 수정해보았습니다 ^^; 컴파일러는 AVR studio 4.0 입니다.


#include<avr/io.h>

#include<avr/interrupt.h>      //인터럽트 헤더 파일

#include<util/delay.h>        //딜레이 헤더 파일


#define LCD_WDATA PORTA     //LCD데이터 포트 정의

#define LCD_WINST PORTA    //

#define LCD_CTRL PORTG    //LCD 제어 포트 정의

#define LCD_EN 0         //LCD 제어(PING0~2)를 효과적으로 하기위한 정의

#define LCD_RW 1

#define LCD_RS 2

#define Byte unsigned char //자주쓰이는 무부호 문자형 자료형을 Byte로 선언




   


/*   [ 정 리 by 도닦는 공돌이] 


     **아래의 프로그램은 다음과 같은 하드웨어용 프로그램입니다.            **

**데이터포트(PORTA의 0~7핀을 모두 사용함), 제어포트(PORTG의 0~2 사용) **


   1) ::IR 명령레지스터 ::


      RS=0 , RW=0 명령레지스터 선택 - 제어명령 쓰기

     RS=0 , RW=1 Busy플래그 읽기, 주소카운터의 내용을 읽기

   


   2) ::DR 데이터레지스터 ::


      RS=1 , RW=0 데이터레지스터 선택 - 쓰기명령

 RS=1 , RW=1 데이터레지스터 선택 - 읽기명령

   

   3) 명령 및 쓰기를 위해서는 RS, RW, E, DATA 신호들을 타이밍에 맞게 입력해야한다.


   4) 데이터를 쓰기전에는 bf값을 읽어보고 실행해야하지만 이 프로그램에선 실행마다 충분한 Delay를 주었고

      BF 프래그를 사용할 만한 처리는 안했기에 함수를 사용하진 않았다. (함수는 존재함)


   5) 주소카운터(ac)로 문자커서 위치선택(DDRAM의 주소)

      윗줄좌측   0X00 ~ 0X0F

      아랫줄좌측 0X40 ~ 0X4F 


   6) ::커서 시프트::


      DB4 =1, DB3=S/C , DB2= R/L


 S/C = 0, R/L =0 : 커서를 좌로 이동 (AC를 -1)

      S/C = 0, R/L =1 : 커서를 우로 이동 (AC를 +1)

      S/C = 1, R/L =0 : 화면전체를 좌로 이동 (커서도 따라감)  

      S/C = 1, R/L =1 : 화면전체를 우로 이동 (커서도 따라감)  

 

 

  

   7) 딜레이 함수의 사용

      IR,DR명령 사용시 화면지움과 커서홈을 제외하고는 40us의 실행시간이 소요됨

 화면지움과 커서홈 IR명령은 1.64ms의 실행시간이 소요


*/



void LCD_Data(Byte ch)

{

LCD_CTRL |=  (1 << LCD_RS);    //RS=1, R/W=0 으로 데이터 쓰기 싸이클

LCD_CTRL &= ~(1 << LCD_RW); 

LCD_CTRL |=  (1 << LCD_EN);    //LCD 사용

_delay_us(50);

LCD_WDATA = ch;                //데이터 출력

_delay_us(50);

LCD_CTRL &= ~(1 << LCD_EN);    //LCD 사용안함


}

  //  0         1      2

void LCD_Comm(Byte ch)   //PG0-EN , PG1-RW, PG2-RS , PG4-TOSC1핀(사용안함)

{       //LCD_CTRL = LCD제어부 포트(4핀인데 실질적인 사용3핀)

LCD_CTRL &= ~(1 << LCD_RS);    // RS=0, RW=0 으로 정의함.

LCD_CTRL &= ~(1 << LCD_RW);    

LCD_CTRL |=  (1 << LCD_EN);    //LCD 사용허가

_delay_us(50);

LCD_WINST = ch;                //명령어 쓰기

_delay_us(50);

LCD_CTRL &= ~(1 << LCD_EN);    //LCD 사용안함

}


void LCD_CHAR(Byte c)  //한문자 출력 함수

{

LCD_Data(c); //CGROM 문자코드의 0x31 ~ 0xFF 는 아스키코드와 일치함!

_delay_ms(1);

}


void LCD_STR(Byte *str)    //↑문자열을 한문자씩 출력함수로 전달

{ while(*str !=0)

{

LCD_CHAR(*str);

str++; 

}

}



void LCD_pos(unsigned char col, unsigned char row)  //LCD 포지션 설정

{

LCD_Comm(0x80 | (row+col*0x40));  // row=행 / col=열 ,DDRAM주소설정

}


void LCD_Clear(void)  // 화면 클리어

{

LCD_Comm(0x01);

_delay_ms(2); //1.6ms이상의 실행시간소요로 딜레이필요 

}



void LCD_Init(void)   //LCD 초기화

{

LCD_Comm(0x38);   //데이터 8비트 사용, 5X7도트 , LCD2열로 사용(6)

_delay_ms(2);

LCD_Comm(0x38);   //데이터 8비트 사용, 5X7도트 , LCD2열로 사용(6)

_delay_ms(2);

LCD_Comm(0x38);   //데이터 8비트 사용, 5X7도트 , LCD2열로 사용(6)

_delay_ms(2);

LCD_Comm(0x0e);   //Display ON/OPFF

_delay_ms(2);

LCD_Comm(0x06);   //주소 +1 , 커서를 우측으로 이동 (3)

_delay_ms(2);

    LCD_Clear();

}



 int main()

 {

DDRA = 0xff; //PORTA(LCD연결포트)를 출력으로 설정

DDRG = 0x0f; //PORTG(LCD컨트롤)의 하위 4비트를 출력으로설정


Byte str1[] = "TEST";


LCD_Init(); //LCD 초기화

LCD_pos(0,1); //LCD 포지션 0열 1행 지정

        LCD_STR(str1); //문자열 str을 LCD출력

LCD_Comm(0x18); //LCD 커서 이동

        while(1);

  }


 








 CLCD 사용하기-2에서는 실질적으로 각각의 파트별로 소스코드를 해체하여 해석해보겠습니다 ^^




+ Recent posts