'시계'에 해당되는 글 2건
- 2019.11.19 Hardware | LCD2004 를 arduino 로 컨트롤 해보기 - 2
- 2018.11.11 Hardware | RTC DS3231 부품 사용기 - 1 2
이 글은, 먼저 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 |
이 포스트는 DS3231 에 대한 이야기 이며, 후속편에 이어집니다.
* Hardware | RTC DS3231 부품 사용기 - 2
- http://chocoball.tistory.com/entry/Hardware-RTC-usning-DS3231-2
1. RTC
보통 internet 이 달린 기기라면 NTP Server 와 연동하여 시간을 맞추고,
특정 시간에 정확하게 일을 시킬 수가 있습니다.
Internet 에 연결되지 않은 기기의 경우는 다음과 같은 과정이 필요하겠죠.
A) 시간을 설정한다.
B) 시간을 기억한다.
C) 특정 시간에 일을 시킨다.
Internet 이 연결되지 않은 기기와 비교해 보면 B) 항목이 필요합니다.
이 "시간을 기억" 하고, 언제든지 현재 시간 정보를 가져올 수 있는 부품이 DS3231 입니다.
그래서 이번에는 DS3231 을 구입하고 사용해 보겠습니다.
자세한 내용은 아래 Arduino 사이트를 참조해 보세요.
* RTC Library
- https://www.arduino.cc/en/Reference/RTC
2. 구입
말할것도 없이 AliExpress 입니다.
배송까지 40일 걸렸습니다. 이정도 되면 배송에 대해서는 해탈해야 합니다.
* 1PCS DS3231 AT24C32 IIC Precision RTC Real Time Clock Memory Module For Arduino new original
건전지 미포함 1.06 USD 무료배송이면 고민거리는 아닙니다.
(배송기간 빼고)
인터넷을 뒤지니, 위의 부품으로 거의 통일되어 있는것 같았습니다.
3. 도착
요로코롬 도착했습니다.
부품 확대 사진입니다.
메인 chip 에 DS321 이라고 적혀 있네요.
발진기인 오실레이터도 보이고, 밑에는 AT24C32 EEPROM (32Kb) 도 있습니다.
참고로 DS3231 칩 안에는 추가로 온도센서도 존재합니다.
- Data sheet : DS3232.pdf
특이하게 "왜 온도센서?" 냐 하면,
전자 발진기 - 오실레이터 - 는 온도에 따라 그 값이 변합니다.
그래서 온도에 따른 변화를 보정하기 위해 온도센서가 자리잡고 있는 것이지요.
뒷면에는 CR2032 버튼 전지를 끼울 수 있는 플라스틱이 존재합니다.
이는 전원이 차단되더라도 "시간을 기억" 하기고 있기 위한 것이지요.
이 건전지 한개로 몇년은 쓴다고 하네요.
4. Layout
배선은 일반 IIC 배선과 동일합니다.
SLA 은 A4에, SCL은 A5 이죠.
DS3231 | Arduino Nano ------------------------- VCC | 3.3V GND | GND SDC | A5 SDA | A4 ------------------------- SSD1306 | Arduino Nano ------------------------- VCC | 3.3V GND | GND SDC | A5 SDA | A4 -------------------------
실재 배선 모양입니다.
I2C의 특성상, 다른 센서 / 부품들 중, I2C 방식이면 arduino 의 동일한 pin 에 꼽아도 따로 인식 됩니다.
이는 각 device 가 가지는 address 가 다르기 때문이지요.
이는 I2C Scanner 를 이용해서 살펴보면, 각각 따로 인식하는 것을 알 수 있습니다.
아래에서 0x3c 는 OLED 이고, 0x68 이 DS3231 입니다.
추가로 나오는 0x57 은 AT24C32 EEPROM 입니다.
여기서 이상한 점은 0x5f 라는 address 입니다.
무얼까... 답을 찾지는 못했지만, 찾는 와중에 한가지 새로운 사실을 알게 됩니다.
5. EEPROM
EEPROM 으로는 ATmega 사의 AT24C32 이 쓰입니다.
이것의 실제 chip 번호는 ATML332 라고 적혀 있습니다.
- https://www.kynix.com/Detail/447536/ATMLH745.html
그렇습니다. EEPROM 이 original 이 아니고 fake 제품인 것이죠.
그러나 제품 구동은 정상적으로 돌아갑니다.
100% original chip 과 동일하지 않기 때문에 보다 복잡한 작업을 시키면 정상적으로 동작하지 않을 지도 모릅니다.
여튼, 앞으로 싼 부품은 좀 걸러야 할지도 모르겠네요.
6. Library 등록
인터넷에 돌아다니는 source 를 등록해서 사용할 수 있지만,
IDE 에서 지원해주는 Library 등록 기능을 이용하여 Example source 를 등록해 봅니다.
우선 IDE 메뉴에서 "Sketch > Add File... > Manage Libraries..." 를 선택합니다.
이건 이제 매번 써먹는 방법이지요?
Libarry Manager 의 검색창에서 "ds3231" 을 쳐서 검색합니다.
그러면 여러가지 source 들이 나오는데, 왠만하면 제일 위에 나오는 것을 선택하면 됩니다.
아래 그림처럼 adafruit 에서 만든 library 이니, 쓸만 할껍니다.
이렇게 하면 IDE 메뉴의 "File > Examples > DS3231" 항목이 생기고 sample source 를 이용할 수 있습니다.
6. sketch - 시간 설정
인터넷에 돌아다니는 source 를 등록해서 사용할 수 있지만,
위에서처럼 Arduino IDE 의 Library Manager 를 통해서 얻은 소스를 활용해 봅니다.
일단 시간을 입력합니다.
소스에 보이듯이 Serial Monitor 에 "YYMMDDwHHMMSS" 를 넣고, 마지막에 "x" 를 붙이면 설정됩니다.
------------------------------------------------
YYMMDDwHHMMSS, with an 'x' at the end
------------------------------------------------
/* Sets the time and prints back time stamps for 5 seconds Based on DS3231_set.pde by Eric Ayars 4/11 Added printing back of time stamps and increased baud rate (to better synchronize computer and RTC) Andy Wickert 5/15/2011 */ #include "DS3231.h" #include "Wire.h" DS3231 Clock; byte Year; byte Month; byte Date; byte DoW; byte Hour; byte Minute; byte Second; bool Century=false; bool h12; bool PM; void GetDateStuff(byte& Year, byte& Month, byte& Day, byte& DoW, byte& Hour, byte& Minute, byte& Second) { // Call this if you notice something coming in on // the serial port. The stuff coming in should be in // the order YYMMDDwHHMMSS, with an 'x' at the end. boolean GotString = false; char InChar; byte Temp1, Temp2; char InString[20]; byte j=0; while (!GotString) { if (Serial.available()) { InChar = Serial.read(); InString[j] = InChar; j += 1; if (InChar == 'x') { GotString = true; } } } Serial.println(InString); // Read Year first Temp1 = (byte)InString[0] -48; Temp2 = (byte)InString[1] -48; Year = Temp1*10 + Temp2; // now month Temp1 = (byte)InString[2] -48; Temp2 = (byte)InString[3] -48; Month = Temp1*10 + Temp2; // now date Temp1 = (byte)InString[4] -48; Temp2 = (byte)InString[5] -48; Day = Temp1*10 + Temp2; // now Day of Week DoW = (byte)InString[6] - 48; // now Hour Temp1 = (byte)InString[7] -48; Temp2 = (byte)InString[8] -48; Hour = Temp1*10 + Temp2; // now Minute Temp1 = (byte)InString[9] -48; Temp2 = (byte)InString[10] -48; Minute = Temp1*10 + Temp2; // now Second Temp1 = (byte)InString[11] -48; Temp2 = (byte)InString[12] -48; Second = Temp1*10 + Temp2; } void setup() { // Start the serial port Serial.begin(57600); // Start the I2C interface Wire.begin(); } void loop() { // If something is coming in on the serial line, it's // a time correction so set the clock accordingly. if (Serial.available()) { GetDateStuff(Year, Month, Date, DoW, Hour, Minute, Second); Clock.setClockMode(false); // set to 24h //setClockMode(true); // set to 12h Clock.setYear(Year); Clock.setMonth(Month); Clock.setDate(Date); Clock.setDoW(DoW); Clock.setHour(Hour); Clock.setMinute(Minute); Clock.setSecond(Second); // Give time at next five seconds for (int i=0; i<5; i++){ delay(1000); Serial.print(Clock.getYear(), DEC); Serial.print("-"); Serial.print(Clock.getMonth(Century), DEC); Serial.print("-"); Serial.print(Clock.getDate(), DEC); Serial.print(" "); Serial.print(Clock.getHour(h12, PM), DEC); //24-hr Serial.print(":"); Serial.print(Clock.getMinute(), DEC); Serial.print(":"); Serial.println(Clock.getSecond(), DEC); } } delay(1000); }
시각을 입력하니 잘 등록되고 읽어집니다.
7. sketch - 시간 설정 + 시간 가져오기 + 온도
기본 sample 을 사용해도 되지만, 찾아다니면서 하나로 된 소스는 아래인것 같습니다.
Serial Monitor 의 입력창에 "T1124154091014" 등으로 입력하면 시각이 입력되면서,
그냥 와두면, "시간 + 온도" 를 표시해 준다.
* Tutorial – Using DS1307 and DS3231 Real-time Clock Modules with Arduino
#include "Wire.h" #define DS3231_I2C_ADDRESS 104 // SCL - pin A5 // SDA - pin A4 // To set the clock, run the sketch and use the serial monitor. // Enter T1124154091014; the code will read this and set the clock. See the code for full details. byte seconds, minutes, hours, day, date, month, year; char weekDay[4]; byte tMSB, tLSB; float temp3231; void setup() { Wire.begin(); Serial.begin(9600); } void loop() { watchConsole(); get3231Date(); Serial.print(weekDay); Serial.print(", "); Serial.print(date, DEC); Serial.print("/"); Serial.print(month, DEC); Serial.print("/"); Serial.print(year, DEC); Serial.print(" - "); Serial.print(hours, DEC); Serial.print(":"); Serial.print(minutes, DEC); Serial.print(":"); Serial.print(seconds, DEC); Serial.print(" - Temp: "); Serial.println(get3231Temp()); delay(1000); } // Convert normal decimal numbers to binary coded decimal byte decToBcd(byte val) { return ( (val/10*16) + (val%10) ); } void watchConsole() { if (Serial.available()) { // Look for char in serial queue and process if found if (Serial.read() == 84) { //If command = "T" Set Date set3231Date(); get3231Date(); Serial.println(" "); } } } void set3231Date() { //T(sec)(min)(hour)(dayOfWeek)(dayOfMonth)(month)(year) //T(00-59)(00-59)(00-23)(1-7)(01-31)(01-12)(00-99) //Example: 02-Feb-09 @ 19:57:11 for the 3rd day of the week -> T1157193020209 // T1124154091014 seconds = (byte) ((Serial.read() - 48) * 10 + (Serial.read() - 48)); // Use of (byte) type casting and ascii math to achieve result. minutes = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); hours = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); day = (byte) (Serial.read() - 48); date = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); month = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); year = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); Wire.beginTransmission(DS3231_I2C_ADDRESS); Wire.write(0x00); Wire.write(decToBcd(seconds)); Wire.write(decToBcd(minutes)); Wire.write(decToBcd(hours)); Wire.write(decToBcd(day)); Wire.write(decToBcd(date)); Wire.write(decToBcd(month)); Wire.write(decToBcd(year)); Wire.endTransmission(); } void get3231Date() { // send request to receive data starting at register 0 Wire.beginTransmission(DS3231_I2C_ADDRESS); // 104 is DS3231 device address Wire.write(0x00); // start at register 0 Wire.endTransmission(); Wire.requestFrom(DS3231_I2C_ADDRESS, 7); // request seven bytes if(Wire.available()) { seconds = Wire.read(); // get seconds minutes = Wire.read(); // get minutes hours = Wire.read(); // get hours day = Wire.read(); date = Wire.read(); month = Wire.read(); //temp month year = Wire.read(); seconds = (((seconds & B11110000)>>4)*10 + (seconds & B00001111)); // convert BCD to decimal minutes = (((minutes & B11110000)>>4)*10 + (minutes & B00001111)); // convert BCD to decimal hours = (((hours & B00110000)>>4)*10 + (hours & B00001111)); // convert BCD to decimal (assume 24 hour mode) day = (day & B00000111); // 1-7 date = (((date & B00110000)>>4)*10 + (date & B00001111)); // 1-31 month = (((month & B00010000)>>4)*10 + (month & B00001111)); //msb7 is century overflow year = (((year & B11110000)>>4)*10 + (year & B00001111)); } else { //oh noes, no data! } switch (day) { case 1: strcpy(weekDay, "Sun"); break; case 2: strcpy(weekDay, "Mon"); break; case 3: strcpy(weekDay, "Tue"); break; case 4: strcpy(weekDay, "Wed"); break; case 5: strcpy(weekDay, "Thu"); break; case 6: strcpy(weekDay, "Fri"); break; case 7: strcpy(weekDay, "Sat"); break; } } float get3231Temp() { //temp registers (11h-12h) get updated automatically every 64s Wire.beginTransmission(DS3231_I2C_ADDRESS); Wire.write(0x11); Wire.endTransmission(); Wire.requestFrom(DS3231_I2C_ADDRESS, 2); if(Wire.available()) { tMSB = Wire.read(); //2's complement int portion tLSB = Wire.read(); //fraction portion temp3231 = (tMSB & B01111111); //do 2's math on Tmsb temp3231 += ( (tLSB >> 6) * 0.25 ); //only care about bits 7 & 8 } else { //oh noes, no data! } return temp3231; }
Serial Monitor 의 결과 입니다.
8. sketch - 시간 설정 + 시간 가져오기 + 온도 + OLED
위의 소스를 조금 바꾸어 OLED 에 표시해주는 소스로 살짝 바꾸었습니다.
일주일 지난 뒤, 측정하니 여전히 잘 동작하고 있네요.
살짝 바꾼 소스 올려 봅니다.
#include "SPI.h" #include "Wire.h" #include "Adafruit_GFX.h" #include "Adafruit_SSD1306.h" Adafruit_SSD1306 display = Adafruit_SSD1306(); #define DS3231_I2C_ADDRESS 104 // SCL - pin A5 // SDA - pin A4 // To set the clock, run the sketch and use the serial monitor. // Enter T1124154091014; the code will read this and set the clock. See the code for full details. byte seconds, minutes, hours, day, date, month, year; char weekDay[4]; byte tMSB, tLSB; float temp3231; void setup() { Serial.begin(9600); display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3C (for the 128x32) display.clearDisplay(); display.display(); delay(1000); Wire.begin(); } void loop() { watchConsole(); get3231Date(); display.clearDisplay(); display.setTextColor(WHITE); display.setCursor(0,0); display.setTextSize(1); display.print("DATE : "); display.print(weekDay); display.print(", "); display.print(date, DEC); display.print("/"); display.print(month, DEC); display.print("/"); display.println(year, DEC); display.print("TIME : "); display.print(hours, DEC); display.print(":"); display.print(minutes, DEC); display.print(":"); display.println(seconds, DEC); display.print("TEMP : "); display.println(get3231Temp()); display.display(); delay(1000); } // Convert normal decimal numbers to binary coded decimal byte decToBcd(byte val) { return ( (val/10*16) + (val%10) ); } void watchConsole() { if (Serial.available()) { // Look for char in serial queue and process if found if (Serial.read() == 84) { //If command = "T" Set Date set3231Date(); get3231Date(); Serial.println(" "); } } } void set3231Date() { //T(sec)(min)(hour)(dayOfWeek)(dayOfMonth)(month)(year) //T(00-59)(00-59)(00-23)(1-7)(01-31)(01-12)(00-99) //Example: 02-Feb-09 @ 19:57:11 for the 3rd day of the week -> T1157193020209 // T1124154091014 seconds = (byte) ((Serial.read() - 48) * 10 + (Serial.read() - 48)); // Use of (byte) type casting and ascii math to achieve result. minutes = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); hours = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); day = (byte) (Serial.read() - 48); date = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); month = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); year = (byte) ((Serial.read() - 48) *10 + (Serial.read() - 48)); Wire.beginTransmission(DS3231_I2C_ADDRESS); Wire.write(0x00); Wire.write(decToBcd(seconds)); Wire.write(decToBcd(minutes)); Wire.write(decToBcd(hours)); Wire.write(decToBcd(day)); Wire.write(decToBcd(date)); Wire.write(decToBcd(month)); Wire.write(decToBcd(year)); Wire.endTransmission(); } void get3231Date() { // send request to receive data starting at register 0 Wire.beginTransmission(DS3231_I2C_ADDRESS); // 104 is DS3231 device address Wire.write(0x00); // start at register 0 Wire.endTransmission(); Wire.requestFrom(DS3231_I2C_ADDRESS, 7); // request seven bytes if(Wire.available()) { seconds = Wire.read(); // get seconds minutes = Wire.read(); // get minutes hours = Wire.read(); // get hours day = Wire.read(); date = Wire.read(); month = Wire.read(); //temp month year = Wire.read(); seconds = (((seconds & B11110000)>>4)*10 + (seconds & B00001111)); // convert BCD to decimal minutes = (((minutes & B11110000)>>4)*10 + (minutes & B00001111)); // convert BCD to decimal hours = (((hours & B00110000)>>4)*10 + (hours & B00001111)); // convert BCD to decimal (assume 24 hour mode) day = (day & B00000111); // 1-7 date = (((date & B00110000)>>4)*10 + (date & B00001111)); // 1-31 month = (((month & B00010000)>>4)*10 + (month & B00001111)); //msb7 is century overflow year = (((year & B11110000)>>4)*10 + (year & B00001111)); } else { //oh noes, no data! } switch (day) { case 1: strcpy(weekDay, "Sun"); break; case 2: strcpy(weekDay, "Mon"); break; case 3: strcpy(weekDay, "Tue"); break; case 4: strcpy(weekDay, "Wed"); break; case 5: strcpy(weekDay, "Thu"); break; case 6: strcpy(weekDay, "Fri"); break; case 7: strcpy(weekDay, "Sat"); break; } } float get3231Temp() { //temp registers (11h-12h) get updated automatically every 64s Wire.beginTransmission(DS3231_I2C_ADDRESS); Wire.write(0x11); Wire.endTransmission(); Wire.requestFrom(DS3231_I2C_ADDRESS, 2); if(Wire.available()) { tMSB = Wire.read(); //2's complement int portion tLSB = Wire.read(); //fraction portion temp3231 = (tMSB & B01111111); //do 2's math on Tmsb temp3231 += ( (tLSB >> 6) * 0.25 ); //only care about bits 7 & 8 } else { //oh noes, no data! } return temp3231; }
동영상도 올려 봅니다.
FIN
DS3231 에 대해서는 이야기 할 내용이 더 있어서 2편에서 더 다루어 보겠습니다.
'Hardware' 카테고리의 다른 글
Hardware | MAX30105 파티클 센서 - 1 (0) | 2018.11.23 |
---|---|
Hardware | RTC DS3231 부품 사용기 - 2 (0) | 2018.11.20 |
Hardware | Transistor 구매 (0) | 2018.11.10 |
Hardware | 스위치 부품 구매하기 (0) | 2018.11.09 |
Hardware | ipTIME N704BCM 무선 공유기 (2) | 2018.11.06 |