/*
 *
 * DDS Sine Generator mit ATMEGS 168
 * Timer2 generates the  31250 KHz Clock Interrupt
 *
 * KHM 2009 /  Martin Nawrath
 * Kunsthochschule fuer Medien Koeln
 * Academy of Media Arts Cologne

 */


#define RX_PIN 4
#define TX_PIN 5
#define PA_PIN 6
#define DO_PIN 11
#define CK_PIN 12
#define LE_PIN 10

#define MOD_PIN 3 // Modulator output, series 5 kOhm to (47nf and 1 kOhm in parallel to the ground)
                  // Output is connected to pin 10 of TL064C on the bottom side of the PCB

#define BTN_PIN 2

//uint8_t data[6] = {0x20, 0x44, 0x00, 0x32, 0x06, 0x31}; // RX 460,112.5 KHz
uint8_t datarx[6] = {0x20, 0x44, 0x00, 0x31, 0xec, 0x36}; // RX 439,375 KHz
//uint8_t data[6] = {0x20, 0x44, 0x00, 0x31, 0xe1, 0x08}; // RX 430,000 KHz
//uint8_t datarx[6] = {0x20, 0x44, 0x00, 0x31, 0xeb, 0x38}; // RX 438,600 KHz
//uint8_t datarx[6] = {0x20, 0x44, 0x00, 0x31, 0xf5, 0x0b}; // RX 446,037.5 KHz

//uint8_t data[6] = {0x20, 0x44, 0x00, 0x32, 0x32, 0x29}; // TX 450,112.5 KHz
//uint8_t datatx[6] = {0x20, 0x44, 0x00, 0x32, 0x1b, 0x2e}; // TX 431.775 KHz
//uint8_t data[6] = {0x20, 0x44, 0x00, 0x32, 0x19, 0x20}; // TX 430,000 KHz
//uint8_t data[6] = {0x20, 0x44, 0x00, 0x32, 0x26, 0x00}; // TX 440,000 KHz
//uint8_t datatx[6] = {0x20, 0x44, 0x00, 0x32, 0x24, 0x10}; // TX 438,600 KHz
//uint8_t datatx[6] = {0x20, 0x44, 0x00, 0x32, 0x1C, 0x24}; // TX 432.450  
uint8_t datatx[6] = {0x20, 0x44, 0x00, 0x32, 0x1C, 0x28}; // TX 432.500  
//uint8_t datatx[6] = {0x20, 0x44, 0x00, 0x32, 0x2d, 0x23}; // TX 446.037.5 KHz


// table of 256 sine values / one sine period / stored in flash memory
const unsigned char sine256[] PROGMEM = {
  127,130,133,136,139,143,146,149,152,155,158,161,164,167,170,173,176,178,181,184,187,190,192,195,198,200,203,205,208,210,212,215,217,219,221,223,225,227,229,231,233,234,236,238,239,240,
  242,243,244,245,247,248,249,249,250,251,252,252,253,253,253,254,254,254,254,254,254,254,253,253,253,252,252,251,250,249,249,248,247,245,244,243,242,240,239,238,236,234,233,231,229,227,225,223,
  221,219,217,215,212,210,208,205,203,200,198,195,192,190,187,184,181,178,176,173,170,167,164,161,158,155,152,149,146,143,139,136,133,130,127,124,121,118,115,111,108,105,102,99,96,93,90,87,84,81,78,
  76,73,70,67,64,62,59,56,54,51,49,46,44,42,39,37,35,33,31,29,27,25,23,21,20,18,16,15,14,12,11,10,9,7,6,5,5,4,3,2,2,1,1,1,0,0,0,0,0,0,0,1,1,1,2,2,3,4,5,5,6,7,9,10,11,12,14,15,16,18,20,21,23,25,27,29,31,
  33,35,37,39,42,44,46,49,51,54,56,59,62,64,67,70,73,76,78,81,84,87,90,93,96,99,102,105,108,111,115,118,121,124

};
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

