Jump to content
43oh

Petit FatFs sound player


Recommended Posts

I've been experimenting with playing sound. I'm using the code below to read raw 8bit unsigned PCM files

from the SDCard booster pack. I've attached the main routine. It is heavily commented so I'm not going

to say much more than here it is. The Petit Fat Filesystem code can be found on the documents tab

of the SDCard booster on the 43oh.com store.

 

I also want to thank oPossum and woodgrainn with their help creating a decent low pass RC filter. I ended up

using an LM386 as an amplifier on the P1.2 PWM output. Here is the schematic for that:

 

http://compendiumarcana.com/forumpics/pwm_amp.png

 

-rick

 

/*
* pcmplayer.cpp - play a 15.625kHz 8bit mono sound file using PWM.
*
* This snippet plays raw PCM audio files from an SDcard.  It looks
* for files named 0.raw, 1.raw ... 9.raw and attempts to play them
* in a loop.  To create these files, use the Audacity program to
* sample sound files, convert stereo to mono, then export them
* as 8bit unsigned data 15625Hz without a header.
*
* The SDcard I/O is handled using Petit FatFs and works with both FAT16
* and FAT32 formatted cards. PetitFS has a streaming read feature which
* allows you process each byte from the file without buffering using
* its callback feature.
*
*
* Inspired from various sources:
*
*  http://www.arduino.cc/playground/Code/PCMAudio
*  http://elm-chan.org/fsw/ff/00index_p.html
*  http://en.wikipedia.org/wiki/Pulse-code_modulation
*  http://en.wikipedia.org/wiki/Pulse-width_modulation
*  http://en.wikipedia.org/wiki/Low-pass_filter
*  http://sim.okawa-denshi.jp/en/CRtool.php
*  http://www.ti.com/lit/an/slaa497/slaa497.pdf
*  http://www.proaxis.com/~wagnerj/PWMfil/PWM%20Filters.pdf
*
*  help on low pass filtering from oPossum and woodgrainn
*
*  3920 bytes on a msp430g2553 with msp430-gcc 4.6.3 -Os
*/

/**
* pins used on msp430g2553
*
*              +-- --+
*              |  V  |
*              |     |
*  P1.2 TA0.1<<<     |
*              |     |
*              |     |
*              |     >>>> MOSI P1.7
*  P1.5 SCK <<<<     <<<< MISO P1.6
*  P2.0  CS <<<<     |
*              |     |
*              |     |
*              +-----+
*
* P1.2 - PWM output, run through a low pass RC filter. I'm using
* a 470ohm resistor, and 100nF cap played back on cheap airline
* headphones. Don't use these values with PC audio card mic or
* line in.
*
* P1.5, P1.6, P1.7  SCLK, MISO, MOSI (USCI SPI) connect to SD card
* P2.0 CS connect to SD card
*
*/

#include 
#include 

extern "C" {
#include "spi.h"
#include "diskio.h"
#include "pff.h"
}

#include "mmc.h"
#include "ringbuffer.h"

static const uint32_t TIMERA_CLK = 16000000;  // SMCLK_FREQ/1
static const uint32_t SAMPLE_RATE = 15625;    // nice power of 2 friendly value sample rate

RingBuffer sample_buffer = {{0}, 0,0}; // structure to buffer our samples

/*
* on_streaming_data() - called when pf_read completes a read
*
* pf_read in streaming mode calls this function after each byte
* is read. When we get a new value we stuff it into a ring buffer.
* The ISR reads from this ring_buffer. This function is the
* producer, the ISR is the consumer.
*/

void on_streaming_data(uint8_t sample)
{
   register int16_t rc, scaled_sample;

   scaled_sample = sample < 24 ? 24 : sample; // avoid low range glitches
   scaled_sample <<= 2; // scale it up from 0-255 to 0-1023
   do {
       /*
        * stuff this at the end of our circular queue
        * if rc == -1 then it would overflow, wait for
        * ISR to read a byte
        */
       rc = sample_buffer.push_back(scaled_sample);
   } while (rc == -1);
}

