Harman 세미콘 아카데미/Atmega128a

[ATmega 128a]⑥ - LCD 8bit, LCD 8bit counter, LCD 4bit

U_Pong 2024. 6. 18. 19:30

2024.6.10 수업날

이번에는 1602A라는 LCD 부품을 가지고 어떻게 작동하는 지에 대해 알아본다.


< LCD 8bit >

아래의 파일은 LCD 1602A의 메뉴얼이다.

CFAH1602A-AGB-JP.PDF
0.25MB

 

LCD에는 총 16개의 핀이 있다.

아래의 표는 핀 번호에 따른 기능과 의미를 해석한 것이다.

 

실질적으로 LCD에 불이 들어오게 하려면, LED+, LED-인 15, 16번 핀에 Vcc와 Gnd에 연결해야한다.

 

전선 정리를 제대로 하지 못했지만, 핀 번호에 따른 기능과 의미를 나타낸 표를 보고 참고하여 연결하였다.

PF의 0~7번 핀은 LCD의 D0~D7과 연결하였다.

PE의 5~7번 핀은LCD의 4~6번 핀과 연결하였다.

 

아래의 그림은 LCD의 타이밍 특성 회로도와 심볼, 최소시간, 최대시간 표를 나타낸 것이다.

아래의 표는 8bit 일때, 4bit 일때 어떻게 LCD를 작동시킬 것인지에 대한 정리표이다.

각 코드에 해당하는 설명을 보고 원하는 작동법을 소스코드에 입력하면 LCD에 표현할 수 있게된다.

 
아래의 그림은 LCD 8bit를 표현할때 어떻게 소스코드를 작성해야하는지에 대한 설명을 나타낸 것이다.

 

LCD 8bit 문자 출력 소스코드
/*
 * lcd8bit.h
 */ 


#ifndef LCD8BIT_H_
#define LCD8BIT_H_

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>

#define LCD_DATA_DDR		DDRF
#define LCD_DATA_PORT		PORTF
#define LCD_DATA_PIN		PINF
#define LCD_RS_DDR			DDRE
#define LCD_RW_DDR			DDRE
#define LCD_E_DDR			DDRE
#define LCD_RS_PORT			PORTE
#define LCD_RW_PORT			PORTE
#define LCD_E_PORT			PORTE
#define LCD_RS				7	//6
#define LCD_RW				6	//7
#define LCD_E				5	//5

#define COMMAND_DISPLAY_CLEAR		0x01	// Clear all display
#define COMMAND_DISPLAY_ON			0x0C	// 화면 ON, 커서 OFF, 커서 점멸 OFF
#define COMMAND_DISPLAY_OFF			0x08	// 화면 OFF, 커서 OFF, 커서 점멸 OFF
#define COMMAND_ENTRY_MODE			0x06	// 커서 우측 이동, 화면이동 없음
#define COMMAND_8BIT_MODE			0x38	// 8비트, 화면 2행, 5X8 Font
#define COMMAND_4BIT_MODE			0x28	// 4비트, 화면 2행, 5X8 Font


void LCD_Data(uint8_t data);
void LCD_WritePin();
void LCD_EnablePin();
void LCD_WriteCommand(uint8_t commandData);
void LCD_WriteData(uint8_t charData);
void LCD_GotoXY(uint8_t row, uint8_t col);
void LCD_WriteString(char *string);
void LCD_WriteStringXY(uint8_t row, uint8_t col, char *string);
void LCD_Init();


#endif /* LCD8BIT_H_ */

 

/*
 * lcd8bit.c
 */ 

// #include <avr/io.h>

#include "lcd8bit.h"


void LCD_Data(uint8_t data)
{
	LCD_DATA_PORT = data;	// 데이터 핀에 8비트 출력
	
}


void LCD_WritePin()
{
	LCD_RW_PORT &= ~(1<<LCD_RW);	// RW핀을 LOW로 설정하여 쓰기모드 진입
	
}


