Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

18F4550 with SOUND! Requesting Review

Status
Not open for further replies.
Hello all! I've been working on this project for some time now, and I think I finally have a good handle of it all. If someone could take a look at this, I would really appreciate it.

I started with a simple LED flasher to simulate muzzle flash in a prop firearm and turn a flashlight on and off with a 12F629. Here is the original schematic and code;

**broken link removed**

Code:
#include "PIC12F629.h"
#include "system.h"

#pragma DATA _CONFIG,	_CPD_OFF &
						_CP_OFF &
						_BODEN_ON &
						_MCLRE_OFF &
						_PWRTE_ON &
						_WDT_OFF &
						_INTRC_OSC_NOCLKOUT

#pragma CLOCK_FREQ 4000000

#define TRIGGER	gpio.0
#define MUZZLE gpio.5
 
void main()
{ 
	cmcon = 7;               		        // Digital I/O
	trisio = 0b00000001;        	        // GP0 Input
	gpio = 0;               		                // All Outputs '0'
	option_reg.NOT_GPPU = 0;  		// Weak Pull-Ups
	wpu.0 = 1;       				// GP0 Weak Pull-Up

	while (TRIGGER == 0)			// While Trigger is Low
	{
	delay_ms(20);
		if (TRIGGER == 0)
		{
			MUZZLE = 1;			// MUZZLE On
			delay_ms(75);			// Muzzle Flash
			MUZZLE = 0;			// MUZZLE Off
			delay_ms(191);
		}
	}
}


So I took this code, and thanks to an excellent project by Andres Olivares **broken link removed**, I came up with a design to simulate muzzle flash, and play one sound when firing, and a separate sound when out of ammo.

This is what I have currently.

**broken link removed**

Code:
M6D Deluxe.c

#include "M6DDeluxe.h"
#include "mmc_driver.h"
#include "vs1011_driver.h"
#include "fat_driver.h"

unsigned int32 current_sector;
unsigned int32 current_file;
unsigned char play_sound;
unsigned char ammo_count;
unsigned char lzr_on;
unsigned char FIRED;


//Funciones generales

void player_init()
{
   //Okazumods - Gokussj5okazu
   
   ADCON1 = 0b00001111; 	// Port A Digital I/O
      
   TRIS_A = 0b00100000;		// PortA.5 Input
   TRIS_B = 0b11110001;		// PortB Inputs

   volume = 0x7F;			// Volume = 127
   current_sector = 0x00;
   current_file = 0;
   play_sound = 0;
   ammo_count = 8;
   lzr_on = 0;
   MUZZLE = 0;
   FIRED = 0;
	
} 

void main()
{

	while (TRIGGER)
	{
	FIRED = 0;
	}

	if (!TRIGGER)
	{
	delay_ms(20);
		if( !TRIGGER)
		{
			if (FIRED == 0)
			{
				if (ammo_count > 0)
				{
					current_file = 0;
					MUZZLE = 1;
					play_sound = 1;
					ammo_count --;
					FIRED = 1;
					delay_ms(75);
					MUZZLE = 0;
					delay_ms(191);
				}
				else
				{
					current_file = 1;
					play_sound = 1;
					delay_ms(286);
				}
			}
		}
	}

	if (!RELOAD)
	{
	ammo_count = 8;
	}

	if (!LZRSW)
	{
	delay_ms(20);
		if (!LZRSW)
		{
			if (lzr_on == 0)
			{
			LZR = 1;
			}
			else
			{
			LZR = 0;
			}
		}
	}


   int mmc_result;
   char cmd;

   player_init();
   delay_ms(10);  

   mmc_result = mmc_init();
   LED = mmc_result;

	if (mmc_result != 0) 
	{
		while (1);
	}

   delay_ms(100);
   
   resetvs1011_hard();

   blink_led(3);
   
   find_first_fat32_partition();
   read_volumeid();
   
   while (play_sound == 1)
   {
	  //mmc_read_block_to_vs1011(current_sector);
	  //current_sector++;

      cmd = play_file(current_file);
   }
}

Code:
fat_driver.h

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif

#define EOD 0
#define UNUSED 1
#define LFNAME 2
#define NFNAME 3

#define FILE_NOT_FOUND 2
#define NEXT_TRACK 0
#define PREVIOUS_TRACK 1

unsigned int8 sector_buffer[512];
unsigned int8 dir_entry[32];

unsigned int16 bytes_per_sector;
unsigned int8 sectors_per_cluster;
unsigned int16 number_of_reserved_sectors;
unsigned int8 number_of_fats;
unsigned int32 sectors_per_fat;
unsigned int32 root_directory_first_cluster;

unsigned int32 partition_lba_begin;
unsigned int32 fat_begin_lba;
unsigned int32 cluster_begin_lba;

void sector_to_serial()
{
   unsigned long i;
   unsigned long j;
   unsigned long count;
   
   count = 0;
   
   for (i = 0; i < 32; i++)
   {
      for (j = 0; j < 16; j++)
      {
         printf("%2X ", sector_buffer[count]);
         count++;
      }
      printf("\r\n");
   }
}

void read_mbr()
{
   unsigned int32 sector_number;
   unsigned int16 i;
  
   sector_number = 0;
  
   XCS = 1;
   XDCS = 1;
   SSPEN = 1;
  
   mmc_open_block(sector_number);
   for (i = 0; i < 512; i++)
   {
      sector_buffer[i] = SPI_READ(0xFF);
   }
   mmc_close_block();
   
   SSPEN = 0;
}

