/* 타이머/카운터 인터럽트 사용을 위해 FrequencyTimer2.h 헤더 파일을 include 한다. */
#include <FrequencyTimer2.h>
// 표현할 문자를 2진수로 정의 새로운 문자 추가 가능.
#define SPACE { \
    {0, 0, 0, 0, 0, 0, 0, 0}, \
    {0, 0, 0, 0, 0, 0, 0, 0}, \
    {0, 0, 0, 0, 0, 0, 0, 0}, \
    {0, 0, 0, 0, 0, 0, 0, 0}, \
    {0, 0, 0, 0, 0, 0, 0, 0}, \
    {0, 0, 0, 0, 0, 0, 0, 0}, \
    {0, 0, 0, 0, 0, 0, 0, 0}, \
    {0, 0, 0, 0, 0, 0, 0, 0} \
  }
#define LK { \
    {0, 0, 0, 1, 0, 0, 1, 0},\
    {0, 0, 0, 1, 0, 1, 0, 0},\
    {1, 0, 0, 1, 1, 0, 0, 0},\
    {1, 0, 0, 1, 1, 0, 0, 0},\
    {1, 0, 0, 1, 0, 1, 0, 0},\
    {1, 0, 0, 1, 0, 0, 1, 0},\
    {1, 0, 0, 0, 0, 0, 0, 0},\
    {1, 1, 1, 1, 1, 0, 0, 0},\
  }
#define Num_0 { \
    {0, 0, 0, 1, 1, 1, 0, 0},\
    {0, 0, 1, 0, 0, 0, 1, 0},\
    {0, 0, 1, 0, 0, 0, 1, 0},\
    {0, 0, 1, 0, 0, 0, 1, 0},\
    {0, 0, 1, 0, 0, 0, 1, 0},\
    {0, 0, 1, 0, 0, 0, 1, 0},\
    {0, 0, 1, 0, 0, 0, 1, 0},\
    {0, 0, 0, 1, 1, 1, 0, 0},\
  }
#define Num_1 { \
    {0, 0, 0, 0, 1, 0, 0, 0},\
    {0, 0, 0, 1, 1, 0, 0, 0},\
    {0, 0, 0, 0, 1, 0, 0, 0},\
    {0, 0, 0, 0, 1, 0, 0, 0},\
    {0, 0, 0, 0, 1, 0, 0, 0},\
    {0, 0, 0, 0, 1, 0, 0, 0},\
    {0, 0, 0, 0, 1, 0, 0, 0},\
    {0, 0, 0, 1, 1, 1, 0, 0},\
  }
#define Num_2 { \
    {0, 0, 0, 1, 1, 1, 0, 0},\
    {0, 0, 1, 0, 0, 0, 1, 0},\
    {0, 0, 0, 0, 0, 0, 1, 0},\
    {0, 0, 0, 0, 0, 0, 1, 0},\
    {0, 0, 0, 1, 1, 1, 0, 0},\
    {0, 0, 1, 0, 0, 0, 0, 0},\
    {0, 0, 1, 0, 0, 0, 0, 0},\
    {0, 0, 1, 1, 1, 1, 1, 0},\
  }
#define Num_3 { \
    {0, 0, 0, 1, 1, 1, 0, 0},\
    {0, 0, 1, 0, 0, 0, 1, 0},\
    {0, 0, 0, 0, 0, 0, 1, 0},\
    {0, 0, 0, 1, 1, 1, 0, 0},\
    {0, 0, 0, 0, 0, 0, 1, 0},\
    {0, 0, 0, 0, 0, 0, 1, 0},\
    {0, 0, 1, 0, 0, 0, 1, 0},\
    {0, 0, 0, 1, 1, 1, 0, 0},\
  }
#define Num_4 { \
    {0, 0, 0, 0, 1, 1, 0, 0},\
    {0, 0, 0, 0, 1, 1, 0, 0},\
    {0, 0, 0, 1, 0, 1, 0, 0},\
    {0, 0, 0, 1, 0, 1, 0, 0},\
    {0, 0, 1, 0, 0, 1, 0, 0},\
    {0, 0, 1, 1, 1, 1, 1, 0},\
    {0, 0, 0, 0, 0, 1, 0, 0},\
    {0, 0, 0, 0, 0, 1, 0, 0},\
  }
#define Num_5 { \
    {0, 0, 1, 1, 1, 1, 1, 0},\
    {0, 0, 1, 0, 0, 0, 0, 0},\
    {0, 0, 1, 0, 0, 0, 0, 0},\
    {0, 0, 1, 1, 1, 1, 0, 0},\
    {0, 0, 0, 0, 0, 0, 1, 0},\
    {0, 0, 0, 0, 0, 0, 1, 0},\
    {0, 0, 1, 0, 0, 0, 1, 0},\
    {0, 0, 0, 1, 1, 1, 0, 0},\
  }
