#include <avr/io.h>      // ieejas-izejas biblioteka
#include <avr/interrupt.h>      // partraukumu biblioteka
#include <util/delay.h>     // aiztures biblioteka
#include <avr/pgmspace.h>  // biblioteka tiesai datu piekluve "program memory"

volatile uint8_t  flag = 0;
volatile uint8_t  buttonInterruptFlag = 0;
volatile uint8_t  _index = 0;

// definejam (nosaucam) izvadu numurus saprotamak
#define SPIPORT PORTB
#define DATA   3
#define CLOCK  5
#define LATCH  2

#define BUTTONPIN PIND
#define BUTTON 2

// definejam "neko nedarishanas instrukciju" (neko nedara vienu uC takti), lai nebutu gari jaraksta
#define NOP asm("nop");

void softSpiTransfer(uint8_t spi_data){ // nosutam 8 bitus (spi_data) sakot ar jaunako bitu
for(uint8_t bitNumber=0; bitNumber<8; bitNumber++){
	SPIPORT &= ~_BV(DATA);  // datu izvads '0'
	if(((spi_data >> bitNumber) & 1) == 1){
		PORTB |= _BV(DATA);  // datu izvads '1'
	}
	SPIPORT |= _BV(CLOCK);  // clock izvads '1'
	NOP;  //nedaram neko 1 takti
	SPIPORT &= ~_BV(CLOCK);  // clock izvads '0'
}
}
 
void showSpiData(){  // paradam aizsutito informaciju
 SPIPORT |= _BV(LATCH);  // latch izvads '1'
 NOP;
 SPIPORT &= ~_BV(LATCH);  // latch izvads '0'
}
 
const uint8_t sequence1data[3*1] PROGMEM = {  // pirma sablona datu kopa PROGMEM atmina
 0b10010010, 0b01001001, 0b00000000 };
 
const uint8_t sequence2data[3*27] PROGMEM = {  // otra sablona datu kopa PROGMEM atmina
 0b00000000, 0b00011000, 0b00000000,
 0b00000000, 0b00111100, 0b00000000,
 0b00000000, 0b01111110, 0b00000000,
 0b00000000, 0b01111110, 0b00000000,
 0b11111111, 0b01111110, 0b11111111,
 0b00000000, 0b01111110, 0b00000000,
 0b00000000, 0b01111110, 0b00000000,
 0b00000000, 0b01111110, 0b00000000,
 0b00000000, 0b01111110, 0b00000000,
 0b11111111, 0b01111110, 0b00000000,
 0b00000000, 0b01111110, 0b00000000,
 0b00000000, 0b01111110, 0b00000000,
 0b11111111, 0b01111110, 0b00000000,
 0b00000000, 0b01111110, 0b00000000,
 0b00000000, 0b01111110, 0b00000000,
 0b00000000, 0b01111110, 0b11111111,
 0b00000000, 0b01111110, 0b00000000,
 0b00000000, 0b01111110, 0b00000000,
 0b00000000, 0b01111110, 0b11111111,
 0b00000000, 0b01111110, 0b00000000,
 0b00000000, 0b01111110, 0b00000000,
 0b00000000, 0b01111110, 0b00000000,
 0b00000000, 0b00111100, 0b00000000,
 0b00000000, 0b00011000, 0b00000000,
 0b00000000, 0b00000000, 0b00000000,
 0b00000000, 0b00000000, 0b00000000,
 0b00000000, 0b00000000, 0b00000000 };
 
const uint8_t sequence3data[16] PROGMEM = {  // tresa sablona datu kopa PROGMEM atmina
 0b00000001,
 0b00000011,
 0b00000111,
 0b00001111,
 0b00011111,
 0b00111111,
 0b01111111,
 0b11111111,
 0b11111110,
 0b11111100,
 0b11111000,
 0b11110000,
 0b11100000,
 0b11000000,
 0b10000000,
 0b00000000    };
 
ISR(INT0_vect) {  //leksim seit, ja konstatets partraukums uz PD2 (INT0)
 cli();    // izsledzam partraukumus, lai seit mus neviens nepartrauktu
 PORTB = 0b00000000;   // nodzesam visas diodes, pogas spiediena laika
 while(!(BUTTONPIN & _BV(BUTTON))){    // gaidam, kamer poga tiks atlaista
  _delay_ms(30);   // 'debounce' pauzite
 }
 buttonInterruptFlag = 1;  // atzimejam, ka ir noticis partraukums, un ir japarsledz rezims
 flag++;        //palielinam mainigo par 1
 if(flag > 3){
  flag = 0;    // ja mainigais lielaks par 3 pieskiram tam veribu 0
 }    // mainiga veriba pec partraukumiem 1-0-1-0-utt.
 sei();    // iesledam partraukumus atpakal
} // pec izpildisanas lecam atpakal, kur partraucamies
 
void special_delay(uint16_t time_ms){// speciala pauze, kura tiek partraukta, ja noticis partraukums
 while((time_ms > 0) && (buttonInterruptFlag == 0)){  // kamer nav noticis partraukums un nav veikts 'time_ms'
_delay_ms(1);                                    // rezes partraukums, turpinam 'cilpot' !
time_ms--;   // pec katras cilpas samazinam cilpu skaititaju par 1
}
}
 
