[LCD]
PIN Description

Instruction Table
#define 으로 정의해놓고 사용하면 편하다.
0x01 | Clear All Display | 0x02 | Cursor Position -> Return home |
Entry_Mode_Set_Options | Cursor_Display_Shift_Options | ||
0x04 | 커서 좌측 이동, 화면 이동 없음 | 0x10 | 커서 선택, 커서 좌측 이동 |
0x05 | 커서 좌측 이동, 화면 이동 | 0x14 | 커서 선택, 커서 우측 이동 |
0x06 | 커서 우측 이동, 화면 이동 없음 | 0x18 | 화면 선택, 화면 좌측 이동 |
0x07 | 커서 우측 이동, 화면 이동 | 0x1C | 화면 선택, 화면 우측 이동 |
Display_Option | Function_ Set_Options | ||
0x08 | 화면 OFF, 커서 OFF, 커서 점멸 OFF | 0x20 | 4bit, 화면 1행, 5x8 Font |
0x09 | 화면 OFF, 커서 OFF, 커서 점멸 ON | 0x24 | 4bit, 화면 1행, 5x11 Font |
0x0A | 화면 OFF, 커서 ON, 커서 점멸 OFF | 0x28 | 4bit, 화면 2행, 5x8 Font |
0x0B | 화면 OFF, 커서 ON, 커서 점멸 ON | 0x2C | 4bit, 화면 2행, 5x11 Font |
0x0C | 화면 ON, 커서 OFF, 커서 점멸 OFF | 0x30 | 8bit, 화면 1행, 5x8 Font |
0x0D | 화면 ON, 커서 OFF, 커서 점멸 ON | 0x34 | 8bit, 화면 1행, 5x11 Font |
0x0E | 화면 ON, 커서 ON, 커서 점멸 OFF | 0x38 | 8bit, 화면 2행, 5x8 Font |
0x0F | 화면 ON, 커서 ON, 커서 점멸 ON | 0x3C | 8bit, 화면 2행, 5x11 Font |
초기 작업


Read / Write mode
: 공통적으로 En이 0→1 이 된 후에 데이터 전송이 일어난다.


