이 글은, 먼저 LCD 20x4 를 처음 사용해 본 글의 후편입니다.
* Hardware | LCD2004 를 arduino 로 컨트롤 해보기 - 1
- https://chocoball.tistory.com/entry/Hardware-LCD2004-arduino-control-1
1. 이론적 배경
LCD2004 및 HD44780 을 사용하는 LCD 에 대해서는 아래 웹사이트에서 거의 완벽하게 설명하고 있습니다.
* Arduino with HD44780 based Character LCDs
- http://www.martyncurrey.com/arduino-with-hd44780-based-lcds/
위의 글은 전반적인 이야기 이고, 아래에 보이는 글 두 개는, 커스텀 글짜에 대한 좀 더 자세한 이야기 입니다.
* How to generate and display self made Custom characters on 16×2 lcd
- https://www.engineersgarage.com/knowledge_share/making-custom-characters-on-16x2-lcd/
An internal CG-RAM(character generated ram) in which we can generate or place our custom character. CG-RAM size is 64 bytes. We can generate/place 8 characters of size 5×8 at a time in CG-RAM.
* Making and displaying Custom characters on lcd with Arduino Uno and 16×2 lcd
- https://www.engineersgarage.com/arduino/making-custom-characters-on-lcd-using-arduino/
CG-RAM is the main component in making custom characters. CG stands for custom generated and RAM you all know random access memory. This CG-RAM stores our custom characters once we declare them in our code. I will come on it later. As you know once we write any type of code we need a memory to store it and a controller to run it. In the 16×2 lcd custom character case its same. We write code(arrays) of character’s which we want to display on lcd. Then we store them in a memory on lcd. In our case this memory is named as CG-RAM(Character generated RAM).
CG-RAM size is 64 Bytes. You can create 8 characters at a time and load them in cg-ram. Each character occupies 8-bytes. Eight characters each of eight byte (8-characters * 8-Bytes) is equal to 8×8=64 Bytes. CG-RAM address in lcd memory starts from 0x40(Hexadecimal) or 64 in decimal.
위의 글들을 요약하자면 다음과 같습니다.
- HD44780 을 사용하는 LCD 에서는 커스텀 글자를 만들 수 있다.
- 64 Bytes 메모리 한계로, 커스컴 글자는 8개까지만 가능.
- 어드레스는, 0x00~0x07 로 접근 가능.
2. 한땀 한땀 만들어 보기
Custom Character 제작 시, 어떤 형식으로 메모리에 올리는지를 자동으로 소스까지 생성해 주는 페이지 입니다.
이 페이지에서, 8x5 형상에 원하는 모양을 마우스로 클릭해 보면, 대충 감을 잡을 수 있습니다.
* LCD Custom Character Generator
- http://maxpromer.github.io/LCD-Character-Creator/
위의 페이지를 따라해 보면, Parallel 연결 + Binary 조합으로 소스 작성은 다음과 같이 됩니다.
대문자 B 로 시작하여, 각 cell 값에 따라 도식처럼 보여줍니다.
이 블로그의 마스코트인 초코볼을 본따 봤습니다. (도트의 한계로 거의 뭐...)
Hexadecimal 로 표현할 때에는, 5자리 2진수를 Hex 코드로 만들어 줍니다. 예를 들면 아래처럼요...
B10111 = 0x17
내 머리로 계산하지 않더라도 Hexadecimal 인 "0x..." 로 표현할 수 있습니다.
코드적으로 보면, 아무래도 Hex 를 사용하는 것이 코드상 더 간결하게 됩니다.
Arduino 연결을 I2C 로 바꾸어 주면,
include 구문과 LiquidCrystal_I2C 를 I2C 접근 주소와 함께 선언할 수 있도록 소스가 추가됩니다.
소스까지 생성해 주니, 이해하기 쉽습니다.
위의 페이지를 통하여, custom character 는 어떻게 해서 생성되는 지를 감각적으로 배워볼 수 있네요.
3. 끝판 왕
한땀 한땀이 아니라, 8개 생성 가능한 메모리를 한번에 만들 수 있는 소개 자료 입니다.
* Alphanumeric LCD HD44780 - big font, big digits generator (excel sheet)
- https://www.avrfreaks.net/forum/alphanumeric-lcd-hd44780-big-font-big-digits-generator-excel-sheet
EXCEL 을 이용하여, 마우스 클릭만으로 폰트를 제작할 수 있게 되어 있습니다.
* dblachut/LCD-font-generator
- https://github.com/dblachut/LCD-font-generator
Custom Character 는 8개까지 가능하니,
all 흑백, all 흰색을 제외하면 7개를 만들고, 추가적으로 하나 더 만들 수 있는 여유가 있습니다.
4. 메모리 들여다 보기
아래 장표는 HD44780 에 기억되어 있는 코드들 입니다.
왼쪽이 아시아 버전 (일본어 가다가나가 미리 들어가 있슴) 과, 오른쪽이 유럽 버전 (뭔가 더 다채로움) 입니다.
그럼, 실제로 커스텀 글짜를 입력해 보고, 메모리에 어떤 문자가 기억되어 있는지 알아 볼 수 있는 소스가 있습니다.
해당 소스를 사용하려면 아래처럼, 관련 Library 를 인스톨 하면 됩니다.
Tools > Manage Libraries... > LiquidCrystal I2C by Frank de Brabander
위의 library 를 인스톨 하면, 예제코드를 받을 수 있습니다.
File > Examples > LiquidCrystal I2C > CustomChars
소스는 다음과 같습니다.
//YWROBOT //Compatible with the Arduino IDE 1.0 //Library version:1.1 #include "Wire.h" #include "LiquidCrystal_I2C.h" #if defined(ARDUINO) && ARDUINO >= 100 #define printByte(args) write(args); #else #define printByte(args) print(args,BYTE); #endif uint8_t bell[8] = {0x4,0xe,0xe,0xe,0x1f,0x0,0x4}; uint8_t note[8] = {0x2,0x3,0x2,0xe,0x1e,0xc,0x0}; uint8_t clock[8] = {0x0,0xe,0x15,0x17,0x11,0xe,0x0}; uint8_t heart[8] = {0x0,0xa,0x1f,0x1f,0xe,0x4,0x0}; uint8_t duck[8] = {0x0,0xc,0x1d,0xf,0xf,0x6,0x0}; uint8_t check[8] = {0x0,0x1,0x3,0x16,0x1c,0x8,0x0}; uint8_t cross[8] = {0x0,0x1b,0xe,0x4,0xe,0x1b,0x0}; uint8_t retarrow[8] = { 0x1,0x1,0x5,0x9,0x1f,0x8,0x4}; LiquidCrystal_I2C lcd(0x27, 20, 4); // set the LCD address to 0x27 for a 16 chars and 2 line display void setup() { lcd.init(); // initialize the lcd lcd.backlight(); lcd.createChar(0, bell); lcd.createChar(1, note); lcd.createChar(2, clock); lcd.createChar(3, heart); lcd.createChar(4, duck); lcd.createChar(5, check); lcd.createChar(6, cross); lcd.createChar(7, retarrow); lcd.home(); lcd.print("Hello world..."); lcd.setCursor(0, 1); lcd.print(" i "); lcd.printByte(3); lcd.print(" arduinos!"); delay(5000); displayKeyCodes(); } // display all keycodes void displayKeyCodes(void) { uint8_t i = 0; while (1) { lcd.clear(); lcd.print("Codes 0x"); lcd.print(i, HEX); lcd.print("-0x"); lcd.print(i+16, HEX); lcd.setCursor(0, 1); for (int j=0; j<16; j++) { lcd.printByte(i+j); } i+=16; delay(4000); } } void loop() { }
위의 소스를 arduino 에 올리고, LCD 20x4 와 I2C 연결해서 얻은 결과 입니다.
Custom Character 가 잘 올라가서 표현되었습니다.
0x00 부터 0xff 까지, 내부 글짜를 모두 훑는 결과는 다음과 같습니다.
확실히 아시아 버전임을 알 수 있네요.
버전이 다르지만, 구동은 같은 HD44780 칩 이므로, 해킹을 통하여 비어있는 address 에도 쓰기가 가능할 것 같은데,
복잡할 듯 하여 그만 두기로 합니다.
5. 큰 폰트
여기까지 해 보면, 큰 폰트 사용하여 시계 등을 표현하는 방법을 대강 생각해 낼 수 있습니다.
이 글의 원래 목적이었던, 아래 그림처럼 표현해 보고자 함이었으니, 이제 커스텀 폰트를 만들어 봅니다.
위의 숫자 들을 잘 살펴 보면, 몇 가지 모양을 조합하여 큰 폰트를 만들어 낸 것을 알 수 있습니다.
유니크한 영역을 살펴 보면, 아무것도 적혀있지 않은 모양까지 포함하면, 모두 8개가 됩니다.
이렇게 딱 맞게 만들다니...
열씸히 만들어 보려고 여러가지 검색하던 중, 좋은 소스를 발견합니다.
* 4-Line LCD Big Numbers
- http://woodsgood.ca/projects/2015/02/27/4-line-lcd-big-numbers/
Software RTC 를 이용하여, 시간 데이터를 가져오고, custom / big font 로 시계를 표현해 주는 소스 입니다.
//************************************************************ // BIG FONT (4-line) LCD CHARACTERS // Adrian Jones, February 2015 //************************************************************ // Build 1 // r1 150214 - initial build with glyphs and big font character table in program memory // r2 150227 - added RTC support //************************************************************ #define build 1 #define revision 2 //************************************************************ #include "avr/pgmspace.h" // for memory storage in program space #include "Wire.h" #include "LiquidCrystal_I2C.h" // library for I@C interface LiquidCrystal_I2C lcd(0x27, 20, 4); const char custom[][8] PROGMEM = { {0x00, 0x00, 0x00, 0x00, 0x01, 0x07, 0x0f, 0x1f}, // char 1: top left triangle {0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x1f, 0x1f}, // char 2: upper block {0x00, 0x00, 0x00, 0x00, 0x10, 0x1c, 0x1e, 0x1f}, // char 3: top right triangle {0x1f, 0x0f, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00}, // char 4: bottom left triangle {0x1f, 0x1e, 0x1c, 0x10, 0x00, 0x00, 0x00, 0x00}, // char 5: bottom right triangle {0x1f, 0x1f, 0x1f, 0x1f, 0x00, 0x00, 0x00, 0x00}, // char 6: bottom block {0x1f, 0x1f, 0x1e, 0x1c, 0x18, 0x18, 0x10, 0x10} // char 7: full top right triangle // room for another one! }; const char bn[][30] PROGMEM = { // organized by row // 0 1 2 3 4 5 6 7 8 9 {0x01,0x02,0x03, 0x01,0x02,0xFE, 0x01,0x02,0x03, 0x01,0x02,0x03, 0x02,0xFE,0x02, 0x02,0x02,0x02, 0x01,0x02,0x03, 0x02,0x02,0x02, 0x01,0x02,0x03, 0x01,0x02,0x03}, {0xff,0xfe,0xff, 0xFE,0xFF,0xFE, 0x01,0x02,0xFF, 0xFE,0x02,0xFF, 0xFF,0x02,0xFF, 0xFF,0x02,0x02, 0xFF,0x02,0x03, 0xFE,0x01,0x07, 0xFF,0x02,0xFF, 0xFF,0xFE,0xFF}, {0xff,0xfe,0xff, 0xFE,0xFF,0xFE, 0xFF,0xFE,0xFE, 0xFE,0xFE,0xFF, 0xFE,0xFE,0xFF, 0xFE,0xFE,0xFF, 0xFF,0xFE,0xFF, 0xFE,0xFF,0xFE, 0xFF,0xFE,0xFF, 0x04,0x06,0xFF}, {0x04,0x06,0x05, 0xFE,0x06,0xFE, 0x06,0x06,0x06, 0x04,0x06,0x05, 0xFE,0xFE,0x06, 0x04,0x06,0x05, 0x04,0x06,0x05, 0xFE,0x06,0xFE, 0x04,0x06,0x05, 0xFE,0xFE,0x06} }; byte col, row, nb=0, bc=0; // general byte bb[8]; // byte buffer for reading from PROGMEM #include "RTClib.h" RTC_Millis RTC; byte hr, mn, se, osec; //*********************** INITIAL SETUP *********************** void setup() { randomSeed(analogRead(0)); RTC.begin(DateTime(__DATE__, __TIME__)); lcd.init(); // initialize the LCD lcd.backlight(); lcd.begin(20, 4); for (nb=0; nb<7; nb++ ) { // create 8 custom characters for (bc=0; bc<8; bc++) bb[bc]= pgm_read_byte( &custom[nb][bc] ); lcd.createChar( nb+1, bb ); } lcd.clear(); lcd.setCursor(4, 0); lcd.print(F("4-Line LARGE")); lcd.setCursor(4, 1); lcd.print(F("TIME DISPLAY")); lcd.setCursor(5, 3); lcd.print(F("V")); lcd.print(build); lcd.print(F(".")); lcd.print(revision); lcd.print(F(" ")); lcd.print(freeRam()); lcd.print(F("B")); printNum(random(0,10),0); printNum(random(0,10),17); delay(5000); lcd.clear(); } //************************* MAIN LOOP ************************ void loop() { DateTime now = RTC.now(); hr = now.hour(); mn = now.minute(); se = now.second(); if(se != osec) { printNum(hr/10,0); printNum(hr%10,3); printColon(6); printNum(mn/10,7); printNum(mn%10,10); printColon(13); printNum(se/10,14); printNum(se%10,17); osec = se; } delay(50); // not strictly necessary } ...
소스가 좀 길어서, SyntaxHighLighter 가 커버하지 못합니다. 따로 소스파일을 올립니다.
참고로, 사이트에 올라와 있는 소스 그대로 사용할 수가 없습니다.
제작된지 시간도 좀 흘렀고, 사용한 library 가 조금 다른 것이 원인일 듯 하네요.
그래서 소스에서 몇가지 살짝 수정하였습니다.
lcd.init(); // initialize the LCD lcd.backlight(); lcd.begin(20, 4);
debug 시, 특히 가장 시간을 많이 잡아 먹은 부분은, 위의 LCD 초기화 부분이었습니다.
원 소스에는 위의 부분이 없거나, 실행 위치가 달라 그대로 사용하면 이상한 폰트들을 마구 뿌립니다.
특히, "lcd.init()" 는 custom character 를 메모리에 입히는 전단계에서 실행되어야 합니다.
추가로, Software RTC 라이브러리가 필요합니다.
Tools > Manage Libraries... > RTClib by Adafruit
모든 준비가 완료되면 arduino 에 업로드 합니다.
역시 삽질 끝에 맛보는 행복. 동영상도 올려봅니다.
참고로, 다른 custom character 들은 숫자들 끼리 공유가 되지만, 7 숫자의 중간 꺾이는 부분에 할당된 character 는 7에서만 사용됩니다.
위의 동영상에서 7 나오는 부분을 잘 보시면 됩니다.
7에서만 사용되는 문자 형상은 위와 같습니다. 공유되기 애매한 모양이죠?
메모리 한개가 여유 있으니, 소스 원작자가 여유를 부린 듯 합니다 :-)
사무실 책상 위에, laptop 옆에 위치 시켰더니, 가독성도 좋고 시간 보는 재미가 있습니다.
사진에는 잘 표현되지 못했지만, 검은색 글씨라 눈도 편안합니다.
모두 Happy Arduino~!
'Hardware' 카테고리의 다른 글
Hardware | ebook 크레마 사운드 액정 수리기 - 2 (2) | 2019.11.21 |
---|---|
Hardware | ebook 크레마 사운드 액정 수리기 - 1 (0) | 2019.11.20 |
Hardware | Arduino MEGA 2560 를 DIY 해보자 - 1 (4) | 2019.11.13 |
Hardware | Arduino Gemma 를 DIY 해보자 (2) | 2019.11.07 |
Hardware | 블랙박스 아이머큐리 TOPAZ 수리기 (5) | 2019.11.01 |