//FONTS
const uint8_t fonts[] PROGMEM = {
        0x00, 0x18, 0x24, 0x62, 0x62, 0x62, 0x7E, 0x62, 0x62, 0x62, 0x00, //00: A
        0x00, 0x7C, 0x32, 0x32, 0x32, 0x3C, 0x32, 0x32, 0x32, 0x7C, 0x00, //01: B
        0x00, 0x3C, 0x62, 0x62, 0x60, 0x60, 0x60, 0x62, 0x62, 0x3C, 0x00, //02: C
        0x00, 0x7C, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x7C, 0x00, //03: D
        0x00, 0x7E, 0x60, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x60, 0x7E, 0x00, //04: E
        0x00, 0x7E, 0x60, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x60, 0x60, 0x00, //05: F
        0x00, 0x3C, 0x62, 0x62, 0x60, 0x60, 0x66, 0x62, 0x62, 0x3C, 0x00, //06: G
        0x00, 0x62, 0x62, 0x62, 0x62, 0x7E, 0x62, 0x62, 0x62, 0x62, 0x00, //07: H
        0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, //08: I
        0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x4C, 0x4C, 0x4C, 0x38, 0x00, //09: J
        0x00, 0x62, 0x64, 0x68, 0x70, 0x68, 0x64, 0x62, 0x62, 0x62, 0x00, //10: K
        0x00, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7E, 0x00, //11: L
        0x00, 0x42, 0x62, 0x76, 0x6A, 0x62, 0x62, 0x62, 0x62, 0x62, 0x00, //12: M
        0x00, 0x42, 0x62, 0x72, 0x6A, 0x66, 0x62, 0x62, 0x62, 0x62, 0x00, //13: N
        0x00, 0x3C, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x3C, 0x00, //14: O
        0x00, 0x7C, 0x62, 0x62, 0x62, 0x7C, 0x60, 0x60, 0x60, 0x60, 0x00, //15: P
        0x00, 0x3C, 0x62, 0x62, 0x62, 0x62, 0x62, 0x6A, 0x6A, 0x3C, 0x08, //16: Q
        0x00, 0x7C, 0x62, 0x62, 0x62, 0x7C, 0x68, 0x64, 0x62, 0x62, 0x00, //17: R
        0x00, 0x3C, 0x62, 0x60, 0x60, 0x3C, 0x06, 0x06, 0x46, 0x3C, 0x00, //18: S
        0x00, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, //19: T
        0x00, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x3C, 0x00, //20: U
        0x00, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x22, 0x14, 0x08, 0x00, //21: V
        0x00, 0x62, 0x62, 0x62, 0x62, 0x62, 0x6A, 0x76, 0x62, 0x42, 0x00, //22: W
        0x00, 0x42, 0x62, 0x74, 0x38, 0x1C, 0x2E, 0x46, 0x42, 0x42, 0x00, //23: X
        0x00, 0x42, 0x62, 0x74, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, //24: Y
        0x00, 0x7E, 0x06, 0x0E, 0x0C, 0x18, 0x30, 0x70, 0x60, 0x7E, 0x00, //25: Z
        0x00, 0x3C, 0x62, 0x62, 0x66, 0x6A, 0x72, 0x62, 0x62, 0x3C, 0x00, //26: 0
        0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, //27: 1
        0x00, 0x3C, 0x46, 0x06, 0x06, 0x1C, 0x20, 0x60, 0x60, 0x7E, 0x00, //28: 2
        0x00, 0x3C, 0x46, 0x06, 0x06, 0x1C, 0x06, 0x06, 0x46, 0x3C, 0x00, //29: 3
        0x00, 0x0C, 0x1C, 0x2C, 0x4C, 0x4C, 0x7E, 0x0C, 0x0C, 0x0C, 0x00, //30: 4
        0x00, 0x7E, 0x60, 0x60, 0x60, 0x7C, 0x06, 0x06, 0x46, 0x3C, 0x00, //31: 5
        0x00, 0x3C, 0x62, 0x60, 0x60, 0x7C, 0x62, 0x62, 0x62, 0x3C, 0x00, //32: 6
        0x00, 0x7E, 0x06, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, //33: 7
        0x00, 0x3C, 0x62, 0x62, 0x62, 0x3C, 0x62, 0x62, 0x62, 0x3C, 0x00, //34: 8
        0x00, 0x3C, 0x46, 0x46, 0x46, 0x3E, 0x06, 0x06, 0x46, 0x3C, 0x00, //35: 9
        0x00, 0x00, 0x02, 0x06, 0x0E, 0x1C, 0x38, 0x70, 0x60, 0x40, 0x00, //36: /
        0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x7E, 0x00, 0x00, 0x00, 0x00, //37: -
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x00, //38: .
        0x00, 0x3C, 0x46, 0x06, 0x06, 0x0C, 0x10, 0x00, 0x30, 0x30, 0x00, //39: ?
        0x00, 0x18, 0x18, 0x18, 0x18, 0x10, 0x10, 0x00, 0x18, 0x18, 0x00, //40: !
        0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, //41: :
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  //42: space
};

