본문 바로가기
대한상공회의소 스마트팩토리 교육/IoT 디바이스 개발

[IoT 디바이스 개발] AVR(ATmega128A)«수업-20» : 텍스트 LCD Display / I2C로 LCD Display 화면 출력 / 초음파 센서의 값을 LCD로 출력하기

by 나는영하 2022. 2. 28.

※ 주의사항 

본 블로그는 수업 내용을 바탕으로 제가 이해한 부분을 정리한 블로그입니다.
본 내용을 참고로만 보시고, 틀린 부분이 있다면 지적 부탁드립니다!

감사합니다😁

 

안녕하세요!!

오늘은 아래와 같은 내용을 확인해보겠습니다.

 

텍스트 LCD Display

I2C 통신으로 LCD 화면 출력

I2C 통신을 통해 초음파 센서값을 LCD로 출력


 

일전에 아두이노를 통해 LCD Display를 출력해 보았습니다.

오늘은 아두이노가 아닌 ATmega128A(AVR)의 I2C 통신을 통해 LCD Display에 출력해보겠습니다.

지난 내용은 아래의 링크를 참고 바랍니다. 😄

2022.01.28 - [IoT 디바이스 개발] - [IoT 디바이스 개발] 아두이노«수업-4» : LCD Display (1602A) 사용법 (LiquidCrystal lcd 함수 / lcd.begin 함수 / lcd.setCursor 함수 등)

 

[IoT 디바이스 개발] 아두이노«수업-4» : LCD Display (1602A) 사용법 (LiquidCrystal lcd 함수 / lcd.begin 함수

※ 주의사항 ※ 본 블로그는 수업 내용을 바탕으로 제가 이해한 부분을 정리한 블로그입니다. 본 내용을 참고로만 보시고, 틀린 부분이 있다면 지적 부탁드립니다! 감사합니다😁 안녕하세요!!

920416.tistory.com


1. 텍스트 LCD(Liquid Crystal Display)

1-1. 텍스트 LCD 핀 설명

LCD Display(1602A - 좌) / PCF 8574T 칩 (우)

- LCD Display에는 총 16개의 PIN 제공

- I2C 통신은 LCD에 직접적으로 결선하는 것이 아닌 LCD 후면에 있는 별도의 칩(PCF 8574T)에 하드웨어적으로 4개의 선(VCC, GND, SDL, SDA)만 결선

- I2C 통신으로는 상단의 핀을 물리적으로 결선할 필요는 없지만 프로그래밍을 위해 핀이 가지는 특징을 알 필요가 있음

 

※ LCD Display PIN 별 설명

핀 이름 설명 핀 이름 설명
VSS 그라운드(GND) D0 데이터 신호 핀
VDD 5V 전원(VCC) D1
VO LCD 전원으로 가변저항을
통해 0 ~ 5V 사이 입력
D2
RS 레지스터 선택(Register Select) D3
R/W 읽기 / 쓰기(Read / Write) D4
E 활성화(enable) D5
A LCD 백라이트 전원 D6
K D7

 

1) VSS / GND :전원을 위한 핀으로 제품의 정격 전압에 해당하는 전원을 연결

2) VO : 해당 핀에 가변저항을 연결해서 저항을 가변시키면 LCD의 글자가 표시를 제어할 수 있다.

2) RS : 텍스트 LCD를 제어하기 위해 제어 레지스트와 데이터 레지스트 2개를 사용하며, 해당 핀은 두개의 레지스터 중 한가지를 선택하기 위해 사용

3) R/W : 읽기와 쓰기를 선택하기 위한 핀. LCD는 일반적으로 데이터를 쓰기 위한 용도로만 사용되기 때문에 R/W핀에 GND를 연결해서 LOW의 신호를 준다. (HIGH는 읽기 모드)

4) E : 하강에지에서 LCD 드라이버가 레지스터에 있는 내용을 처리하기 위해 사용되는 신호. (특정한 동작을 하기위해 레지스터에 값을 저장했다면 해당 핀을 HIGH로 주었다 LOW로 주면 데이터를 처리한다)

5) D0 ~ D7 : 데이터 신호 핀. (해당 값에 따라서 RS, R/W핀과 같이 특정한 동작을 수행하기도 한다)

 

1-2. 텍스트 LCD 모듈 명령어

Datasheet의 모듈 명령어

※ 상단의 모듈 명령어중 대표적으로 사용되는 명령어 6개

1) Clear Display : 공백문자(코드 0x20)로 화면을 지우고, 커서를 홈위치(주소 0번 )로 이동시킨다.

