728x90
ATmega128을 활용하여 LCD를 제어해보려한다.
기본적인 명령어는 아래의 표와 같다.
LCD를 제어하기위한 헤더파일의 코드는 아래와 같다.
헤더파일의 코드 내에는 주로 LCD를 다룰 함수들과 define으로 이루어져있다.
#ifndef LCD_H_
#define LCD_H_
#define F_CPU 16000000UL
#include <avr/io.h>
#include <stdio.h>
#include <util/delay.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 5
#define LCD_RW 6
#define LCD_E 7
#define COMMAND_DISPLAY_CLEAR 0x01
#define COMMAND_DISPLAY_ON 0x0C
#define COMMAND_DISPLAY_OFF 0x08
#define COMMAND_ENTRY_MODE 0x06
#define COMMAND_8_BIT_MODE 0x38
#define COMMAND_4_BIT_MODE 0x28
void LCD_Data(uint8_t data);
// void LCD_Data4bit(uint8_t data);
void LCD_WritePin();
void LCD_ReadPin();
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 LCDInit();
#endif /* LCD_H_ */
LCD 제어관련 소스코드는 아래와 같다.
소스코드에서는 헤더파일에서 다룬 함수들의 동작을 구체적으로 다룬다.
#include "lcd.h"
void LCD_Data(uint8_t data)
{
LCD_DATA_PORT = data; // 데이터 핀에 8비트 출력
}
// void LCD_Data4bit(uint8_t data);
void LCD_WritePin()
{ // LOW
LCD_RW_PORT &= ~(1<<LCD_RW); // RW핀을 LOW -> 쓰기모드로 진입
}
void LCD_ReadPin()
{
LCD_RW_PORT |= (1<<LCD_RW); // RW핀을 High -> 읽기 모드
}
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);
}
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)
{
col %= 16;
row %= 2;
uint8_t address = (0x40 * row) + col; // 주소계산
uint8_t command = 0x80 + address; // command 값 계산(주소 설정)
LCD_WriteCommand(command); // 주소 command 전송
}
void LCD_WriteString(char *string)
{
for (uint8_t i = 0; string[i]; i++)
{
LCD_WriteData(string[i]); // string Display
}
}
void LCD_WriteStringXY(uint8_t row, uint8_t col, char *string)
{
LCD_GotoXY(row, col);
LCD_WriteString(string);
}
void LCDInit()
{
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_8_BIT_MODE);
_delay_ms(5);
LCD_WriteCommand(COMMAND_8_BIT_MODE);
_delay_ms(1);
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문의 코드는 아래와 같다.
// #define F_CPU 16000000UL
// #include <avr/io.h>
// #include <util/delay.h>
// #include <stdio.h>
#include "lcd.h"
int main(void)
{
LCDInit();
LCD_GotoXY(0, 0);
LCD_WriteString("Hello AVR");
LCD_GotoXY(1, 0);
LCD_WriteString("Good ATmega128A");
while (1)
{
}
}
코드를 프로그래밍하면 LCD에는 다음과 같은 문구가 출력되는 것을 확인할 수 있다.
이제는 8bit가 아닌 4bit mode로 LCD를 제어해보려고 한다.
헤더파일에서 바뀐점은 'void LCD_Data4bit(uint8_t data);' 주석처리가 해제됐다는 점이다.
헤더파일의 코드는 아래와 같다.
#ifndef LCD_H_
#define LCD_H_
#define F_CPU 16000000UL
#include <avr/io.h>
#include <stdio.h>
#include <util/delay.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 5
#define LCD_RW 6
#define LCD_E 7
#define COMMAND_DISPLAY_CLEAR 0x01
#define COMMAND_DISPLAY_ON 0x0C
#define COMMAND_DISPLAY_OFF 0x08
#define COMMAND_ENTRY_MODE 0x06
#define COMMAND_8_BIT_MODE 0x38
#define COMMAND_4_BIT_MODE 0x28
void LCD_Data(uint8_t data);
void LCD_Data4bit(uint8_t data);
void LCD_WritePin();
void LCD_ReadPin();
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 LCDInit();
#endif /* LCD_H_ */
소스코드는 아래의 코드와 같다.
#include "lcd.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비트 출력
LCD_EnablePin(); // LCD 동작 신호 전송
LCD_DATA_PORT = (LCD_DATA_PORT & 0x0f) | ((data & 0x0f) << 4); // 하위 4비트 출력
LCD_EnablePin(); // LCD 동작 신호 전송
}
void LCD_WritePin()
{ // LOW
LCD_RW_PORT &= ~(1<<LCD_RW); // RW핀을 LOW -> 쓰기모드로 진입
}
void LCD_ReadPin()
{
LCD_RW_PORT |= (1<<LCD_RW); // RW핀을 High -> 읽기 모드
}
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);
}
void LCD_WriteCommand(uint8_t commandData)
{
LCD_RS_PORT &= ~(1<<LCD_RS); // RS핀을 Low로 설정 -> 명령어모드 진입
LCD_WritePin(); // 데이터 쓰기모드
LCD_Data4bit(commandData); // 명령어를 데이터핀에 출력
//LCD_EnablePin(); // LCD 동작신호 전송
}
void LCD_WriteData(uint8_t charData)
{
LCD_RS_PORT |= (1<<LCD_RS); // RS핀을 High로 설정 -> 문자모드 진입
LCD_WritePin(); // 데이터 쓰기모드
LCD_Data4bit(charData); // 명령어를 데이터핀에 출력
//LCD_EnablePin(); // LCD 동작신호 전송
}
void LCD_GotoXY(uint8_t row, uint8_t col)
{
col %= 16;
row %= 2;
uint8_t address = (0x40 * row) + col; // 주소계산
uint8_t command = 0x80 + address; // command 값 계산(주소 설정)
LCD_WriteCommand(command); // 주소 command 전송
}
void LCD_WriteString(char *string)
{
for (uint8_t i = 0; string[i]; i++)
{
LCD_WriteData(string[i]); // string Display
}
}
void LCD_WriteStringXY(uint8_t row, uint8_t col, char *string)
{
LCD_GotoXY(row, col);
LCD_WriteString(string);
}
void LCDInit()
{
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);
_delay_ms(5);
LCD_WriteCommand(0x03);
_delay_ms(5);
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_DISPLAY_ON);
LCD_WriteCommand(COMMAND_ENTRY_MODE);
}
메인소스의 코드는 아래와 같다.
#include <avr/io.h>
#include "lcd.h"
int main(void)
{
LCDInit();
LCD_GotoXY(0, 0);
LCD_WriteString("Hello 4BIT");
LCD_GotoXY(1, 0);
LCD_WriteString("4BIT GOOD");
while (1)
{
}
}
출력되는 화면은 아래의 사진과 같다.
I2C
이번에는 I2C 통신을 활용하여 LCD를 제어해보려한다.
def.h 헤더파일로 기본적인 헤더파일을 선언하였다.
#ifndef DEF_H_
#define DEF_H_
#define F_CPU 16000000UL
#include <avr/io.h>
#include <stdio.h>
#include <util/delay.h>
#include <stdbool.h>
#include <stdint.h>
#endif /* DEF_H_ */
I2C 헤더파일의 코드내용은 아래와 같다.
#ifndef I2C_H_
#define I2C_H_
#include "def.h"
#define I2C_DDR DDRD
#define I2C_SCL PORTD0
#define I2C_SDA PORTD1
void I2C_Init();
void I2C_Start();
void I2C_Stop();
void I2C_TxData(uint8_t data);
void I2C_TxByte(uint8_t devAddress, uint8_t data);
#endif /* I2C_H_ */
I2C 소스파일의 코드는 아래와 같다.
#include "I2C.h"
void I2C_Init()
{
I2C_DDR |= (1<<I2C_SCL) | (1<<I2C_SDA);
TWBR = 72; // TWBR = 비트 Rate 선정하는 것 (100kHz)
// TWBR = 32; // TWBR = 비트 Rate 선정하는 것 (200kHz)
// TWBR = 12; // TWBR = 비트 Rate 선정하는 것 (400kHz)
}
void I2C_Start()
{
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
while(!(TWCR & (1<<TWINT))); // TWINT 시점 결정
}
void I2C_Stop()
{
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
}
void I2C_TxData(uint8_t data) // 1byte 전송
{
TWDR = data;
TWCR = (1<<TWINT) | (1<<TWEN);
while(!(TWCR & (1<<TWINT))); // 전송 완료 대기
}
void I2C_TxByte(uint8_t devAddress, uint8_t data)
{
I2C_Start();
I2C_TxData(devAddress);
I2C_TxData(data);
I2C_Stop();
}
I2C_LCD 헤더파일의 코드는 아래와 같다.
#ifndef I2C_LCD_H_
#define I2C_LCD_H_
#include "def.h"
#include "I2C.h"
#define LCD_RS 0
#define LCD_RW 1
#define LCD_E 2
#define LCD_BACKLIGHT 3
#define LCD_DEV_ADDR (0x27<<1)
#define COMMAND_DISPLAY_CLEAR 0x01
#define COMMAND_DISPLAY_ON 0x0C
#define COMMAND_DISPLAY_OFF 0x08
#define COMMAND_ENTRY_MODE 0x06
#define COMMAND_4_BIT_MODE 0x28
void LCD_Data4bit(uint8_t data);
void LCD_EnablePin();
void LCD_WriteCommand(uint8_t commandData);
void LCD_WriteData(uint8_t charData);
void LCD_BACKLIGHTOn();
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 소스파일의 코드는 아래와 같다.
#include "I2C_LCD.h"
uint8_t I2C_LCD_Data;
void LCD_Data4bit(uint8_t data)
{
I2C_LCD_Data = (I2C_LCD_Data & 0x0f) | (data & 0xf0); // 상위 4비트 출력
LCD_EnablePin(); // LCD 동작 신호 전송
I2C_LCD_Data = (I2C_LCD_Data & 0x0f) | ((data & 0x0f) << 4); // 하위 4비트 출력
LCD_EnablePin(); // LCD 동작 신호 전송
}
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); // 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);
_delay_us(1600);
}
void LCD_WriteCommand(uint8_t commandData)
{
I2C_LCD_Data &= ~(1<<LCD_RS);
I2C_LCD_Data &= ~(1<<LCD_RW);
LCD_Data4bit(commandData);
}
void LCD_WriteData(uint8_t charData)
{
I2C_LCD_Data |= (1<<LCD_RS);
I2C_LCD_Data &= ~(1<<LCD_RW);
LCD_Data4bit(charData);
}
void LCD_BACKLIGHTOn()
{
I2C_LCD_Data |= (1<<LCD_BACKLIGHT);
I2C_TxByte(LCD_DEV_ADDR, I2C_LCD_Data);
}
void LCD_GotoXY(uint8_t row, uint8_t col)
{
col %= 16;
row %= 2;
uint8_t address = (0x40 * row) + col; // 주소계산
uint8_t command = 0x80 + address; // command 값 계산(주소 설정)
LCD_WriteCommand(command); // 주소 command 전송
}
void LCD_WriteString(char *string)
{
for (uint8_t i = 0; string[i]; i++)
{
LCD_WriteData(string[i]); // string Display
}
}
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);
LCD_WriteCommand(0x03);
_delay_ms(5);
LCD_WriteCommand(0x03);
_delay_ms(5);
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_DISPLAY_ON);
LCD_WriteCommand(COMMAND_ENTRY_MODE);
LCD_BACKLIGHTOn();
}
LCD 출력을 정할 main의 코드는 아래와 같다.
#include "I2C_LCD.h"
int main(void)
{
uint16_t count = 0;
uint8_t buff[30];
LCD_Init();
LCD_WriteStringXY(0, 0, "Hello ATmega128a");
while (1)
{
sprintf(buff, "count : %-5d", count++);
LCD_WriteStringXY(1, 0, buff);
_delay_ms(200);
}
}
동작 영상은 아래와 같다.
728x90
'Language > AVR' 카테고리의 다른 글
STM32 : UART (2) | 2024.09.12 |
---|---|
STM32 : LED (0) | 2024.09.12 |
ATmega 128 : 4-Digit FND 동작 구현 (0) | 2024.05.30 |
ATmega 128 : FND 동작 구현 (0) | 2024.05.29 |
ATmega 128 : Led Bar 버튼 인터럽트 동작 구현 (0) | 2024.05.29 |