/**
* Timer0 CCR0 overflow handler
*
* This method is called each time the CCR0 value counts up to 1023.
* We grab the next sample from the circular buffer and scale it
* up from the 8 bit unsigned value read from the disk to match
* the CCR0 scale.
*/

#pragma vector = TIMER0_A0_VECTOR
void __interrupt TIMER0_OVERFLOW_VECTOR_ISR(void)
{
//    P1OUT ^= BIT0;
   TACCR1 = sample_buffer.pop_front();
//    P1OUT ^= BIT0;
}

int main(void)
{
   FRESULT res; // PetitFS variables
   DRESULT dres;
   FATFS fs;

   WDTCTL = WDTPW + WDTHOLD;        // Stop WDT

   DCOCTL = 0;                      // Run at 16 MHz
   BCSCTL1 = CALBC1_16MHZ;
   DCOCTL = CALDCO_16MHZ;
   //DCOCTL += 3;                   // fine tune 15.9MHz to 16.0MHz on my chip

   __delay_cycles(0xffff);          // delay for power up and stabilization

   spi_initialize();                // configure SPI for Petit FatFs 8MHz

//  P1OUT &= ~BIT0;
   P1DIR |= BIT2 /*| BIT0*/;        // P1.2 output
   P1SEL |= BIT2;                   // P1.2 use TA0.1

   //P1DIR |= BIT4; P1SEL |= BIT4;  // output SMCLK, useful for measuring

   /**
    * configure TimerA0 with a 15.625k frequency,
    * setup TA0.1 (P1.2) for the PWM output.
    */
   TACCR0 = (TIMERA_CLK/SAMPLE_RATE)-1;   // PWM Period 15.625k (64 us)
   TACCR1 = TACCR0/2;                     // Set initial PWM duty cycle to 50%
   TACCTL0 = CCIE;                        // Enable CCRO CCIFG interrupt. The TACCR0 CCIFG
                                          // interrupt flag is set when the timer counts to
                                          // the TACCR0 value.

   TACCTL1 = OUTMOD_7;                    // Output is reset (0) when timer counts
                                          // to TACCR1, It is set (1) when timer counts
                                          // to TACCR0 value

   TACTL = TASSEL_2 | ID_0 | MC_1;        // use SMCLK / 1, in UP MODE to CCR0
                                          // value 1024-1 cycles((16MHz/1)/15.625kHz)

   __enable_interrupt();

   /**
    * play the same songs in a loop
    */
   while(1) {
       dres = disk_initialize();
       if ( dres == RES_OK ) {
           res = pf_mount(&fs);
           if ( res == FR_OK ) {
               int n;
               char fileName[] = "0.raw";

               for ( n=0; n < 10; n++ ) {
                   fileName[0] = n+'0';
                   res = pf_open(fileName);
                   if ( res == FR_OK ) {
                       WORD bytes_read;

                       do {
                           res = pf_read(0, 32768, &bytes_read); /* pf_read calls on_streaming_data() for each byte read */
                           if (res != FR_OK) {
                               break;
                           }
                       } while (bytes_read == 32768); /* read a cluster at a time */
                   }
               }
           }
       }
   }

   return 0;
}

Link to post
Share on other sites

Here are some sample sounds to play. The are all 8 bit unsigned raw files, without any header sampled at 15625. You can import them into Audacity to see what they should sound like on your msp430g2553. Or you could copy them on to an SDcard at the top level directory to play them.

 

-rick

testsounds.zip

Link to post
Share on other sites
  • 1 year later...

I was wondering if you had a zip file of all the includes that you used for this project? I am trying to piece this together using code i've found online but its not quite working out.

 

Thanks.

You need to quote his post, so he receives a notification when you ask a question. The quote button in on the bottom right of each post.

Link to post
Share on other sites

I was wondering if you had a zip file of all the includes that you used for this project? I am trying to piece this together using code i've found online but its not quite working out.

 

Thanks.

I don't, this is all there is. TBH trying to do this with a file system is just an exercise in frustration. You might look at just using raw reads and writes to the sdcard without a filesystem.

Link to post
Share on other sites
  • 2 months later...
  • 1 year later...
  • 6 months later...
  • 1 month later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...