//===============================================================================
// miniDragon+ test program for GNU Complier.
//
// Written by Jim Donelson 68hc12@jimdonelson.com
//
//  GNU General Public License applies.
//
// same basic features as test.asm
// this is the GNU tool chain version.
// Built with EmbeddedGNU and GNU Version 3.3.6
//  Starts at 2000h
// This assumes a vector forwardng table at $3E00 in
// DBG12 format.
// adjust path to libs in Options/Project Option/Edit Profile
//
// What happens:
// The LED will count. If you press any of the switchs (1-4)
// it will count faster.
// The pot will act as a brightenss countrol for the LED.
// A song will play. If you put a jumper on sw4 it will only play once.
// If you have an LCD connected, the current ADC value will be displayed.
// The LCD will scroll, and the ADC value will change the scroll speed.
// If you have the keypad connected, pressed keys will be displayed on the LCD
// If it is a 20x4 LCD the lower two lines will have text displayed.
//
// This project has debug symbols output enabled.
// Use NOICE to debug. Get a BDM!!!
//
//===============================================================================

#include "main.h"

//===============================================================================
//===============================================================================
#include "lcd.h"
//===============================================================================
//===============================================================================

static volatile byte rtiCnt; // Forground loop cycle counter.
static volatile byte subCnt; // Counter to support rtiCnt
// Used for brightness control
static volatile  byte LEDDutyCycle = 128;    // How bright it should be.
static volatile  byte LEDytyCycleCounter=0;  // Counts ticks 
//===============================================================================
//===============================================================================

int t5delay;          // T5 is the note.
int t6delay;          // T6 in used to time the notes.
int t6acc = 1000;     // T6 count down timer
int t6start = 1700;   // T6 reset value for a whole note.
int seconds = 0;
int song_index=0;     // index into the song array
int song_counter = 0;// how may times the song has played

char *initMessage = "Wytec miniDragon12+ Test GNU Build v1.2 Press Keypad";

#define NUMBER_OF_DIGITS 16
//===============================================================================
// Musical note frequencys
//===============================================================================
#define c3		45866		// 261.63 Hz at 24 MHz
#define c3s		43293		// 277.18 Hz at 24 MHz
#define d3		40864		// 293.66 Hz at 24 MHz
#define d3s		38569		// 311.13 Hz at 24 MHz
#define e3		36404		// 329.63 Hz at 24 MHz
#define f3		34361		// 349.23 Hz at 24 MHz
#define f3s		32433		// 369.99 Hz at 24 MHz
#define g3		30613		// 391.99 Hz at 24 MHz
#define g3s		28894		// 415.31 Hz at 24 MHz
#define a3		27273		// 440.00 Hz at 24 MHz
#define a3s		25742		// 466.16 Hz at 24 MHz
#define b3		24297		// 493.88 Hz at 24 MHz

#define c4		22934		// 523.25 Hz at 24 MHz
#define c4s		21646		// 554.37 Hz at 24 MHz
#define d4		20431		// 587.33 Hz at 24 MHz
#define d4s		19285		// 622.25 Hz at 24 MHz
#define e4		18202		// 659.26 Hz at 24 MHz
#define f4		17181		// 698.46 Hz at 24 MHz
#define f4s		16216		// 739.99 Hz at 24 MHz
#define g4		15306		// 783.99 Hz at 24 MHz
#define g4s		14447		// 830.61 Hz at 24 MHz
#define a4		13636		// 880.00 Hz at 24 MHz
#define a4s		12871		// 932.32 Hz at 24 MHz
#define b4		12149		// 987.77 Hz at 24 MHz

#define c5		11467		// 1046.50 Hz at 24 MHz
#define c5s		10823		// 1108.73 Hz at 24 MHz
#define d5		10216		// 1174.66 Hz at 24 MHz
#define d5s		9642		// 1244.51 Hz at 24 MHz
#define e5		9101		// 1318.51 Hz at 24 MHz
#define f5		8590		// 1396.91 Hz at 24 MHz
#define f5s		8108		// 1479.98 Hz at 24 MHz
#define g5		7653		// 1567.98 Hz at 24 MHz
#define g5s		7225		// 1661.22 Hz at 24 MHz
#define a5		6818		// 1760.00 Hz at 24 MHz
#define a5s		6435		// 1864.66 Hz at 24 MHz
#define b5		6074		// 1975.53 Hz at 24 MHz

