//I2C 버스 인터페이스를 사용하기위해 Wire.h를 포함시킵니다.
//RTC 사용을 위해 DS3231.h를 포함시킵니다.
//Dotmatrix LED Display 사용을 위해 LedControl.h를 포함시킵니다.
#include <Wire.h>
#include <DS3231.h>
#include <LedControl.h>
//LED 연결핀을 정의합니다. LedControl()함수로 객체를 생성할 때 사용합니다.
//MAX_DEVICE는 8x8 LED 모듈의 갯수입니다. LED_TYPE은 모듈을 연결하는 방식으로
//실행 시 글자모양이 뒤집혀서 나오면 0 또는 1을 서로 바꾸어 줍니다.
#define LED_DIN 11 //LED 연결
#define LED_CLK 13
#define LED_CS 10
#define MAX_DEVICE 4
#define LED_TYPE 1 //0 or 1
//시각 설정을 위해 D2와 D3 두개의 버튼을 사용합니다.
#define SET_BUTTON 2 //버튼 연결
#define UP_BUTTON 3
//WINK_SPEED는 콜론이 깜박이는 시간을 지정합니다. 이 숫자는
//설정 모드에서 각 자리수가 깜박이는 시간에도 영향을 줍니다.
#define WINK_SPEED 1000
//객체를 생성합니다. LedControl()은 네개의 인수를 받습니다. 핀번호 3개와
//연결된 디바이스의 갯수입니다.
DS3231 rtc;
LedControl lc = LedControl(LED_DIN, LED_CLK, LED_CS, MAX_DEVICE);
byte mode=0;
bool h12, pm;
byte digit[4];
byte startCol[4] = {2, 8, 17, 23};
//출력할 숫자의 형상, 행열 바뀜
byte font[11][5] = {
{B00111110, B01000001, B01000001, B01111111, B00111110}, //"0"
{B00000000, B01000010, B01111111, B01111111, B01000000}, //"1"
{B01000010, B01100001, B01010001, B01001111, B01000110}, //"2"
{B00100010, B01000001, B01001001, B01111111, B00110110}, //"3"
{B00011100, B00010010, B01111111, B01111111, B00010000}, //"4"
{B00101111, B01000101, B01000101, B01111101, B00111001}, //"5"
{B00111110, B01001001, B01001001, B01111011, B00110010}, //"6"
{B00000011, B00000001, B01110001, B01111111, B00000111}, //"7"
{B00110110, B01001001, B01001001, B01111111, B00110110}, //"8"
{B00100110, B01001001, B01001001, B01111111, B00111110}, //"9"
{B00000000, B00000000, B00000000, B00000000, B00000000} //" "
};
void setup()
{
pinMode(SET_BUTTON, INPUT_PULLUP);
pinMode(UP_BUTTON, INPUT_PULLUP);
//외부 인터럽트를 설정해 줍니다.
attachInterrupt(0, changeMode, FALLING);
attachInterrupt(1, changeValue, FALLING);
Wire.begin();
//도트 매트릭스 4개를 각각 초기화합니다.
for(int i=0; i<4; i++){ // 도트 매트릭스 0~3번
lc.shutdown(i,false); // 디스플레이 초기화
lc.setIntensity(i,5); // 도트 매트릭스 밝기 (매트릭스 번호, 밝기) 1~15
lc.clearDisplay(i); // led 를 전체 꺼주는 함수
}
Serial.begin(9600);
}
void loop()
{
if (mode == 0) { //mode가 0이면 RTC에서 현재 시각을 가져옵니다.
digit[0]=rtc.getHour(h12, pm)/10;
digit[1]=rtc.getHour(h12, pm)%10;
digit[2]=rtc.getMinute()/10;
digit[3]=rtc.getMinute()%10;
}
else if (mode > 4) { //mode가 4보다 크면 RTC를 새로운 값으로 셋팅합니다.
mode = 0;
rtc.setHour(digit[0]*10+digit[1]);
rtc.setMinute(digit[2]*10+digit[3]);
}
displayClock(); //화면에 시각을 표시합니다. 모드가 1~4이면 해당 자리수가 깜박입니다.
delay(100);
}
//displayDigit() 함수를 이용하여 현재 시간을 표시하는 함수입니다.
//mode가 1~4이면 해당 자리수를 WINK_SPEED에 지정한 간격으로 깜박이게 처리합니다.
void displayClock() {
for (int i=0; i<4; ++i) {
if (mode==i+1) displayDigit(startCol[i], millis()/WINK_SPEED%2 ? digit[i] : 10);
else displayDigit(startCol[i], digit[i]);
}
displayColon(14);
}
//첫번째 인수로 받은 위치에 두번째 인수로 받은 숫자를 출력합니다.
//만약 두번째 인수로 받은 숫자가 9보다 크면 공백문자를 출력합니다.
//이는 깜박임 처리를 위한 것입니다.
void displayDigit(byte start, byte number) {
if (number > 9) number = 10;
for (int i=0; i<5; ++i) {
if (LED_TYPE) lc.setColumn(start/8, start%8, font[number][i]);
else lc.setColumn(start/8, 7-start%8, font[number][i]);
++start;
}
}
//인수로 받은 위치에 WINK_SPEED로 지정한 간격으로 깜박이는 콜론(:)을 출력합니다.
void displayColon(byte start) {
bool wink = millis()/WINK_SPEED%2;
if (mode != 0) wink = false;
if (LED_TYPE) {
lc.setColumn(start/8, start%8, wink ? B00000000 : B00110110);
lc.setColumn((start+1)/8, (start+1)%8, wink ? B00000000 : B00110110);
}
else {
lc.setColumn(start/8, 7-start%8, wink ? B00000000 : B00110110);
lc.setColumn((start+1)/8, 7-(start+1)%8, wink ? B00000000 : B00110110);
}
}
//changeMode()함수는 attachInterrupt(0, changeMode, FALLING)으로
//정의된 D2 버튼이 눌릴 때 호출되는 인터럽트 함수입니다.
//버튼이 눌릴 때마다 변수 mode를 1씩 증가시키는 단순한 함수입니다.
//ppTime과 cpTime 변수는 물리적 스위치의 채터링 현상을 방지하기 위해
//사용하는 변수로 함수가 호출되면 이전 호출시간과 비교하여 100ms 이내의
//호출이면 정상적인 호출이 아닌 채터링에 의한 호출로 간주하고 무시하도록 하였습니다.
void changeMode() {
static unsigned long ppTime=0;
if(millis() - ppTime > 500) {
++mode;
Serial.println(mode);
}
ppTime = millis();
}
//changeValue()함수는 attachInterrupt(1, changeValue, FALLING)으로
//정의된 D3 버튼이 눌릴 때 호출되는 인터럽트 함수입니다.
//버튼이 눌릴 때마다 현재 mode를 참조하여 각각의 자리수를 1씩 증가시킵니다.
void changeValue() {
static unsigned long ppTime=0;
if(millis() - ppTime > 500) {
if (mode == 1) {
++digit[0];
if (digit[0] > 2) digit[0] = 0;
} else if (mode == 2) {
++digit[1];
if (digit[0] == 2 && digit[1] > 3) digit[1] = 0;
if (digit[1] > 9) digit[1] = 0;
} else if (mode == 3) {
++digit[2];
if (digit[2] > 5) digit[2] = 0;
} else {
++digit[3];
if (digit[3] > 9) digit[3] = 0;
}
}
ppTime = millis();
}