2) Entry Mode Set : 데이터 읽기 또는 쓰기 후 메모리의 증가 또는 감소 방향을 지정한다.

- I/D값이 0이면 왼쪽, 1이면 오른쪽으로 커서가 이동한다.

- S = 1 이면 I/D의 값에 따라 디스플레이를 옮기지만 커서는 고정된 위치에 나타난다.

3) Cursor or Display Shift : 화면에 출력된 내용의 변경 없이 커서와 화면을 이동시킨다.

S/C R/L 설명
0 0 커서를 왼쪽으로 옮긴다.
메모리 주소 1 감소
0 1 커서를 오른쪽으로 옮긴다.
메모리 주소 1 증가
1 0 화면을 왼쪽으로 옮긴다.
커서 역시 왼쪽 이동
1 1 화면을 오른쪽으로 옮긴다.
커서 역시 오른쪽으로 이동

4) Display ON/OFF : 디스플레이(D), 커서(C), 커서 깜빡임(B)을 ON / OFF 시킨다.

5) Function Set : 데이터 비트 크기(DL = 1, 8비트), 디스플레이 행 수(N), 폰트 크기(F) 설정

N F 행 수  폰트 크기
0 0 1 5 x 8
0 1 1 5 x 10
1 - 2 5 x 8

6) Set DDRAM address : 주소 카운터에 DDRAM 주소를 설정한다.(커서 이동 관련 명령어)

 


 

2. I2C 통신으로 LCD Display 화면 출력 실습 - ②

- ATmega128A SCL/SDA 핀 → I2C 8574(PCF 8574T 칩)  → LCD Display(HD44780 칩) 경로를 통해 ATmega128내의 I2C 통신으로 LCD Display에 문자열을 출력하는 실습을 2개의 글로 나누어서 실습해보겠습니다. 오늘은 PCF 8574T 칩에서 LCD Display로 데이터를 보내는 과정이 담긴 오픈 라이브러리를 해석하고 정리해보겠습니다. 😁

ATmega128A → I2C 8574칩의 데이터 처리 과정은 지난 내용을 참고 하세요!!

 

2022.02.25 - [IoT 디바이스 개발] - [IoT 디바이스 개발] AVR(ATmega128A)«수업-19» : I2C(Inter-Integrated Circuit) 통신

 

[IoT 디바이스 개발] AVR(ATmega128A)«수업-19» : I2C(Inter-Integrated Circuit) 통신

※ 주의사항 ※ 본 블로그는 수업 내용을 바탕으로 제가 이해한 부분을 정리한 블로그입니다. 본 내용을 참고로만 보시고, 틀린 부분이 있다면 지적 부탁드립니다! 감사합니다😁 안녕하세요!!

920416.tistory.com

 


2-1. clcd 헤더 파일

#ifndef CLCD_H_
#define CLCD_H_

#ifndef F_CPU
#define F_CPU 16000000L
#endif

#include "i2c.h"
#include <util/delay.h>
#include <avr/io.h>
#include <stdint.h>

void twi_init(void);
void i2c_lcd_init(void);
void i2c_lcd_command(uint8_t command);
void i2c_lcd_command_8(uint8_t command);
void i2c_lcd_data(uint8_t data);
void i2c_lcd_goto_XY(uint8_t row, uint8_t col);
void i2c_lcd_write_string(char string[]);

#endif

- LCD Display 초기화, 4비트(8비트) 명령어 레지스트 처리, 4비트 모드 LCD 데이터 레지스트 처리, LCD 커서 위치 이동, 문자열 데이터 출력 관련 함수 선언

 

2-2. clcd 소스 파일  

※ 자주 사용되고 필요한 함수부분만 발췌해서 간단한 설명을 하겠습니다.

 

1) LCD Display 초기화 함수

void i2c_lcd_init(void)
{
	i2c_init();
	_delay_ms(50);
	
	i2c_lcd_command_8(0x30); _delay_ms(5);
	i2c_lcd_command_8(0x30); _delay_us(100);
	i2c_lcd_command_8(0x30); _delay_us(100);
	i2c_lcd_command_8(0x20); _delay_us(100);
	
	i2c_lcd_command(0x28); _delay_us(50);
	i2c_lcd_command(0x08); _delay_us(50); //display on/off conrol 
	i2c_lcd_command(0x01); _delay_ms(3); // clear display
	i2c_lcd_command(0x06); _delay_us(50);
	i2c_lcd_command(0x0c); _delay_us(50); // display on , cursor & cursor blink off
}

