#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <avr/io.h>
//PORTD
#define A_High PORTD|=0x04 // A
#define A_Low PORTD&=~0x04 // Arduino Uno 1번핀
#define B_High PORTD|=0x08 // B
#define B_Low PORTD&=~0x08 // Arduino Uno 2번핀
#define C_High PORTD|=0x10 // C
#define C_Low PORTD&=~0x10 // Arduino Uno 3번핀
#define D_High PORTD|=0x20 // C
#define D_Low PORTD&=~0x20 // Arduino Uno 3번핀
#define R1_ON PORTD|=0x40 // Red1
#define R1_OFF PORTD&=~0x40 // Arduino Uno 5번핀
#define G1_ON PORTD|=0x80 // Green1
#define G1_OFF PORTD&=~0x80 // Arduino Uno 6번핀
#define B1_ON PORTB|=0x01 // Blue1
#define B1_OFF PORTB&=~0x01 // Arduino Uno 8번핀
// PORTB
#define R2_ON PORTB|=0x02 // Red2
#define R2_OFF PORTB&=~0x02 // Arduino Uno 8번핀
#define G2_ON PORTB|=0x04 // Green2
#define G2_OFF PORTB&=~0x04 // Arduino Uno 9번핀
#define B2_ON PORTB|=0x08 // Blue2
#define B2_OFF PORTB&=~0x08  // Arduino Uno 10번핀
#define Clk_enable PORTB|=0x10 // Clk
#define Clk_disable PORTB&=~0x10 // Arduino Uno 11번핀
#define OE_enable PORTB&=~0x20 // Output_Enable(OE)
#define OE_disable PORTB|=0x20 // Arduino Uno 12번핀
// PORTC