#define c6		5733		// 2093.00 Hz at 24 MHz
#define c6s		5412		// 2217.46 Hz at 24 MHz
#define d6		5109		// 2349.32 Hz at 24 MHz
#define d6s		4821		// 2489.02 Hz at 24 MHz
#define e6		4551		// 2637.02 Hz at 24 MHz
#define f6		4295		// 2793.83 Hz at 24 MHz
#define f6s		4054		// 2959.96 Hz at 24 MHz
#define g6		3827		// 3135.97 Hz at 24 MHz
#define g6s		3612		// 3322.44 Hz at 24 MHz
#define a6		3409		// 3520.00 Hz at 24 MHz
#define a6s		3218		// 3729.31 Hz at 24 MHz
#define b6		3037		// 3951.07 Hz at 24 MHz

#define c7		2867		// 4186.01 Hz at 24 MHz
#define c7s		2706		// 4434.92 Hz at 24 MHz
#define d7		2554		// 4698.64 Hz at 24 MHz
#define d7s		2411		// 4978.03 Hz at 24 MHz
#define e7		2275		// 5274.04 Hz at 24 MHz
#define f7		2148		// 5587.66 Hz at 24 MHz
#define f7s		2027		// 5919.92 Hz at 24 MHz
#define g7		1913		// 6271.93 Hz at 24 MHz
#define g7s		1806		// 6644.88 Hz at 24 MHz
#define a7		1705		// 7040.00 Hz at 24 MHz
#define a7s		1609		// 7458.63 Hz at 24 MHz
#define b7		1519		// 7902.13 Hz at 24 MHz
#define c8		1		// for rest note

//===============================================================================
// Quick look up table - save bytes for a song.
//===============================================================================
unsigned int notes[]={
		c3,c3s,d3,d3s,e3,f3,f3s,g3,g3s,a3,a3s,b3,
		 0,0,0,0,		// We do this so the upper nibble is octave of a note
		c4,c4s,d4,d4s,e4,f4,f4s,g4,g4s,a4,a4s,b4,
		 0,0,0,0,
		c5,c5s,d5,d5s,e5,f5,f5s,g5,g5s,a5,a5s,b5,
		 0,0,0,0,
		c6,c6s,d6,d6s,e6,f6,f6s,g6,g6s,a6,a6s,b6,
		 0,0,0,0,
		c7,c7s,d7,d7s,e7,f7,f7s,g7,g7s,a7,a7s,b7,
		 0,0,0,0,
		c8
};
// Indexes of notes.
#define note_c		0
#define note_cs		1
#define note_d		2
#define note_ds		3
#define note_e		4
#define note_f		5
#define note_fs		6
#define note_g		7
#define note_gs		8
#define note_a		9
#define note_as		10
#define note_b		11


#define WHOLE	    1
#define HALF	    2
#define QUARTER     4
#define EIGHTH      8
#define SIXTEENTH   16

#define REST        0xfe
#define SONG_END    0xff

byte song[]={
//   Declare a song.....
		0x20+note_e,EIGHTH,
		0x20+note_ds,EIGHTH,
		0x20+note_e,EIGHTH,
		0x20+note_ds,EIGHTH,
		0x20+note_e,EIGHTH,
		0x10+note_b,EIGHTH,
		0x20+note_d,EIGHTH,
		0x20+note_c,EIGHTH,
		0x10+note_a,QUARTER,
		0x00+note_e,EIGHTH,
		0x00+note_a,EIGHTH,
		0x10+note_c,EIGHTH,
		0x10+note_e,EIGHTH,
		0x10+note_a,EIGHTH,
		0x10+note_b,QUARTER,
		0x00+note_gs,EIGHTH,
		0x10+note_d,EIGHTH,
		0x10+note_e,EIGHTH,
		0x10+note_gs,EIGHTH,
		0x10+note_b,EIGHTH,
		0x20+note_c,QUARTER,
		0x00+note_e,EIGHTH,
		0x00+note_a,EIGHTH,
		0x10+note_e,EIGHTH,
		REST,HALF,
		REST,QUARTER,
		SONG_END,SONG_END,
};
//===============================================================================
// Converstion routines.
//===============================================================================

void _uitoa(unsigned int value, char* string, unsigned char radix)
{
  unsigned char index, i;
  index = NUMBER_OF_DIGITS;
  i = 0;

  do {
    string[--index] = '0' + (value % radix);
    if ( string[index] > '9') string[index] += 'A' - '9' - 1;
    value /= radix;
  } while (value != 0);

  do {
    string[i++] = string[index++];
  } while ( index < NUMBER_OF_DIGITS );

  string[i] = 0; /* string terminator */
}