- LCD Display 초기화 관련 내용은 DataSheet를 참고하였다.

※ LCD Display 4-Bit Mode 초기화 과정

LCD Display 초기화 과정(4비트)

(1) 전원 투입 후, 약 15ms 대기 한다.

(2) '3'의 값을 3번 전송후 일정한 시간을 대기한다.(4.1ms, 100μs)

(3) Function Set

데이터 비트 처리는 4비트, 2행, 5 x 8폰트의 크기로 설정한다. (0x28)

(4) Display, Cursor, Cursor Blink ALL OFF (0x08);

(5) Clear Display

(6) Entry Mode Set 

화면과 커서 이동방향 지정

(7) Display ON, Cursor, Cusor Blink OFF

 

2) 4비트 명령어 처리 함수

void i2c_lcd_command_8(uint8_t command)
{
	uint8_t c_buf[4];
	
	c_buf[0] = (command&0xf0) | RS0_EN1 | Backlight;
	c_buf[1] = (command&0XF0) | RS0_EN0 | Backlight;
	
	i2c_transmit_nbytes(I2C_addr_PCF8574,c_buf,2);
}

- 4Bit의 명령어를 I2C 통신을 통해 1Byte(8bit)씩 전송한다.

 

※ 의문사항 : 4비트 데이터 뒤에 RS0_EN1, RS0_EN0 | Backlight이 붙는 이유는 무엇인가??

- 문자열 데이터는 4bit로 나누어져 4bit의 제어신호와 함께 PCF I2C 칩을 통해 1Byte씩 전송한다.

4bit Data BackLight EN(Enable) R/W(Read/Write) RS(Register)

- RS0_EN1 / RS0_EN0이 의미하는 내용은 명령어 레지스트를 선택하고 Enable을 HIGH와 LOW를 줌으로써 PCF에서 LCD를 동작시키도록 한다.

- 즉, ASCII Code의 0xAB인 문자를 출력할때는 0XA0과 0XB0을 나누어서 제어신호(4bit)와 함께 1바이트씩 총 4바이트의 신호를 I2C를 통해 전달한다.

 

3) 8비트 명령어(데이터) 처리 함수

void i2c_lcd_command(uint8_t command)
{
	uint8_t c_buf[4];
	
	c_buf[0] = (command&0xf0) | RS0_EN1 | Backlight;
	c_buf[1] = (command&0XF0) | RS0_EN0 | Backlight;
	c_buf[2] = ((command<<4)&0XF0) | RS0_EN1 |Backlight;
	c_buf[3] = ((command<<4)&0XF0) | RS0_EN0 |Backlight;
	
	i2c_transmit_nbytes(I2C_addr_PCF8574,c_buf,4);
}

- 1Byte의 데이터(command 인자)를 전달받아 4비트의 데이터 + 4비트 제어신호로 결합해서 총 4Byte의 신호를 I2C를 통해 전달한다.

- RS값이 0으로 명령어 레지스트에 해당되고, I2C 통신을 위한 Slave의 주소는 PCF 8574T 칩의 후면에서 확인 가능하다.

- 명령어(RS = 0)와 데이터(RS = 1)는 RS 레지스터의 값만을 바꿔주고 동일한 방법으로 전달하면 된다.

 

4) LCD 커서 이동 함수

void i2c_lcd_goto_XY(uint8_t row, uint8_t col)
{
	uint8_t address = (0x40 * row) + col;
	uint8_t command = 0x80 | address;
	
	i2c_lcd_command(command);
}

- LCD 모듈 명령어의 Set DDRAM address를 사용한 LCD Cursor 이동 관련 함수

 

※ 추후 clcd.h와 clcd.c파일을 라이브러리로 저장해서 i2c.h, i2c.c 파일과 같이 I2C 통신이나 LCD Display를 사용할때 사용하겠습니다.

 

2-3. LCD 출력 실습

- i2c_lcd_write_string 함수를 통해 문자열을 LCD Display에 표시

- i2c_lcd_goto_XY 함수를 통해 이동하고자 하는 좌표로 Cursor를 이동 하였다.


 

3. I2C 통신으로 초음파 센서 값을 LCD로 출력

3-1. 초음파 센서 사용 방법 및 원리

- TRIG에서 10μs만큼의 HIGH신호를 발생하면 ECHO가 HIGH가 되고, TRIG에서 발생한 초음파사 물체에 부딪혀 반사되어 다시 돌아오면 LOW가 된다.