void LCD_EnablePin()
{
	LCD_E_PORT &= ~(1<<LCD_E);	// E핀을 LOW로 설정
	LCD_E_PORT |= (1<<LCD_E);	// E핀을 HIGH로 설정해서 동작 신호를 전송
	LCD_E_PORT &= ~(1<<LCD_E);	// E핀을 LOW로 설정
	_delay_us(2000);			// 1600 이상
}


void LCD_WriteCommand(uint8_t commandData)
{
	LCD_RS_PORT &= ~(1<<LCD_RS);	//RS핀을 LOW로 설정해서 명령어 모드로 진입
	LCD_WritePin();					// 데이터 쓰기모드 진입
	LCD_Data(commandData);			// 명령어를 데이터핀에 출력
	LCD_EnablePin();				// LCD 동작 신호 전송
}


void LCD_WriteData(uint8_t charData)
{
	LCD_RS_PORT |= (1<<LCD_RS);		// RS핀을 HIGH로 설정해서 문자 모드로 진입
	LCD_WritePin();					// 데이터 쓰기모드 진입
	LCD_Data(charData);				// 명령어를 데이터핀에 출력
	LCD_EnablePin();				// LCD 동작 신호 전송
}


void LCD_GotoXY(uint8_t row, uint8_t col) //row:가로, col:세로
{
	col %= 16;	// col을 16으로 제한
	row %= 2;	// row를 2로 제한
	uint8_t address = (0x40 * row) + col;		// 주소 계산
	uint8_t command = 0x80 + address;			// 커맨드 값 계산(주소설정)
	LCD_WriteCommand(command);					// 주소 커맨드를 전송
}


void LCD_WriteString(char *string)
{
	for (uint8_t i=0; string[i]; i++)
	{
		LCD_WriteData(string[i]);	// 문자열을 출력
	}
}


void LCD_WriteStringXY(uint8_t row, uint8_t col, char *string)
{
	LCD_GotoXY(row, col);
	LCD_WriteString(string);
}


void LCD_Init()
{
	LCD_DATA_DDR = 0xff;
	LCD_RS_DDR |= (1<<LCD_RS);
	LCD_RW_DDR |= (1<<LCD_RW);
	LCD_E_DDR |= (1<<LCD_E);
	
	_delay_ms(20);		// 초기화 시간 대기 (충분한 시간)
	LCD_WriteCommand(COMMAND_8BIT_MODE);		// 8bit 모드 설정
	_delay_ms(5);
	LCD_WriteCommand(COMMAND_8BIT_MODE);		// 8bit 모드 설정
	_delay_ms(2);
	LCD_WriteCommand(COMMAND_8BIT_MODE);		// 8bit 모드 설정
	LCD_WriteCommand(COMMAND_8BIT_MODE);		// 8bit 모드 설정
	LCD_WriteCommand(COMMAND_DISPLAY_OFF);
	LCD_WriteCommand(COMMAND_DISPLAY_CLEAR);
	LCD_WriteCommand(COMMAND_DISPLAY_ON);
	LCD_WriteCommand(COMMAND_ENTRY_MODE);

	// _delay_ms
}

 

/*
 * CLCD_8bit.c
 *
 * Created: 2024-06-16 오후 9:41:32
 * Author : jsmok
 */ 

//#define F_CPU 16000000UL
//#include <avr/io.h>
#include "lcd8bit.h"


int main(void)
{
	LCD_Init();
	
	LCD_GotoXY(0,0);
	LCD_WriteString("Hello LCD");
	LCD_GotoXY(1,0);
	LCD_WriteString("GOOD AVR");
	
	while (1)
	{
	}
}

 

LCD 8bit 결과

 

이때, 영상에서 보이는 것처럼 '가변저항'을 사용하여 LCD의 밝기를 조정할 수 있다.

가변저항에는 총 3개의 핀이 있는데,

가운데의 핀에는 LCD의 3번 핀에 연결하고, 양 끝쪽의 핀에는 Vcc와 Gnd에 연결한다.

(양 끝쪽의 핀에는 정해진 극성이 없으므로 어느쪽에 Vcc, Gnd를 연결할지는 상관없다)


이번에는 LCD에 카운터가 출력되는 소스코드를 작성해보자.