void read_sector(unsigned int32 sector_number)
{
   unsigned int16 i;
   unsigned int32 lba;
   
   XCS = 1;
   XDCS = 1;
   SSPEN = 1;
   
   lba = partition_lba_begin + sector_number;
  
   mmc_open_block(lba);
   for (i = 0; i < 512; i++)
   {
      sector_buffer[i] = SPI_READ(0xFF);
   }
   mmc_close_block();
   
   SSPEN = 0;
}

void mmc_read_block_to_vs1011_test(int32 block_number)
{
   unsigned int8 i, j;
   unsigned int16 count;
   
   read_sector(block_number);
   
   XCS = 1;
   XDCS = 1;
   MMC_CS = 0;
   SPI_READ(0xFF);
   MMC_CS = 1;
   
   SSPEN = 0;
   XDI_TRIS = 0;
   XDCS = 0;
   
   count = 0;
   for (i = 0; i < 16; i++)
   {
      while (!XDREQ);
      
      for (j = 0; j < 32; j++)
      {
         vs_spi_write(sector_buffer[count]);
         count++;
      }
   }
   
   XDCS = 1;
   XDI_TRIS = 1;
   SSPEN = 1;
   
   SPI_READ(0xFF);
}

int read_direntry(int8 num)
{
   unsigned int16 offset;
   unsigned int16 i;
   
   offset = (unsigned int16)num * 32;
   
   for (i = 0; i < 32; i++)
   {
      dir_entry[i] = sector_buffer[offset + i];
   }
   return TRUE;
}

unsigned int32 make_int32(unsigned int8 lsb, unsigned int8 mb1, unsigned int8 mb2, unsigned int8 msb)
{
   int32 result = 0;
   result = (unsigned int32)msb;
   result <<= 8;
   result |= (unsigned int32)mb2;
   result <<= 8;
   result |= (unsigned int32)mb1;
   result <<= 8;
   result |= (unsigned int32)lsb;
   
   return result;
}
unsigned int16 make_int16(unsigned int8 lsb, unsigned int8 msb)
{
   unsigned int16 result = 0;
   result = (unsigned int16)msb;
   result <<= 8;
   result |= (unsigned int16)lsb;

   return result;
}

unsigned int8 find_first_fat32_partition()
{
	int8 part_sig;

	partition_lba_begin = 0;

	read_mbr();

	part_sig = sector_buffer[0x1C2];
	if ((part_sig != 0x0B) && (part_sig != 0x0C))
    {
    	// Data is not a valid FAT32 file system
        return FALSE;
    }

    // Look for the offset
    partition_lba_begin = make_int32(sector_buffer[0x1C6], sector_buffer[0x1C6 + 1], sector_buffer[0x1C6 + 2], sector_buffer[0x1C6 + 3]);

    return TRUE;
}

   unsigned int8 read_volumeid()
{
   unsigned int16 signature;
   unsigned int8 part_sig;
   
   signature = 0;
   
   // Leemos el primer sector despues del offset
   read_sector(0);

   // Signature
   signature = make_int16(sector_buffer[0x1FE], sector_buffer[0x1FE + 1]);
   #ifdef DEBUG
   printf("Sector Zero Signtaure: %LX\r\n", signature);
   #endif

   if (signature == 0xAA55)
   {
      // Bytes per Sector
      bytes_per_sector = make_int16(sector_buffer[0x0B], sector_buffer[0x0B + 1]);
      #ifdef DEBUG
      printf("Bytes per sector: %Lu\r\n", bytes_per_sector);
      #endif

      // Sectors per Cluster
      sectors_per_cluster = sector_buffer[0x0D];
      #ifdef DEBUG
      printf("Sectors per cluster: %u\r\n", sectors_per_cluster);
      #endif

      // Number of Reserved Sectors
      number_of_reserved_sectors = make_int16(sector_buffer[0x0E], sector_buffer[0x0E + 1]);
      #ifdef DEBUG
      printf("Number of reserved sectors: %Lu\r\n", number_of_reserved_sectors);
      #endif

      // Number of FATs
      number_of_fats = sector_buffer[0x10];
      #ifdef DEBUG
      printf("Number of FATs: %u\r\n", number_of_fats);
      #endif

      // Sectors per FAT
      sectors_per_fat = make_int32(sector_buffer[0x24], sector_buffer[0x24 + 1], sector_buffer[0x24 + 2], sector_buffer[0x24 + 3]);
      #ifdef DEBUG
      printf("Sectors per FAT: %Lu\r\n", sectors_per_fat);
      #endif

      // Root Directory First Cluster
      root_directory_first_cluster = make_int32(sector_buffer[0x2C], sector_buffer[0x2C + 1], sector_buffer[0x2C + 2], sector_buffer[0x2C + 3]);
      #ifdef DEBUG
      printf("Root Directory First Cluster: %Lu\r\n", root_directory_first_cluster);
      #endif

      // Computamos otros valores usando los datos obtenidos
      fat_begin_lba = number_of_reserved_sectors;
      #ifdef DEBUG
      printf("FAT Begin LBA: %Lu\r\n", fat_begin_lba);
      #endif
      cluster_begin_lba = fat_begin_lba + (number_of_fats * sectors_per_fat);
      #ifdef DEBUG
      printf("Cluster Begin LBA: %Lu\r\n", cluster_begin_lba);
      #endif
      
      return TRUE;
   } else {
      return FALSE;
   }
}

