이글의 전부 또는 일부, 사진, 소스프로그램 등은 저작자의 동의 없이는 상업적인 사용을 금지합니다. 또한, 비상업적인 목적이라하더라도 출처를 밝히지 않고 게시하는 것은 금지합니다.

 

 


(본 글은 2017.09.29.에 필자의 다른 티스토리 http://avrlab.tistory.com에 적었던 것을 옮겨왔습니다.)

 

 

 


이번 글에서는 LCM4004A를 4비트로 제어하는 방법을 살펴 봅니다.

 

이전 글에서 언급한 바와 같이 LCD를 8비트로 제어하면 데이터 전송에 8비트, LCD 제어에 4비트 총 12비트를 사용합니다. 비교적 입출력 포트가 적은 마이크로컨트롤러에서 LCD 제어에 12비트를 할당하면 다른 목적으로 사용할 수 있는 입출력 포트 수가 많이 줄어듭니다.

 

그래서 데이터를 4비트로 나누어 두 번에 보내는 방법을 많이 사용합니다. 이렇게 하면 두 번에 나누어 보내기 때문에 다소 시간은 걸리겠지만, 4개의 입출력 포트를 추가로 확보할 수 있게 됩니다. 여기에 LCD로부터 데이터를 읽어오는 작업을 하지 않는다면 LCD의 RW 비트를 low(GND)에 묶어 놓는 방법으로 입출력 포트를 하나 더 확보할 수 있습니다. 즉, 4비트로 LCD를 제어하면 최소 7개의 입출력 포트를 사용하면 됩니다.

 

이 글에서는 1편에서 소개한 연결도에 큰 변화를 주지 않고 4비트로 제어하는 것을 다루도록 하겠습니다. 즉 PD4, PD5, PD6,PD7에 각각 LCD의 E2, E1, RW, RS를 연결하고, PC4 ~ PC7에 LCD의 DB4 ~ DB7을 연결하여 제어합니다. 1편에 있는 글에서 PC0 ~ PC3에 연결된 4개의 선을 제거한 상태로 LCD를 제어하고자합니다.

 

 

 

 

 

(1) 명령, 데이터를 전달하는 함수 만들기

 

 

 

 

명령을 전달하는 함수의 기본은 같습니다. 다만 4비트로 전달해야 하기 때문에 처음에는 명령의 상위 비트 4개(DB4~D7)을 전달하고, 두 번째로 나머지 4비트를 DB4~DB7로 보내야 합니다다음은 4비트로 명령을 전달하는 함수 LCD_WRITE_COMMAND 함수입니다.

명령을 전달하는 함수를 8비트와 같은 이름의 함수 LCD_WRITE_COMMAND라고 이름을 붙인 이유는 이렇게 함으로써 프로그램의 다른 함수들을 수정하지 않고 그대로 사용할 수 있기 때문입니다.

 


 

//////////////////////////////////////////////////
// LCD_WRITE_COMMAND:
// PARAM AL:COMMAND
// RETURN NONE
// CHANGED NONE
//////////////////////////////////////////////////
LCD_WRITE_COMMAND:
    CBI     LCD_CTRL_PORT,LCD_RS_BIT      // RS = 0
    RJMP  LCD_WRITE_QUIT

//////////////////////////////////////////////////
// LCD_WRITE_DATA:
// PARAM AL:COMMAND
// RETURN NONE
// CHANGED NONE
//////////////////////////////////////////////////
LCD_WRITE_DATA:
LCD_PUT_CHAR:
    SBI  LCD_CTRL_PORT,LCD_RS_BIT         // RS = 1

LCD_WRITE_QUIT:
    PUSH AH
    OUT   LCD_DATA_PORT,AL                  // 데이터 출력
    LDS    AH,LCD_CURRENT_EN
    CBI     LCD_CTRL_PORT,LCD_RW_BIT      // RW = 0
    SBRC   AH,LCD_E1_BIT                        // E = 1  상위 4비트 전달
    SBI      LCD_CTRL_PORT,LCD_E1_BIT

    SBRC    AH,LCD_E2_BIT
    SBI      LCD_CTRL_PORT,LCD_E2_BIT
    RCALL DELAY_1US
    SBRC   AH,LCD_E1_BIT

    CBI     LCD_CTRL_PORT,LCD_E1_BIT      // E = 0
    SBRC  AH,LCD_E2_BIT
    CBI     LCD_CTRL_PORT,LCD_E2_BIT
    RCALL DELAY_100US

    SWAP AL                                        // 상위 4비트, 하위4비트 바꿈
    OUT   LCD_DATA_PORT,AL                 // 데이터 출력
    SBRC  AH,LCD_E1_BIT                        // E = 1   하위 4비트 전달
    SBI     LCD_CTRL_PORT,LCD_E1_BIT
    SBRC  AH,LCD_E2_BIT
    SBI     LCD_CTRL_PORT,LCD_E2_BIT
    RCALL DELAY_1US
    SBRC  AH,LCD_E1_BIT                        // E = 0
    CBI     LCD_CTRL_PORT,LCD_E1_BIT
    SBRC   AH,LCD_E2_BIT
    CBI     LCD_CTRL_PORT,LCD_E2_BIT
    RCALL DELAY_100US
    POP  AH
    RET


 

위의 프로그램 루틴은 2편에서 작성한 명령 전달 함수 LCD_WRITE_COMMAND와  데이터 전달 함수 LCD_WRITE_DATA를 합쳐 놓은 루틴을 4비트 버전으로 재작성한 것입니다. 2편에서 명령 전달 함수와 데이터 전달 함수가 유일하게 다른 RS 비트를 설정하는 부분을 맨 앞으로 빼서 처리한 다음 공통 루틴을 사용하도록 했었습니다.

 

간단히 주석에 설명한 바와 같이 레지스터 AL로 전달 받은 내용을 LCD_DATA_PORT에 출력한 후에 E를 high로 설정했다가 low로 함으로써 상위 4비트를 전달합니다. 그 이후에 SWAP 명령으로 레지스터 AL의 상위 4비트와 하위 4비트를 교환한 다음에, 위와 같은 과정을 다시 한 번 반복함으로써 하위 4비트를 전달합니다.

 

SWAP 명령은 어셈블리어로 프로그래밍할 때만 사용 가능합니다. 단 하나의 명령으로 원하는 바를 얻을 수 있습니다. 이런 점이 어셈블리어로 프로그래밍하는 재미가 아닐까 생각합니다. C로는 이 명령을 사용할 수 없습니다. C언로 프로그래밍한다면  cmd =<< 4로 표현해야 하지 않을까 싶습니다. C로 프로그래밍하여 분석해 보지는 않았지만 cmd =<< 4는 곰셈 명령인 MUL로 번역되거나 아니면 LSL 명령 4번으로 번역되지 않을까 싶습니다.

 

 

 

 

 

 

(2) LCD로부터 값 읽어 오는 함수 만들기

 

 

 

다음은 2편에서 만들었던 데이터 읽어오는 함수 LCD_READ_DATA와 Busy Flag와 어드레스 카운터 값을 읽어 오는 함수를 4비트 버전으로 재작성한 것입니다.

 

 


 

//////////////////////////////////////////////////
// LCD_READ_DATA:
// PARAM NONE
// RETURN AL
// CHANGED AL
//////////////////////////////////////////////////
LCD_READ_DATA:
    SBI  LCD_CTRL_PORT,LCD_RS_BIT      ; RS = 1
    RJMP LCD_READ_QUIT

 

//////////////////////////////////////////////////
// LCD_READ_BF_ADDRESS:
// PARAM NONE
// RETURN AL
// CHANGED AL
//////////////////////////////////////////////////
LCD_READ_BF_ADDRESS:
    CBI  LCD_CTRL_PORT,LCD_RS_BIT              // RS = 0

LCD_READ_QUIT:
    PUSH AH
    PUSH BL
    LDI  BL,0                                             // set LCD_DATA_PORT input
    OUT  LCD_DATA_DDR,BL
    SBI  LCD_CTRL_PORT,LCD_RW_BIT             // RW = 1
    LDS  BL,LCD_CURRENT_EN
    SBRC BL,LCD_E1_BIT                               // E = 1
    SBI  LCD_CTRL_PORT,LCD_E1_BIT
    SBRC BL,LCD_E2_BIT
    SBI  LCD_CTRL_PORT,LCD_E2_BIT
    RCALL DELAY_100US
    IN  AL,LCD_DATA_PIN                             // read data from LCD_DATA_PIN
    SBRC BL,LCD_E1_BIT                               // E = 0
    CBI  LCD_CTRL_PORT,LCD_E1_BIT
    SBRC BL,LCD_E2_BIT
    CBI  LCD_CTRL_PORT,LCD_E2_BIT
    ANDI AL,0xF0                                      // 상위 4비트를 레지스터 AH에 저장
    MOV  AH,AL
    RCALL DELAY_100US

    SBRC BL,LCD_E1_BIT                             // E = 1
    SBI  LCD_CTRL_PORT,LCD_E1_BIT
    SBRC BL,LCD_E2_BIT
    SBI  LCD_CTRL_PORT,LCD_E2_BIT
    RCALL DELAY_100US
    IN  AL,LCD_DATA_PIN
    SBRC BL,LCD_E1_BIT
    CBI  LCD_CTRL_PORT,LCD_E1_BIT            // E = 0
    SBRC BL,LCD_E2_BIT
    CBI  LCD_CTRL_PORT,LCD_E2_BIT
    ANDI AL,0xF0
    SWAP AL                                          // 하위 4비트로
    OR  AL,AH                                        // 상위 4비트를 합침
    LDI  BL,LCD_DATA_DDR_VALUE
    OUT  LCD_DATA_DDR,BL
    RCALL DELAY_100US
    POP  AH
    POP  BL
    RET


 

간단히 주석에 설명한 바와 같이 4비트씩 두 번에 걸쳐 가져온다는 점을 제외하면 기본적인 과정은 8비트와 같습니다. 지연시간은 여러번 시행 착오 끝에 100uS로 정했습니다. 원래 tc가 1200nS 즉 1.2mS이어야 하는데 위와 같이 100uS만 주어도 오류없이 잘 동작합니다.

 

 

 

 

 

 

 

(3) 초기화 하기

 

 

 

사실 위의 함수 작성보다 안정적으로 확실히 초기화 하는 방법 찾기가 더욱 힘들었습니다.

이전의 글 Atmega32로 LCM4004A 다루기(3편)-LCD구동 8비트 2/2에서 다루었던 문자형 LCD 초기화 순서를 다시 살펴 보기로 합니다.

 

 

ㄱ) 전원 투입 후 전원이 안정될 때까지 대략 30mS 내지 50mS 정도 기다린다.