#define Num_6 { \
    {0, 0, 0, 1, 1, 1, 0, 0},\
    {0, 0, 1, 0, 0, 0, 1, 0},\
    {0, 0, 1, 0, 0, 0, 0, 0},\
    {0, 0, 1, 1, 1, 1, 0, 0},\
    {0, 0, 1, 0, 0, 0, 1, 0},\
    {0, 0, 1, 0, 0, 0, 1, 0},\
    {0, 0, 1, 0, 0, 0, 1, 0},\
    {0, 0, 0, 1, 1, 1, 0, 0},\
  }
#define Num_7 { \
    {0, 0, 1, 1, 1, 1, 1, 0},\
    {0, 0, 1, 0, 0, 0, 1, 0},\
    {0, 0, 0, 0, 0, 0, 1, 0},\
    {0, 0, 0, 0, 0, 1, 0, 0},\
    {0, 0, 0, 0, 0, 1, 0, 0},\
    {0, 0, 0, 0, 1, 0, 0, 0},\
    {0, 0, 0, 0, 1, 0, 0, 0},\
    {0, 0, 0, 0, 1, 0, 0, 0},\
  }
#define Num_8 { \
    {0, 0, 0, 1, 1, 1, 0, 0},\
    {0, 0, 1, 0, 0, 0, 1, 0},\
    {0, 0, 1, 0, 0, 0, 1, 0},\
    {0, 0, 0, 1, 1, 1, 0, 0},\
    {0, 0, 1, 0, 0, 0, 1, 0},\
    {0, 0, 1, 0, 0, 0, 1, 0},\
    {0, 0, 1, 0, 0, 0, 1, 0},\
    {0, 0, 0, 1, 1, 1, 0, 0},\
  }
#define Num_9 { \
    {0, 0, 0, 1, 1, 1, 0, 0},\
    {0, 0, 1, 0, 0, 0, 1, 0},\
    {0, 0, 1, 0, 0, 0, 1, 0},\
    {0, 0, 1, 0, 0, 0, 1, 0},\
    {0, 0, 0, 1, 1, 1, 1, 0},\
    {0, 0, 0, 0, 0, 0, 1, 0},\
    {0, 0, 1, 0, 0, 0, 1, 0},\
    {0, 0, 0, 1, 1, 1, 0, 0},\
  }
/* 각 핀(Pin) 정의, 프로그램 가독성을 위하여 배열로 선언 */
int pins[17] = { -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, A0, A1};
// 가로 열 핀(Pin) 정의(col[xx] of leds = pin yy on led matrix), ex) pins[1]은 디지털0번핀
int cols[8] = {pins[1], pins[2], pins[3], pins[4], pins[5], pins[6], pins[7], pins[8]};
int cols2[8] = {pins[9], pins[10], pins[11], pins[12], pins[13], pins[14], pins[15], pins[16]};
// 세로 행 핀(Pin) 정의(row[xx] of leds = pin yy on led matrix)
int rows[8] = {pins[24], pins[23], pins[22], pins[21], pins[4], pins[3], pins[2], pins[1]};
const int DS = A2;
const int ST_CP = A3;
const int SH_CP = A4;
/* 표현할 문자 패턴, numPatterns 의 개수에 맞게 표시
  Define부에서 각 문자 별로 정의 됨 */
