1602 I2C LCD Module
TWI(Two Wire Interface)
TWBR, TWCR, TWDR 등을 사용하여 I2C 통신설정
void TWI_Init(void){
// TWI Bit Rate Register
// TWBR = 0x48; // 0b01001000
TWBR = (1<<TWBR5) | (1<<TWBR3); // SCL Frequency, Datasheet for reference
}
void TWI_Start(void) {
// TWI Control Register
// Interrupt, Start bit, Enable Set
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
while(!(TWCR & (1<<TWINT))); // TWI Setup complete -> Interrupt flag set
}
void TWI_Transmit(unsigned char data) {
// TWI Data Register(Data buffer, 8bit)
TWDR = data;
TWCR = (1<<TWINT) | (1<<TWEN); // Transmit -> Interrupt
while(!(TWCR & (1<<TWINT)));
}
void TWI_Transmit_Addr(unsigned char data, unsigned char addr){
TWI_Start();
TWI_Transmit(addr);
TWI_Transmit(data);
TWI_Stop();
}
void TWI_Stop(void) {
// TWCR Register init
TWCR = (1<<TWINT) | (1<<TWSTO) | (1<<TWEN);
}
c를 왼쪽으로 쉬프트 연산하면 상위 4bit는 원본의 하위 4bit.
원본 8bit를 보내려면 총 함수를 2번 호출해야함.
함수내부에서는 Enable신호를 보내고 데이터를 보내고 다시 Disable신호를 보내는 작업을 함.
LCD초기화 과정
volatile unsigned char PortLCD = 0; // Higher 4bit(0xF0) == data bit, Lower 4bit(0x0F) == Control bit
void LCD_Init(void) {
// sendByte(0b00110011, 0);
sendHalfByte(0b00000011);
_delay_ms(5);
sendHalfByte(0b00000011);
_delay_us(100);
// sendByte(0b00110010, 0);
sendHalfByte(0b00000011);
_delay_ms(1);
sendHalfByte(0b00000010);
_delay_ms(1);
sendByte(0b00101000, 0); // Data 4bit, Line 2, Font 5x8
_delay_ms(1);
sendByte(0b00001110, 0); //Display ON, Cursor ON, Blink OFF
_delay_ms(1);
TWI_Transmit_Addr(PortLCD |= 0x08, 0x4E); // BackLight ON
TWI_Transmit_Addr(PortLCD &= ~0x02, 0x4E); // LCD Write ON
}
4bit씩 보내기
void sendHalfByte(unsigned char c) {
// Higher 4 Bit
c <<= 4;
TWI_Transmit_Addr(PortLCD |= 0x04, 0x4E); // Enable E, Higher 4bit
_delay_us(50);
// Lower 4 Bit
TWI_Transmit_Addr(PortLCD | c, 0x4E);
TWI_Transmit_Addr(PortLCD &= ~0x04, 0x4E); // Disable E, Lower 4bit
_delay_us(50);
}
주소를 설정할 때 주소는 7bit 0x27 -> R/W bit를 추가한 8bit 에서는 0x4E로 보내주어야 함.
7bit+R/W bit (0x27<<1 == 0x4E)
LCD에 문자 출력
void sendByte(unsigned char c, unsigned char mode) {
if(mode == 0) TWI_Transmit_Addr(PortLCD &= ~0x01, 0x4E);
else TWI_Transmit_Addr(PortLCD |= 0x01, 0x4E);
unsigned char hc = 0;
hc = c >> 4;
sendHalfByte(hc);
sendHalfByte(c);
}
void LCD_sendString(char s[]) {
char n;
for(n=0; s[n]!='\0'; n++)
sendByte(s[n], 1);
}
기본적으로 ASCII코드와 LCD의 문자 bit가 일정함.
ex) ASCII 'a' : 0110 0001
시작위치 설정 및 Line 선택
void LCD_setPosition(unsigned char x, unsigned char line) { // HD44780 data sheet for reference
switch(line) {
case 0:
sendByte(x | 0x80, 0);
break;
case 1:
sendByte((x+0x40) | 0x80, 0);
break;
}
}
일반적으로 HD44780의 명령어 포맷은 다음과 같음.
첫 번째 라인의 시작 주소: 0x00부터 0x27까지 (0x00, 0x01, ..., 0x27)
두 번째 라인의 시작 주소: 0x40부터 0x67까지 (0x40, 0x41, ..., 0x67)
수정사항: sendHalfByte부분 비효율적임. (0x27 << 1)을 사용하여 한번에 읽기쓰기를 정하고 사용하기.
참고 : https://wowon.tistory.com/263?category=615510
https://github.com/codecranch/avr-twi-lcd/blob/main/lcd.c
https://kogun.tistory.com/23