unsigned int32 compute_lba(unsigned int32 cluster_number)
{
   return cluster_begin_lba + ((cluster_number - 2) * ((unsigned int32)sectors_per_cluster));
}

unsigned int8 check_if_eod()
{
   if (dir_entry[0] == 0x00)
   {
      return TRUE;
   }
   else
   {
      return FALSE;
   }
}

unsigned int8 get_attrib()
{
   return dir_entry[0x0B];
}

unsigned int8 determine_direntry_type()
{
   unsigned int8 attr;
   
   if (check_if_eod() == TRUE)
   {
      return EOD;
   }
   else
   {
      if (dir_entry[0] == 0xE5)
      {
         return UNUSED;
      }
      else
      {
         attr = get_attrib();
         if (bit_test(attr, 0) && bit_test(attr, 1) && bit_test(attr, 2) && bit_test(attr, 3))
         {
            return LFNAME;
         }
         else
         {
            return NFNAME;
         }
      }
   }
}

void get_filename()
{
   unsigned int i;
   
   printf("FILE: ");
   for (i = 0; i < 8; i++)
   {
      printf("%c", dir_entry[i]);
   }
   printf(".");
   for (i = 8; i < 11; i++)
   {
       printf("%c", dir_entry[i]);
   }
   printf("\r\n");
}

unsigned int32 read_fat(unsigned int32 cluster_number)
{
   unsigned int32 calc;
   unsigned int32 offset;
   unsigned int32 rest;
   unsigned int32 soff;
   
   calc = cluster_number / ((unsigned int32)bytes_per_sector / 4);
   offset = fat_begin_lba + calc;
   rest = cluster_number - (calc * ((unsigned int32)bytes_per_sector / 4));
   soff = rest * 4;
   
   read_sector(offset);
   
   return make_int32(sector_buffer[soff], sector_buffer[soff + 1], sector_buffer[soff + 2], sector_buffer[soff + 3]);
}

unsigned int32 get_file_first_cluster()
{
   return make_int32(dir_entry[0x1A], dir_entry[0x1A + 1], dir_entry[0x14], dir_entry[0x14 + 1]);
}

unsigned int32 get_file_size()
{
   return make_int32(dir_entry[0x1C], dir_entry[0x1C + 1], dir_entry[0x1C + 2], dir_entry[0x1C + 3]);
}

char get_file(unsigned int32 first_cluster, unsigned int32 size)
{
   unsigned int32 current_cluster;
   unsigned int32 current_lba;
   unsigned int32 current_sector;
   unsigned int32 length_count;
   
   current_cluster = first_cluster;
   current_lba = compute_lba(current_cluster);
   current_sector = 0;
   length_count = 0;

   mmc_read_block_to_vs1011(partition_lba_begin + current_lba);
   //mmc_read_block_to_serial(partition_lba_begin + current_lba);
   #ifdef DEBUG
   printf("GETFILE: Current LBA = %Lu\r\n", current_lba);
   #endif
   
   do
   {
      length_count += 512;
      #ifdef DEBUG2
      printf("GETFILE: Length count = %Lu from %Lu\r\n", length_count, size);
      #endif
      if (length_count <= size)
      {
         #ifdef DEBUG2
         printf("GETFILE: Incrementing current sector count...\r\n");
         #endif
         current_sector++;
         if (current_sector < sectors_per_cluster)
         {
            #ifdef DEBUG2
            printf("GETFILE: Incrementing current LBA count...\r\n");
            #endif
            current_lba++;
         }
         else
         {
            #ifdef DEBUG2
            printf("GETFILE: Resetting current sector count...\r\n");
            #endif
            current_sector = 0;
            #ifdef DEBUG2
            printf("GETFILE: Reading FAT for next cluster information...\r\n");
            #endif
            current_cluster = read_fat(current_cluster);
            #ifdef DEBUG2
            printf("GETFILE: Computing LBA for current sector count...\r\n");
            #endif
            current_lba = compute_lba(current_cluster);
         }
         mmc_read_block_to_vs1011(partition_lba_begin + current_lba);
         //mmc_read_block_to_serial(partition_lba_begin + current_lba);
         #ifdef DEBUG
         printf("GETFILE: Current LBA = %Lu\r\n", current_lba);
         #endif
      }
      else
      {
         #ifdef DEBUG2
         printf("GETFILE: File length reached!\r\n");
         #endif
         break;
      }
      
      #ifdef DEBUG2
      printf("GETFILE: Current cluster = %Lu\r\n", current_cluster);
      #endif
   }
   while (current_cluster < 0xFFFFFFF8);
   
   #ifdef DEBUG
   printf("GETFILE: Sending extra zeroes...\r\n");
   #endif
   morezeroes();
   
   #ifdef DEBUG
   printf("GETFILE: End of File!\r\n");
   #endif
   
   LED = 0;
   return 0;
}