// Buf 80x60
uint8_t frameBuf[600]; //160*120/8/2/2

void clear_frame_buf()
{
  for (int i=0; i<600; i++)
    frameBuf[i] = 0;
}

void set_pix(uint8_t x, uint8_t y)
{
  uint16_t pos = y*80 + x;
  frameBuf[pos>>3] |= (1<<(pos&7));
}

void put_char(uint8_t sx, uint8_t sy, char ch)
{
  byte fontNumber;

  if(ch >= 65 && ch <= 90) //A to Z
  {
          fontNumber = ch - 65;
  }
  else if(ch >= 48 && ch <= 57) //0 to 9
  {
          fontNumber = ch - 22;
  }
  else if(ch == '/'){fontNumber = 36;}
  else if(ch == '-'){fontNumber = 37;}
  else if(ch == '.'){fontNumber = 38;}
  else if(ch == '?'){fontNumber = 39;}
  else if(ch == '!'){fontNumber = 40;}
  else if(ch == ':'){fontNumber = 41;}
  else if(ch == ' '){fontNumber = 42;}
  else              {fontNumber = 42;}  

  for(int y=0; y<11; y++)
  {
    //char bb = pgm_read_byte(fonts+fontNumber*11+y);
    char bb = pgm_read_byte(fonts+fontNumber*11+y);
    //if((fonts[fontNumber][y] & mask) != 0)

     for(int x=0; x<8; x++)    
     {
        uint8_t mask;
        mask = 1<<(7 - x);

        if((bb & mask) != 0)
         set_pix(sx+x, sy+y);                               
     }
  }
  
}

void put_stringP(uint8_t sx, uint8_t sy, char* s)
{
  uint8_t x = sx;
  uint8_t y = sy;
  char c;
  for (char*p = s; c=pgm_read_byte(p); p++)
  {
    if ( c != '\n' )
    {
      put_char(x, y, c);
      x += 8;
    }
    else
    {
      x = sx;
      y += 12;
    }
  }
}

void put_string(uint8_t sx, uint8_t sy, char* s)
{
  uint8_t x = sx;
  uint8_t y = sy;
  char c;
  for (char*p = s; c=*p; p++)
  {
    if ( c != '\n' )
    {
      put_char(x, y, c);
      x += 8;
    }
    else
    {
      x = sx;
      y += 12;
    }
  }
}

unsigned long img_counter = 0;

//double dfreq;
// const double refclk=31372.549;  // =16MHz / 510
const double refclk=31376.6;      // measured

// variables used inside interrupt service declared as voilatile
volatile byte icnt;              // var inside interrupt
volatile byte icnt1;             // var inside interrupt
volatile byte c4ms;              // counter incremented all 4ms
volatile unsigned long phaccu;   // pahse accumulator
volatile unsigned long tword_m;  // dds tuning word m

void set_audio_fq(uint16_t af)
{
  tword_m=pow(2,32)*af/refclk;  // calulate DDS new tuning word 
}

volatile uint32_t wait_timer = 0;

// Wait functions are using PWM timer

void wait_ms(uint16_t ms)
{
  wait_timer = 31.37254901960784 * ms;
  while (wait_timer) 
  {
  }
}

void wait_us(uint16_t us)
{
  wait_timer = 0.03137254901960784 * us;
  while (wait_timer) 
  {
  }
}

void shift_byte(uint8_t data)
{
  int i;
  for (i = 0x80; i > 0; i >>= 1)
  {
    digitalWrite(CK_PIN, LOW);
    digitalWrite(DO_PIN, data & i ? HIGH : LOW);
    digitalWrite(CK_PIN, HIGH);
  }
}