- 즉 ECHO 신호가 HIGH에서 LOW가 되는 시간을 측정해서 물체와의 거리를 확인한다.

- 거리 측정 방법 : (초음파 속도(340 m/s) * 걸린 시간) / 2 (왕복이 아닌 편도 길이 측정)

※ 초음파가 방사된 후 38ms동안 돌아오지 않는다면 LOW가 된다.

 

3-2. I2C 통신으로 초음파 센서 값을 LCD로 출력 실습

 

1) 초음파 거리 측정 함수부

void Timer_init(void)
{
	TCCR1B |= (1<<CS12) | (1<<CS10); //1024 분주비 설정
}

uint8_t measure_distance(void)
{
	// TRIG 핀에서 LOW >> HIGH >> LOW 신호 송출
	PORTB &= ~(1<<PB1);
	_delay_us(1);
	PORTB |= (1<<PB1);
	_delay_us(10);
	PORTB &= ~(1<<PB1);
	
	//에코 핀이 HIGH 수신 대기
	TCNT1 = 0;
	while(!(PINB & 0X01))
		if(TCNT1 > 65000) return 0;	
	
	//에코 핀이 LOW 될떄 까지의 시간 측정
	TCNT1=0;
	while (PINB & 0X01) {
		if(TCNT1 > 65000){ 
			TCNT1 = 0;
			break;
		}
	}
	//에코 핀의 펄스 폭을 마이크로초 단위로 계산
	double pulse_width = 1000000.0 * TCNT1 * PRESCALER / F_CPU;
	return (pulse_width / 58); // 센티미터 단위로 거리 반환
}

- TIMER1에 1024의 분주비를 설정함과 동시에 TIMER1 동작 수행

- PORTB의 1번핀에 연결되어있는 TRIG 핀에 10μs동안 신호 출력

- ECHO핀이 HIGH에서 LOW가 되는데 까지 걸리는 시간(TCNT1)을 측정

- ECHO의 HIGH Pulse Width값을 통해 물체와의 거리를 계산 

 

※ TCNT1 > 65000 ???

- 1024분주 타이머로 65000펄스는 4.16s에 해당된다. 초음파의 속도로 4.16s에 해당되는 거리는 707.2m이다.

(340m/s * 4.16(s) / 2) = 707.2

- 즉, 1번 타이머로는 오버플로 없이 약 700m 까지의 초음파 거리를 측정할 수 있는데 실습에 사용되는 HC-SR04 센서는 최대 4m까지 측정 가능한 센서이다. 따라서 설정값이 지나치게 높게 되어있고, 4m에 해당되는 TCNT1의 값은 367이다. 

 

2) 메인 함수부

int main(void)
{
	uint8_t distance;
	char dinstance_char[4];
	stdout = &OUTPUT;
	stdin = &INPUT;
	
	UART0_init();
	i2c_lcd_init();
	
	DDRB |= 0X02; //트리거핀 출력
	DDRB &= 0XFE; // 에코핀 입력
	
	Timer_init();
	i2c_lcd_write_string("Measure Start!");
	_delay_ms(1000);
	i2c_lcd_command(0x01); //lcd clear;
	i2c_lcd_goto_XY(0,0);
	i2c_lcd_write_string("Distance : ");
	i2c_lcd_goto_XY(1,14);
	i2c_lcd_write_string("CM");
	
	while (1)
	{	
		distance = measure_distance(); // 거리 측정
		// 3자리수를 지정 안해주면 3자리에서 2자리로 변경될때 잔상이 남는다.
		sprintf(dinstance_char, "%3d", distance); 		
		i2c_lcd_goto_XY(1,10);
		i2c_lcd_write_string(dinstance_char);
		_delay_ms(1000);
			}
	return 0;
}

- sprintf 함수를 통해 정수형 데이터를 문자열 데이터로 변경(유용한 함수로 기억해두면 좋을듯!!)

- sprintf의 파라메터 중 가운데의 값을 "%3d"로 지정한 이유는 물체간의 거리가 1cm ~ 200cm까지 가변하기 때문에 3자리에 해당되는 값을 계속 변경시켜 주기 위해서.(%3d로 안하고 %d로 하면 3자리에서 2자리로 거리가 변할때 백의자리에 해당되는 숫자가 없어지지 않고 그대로 남아있게된다.)

 

- HC-SR04의 Datasheet상에는 최대 측정 가능 거리가 4m라고 하지만 실제 실습으로는 최대 200cm까지 밖에 안되었습니다...😥

댓글