void read_root_directory()
{
   unsigned int32 current_cluster;
   unsigned int32 current_lba;
   unsigned int32 current_sector;
   signed int8 current_direntry;
   
   unsigned int32 file_cluster;
   unsigned int32 file_size;
   
   unsigned int8 entry_type;
   unsigned int8 attrib;
   
   char cmd;
   
   //unsigned int8 i;
   
   current_cluster = root_directory_first_cluster;
   #ifdef DEBUG
   printf("ROOT: Current Cluster = %Lu\r\n", current_cluster);
   #endif
   current_lba = compute_lba(current_cluster);
   #ifdef DEBUG
   printf("ROOT: Root Directory LBA = %Lu\r\n", current_lba);
   #endif
   current_direntry = 0;
   current_sector = 0;
   
   #ifdef DEBUG
   printf("ROOT: Reading current LBA...\r\n");
   #endif
   read_sector(current_lba);
   
   do
   {
      cmd = 0;
      
      #ifdef DEBUG
      printf("ROOT: Reading dir entry...\r\n");
      #endif
      read_direntry(current_direntry);
      
      #ifdef DEBUG
      printf("ROOT: Determining dir entry type...\r\n");
      #endif
      entry_type = determine_direntry_type();
      
      #ifdef DEBUG
      printf("ROOT: Entry type = %u\r\n", entry_type);
      #endif
      if (entry_type == NFNAME)
      {
         #ifdef DEBUG
         printf("ROOT: Getting attrib...\r\n");
         #endif
         attrib = get_attrib();
         #ifdef DEBUG
         printf("ROOT: Attrib = %X\r\n", attrib);
         #endif
         if (!bit_test(attrib, 3) && !bit_test(attrib, 4))
         {
            #ifdef DEBUG
            printf("ROOT: It's a file...\r\n");
            get_filename();
            #endif
            
            file_cluster = get_file_first_cluster();
            #ifdef DEBUG
            printf("ROOT: File first cluster = %Lu\r\n", file_cluster);
            #endif
            file_size = get_file_size();
            
            #ifdef DEBUG
            printf("ROOT: Playing file...\r\n");
            #endif
            cmd = get_file(file_cluster, file_size);
            
            #ifdef DEBUG2
            printf("ROOT: Waiting for PLAY signal for next file...\r\n");
            while (PLAY_PAUSE);
            #endif
            
            read_sector(current_lba);
         }
         #ifdef DEBUG
         else
         {
            if (bit_test(attrib, 3))
            {
               printf("ROOT: It's the Volume ID...\r\n");
            } else {
               printf("ROOT: It's a subdirectory...\r\n");
            }
         }
         #endif
      }
      
      if (cmd == 0)
      {
         current_direntry++;
         if (current_direntry > 15)
         {
            current_direntry = 0;
         
            current_sector++;
            if (current_sector < sectors_per_cluster)
            {
               current_lba++; 
            }
            else
            {
               current_sector = 0;
               current_cluster = read_fat(current_cluster);
               current_lba = compute_lba(current_cluster);
            }
            read_sector(current_lba);
         }
      } else {
         current_direntry--;
         if (current_direntry < 0)
         {
            current_direntry = 15;
         
            current_sector--;
            if (current_sector > sectors_per_cluster)
            {
               current_lba--; 
            }
            else
            {
               current_sector = sectors_per_cluster;
               current_cluster = read_fat(current_cluster);
               current_lba = compute_lba(current_cluster);
            }
            read_sector(current_lba);
         }
      }
   }
   while (check_if_eod() == FALSE);
}

char play_file(unsigned int32 file_index)
{
   unsigned int32 current_cluster;
   unsigned int32 current_lba;
   unsigned int32 current_sector;
   unsigned int8 current_direntry;
   
   unsigned int32 file_cluster;
   unsigned int32 file_size;
   unsigned int32 current_index;
   
   unsigned int8 entry_type;
   unsigned int8 attrib;
   
   char cmd;
   
   current_cluster = root_directory_first_cluster;
   current_lba = compute_lba(current_cluster);
   current_direntry = 0;
   current_sector = 0;
   
   read_sector(current_lba);
   
   current_index = 0;
   
   cmd = FILE_NOT_FOUND;
   
   do
   {
      read_direntry(current_direntry);

      entry_type = determine_direntry_type();
      
      if (entry_type == NFNAME)
      {
         attrib = get_attrib();
         if (!bit_test(attrib, 3) && !bit_test(attrib, 4))
         {
			get_filename();
            file_cluster = get_file_first_cluster();
            file_size = get_file_size();
            
            if (current_index == file_index)
            {
               cmd = get_file(file_cluster, file_size);
               return cmd;
            }
            
            current_index++;
            
            read_sector(current_lba);
         }
      }
      
      current_direntry++;
      if (current_direntry > 15)
      {
         current_direntry = 0;
         
         current_sector++;
         if (current_sector < sectors_per_cluster)
         {
            current_lba++; 
         }
         else
         {
            current_sector = 0;
            current_cluster = read_fat(current_cluster);
            current_lba = compute_lba(current_cluster);
         }
         read_sector(current_lba);
      }
   }
   while (check_if_eod() == FALSE);
   
   return cmd;
}
 
Code:
M6D Deluxe.h

#include <18F4550.h>

#fuses HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL5,CPUDIV1,VREGEN

#define DEBUG
//#define DEBUG2 // Second Level Debug

#use delay(clock=48,000,000)
#use rs232(baud=115200, parity=N, xmit=PIN_C2, bits=8)

#use fast_io(a)
#use fast_io(b)
#use fast_io(c)

#byte PORT_E    = 0xF84
#byte PORT_D    = 0xF83
#byte PORT_C    = 0xF82
#byte PORT_B    = 0xF81
#byte PORT_A    = 0xF80