#define LE_enable PORTC|=0x08 // Latch_Enable(LE)
#define LE_disable PORTC&=~0x08 // Arduino Uno 13번핀 
const int __attribute__((progmem)) string[][16] = { 0x00, 0x00, 0x00, 0x6303, 0x3303, 0x1b03, 0xf03, 0x703, 0xf03, 0x1b03, 0x3303, 0x6303, 0xc37f, 0x00, 0x00, 0x00
};  // LK
volatile unsigned char BCD_Num = 0; // 도트매트릭스에 표시할 문자 색상 결정
unsigned char Dot_char_cnt = 0; // 도트매트릭스에 표시될 문자 카운팅
unsigned int str_speed_cnt = 0;
ISR(TIMER2_OVF_vect)
{
  // 0.1mS 오버플로우 인터럽트 발생 
  static unsigned int string_moving_speed_value = 500;
  // 도트매트릭스 시프트 속도
  static unsigned int cnt = 0;
  str_speed_cnt++;
  if (str_speed_cnt > string_moving_speed_value)
  // 문자가 50mS 머문 후 시프트 시작
  {
    str_speed_cnt = 0;
  }
  TCNT2 = 0xe7;
}
void setup()
{
  DDRB = 0xff;  // PORTB 출력방향 설정
  DDRC = 0xff;  // PORTC 출력방향 설정
  DDRD = 0xff;  // PORTD 출력방향 설정
  TCCR2A = 0x00;
  TCCR2B = 0x04;  // 타이머 카운터2 노멀모드, 64분주 
  TIMSK2 = 0x01;  // 타이머 카운터2 인터럽트 허용
  TCNT2 = 0xe7; // 타이머 카운터2 초기값: 231
  SREG = 0x80;  // 인터럽트 허용
}
void row_dynamic(unsigned char i)
{
  switch (i)  // 행 이동
  {
    case 0:
      A_Low;
      B_Low;
      C_Low;
      break;  // 1행 LED 
    case 1:
      A_High;
      B_Low;
      C_Low;
      break;  // 2행 LED 
    case 2:
      A_Low;
      B_High;
      C_Low;
      break;  // 3행 LED 
    case 3:
      A_High;
      B_High;
      C_Low;
      break;  // 4행 LED 
    case 4:
      A_Low;
      B_Low;
      C_High;
      break;  // 5행 LED 
    case 5:
      A_High;
      B_Low;
      C_High;
      break;  // 6행 LED 
    case 6:
      A_Low;
      B_High;
      C_High;
      break;  // 7행 LED 
    case 7:
      A_High;
      B_High;
      C_High;
      break;  // 8행 LED
    case 8:
      A_Low;
      B_Low;
      C_Low;
      break;  // 9행 LED 
    case 9:
      A_High;
      B_Low;
      C_Low;
      break;  // 10행 LED 
    case 10:
      A_Low;
      B_High;
      C_Low;
      break;  // 11행 LED 
    case 11:
      A_High;
      B_High;
      C_Low;
      break;  // 12행 LED 
    case 12:
      A_Low;
      B_Low;
      C_High;
      break;  // 13행 LED 
    case 13:
      A_High;
      B_Low;
      C_High;
      break;  // 14행 LED 
    case 14:
      A_Low;
      B_High;
      C_High;
      break;  // 15행 LED 
    case 15:
      A_High;
      B_High;
      C_High;
      break;  // 16행 LED 
  }
}
void shift_Register2(unsigned char high, unsigned char low, unsigned char cnt_row)
{
  // 점등시키고자 하는 LED 16bit를 high(8bit), low(8bit)로 나누어
  //cnt_row 인자값에 따라 상위 8비트, 
  // 하위 8비트 출력
  static unsigned char clk = 0;
  static unsigned int buff = 0;
  buff = high << 8; // 상위 8bit 좌측으로 8번 시프트 
  buff |= low;  // 상위 8bit, 하위 8bit 결합
  BCD_Num = 2;
  // 출력 문자 핑크색 설정,BCD_NUM 값에 따라 출력 문자 색상 변경 가능
  for (clk = 0; clk < 16; clk++)
  {
    // 16비트 데이터를 1비트씩 시프트레지스터에 입력
    if (buff &(1 << clk))
    {
      switch (BCD_Num)
      {
        case 0:
          R1_OFF;
          G1_OFF;
          B1_OFF;
          R2_OFF;
          G2_OFF;
          B2_OFF;
          break;
        case 1: // Green
          if (cnt_row < 8)
          {
            // 상위 8비트 디스플레이 ON, 하위 8비트 디스플레이 OFF 
            R1_OFF;
            G1_ON;
            B1_OFF;
            R2_OFF;
            B2_OFF;
            G2_OFF;
          }
          else
          {
            // 하위 8비트 디스플레이 ON, 상위 8비트 디스플레이 OFF
            R2_OFF;
            G2_ON;
            B2_OFF;
            R1_OFF;
            B1_OFF;
            G1_OFF;
          }
          break;
        case 2: // Pink
          if (cnt_row < 8)
          {
            // 상위 8비트 디스플레이 ON, 하위 8비트 디스플레이 OFF 
            R1_ON;
            B1_ON;
            G1_OFF;
            R2_OFF;
            B2_OFF;
            G2_OFF;
          }
          else
          {
            // 하위 8비트 디스플레이 ON, 상위 8비트 디스플레이 OFF
            R2_ON;
            B2_ON;
            G2_OFF;
            R1_OFF;
            B1_OFF;
            G1_OFF;
          }
          break;
        case 3: // Blue
          if (cnt_row < 8)
          {
            // 상위 8비트 디스플레이 ON, 하위 8비트 디스플레이 OFF 
            R1_OFF;
            G1_OFF;
            B1_ON;
            R2_OFF;
            B2_OFF;
            G2_OFF;
          }
          else
          {
            // 하위 8비트 디스플레이 ON, 상위 8비트 디스플레이 OFF
            R2_OFF;
            G2_OFF;
            B2_ON;
            R1_OFF;
            B1_OFF;
            G1_OFF;
          }
          break;
        case 4: // Blue
          if (cnt_row < 8)
          {
            // 상위 8비트 디스플레이 ON, 하위 8비트 디스플레이 OFF 
            R1_OFF;
            G1_OFF;
            B1_ON;
            R2_OFF;
            B2_OFF;
            G2_OFF;
          }
          else
          {
            // 하위 8비트 디스플레이 ON, 상위 8비트 디스플레이 OFF
            R2_OFF;
            G2_OFF;
            B2_ON;
            R1_OFF;
            B1_OFF;
            G1_OFF;
          }
          break;
        case 5: // yellow
          if (cnt_row < 8)
          {
            // 상위 8비트 디스플레이 ON, 하위 8비트 디스플레이 OFF 
            R1_ON;
            G1_ON;
            B1_OFF;
            R2_OFF;
            B2_OFF;
            G2_OFF;
          }
          else
          {
            // 하위 8비트 디스플레이 ON, 상위 8비트 디스플레이 OFF
            R2_ON;
            G2_ON;
            B2_OFF;
            R1_OFF;
            B1_OFF;
            G1_OFF;
          }
          break;
        case 6: // yellow
          if (cnt_row < 8)
          {
            // 상위 8비트 디스플레이 ON, 하위 8비트 디스플레이 OFF 
            R1_ON;
            G1_ON;
            B1_OFF;
            R2_OFF;
            B2_OFF;
            G2_OFF;
          }
          else
          {
            // 하위 8비트 디스플레이 ON, 상위 8비트 디스플레이 OFF
            R2_ON;
            G2_ON;
            B2_OFF;
            R1_OFF;
            B1_OFF;
            G1_OFF;
          }
          break;
        case 7: // 적색 LED  
          if (cnt_row < 8)
          {
            // 상위 8비트 디스플레이 ON, 하위 8비트 디스플레이 OFF 
            R1_ON;
            G1_OFF;
            B1_OFF;
            R2_OFF;
            B2_OFF;
            G2_OFF;
          }
          else
          {
            // 하위 8비트 디스플레이 ON, 상위 8비트 디스플레이 OFF
            R2_ON;
            G2_OFF;
            B2_OFF;
            R1_OFF;
            B1_OFF;
            G1_OFF;
          }
          break;
        case 8: // white
          if (cnt_row < 8)
          {
            // 상위 8비트 디스플레이 ON, 하위 8비트 디스플레이 OFF 
            R1_ON;
            G1_ON;
            B1_ON;
            R2_OFF;
            B2_OFF;
            G2_OFF;
          }
          else
          {
            // 하위 8비트 디스플레이 ON, 상위 8비트 디스플레이 OFF
            R2_ON;
            G2_ON;
            B2_ON;
            R1_OFF;
            B1_OFF;
            G1_OFF;
          }
          break;
        case 9: // Blue
          if (cnt_row < 8)
          {
            // 상위 8비트 디스플레이 ON, 하위 8비트 디스플레이 OFF 
            R1_OFF;
            G1_OFF;
            B1_ON;
            R2_OFF;
            B2_OFF;
            G2_OFF;
          }
          else
          {
            // 하위 8비트 디스플레이 ON, 상위 8비트 디스플레이 OFF
            R2_OFF;
            G2_OFF;
            B2_ON;
            R1_OFF;
            B1_OFF;
            G1_OFF;
          }
          break;
        case 10:  // Green 
          if (cnt_row < 8)
          {
            // 상위 8비트 디스플레이 ON, 하위 8비트 디스플레이 OFF 
            R1_OFF;
            G1_ON;
            B1_OFF;
            R2_OFF;
            B2_OFF;
            G2_OFF;
          }
          else
          {
            // 하위 8비트 디스플레이 ON, 상위 8비트 디스플레이 OFF
            R2_OFF;
            G2_ON;
            B2_OFF;
            R1_OFF;
            B1_OFF;
            G1_OFF;
          }
          break;
        case 11:  // red 
          if (cnt_row < 8)
          {
            // 상위 8비트 디스플레이 ON, 하위 8비트 디스플레이 OFF 
            R1_ON;
            G1_OFF;
            B1_OFF;
            R2_OFF;
            B2_OFF;
            G2_OFF;
          }
          else
          {
            // 하위 8비트 디스플레이 ON, 상위 8비트 디스플레이 OFF
            R2_ON;
            G2_OFF;
            B2_OFF;
            R1_OFF;
            B1_OFF;
            G1_OFF;
          }
          break;
        case 12:  // Green
          if (cnt_row < 8)
          {
            // 상위 8비트 디스플레이 ON, 하위 8비트 디스플레이 OFF 
            R1_OFF;
            G1_ON;
            B1_OFF;
          }
          else
          {
            // 하위 8비트 디스플레이 ON, 상위 8비트 디스플레이 OFF
            R2_OFF;
            G2_ON;
            B2_OFF;
            R2_OFF;
            B2_OFF;
            G2_OFF;
          }
          break;
        case 13:  // yellow 
          if (cnt_row < 8)
          {
            // 상위 8비트 디스플레이 ON, 하위 8비트 디스플레이 OFF 
            R1_ON;
            G1_ON;
            B2_OFF;
            R2_OFF;
            B2_OFF;
            G2_OFF;
          }
          else
          {
            // 하위 8비트 디스플레이 ON, 상위 8비트 디스플레이 OFF
            R2_ON;
            G2_ON;
            B2_OFF;
            R1_OFF;
            B1_OFF;
            G1_OFF;
          }
          break;
        case 14:
          if (cnt_row < 8)
          {
            // 상위 8비트 디스플레이 ON, 하위 8비트 디스플레이 OFF 
            R1_ON;
            G1_OFF;
            B1_OFF;
            R2_OFF;
            B2_OFF;
            G2_OFF;
          }
          else
          {
            // 하위 8비트 디스플레이 ON, 상위 8비트 디스플레이 OFF
            R2_ON;
            G2_OFF;
            B2_OFF;
            R1_OFF;
            B1_OFF;
            G1_OFF;
          }
          break;
        default:
          R1_OFF;
          G1_OFF;
          B1_OFF;
          // 상위 8비트 디스플레이 OFF, 하위 8비트 디스플레이 OFF 
          R2_OFF;
          G2_OFF;
          B2_OFF;
          break;
      }
    }
    else
    {
      R1_OFF;
      G1_OFF;
      B1_OFF;
      R2_OFF;
      G2_OFF;
      B2_OFF;
    }
    Clk_enable; // CLK 신호 활성화
    Clk_disable;
  }
}
void ActivePulse()  // 스트로브 신호
{
  LE_enable;
  LE_disable; // LE신호 활성화   
  OE_enable;  // OE신호 활성화
  delayMicroseconds(600); // 600uS 딜레이 
  OE_disable;
}
void dot1_display_shift(unsigned char first)  // 도트매트릭스 문자 출력 함수
{
  static unsigned int i_cnt = 0;
  unsigned int buff1[16] = { 0 }; // 도트매트릭스1 출력 문자 저장
  unsigned char high1 = 0;  // 도트매트릭스1 상위 bit 저장 변수
  unsigned char low1 = 0; // 도트매트릭스1 하위 bit 저장 변수
  register unsigned int i = 0;
  for (i_cnt = 0; i_cnt < 16; i_cnt++)  // 출력할 문자 복사 
  {
    buff1[i_cnt] = pgm_read_word(&string[first][i_cnt]);
    // 도트매트릭스1에 출력할 문자 저장
  }
  for (i = 0; i < 16; i++)
  {
    high1 = (buff1[i] >> 8);  // 도트매트릭스1 상위 8bit 저장
    low1 = (buff1[i] &0xff);  // 도트매트릭스1 하위 8bit 저장
    row_dynamic(i); // 0~15행 접근
    shift_Register2(high1, low1, i);  // 도트매트릭스1 16bit LED 점등
    shift_Register2(0x00, 0x00, i); // 도트매트릭스2 16bit LED OFF
    ActivePulse();  // 스트로브 신호
  }
}
void loop()
{
  dot1_display_shift(Dot_char_cnt);
}