h파일, c파일의 내용은 동일하고 main.c의 내용만 위의 소스파일대로 수정하면 된다.

LCD 8bit 카운터 출력 소스코드
/*
 * CLCD_8bit.c
 */ 

//#define F_CPU 16000000UL
//#include <avr/io.h>
#include "lcd8bit.h"


int main(void)
{
	char buff[30];
	LCD_Init();
	sprintf(buff, "Hello AVR");
	LCD_WriteStringXY(0,0,buff);
	int count = 0;
	
	while (1)
	{
		sprintf(buff, "count: %d", count++);	// 200ms 마다 카운트가 점점 올라감, 일정 수에 도달하면 오버클록이 되서 초기화
		LCD_WriteStringXY(1,0,buff);
		_delay_ms(200);
	}
}

 

LCD 8bit 카운터 출력 결과

 

 

< LCD 4bit >

아래의 그림은 LCD 4bit를 표현할때 어떻게 소스코드를 작성해야하는지에 대한 설명을 나타낸 것이다.

 

아래의 그림은 LCD 4bit를 표현할때, 교수님께서 설명해주신 코드 분석이다.

아래의 소스코드에도 주석을 써놓았지만, 이해하기 쉽도록 설명해주신 그림을 첨부한다.

 

LCD 4bit 문자 출력 소스코드
/*
 * lcd4bit.h
 */ 


#ifndef LCD4bit_H_	//파일 이름명 바꾸면 여기도 바꿔야함
#define LCD4bit_H_	//파일 이름명 바꾸면 여기도 바꿔야함

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>

#define LCD_DATA_DDR		DDRF
#define LCD_DATA_PORT		PORTF
#define LCD_DATA_PIN		PINF
#define LCD_RS_DDR			DDRE
#define LCD_RW_DDR			DDRE
#define LCD_E_DDR			DDRE
#define LCD_RS_PORT			PORTE
#define LCD_RW_PORT			PORTE
#define LCD_E_PORT			PORTE
#define LCD_RS				7	//6
#define LCD_RW				6	//7
#define LCD_E				5	//5

#define COMMAND_DISPLAY_CLEAR		0x01	// Clear all display
#define COMMAND_DISPLAY_ON			0x0C	// 화면 ON, 커서 OFF, 커서 점멸 OFF
#define COMMAND_DISPLAY_OFF			0x08	// 화면 OFF, 커서 OFF, 커서 점멸 OFF
#define COMMAND_ENTRY_MODE			0x06	// 커서 우측 이동, 화면이동 없음
#define COMMAND_8BIT_MODE			0x38	// 8비트, 화면 2행, 5X8 Font

#define COMMAND_4BIT_MODE			0x28	// 4비트, 화면 2행, 5X8 Font-->8비트꺼 복사해서 4비트로 추가함


void LCD_Data(uint8_t data);	// 8bit

void LCD_Data4Bit(uint8_t data);	// 4bit

void LCD_WritePin();
void LCD_EnablePin();
void LCD_WriteCommand(uint8_t commandData);
void LCD_WriteData(uint8_t charData);
void LCD_GotoXY(uint8_t row, uint8_t col);
void LCD_WriteString(char *string);
void LCD_WriteStringXY(uint8_t row, uint8_t col, char *string);
void LCD_Init();


#endif /* LCD4BIT_H_ */ //파일 이름명 바꾸면 여기도 바꿔야함

 

/*
 * lcd4bit.c
 */ 

#include "lcd4bit.h"	//파일 이름명 바꾸면 여기도 바꿔야함


void LCD_Data(uint8_t data)
{
	LCD_DATA_PORT = data;	// 데이터 핀에 8비트 출력
	
}


void LCD_Data4Bit(uint8_t data)
{
	LCD_DATA_PORT = (LCD_DATA_PORT & 0x0f) | (data & 0xf0); // 상위4비트 추출해서 1차 보내고
	LCD_EnablePin();
	LCD_DATA_PORT = (LCD_DATA_PORT & 0x0f) | ((data & 0x0f)<<4); // 하위4비트로 추출해서 4비트 쉬프트 하고 2차 보냄, 중요!!
	LCD_EnablePin();
	// 0x03; 0000 -> 0011
}


