Controller 없는 GLCD 구동 방법
정식으로 전자 공학을 배운 적도 없고, GLCD 제어 방법을 공식적으로 배운 적이 없습니다. 그냥 매뉴얼을 보고 어림짐작으로 알아낸 것들과 시행착오 끝에 터득한 사실들입니다. 혹시 부적절하거나 잘못된 내용이 있으면 가르쳐 주시기 바랍니다.
① 왜 controller가 없는 GLCD를 쓸까?
GLCD controller로는 삼성의 KS0066 계열, EPSON의 ESD1335 계열, HITACHI HD44780 계열, Sitronix ST7565 계열 등 다양한 제품군이 있습니다. 이들 controller들은 GLCD를 제어하는 기능을 가지고 있으며, 대부분의 경우 자체 메모리를 내장하고 있습니다.
MCU는 직접 GLCD를 제어하지 않고 자료만 GLCD controller에 전달하면, 나머지는 GLCD가 알아서 표시를 하는 셈입니다.
GLCD controller를 통해서 GLCD를 관리하면 CPU의 부하는 줄어듭니다. 그러나 이에 따른 문제도 발생합니다. CPU가 GLCD controller를 제어하는 명령이나 방식이 controller마다 다릅니다. 어떠한 이유로 GLCD가 고장나서 다른 것으로 교체하려면 같은 GLCD controller 또는 호환이 되는 GLCD controller를 사용한 GLCD로 바꾸어야 합니다.
GLCD controller가 가지고 있는 메모리를 CPU 등의 본체에 내장시키고, CPU가 직접 GLCD를 제어하면, GLCD의 신호선만 맞으면 어떤 GLCD든지 사용할 수 있습니다. 이점이 시중의 많은 제품들이 controller가 내장되지 않은 형태의 GLCD들을 사용하는 이유입니다.
② Conroller 없는 GLCD 제어
Controller가 없는 GLCD는 직접 common driver와 segment driver에 신호를 전송하여 제어합니다. 쉽게 표현하자면 common driver는 행을 관리하는 역할을 하고, segment driver는 열을 관리하는 역할을 하는 것으로 이해하고 있습니다. Common driver와 segment driver가 합작하여 동적으로 점을 표현하는 것 같습니다.
동적 구동의 예를 들자면 common dirver가 0번째 행을 high로 유지하고 있을 때에 segment driver가 1, 2, 3 열은 low로 4, 5, 6 열은 high로 유지하여, 0행의 1, 2, 3 번째 점은 찍히고 4, 5, 6 번째 점은 찍히지 않게 하는 방법으로 동작시키는 것입니다.
Common driver 전용인 칩들도 있고, segment driver 전용인 칩들도 있으며, 두 기능을 다가지고 있어서 필요에 따라 common driver로 사용할 수도 있고 segment driver로 사용할 수도 있는 칩들도 있습니다.
Common driver와 segment driver를 제어에 사용하는 일반적인 신호에 대하여 간단히 살펴보면 다음과 같습니다.
1) FLM(First Line Mark) : CPU쪽에서 화면의 첫 행 데이터를 보낸다는 신호입니다. 이 신호는 보통 첫 번째 common driver나 segment driver의 D0 혹은 D3에 연결되어 있습니다.
2) CL1(LP: Latch Pulse) : latch는 데이터를 보관시킨다는 뜻입니다. common driver와 segment driver는 이 latch 신호가 발생하는 순간의 데이터를 보관합니다.
3) CL2 : 이 신호는 segment driver와만 연결되어 있습니다. 이 신호가 연결되어 있는 칩은 segment driver이고, 연결되지 않은 칩은 common driver입니다.
4) VDD : driver 칩들의 전원 보통 3.3V 또는 5V. 데이터시트를 보고 확인합니다.
5) Vss : Ground.(0V)
6) Vee : LCD 패널 표시에 필요한 전압입니다. 이 전압은 각 드라이버의 데이터시트를 보고 파악해야 합니다. 보통은 -20V 이내의 음전원을 필요로 합니다만, + 전원을 요구하는 경우도 있습니다.
7) 데이터 신호 : 일반적으로 4개의 신호를 병렬로 받습니다. (D0 - D3)
8) Display Off : 화면 출력을 정지시키는 신호
9) Vo : LCD의 contrast 등 밝기를 정하기 위한 전압
10) M : FLM 신호를 전송할 때마다 low와 high를 바꾸어서 전송하는 신호
위 신호들 중에는 GLCD에 따라 사용하지 않는 신호들도 있습니다.
위 신호들 중에는 common driver, segment driver와 연결된 상태를 파악해서 어렵지 않게 알아낼 수 있는 신호들이 있습니다.
첫째, Vdd ,Vss, Vee 등은 각 driver 칩들의 데이터시트를 보고 알아낼 수 있습니다. driver 칩의 Vdd, Vss, Vee와 연결된 신호를 찾으면 됩니다.
둘째, D0~D3도 segment driver의 데이터시트를 보고 알아낼 수 있습니다. Segment driver의 D0 ~ D3(경우에 따라 D1 ~ D4) 신호와 연결된 신호를 찾으면 됩니다.
>세째, displayoff 신호도 각 driver 칩의 데이터시트를 찾아 보고 이와 연결된 신호선을 찾으면 됩니다.
네째, FLM 선은 어느 칩 하나에만 연결되어 있습니다. 첫 common driver나 첫 segment driver의 D0 또는 D3 하나에만 연결된 신호를 찾으면 됩니다.
다섯째, CL1은 segment driver와 common driver 모두에 연결되어 있습니다. 각 드라이버의 데이터시트에서 CL 비슷한 신호핀을 찾아 내어, 이 신호가 모든 칩의 CL 신호와 연결되어 있으면 CL1입니다. 반면에 CL2는 segment driver와만 연결되어 있습니다.
이상과 같은 방법으로 common driver와 segment driver의 데이터시트를 분석하면 대부분의 신호들은 어렵지 않게 찾을 수 있습니다.
그러나 common driver와 segment driver가 노출되지 않는 COG 형식의 GLCD는 신호 찾기가 좀 어렵습니다.
Controller가 없는 GLCD를 제어할 때에 심각히 고려해야할 사항이 하나 있습니다. Controller가 없기 때문에 GLCD 자체에는 화면에 표시할 내용을 기록할 메모리가 없습니다. 이 메모리는 CPU쪽에서 만들어 관리해야 합니다. 320 * 240의 단색 GLCD는 총 9,600(320 * 240 / 8) 바이트의 메모리가 필요합니다. 9,600바이트는 9K 바이트가 좀 넘습니다. atmega128의 내부 SRAM은 4K 바이트에 불과합니다. 따라서 320 * 240의 GLCD를 제어하려면 atmega128에 외장 메모리를 부착해야 합니다.
본 글에서는 꽤 오래 전에 구입해서 가지고 있던 atmega128 board V3.1로 320*240 규격의 GLCD를 제어할 예정입니다. 이 보드에는 0x1000 번지부터 0x7FFF 번지의 외장 메모리 28K 바이트를 사용할 수 있습니다.
(atmega128 V3.3 사진 출처: 디바이스마트)
③ GLCD 제어
320*240 GLCD를 제어하는 방법을 설명합니다. 다른 해상도의 경우도 행과 열의 수 차이에 따라 약간의 숫자만 달라질 뿐 원리는 동일합니다. 따라서 숫자를 매크로로 정의해서 프로그램하면 다른 GLCD에 적용하기가 아주 쉽습니다.
제어 순서는 다음과 같습니다.
1) 첫 행 신호(FLM)을 보낸다.
2) 0행에 해당하는 신호들을 보낸다.
320*240 GLCD이 경우 한 행은 40바이트(320 / 4)를 보냅니다. 실제로 GLCD로 데이터를 전송할 때에는 D0 ~ D3까지 4비트이므로 1바이트를 반으로 쪼개서 두 번 보냅니다. 4비트를 보내는 동기 신호로 CL2를 사용합니다. 즉 4비트를 설정해 놓고 CL2를 low -> high -> low로 해서 펄스를 하나 보냅니다. 결국 한 행을 다 보내기 위해서는 CL2에 펄스를 80회 보내야 합니다.
3) 한 행을 다 보냈으므로 CL1(Latch Pulse)를 보냅니다.
4) 320*240 GLCD는 240행이므로 2), 3)의 과정을 240회 반복합니다.
5) M 신호가 있으면 반전시키면서 1)행부터 반복합니다.
GLCD의 매뉴얼들을 보면 대체로 70Hz로 표시할 것을 권장하는 것 같습니다. 한 화면에 240행이 있으므로 1초에 16,800(240 * 70)행을 표시하면 됩니다. AVR을 16MHz로 동작시킨다면 약 952 클럭마다 한 번씩 인터럽트를 걸어서 한 행을 표시하면 됩니다. 이런 용도로는 timer interrupt가 딱 제격입니다.
952는 8의 배수이므로 8비트 타이머 Timer0의 클럭을 CPU의 1/8로 동작하게 한 후에 TCNT0의 값이 119가 되었을 때에 인터럽트가 걸리도록 하면 거의 정확한 70Hz 동작을 얻을 수 있습니다. Timer0에서 이런 용도로 쓰기에 가장 적합한 방법은 CTC(Clear Timer on Compare match) 모드입니다. 이 모드에서는 TCNT0 레지스터의 값이 OCR0 레지스터의 값과 같아지면 Timer0 Compare Match interrupt가 발생합니다. 이 인터럽트의 서비스 루틴에서 한 행을 출력하면 됩니다.
이 때에 주의할 점은 이 인터럽트는 952 클럭마다 한번씩 발생하므로 인터럽트 서비스 루틴이 그 이전에 끝나야 합니다. 위에서 controller가 없는 GLCD를 CPU가 직접 제어할 때에는 CPU의 부담이 많다고한 이유입니다. 이런 이유로 C와 같은 고급 언어보다 어셈블리로 작성하는 것이 더 안정적이라고 판단합니다.
다음은 atmega128 등에서 Timer0의 Compare Match interrupt가 952클럭마다 발생하도록 설정하는 루틴입니다.
.EQU F_CPU = 16000000 .EQU FREQ= 70 .EQU GLCD_SIZE_X = 320 .EQU GLCD_SIZE_Y = 240 .EQU T_DEVIDER = 8 .EQU OCR0_VALUE= (F_CPU / FREQ / GLCD_SIZE_Y / T_DEVIDER) .EQU TCCR0_VALUE = ((1 << WGM01) | (1 << CS01)) // CTC mode, 1/8 clk .DEF AL= R24 LDI AL,OCR0_VALUE OUT OCR0,AL LDI AL,TCCR0_VALUE OUT TCCR0,AL LDI AL,0 OUT TCNT0,AL LDI AL,(1 << OCIE0) OUT TIMSK,AL SEI
이와 같이 하지 않고 16비트 타이머인 Timer1이나 Timer3, 또는 다른 8비트 타이머인 TImer2 등을 사용해도 됩니다.
다음은 위의 타이머 인터럽트가 발생했을 때에 한 행을 화면에 출력시키는 인터럽트 서비스 루틴의 예입니다.
.EQU EXTENDED_RAMEND = 0x8000 .EQU VRAM = EXTERN_RAMEND - (GLCD_SIZE_X * GLCD_SIZE_Y / 8) .EQU FONTSIZE_X= 8 // 8 dot * 16 dot font .EQU FONTSIZE_Y= 16 .EQU GLCD_COLUMNS= (GLCD_SIZE_X / FONTSIZE_X) // characters per line .EQU GLCD_ROWS = (GLCD_SIZE_Y / FONTSIZE_Y) // total lines .DEF CUR_INT_ROW = R16 // current row .CSEG __TIMER0_COMP: PUSH AL IN AL,SREG PUSH AL PUSH AH PUSH ZL PUSH ZH PUSH R0 PUSH R1 LDI ZL,LOW(pVRAM) LDI ZH,HIGH(pVRAM) TST CUR_INT_ROW // if current row == 0 FLM -> high BRNE __TIMER1_COMPA_NOT_1STLINE_1 SBI GLCD_CTRL_PORT,GLCD_FLM __TIMER1_COMPA_NOT_1STLINE_1: LDI AL,GLCD_COLUMNS // calculate pVram address MUL CUR_INT_ROW,AL ADD ZL,R0 ADC ZH,R1 LDI AH,GLCD_COLUMNS // transfer data 80 bytes __TIMER1_COMPA_LOOP: LD AL,Z+ // upper 4bits OUT GLCD_DATA_PORT,AL SBI GLCD_CTRL_PORT,GLCD_CL2 CBI GLCD_CTRL_PORT,GLCD_CL2 SWAP AL OUT GLCD_DATA_PORT,AL // lower 4bits SBI GLCD_CTRL_PORT,GLCD_CL2 CBI GLCD_CTRL_PORT,GLCD_CL2 DEC AH BRNE __TIMER1_COMPA_LOOP SBI GLCD_CTRL_PORT,GLCD_CL1 CBI GLCD_CTRL_PORT,GLCD_CL1 TST CUR_INT_ROW // if current row == 0 FLM-> low BRNE __TIMER1_COMPA_NOT_1STLINE_2 CBI GLCD_CTRL_PORT,GLCD_FLM __TIMER1_COMPA_NOT_1STLINE_2: INC CUR_INT_ROW // prepare for next interrupt CPI CUR_INT_ROW,GLCD_SIZE_Y BRLO __TIMER1_COMPA_END CLR CUR_INT_ROW __TIMER1_COMPA_END: POP R1 POP R0 POP ZH POP ZL POP AH POP AL OUT SREG,AL POP AL RETI
위의 인터럽트 서비스 루틴을 실행시켜보니까 약 700 클럭 정도가 걸립니다. 952 클럭 내에 여유있게 들기 때문에 16MHz클럭을 사용하는 시스템에서는 이대로 사용해도 됩니다.
앞에서 언급했든이 controller가 없는 GLCD에는 메모리가 없기 때문에 CPU가 전달해 준 데이터를 저장할 공간이 없습니다. 이에 따라 외장 메모리의 일정 부분을 video ram으로 확보해 놓고, 화면에 표시할 내용을 이곳에 저장해 놓습니다. 이렇게 video ram에 저장된 데이터를 위의 인터럽트 서비스 루틴에서 GLCD로 전송합니다. CPU 쪽에서 video ram으로 확보한 메모리의 시작 주소를 매크로 pVRAM으로 정의하였습니다.
pVRAM은 외장 메모리의 마지막 번지에서 9,600을 뺀 값을 가집니다. GLCD의 i번째 행의 첫 바이트 주소는 pVRAM + i * (320 / 8) 번지입니다.
320*240 GLCD를 제어하기 위한 준비는 거의 마쳤습니다. 위 루틴에서 몇 개의 매크로만 수정하면 다른 해상도의 controller 없는 GLCD를 다루는 루틴으로 쉽게 바꿀 수 있습니다.
'AVR > GLCD' 카테고리의 다른 글
컨트롤러 없는 GLCD 제어하기 - LTBE9H372K8K(2편) (1) | 2018.11.09 |
---|---|
컨트롤러 없는 GLCD 제어하기 - LTBE9H372K8K(1편) (0) | 2018.11.09 |
AT-320240Q1 LCD 동작시키기(3) (0) | 2018.11.09 |
AT-320240Q1 LCD 동작시키기(2) (0) | 2018.11.09 |
AT-320240Q1 LCD 동작시키기(1) (0) | 2018.11.09 |