#byte SSPBUF     = 0xFC9
#byte SSPADD     = 0xFC8
#byte SSPSTAT    = 0xFC7
#byte SSPCON1    = 0xFC6
#byte SSPCON2    = 0xFC5

#byte ADCON0     = 0xFC2
#byte ADCON1     = 0xFC1

#byte OSCCON   = 0xFD3

#byte TRIS_E    = 0xF96
#byte TRIS_D    = 0xF95
#byte TRIS_C    = 0xF94
#byte TRIS_B    = 0xF93
#byte TRIS_A    = 0xF92

#bit XDI_TRIS   = TRIS_B.0
#bit XDO_TRIS   = TRIS_C.7

#bit  SMP   =  SSPSTAT.7
#bit  CKE   =  SSPSTAT.6
#bit  BF    =  SSPSTAT.0

#bit  SSPEN = SSPCON1.5
#bit  CKP   = SSPCON1.4
#bit  SSPM3 = SSPCON1.3
#bit  SSPM2 = SSPCON1.2
#bit  SSPM1 = SSPCON1.1
#bit  SSPM0 = SSPCON1.0

#bit MMC_CS = PORT_A.0
#bit XCS    = PORT_A.1
#bit XDCS   = PORT_A.2
#bit LED    = PORT_A.3
#bit XRESET = PORT_A.4
#bit XDREQ  = PORT_A.5

#bit XDI 		= PORT_B.0
#bit XCLK 		= PORT_B.1
#bit MUZZLE		= PORT_B.2
#bit LZR		= PORT_B.3
#bit LZRSW		= PORT_B.5
#bit RELOAD		= PORT_B.6
#bit TRIGGER	= PORT_B.7



void blink_led(int times)
{
   int i;
  
   for (i = 0; i < times; i++)
   {
      LED = 1;
      delay_ms(100);
      LED = 0;
      delay_ms(100);
   }
}

Code:
mmc_driver.h

// Funciones MMC
int mmc_response(unsigned char response)
{
   // Lee la memoria MMC hasta que obtenemos la respuesta
   // que queremos o hacemos un timeout
   unsigned long count = 0x0FFF;
   //printf("mmc_response(): Leemos la memoria esperando una respuesta...\r\n");
   while (spi_read(0xFF) != response && --count > 0);
   
   // El loop fue terminado debido a un timeout
   if (count == 0)
   {
      //printf("mmc_response(): Hubo un timeout!\r\n");
      return 1;
   }
   // En caso contrario terminamos sin problemas (una respuesta fue recibida)
   else
   {
      //printf("mmc_response: Recibimos respuesta.\r\n");
      return 0;
   }
}

char mmc_open_block(int32 block_number)
{
   int8 addr[4];
   
   MMC_CS = 0;                     // set SS = 0 (on)

   /*block_number *= 2;
   addr[0] = *(((char*)&block_number)+2);
   addr[1] = *(((char*)&block_number)+1);
   addr[2] = *(((char*)&block_number)+0);
   addr[3] = 0x00;*/
   
   block_number *= 512;
   addr[0] = MAKE8(block_number, 3);
   addr[1] = MAKE8(block_number, 2);
   addr[2] = MAKE8(block_number, 1);
   addr[3] = MAKE8(block_number, 0);
   
   #ifdef DEBUG2
   printf("OPEN_BLOCK: Address = %2X %2X %2X %2X\r\n", addr[0], addr[1], addr[2], addr[3]);
   #endif
   
   SPI_WRITE(0x51);  // send mmc read single block command
   SPI_WRITE(addr[0]); // arguments are address
   SPI_WRITE(addr[1]);
   SPI_WRITE(addr[2]);
   SPI_WRITE(addr[3]);
   SPI_WRITE(0xFF);                // checksum is no longer required but we always send 0xFF
   
   if((mmc_response(0x00))==1) return 1;   // if mmc_response returns 1 then we failed to get a 0x00 response (affirmative)
   if((mmc_response(0xFE))==1) return 1;   // wait for data token
   
   return 0;
}

void mmc_close_block(void){
   SPI_READ(0xFF);                 // Bytes de Checksum que no son realmente necesarios
   SPI_READ(0xFF);
   MMC_CS = 1;                     // Deseleccionamos la memoria MMC/SD
   SPI_WRITE(0xFF);                // Le damos a la memoria algunos ciclos de reloj para que termine
}

int mmc_read_block_to_serial(int16 block_number)
{
   unsigned long i;
   unsigned long j;
   
   XCS = 1;
   XDCS = 1;
   SSPEN = 1;
   
   printf("\r\n");
   mmc_open_block(block_number);
   for (i = 0; i < 32; i++)
   {
      for (j = 0; j < 16; j++)
      {
         //SSPBUF = 0xFF;
         //while (BF == 0);
         printf("%2X ", spi_read(0xFF));
      }
      printf("\r\n");
   }
   mmc_close_block();
   
   SSPEN = 0;
   
   return 0;
}