void init_pll()
{
  digitalWrite(PA_PIN, LOW);
  pinMode(PA_PIN, OUTPUT);
  digitalWrite(RX_PIN, HIGH);
  pinMode(RX_PIN, OUTPUT);
  digitalWrite(TX_PIN, HIGH);
  pinMode(TX_PIN, OUTPUT);
  digitalWrite(LE_PIN, HIGH);
  pinMode(LE_PIN, OUTPUT);
  digitalWrite(CK_PIN, LOW);
  pinMode(CK_PIN, OUTPUT);
  pinMode(DO_PIN, OUTPUT);

  pinMode(BTN_PIN, INPUT_PULLUP);
  digitalWrite(BTN_PIN, HIGH);


  digitalWrite(RX_PIN, HIGH);
  _delay_ms(1);
    
  digitalWrite(LE_PIN, LOW);
  shift_byte(datarx[0]);
  digitalWrite(LE_PIN, HIGH);
  _delay_ms(1);

  digitalWrite(LE_PIN, LOW);
  shift_byte(datarx[1]);
  shift_byte(datarx[2]);
  digitalWrite(LE_PIN, HIGH);
  _delay_ms(1);

  digitalWrite(LE_PIN, LOW);
  shift_byte(datatx[3]);
  shift_byte(datatx[4]);
  shift_byte(datatx[5]);
  digitalWrite(LE_PIN, HIGH);

  digitalWrite(TX_PIN, LOW);

  digitalWrite(PA_PIN, HIGH);

  _delay_ms(10);
}


void setup()
{
   digitalWrite(MOD_PIN, LOW);
   pinMode(MOD_PIN, OUTPUT);
  
    init_pll();

//  Serial.begin (9600);
//  Serial.println("DDS Test");
//  Serial.print(F_CPU);


  Setup_timer2();

  // disable interrupts to avoid timing distortion
  cbi (TIMSK0,TOIE0);              // disable Timer0 !!! delay() is now not available

  set_audio_fq(0);

  sbi (TIMSK2,TOIE2);              // enable Timer2 Interrupt
}

byte state = 0;

volatile uint8_t pic_line = 120;
volatile uint8_t pic_col = 0;

volatile uint8_t pixel_clk = 0;
volatile uint16_t line_clk = 0;


void loop()
{
     digitalWrite(LED_BUILTIN, HIGH);

      // If button in pressed during start-up, generate signal with 1 kHz FM
     if (0 == digitalRead(BTN_PIN))
     {
          set_audio_fq(1000);

          while(1) {}
     }

              // Silence
              set_audio_fq(0);
              wait_ms(2000);

                // Prepare text
                clear_frame_buf();
//                set_pix(10,10);
//                put_char(20,20,'A');
//                put_char(30,30,'B');
//                put_char(40,40,'C');

//                  put_string(10, 0, PSTR("UT4UWJ"));
                  put_stringP(10, 7, PSTR("UT4UWJ\nKO50FK\n432.500\n"));


                  img_counter ++;
                  char s[13] = "N:";
                  ultoa(img_counter, s+2, 9);
                  put_string(10, 7+12*3, s);
                  

// https://github.com/uzura-saan/arduino_sstv_bw8/blob/master/arduino_sk_19/arduino_sk_19.ino

                //--VIS CODE
                //VIS CODE for ROBOT B/W 8S is B0000010 (DECIMAL 2)
                set_audio_fq(1900);
                wait_ms(300);
                set_audio_fq(1200); //BREAK
                wait_ms(10);
                set_audio_fq(1900);
                wait_ms(300);
                set_audio_fq(1200); //START BIT
                wait_ms(30);
                set_audio_fq(1300); //BIT 0 (LSB FIRST)
                wait_ms(30);
                set_audio_fq(1100); //BIT 1
                wait_ms(30);
                set_audio_fq(1300); //BIT 2, 3, 4, 5, 6
                wait_ms(150);
                set_audio_fq(1100); //EVEN PARITY
                wait_ms(30);
                set_audio_fq(1200); //STOP BIT
                wait_ms(30);
                //--VIS DONE

               //--sync
                set_audio_fq(1200);
                wait_us(10000);
                //--

              // Start sending picture

              pixel_clk = 11;
              line_clk = 0;
              pic_col = 0;
              pic_line = 0; // This is a trigger

              while (pic_line < 120)
              {
                // Wait until the whole picture is transmitted
              }
//return;
              // Silence
              set_audio_fq(0);
              wait_ms(2000);

              // TX OFF
              //digitalWrite(PA_PIN, LOW);
              //digitalWrite(TX_PIN, HIGH);
              digitalWrite(PA_PIN, LOW);
              digitalWrite(LED_BUILTIN, LOW);

              // Pause
              for(int p=0; p<60*5; p++)
                wait_ms(1000);

//return;
              // Re-init PLL
              init_pll();

              
/*  
  while(1) {
     if ((0 == digitalRead(PTT_PIN)) && (0 == state))
     {
       state = 1;
       Serial.println("TX");
      digitalWrite(RX_PIN, HIGH);
      digitalWrite(LE_PIN, LOW);
      shift_byte(datatx[3]);
      shift_byte(datatx[4]);
      shift_byte(datatx[5]);
      digitalWrite(LE_PIN, HIGH);
      digitalWrite(TX_PIN, LOW);
      digitalWrite(PA_PIN, HIGH);
     }

     if ((1 == state) && (digitalRead(PTT_PIN)))
     {
      digitalWrite(PA_PIN, LOW);
      state = 0;
       Serial.println("RX");
      digitalWrite(TX_PIN, HIGH);
      digitalWrite(LE_PIN, LOW);
      shift_byte(datarx[3]);
      shift_byte(datarx[4]);
      shift_byte(datarx[5]);
      digitalWrite(LE_PIN, HIGH);
      digitalWrite(RX_PIN, LOW);
     
     }
    
     if (c4ms > 250) {                 // timer / wait fou a full second
      c4ms=0;
//      dfreq=67;
//
//      cbi (TIMSK2,TOIE2);              // disble Timer2 Interrupt
//      tword_m=pow(2,32)*dfreq/refclk;  // calulate DDS new tuning word
//      sbi (TIMSK2,TOIE2);              // enable Timer2 Interrupt 

      Serial.print(dfreq);
      Serial.print("  ");
      Serial.println(tword_m);
    }

    

//   sbi(PORTD,6); // Test / set PORTD,7 high to observe timing with a scope
//   cbi(PORTD,6); // Test /reset PORTD,7 high to observe timing with a scope
  }
 */ 
 }
