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.

PIC - Adding Audio Playback to Current Program

Status
Not open for further replies.
Hello! Several years ago I received some excellent help with an ammo counter project, and I would like to add to it.

Basically, I was using a PIC12F629 to simulate gunfire. Here's what I came up with thanks to som excellent assistance.

Code:
#include <system.h>
#include <PIC12F629.h>
#include <boostc.h>

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

#pragma CLOCK_FREQ 4000000

void main()
{ 
  cmcon = 7;                        	// Comparator off, digital I/O
  trisio = 0b00100100;					// All Output, GP5/2 input
  gpio = 0;								// Make all outputs '0'
  option_reg.NOT_GPPU = 0;          	// Enable weak pull-ups
  wpu = 0;								// All Pull-ups Off 
  wpu.5 = 1;							// Enable GP5 Pull-Up
  wpu.2 = 1;							// Enable GP2 Pull-Up
  
for(;;)									// Forever
 {
	if(gpio.5 ==0)						// Debounce Check
	{
	delay_ms(50);
		if(gpio.5 == 0)                 // While GP5 switch held low
		{ 
		gpio.4 = 0;                     // GP4 LED Off
		delay_ms(10);					//
		gpio.4 = 1;						// GP4 LED On
		delay_ms(35);                   //
		gpio.4 = 0;                     // GP4 LED Off
		delay_ms(122);                  //
		}
	}
 }
}


It works great, so that's no issue. What I would like to do now, is add audio playback. Basically, I need it to playback a very small audio file each time the trigger is pulled, along with the flash.

Now, I've seen several projects involving PIC audio playback;

https://www.dmitry.gr/index.php?r=05.Projects&proj=02.%20Single-chip%20audio%20Player
https://www.enide.net/webcms/index.php?page=pcm2pwm
https://nerdclub-uk.blogspot.com/2012/10/playing-audio-with-pic-16f1825.html
https://www.uchobby.com/index.php/2008/07/21/dspic-wav-player/


Unfortunately, despite my greatest efforts, I cannot get any of the code to work in SourceBoost with BoostC.

Could someone help me figure this all out? Fortunately my hardware skills are excellent, but my understanding of programming is terrible. I do know that I would like to stick to a PIC similar in structure to the PIC12F629, and if possible, I would like to store the audio file on a flash ic.

Any help would be massively appreciated!
 
You'll need to choose a different PIC if you want to emulate the projects you linked to. The PIC12F629 doesn't have a PWM module, which makes things harder.

However, if your serial flash IC uses SPI and can be read from continuously, then you could potentially use its serial output as the signal to the speaker; delta modulation (1-bit) of the audio would also be beneficial. The pic would start the serial transfer, then just continuously toggle the SCK line until the sound had finished playing.
 
Ok, another PIC that I have successfully programmed for is the PIC16F887. If I'm not mistaken, it does have the necessary PWM module. I'll just got with it for simplicity's sake. The largest file I am wanting to playback will be 1 second long, with say...22500hz sample at 8bit.
 
Ok, another PIC that I have successfully programmed for is the PIC16F887. If I'm not mistaken, it does have the necessary PWM module. I'll just got with it for simplicity's sake. The largest file I am wanting to playback will be 1 second long, with say...22500hz sample at 8bit.

Then you'll need 22.5K of 8 bit ROM / Flash to store the waveform, either use external EEPROM or figure out a compression scheme. The 887 has 14K of Flash internally. And yes the 887 has PWM.

Roman Black (a member here) did some neat stuff with PIC sound. https://www.romanblack.com/picsound.htm
 
Ok, I take back my previous statement. My largest file I need to playback is;

Format: PCM
Format settings, Endianness: Little
Format settings, Sign: Unsigned
Codec ID: 1
Duration: 4s 316ms
Bit rate mode: Constant
Bit rate: 128 Kbps
Channel(s): 1 channel
Sampling rate: 16.0 KHz
Bit depth: 8 bits
Stream size: 67.5 KiB (100%)


So just under 70KB. Do you know of some sort of external storage that I could interface with the PIC and access via USB to store this? Similiar to this project, but an IC instead of the SD card. My reason for doing this is so that I can swap WAV files easily via USB.

