This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Audio Playback using MSP430G2553

Hi,

        I am trying to build a code which reads HEX data(converted from .wav  audio file PCM ,8Khz,mono), which is stored in EEPROM via I2C. I am using the source code form eZ430-SPEECH. The idea here in the source code from "TI" is to generate PWM signals on TIMERA0 according to stored hex values in EEPROM. Pin1.1 TA0.0 is used in OUTMODE_3 set/reset mode to provide PWM. I could able to go through the code and can see the process flow (step by step debug), But I was unable to listen any sound when I connect a simple headphone to P1.1 and GND pins. Please help me in understanding the process of Audio Playback. Here I am attaching my code and the HEX file of generated audio data. please comment on this. I am using CCS5.2

7178.ODG_voice_test1.0.rar

5164.clear_voiceHEX.rar

 I have figured out the basic flaw in the code and modified it, I have set PWM functionality to TA0.0 ( P1.1 ) which cannot do it (PWM), I have changed it to P1.2 and now I can see PWM signals over the scope. But the sound dnt make any sense its just Gibberish. Here is the new modified code please say me where I am going wrong.

#include <msp430.h>
#include <stdint.h>
#include "I2CMaster.h"

typedef unsigned char u8_t;
typedef unsigned short int u16_t;
typedef signed char i8_t;
typedef short int i16_t;

// Data buffer for I2C ops.
#define    BUFF_SZ            64
 uint8_t Buffer[ BUFF_SZ ];

// If this variable is locally declared as uint32_t in main it is not
// properly allocated/handled by the compiler unless optimization is
// turned on
uint32_t ImgSize = 0;
 uint8_t uAvgCntr;
 uint8_t uAudioSample1;
 uint8_t uAudioSample2;
int iAudioSampleDifference;
uint32_t lAudioSampleCnt;
uint32_t lAudioSampleLength;

void initTA0(void)
{
    // Initialise Timer_A for PWM output
      TACTL = TASSEL_2 | ID_1 | TACLR; //|TAIE; // clock = SMCLK /2 = 16 MHz / 2
                                                // overflow interrupt enabled
      TACCTL1 = CM_0 | OUTMOD_7 | CCIE;         // CM_0: Compare mode (no capture mode)
                                                // OUTMOD_7 set/rest mode PWm
                                                // CCIE: Compare Interrupt Enabled
      TACCR0 = 0xFF; // 8-bit counter from 00h to FFh---sri Notes: Usually this goes as PWM period
    // Set P1.2 as output for PWM signals
    //sri Notes: P1.1 cannot be used for PWM output
        P1DIR |= BIT2;                            // Pin4-P1.2/TA0.1 is output
        P1SEL |= BIT2;                          // Pin4-P1.2/TA0.1 is TA1 output WITH PWM functionality

}

#define StartTimer()    TACTL |= MC_1; //START TIMER IN UP MODE
#define StopTimer()        TACTL |= MC_0;

void readImgSz(void)
{
    // Read the header of the binary image to get its size. Start
    // reading from memory address 0.
    Buffer[0] = 0x00;
    Buffer[1] = 0x00;
    I2CWrite( EEPROM_ADDR , Buffer , 2 );
    I2CRead( EEPROM_ADDR , Buffer , 4 );
    // 32-bit size value
    ImgSize = (uint32_t) Buffer[0];
    ImgSize <<= 8;
    ImgSize |= (uint32_t) Buffer[1];
    ImgSize <<= 8;
    ImgSize |= (uint32_t) Buffer[2];
    ImgSize <<= 8;
    ImgSize |= (uint32_t) Buffer[3];
}