void _itoa(int value, char* string, unsigned char radix)
{
  if (value < 0 && radix == 10) {
    *string++ = '-';
    value = -value;
  }
  _uitoa(value, string, radix);
}
int _strlen(char *s)
{
    int i=0;
    while(*(s++) )
        i++;
    return i;
}
char* _strcat(char* s1, char* s2)
{
  char *s = s1;
  while(*s1 )   // move to end of s1
     s1++;
  while(*(s2) ) // copy s2 into s1
    *(s1++) = *(s2++) ;
   *s1 = 0;
   return s;
}
//===============================================================================
// miniDragon+ - 7 Segment LED Support
//
// Connected to Port H
// 7 0-6 lines of Port H used.
// Initialization:
// Port H needs to be set to output.
//   DDRH = 0xff;          // Init Port H LED set porth pins to output 
//===============================================================================

//
// LED Segment Decoder Table
//
byte segm_ptrn[]={   // segment pattern
//0    1    2    3    4    5    6    7
0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,     //0-7
//8   9   A    B     C    d    E   F
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,     //8-F
//G   H     J   n     o   o
0x3d,0x76,0x74,0x1e,0x38,0x54,0x63,0x5c,	 //10-17
//blk  -   =    =     =    =
0x00,0x01,0x48,0x41,0x09,0x49	             //20-23
};

// Used by the brightness controller.
static byte LEDValue;

void LEDOut( byte value  ) 
{
      // This is done so bit 7 is untouched and can be used.
      PTH &= 0x80;    // Clear out lower 7 bits so can or in others.
      PTH |= LEDValue =  segm_ptrn[value];  
}
//===============================================================================
// Keypad
// 4x4 Key Pad Support
//
//  Connected to PORTA 8 bits used.
//
//  Port Set Up:
//  DDRA = 0x0f;          // pa0-pa3 are outputs, pa4-pa7 are inputs
//  PUCR = PUCR | 1;      // Init Port A enable pullups on porta for Key Pad.
//===============================================================================
char KPMessage[]="Key : X         ";
#define KBDEBOUNCE   1      // How many times the kb must be equal.
#define ROW_MASK     0xf0   // Mask for the row data.
#define COL1  0xe0
#define COL2  0xd0
#define COL3  0xb0
#define COL4  0x70
//===============================================================================
// Keypad - These tables map row/cols to numbers 0-15.
//===============================================================================
// define this if you want to plug the keypad in the other way.
#ifndef INV_KEYPAD
byte _kbdecoder[4][4]={
{0xf,0xb,0x7,0x3},
{0xe,0xa,0x6,0x2},
{0xd,0x9,0x5,0x1},
{0xc,0x8,0x4,0x0},
};
#else
byte _kbdecoder[4][4]={
{0x0,0x1,0x2,0x3},
{0x4,0x5,0x6,0x7},
{0x8,0x9,0xa,0xb},
{0xc,0xd,0xe,0xf},
};
#endif
static volatile byte in_digit   = 0;