//******************************************************************
// timer2 setup
// set prscaler to 1, PWM mode to phase correct PWM,  16000000/510 = 31372.55 Hz clock
void Setup_timer2() {

// Timer2 Clock Prescaler to : 1
  sbi (TCCR2B, CS20);
  cbi (TCCR2B, CS21);
  cbi (TCCR2B, CS22);

  // Timer2 PWM Mode set to Phase Correct PWM
  cbi (TCCR2A, COM2B0);  // clear Compare Match
  sbi (TCCR2A, COM2B1);

  sbi (TCCR2A, WGM20);  // Mode 1  / Phase Correct PWM
  cbi (TCCR2A, WGM21);
  cbi (TCCR2B, WGM22);
}

//******************************************************************
// Timer2 Interrupt Service at 31372,550 KHz = 32uSec
// this is the timebase REFCLOCK for the DDS generator
// FOUT = (M (REFCLK)) / (2 exp 32)
// runtime : 8 microseconds ( inclusive push and pop)
ISR(TIMER2_OVF_vect) {

//  sbi(PORTD,7);          // Test / set PORTD,7 high to observe timing with a oscope

  // Generate sine wave

  phaccu=phaccu+tword_m; // soft DDS, phase accu with 32 bits
  icnt=phaccu >> 24;     // use upper 8 bits for phase accu as frequency information
                         // read value fron ROM sine table and send to PWM DAC
  OCR2B=pgm_read_byte_near(sine256 + icnt);    

/*
  if(icnt1++ == 125) {  // increment variable c4ms all 4 milliseconds
    c4ms++;
    icnt1=0;    
   }   
*/

  // Advance wait timer

  if (wait_timer)
    wait_timer--;

  // Output the image

  if (pic_line < 120)
  {
      pixel_clk++;
      
      if (pixel_clk==12) // Next pixel
      {
          pixel_clk = 0;

          if (pic_col<160)
          {
            uint16_t pos = pic_line/2*80 + pic_col/2;
            uint8_t b = frameBuf[pos>>3] & (1<<(pos&7));
  
            if (b)
              set_audio_fq(1500); // Symbol, set pixel to Black
            else                
            {  
              // Set pixel to rainbow color
              
              //set_audio_fq(1700+(pic_col&0b11111000)*3);
              set_audio_fq(1700+(pic_col&0b11110000)*4);
              //set_audio_fq(2300-(pic_col&0b11110000)*3);
              //set_audio_fq(1700+pic_col*3);
            }

            pic_col++;
          }
          else
          if (pic_col==160)
          {
            set_audio_fq(1200); // Synch
            pic_col = 161;
            pixel_clk = 11;
            line_clk = 0;
          }
          else
          {
           pixel_clk = 11;
           //  line_clk++;
            // if (line_clk >=2091)        
            if (line_clk < 171)
              line_clk++;
            else        
            {
              pic_col = 0;
              pic_line++;
            }
               
          }
      }


  }



}