https://www.dmitry.gr/index.php?r=05.Projects&proj=02. Single-chip audio Player
 
Last edited:
Neat project and SPI that's how <2GB SD cards communicate so an SPI EEPROM should work (you'll have to modify the code and somehow format it to FAT16)
As for USB how do you plan to connect it and program it to communicate? Why not just socket an SD card and build the project verbatim.
 
Thanks for the tips. I had and still somewhat am considering that exact project, but I plan on making several of these units for prop weapons. I was hoping for a bit more of a permanent setup where someone couldn't remove the storage. If there was some form of embedded SD chip available that would work perfectly.

If I run, say a 256K EEPROM with SPI, I assume I would have to program each file each time in hex? Sorry for all of the questions, aside from flashing LEDS, my MCU experience is severely lacking.
 
Actually, now that I think about it, I think I can design something similar with just with a controller and the flash memory myself, same design as an SD card. If I can get some help with the coding in a few days, I think I will try and go this route.
 
You could use a PIC with USB built in and a EEPROM. Easy enough to design but the programming isn't beginner stuff.

I'd study Romans compression routine to avoid any external storage and get used to either the free MPLABX & XC8 or the paid version of Swordfish BASIC (it has a USB library). I think OshonSoft's PIC simulator may have a USB module.

An EEPROM will keep its programming after the power's off. But you'll have to program it initially somehow. That's why the SD card approach is the easiest (all you need is a SD card reader for your PC to program it).
 
There's a suitable project in an old Practical Electronics issue - it used an R2R ladder converter and something like a 16F628 (18 pin) - is was a Halloween project, to make a device that detected people and spoke to them.
 
Thanks for all the help guys. I think I am just going to go with a socketed microSD card, as the coding seems to be more on my level of understanding.

Here are the main two projects I am trying to emulate;

https://nerdclub-uk.blogspot.com/2012/10/playing-audio-with-pic-16f1825.html
https://pic-microcontroller.com/pic-sound-player-pcm-to-pwm-converter-using-pic18f1320/

I'm using SourceBoost-C++, so I decided to give the first project a go first. I followed their steps, and was able to successfully compile the program, but I am getting an error at the end that seems to be a debugging error.

"Couldn't find function/label by name:main"

Code:
Building...
"C:\Program Files (x86)\SourceBoost\boostc++_pic16.exe" main.c  -16x -t PIC16F1825  -idx 1 -obj Debug -d _DEBUG
BoostC++ Optimizing C++ Compiler Version 7.20 (for PIC16 architecture)
http://www.sourceboost.com
Copyright(C) 2004-2013 Pavel Baranov
Copyright(C) 2004-2013 David Hobday

Single user Lite License (Unregistered) for 0 node(s)
Limitations: PIC12,PIC16 max code size:2048 words, max RAM banks:2, Non commercial use only


main.c

success
"C:\Program Files (x86)\SourceBoost\boostlink_picmicro.exe"  -idx 1  /ld "C:\Program Files (x86)\SourceBoost\lib" libc.pic16x.lib Debug\main.obj  /t PIC16F1825 /d "Debug" /p test
Couldn't find function/label by name:main

BoostLink Optimizing Linker Version 7.20
http://www.sourceboost.com
Copyright(C) 2004-2013 Pavel Baranov
Copyright(C) 2004-2013 David Hobday




failure
error: failed
Done


I'll post again in a tad, I'm going to try modifying the code for the PIC16F887.
 
Ok, or various reasons, I have decided to use a PIC16F1938. I started with the code for the PIC16F1825 and modified it for the 1938. In actuality I just had to change APFCON1.2 to APFCON.2 but it seems to have worked. How does it look?


Code:
#include "common.h"
#include "SD.h"
#include "boostc.h"
#include "PIC16F1938.h"

#define FLAG_TIMEOUT            0x80
#define FLAG_PARAM_ERR            0x40
#define FLAG_ADDR_ERR            0x20
#define FLAG_ERZ_SEQ_ERR      0x10
#define FLAG_CMD_CRC_ERR      0x08
#define FLAG_ILLEGAL_CMD      0x04
#define FLAG_ERZ_RST            0x02
#define FLAG_IN_IDLE_MODE      0x01