const int numPatterns = 11;  // 표현할 글자 수
#define Row_Sig_AllOff 8
byte patterns[numPatterns][8][8] = {
  LK,
  Num_0,
  Num_1,
  Num_2,
  Num_3,
  Num_4,
  Num_5,
  Num_6,
  Num_7,
  Num_8,
  Num_9
};
byte col = 0;
byte leds[8][8];
int pattern = 0;
void clearLeds()  // 8x8 도트매트릭스 모듈 메모리 초기화 함수
{
  for (int i = 0; i < 8; i++)
  {
    for (int j = 0; j < 8; j++)
    {
      leds[i][j] = 0;
    }
  }
}
void setPattern(int pattern)  // 8x8 도트매트릭스 모듈 문자 복사 함수
{
  /* 3차원배열 patterns 를 2차원 배열인 leds에 복사한다.
    깊이(높이)는 pattern 인자 값을 전달 받아 결정 된다 */
  for (int i = 0; i < 8; i++)
  {
    for (int j = 0; j < 8; j++)
    {
      leds[i][j] = patterns[pattern][i][j];
    }
  }
}
void RowPulseControl(unsigned char data)  // 세로 행(Row) 핀(Pin) 출력 제어 함수
{
  /* 공통 애노드 타입 8x8 도트매트릭스 LED 모듈로 변경 됨
    인터럽트 서비스 루틴을 나가기 전에 세로 행(Row) 핀(Pin)에 해당 핀(Pin)에만 하이(HIGH)를
    출력하고 그 외 핀(Pin)들은 로우(LOW)를 넣어 주도록 한다 */
  switch (data)
  {
    case 0:
      shiftRegister(0x80); break;  // 첫번째 ROW
    case 1:
      shiftRegister(0x40); break;  // 두번째 ROW
    case 2:
      shiftRegister(0x20); break;  // 세번째 ROW
    case 3:
      shiftRegister(0x10); break;  // 네번째 ROW
    case 4:
      shiftRegister(0x08); break;  // 다섯번째 ROW
    case 5:
      shiftRegister(0x04); break;  // 여섯번째 ROW
    case 6:
      shiftRegister(0x02); break;  // 일곱번째 ROW
    case 7:
      shiftRegister(0x01); break;  // 여덟번째 ROW
    case Row_Sig_AllOff:
      shiftRegister(0x00); break;  // 전 ROW 핀(Pin) 신호 OFF
  }
}
void display()  // 타이머/카운트 인터럽트를 사용하여 1ms마다 display()함수가 호출
{
  static bool toggle = 0;
  static unsigned cnt_time = 0;
  static unsigned char cnt = 0;
  RowPulseControl(Row_Sig_AllOff);  // 이전 세로 행(Row) 신호는 끄도록 한다
  cnt_time++;
  if (cnt_time == 1000)  // 1 초가 되면?
  {
    cnt_time = 0;
    pattern++;  // 3차원 배열의 깊이를 0부터 10까지 증가
    if (pattern > 10)
    {
      pattern = 0;
      toggle ^= 1;
    }
  }
  cnt++;  // 세로 행 증가
  /* 타이머/카운터 인터럽트 서비스 루틴이 발생 될 때마다 cnt값은 증가 하도록 한다 */
  if (cnt == 8)
  {
    cnt = 0;
  }
  /* 가로 열 신호 출력, 현재 leds에 저장된 열 값을 0~7번방에 걸쳐 체크하여
    "1"일때는 HIGH, "0"일때는 LOW를 출력하도록 한다 */
  for (int col = 0; col < 8; col++)
  {
    switch (toggle)
    {
      case 0:
        if (leds[cnt][col] == 1)
        {
          digitalWrite(cols[col], LOW);     // 노란색 LED ON
        }
        else
        {
          digitalWrite(cols[col], HIGH);    // 노란색 LED OFF
        }
        digitalWrite(cols2[col], HIGH);     // 빨간색 LED OFF
        break;
      case 1:
        if (leds[cnt][col] == 1)
        {
          digitalWrite(cols2[col], LOW);   // 빨간색 LED ON
        }
        else
        {
          digitalWrite(cols2[col], HIGH);  // 빨간색 LED OFF
        }
        digitalWrite(cols[col], HIGH);     // 노란색 LED OFF
        break;
    }
  }
  RowPulseControl(cnt);  // 세로 행(Row) 핀(Pin) 출력 제어 함수
  /* 타이머/카운터 인터럽트 서비스 루틴을 빠져나가기 전에 세로 행(Row) 신호를
    반드시 제어하여야 한다 */
}
void shiftRegister(unsigned char data)  // 74HC595 시프트 레지스터 IC 구동 함수
{
  int i = 0;
  digitalWrite(ST_CP, LOW);     // 래치 OFF
  for (i = 0; i < 8; i++)
  {
    digitalWrite(SH_CP, LOW);   // 플립플롭 클럭 핀(Pin) 로우
    if (data & (0x80 >> i))
    {
      digitalWrite(DS, HIGH);   // 시리얼 데이터 핀(Pin) 하이이면
    }
    else
    {
      digitalWrite(DS, LOW);   // 시리얼 데이터 핀(Pin) 로우이면
    }
    digitalWrite(SH_CP, HIGH);  // 플립플롭 클럭 핀(Pin) 하이
  }
  digitalWrite(ST_CP, HIGH);    // 래치 ON
}
void setup()  // 초기화
{
  /* 8x8 도트매트릭스 모듈과 연결된 핀(Pin)들
    (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, A0, A1)을 for문을 이용하여 출력 포트로 설정 */
  for (int i = 1; i <= 17; i++)
  {
    pinMode(pins[i], OUTPUT);
  }
  pinMode(SH_CP, OUTPUT);  // 래치 클럭 핀(Pin) 출력 포트로 설정
  pinMode(ST_CP, OUTPUT);  // 플립플럽 클럭 핀(Pin) 출력 포트로 설정
  pinMode(DS, OUTPUT);    // 시리얼 데이터 입력 핀(Pin) 출력 포트로 설정
  /* 8x8 도트매트릭스 모듈 사용 메모리 초기화 */
  clearLeds();
  /* 타이머/카운터 인터럽트 초기화 */
  FrequencyTimer2::disable();
  FrequencyTimer2::setPeriod(1000);         // 타이머/카운터 인터럽트 주기 설정 1000us = 1ms
  FrequencyTimer2::setOnOverflow(display);  // 인터럽트 서비스 루틴 함수 선언
}
void loop()  // 무한 루프
{
  setPattern(pattern);  // 8x8 도트매트릭스 모듈 문자 복사 함수
}