int mmc_init()
{
   int i;
   
   // Configuramos el modulo SPI
   //printf("mmc_init(): Configuramos el modulo SPI...\r\n");
   //SETUP_SPI(SPI_MASTER | SPI_H_TO_L | SPI_CLK_DIV_4 | SPI_SS_DISABLED);
   //SSPSTAT |= 0x40;
   //SSPCON1 &= 0xEF;                          
   
   SSPEN = 0;
   SMP = 0; // Input data sampled at middle of data output time
   CKE = 1; // Rising edge is data capture
   CKP = 0; // High value is passive state
   SSPEN = 1; // Enables SPI module

   //printf("mmc_init(): Inicializando la memoria MMC...\r\n");
   // Chip-Select a 1 (OFF)
   XDCS = 1;
   XCS = 1;
   MMC_CS = 1;
   // Inicializamos la memoria MMC en modo SPI enviando ciclos de reloj
   for(i = 0; i < 10; i++)
   {
      spi_write(0xFF);
   }
   // Le decimos a la memoria MMC ir al modo SPI cuando reciba un RESET
   MMC_CS = 0;
   
   // Enviamos el comando RESET
   spi_write(0x40);
   // Enviamos cuatro bytes 0x00 como los argumentos del comando RESET
   spi_write(0x00);
   spi_write(0x00);
   spi_write(0x00);
   spi_write(0x00);
   // Checksum precalculado ya que aun estamos en modo MMC
   spi_write(0x95);
   
   // Si la respuesta de la memoria es 1, quiere decir que hubo un timeout
   // mientras esperabamos por el byte 0x01 desde la MMC
   if (mmc_response(0x01) == 1)
   {
      //printf("mmc_init(): Timeout (1)!\r\n");
      return 1;
   }

   // Si pasamos este punto, recibimos respuesta desde la MMC
   i = 0;
   while((i < 255) && (mmc_response(0x00) == 1))
   {
      // Debemos seguir enviando commandos si es que hay respuesta
      spi_write(0x41); // Enviamos el comando uno para sacar la memoria del estado de espera
      spi_write(0x00);
      spi_write(0x00);
      spi_write(0x00);
      spi_write(0x00);
      spi_write(0xFF); // Ya no se necesitan checksums, pero siempre enviamos 0xFF
      i++;
   }
   
   // Si el numero de iteraciones es mayor a 254, hubo un timeout esperando la
   //respuesta desde la memoria
   if(i >= 254)
   {
      //printf("mmc_init(): Timeout (2)!\r\n");
      return 1;
   }
   
   // Pasado este punto, estamos fuera del estado de espera
   
   // Deseleccionamos la memoria MMC del bus SPI
   MMC_CS = 1;
   // Ciclos de reloj extra para permitir a la MMC finalizar lo que este haciendo
   spi_write(0xFF);
   // Seleccionamos la memoria MMC del bus SPI
   MMC_CS = 0;
   // Enviamos el comando uno para salir del estado de espera
   SPI_WRITE(0x50);
   SPI_WRITE(0x00);
   SPI_WRITE(0x00);
   SPI_WRITE(0x02);
   SPI_WRITE(0x00);
   SPI_WRITE(0xFF);
   
   // Si recibimos un uno, hubo un timeout
   if((mmc_response(0x00)) == 1)
   {
      //printf("mmc_init(): Timeout (3)!\r\n");
      return 1;
   }
   
   // Deseleccionamos la memoria del bus SPI
   MMC_CS = 1;
   
   //printf("mmc_init(): La memoria MMC se ha inicializado correctamente.\r\n");
   return 0;
}

Code:
vs1011_driver.h

// Debe haber definidos XCLK y XDI

char volume;

void vs_spi_write(char aa)
{
   char i;
   
   i = 7;
   XCLK = 0;
   XDI = 0;
   delay_cycles(10);
   
   do
   {
      XDI = bit_test(aa, i);
      XCLK = 1;
      delay_cycles(10);
      XCLK = 0;
   }
   while((i--) > 0);
}

void morezeroes(void)
{
   int16 c;
   XCS = 1;
   SSPEN = 0;
   while (!XDREQ);
   
   XDCS = 0;
   for (c = 0; c < 2048; c++)
   {
      vs_spi_write(0);
      while (!XDREQ);
   }
   XDCS = 1;
   SSPEN = 1;
}

void vs_command(char inout, char address, char a, char b)
{
   MMC_CS = 1; // MMC disabled
   XDCS = 1; // VS1011 data disabled
   SSPEN = 0; // Disable SPI module
   XDI_TRIS = 0; // SDI as digital output
   XCS = 0; // VS1011 command enabled
   vs_spi_write(inout); 
   vs_spi_write(address); // Load the buffer with address
   vs_spi_write(a); // Load buffer with hi  data(ignored on read command)
   vs_spi_write(b); // Load buffer with data low value
   XCS = 1; // Deselect the VS1011 command registers (go into sdi mode)
   XDI_TRIS = 1; // SDI as input
   SSPEN = 1;
}

void vs_data(char d1, char d2, char d3, char d4)
{
   MMC_CS = 1; // MMC disabled
   XCS = 1; // VS1011 command disabled
   SSPEN = 0; // Disable SPI module
   XDI_TRIS = 0; // SDI as digital output
   XDCS = 0; // VS1011 data enabled
   vs_spi_write(d1); 
   vs_spi_write(d2); // Load the buffer with address
   vs_spi_write(d3); // Load buffer with hi  data(ignored on read command)
   vs_spi_write(d4); // Load buffer with data low value
   XDCS = 1; // Deselect the VS1011 data registers
   XDI_TRIS = 1;
   SSPEN = 1;
}

