9/10 LCD

정유석·2024년 9월 10일
post-thumbnail

cpu구조
RISC-5
CISC
RISC
AMBA
AXI4
APB
AHB
ASB

LCD(뒤에 칩이 없는)

명령어 요약

ATmega

오른쪽 솔루션 창에서 솔루션 우클릭 add->new project, 생성된 프로젝트에서 우클릭 add->new item(헤더파일 또는 코드파일 생성)

DDRx: GPIO 핀의 데이터 방향을 설정 (입력 또는 출력).
PORTx: 출력 핀의 상태를 설정하거나 입력 핀의 풀업 저항을 활성화.
PINx: 입력 핀의 현재 상태를 읽음

VO에 저항을 연결하여 문자의 밝기 조절, 가변 저항을 달아 조절가능

8bit LCD

LCD.c

#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 -> 쓰기모드 진입, LCD_RW는 6으로 정의
}
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;		//커맨드 값계산(주소설정)
	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 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);
	LCD_WriteCommand(COMMAND_DISPLAY_ON);
}

LCD.h

#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 //화면 clear
#define COMMAND_DISPLAY_ON		0x0c //디스플레이on, 커서off
#define COMMAND_DISPLAY_OFF		0x08 //디스플레이off, 커서off
#define COMMAND_ENTRY_MODE		0x06 //커서 우측 이동, 화면이동 없음
#define COMMAND_8_BIT_MODE		0x38 //8비트, 화면2행, 5x8 font
#define COMMAND_4_BIT_MODE		0x28 //4비트, 화면2행, 5x8 font

void LCD_DATA(uint8_t data);
//void LCD_DATA4bit(uint8_t data); //4bit 모드
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_ */

main.c

//#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) 
    {
    }
}

4bit LCD

LCD.c

#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); //LCD_DATA_PORT변수 초기화 후, 상위 4비트 출력
	LCD_EnablePin();										//LCD 동작 신호 전송
	LCD_DATA_PORT = (LCD_DATA_PORT & 0x0f) | (data & 0x0f) << 4;	//LCD_DATA_PORT변수 초기화 후, 하위 4비트 출력
	LCD_EnablePin();												//LCD 동작 신호 전송
}
void LCD_WritePin()
{	// LOW로 만들기 위함
	LCD_RW_PORT &= ~(1<<LCD_RW); //RW핀을 LOW -> 쓰기모드 진입, LCD_RW는 6으로 정의
}
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(2000);
}
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;		//커맨드 값계산(주소설정)
	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 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(1);
	LCD_WriteCommand(0x03);
	
	LCD_WriteCommand(0x02);
	
	LCD_WriteCommand(COMMAND_4_BIT_MODE); //8비트 보다 한 라인 추가
	
	LCD_WriteCommand(COMMAND_DISPLAY_OFF);
	LCD_WriteCommand(COMMAND_DISPLAY_CLEAR);
	LCD_WriteCommand(COMMAND_DISPLAY_ON);
	LCD_WriteCommand(COMMAND_ENTRY_MODE);
	//LCD_WriteCommand(COMMAND_DISPLAY_ON);
}

LCD.h

#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 //화면 clear
#define COMMAND_DISPLAY_ON		0x0c //디스플레이on, 커서off
#define COMMAND_DISPLAY_OFF		0x08 //디스플레이off, 커서off
#define COMMAND_ENTRY_MODE		0x06 //커서 우측 이동, 화면이동 없음
#define COMMAND_8_BIT_MODE		0x38 //8비트, 화면2행, 5x8 font
#define COMMAND_4_BIT_MODE		0x28 //4비트, 화면2행, 5x8 font

void LCD_DATA(uint8_t data);
void LCD_DATA4bit(uint8_t data); //4bit 모드
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_ */

main.c

#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 LCD (4bit)

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.c

TWBR 계산 공식



#include "I2C.h"

void I2C_Init() {
    I2C_DDR |= (1<<I2C_SDA | 1<<I2C_SCL);

    // ATmega 128A의 DataSheet 참고
    TWBR = 72; // 100KHz
    // TWBR = 72; // 200KHz
    // TWBR = 72; // 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();
}

/*
#include "I2C.h"

void I2C_Init() {
	I2C_DDR |= (1<<I2C_SDA | 1<<I2C_SCL);

	// ATmega 128A의 DataSheet 참고
	TWBR = 72; // 100KHz
	// TWBR = 72; // 200KHz
	// TWBR = 72; // 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.h


#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_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); //LCD_DATA_PORT변수 초기화 후, 상위 4비트 출력
	LCD_EnablePin();										//LCD 동작 신호 전송
	I2C_LCD_Data = (I2C_LCD_Data & 0x0f) | (data & 0x0f) << 4;	//LCD_DATA_PORT변수 초기화 후, 하위 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 commadData)
{
	I2C_LCD_Data &= ~(1<<LCD_RS);
	I2C_LCD_Data &= ~(1<<LCD_RW);
	LCD_DATA4bit(commadData);
}
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;		//커맨드 값계산(주소설정)
	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()
{
	I2C_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);
	_delay_ms(5);
	LCD_WriteCommand(0x03);
	_delay_ms(1);
	LCD_WriteCommand(0x03);
	
	LCD_WriteCommand(0x02);
	
	LCD_WriteCommand(COMMAND_4_BIT_MODE); //8비트 보다 한 라인 추가
	
	LCD_WriteCommand(COMMAND_DISPLAY_OFF);
	LCD_WriteCommand(COMMAND_DISPLAY_CLEAR);
	LCD_WriteCommand(COMMAND_DISPLAY_ON);
	LCD_WriteCommand(COMMAND_ENTRY_MODE);
	//LCD_WriteCommand(COMMAND_DISPLAY_ON);
	LCD_BacklightOn();
}

I2C_LCD.h


#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 //화면 clear
#define COMMAND_DISPLAY_ON		0x0c //디스플레이on, 커서off
#define COMMAND_DISPLAY_OFF		0x08 //디스플레이off, 커서off
#define COMMAND_ENTRY_MODE		0x06 //커서 우측 이동, 화면이동 없음
#define COMMAND_4_BIT_MODE		0x28 //4비트, 화면2행, 5x8 font

void LCD_DATA4bit(uint8_t data);
void LCD_EnablePin();
void LCD_WriteCommand(uint8_t commadData);
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_ */

main.c

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

실험


8bit 방식의 초기화 순서이다. 이 때 약간의 시간을 두고 초기화 신호를 0x3?의 신호를 3번 보내게 된다.
그 후

이 부분은 어떤식으로 설정해도 된다는 것을 확인했다. 결국 처음 3번 같은 신호를 보내는 것이 끝나면 LCD를 동작시키기 위한 초기화는 끝이고 그 다음은 디스플레이나 커서를 어떻게 설정할지 초기화 할지인것같다. 아마 4bit 방식도 같을 것으로 예상됨


profile
개인 기록공간

0개의 댓글