| yerpa 
 
 
 Joined: 19 Feb 2004
 Posts: 58
 Location: Wisconsin
 
 
			      
 
 | 
			
				| 8-bit wav file player with Xmodem RS-232 for 16F873 |  
				|  Posted: Fri Jan 19, 2007 3:51 pm |   |  
				| 
 |  
				| Here it is, offered strictly as-is.  Please do not make fun of my nasty coding style!  Note that this is a re-post - I forgot to disable HTML last time around. 
 
  	  | Code: |  	  | /////////////////////////////////////////////////////////////////////////
 ////  T2K2v2.C  by RPL   15 OCT 2002                                 ////
 ////  Program for TONE2002 board, with audio playback, xmodem file   ////
 ////  transfers, and a simple file system.                           ////
 ////                                                                 ////
 /////////////////////////////////////////////////////////////////////////
 
 #include <16f873.h>
 #fuses HS, NOWDT, PUT, NOBROWNOUT, NOPROTECT, NOWRT, NOLVP
 #use fast_io(A)
 
 /*  DEFINE EQUATES FOR PORTS */
 
 #byte PORTA = 5
 #byte PORTB = 6
 #byte RTCC  = 1
 #byte TMR0  = 0x01   //Timer 0.
 #byte TMR1L = 0x0E   //Timer 1 LOW BYTE.
 
 #define EEPROM_SELECT PIN_A2
 #define EEPROM_DI     PIN_A0
 #define EEPROM_DO     PIN_A4
 #define EEPROM_CLK    PIN_A1
 
 #define ACK 0x06
 #define CAN 0x18
 #define DLE 0x10
 #define EOT 0x04
 #define NAK 0x15
 #define SOH 0x01
 #define SYN 0x22
 #define XOF 0x13
 #define XON 0x11
 
 #define EEPROM_ADDRESS byte
 #define EEPROM_SIZE    256
 
 #use delay(Clock=18432000, RESTART_WDT)
 #use RS232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
 
 void clr_all_ptrs();
 void dir();
 byte read_AT45_stat();
 void write_AT45_stream128(long page, byte addr2);
 void read_AT45_stream128(long page, byte addr2);
 void read_AT45_stream64(long page, byte addr2);
 byte xmodem();
 void hexdump();
 
 // Define static variables...
 
 static byte toggle_lo;
 static byte toggle_hi;
 static byte softime;
 static byte sample;
 static byte sptr;
 static byte pflag;
 static byte sflag;
 static byte filenum;
 static long file_start;
 static long file_end;
 static byte rambuf1[64];
 static byte rambuf2[64];
 
 
 // Define constants...
 
 byte const hexlist[16]={'0', '1', '2', '3', '4', '5', '6', '7',
 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
 
 // INTERRUPT ROUTINE FOR RTCC...
 
 #INT_RTCC
 void time0_handler(void)
 {
 #asm
 sync:   btfss 1, 3               //Synchronize (de-jitter) the samples.
 goto  sync
 btfss 1, 0
 nop
 #endasm
 
 PORTB=sample;                 // Write sample to DAC.
 RTCC=0x4f;
 sptr++;
 if ((sptr & 0x40)!=0) sample = rambuf2[sptr & 0x3f];
 if ((sptr & 0x40)==0) sample = rambuf1[sptr & 0x3f];
 if ((sptr & 0x3f)==0) sflag=0;
 if (pflag==0) sample=0;
 toggle_lo++;
 if (toggle_lo==0)
 {
 toggle_hi++;
 softime++;
 }
 if (toggle_hi & 0x10)
 {
 output_low(PIN_A3);        // Toggle on-board LED.
 }
 if (!(toggle_hi & 0x10))
 {
 output_high(PIN_A3);
 }
 
 }
 
 
 main()
 {
 byte ch, i, temp;
 byte ebyte;
 long page;
 filenum=0;
 SET_RTCC(0);
 setup_timer_1(T1_INTERNAL);          //Free-running.
 setup_timer_2(T2_DIV_BY_1, 0x1F, 1); //Not really used.
 
 
 SETUP_CCP1(CCP_OFF);
 SETUP_CCP2(CCP_OFF);
 //   SETUP_COUNTERS(RTCC_INTERNAL, WDT_18MS);
 SETUP_COUNTERS(RTCC_INTERNAL, RTCC_DIV_2);
 SETUP_PORT_A(NO_ANALOGS);
 SET_TRIS_A(0x10);                    //Port A0-A3, A5-A7=outputs,
 //Port A4=input.
 SET_TRIS_B(0x00);                    //Port B0-B7=outputs.
 ENABLE_INTERRUPTS(RTCC_ZERO);
 //   ENABLE_INTERRUPTS(INT_TIMER1);
 SETUP_TIMER_1(T1_INTERNAL | T1_DIV_BY_8);
 SET_TIMER1(0x3F);
 ENABLE_INTERRUPTS(GLOBAL);
 PORTB=0;
 pflag=0;
 putc(XON);
 printf ("\r\n T2Kv2 \r\n");
 top:
 printf ("\r\nS=Status, X=Xmodem transfer, H=Hexdump, P=Play.\r\n");
 printf ("C=Clear all, D=Directory.\r\n\r\n>");
 ch=getc();
 if ((ch=='S') || (ch=='s'))
 {
 printf ("\r\nreading Status...\r\n");
 ebyte = read_AT45_stat();
 printf ("\r\nStatus=");
 if (ebyte & 0x80) putc (0x31);
 if (!(ebyte & 0x80)) putc (0x30);
 if (ebyte & 0x40) putc (0x31);
 if (!(ebyte & 0x40)) putc (0x30);
 if (ebyte & 0x20) putc (0x31);
 if (!(ebyte & 0x20)) putc (0x30);
 if (ebyte & 0x10) putc (0x31);
 if (!(ebyte & 0x10)) putc (0x30);
 putc (0x020);
 if (ebyte & 0x08) putc (0x31);
 if (!(ebyte & 0x08)) putc (0x30);
 if (ebyte & 0x04) putc (0x31);
 if (!(ebyte & 0x04)) putc (0x30);
 if (ebyte & 0x02) putc (0x31);
 if (!(ebyte & 0x02)) putc (0x30);
 if (ebyte & 0x01) putc (0x31);
 if (!(ebyte & 0x01)) putc (0x30);
 }
 
 if ((ch=='X') || (ch=='x'))
 {
 ch=0;
 read_AT45_stream64(0, 0);
 output_high(EEPROM_SELECT);
 file_end=0;
 for (filenum=0; filenum < 7; filenum++)
 {
 i=filenum*4;
 file_start = rambuf1[i];
 file_start <<= 8;
 file_start |= rambuf1[i+1];
 if (file_start==0) break;
 file_end = rambuf1[i+2];
 file_end <<= 8;
 file_end |= rambuf1[i+3];
 }
 file_start = file_end + 1;
 if (filenum>=6) break;
 i = 4 * filenum;
 printf ("\r\nStart xmodem file %d transfer now...\r\n", filenum);
 ebyte=xmodem();
 if (ebyte==0)
 {
 delay_ms(25);
 read_AT45_stream64(0, 0);
 rambuf1[i]=(file_start >> 8);
 rambuf1[i+1]=(file_start & 0xFF);
 rambuf1[i+2]=(file_end >> 8);
 rambuf1[i+3]=(file_end & 0xFF);
 delay_ms(25);
 write_AT45_stream128(0, 0);
 output_high(EEPROM_SELECT);
 printf ("\r\nTransfer Complete!\r\n");
 }
 if (ebyte==1) printf ("\r\nTransfer Failed!\r\n");
 }
 
 if ((ch=='H') || (ch=='h')) hexdump();
 
 if ((ch=='C') || (ch=='c'))
 {
 clr_all_ptrs();
 printf ("\r\nDone.\r\n");
 }
 
 if ((ch=='D') || (ch=='d')) dir();
 
 if ((ch=='P') || (ch=='p'))             // PLAY loop is here.
 {
 ch=0;
 while ((ch < 0x30) || (ch > 0x35))
 {
 printf ("\r\nPick a file number to play (0-5):\r\n");
 ch=getc();
 }
 i=ch-0x30;
 i=4*i;
 disable_interrupts(global);
 read_AT45_stream64(0,0);        //Get directory.
 file_start=rambuf1[i];
 file_start <<= 8;
 file_start |= rambuf1[i+1];
 file_end=rambuf1[i+2];
 file_end <<= 8;
 file_end |= rambuf1[i+3];
 page=file_start;
 if (page==0) break;             //If no sample saved, quit.
 temp=0;
 sptr=0;
 read_AT45_stream64(page,temp);  //Pre-fill rambuf1[]
 temp=1;
 read_AT45_stream64(page,temp);  //Pre-fill rambuf2[]
 sflag=1;
 pflag=1;
 enable_interrupts(global);
 
 while ((kbhit()==FALSE) && (page <= file_end))
 {
 if (sflag==0)
 {
 temp++;
 temp &= 0x03;
 if ((temp & 0x03)==0) page++;
 read_AT45_stream64(page,temp); // Keep buffer loaded with samples.
 sflag=1;
 }
 }
 pflag=0;
 }
 output_low(PIN_A5);     //o-scope diagnostic
 output_high(PIN_A5);
 goto top;
 }
 
 
 // Read AT45DB041B status byte.  By RPL.
 
 byte read_AT45_stat() {
 
 byte cmd;
 byte i,data;
 
 cmd=0xD7;
 
 output_low(EEPROM_SELECT);
 for(i=8;i>0;i--)
 {
 output_bit(EEPROM_DI, shift_left(&cmd,1,0));
 output_high(EEPROM_CLK);
 output_low(EEPROM_CLK);
 }
 for(i=8;i>0;i--)
 {
 output_high(EEPROM_CLK);
 output_low(EEPROM_CLK);
 shift_left(&data,1,input(EEPROM_DO));
 }
 output_high(EEPROM_SELECT);
 return(data);
 }
 
 // Main mem page 128-byte write thru buffer #1 on AT45DB041B chip. By RPL.
 // Call with page address (=0-2047) and addr2 (=0 or 1).  If addr2==0,
 // data is written from rambuf1[] & rambuf2[] to the 1st (128) bytes
 // of selected page, but the chip select is not raised at the end, so
 // the write operation is not completed.  When addr2==1, data is
 // written to 2nd (128) bytes of selected page, followed by raising
 // the chip select to finish the operation.  Warning: Don't do any other
 // chip operations after a call with addr2==0.  That operation doesn't
 // end until a call with addr2==1.
 
 void write_AT45_stream128(long page, byte addr2)
 {
 
 byte cmd;
 byte i, j;
 
 cmd=0x82;
 
 output_low(EEPROM_SELECT);
 
 if (addr2==0)
 {
 for(i=8;i>0;i--)            // Write command = 0x82 to mem chip.
 {
 output_bit(EEPROM_DI, shift_left(&cmd,1,0));
 output_high(EEPROM_CLK);
 output_low(EEPROM_CLK);
 }
 for(i=4;i>0;i--)            // Load (4) "0" (reserved) page bits.
 {
 output_bit(EEPROM_DI, 0);
 output_high(EEPROM_CLK);
 output_low(EEPROM_CLK);
 }
 for(i=5;i>0;i--)            // Line up page bit P10
 {
 shift_left(&page,2,0);
 }
 for(i=12;i>0;i--)           // Load page bits P10-P0 & byte addr
 // bit B8. Note B8=0.
 {
 output_bit(EEPROM_DI, shift_left(&page,2,0));
 output_high(EEPROM_CLK);
 output_low(EEPROM_CLK);
 }
 for(i=8;i>0;i--)              // Load byte addr bits B7-B0=0.
 {
 output_bit(EEPROM_DI, 0);
 output_high(EEPROM_CLK);
 output_low(EEPROM_CLK);
 }
 }
 
 for(j=0;j<64;j++)       // Write 64 bytes of rambuf1[] to mem. chip.
 {
 cmd=rambuf1[j];
 for(i=8;i>0;i--)
 {
 output_bit(EEPROM_DI, shift_left(&cmd,1,0));
 output_high(EEPROM_CLK);
 output_low(EEPROM_CLK);
 }
 }
 for(j=0;j<64;j++)       // Write 64 bytes of rambuf2[] to mem. chip.
 {
 cmd=rambuf2[j];
 for(i=8;i>0;i--)
 {
 output_bit(EEPROM_DI, shift_left(&cmd,1,0));
 output_high(EEPROM_CLK);
 output_low(EEPROM_CLK);
 }
 }
 if (addr2 !=0) output_high(EEPROM_SELECT);
 return;
 }
 // Continuous 128-byte array read from AT45DB041B chip. By RPL.
 // Call with page address (0-2047) and addr2 (0 or 1).  If addr2==0,
 // data is read from the 1st (128) bytes of page and written into
 // rambuff1[] & rambuff2[]. If addr2 != 0, data is read from 2nd (128)
 // bytes of selected page...
 
 void read_AT45_stream128(long page, byte addr2)
 {
 byte cmd;
 byte i, j;
 
 cmd=0x68;
 
 output_low(EEPROM_SELECT);
 for(i=8;i!=0;i--)            // Send command to mem chip.
 {
 output_bit(EEPROM_DI, shift_left(&cmd,1,0));
 output_high(EEPROM_CLK);
 output_low(EEPROM_CLK);
 }
 cmd=0;
 for(i=4;i!=0;i--)            // Load (4) "0" (reserved) page bits.
 {
 output_bit(EEPROM_DI, 0);
 output_high(EEPROM_CLK);
 output_low(EEPROM_CLK);
 }
 for(i=5;i!=0;i--)            // Line up page bit P10
 {
 shift_left(&page,2,0);
 }
 for(i=12;i!=0;i--)            // Load page bits P10-P0 & byte addr
 // bit B8. Note B8=0.
 {
 output_bit(EEPROM_DI, shift_left(&page,2,0));
 output_high(EEPROM_CLK);
 output_low(EEPROM_CLK);
 }
 cmd=0;
 if (addr2 !=0) cmd = 0x80;
 for(i=8;i!=0;i--)              // Load byte addr bits B7-B0.
 {
 output_bit(EEPROM_DI, shift_left(&cmd,1,0));
 output_high(EEPROM_CLK);
 output_low(EEPROM_CLK);
 }
 cmd=0;
 for(i=32;i!=0;i--)              // Load (32) "don't care" bits.
 {
 output_bit(EEPROM_DI, 0);
 output_high(EEPROM_CLK);
 output_low(EEPROM_CLK);
 }
 for(j=0;j<64;j++)             // Read 64 bytes into rambuf1[].
 {
 for(i=8;i!=0;i--)
 {
 output_high(EEPROM_CLK);
 output_low(EEPROM_CLK);
 shift_left(&rambuf1[j],1,input(EEPROM_DO));
 }
 }
 for(j=0;j<64;j++)             // Read 64 bytes into rambuf2[].
 {
 for(i=8;i!=0;i--)
 {
 output_high(EEPROM_CLK);
 output_low(EEPROM_CLK);
 shift_left(&rambuf2[j],1,input(EEPROM_DO));
 }
 }
 output_high(EEPROM_SELECT);
 return;
 }
 
 // Continuous 64-byte array read from AT45DB041B chip. By RPL.
 // Call with page address (0-2047) and addr2.  If if lo nibble of
 // addr2==0, data is read from the 1st (64) bytes of page into rambuf1[].
 // If lo nib of addr2==1, data is read from 2nd (64) bytes of page into
 // rambuf2[]. If lo nib==2, data read from 3rd (64) bytes of page into
 // rambuf1[].  If lo nib==3, data from 4th (64) bytes of page into
 // rambuf2[]. If addr2 has hi bit set, data is read into rambuf2[]...
 
 void read_AT45_stream64(long page, byte addr2)
 {
 byte cmd;
 byte i, j;
 
 cmd=0x68;
 
 output_low(EEPROM_SELECT);
 for(i=8;i!=0;i--)            // Send command to mem chip.
 {
 output_bit(EEPROM_DI, shift_left(&cmd,1,0));
 output_high(EEPROM_CLK);
 output_low(EEPROM_CLK);
 }
 output_bit(EEPROM_DI, 0);
 for(i=4;i!=0;i--)            // Load (4) "0" (reserved) page bits.
 {
 output_high(EEPROM_CLK);
 output_low(EEPROM_CLK);
 }
 for(i=5;i!=0;i--)            // Line up page bit P10
 {
 shift_left(&page,2,0);
 }
 for(i=12;i!=0;i--)            // Load page bits P10-P0 & byte addr
 // bit B8. Note B8=0.
 {
 output_bit(EEPROM_DI, shift_left(&page,2,0));
 output_high(EEPROM_CLK);
 output_low(EEPROM_CLK);
 }
 
 if ((addr2 & 0x0f)==0) cmd=0;
 if ((addr2 & 0x0f)==1) cmd=0x40;
 if ((addr2 & 0x0f)==2) cmd=0x80;
 if ((addr2 & 0x0f)==3) cmd=0xC0;
 
 for(i=8;i!=0;i--)              // Load byte addr bits B7-B0.
 {
 output_bit(EEPROM_DI, shift_left(&cmd,1,0));
 output_high(EEPROM_CLK);
 output_low(EEPROM_CLK);
 }
 output_bit(EEPROM_DI, 0);
 for(i=32;i!=0;i--)              // Load (32) "don't care" bits.
 {
 output_high(EEPROM_CLK);
 output_low(EEPROM_CLK);
 }
 if ((addr2==0)||(addr2==2))     // Use rambuf1[]?
 {
 for(j=0;j<64;j++)             // Read 64 bytes into rambuf1[].
 {
 for(i=8;i!=0;i--)
 {
 output_high(EEPROM_CLK);
 output_low(EEPROM_CLK);
 shift_left(&rambuf1[j],1,input(EEPROM_DO));
 }
 }
 }
 if ((addr2==1)||(addr2==3))     // Use rambuf2[]?
 {
 for(j=0;j<64;j++)             // Read 64 bytes into rambuf2[].
 {
 for(i=8;i!=0;i--)
 {
 output_high(EEPROM_CLK);
 output_low(EEPROM_CLK);
 shift_left(&rambuf2[j],1,input(EEPROM_DO));
 }
 }
 }
 output_high(EEPROM_SELECT);
 }
 
 
 
 // This subroutine runs the upload (receive) part of the xmodem protocol...
 
 byte xmodem()
 {
 byte i, j, k, seq, csum;
 long page;
 seq=0;
 k=0;
 csum=0;
 softime=0;
 page=file_start;
 putc(NAK);               //Initiate communication by sending NAKs.
 while (kbhit()==FALSE)
 {
 if (softime >= 0xf0)
 {
 softime=0;
 putc(NAK);
 }
 }
 while(1)
 {
 i=getc();
 while (i!=SOH)
 {
 i=getc();
 if (i==EOT)
 {
 putc(ACK);       //ACK the EOT. Transfer complete.
 putc(ACK);
 output_high(EEPROM_SELECT); // In case last packet was even #.
 file_end=page;
 return(0);
 }
 if (i==CAN) return(1);
 }
 seq=getc(); //Get packet sequence #.
 seq=getc(); //Get complemented packet sequence #.
 for (i=0;i<128;i++)
 {
 j=getc();
 if (i<64)  rambuf1[i]=j;
 if (i>=64) rambuf2[i-64]=j;
 }
 csum=getc();     //Get checksum.
 write_AT45_stream128(page, k);
 delay_ms(25);
 k++;
 k &= 0x01;
 if (k==0) page++;
 putc(ACK);       //ACK the packet.
 }
 return(1);
 }
 
 
 void hexdump()
 {
 long page;
 byte addr2, i, j, k, m, ch;
 page=0;
 addr2=0;
 putc(0x0d);
 putc(0x0a);
 k=0;
 ch=0;
 while ((ch != 'Q') && (ch != 'q'))
 {
 read_AT45_stream128(page, addr2);
 j=0;
 while (j < 8)
 {
 m=k;
 if (addr2 != 0) m=k+0x80;
 printf ("%4lx-%2x", page, m);
 putc(':');
 for(i=0;i<16;i++)  // Display in hex.
 {
 ch=rambuf1[k];
 if (j >=4) ch=rambuf2[k-64];
 ch >>= 4;
 ch &= 0x0f;
 putc(hexlist[ch]);
 ch=rambuf1[k];
 if (j >=4) ch=rambuf2[k-64];
 ch &= 0x0f;
 putc(hexlist[ch]);
 putc(0x20);
 if (i==7) putc(0x20);
 k++;
 }
 for(i=0;i<5;i++) putc(0x20);
 k-=16;
 for(i=0;i<16;i++)   // Display in ASCII.
 {
 ch=rambuf1[k];
 if (j >=4) ch=rambuf2[k-64];
 if (ch < 0x20) ch='.';
 if (ch > 0x7e) ch='.';
 putc(ch);
 k++;
 }
 putc(0x0d);
 putc(0x0a);
 j++;
 }
 printf ("\n\r\M=More, N=skip 100 pages, S=Skip 10 pages, Q=Quit:\r\n");
 ch=getc();
 k=0;
 addr2++;
 addr2 &= 0x01;
 if (addr2==0) page++;
 if ((ch=='N') || (ch=='n'))
 {
 page+=100;
 addr2=0;
 }
 if ((ch=='S') || (ch=='s'))
 {
 page+=10;
 addr2=0;
 }
 }
 }
 
 // This subroutine clears the 1st page of the AT45DB041B chip.
 // The 1st page is used in this app to hold file pointers & file info.
 
 void clr_all_ptrs()
 {
 byte i;
 long j;
 for (i=0; i < 64; i++)
 {
 rambuf1[i]=0;
 rambuf2[i]=0;
 }
 write_AT45_stream128(0, 0);   // Clear page 0 (the directory).
 write_AT45_stream128(0, 1);
 delay_ms(25);
 
 //   for (j=0; j<2048; j++)
 //   {
 //     write_AT45_stream128(j, 0);
 //     write_AT45_stream128(j, 1);
 //     delay_ms(25);
 //   }
 
 }
 
 
 // This function displays a simple directory listing.
 
 void dir()
 {
 byte i, k;
 read_AT45_stream128(0, 0);
 for (i=0; i<6; i++)
 {
 k=4*i;
 printf ("\r\nFile %d: Start page %2x%2x", i, rambuf1[k], rambuf1[k+1]);
 printf ("   End page %2x%2x", rambuf1[k+2], rambuf1[k+3]);
 }
 }
 
 | 
 
 Last edited by yerpa on Sun Jan 21, 2007 12:34 pm; edited 1 time in total
 |  |