LCD-1602A Data Sheet
[LCD_8bit]
LCD 출력 기본
<LCD.h>
#ifndef LCD_H_ #define LCD_H_ #define F_CPU 16000000UL #include <avr/io.h> #include <util/delay.h> #include <stdio.h> #define LCD_DATA_DDR DDRC #define LCD_DATA_PORT PORTC // 데이터 포트 #define LCD_DATA_PIN PINC #define LCD_RS_DDR DDRB // 제어 핀 연결 #define LCD_RW_DDR DDRB #define LCD_E_DDR DDRB #define LCD_RS_PORT PORTB #define LCD_RW_PORT PORTB #define LCD_E_PORT PORTB #define LCD_RS 5 #define LCD_RW 6 #define LCD_E 7 // COMAND #define COMMAND_DISPLAY_CLEAR 0x01 #define COMMAND_DISPLAY_ON 0x0C // 화면 ON, 커서 OFF, 커서 점멸 OFF #define COMMAND_DISPLAY_OFF 0x08 // 화면 OFF, 커서 OFF, 커서 점멸 OFF #define COMMAND_ENTRY_MODE 0x06 // 커서 우측 이동, 화면 이동 없음 #define COMMAND_8_BIT_MODE 0x38 // 8비트, 화면 2행, 5x8 Font #define COMMAND_4_BIT_MODE 0x28 // 4비트, 화면 1행, 5x11 Font // 함수 원형 선언 void LCD_Data(uint8_t data); void LCD_Data4bit(uint8_t data); void LCD_EnablePin(); void LCD_WritePin(); void LCD_ReadPin(); 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 /* LCD_H_ */
<LCD.c>
#include "LCD.h" void LCD_Data(uint8_t data) { LCD_DATA_PORT = data; // 데이터 핀에 데이터 출력 } void LCD_Data4bit(uint8_t data) { LCD_DATA_PORT = (LCD_DATA_PORT & 0x0f) | (data & 0xf0); // 상위 4bit 출력 // 이전 상위 비트 다 날라감 | data의 상위비트 살림 LCD_EnablePin(); LCD_DATA_PORT = (LCD_DATA_PORT & 0x0f) | ((data & 0x0f)<<4); // 하위 4bit 출력 LCD_EnablePin(); } void LCD_EnablePin() { LCD_E_PORT &= ~(1<<LCD_E); // E 핀을 Low 설정 LCD_E_PORT |= (1<<LCD_E); // E 핀을 High 설정 // Low -> High 되어야 Data들이 전송된다. LCD_E_PORT &= ~(1<<LCD_E); // 다시 Low 설정 _delay_us(1800); // 이 시간 조절해서, 문자 간 출력 딜레이 설정 가능 } void LCD_WritePin() { LCD_RW_PORT &= ~(1<<LCD_RW); // RW 핀을 Low로 설정하여 쓰기 모드 } void LCD_ReadPin() { LCD_RW_PORT |= (1<<LCD_RW); // RW 핀을 high로 설정하여 읽기 모드 } 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(); } void LCD_gotoXY(uint8_t row, uint8_t col) { col %= 16; // 열 인덱스를 0부터 15로 제한 row %= 2; // 행 인덱스를 0부터 1로 제한 uint8_t address = (0x40 * row) + col; // 주소 계산 uint8_t command = 0x80 + address; // command 값 계산 LCD_WriteCommand(command); // 주소 설정 command를 LCD에 전달 } void LCD_WriteString(char *string) { for (uint8_t i = 0 ; string[i]; i++) { LCD_WriteData(string[i]); // 문자열의 각 문자를 LCD에 출력 } } 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); // 초기화하고 15ms 이상 딜레이 LCD_WriteCommand(COMMAND_8_BIT_MODE); _delay_ms(5); // 4.3ms 이상 딜레이 LCD_WriteCommand(COMMAND_8_BIT_MODE); _delay_ms(5); LCD_WriteCommand(COMMAND_8_BIT_MODE); LCD_WriteCommand(COMMAND_8_BIT_MODE); // 플로우 차트 따라간 것일 뿐 LCD_WriteCommand(COMMAND_DISPLAY_OFF); LCD_WriteCommand(COMMAND_DISPLAY_CLEAR); LCD_WriteCommand(COMMAND_DISPLAY_ON); LCD_WriteCommand(COMMAND_ENTRY_MODE); // 입력 모드 설정 }
<main.c>
#include "LCD.h" int main(void) { LCD_Init(); LCD_gotoXY(0,0); // 첫째 줄에 출력해라. LCD_WriteString("Hello LCD"); LCD_gotoXY(1,0); // 둘째 줄에 출력해라. LCD_WriteString("Good AVR"); while (1) { } }