void special_short_delay(uint16_t time_us){// speciala pauze, kura tiek partraukta, ja noticis partraukums
 while((time_us > 0) && (buttonInterruptFlag == 0)){  // kamer nav noticis partraukums un nav veikts 'time_ms'
  _delay_us(1);                                    // rezes partraukums, turpinam 'cilpot' !
  time_us--;   // pec katras cilpas samazinam cilpu skaititaju par 1
 }
}
 
void showSequence1(){ // paradam pirmo sablonu un atgriezamies atpakal
 uint8_t count = 0;
 while((count < (3*1)) && (buttonInterruptFlag == 0)){ // ja nav paradits viss efekts un nav noticis partraukums, turpinam
  softSpiTransfer(pgm_read_byte(sequence1data + count+2)); // sutam 3 registra datus ("3")
  softSpiTransfer(pgm_read_byte(sequence1data + count+1)); // sutam 2 registra datus ("2")
  softSpiTransfer(pgm_read_byte(sequence1data + count)); // sutam 1 registra datus ("1")
  count+=3;
  showSpiData();
  special_delay(250);
 }
}
 
void showSequence2(){ // paradam otro sablonu un atgriezamies atpakal
 uint8_t count2 = 0;
 while((count2 < (3*27)) && (buttonInterruptFlag == 0)){ // ja nav paradits viss efekts un nav noticis partraukums, turpinam
  softSpiTransfer(pgm_read_byte(sequence2data + count2+2)); // sutam 3 registra datus ("3")
  softSpiTransfer(pgm_read_byte(sequence2data + count2+1)); // sutam 2 registra datus ("2")
  softSpiTransfer(pgm_read_byte(sequence2data + count2)); // sutam 1 registra datus ("1")
  count2+=3;
  showSpiData();
  special_delay(70);
 }
}
 
void showSequence3(){ // paradam tresho sablonu un atgriezamies atpakal
 uint8_t count3 = 0;
 while((count3 < 16) && (buttonInterruptFlag == 0)){ // ja nav paradits viss efekts un nav noticis partraukums, turpinam
  softSpiTransfer(pgm_read_byte(sequence3data + count3)); // sutam 3 registra datus ("3")
  softSpiTransfer(pgm_read_byte(sequence3data + count3)); // sutam 2 registra datus ("2")
  softSpiTransfer(pgm_read_byte(sequence3data + count3)); // sutam 1 registra datus ("1")
  count3++;
  showSpiData();
  special_delay(100);
 }
}
 
void showSequence4(){  // paradam ceturto sablonu un atgriezamies atpakal
 uint8_t count4 = 0, count4_1 = 0;
 while((count4 < 150) && (buttonInterruptFlag == 0)){ // ja nav paradits viss efekts un nav noticis partraukums, turpinam
  count4_1 = 0;
  while((count4_1 < 10) && (buttonInterruptFlag == 0)){
   softSpiTransfer(0b11111111);
   softSpiTransfer(0b11111111);
   softSpiTransfer(0b11111111);
   showSpiData();
   special_short_delay(count4);  // speciala pauze, kura tiek partraukta, ja mainiga noticis partraukums
   softSpiTransfer(0b00000000);
   softSpiTransfer(0b00000000);
   softSpiTransfer(0b00000000);
   showSpiData();
   special_short_delay(150-count4);  // speciala pauze, kura tiek partraukta, ja mainiga noticis partraukums
   count4_1++;
  }
  count4++;
 }
 count4 = 0;
 while((count4 < 150) && (buttonInterruptFlag == 0)){ // ja nav paradits viss efekts un nav noticis partraukums, turpinam
  count4_1 = 0;
  while((count4_1 < 10) && (buttonInterruptFlag == 0)){
   softSpiTransfer(0b11111111);
   softSpiTransfer(0b11111111);
   softSpiTransfer(0b11111111);
   showSpiData();
   special_short_delay(150-count4);  // speciala iisa pauze, kura tiek partraukta, ja mainiga noticis partraukums
   softSpiTransfer(0b00000000);
   softSpiTransfer(0b00000000);
   softSpiTransfer(0b00000000);
   showSpiData();
   special_short_delay(count4);  // speciala iisa pauze, kura tiek partraukta, ja mainiga noticis partraukums
   count4_1++;
  }
  count4++;
 }
}
 
void ioinit(){
 DDRB = 0b11111111;     // PORTB visas izejas
 DDRD = 0b11111011;    // PD2 ieeja (pogai)
 PORTD |= _BV(BUTTON);  // iesledzam "pullup" pogai
 
//  Ja lietojat Atmega8, tad INT0 partraukumu konfigurejam sadi
 MCUCR = 0;    // partraukums uz INT0 un INT1 kajam pie nulles limena, un mes negulam
 GICR |= _BV(INT0);    // aktivizejam INT0 partraukumu
 
//  Ja lietojat Atmega48/88/168/328, tad INT0 partraukumu konfigurejam sadi
//  EICRA = 0;    // partraukums uz INT0 un INT1 kajam pie nulles limena
//  EIMSK |= _BV(INT0);    // aktivizejam INT0 partraukumu
 sei();    // iesledzam partraukumus
}
 
int main(void){
 ioinit();     // sagatavojam mikroprocesoru
 while(1){     // izpildit vienmer (bezgaliga cilpa)
  buttonInterruptFlag = 0;  // atzimejam, ka esam jau 'izlekusi' ara noefekta, lai tagad tur paliktu
  switch(flag){
   case 0: showSequence1(); break;
   case 1: showSequence2(); break;
   case 2: showSequence3(); break;
   case 3: showSequence4(); break;
   default: break;
  }
 }
 return 0;
}