//===============================================================================
// ScanKeyPad()
// This must be called by a timer or main loop at about 10ms intervals.
//===============================================================================
byte ScanKeyPad()
{
  byte d;
  byte rowsel = 0xf7; //pa3=low, pa0-pa2=high
  byte i;
  byte input;
  byte j;
  static volatile byte last_digit = -1;
  static volatile byte kbdebounce = 0;

            
  DDRA = 0x0f; // pa0-pa3 are outputs, pa4-pa7 are inputs

  // Scan the keypad by lowering one row line at a time.
  // then checking if a col went low.
  for( i = 0, rowsel = 0xf7 ; i < 4 ; ++i)
  {
     PORTA = rowsel;
     for( d = 0 ; d < 16 ; ++d )  // small delay to settle.
        ;                      
     input = PORTA & ROW_MASK;
     
     if( input != ROW_MASK )      // a line was lo, so key pressed
        break;
     rowsel >>= 1;                // select the next row...
  }
  d = 255;
  if( i < 4 )  // Did we find any lows?
  {
       // if 2 keys are pressed, then none will match.
      switch(input) 
      {
        case COL1: j = 3; break;
        case COL2: j = 2; break;
        case COL3: j = 1; break;
        case COL4: j = 0; break;
        default:
            j = 255;;
      }
      // Have a match ?
      if( j != 255 )
        d =  _kbdecoder[i][j];
   }
  // Did anything decode ?
  if( d != 255 )
  {
      if( last_digit == d    )
      {
        if(kbdebounce != 0xff)
          ++kbdebounce;
      }
      else 
      {
        kbdebounce = 0;
        last_digit = d;
      }
      //
      // Valid key press detected ?
      //
      if(KBDEBOUNCE == kbdebounce ) 
      {
        kbdebounce = 0xff;  // means we have seen this key press  
//------------------------------------------------------
// Application specific code 
//
        if( 9 == in_digit  ) 
        {
           while( in_digit--)
              KPMessage[6+in_digit] = 0x20;
           in_digit = 0;
        }
        // Convert to Hex ASCII      
        KPMessage[6+in_digit] = d | 0x30;        
        if( d > 9 )
           KPMessage[6+in_digit] = (d-10) + 'A';

        LCDScrollLine(2,&KPMessage[0]);
        in_digit++;
        
         if( 4 == in_digit )
           KPMessage[6+(in_digit++)] = ':';
//------------------------------------------------------                         

        return d;
      }
  }           
  else
  {
    // Nothing found reset debouce.
    last_digit = -1;
    kbdebounce = 0;
  }
    
  return -1;        
         
}
//===============================================================================
// miniDragon+ Switches
// Not debounced.
//===============================================================================
// The switches go low when active.
#define SW1  0x10
#define SW2  0x08
#define SW3  0x20
#define SW4  0x40 
#define SWMASK  (SW1 + SW2 + SW3 + SW4)
#define SWPORT    PORTAD0
volatile byte switches;
//===============================================================================
//  byte ReadSwitches()  Return Raw switches
//===============================================================================
byte ReadSwitches()
{
 
      switches = SWPORT & SWMASK;
      return switches;
}
//===============================================================================
// byte ReadSwitch(byte which)
// Return TURE if switch is pressed or has a jumper on it.
//===============================================================================
byte ReadSwitch(byte which)
{
  return 0 == (SWPORT & which);
}
//===============================================================================
// ADC
//===============================================================================
// ADC Defines.
#define MULTI_MODE    0x10
#define SINGLE_MODE   0
#define SCAN_MODE     0x20
#define NO_SCAN_MODE	0
#define POT_CHANNEL_NUM   0x7		// reading input from AN07
#define SCF           0x80          //Sequence Complete Flag in ATD0STAT0

byte ADTValue;
char LCDMessage[32]="ADC 07 =        ";
//===============================================================================
// byte ReadADC(byte channel) - Read the 8 MSBs of a channel.
//===============================================================================

byte ReadADC(byte channel) 
{
  byte status;

  // This resets the conversion done flag and starts a new conversion.
  ATD0CTL5 = SINGLE_MODE+NO_SCAN_MODE+channel;
  
  for (;;) 
  {
     status = ATD0STAT0;
     if( status & SCF )
       break;
  }
  
  status = ATD0DR7H;     // Just the hi byte.
  ADTValue = status;
  
  return status;
}

//===============================================================================
//
// main
// Perform initialization and forground loop
// Called from startup.
//
//===============================================================================
 __interrupt void RealTimeInterrupt(void);
 #define LED_COUNT_RATE   50
