#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 char move_motion = 0;  // 도트매트릭스의 디스플레이 모션 방식 설정
unsigned int Move_cnt = 16; // 도트매트릭스 왼쪽, 오른쪽 시프트 변수
unsigned int Move_cnt2 = 0; // 도트매트릭스 왼쪽, 오른쪽 시프트 변수2 
unsigned int str_speed_cnt = 0;
bool flag_Oe = 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;
    Move_cnt--;
    Move_cnt2++;
    if (Move_cnt == 0 || Move_cnt2 == 16)
    {
      Move_cnt = 16;
      Move_cnt2 = 0;
      move_motion++;
      if (move_motion > 1)
      {
        move_motion = 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: // 적색 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;
        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 int buff2[16] = { 0 }; // 도트매트릭스2 출력 문자 저장
  unsigned int buff3[16] = { 0 }; // 도트매트릭스1 ORing 출력 문자 저장
  unsigned int buff4[16] = { 0 }; // 도트매트릭스2 ORing 출력 문자 저장
  unsigned char high1 = 0;  // 도트매트릭스1 상위 bit 저장 변수 
  unsigned char low1 = 0; // 도트매트릭스1 하위 bit 저장 변수
  unsigned char high2 = 0;  // 도트매트릭스2 상위 bit 저장 변수
  unsigned char low2 = 0; // 도트매트릭스2 하위 bit 저장 변수  
  register unsigned int i = 0;
  for (i_cnt = 0; i_cnt < 16; i_cnt++)  // 출력할 문자 복사 
  {
    if (move_motion == 0)
    {
      buff1[i_cnt] = pgm_read_word(&string[first][i_cnt]) >> Move_cnt;
      // 오른쪽, 도트매트릭스1 안으로 이동
      buff2[i_cnt] = pgm_read_word(&string[first][i_cnt]) << Move_cnt2;
      // 오른쪽, 도트매트릭스2 밖으로 이동      
    }

    if (move_motion == 1)
    {
      buff3[i_cnt] = pgm_read_word(&string[first][i_cnt]) << Move_cnt2;
      // 오른쪽, 도트매트릭스1 밖으로 이동
      buff4[i_cnt] = pgm_read_word(&string[first][i_cnt]) >> Move_cnt;
      // 오른쪽, 도트매트릭스2 안으로 이동
    }
  }

  for (i = 0; i < 16; i++)
  {
    buff1[i] |= buff3[i];
    buff2[i] |= buff4[i];
    high1 = (buff1[i] >> 8);  // 도트매트릭스1 상위 8bit 저장
    low1 = (buff1[i] &0xff);  // 도트매트릭스1 하위 8bit 저장
    high2 = (buff2[i] >> 8);  // 도트매트릭스2 상위 8bit 저장
    low2 = (buff2[i] &0xff);  // 도트매트릭스2 하위 8bit 저장
    row_dynamic(i); // 0~15행 접근
    shift_Register2(high1, low1, i);  // 도트매트릭스1 16bit LED 점등
    shift_Register2(high2, low2, i);  // 도트매트릭스2 16bit LED 점등
    ActivePulse();  // 스트로브 신호
  }
}

void loop()
{
  dot1_display_shift(Dot_char_cnt);
}