void main (void)
{
    WDTCTL = WDTPW + WDTHOLD;        // Stop watchdog timer
    DCOCTL = CALDCO_16MHZ;            // Select DCO@16MHz as clock sources
    BCSCTL1 = CALBC1_16MHZ;
    I2CInit();                        // Initialise I2C
    _enable_interrupt();            // Enable interrupts
    initTA0();                        //Initialise TimerA

    while(1)
    {
                readImgSz();
                I2CRead( EEPROM_ADDR , Buffer , 2 );

                    uAudioSample1 = Buffer[0];
                    uAudioSample2 = Buffer[1];
                    uAvgCntr = 0;

                StartTimer();

                for (lAudioSampleCnt = ImgSize-1; lAudioSampleCnt > 0; lAudioSampleCnt--)
                    {
                      __bis_SR_register(LPM0_bits + GIE);   // LPM0: keep DCO running
                      I2CRead( EEPROM_ADDR , Buffer , 1 );
                      uAudioSample1 = uAudioSample2;
                      uAudioSample2 = Buffer[0];
                    }

                StopTimer();
    }

}
//******************************************************************************
// Timer A1 interrupt service routine
//******************************************************************************
#pragma vector=TIMER0_A1_VECTOR
__interrupt void Timer0_A1_ISR (void)
{                                           // Timer interrupt (PWM, one sample)
  switch (__even_in_range(TAIV,10))
  {
    case 2:                                 // Vector 2: TACCR1
            iAudioSampleDifference = (int) uAudioSample2 - (int) uAudioSample1;
            if (iAudioSampleDifference > 68)
            {
              TACTL   &= ~TAIFG;            // clear overflow flag TAIFG IFG
              TACTL   |= TAIE;              // enable overflow interrupt TAIFG
              TACCTL1 &= ~CCIE;             // turn off TACCR1 IFG
            }
            switch (__even_in_range(uAvgCntr,6))
            {
              case 0: TACCR1 = (uAudioSample1
                               + uAudioSample1
                               + uAudioSample1
                               + uAudioSample2) >> 2;
                      break;

              case 2: TACCR1 = (uAudioSample1
                               + uAudioSample2) >> 1;
                      break;

              case 4: TACCR1 = (uAudioSample1
                               + uAudioSample2
                               + uAudioSample2
                               + uAudioSample2) >> 2;
                      break;

              case 6: TACCR1 = uAudioSample2;
                      __bic_SR_register_on_exit(LPM4_bits);

                                            // Clear all LPM bits from 0(SR)
                                            // this restarts audio output loop
                                            // and read next audio sample from
                                            // SPI-Flash memory
                      break;

            }
            uAvgCntr+=2;                    // increment averaging counter (always by 2)
            uAvgCntr = uAvgCntr & 0x06;     // averaging counter range: 0, 2, 4, 6
            break;
    case 4:                                 // Vector 4: TACCR2
            break;
    case 10:                                // Vector 10: TAIFG Flag
            TACCTL1 &= ~CCIFG;              // clear TACCR1 IFG
            TACCTL1 |= CCIE;                // enable TACCR1 IFG
            TACTL   &= ~TAIE;               // turn off overflow interrupt TAIFG
            break;
  }
} // __interrupt void TimerA1 (void)

  • the current sourced by P1.1 may not be quite enough to drive headphones. You may want to create a "source follower" circuit that is driven by P1.1.

    That's just my first reaction to your post, never tried generating speech from a digital output myself. An interesting endeavor though.

  • HI Arturs Elksnis,

                      Thank you so much for your quick reply. I have earlier experience of driving "Head phones" out of P1.1 with PWM (1KHZ--50% duty cycle)out put. I don't know weather Audio can drive it. Anyway I will put a source follower just to make sure. I have this strong feeling of something went wrong in the code or something haven't coded properly. I am sorry if this kind of review should not be asked, but this forums have solved many issues so, I hope I get one solution as well.

                                             Thank you.

    Regards,

    Sri.

  • It seems you are connecting PWM output to headphones directly. That will not work. You need to generate PWM and then filter it, use a amplifier after that and then you connect a speaker or headphone.

    Also, your PWM frequency has to be high enough compared to the audio signal you want to generate. Because in this scenario, we are superimposing an audio signal on a high frequency signal using PWM. The high frequencies will have to filtered for generating anything intelligible. Use FilterPro for designing a filter, use a amp and then see what you get.

  • sri-sri said:
    I have earlier experience of driving "Head phones" out of P1.1 with PWM (1KHZ--50% duty cycle)out put.

    Well, for the headphone, this is not PWM, this is a 1kHz rectangular audio signal (actually a 1kHz sune wave, a 3kHz sine wave with attenuation, a 5kHz sine wave with even more attenuation etc.)

    To generate an audio signal through PWM, the PWM frequency must be a multiple of the maximum audio frequency.

    For an audio signal of 20kHz, you'll need a PWM frequency of 40kHz at least, better a multiple of it. Then the PWM rectangular output is fed into a low-pass filter. To some extent, the magnetic coils of the headphone can serve as a simple low-pass filter, but this will drop quality. So the average voltage of multiple, filtered PWM cycles forms the momentary value of the audio signal.

  • Hi Jens-Michael Gross,

                 Yes, we need  [ frequency of the carrier > 2(frq of message signal) ] in my case I have 8khz PCM, 8-bit ,mono audio signal generated from Audio-alchemy s/w. I have the PWM period of  about 31.25 KHZ  [TACCRO = 0XFF] Ie., 8000000 HZ(SMCLK)/255.  this is >  than the audio frequency. I am sorry for not putting up all these details precisely, But the problem is I was unable to hear any meaningful sound. Off course without LPF. I taught I would at least listen the data with lot of noise.

  • Hi all ,

              solved the issue, I am just putting up as reference to others here, The code works fine and the problem is only with the data that has been written to EEPROM. All that needs to do is generate any audio, convert to intel hex using intelhex library (python codes) dump in to EEPROM using aardvark etc. and the code posted works really well.

    Regards,

    sri.

  • Hi
    I would like to Play one second Audio file from MSP430 internal Flash instead of external EEPROM. I would like to add Audio Header file in CCS Project and Point there for converting HEX data to PWM. What changes are needed?
    Please can you help me.

    Regards,
    Raju
  • If you want to place the data in internal eeprom (code area), then it is easier to include it as code rather than adding it manually to the linker output.

    If you define an array like this:

    const unsigned char data[size] = {
    #include "data.csv"
    0};

    you can have your data in a simple CSV (comma separated value) file "data.csv". The values can be decimals or hex values (with preceding '0x' then), separated by comma. The '0' above is for the case that the last line in your file ends with a comma, which happens when the file is e.g. exported from an Excel column. If there is no comma after the last value in the file, then the '0' must be omitted.
    Well, and the size of the file should be known.

    The code must be changed so that instead of reading from I2C byte by byte, instead it is read from the array with increasing index (up to size).

**Attention** This is a public forum