#pragma CLOCK_FREQ 32000000

#pragma DATA _CONFIG1, _FOSC_INTOSC & _WDTE_SWDTEN & _PWRTE_ON & _MCLRE_OFF & _CP_ON & _CPD_ON & _BOREN_OFF & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF
#pragma DATA _CONFIG2, _WRT_OFF & _PLLEN_OFF & _STVREN_ON & _LVP_OFF

void UARTInit(){
      //
      // UART
      //
      baudcon.4 = 0;      // SCKP      synchronous bit polarity
      baudcon.3 = 0;      // BRG16      enable 16 bit brg
      baudcon.1 = 0;      // WUE      wake up enable off
      baudcon.0 = 0;      // ABDEN      auto baud detect
     
      txsta.6 = 0;      // TX9      8 bit transmission
      txsta.5 = 1;      // TXEN      transmit enable
      txsta.4 = 0;      // SYNC      async mode
      txsta.3 = 0;      // SEDNB      break character
      txsta.2 = 0;      // BRGH      high baudrate
      txsta.0 = 0;      // TX9D      bit 9

      rcsta.7 = 1;      // SPEN serial port enable
      rcsta.6 = 0;      // RX9 8 bit operation
      rcsta.5 = 1;      // SREN enable receiver
      rcsta.4 = 1;      // CREN continuous receive enable
     
      spbrgh = 0;      // brg high byte
      spbrg = 51;      // brg low byte ()
     
      apfcon.2=1;      // tx onto RA.0            
}

void UARTSend(unsigned char c){
      txreg = c;
      while(!txsta.1);
}

void UARTPrint(char *s){
      while(*s) {
      UARTSend(*s);
      s++;
      }     
}

void UARTPrint(char c){UARTSend(c);}

void UARTPrintNumber(unsigned long n){
      unsigned long k=1000000000;
      while(k>0) {
            UARTSend('0'+n/k);
            n%=k;
            k/=10;
      }
}

void UARTPrintLn(char *s){
      UARTPrint(s);
      UARTPrint("\r\n");
}

void UARTByte(unsigned char b){
      const char *hex="0123456789abcdef";
      UARTPrint("0x");
      UARTSend(hex[b>>4]);
      UARTSend(hex[b & 0xf]);
}

void UARTLog(char *s, unsigned short iv){
      UARTPrint(s);
      UARTPrint(" ");
      UARTPrintNumber(iv);
      UARTPrintLn(" ");
}

void fatal(UInt8 val){            //fatal error: flash led then go to sleep
      UInt8 i, j, k;

      for(j = 0; j < 5; j++){     
            for(k = 0; k < val; k++)
            {
                  delay_ms(100);
                  P_LED = 1;
                  delay_ms(100);
                  P_LED = 0;
            }
           
            UARTLog("Error",val);
           
            delay_ms(250);
            delay_ms(250);
      }
     
      while(1){
            asm sleep
      }
}
 
SPI Slave Select?

Ok, I spent most of today reading up on this and trying to figure it out, and I think I'm pretty close. I'm concerned about the APFCON settings. Basically, determining which pin will be my TX. I also think SS is defaulted to RA5 Here's what I've got so far...

Code:
#include "common.h"
#include "SD.h"
#include "PIC16F1938.h"
#include "BoostC.h"

#define FLAG_TIMEOUT            0x80
#define FLAG_PARAM_ERR            0x40
#define FLAG_ADDR_ERR            0x20
#define FLAG_ERZ_SEQ_ERR      0x10
#define FLAG_CMD_CRC_ERR      0x08
#define FLAG_ILLEGAL_CMD      0x04
#define FLAG_ERZ_RST            0x02
#define FLAG_IN_IDLE_MODE      0x01

#define ERROR_LED	portc.2 
#define MUZZ_LED	porta.1

