Play .wav sounds using mid-range PIC and PWM output + Matlab + CCS PICC
The aim of this paper is to explain how to play sounds using a mid-range 8bit PIC microcontroller and it's PWM output. It works as a DAC (digital to analog converter).The sound will be a .wav file that we'll introduce in the PIC memory after processing the data a little using MATLAB.
The main concern is sound quality, as PIC memory limitations or PWM output resolution make it impossible to get a good sound quality, but for spoken voices and simple sounds will be more than enough.
I used the following and it is proved in this way:
* PIC 18F4550
* CCS PICC (C compiler for PICs)
* MATLAB
* Word Processor
* A sound file in .wav format (16bit / 44100Hz)
The audio file
The original file is bocina3.wav, is a sound file sampled at 44100Hz, 16 bits /mono. This means that 44100 times every second, we make an analog to digital conversion (ADC) of the sound and store the data in a 16 bit variable.
First, I recommend you to convert the original sound to mono instead of stereo, unless you want to use a dual-pwm output. In adition, try to reduce the sample time as much as you can. Remember the memory limitations.
Remember that the Nyquist theorem says that you have to sample at at least the double of the original max. frecuency. It means that if you want to sample an human voice sound frequency that usually not exceed 2000Hz, you can sample at 4000Hz. Why this is important? Because the file size! Imagine you have a 10-second sound and you want to put this into the microcontroller. Let us now calculate the size of the file that you will have depending on the sampling rate:
* 44100Hz, 8 bits are (3.528 million bits) 441k
* 4000Hz, 8 bits are (320,000 bits) 40K
You can see that the first file (441k) will not be easy to stick into the PIC memory. You could try a couple of 24xx1025 memories using the I2C bus, assuming that the microcontroller can read the bus and play in real time. But the second file (40k) could fit in a mid-range PIC!
Create the PIC ROM table using Matlab.
What we do now is to load the data as static variables into the controller. This mean ROM data, read-only at runtime. These variables contain the sound. We will use this example, an 8-bit variable, so we have 28 = 256 different sound tones. Not for music lovers.
Open Matlab and load the sound into a vector. Run "help wavread" in Matlab if you don't know what the following instruction means. Basically you enter the vector destination, sampling frecuency, the number of bits to use ...
- [Datos, Fs, NBits, opts] = wavread ('d: \ bocina3.wav')
The problem is that the data vary between -1 and 1 and we need it between 0 and 255 (8 bits). So execute the following instruction:
- Datos = data * 127
The value 127 is assuming that the maximum / minimum value is 1 / -1. If not, this value can be a bit more to maximize resolution of the conversion. If you don't know what it means, use 127.
- Datos = datos * +127
- Datos = fix (datos)
Now save the vector into a file (0-255) in CSV (Comma Separated Values) format. Write down the vector size in MATLAB before closing. We're going to need it later, while writting the C code to define the size of the static vector.
- Csvwrite ('d: \ datos.csv', data);
That's it! Just one step more. Open the file with a word processor You'll see a bunch of numbers. That's the sound written in digital values.
Use the replace option and copy & cut. The text should be like this:
- Const Char datos [X] = {127,127,127,127 ,.... your data ..., 125.54};
* Where X is the vector length from MATLAB that you wrote down.
We define "datos" as a static vector "const" which is saved as read-only memory (ROM) that generally has more capacity than the RAM.
You can copy the line into the CCS PICC compiler.
You can download an example of how it should be before copying and pasting into the CCS PICC. You have to copy a value per line, otherwise, the text editor will hang
Program configuration example and PIC
main.c
#USE RS232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)
#use delay(clock=8000000)
Const char datos[1000]={127,127,127,127,....tus datos...,125,54};
int16 i;
void main()
{
setup_adc_ports(NO_ANALOGS|VSS_VDD);
setup_adc(ADC_OFF);
setup_spi(SPI_SS_DISABLED);
setup_timer_1(T1_DISABLED);
SETUP_CCP1(CCP_PWM_HALF_BRIDGE|CCP_PWM_L_L|CCP_SHUTDOWN_AC_F);
setup_wdt(WDT_OFF);
setup_timer_2(T2_DIV_BY_1,110,1);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
while(1==1){
setup_ccp1(CCP_CAPTURE_FE);
delay_ms(3000);
SETUP_CCP1(CCP_PWM_HALF_BRIDGE|CCP_PWM_L_L|CCP_SHUTDOWN_AC_F);
for (i=0;i<1000;i++) {set_pwm1_duty(datos[i]);}
for (i=0;i<1000;i++) {set_pwm1_duty(datos[i]);}
for (i=0;i<1000;i++) {set_pwm1_duty(datos[i]);}
}
}
main.h (configuration):
#include <18F4550.h>
#device adc=8
#FUSES NOWDT //No Watch Dog Timer
#FUSES WDT128 //Watch Dog Timer uses 1:128 Postscale
#FUSES INTRC_IO //Internal RC Osc, no CLKOUT
#FUSES NOPROTECT //Code not protected from reading
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOPUT //No Power Up Timer
#FUSES NOCPD //No EE protection
#FUSES STVREN //Stack full/underflow will cause reset
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOWRT //Program memory not write protected
#FUSES NOWRTD //Data EEPROM not write protected
#FUSES IESO //Internal External Switch Over mode enabled
#FUSES FCMEN //Fail-safe clock monitor enabled
#FUSES PBADEN //PORTB pins are configured as analog input channels on RESET
#FUSES NOWRTC //configuration not registers write protected
#FUSES NOWRTB //Boot block not write protected
#FUSES NOEBTR //Memory not protected from table reads
#FUSES NOEBTRB //Boot block not protected from table reads
#FUSES NOCPB //No Boot Block code protection
#FUSES NOMCLR //Master Clear pin NOT enabled
#FUSES LPT1OSC //Timer1 configured for low-power operation
#FUSES NOXINST //Extended set extension and Indexed Addressing mode disabled (Legacy mode)
#FUSES ICPRT //ICPRT enabled
#use delay(clock=8000000)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)
You can download the files here: main.h main.c
Electronic Schematic diagram
To convert a PWM signal to an analog signal -our sound- you need to build an RC network at the PIC PWM output. It is very useful to read the Microchip apllication note. There, you will find the equations to calculate the RC network for your specific design and the Timer2 values, PWM frecuency, etc.

Here, we can see the R1-C1 (RC network) and a non-inverted operational amplifier (gain=10) to make the sound a bit louder. We are going to calculate R1-C1 values:
Choose a C value, for example, C=0.01uF. Don't look at the circuit diagram above. For a frecuency f=20khz, C=0.01uF we calculate R= 795 Ohm. Now we use:
![]()
Being f the sound max. frecuency. Now you have R and C calculated. Ya tienes R y C.
You just need to configure the Timer2 to play the sound at the same speed as you sampled it. It depends on the PIC frecuency, sample frecuency, timer clock speed, overflow. There's an equation to calculate this:
Try with setup_timer_2(T2_DIV_BY_16, x, 1); Where x is:

***Common values:
For sample sounds at 22050Hz: CPU clock 4 Mhz, Timer2(Div_by_1,125,1), R=795 Ohm, C=10nF
For sample sounds at 44100Hz: CPu clock 8 Mhz, Timer2(Div_by_1,125,1), R=1,5KOhm, C=10nF
Dim lights Embed Embed this video on your site
| Próximo > |
|---|