void LCD_WritePin()
{
	LCD_RW_PORT &= ~(1<<LCD_RW);	// RW핀을 LOW로 설정하여 쓰기모드 진입
	
}


void LCD_EnablePin()
{
	LCD_E_PORT &= ~(1<<LCD_E);	// E핀을 LOW로 설정
	LCD_E_PORT |= (1<<LCD_E);	// E핀을 HIGH로 설정해서 동작 신호를 전송
	LCD_E_PORT &= ~(1<<LCD_E);	// E핀을 LOW로 설정
	_delay_us(1600);			// 1600 이상
}


void LCD_WriteCommand(uint8_t commandData)
{
	LCD_RS_PORT &= ~(1<<LCD_RS);	//RS핀을 LOW로 설정해서 명령어 모드로 진입
	LCD_WritePin();					// 데이터 쓰기모드 진입
	LCD_Data4Bit(commandData);			// 명령어를 데이터핀에 출력
	//LCD_EnablePin(); --> 하위 4비트에서 해주므로~				// LCD 동작 신호 전송
}


void LCD_WriteData(uint8_t charData)
{
	LCD_RS_PORT |= (1<<LCD_RS);		// RS핀을 HIGH로 설정해서 문자 모드로 진입
	LCD_WritePin();					// 데이터 쓰기모드 진입
	LCD_Data4Bit(charData);				// 명령어를 데이터핀에 출력
	//LCD_EnablePin(); --> 하위 4비트에서 해주므로~			// LCD 동작 신호 전송
}


void LCD_GotoXY(uint8_t row, uint8_t col) //row:가로, col:세로
{
	col %= 16;	// col을 16으로 제한
	row %= 2;	// row를 2로 제한
	uint8_t address = (0x40 * row) + col;		// 주소 계산
	uint8_t command = 0x80 + address;			// 커맨드 값 계산(주소설정)
	LCD_WriteCommand(command);					// 주소 커맨드를 전송
}


void LCD_WriteString(char *string)
{
	for (uint8_t i=0; string[i]; i++)
	{
		LCD_WriteData(string[i]);	// 문자열을 출력
	}
}


void LCD_WriteStringXY(uint8_t row, uint8_t col, char *string)
{
	LCD_GotoXY(row, col);
	LCD_WriteString(string);
}


void LCD_Init()
{
	LCD_DATA_DDR = 0xff;
	LCD_RS_DDR |= (1<<LCD_RS);
	LCD_RW_DDR |= (1<<LCD_RW);
	LCD_E_DDR |= (1<<LCD_E);
	
	_delay_ms(20);		// 초기화 시간 대기 (충분한 시간)
	LCD_WriteCommand(0x03);		// 4bit 모드 설정
	_delay_ms(5);
	LCD_WriteCommand(0x03);		// 4bit 모드 설정
	_delay_ms(2);
	LCD_WriteCommand(0x03);		// 4bit 모드 설정
	LCD_WriteCommand(0x02);		// 4bit 모드 설정
	
	LCD_WriteCommand(COMMAND_4BIT_MODE); // 4비트에서 추가됨
	
	LCD_WriteCommand(COMMAND_DISPLAY_OFF);
	LCD_WriteCommand(COMMAND_DISPLAY_CLEAR);
	LCD_WriteCommand(COMMAND_DISPLAY_ON);
	LCD_WriteCommand(COMMAND_ENTRY_MODE);

}

 

/*
 * CLCD_4bit.c
 */ 

//#define F_CPU 16000000UL
//#include <avr/io.h>
#include "lcd4bit.h"	//파일 이름명 바꾸면 여기도 바꿔야함


int main(void)
{
	LCD_Init();
	LCD_GotoXY(0,0);
	LCD_WriteString("Hello 4BIT");
	LCD_GotoXY(1,0);
	LCD_WriteString("4BIT Good");
	
	while (1)
	{
	}
}

 

LCD 4bit 결과

LCD 8bit, LCD 8bit counter, LCD 4bit 끝!