volatile static int LEDCountRate = LED_COUNT_RATE;
int  main(void)
{
  byte LED = 0;
  asm("sei");                 // disable the global interrupts

  // Handy for debuging start up.
  LEDOut(0);

 // Poke in the interurrpt vectors for DBUG12

  UserRTI       = (unsigned short)&RealTimeInterrupt;  // 3e70
  UserTimerCh5  = (unsigned short)&Timer5Interrupt;    // 3e64
  UserTimerCh6  = (unsigned short)&Timer6Interrupt;    // 3e62

  // Port set ups.
  DDRH = 0xff;          // LED set porth pins to output 
  DDRM = 0xff;          // LCD set portm pins to output
  PUCR = PUCR | 1;      // enable pullups on porta for Key Pad.

  // ATD block set up.
  ATD0CTL2 = 0x80;      // Enable power up mode for ADC Block
  ATD0CTL3 = 0x40;      // Set 8 bit conversions.
    
  // Set the enable digital input for the switches SW1-SW4
  ATD0DIEN  = 0xff;

  LEDOut(LED);

  rtiCnt = 0;           // roll over counter for forground loop.      
  
  // Set the Real Time Clock Rate
  RTICTL = 0x10;        // 1/(16Mz/2^10) = 64 us or 15.6 ticks per ms      
  CRGINT |= 0x80;       // enable the interrupt for RTI 


  // Set up timer 5 for the speaker.
  t5delay = 1000;
  t6delay = 3000; // 1ms
  t6acc = t6start;
  TSCR1 = 0x90;               // Enable TCNT and fast clear
  TSCR2 = 0x03;               // Set prescaler to 1:8
  TIOS  |= TIOS_IOS6_MASK;    // Enable OC6 for timer
//  TIOS  |= TIOS_IOS5_MASK;  // Disable the speaker until song starts.  
  TCTL1 = 4;                  // Set toggle mode for OC5
  
  TC5 = TCNT + t5delay;       // Init the comnpare registers
  TC6 = TCNT;                 // This also resets the inturrupt.
  TIE |= TIOS_IOS5_MASK ;     // Enable the timer interrupt bits.
  TIE |= TIOS_IOS6_MASK;

  LEDOut(2);
  
  asm("cli");                 // enable the global interrupts
  
  LCD_Init();
  LCDWriteLine(1,"Ready...");
  LCDSetCharDelay(2, 1500);
  LCDScrollLine(2,initMessage);
  LCDWriteLine(3,"ABCDEFGHIJKLMNOPQRST"); 
  LCDWriteLine(4,"!@#$%^&*(){}[]:;?><"); 

  for (;;) {
    if(0 == rtiCnt % 4)
       LCDUpdateScroll();
    // Main forground loop.
    // Do things at different intervals.
    if (rtiCnt >= LEDCountRate)
    {
      // 500 ms. Jobs
      ++LED;
      if( LED == 16)
        LED = 0;
      LEDOut(LED  );

      LEDCountRate = LED_COUNT_RATE;

      if(ReadSwitch( SW1) )
        LEDCountRate >>= 1;
        
      if(ReadSwitch( SW2) )
        LEDCountRate >>= 1;
        
      if(ReadSwitch( SW3) )
        LEDCountRate >>= 1;
        
      if(ReadSwitch( SW4) )
        LEDCountRate >>= 1;

      rtiCnt = 0;
    }
    
    if(0 == rtiCnt % 10 ) 
    {
      LEDDutyCycle = ReadADC(POT_CHANNEL_NUM);
      
      _uitoa(LEDDutyCycle,&LCDMessage[9],10);
      
      if( LEDDutyCycle > 20 )
      {
        LCDSetCharDelay(2, LEDDutyCycle * 25);
      }
      
      LCDWriteLine(1,&LCDMessage[0]);
 
    }

    if(  0 == rtiCnt % 2 )
      ScanKeyPad();  
  }
} 


//===============================================================================
//  RealTimeInterrupt
// - Led duty cycle
// - Counters for the foreground loop
//===============================================================================
 __interrupt void RealTimeInterrupt(void)
{
  ++subCnt;
  
  // Brightness control for LED
  if( 0xff == LEDytyCycleCounter )
     LEDytyCycleCounter = 0; 
  LEDytyCycleCounter++;
  
  // The pot on ATD 7 controls the brightness
  if(LEDytyCycleCounter < LEDDutyCycle )
    PTH = LEDValue;
  else
    PTH = 0;
  
  if( 156 == subCnt )   
  {
     rtiCnt++;   
     subCnt = 0;
  }

  CRGFLG = 0x80; // clear rti flag         
} 
//===============================================================================
// Timer 6 interrupt
// Sequence the song and set up timer 5 for the note to be played.
//===============================================================================
__interrupt void Timer6Interrupt(void)
{
    unsigned int freq;
    int duration;
    byte note;
    
    --t6acc;
    
     if( t6acc == 0 )
     {
        // Look up note and duration for next note.
        note = song[song_index++];  
        duration =  song[song_index++];
        
        // if there is a jumper on SW4, then the song will only play 1 time.
        if( note == REST || note == SONG_END ||
                   (ReadSwitch( SW4) && song_counter >= 1))
        {
            TIOS  &= ~TIOS_IOS5_MASK;     // Shut off the speaker
            
        } else 
        {
           TIOS  |= TIOS_IOS5_MASK;       // Speaker on
           freq = notes[note]/3;          // 4,6,12
        }
        
        t6acc = t6start/duration;
        
        t5delay = freq;
        TC5 += t5delay;
        if(  song[song_index] == 0xff )
        {
           ++song_counter; 
           song_index = 0;
        }

     }
    // Reset the interrupt flag and set up next count.
    TC6 += t6delay;
}
//===============================================================================
// Generate a square wave on PT5 pin 16
//===============================================================================
__interrupt void Timer5Interrupt(void)
{
  TC5 += t5delay;
}