하나씩 출력 + 반복
// c파일에서 조정 // void LCD_EnablePin() { LCD_E_PORT &= ~(1<<LCD_E); LCD_E_PORT |= (1<<LCD_E); LCD_E_PORT &= ~(1<<LCD_E); _delay_ms(100); // 이 시간 조절해서, 문자 간 출력 딜레이 설정 가능 } // main.c 에서 조정 // int main(void) { LCD_Init(); while (1) { LCD_gotoXY(0,0); LCD_WriteString("Hello LCD!"); LCD_gotoXY(1,0); LCD_WriteString("Good AVR!"); _delay_ms(500); LCD_WriteCommand(COMMAND_DISPLAY_OFF); LCD_WriteCommand(COMMAND_DISPLAY_CLEAR); LCD_WriteCommand(COMMAND_DISPLAY_ON); LCD_WriteCommand(COMMAND_ENTRY_MODE); } }
입력받은 값 출력
int main() { char buff[30]; LCD_Init(); sprintf(buff, "Hello AVR"); // stdio.h 헤더파일 추가해줄 것 LCD_WritestringXY(0, 0, buff); int count = 0; while (1) { sprintf(buff, "count : %d", count++); LCD_WritestringXY(1, 0, buff); _delay_ms(30); } }
※ sprintf 함수
sprintf ( str , "문자열" );
▶ str 문자열에는 "문자열" 이 저장된다.
sprintf ( str , "%d" , 78);
▶ str 문자열에는 문자열 "78"이 저장된다,
즉, 출력값을 문자열에 저장하여 출력하는 함수이다.
[LCD_4Bit]
<LCD_4bit.h>
#ifndef LCD_4BIT_H_ #define LCD_4BIT_H_ #define F_CPU 16000000UL #include <avr/io.h> #include <util/delay.h> #include <stdio.h> #define LCD_DATA_DDR DDRC #define LCD_DATA_PORT PORTC // 데이터 포트 #define LCD_DATA_PIN PINC #define LCD_RS_DDR DDRB // 제어 핀 연결 #define LCD_RW_DDR DDRB #define LCD_E_DDR DDRB #define LCD_RS_PORT PORTB #define LCD_RW_PORT PORTB #define LCD_E_PORT PORTB #define LCD_RS 5 #define LCD_RW 6 #define LCD_E 7 // COMAND #define COMMAND_DISPLAY_CLEAR 0x01 #define COMMAND_DISPLAY_ON 0x0C // 화면 ON, 커서 OFF, 커서 점멸 OFF #define COMMAND_DISPLAY_OFF 0x08 // 화면 OFF, 커서 OFF, 커서 점멸 OFF #define COMMAND_ENTRY_MODE 0x06 // 커서 우측 이동, 화면 이동 없음 #define COMMAND_8_BIT_MODE 0x38 // 8비트, 화면 2행, 5x8 Font #define COMMAND_4_BIT_MODE 0x28 // 4비트, 화면 1행, 5x11 Font // 함수 원형 선언 void LCD_Data(uint8_t data); void LCD_Data4bit(uint8_t data); void LCD_EnablePin(); void LCD_WritePin(); void LCD_ReadPin(); 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(); void LCD_Data4bit_Init(uint8_t data); #endif /* LCD_4BIT_H_ */
<LCD_4bit.c>
#include "LCD_4bit.h" void LCD_Data(uint8_t data) { LCD_DATA_PORT = data; // 데이터 핀에 데이터 출력 } void LCD_Data4bit(uint8_t data) { LCD_DATA_PORT = (LCD_DATA_PORT & 0x0f) | (data & 0xf0); // 상위 4bit 출력 // 이전 상위 비트 다 날라감 | data의 상위비트 살림 LCD_EnablePin(); LCD_DATA_PORT = (LCD_DATA_PORT & 0x0f) | ((data & 0x0f)<<4); // 하위 4bit 출력 LCD_EnablePin(); } void LCD_Data4bit_Init(uint8_t data) { LCD_RW_PORT &= ~(1<<LCD_RW); LCD_EnablePin(); } void LCD_EnablePin() { LCD_E_PORT &= ~(1<<LCD_E); // E 핀을 Low 설정 LCD_E_PORT |= (1<<LCD_E); // E 핀을 High 설정 // Low -> High 되어야 Data들이 전송된다. LCD_E_PORT &= ~(1<<LCD_E); // 다시 Low 설정 _delay_us(1800); // 이 시간 조절해서, 문자 간 출력 딜레이 설정 가능 } void LCD_WritePin() { LCD_RW_PORT &= ~(1<<LCD_RW); // RW 핀을 Low로 설정하여 쓰기 모드 } void LCD_WriteCommand(uint8_t commandData) { LCD_RS_PORT &= ~(1<<LCD_RS); // RS 핀을 Low로 설정하여 명령어 모드 LCD_WritePin(); // 데이터 쓰기 모드로 설정 LCD_Data4bit(commandData); // 명령어 데이터를 데이터 핀에 출력_4bit // LCD_EnablePin(); // LCD 동작 신호 전송 } void LCD_WriteData(uint8_t charData) { LCD_RS_PORT |= (1<<LCD_RS); // RS 핀을 High로 설정하여 문자 데이터 모드 LCD_WritePin(); LCD_Data4bit(charData); //_4bit // LCD_EnablePin(); } void LCD_gotoXY(uint8_t row, uint8_t col) { col %= 16; // 열 인덱스를 0부터 15로 제한 row %= 2; // 행 인덱스를 0부터 1로 제한 uint8_t address = (0x40 * row) + col; // 주소 계산 uint8_t command = 0x80 + address; // command 값 계산 LCD_WriteCommand(command); // 주소 설정 command를 LCD에 전달 } void LCD_WriteString(char *string) { for (uint8_t i = 0 ; string[i]; i++) { LCD_WriteData(string[i]); // 문자열의 각 문자를 LCD에 출력 } } 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); // 초기화하고 15ms 이상 딜레이 LCD_WriteCommand(0x03); _delay_ms(5); // 4.3ms 이상 딜레이 LCD_WriteCommand(0x03); _delay_ms(5); LCD_WriteCommand(0x03); LCD_WriteCommand(0x02); // data sheet의 플로우 차트 따라간 것일 뿐 LCD_WriteCommand(COMMAND_4_BIT_MODE); LCD_WriteCommand(COMMAND_DISPLAY_OFF); LCD_WriteCommand(COMMAND_DISPLAY_CLEAR); LCD_WriteCommand(COMMAND_DISPLAY_ON); LCD_WriteCommand(COMMAND_ENTRY_MODE); // 입력 모드 설정 }
<main.c>
#include "LCD_4bit.h" // LCD 4bit // // 4bit는 직렬 방식으로 2번 보낸다. // 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++); LCD_WritestringXY(1,0,buff); _delay_ms(300); } }
[LCD_I2C]
I2C통신이란?

: Inter Integrated Circuit
여러 개의 슬레이브와 통신이 가능하며, 여러 개의 마스터끼리도 통신이 가능하다.
데이터를 주고 받는데 필요한 통신선은 2개이다.
- SDA(Serial Data Line)
- SCL(Serial Clock Line)
시리얼 통신 방식
구분 | UART | SPI | I2C | ||
동기 / 비동기 | 비동기 | 동기 | 동기 | ||
전이중/반이중 | 전이중 | 전이중 | 반이중 | ||
연결 방식 | 1:1 | 1:N | 1:N | ||
연결 선 개수 | 1개 슬레이브 연결 | 데이터 | 2 | 2 | 1(반이중) |
클록 | 0 | 1 | 1 | ||
제어 | 0 | 1 | 0 | ||
합계 | 2 | 4 | 2 | ||
슬레이브 선택 | - | 하드웨어 (SS라인) |
소프트웨어 (주소 지정) |
전이중 : 전화기, 반이중 : 무전기를 생각하면 쉽다.
I2C 통신의 특징
- 동기식 통신 방법
- 각 슬레이브 장치는 고유의 주소값(7bit)을 가진다.
- 버스에 연결된 장치들이 고유의 주소값을 가지고 master/slave 관계를 가진다.
- 세 가진 전송 속도를 지원
표준 모드(Standard Mode) | 100Kbps |
고속 모드(Fast Mode) | 400Kbps |
초고속 모드(High Speed Mode) | 3.4Mbps |
I2C 통신 방법
- 마스터가 통신의 시작 및 통신의 종료를 선언하여 통신이 이루어짐
- 마스터가 특정 주소를 가진 슬레이브와 연결하여 송수신을 수행
- 마스터가 클록 신호 SCL을 발생하여 통신
- 통신을 위하여 기본적으로 SDA와 SCL은 High 상태를 유지하기 위해 PULL-UP저항을 달아줘야 한다.

⊙ 마스터에서 슬레이브로 쓰기
: START → 슬레이브 W 모드 → 마스터에서 데이터 전송 → 슬레이브에서 받고 ACK 신호 출력 → .... → ACK → STOP
⊙ 마스터가 슬레이브에서 읽기
: START → 마스터에서 R 모드 → 슬레이브에서 데이터 전송 → 마스터에서 ACK → .... → 마스터에서 NACK → STOP

⊙ START
: SCL이 High일 때, SDA가 High에서 Low로 떨어지면 Start bit
⊙ STOP
: SCL이 High일 때, SDA가 Low에서 High로 올라가면 Stop bit
Register
1. TWBR (TWI Bit Rate Register)


- Master SCL 주파수를 구할 때 쓰는 bitrate를 저장하는 레지스터
- TWER이 72인 경우, SCL은 100,000Hz(100KHz)에 해당(분주율 1 가정)
- TWER 32 = 200KHz, TWER 12 = 400KHz
- TWPS : TWI Reg 내부에 있는 분주비의 값
2. TWCR (TWI Control Register)

1) TWINT(TWI Interrupt Flag, 7번 bit)
- TWI의 현재 작업이 완료되면 하드웨어적으로 1로 세트되며,
응용 소프트웨어에서 1을 Write하여 TWINT = 0 으로 클리어한다.
- SERG 레지스터의 I bit(전역)가 set되고 TWCR 레지스터의 TWIE가 set되어 있을 때
현재 작업이 완료되고 TWINT bit가 set되면, ISR을 실행한다.,
2) TWEA(TWI Enable Acknowledge bit, 6번 bit)
- ACK 펄스의 생성을 제어한다. 본 bit가 set되어 있을 때,
아래와 같은 상황에서 ACK 펄스 생성
① 슬레이브 모드에서 자신의 주소를 수신하고 나서
② 슬레이브 모드에서 TWAR 레지스터의 TWGCE bit가 세트되어 있고
슬레이브로 전달되는 일반 호출을 수신한 경우
③ 마스터나 슬레이브 모드에서 한 Byte 데이터를 수신한 경우
3) TWSTA(TWI Start Condition bit, 5번 bit)
- 마스터 모드에서 전송을 시작하기 위해 해당 bit를 set
4) TWSTO(TWI Stop Condition bit, 4번 bit)
- 마스터 모드에서 전송을 종료하기 위해 해당 bit를 set
5) TWWC(TWI Write Collision bit, 3번 bit)
- TWINT bit가 0인 경우,(데이터 전송 작업이 완료되지 않은 경우)
TWDR 레지스터에 데이터를 기록하려고 하면 set된다.
- TWINT bit가 1인 경우, TWDR 레지스터에 데이터를 기록하면 클리어된다.
6) TWEN(TWI Enable bit, 2번 bit)
- TWI 동작을 활성화하고 TWI의 인터페이스를 활성화한다.
- Enable = 1(PORT0, PORT1 사용)
3. TWDR (TWI Data Register)