#pragma CLOCK_FREQ 32000000
#pragma DATA _CONFIG1,
		_FOSC_INTOSC &		// INTOSC oscillator: I/O Functions
		_WDTE_OFF &			// WDT Disabled
		_PWRTE_ON &			// PWRT Enabled
		_MCLRE_OFF &		// MCLR/VPP pin function is Input
		_CP_OFF &			// Program memory code protection is disabled
		_CPD_OFF &			// Data memory code protection is disabled
		_BOREN_NSLEEP &		// Brown-out Reset Disabled During Sleep
		_CLKOUTEN_OFF &		// CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin
		_IESO_OFF &			// Internal/External Switchover mode is disabled
		_FCMEN_OFF			// Fail-Safe Clock Monitor is disabled

#pragma DATA _CONFIG2,
		_WRT_OFF &			// Write protection off
		_VCAPEN_RA0 &		// VCAP on RA0
		_PLLEN_OFF &		// 4x PLL disabled
		_STVREN_ON &		// Stack Overflow or Underflow will cause a Reset
		_BORV_25 &			// Brown-out Reset Voltage (VBOR) set to 2.5 V
		_LVP_OFF			// High-voltage on MCLR/VPP must be used for programming
     

//void MuzzleFlash(void)
//{
//	ansela=0;				//ANSELA All Digital
//	trisa.0=0;				//make output
//	while(1)              				
//	{ 
//		porta.0 = 1;                     				
//		delay_ms(200);						
//		porta.0 = 0;
//		delay_ms(200);
//	}
//}

void UARTInit()
{
      //
      // UART
      //
      baudcon.4 = 0;		// SCKP			synchronous bit polarity
      baudcon.3 = 0;		// BRG16		enable 16 bit brg
      baudcon.1 = 0;		// WUE			wake up enable off
      baudcon.0 = 0;		// ABDEN		auto baud detect
     
      txsta.6 = 0;    		// TX9			bit transmission
      txsta.5 = 1;			// TXEN			transmit enable
      txsta.4 = 0;			// SYNC			async mode
      txsta.3 = 0;			// SEDNB		break character
      txsta.2 = 0;			// BRGH			high baudrate
      txsta.0 = 0;			// TX9D			bit 9

      rcsta.7 = 1;			// SPEN			serial port enable
      rcsta.6 = 0;			// RX9 			8 bit operation
      rcsta.5 = 1;			// SREN 		enable receiver
      rcsta.4 = 1;			// CREN 		continuous receive enable
     
      spbrgh = 0;			// brg high byte
      spbrg = 51;			// brg low byte ()
     
      apfcon = 1;			// tx onto RA1
           
}

void UARTSend(unsigned char c)
{
      txreg = c;
      while(!txsta.1);
}

void UARTPrint(char *s)
{
      while(*s) 
      {
      UARTSend(*s);
      s++;
      }     
}

void UARTPrint(char c)
{
UARTSend(c);
}

void UARTPrintNumber(unsigned long n)
{
      unsigned long k=1000000000;
      while(k>0) 
      {
            UARTSend('0'+n/k);
            n%=k;
            k/=10;
      }
}

void UARTPrintLn(char *s)
{
      UARTPrint(s);
      UARTPrint("\r\n");
}

void UARTByte(unsigned char b)
{
      const char *hex="0123456789abcdef";
      UARTPrint("0x");
      UARTSend(hex[b>>4]);
      UARTSend(hex[b & 0xf]);
}

void UARTLog(char *s, unsigned short iv)
{
      UARTPrint(s);
      UARTPrint(" ");
      UARTPrintNumber(iv);
      UARTPrintLn(" ");
}

void fatal(UInt8 val)					//fatal error: flash led then go to sleep
{           							 
      UInt8 i, j, k;

      for(j = 0; j < 5; j++)
      {     
            for(k = 0; k < val; k++)
            {
                  delay_ms(100);
                  ERROR_LED = 1;
                  delay_ms(100);
                  ERROR_LED = 0;
            }
           
            UARTLog("Error",val);
           
            delay_ms(250);
            delay_ms(250);
      }
     
      while(1)
      {
            asm sleep
      }
}


I'm sure it's wrong, but at the end of void UARTInit(), I believe I've correctly set TX as RA1. Also, I read somewhere SS should be on RA5 by default? If not, what would the correct APFCON be to set it as such?
 
Last edited:
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top