void vs_data_single(char d1)
{
   MMC_CS = 1; // MMC disabled
   XCS = 1; // VS1011 command disabled
   SSPEN = 0; // Disable SPI module
   XDI_TRIS = 0; // SDI as digital output
   XDCS = 0; // VS1011 data enabled
   vs_spi_write(d1); 
   XDCS = 1; // Deselect the VS1011 data registers
   XDI_TRIS = 1;
   SSPEN = 1;
}

void resetvs1011_soft()
{
   #ifdef DEBUG
   printf("RESET_SOFT: Deselecting everything...\r\n");
   #endif
   MMC_CS = 1;
   XDCS = 1;
   XCS = 1;
   SSPEN = 0;
   
   #ifdef DEBUG
   printf("RESET_SOFT: Soft Resetting...\r\n");
   #endif
   // Soft Reset of VS10xx
   vs_command(0x02, 0x00, 0x08, 0x04);
   delay_ms(1);
   
   #ifdef DEBUG
   printf("RESET_SOFT: Waiting for DREQ...\r\n");
   #endif
   while (!XDREQ); // Wait for startup
   
   delay_ms(1);
   
   #ifdef DEBUG
   printf("RESET_SOFT: Sending zeroes to data channel...\r\n");
   #endif
   XDCS = 0;
   vs_spi_write(0x00);
   vs_spi_write(0x00);
   vs_spi_write(0x00);
   vs_spi_write(0x00);
   XDCS = 1;
   
   delay_ms(50);
   
   #ifdef DEBUG
   printf("RESET_SOFT: Enabling SPI module...\r\n");
   #endif
   SSPEN = 1;
}

void set_volume()
{
   vs_command(0x02, 0x0B, volume, volume); // Volume
}

void resetvs1011_hard(void){
   XRESET = 0;
   delay_ms(10);
   XRESET = 1;
   delay_ms(10);
   while (!XDREQ);
   //vs_command(0x02, 0x00, 0b00001000, 0b00100010);
   delay_ms(1);
   vs_command(0x02, 0x00, 0b00001000, 0b00100010); // New Mode, MPG II, SDI tests (byte 2 - bit 5), Rising Edge, MSB First 
   delay_ms(1);
   set_volume();
   //delay_ms(1);
   //vs_command(0x02,0x03,0x98,0x00); // 12.288 Mhz crystal
}

void resetvs1011_hard2()
{
   #ifdef DEBUG
   printf("RESET_HARD: Deselecting everything...\r\n");
   #endif
   MMC_CS = 1;
   XCS = 1;
   XDCS = 1;
   SSPEN = 0; // Disables the SPI module
   
   #ifdef DEBUG
   printf("RESET_HARD: Performing Hard Reset (XRESET=0)...\r\n");
   #endif
   XRESET = 0; // Hard Reset
   delay_ms(10);
   XRESET = 1; // Release from reset
   delay_ms(10);
   #ifdef DEBUG
   printf("RESET_HARD: Setting the volume...\r\n");
   #endif
   vs_command(0x02, 0x0B, 0xFF, 0xFF); // Set volume
  
   #ifdef DEBUG  
   printf("RESET_HARD: Waiting for DREQ...\r\n");
   #endif
   while (!XDREQ); // Wait for DREQ
   
   // Slow sample rate for slow analog part startup
   #ifdef DEBUG
   printf("RESET_HARD: Slowing down analog parts startup...\r\n");
   #endif
   vs_command(0x02, 0x05, 0x00, 0x0A);
   delay_ms(100);
   
   #ifdef DEBUG
   printf("RESET_HARD: Swithing the analog parts on...\r\n");
   #endif
   // Switch on the analog parts
   vs_command(0x02, 0x0B, 0xFE, 0xFE);
   delay_ms(1);
   vs_command(0x02, 0x05, 0x1F, 0x40); // 8kHz
   delay_ms(1);
   vs_command(0x02, 0x0B, 0x0A, 0x0A);
   
   #ifdef DEBUG
   printf("RESET_HARD: Enabling SPI module...\r\n");
   #endif
   SSPEN = 1;
}

int mmc_read_block_to_vs1011(int32 block_number)
{
   unsigned int8 i, j;
   
   #ifdef DEBUG2
   printf("DSP: Enabling SPI module (SSPEN=1)...\r\n");
   #endif
   SSPEN = 1; // Habilitamos el módulo SPI del PIC
   XCS = 1;
   XDCS = 1;
   MMC_CS = 0;
   
   #ifdef DEBUG2
   printf("DSP: Opening block = %Lu\r\n", block_number);
   #endif
   mmc_open_block(block_number);
   
   #ifdef DEBUG2
   printf("DSP: Disabling SPI module (SSPEN=0)...\r\n");
   #endif
   SSPEN = 0; // Desactivamos el módulo SPI del PIC para controlar el chip VS1011 directamente por software
   XCS = 1;
   #ifdef DEBUG2
   printf("DSP: Selecting VS1011 data SPI stream (XDCS=0)...\r\n");
   #endif
   
   #ifdef DEBUG2
   printf("DSP: Generating clock cycles...\r\n");
   #endif
   for (i = 16; i > 0; i--)
   {
      #ifdef DEBUG2
      printf("DSP: Waiting for DREQ signal to go high...\r\n");
      #endif
      while (!XDREQ); // Esperamos por DREQ como se especifica en la hoja de especificaciones del VS1011
      #ifdef DEBUG2
      printf("DSP: Making the cycles...\r\n");
      #endif
      for (j = 0; j < 32; j++)
      {
		 XDCS = 0; // Permitimos al chip VS1011 que lea los datos que fluyen por el bus SPI desde la memoria
         // Generamos algunos pulsos de reloj para el VS1011
         XCLK=1;XCLK=0; // Haciendolo de esta manera nos ahorramos algunos ciclos de reloj del PIC
         XCLK=1;XCLK=0;
         XCLK=1;XCLK=0;
         XCLK=1;XCLK=0;
         XCLK=1;XCLK=0;
         XCLK=1;XCLK=0;
         XCLK=1;XCLK=0;
         XCLK=1;XCLK=0;
		 XDCS = 1;
      }
   }
   
   #ifdef DEBUG2
   printf("DSP: Disabling VS1011 data SPI stream (XDCS=1)...\r\n");
   #endif
   XDCS = 1; // Deseleccionamos el chip VS1011
   #ifdef DEBUG2
   printf("DSP: Enabling SPI module (SSPEN=1)...\r\n");
   #endif
   SSPEN = 1; // Volvemos a activar el módulo SPI para que exista comunicación entre la MMC/SD y el PIC
   #ifdef DEBUG2
   printf("DSP: Closing block...\r\n");
   #endif
   mmc_close_block();

   return 0;
}

