[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);
}
}