#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <avr/io.h>

#define A_High PORTD|=0x04 // A
#define A_Low PORTD&=~0x04 // Arduino Uno 2번핀
#define B_High PORTD|=0x08 // B
#define B_Low PORTD&=~0x08 // Arduino Uno 3번핀
#define C_High PORTD|=0x10 // C
#define C_Low PORTD&=~0x10 // Arduino Uno 4번핀
#define D_High PORTD|=0x20 // C
#define D_Low PORTD&=~0x20 // Arduino Uno 5번핀
#define red_enable PORTD|=0x40 // Red
#define red_disable PORTD&=~0x40 // Arduino Uno 6번핀
#define green_enable PORTD|=0x80 // Green
#define green_disable PORTD&=~0x80 // Arduino Uno 7번핀
#define LE_enable PORTC|=0x08 // Latch_Enable(LE)
#define LE_disable PORTC&=~0x08 // Arduino Uno A3번핀 
#define Clk_enable PORTB|=0x10 // Clk
#define Clk_disable PORTB&=~0x10 // Arduino Uno 12번핀
#define OE_enable PORTB&=~0x20 // Output_Enable(OE)
#define OE_disable PORTB|=0x20 // Arduino Uno 13번핀


const int __attribute__((progmem)) string[][16] = {
0x00,0x00,0x00,0xc183,0x6183,0x3183,0x1983,0xd83,0x783,0xd83,0x1983,0x3183,0x6183,0xc1ff,0x180,0x00,
}; // LK
unsigned char Dot_char_cnt = 0; // 도트매트릭스에 표시될 문자 카운팅
unsigned char flag_cnt = 0; // 도트매트릭스에 표시될 색상 카운팅
unsigned char move_motion = 0; // 도트매트릭스의 디스플레이 모션 방식 설정
unsigned int Move_cnt = 16;
unsigned int Move_cnt2 = 0; // 도트매트릭스 시프트 변수
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;
        if (flag_Oe) {
                // 스트로브 신호 활성화되면  
                OE_enable; // OUTPUT 신호 활성화
                cnt++; // cnt 변수 증가
                if (cnt >= 5) {
                        // OUTPUT 신호 주기 생성
                        cnt = 0;
                        flag_Oe = 0;
                        OE_disable;
                }
        }
        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;
                                flag_cnt++; // 도트매트릭스 디스플레이 컬러 변경
                                if (flag_cnt == 3) {
                                        flag_cnt = 0;
                                }
                        }
                }
        }
        TCNT2 = 0xe7;
}
void setup() {
         DDRD=0XFC;
        DDRC=0X08;
        DDRB=0X30;

        TCCR2A = 0x00;
        TCCR2B = 0x04; // 타이머 카운터2 노멀 모드, 64분주 
        TIMSK2 = 0x01; // 타이머 카운터2 인터럽트 허용
        TCNT2 = 0xe7; // 타이머 카운터2 초기값: 231
        SREG = 0x80; // 인터럽트 허용
}
void row_dynamic(unsigned int i) {
        static unsigned int str_cnt = 0;
        switch (i) // 행 이동
        {
        case 0:
                A_Low;
                B_Low;
                C_Low;
                D_Low;
                break; // 1행 LED 
        case 1:
                A_High;
                B_Low;
                C_Low;
                D_Low;
                break; // 2행 LED 
        case 2:
                A_Low;
                B_High;
                C_Low;
                D_Low;
                break; // 3행 LED 
        case 3:
                A_High;
                B_High;
                C_Low;
                D_Low;
                break; // 4행 LED 
        case 4:
                A_Low;
                B_Low;
                C_High;
                D_Low;
                break; // 5행 LED 
        case 5:
                A_High;
                B_Low;
                C_High;
                D_Low;
                break; // 6행 LED 
        case 6:
                A_Low;
                B_High;
                C_High;
                D_Low;
                break; // 7행 LED 
        case 7:
                A_High;
                B_High;
                C_High;
                D_Low;
                break; // 8행 LED
        case 8:
                A_Low;
                B_Low;
                C_Low;
                D_High;
                break; // 9행 LED 
        case 9:
                A_High;
                B_Low;
                C_Low;
                D_High;
                break; // 10행 LED 
        case 10:
                A_Low;
                B_High;
                C_Low;
                D_High;
                break; // 11행 LED 
        case 11:
                A_High;
                B_High;
                C_Low;
                D_High;
                break; // 12행 LED 
        case 12:
                A_Low;
                B_Low;
                C_High;
                D_High;
                break; // 13행 LED 
        case 13:
                A_High;
                B_Low;
                C_High;
                D_High;
                break; // 14행 LED 
        case 14:
                A_Low;
                B_High;
                C_High;
                D_High;
                break; // 15행 LED 
        case 15:
                A_High;
                B_High;
                C_High;
                D_High;
                break; // 16행 LED
        }
}
void shift_Register(unsigned char out) {
        // 점등 시키고자 하는 LED Bit 입력
        unsigned char clk = 0;
        for (clk = 0; clk < 8; clk++) {
                // 8비트 데이터를 1비트씩 시프트레지스터에 입력
                if (out & (0x80 >> clk)) {
                        switch (flag_cnt) {
                        case 0:
                                green_disable;
                                red_enable;
                                break; // 적색으로 LED 점등
                        case 1:
                                green_enable;
                                red_disable;
                                break; // 녹색으로 LED 점등
                        case 2:
                                green_enable;
                                red_enable;
                                break; // 노란색으로 LED 점등
                        }
                }
                else {
                        green_disable;
                        red_disable;
                }
                Clk_enable; // CLK 신호 활성화
                Clk_disable;
        }
}
void ActivePulse() {
        // 스트로브 신호
        LE_enable;
        LE_disable; // LE 신호 활성화   
        flag_Oe = 1; // OE 신호 활성화
}
void dot1_display_shift(unsigned char first) {
        // 도트매트릭스 오른쪽 시프트 함수
        static unsigned int i_cnt = 0;
        unsigned int buff1[16] = {
                0
        };
        unsigned char high1 = 0; // Dot1 상위 bit
        unsigned char low1 = 0; // Dot1 하위 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;
                        // 오른쪽, 도트매트릭스 안으로 이동
                }
                if (move_motion == 1) {
                        buff1[i_cnt] = pgm_read_word( & string[first][i_cnt]) >> Move_cnt2;
                        // 오른쪽, 도트매트릭스 밖으로 이동
                }
        }
        for (i = 0; i < 16; i++) {
                high1 = (buff1[i] >> 8); // Dot1 상위 8bit 저장
                low1 = (buff1[i] & 0xff); // Dot1 하위 8bit 저장
                shift_Register(high1); // 상위 8bit LED 점등
                shift_Register(low1); // 하위 8bit LED 점등
                row_dynamic(i); // 0~15행 접근
                ActivePulse(); // 스트로브 신호 
        }
}
void loop() {
        dot1_display_shift(Dot_char_cnt);
}