: 송수신되는 데이터를 저장하는 레지스터
4. TWSR (TWI Status Register)

- TWS7 ~ TWS3 bit는 TWI의 로직과 버스의 상태를 반영한다.
- TWPS1, TWPS0는 분주비를 나타낸다.
TWPS1 | TWPS0 | Prescaler Value |
0 | 0 | 1 |
0 | 1 | 4 |
1 | 0 | 16 |
1 | 1 | 64 |
C code Sample

I2C 예제
<I2C.h>
#ifndef I2C_H_ #define I2C_H_ #define F_CPU 16000000UL #include <avr/io.h> #include <util/delay.h> #include <stdio.h> #define I2C_DDR DDRD #define I2C_SCL PORTD0 // SCL : Clock Line #define I2C_SDA PORTD1 // SDA : Data Line // 함수 원형 선언 // void I2C_Init(); void I2C_Start(); void I2C_Stop(); void I2C_TxData(uint8_t data); void I2C_TxByte(uint8_t devAddrRW, uint8_t data); #endif /* I2C_H_ */
<I2C.c>
#include "I2C.h" void I2C_Init() { I2C_DDR |= (1<<I2C_SCL) | (1<<I2C_SDA); // 출력 설정 TWBR = 72; // 100KHz // TWBR = 32; // 200KHz // TWBR = 12; // 400KHz } void I2C_Start() { TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); // 1을 보내는 것은, S/W적으로 플래그를 Clear해주는 것이다. // 1 Set하여 인터럽트 발생시키는 것이 아님. while(!(TWCR & (1<<TWINT))); // 하드웨어적으로 TWINT 시점을 결정 // TWI 통신 동작이 완료될 때까지 대기하는 역할 // 기다렸다가 조건이 거짓이 되면 while문을 빠져나간다. // 즉, TWCR에서 TWINT bit가 1이 되면, while 문의 조건이 거짓이 되고 빠져나가게 된다. } void I2C_Stop() { TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO); // Stop 비트 설정 } void I2C_TxData(uint8_t data) // data 1바이트 전송 { TWDR = data; // TWDR Register에 data 값 저장 TWCR = (1<<TWINT) | (1<<TWEN); // 플래그 세우고 En = 1로 바꿔서 데이터 전송 완료됨을 알림 while (!(TWCR & (1<<TWINT))); // 전송 완료 대기 } void I2C_TxByte(uint8_t devAddrRW, uint8_t data) { I2C_Start(); I2C_TxData(devAddrRW); // 읽을 것이냐 쓸 것이냐 결정 I2C_TxData(data); // 입/출력할 데이터 보냄 I2C_Stop(); }
<I2C_LCD.h>
#ifndef I2C_LCD_H_ #define I2C_LCD_H_ #include "I2C.h" #define LCD_RS 0 // Register Select // 제어 레지스터 : 0 , 데이터 레지스터 : 1 #define LCD_RW 1 // Read/Write 설정하는 레지스터 bit / Read : 1, Write : 0 #define LCD_E 2 // 여기서 2는 LCD board상 2번 핀(E핀)을 뜻한다. #define LCD_BACKLIGHT 3 #define LCD_DEV_ADDR (0x27<<1) // I2C LCD 주소 0x27, <<1 : Write 모드 유지 #define COMMAND_DISPLAY_CLEAR 0x01 #define COMMAND_DISPLAY_ON 0x0c #define COMMAND_DISPLAY_OFF 0x08 #define COMMAND_4_BIT_MODE 0x28 #define COMMAND_ENTRY_MODE 0x06 // 함수 원형 선언 // void LCD_Data4bit(uint8_t data); void LCD_EnablePin(); void LCD_WriteCommand(uint8_t commandData); void LCD_WriteData(uint8_t charData); void LCD_BackLight(); 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 /* I2C.LCD_H_ */
<I2C_LCD.c>
#include "I2C_LCD.h" uint8_t I2C_LCD_Data; void LCD_Data4bit(uint8_t data) { I2C_LCD_Data = (I2C_LCD_Data & 0x0f) | (data & 0xf0); // 상위 4bit 출력 // 이전 상위 비트는 다 날라감 | data의 상위 비트는 살림 LCD_EnablePin(); I2C_LCD_Data = (I2C_LCD_Data & 0x0f) | ((data & 0x0f)<<4); // 하위 4bit LCD_EnablePin(); // 상위 4bit 받고 -> Enable해서 출력하고 -> 하위 4bit 받고 상위로 시프트하고 -> Enabla해서 출력 // [7:4] 핀만 사용해서 반반 나눠서 한 번씩출력하면, 총 8bit를 출력할 수 있다. } void LCD_EnablePin() { I2C_LCD_Data &= ~(1<<LCD_E); // E low 설정 I2C_TxByte(LCD_DEV_ADDR, I2C_LCD_Data); I2C_LCD_Data |= (1<<LCD_E); // High 설정 I2C_TxByte(LCD_DEV_ADDR, I2C_LCD_Data); I2C_LCD_Data &= ~(1<<LCD_E); // E low 설정 I2C_TxByte(LCD_DEV_ADDR, I2C_LCD_Data); // 0->1 일때, data 출력 -> 다음 출력을 위해 0으로 다시 설정 _delay_us(1600); // 1Byte 출력하고 1600us 대기(LCD는 매우 느린 부품이므로 빨리 출력하면 글자 다 깨짐) } void LCD_WriteCommand(uint8_t commandData) { I2C_LCD_Data &= ~(1<<LCD_RS); // Command일 때는 Control register(TWCR)을 쓰고 I2C_LCD_Data &= ~(1<<LCD_RW); // Write 모드 LCD_Data4bit(commandData); } void LCD_WriteData(uint8_t charData) { I2C_LCD_Data |= (1<<LCD_RS); // Data받을 때는 Data Register(TWDR) 쓰고 I2C_LCD_Data &= ~(1<<LCD_RW); // Write 모드 LCD_Data4bit(charData); } void LCD_BackLight() // LCD의 Back_Light를 켜주는 함수 { I2C_LCD_Data |= (1<<LCD_BACKLIGHT); I2C_TxByte(LCD_DEV_ADDR, I2C_LCD_Data); } void LCD_gotoXY(uint8_t row, uint8_t col) // LCD 화면의 커서를 지정된 행(row)과 열(col) 위치로 이동시키는 함수 { col %= 16; // 0~15 사이의 값 지정 가능 row %= 2; // 0~1 사이의 값 지정 가능 uint8_t address = (0x40 * row) + col; // 주어진 row와 col 값을 이용하여 LCD 화면의 주소(address, 커서 위치)룰 계산한다. // 첫 번째 행(row 0)의 주소 범위는 0x00 ~ 0x0f(0~15) // 두 번째 행(row 1)의 주소 범위는 0x40 ~ 0x4f(64~79) // 예시 : row 1, col 3 -> address = 0x43 uint8_t command = 0x80 + address; // 계산된 주소를 이용하여 이동시키는 명령어 command 생성 // 0x80을 사용하는 이유는 특정 주소값이 아닌, 첫 번째 행의 시작을 나타내는 상징적인 값으로 사용된다, // 이렇게 함으로써 코드의 가독성을 높이고, 행과 열 값을 쉽게 결합하여 원하는 주소 값을 계산할 수 있다. LCD_WriteCommand(command); } void LCD_WriteString(char *string) { for (uint8_t i = 0 ; string[i] ; i++) // 받은 문자열의 포인터가 보는 곳을 한 비트씩 분해해서 LCD_WriteData 함수에 하나씩 뿌려줌 { 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() { I2C_Init(); _delay_ms(20); // 딜레이들은 Data sheet 내의 4-bit Interface 참조 LCD_WriteCommand(0x03); _delay_ms(10); LCD_WriteCommand(0x03); _delay_ms(1); LCD_WriteCommand(0x03); LCD_WriteCommand(0x02); LCD_WriteCommand(COMMAND_4_BIT_MODE); LCD_WriteCommand(COMMAND_DISPLAY_OFF); LCD_WriteCommand(COMMAND_DISPLAY_CLEAR); LCD_WriteCommand(COMMAND_ENTRY_MODE); LCD_WriteCommand(COMMAND_DISPLAY_ON); LCD_BackLight(); }

<main.c>
int main(void) { uint16_t count = 0; char buff[30]; LCD_Init(); LCD_WriteStringXY(0,0,"Hello ATmega128"); // 0행 0열부터 해당 문자열 출력하여라 while (1) { sprintf(buff, "count : %-5d", count++); LCD_WriteStringXY(1, 0, buff); _delay_ms(500); } }