ㄴ) Function Set 명령을 보내고 4.1mS 이상 기다린다.

ㄷ) 다시 Function Set 명령을 보내고 100uS 이상을 기다린다.

ㄹ) Function Set 명령을 한 번 더 보낸다.

ㅁ) Function Set 명령을 또 보낸다.

ㅂ) Return Home 명령을 보낸다.

ㅅ) Clear Display 명령을 보낸다.

ㅇ) Entry Mode Set 명령을 보낸다.

ㅈ) DDRAM 어드레스 카운터를 설정한다.

ㅊ) Display ON/OFF Control 명령을 보낸다.

 

위의 과정 중 8비트 초기화에서는 ㄴ)~ㄹ) 단계는 생략해도 잘 되었습니다. ㄴ)~ㄹ) 과정은 일부 컨트롤러의 결함 때문에 필요하므로 해당 컨트롤러가 아니면 생략해도 문제가 없었습니다. 실제로 현재 시중에 유통되는 컨트롤러들은 이러한 결함을 다 제거한 것 같습니다.

 

기본적으로 문자형 LCD 커트롤러들은 전원이 투입되거나 RESET된 후에는 8비트 제어 상태로 됩니다. 그렇기 때문에 8비트로 제어할 때에는 ㄴ)~ㄹ)의 단계를 생략해도 별 문제가 없습니다. 그러나 4비트로 제어할 때에는 ㄴ)~ㄹ) 과정을 생략하면 정상적으로 동작하지 않습니다. 처음 전원을 투입하면 정상적으로 동작하다가 RESET 버튼을 누르면 화면이 이상하게 나오기도 합니다.

 

