//=============================================================================== // 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; int fred = 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,"!@#$%^&*(){}[]:;?><"); DDRA = 0xff; PTA = 0x00; while(!(ReadSwitch(SW1))); PTA = 0x56; rtiCnt = 1; while(1) { if (0 == rtiCnt % 41) { ++LED; if (LED == 16) LED=0; LEDOut(LED); rtiCnt=1; PTA = 0x00; } } } //=============================================================================== // RealTimeInterrupt // - Led duty cycle // - Counters for the foreground loop //=============================================================================== __interrupt void RealTimeInterrupt(void) { ++subCnt; 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; }