LCD
LCD란 Liquid Crystal Display로 액정 표시장치 입니다. LCD의 경우 후면에 백라이트를 두고, 전면에 액정을 두어 액정이 전기 신호에 따라 빛을 차단하거나 통과시키는 방식으로 빛을 냅니다. 소비전력이 낮아 휴대용 기기에 사용하기 좋습니다.
단점은 선명한 표시를 위해서 백 라이트가 필요하고 한글이나 특수문자른 잘 표현이 되지 않습니다.
1602 I2C LCD
1602 LCD란 세로 16칸, 가로 2칸으로 구성된 액정 표시 장치입니다. 1602 LCD는 연결을 위해서 16개의 핀으로 구성되어 있습니다.
I2C 인터페이스 (Inter-Integrated Circuit Interface)
아두이노 우노에는 14개의 디지털 입출력 핀이 14개, 아날로그 입력 핀이 6개로 총 20개의 핀이 있습니다. 우노 보드에 1602 LCD와 함께 다른 부품을 사용하고자 하면, 핀의 연결 수가 많아져 연결 포트가 충분하지 않을 수 있습니다. 이러한 문제를 해결하기 위해서 사용하는 것이 I2C 인터페이스 모듈입니다.
I2C는 풀업 저항이 연결된 SCL(Serial Clock)핀과 SDA(Serial Data)핀을 사용하여 한 방향으로 1:N = 마스터(아두이노):다수 슬레이브(센서 모듈)를 연결할 수 있는 근거리 통신 인터페이스 입니다.
아두이노는 I2C통신 핀으로 SDA, SCL핀을 사용하거나 아날로그 A4, A5 핀을 SDA,SCL 각각의 기능으로 사용할 수 있습니다.
단,아두이노 우노 보드에서는 I2C 통신 인터페이스와 A4,A5 핀을 동시에 사용할 수 없습니다.
관련 라이브러리
I2C 통신을 하기위해서는 Wire 라이브러리를 사용해야합니다. Wire Library에 대한 참고 글들은 이러 합니다.
- https://m.blog.naver.com/PostView.nhn?blogId=yuyyulee&logNo=220325361752&proxyReferer=https%3A%2F%2Fwww.google.com%2F
- https://www.arduino.cc/en/Reference/Wire
LCD를 조작하기 위해서 사용하는 라이브러리에는 LiquidCrystal과 LiquidCrystal_I2C 두가지가 일반적으로 많이 사용됩니다. 이 둘의 차이점은 LiquidCrystal은 LCD의 조작을 위해 8개의 핀을 모두 사용하지만, LiquidCrystal의 경우 I2C 모듈에 연결된 핀 2가지를 활용하는데 있습니다. LiquidCrystal_I2C 라이브러리는 LiquidCrystal 라이브러리에서 할 수 있는 것들을 모두 사용할 수 있습니다. 각각의 라이브러리에 대한 자세한 설명은 다음 글들을 참고하시기 바랍니다.
- LquidCrystal : https://www.arduino.cc/en/Reference/LiquidCrystal
- LiquidCrystal_I2C : https://github.com/johnrickman/LiquidCrystal_I2C/blob/master/LiquidCrystal_I2C.h
- 차이점 : https://forum.arduino.cc/index.php?topic=309933.0
진행하는 예제에서는 I2C 모듈을 사용하기에 LiquidCrystal_I2C 라이브러리를 활용하겠습니다.
다음 코드는 LCD 사용에 관한 예시 코드 입니다.
1.LCD 주소 스캐닝
I2C LCD 기본 주소는 0x27(16진수 값) 이지만, 간혹 모듈 사용 시 오류가 생길 경우 주소를 확인해야합니다. 사용하고자 하는 I2C LCD 인터페이스 모듈의 주소를 알아내기 위해서 주소 스캐닝을 먼저 하는 것이 좋습니다.
#include <Wire.h> // Wire 라이브러리는 I2C(혹은 TWI) 기기와 통신 할 수 있게 하는 라이브러리입니다.
void setup() {
Wire.begin(); // Master와 Slave와 같이 Wire Library와 I2C bus를 연결합니다.
// beign(address)에서 address 지정을 하지 않았으므로 Master 모드로 설정됩니다.
// Slave 모드 일경우 자신의 주소 값(address)을 지정합니다.
Serial.begin(9600); // Serial 통신을 속도 9600으로 시작합니다.
while(!Serial); // Serial 모니터를 기다립니다.
Serial.println("I2C Address Scanner\n");
}
void loop() {
byte error,address; // 데이터를 전송할 Slave의 주소 address 변수, error의 종류를 나타내 줄 error 변수
int nDevices; // 연결된 장치의 수를 저장할 변수
Serial.println("Scanning...");
nDevices = 0;
for(address = 1; address<127; address++){ // 주소는 7비트의 데이터로, 127을 넘을 수 없습니다.
Wire.beginTransmission(address); // 마스터에서 전송을 시작하기 위해서 슬레이브의 주소 값을 지정합니다.
error = Wire.endTransmission(); // 데이터 버퍼에 저장된 데이터를 전송합니다. 마스터 모드에서만 사용합니다.
// 전송이 완료된 후 전송의 성공 여부를 byte형의 값으로 반환 합니다.
if (error == 0){ // 전송이 성공한 경우 입니다.
Serial.print("I2C device found at 0x");
if(address < 16){
Serial.print("0"); // 주소 값을 16진수로 표현하기에, 16보다 낮은 수가 오면 앞자리에 0을 붙입니다.
}
Serial.print(address,HEX);
Serial.println(" !");
nDevices++; // 연결된 장치의 수를 증가 시킵니다.
}else if(error == 4){ // 알 수 없는 오류가 발생하는 경우입니다.
Serial.print("Unknown error at address 0x");
if(address < 16){
Serial.print("0");
}
Serial.println(address,HEX);
}
}
if(nDevices == 0){ // nDevices 가 0이면, 연결된 장치가 없는 것이고 0이 아니면 연결되어 있음을 나타냅니다.
Serial.println("No I2C devices found\n");
}else{
Serial.println("done\n");
}
delay(5000); // 다음 스캔까지 5초간 지연 시간을 갖습니다.
}
2.Display Scroll
LCD화면에 문자를 출력하고, 자동 스크롤 기능과, 사용자 지정 스크롤 기능에 대한 코드입니다.
#include<Wire.h>
#include<LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2); // I2C 모듈에 연결된 LCD에 대해서 초기 설정을 합니다.
// I2C 모듈의 주소는 0x27이며, 한줄에 16칸이고 2줄인 LCD를 사용합니다.
void setup() {
lcd.init(); // LCD를 초기화 해줍니다. 해당 코드 미입력시, LCD가 동작하지 않습니다.
lcd.backlight(); // Backlight를 켜줍니다.
}
void loop() {
lcd.setCursor(0,0); // LCD의 첫번째 줄, 첫번째 칸에 입력지점을 설정합니다.
// LCD의 첫번째 줄에 0~9까지 출력합니다.
for(int num = 0; num < 10; num++){
lcd.print(num); // LCD에 num을 출력합니다. print(data)는 data를 문자 그대로 출력합니다.
delay(500);
}
lcd.setCursor(16,1); // LCD의 두번째 줄, 마지막 칸에 입력지점을 설정합니다.
lcd.autoscroll(); // LCD의 자동 스크롤 기능을 킵니다.
// 이는 이전에 출력된 문자들을 모두 한칸씩 밀어냅니다. 방향은 현재의 text direction을 따릅니다.
// 기본설정은 왼쪽에서 오른쪽으로 텍스트가 써지기에 scroll방향은 왼쪽이됩니다.
// 만약 text direction이 오른쪽에서 왼쪽이면, scroll 방향은 오른쪽이 됩니다.
//LCD의 두번째 줄의 마지막에서 0~4까지 출력합니다.
for(int num = 0; num < 5; num++){
lcd.print(num);
delay(500);
}
lcd.noAutoscroll(); // LCD의 자동 스크롤 기능을 끕니다.
//출력된 문자를 한칸씩 오른쪽으로 움직입니다.
for(int positionCounter = 0; positionCounter < 4 ; positionCounter++){
lcd.scrollDisplayRight(); // 화면의 텍스트들을 모두 오른쪽으로 한칸 이동합니다.
delay(500); // 지연시간을 0.5초 갖습니다.
}
//출력된 문자를 한칸씩 왼쪽으로 6번 움직입니다.
for(int positionCounter = 0; positionCounter < 6 ; positionCounter++){
lcd.scrollDisplayLeft(); // 화면의 텍스트들을 모두 왼쪽으로 한칸 이동합니다.
delay(500); // 지연시간을 0.5초 갖습니다.
}
for(int positionCounter = 0; positionCounter < 4 ; positionCounter++){
lcd.scrollDisplayRight(); // 화면의 텍스트들을 모두 오른쪽으로 한칸 이동합니다.
delay(500); // 지연시간을 0.5초 갖습니다.
}
for(int counter = 0 ; counter < 2; counter++){
lcd.noDisplay(); // LCD 화면을 끕니다. 단, 현재 보이는 문자들의 손실은 없습니다.
delay(1000);
lcd.display(); // noDisplay()를 통해 저장한 문자들의 정보를 가지고 LCD 화면을 킵니다.
delay(1000);
}
lcd.clear(); // LCD의 화면에 보이는 것을 없애고, cussor를 왼쪽 상단에 위치시킵니다.
}
scroll을 통해 텍스트가 이동하는 걸 볼 수 있었습니다. scroll을 한 방향으로 계속 진행(기본 방향인 왼쪽으로 진행)시키면 어떨까 싶어 48번을 한방향으로 움직였을 때, 문자열이 처음의 모습으로 돌아온 뒤 추가로 왼쪽으로 몇칸 더 움직인 것을 확인 할 수 있었습니다. 그래서 몇번을 더 움직이면 처음 상태로 돌아올 수 있는지 확인 해보았습니다. 그 결과 40번을 움직이면 처음 상태로 돌아오는 것을 확인 했습니다.
결론적으로 1602 LCD 기준, LCD가 출력하는 2줄 16칸 이외에도 LCD의 내부적 확장 화면은 총 2줄 40칸의 순환임을 알 수 있었습니다.
3.Text Direction Change
LCD에 문자를 출력할 때 중간 중간 출력 방향을 바꿔 주었습니다.
#include<Wire.h>
#include<LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2); // I2C모듈의 주소인 0x27을 이용해 LCD의 초기 설정을 합니다.
char thisChar = 'a'; // LCD에 현재 출력할 문자에 대한 변수를 생성합니다.
void setup() {
lcd.init(); // LCD를 초기화 해줍니다.
lcd.backlight(); // LCD의 BackLight를 킵니다.
}
void loop() {
if(thisChar == 'f'){ // 현재 출력할 알파벳이 'e'이면, Text Direction을 (오른쪽→왼쪽)으로 바꿉니다.
lcd.rightToLeft();
}
if(thisChar == 'j'){ // 현재 출력할 알파벳이 'j'dlaus, LCD의 출력 위치를 두번 째줄 마지막칸으로 바꿉니다.
lcd.setCursor(15,1);
}
if(thisChar == 's'){ // 현재 출력할 알파벳이 's'이면, Text Direction을 (왼쪽→오른쪽)으로 바꿉니다.
lcd.leftToRight();
}
if(thisChar > 'z'){ // 현재 출력할 알파벳이 'z'를 넘어가면, Cursor를 home(왼쪽 최상단)으로 옮기고, thisChar를 a로 바꿉니다.
lcd.home();
thisChar = 'a';
}
lcd.write(thisChar); // lcd에 알파벳을 출력합니다. Write(data)는 data를 ASCII Code에 해당하는 문자로 바꾸어 출력합니다.
delay(1000); // 1초의 지연시간을 갖고 출력합니다.
thisChar++; // 현재 출력할 알파벳을 하나 증가시킵니다.
}
4.Custom Character & Serial Display
Serial 통신으로 입력한 문자와 Custom문자를 합쳐서 출력하는 코드입니다.
#include<Wire.h>
#include<LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2); //I2C모듈의 주소인 0x27을 통해 LCD 초기 설정
// Custom 문자 정의
byte smiley[8] ={
0b00000,
0b00000,
0b01010,
0b00000,
0b00000,
0b10001,
0b01110,
0b00000
};
byte sady[8] ={
0b00000,
0b00000,
0b01010,
0b00000,
0b00000,
0b01110,
0b10001,
0b00000
};
String lastString = "LCD ON"; // 최근에 입력 받은 메세지
String nowString = "Just Do It"; // 현재 입력된 메세지
void setup() {
lcd.init(); // LCD 초기화
lcd.backlight(); // LCD의 Backlight를 킵니다.
lcd.createChar(0,smiley); // 바이트 숫자 0에 새로운 문자 smiley를 저장합니다.
lcd.createChar(1,sady); // 바이트 숫자 1에 새로운 문자 sady를 저장합니다.
// createChar(num,data)num에는 byte숫자 0~7까지 총 8개의 문자를 정의할 수 있고, data는 byte 배열의 이름입니다.
Serial.begin(9600); // 시리얼 속도 9600으로 통신을 시작합니다.
lcd.setCursor(0,0); // (0,0)위치로 출력 위치 설정
lcd.print(lastString); // 이전 메세지를 출력
lcd.write(byte(1)); // 이전 메세지 뒤에 sady를 출력, byte숫자 1에 저장된 문자 출력
lcd.setCursor(0,1); // (0,1)위치로 출력위치 설정
lcd.print(nowString); // 현재 입력 받은 메세지 출력
lcd.write((byte)0); // 현재 메세지 뒤에 smiley출력, byte숫자 0에 저장된 문자 출력
}
void loop() {
if(Serial.available()){ // Serial Communication이 도착하면 실행
// Serial.available은 Serial Receive Buffer에 저장된 수신 데이터들의 바이트 수를 반환 합니다.
delay(100); // 전체 메세지가 도착할 때까지 지연
lastString = nowString; // 이전의 현재 메세지를 이전 메세지로 저장
nowString = Serial.readString(); // Serial에 입력한 메세지를 현재 메세지로 저장
Serial.println(nowString); // Serial Monitor에 입력한 메세지를 출력합니다.
lcd.clear(); // lcd화면 지우기
lcd.setCursor(0,0); // (0,0)위치로 출력 위치 설정
lcd.print(lastString); // 이전 메세지를 출력
lcd.write(byte(1)); // 이전 메세지 뒤에 sady를 출력, byte숫자 1에 저장된 문자 출력
lcd.setCursor(0,1); // (0,1)위치로 출력위치 설정
lcd.print(nowString); // 현재 입력 받은 메세지 출력
lcd.write((byte)0); // 현재 메세지 뒤에 smiley출력, byte숫자 0에 저장된 문자 출력
}
}