전원이 투입되거나 RESET된 후에는 8비트 제어 상태로 되기 때문에, 4비트로 제어하는 경우에는 초기화 과정에서 4비트로 제어되도록 Function Set 명령으로 지정해야 합니다. 4비트로 제어되도록 지정하는 Funtion Set 명령을 전송할 때에 LCD는 8비트 제어 상태이므로 4비트 제어로 설정하는 명령은 8비트로 전달해야 합니다.

 

그러므로 앞의 글 Atmega32로 LCM4004A 다루기(2편)-LCD구동 8비트 1/2에서 다루었던 8비트 명령 함수 LCD_WRITE_COMMAND를 다음과 같이 LCD_WRITE_COMMAND_8BIT로 이름을 바꾸어 사용합니다.

 


 

//////////////////////////////////////////////////
// LCD_WRITE_COMMAND_8BIT:
// PARAM AL:COMMAND
// RETURN NONE
// CHANGED NONE
//////////////////////////////////////////////////
LCD_WRITE_COMMAND_8BIT:
    CBI     LCD_CTRL_PORT,LCD_RS_BIT
    OUT    LCD_DATA_PORT,AL
    CBI     LCD_CTRL_PORT,LCD_RW_BIT
    SBI     LCD_CTRL_PORT,LCD_E1_BIT
    SBI     LCD_CTRL_PORT,LCD_E2_BIT
    RCALL DELAY_100US
    CBI     LCD_CTRL_PORT,LCD_E1_BIT       ; E = 0
    CBI     LCD_CTRL_PORT,LCD_E2_BIT
    RCALL DELAY_1MS
    RET

 


 

 

위의 LCD_WRITE_COMMAND 함수와 LCD_WRITE_COMMAND_8BIT 함수를 이용하여 다음과 같이 4비트 초기화 함수 LCD_INIT 함수를 만듭니다.

 

 


 