void sinetest_on()
{
   vs_data(0x53, 0xEF, 0x6E, 0x44);
   vs_data(0, 0, 0, 0);
}

void sinetest_off()
{
   vs_data(0x45, 0x78, 0x69, 0x74);
   vs_data(0, 0, 0, 0);
}


Now for the question, should this all work as intended? If you see any issues, please let me know! Thanks!
 
Well there are a few reasons I chose the MP3 player setup. First, the quality. Roman Black's encoder doesn't sound bad, at least from the samples, but I figured if I could improve, why not. Second, being able to change files on the fly just by swapping an SD card is quite a bit more convenient. Third, and most important, the coding for the MP3 project was a bit easier for me to wrap my head around initially.

If I were to use Roman Black's BTc, do you know of a good example for calling the playback in C? I do have his encoder and have exported a C file for a particular sound file, but I am really clueless how to call for it to playback.
 
How would I go about calling either of these sounds to play?

Code:
//=====================================================
// AUTO CREATED FILE made by Windows BTc Sound Encoder 
// v2.0    Copyright 2002-2008 - Roman Black
// 
// Hippyware.  
// www.RomanBlack.com 
//=====================================================

// File Details:
// Size 26798 bits     (3349 bytes)
// Sound encoded at 44100 bits/sec 
// using BTc64 1.5bit Algorithm to be decoded on
// the following circuit:
// 
// 
//             R  = 6491 ohms  
//             each 2R = 12982 ohms  
// 
// 
// Digital ----------2R-----,     
//                          |     
// Digital ----------2R-----*----- Analogue    
//                          |      out        
//                          |     
//                          |     
//                          C = 0.22 uF    
//                          |     
//                          |     
//                          |     
//                         Gnd     
// 
// 
//=====================================================
// Bitstream data is MikroC .C table format,
// in 'functions' of 2048 bytes with a .C 'return' as
// the last of every 256 byte block. At the end of every 
// 2048 bytes the C compiler inserts its own return. 
// 
// Bits are played from left to right, from ms_bit to
// ls_bit.
//=====================================================
//-------------------------------------------------- 
void sound_data1()    org (1 * 256) 
{ 
   asm retlw 0x7B ; 
   //Skip Some Lines
   asm retlw 0xC3 ; 
   // reserved for MikroC return!  
   //------------------------------------- 
} 
 
 
 
//-------------------------------------------------- 
void sound_data2()    org (8 * 256) 
{ 
   asm retlw 0xAE ; 
   //Skip Some Lines
   asm retlw 0x00 ; 
   // reserved for MikroC return!  
   //------------------------------------- 
} 
 
 
 

//---------------------------------------------------
// End of file. 
//---------------------------------------------------
 
That code looks like it is using the ASM call and return system (ASM table read) but formatted for inclusion in a C file. To use that you would call manually using a few lines of ASM to set PCLATH etc and call a table read.

The BTc Encoder program that made that file can export in other code formats.

All those code formats will put the sound data in the microcontrollers ROM. Does your micro have enough ROM to hold the entire sound file? Normally that only works for short sounds, under a second or so.

Otherwise you export the sound as raw binary, and put it in a memory device like an SPI EEPROM or an SD card. Which is more common now with large memory available so cheap. And if you use external memory you might as well just use 8bit mono sound, which are larger but give a better sound quality than the BTc system. My BTc encoder system is getting pretty out of date now when people can buy 4Gbyte memory cards for a couple of bucks! :)
 
If you call ASM routines from C-program, you need to know your compiler very well. You should search the compilers manual/documentation for some advice how to call ASM routines, or how to do inline ASM. What compiler are you using?
 
I'm using both CSS and SourceBoost. I'm equally experienced with both. This is why I initially strayed away from RB's encoder. For me, the extra hardware is easier for me to understand than the coding required for all of this. Plus, the quality, and ease of swapping files for different projects.

Any thoughts regarding what I have and posted so far in the first post? I think it all should work, but I'm not 100%.
 
Status
Not open for further replies.

New Articles From Microcontroller Tips

Back
Top