//////////////////////////////////////////////////
// LCD_INIT:
// PARAM NONE
// RETURN NONE
// CHANGED AL
//////////////////////////////////////////////////
LCD_INIT:
    LDI     AL,50                                                //ㄱ) 전원 안정화를 위해 50mS 대기
    RCALL DELAY_MS

    LDI     AL,LCD_DATA_DDR_VALUE                    // LCD_DATA_PORT를 출력으로 설정
    OUT   LCD_DATA_DDR,AL
    LDI     AL,LCD_CTRL_DDR_VALUE                     // LCD_CTRL_PORT의 RS, RW, E1, E2 설정
    OUT   LCD_CTRL_DDR,AL
    LDI     AL,0                                                 // LCD_DATA_PORT, LCD_CTRL_PORT 초기화
    OUT    LCD_CTRL_PORT,AL
    OUT    LCD_DATA_PORT,AL

    LDS     AL,LCD_CURRENT_EN                           // CURRENT_EN을 스택에 저장
    PUSH  AL
    LDI     AL,(1 << LCD_E1_BIT) | (1 << LCD_E2_BIT) // E1, E2 모두 초기화
    RCALL LCD_WRITE_CURRENT_EN
    LDI     AL,LCD_FUNCTIONSET | LCD_8BITMODE   // ㄴ) Function Set 명령
    RCALL LCD_WRITE_COMMAND_8BIT
    LDI     AL,5                                                    // 5mS 대기
    RCALL DELAY_MS
    LDI     AL,LCD_FUNCTIONSET | LCD_8BITMODE  // ㄷ) Function Set 명령
    RCALL LCD_WRITE_COMMAND_8BIT
    LDI     AL,5                                                    // 5mS 대기
    RCALL DELAY_MS

    LDI     AL,LCD_FUNCTIONSET | LCD_8BITMODE  // ㄹ) Function Set 명령
    RCALL LCD_WRITE_COMMAND_8BIT


    // ㅁ) 4BIT 제어를 위한 Function Set 명령
    LDI     AL,LCD_FUNCTIONSET | LCD_4BITMODE | LCD_2LINE | LCD_5x8DOTS
    RCALL LCD_WRITE_COMMAND_8BIT


    RCALL LCD_RETURN_HOME                                // ㅂ) Return Home 명령

    RCALL LCD_CLEAR_DISPLAY                                // ㅅ) Clear Display 명령
    LDI     AL,LCD_ENTRYMODESET | LCD_ENTRY_RIGHT | LCD_ENTRY_NOSHIFT
    RCALL LCD_WRITE_COMMAND                          // ㅇ) 커서 오른쪽 이동 Entry Mode Set
    LDI     AL,LCD_SETDDRAMADDR                         // ㅈ) DDRAM 어드레스 카운터  0
    RCALL LCD_WRITE_COMMAND
    LDI     AL,LCD_DISPLAYCONTROL | LCD_DISPLAYON  // ㅊ) Display ON/OFF Control 명령
    RCALL LCD_WRITE_COMMAND

    POP    AL                                                      // CURRENT_EN 값 복구
    RJMP LCD_WRITE_CURRENT_EN

 

--------------------------------------------------------------------------------

 

앞의 글 Atmega32로 LCM4004A 다루기(4편)-LCD구동 8비트 2/2에서 만들었던 유용한 LCD 제어 함수 LCD_GOTOXY, LCD_SET_CURSOR, LCD_SHIFT, LCD_PUTSTRING 함수들은 4비트 제어에서도 그대로 사용할 수 있습니다.

 

 

다음은 LCD를 4비트로 제어하는 소스 프로그램입니다. LCD를 제어하는 함수들은 LCM4004A_4Bit,asm에 들어 있습니다. 앞 글의 예제에서와 마찬가지로 LCM4004A_ExM32_4Bit.asm에서 .INCLUDE 문으로 소스를 포함시킨 후에 에서 필요한 함수들을 호출하여 사용합니다. 앞 글의 예제 프로그램과 "LCM4004A_8Bit.asm"을 "LCM4004A_4Bit.asm"으로 바꾼 것 외에는 모두 동일합니다.

 

 

LCM4004A_ExM32_4Bit.asm

 

LCM4004A_4Bit.asm

Delay_16mHz.asm

 

 

이상으로 문자형 LCD를 8비트로 제어하는 방법과 4비트로 제어하는 방법을 모두 마치겠습니다. 다음 글에서는 #ifdef 제어문을 사용하여 8비트로 제어하는 방법과 4비트를 제어하는 방법을 하나의 소스 프로그램에 담아 놓고 컴파일시에 8비트 혹은 4비트를 선택하여 컴파일하도록 하는 방법을 다루겠습니다.


블로그 이미지

엠쿠스

Microprocessor(STM32, AVR)로 무엇인가를 만들어 보고자 학습 중입니다.

,