Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

MP3 Player with internal DAC

Status
Not open for further replies.

Sm.r.Tadayoni

Junior Member level 1
Joined
Jul 28, 2010
Messages
15
Helped
1
Reputation
2
Reaction score
1
Trophy points
1,283
Activity points
1,439
Hello my Friends... :)

I wanna to create a MP3 Player with ARM - STM32F103RE and internal DAC. As you know this micro controller has 2 DAC that can produce sound in stereo mode in 72MHz core clock. So i choose it for my project.

As i said, i choosed this mcu and i wrote a program for decoding MP3 (base on Helix codec and LPC1768 example code). But there is a problem about the producing of sound. there are lots of wind noise and "kh" in my speaker. i checked all of hardware connection to remove noises but after more times i understood the problem is about AudioBuffer of MP3 decoder. In the LPC1768 example the audio data has stored in audiobuf array and with the DMA send data to the UDA1380 Codec IC. So i replaced the DAC instead of UDA1380 in my project and use DMA to send audiobuf data on DAC corresponding register.

So my question is, why i have not pure sound on speaker?
 

Apply sin wave from genetator to amplifier and measure on speaker. Compare input and output.
 

Thanks for your reply, I've checked it and i can hear pure whistle sound. Also i have checked my DAC with demo wave sound (16000Hz mono) and it was pretty good. But mp3 player has noisy sound.
 

My Variables are :
Code:
uint8_t 		readBufAddress[8196] 	__attribute__((at((uint32_t) 0x20000800)));

#define  READBUF_BASEADDR	&readBufAddress
#define  READBUF_SIZE_USB  (1024*8)
#define  READBUF_SIZE_SD  (1024*8)

#define  	AUDIOBUF_BASEADDR	((uint32_t) (0x2000A020)) 

#define      AUDIOBUF_SIZE		(0x1200)	/* 4608 byte (4.5KB) */

/* Allocate 2 or 3 audio buffer to store PCM after decoding */
#define  MAX_AUDIOBUF_NUM	3
#if (MAX_AUDIOBUF_NUM < 2) || (MAX_AUDIOBUF_NUM > 3)
    #error Invalid MAX_AUDIOBUF_NUM setting.
#endif

enum {AUDIOBUF0=0, AUDIOBUF1=1, AUDIOBUF2};
typedef struct
{
	uint32_t 	BaseAddr;  /* base address for the audio buf */
	int32_t 	Size;		/* size of the audio buf */
	uint8_t 	Empty;		/* buf status, 1-> empty */
} AUDIOBUF;

AUDIOBUF audiobuf[MAX_AUDIOBUF_NUM] = {
	{AUDIOBUF_BASEADDR, -1, 1},
	{AUDIOBUF_BASEADDR+AUDIOBUF_SIZE, -1, 1},
#if MAX_AUDIOBUF_NUM==3
	{AUDIOBUF_BASEADDR+AUDIOBUF_SIZE*2, -1, 1},
#endif
};

This is my DAC configuration :
Code:
void DAC_Init(void) {
	/* Configure the DAC peripheral */
	DacHandle.Instance = DACx;

	DAC_Ch1_Config();
}

void DAC_Ch1_Config(void) {
  /* Initialize the DAC peripheral */
  if (HAL_DAC_Init(&DacHandle) != HAL_OK)
  {
    /* Initialization Error */
    Error_Handler(ERROR_TOGGLE_TIME_DAC);
  }

  /* DAC channel1 Configuration */
  sConfig.DAC_Trigger = DAC_TRIGGER_T6_TRGO;
  sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;

  if (HAL_DAC_ConfigChannel(&DacHandle, &sConfig, DACx_CHANNEL) != HAL_OK)
  {
    /* Channel configuration Error */
    Error_Handler(ERROR_TOGGLE_TIME_DAC);
  }	
}

void DMA_SendData(uint32_t *buf, uint32_t len) {

	if (HAL_DAC_Start_DMA(&DacHandle, DACx_CHANNEL, buf, len, DAC_ALIGN_8B_R) != HAL_OK)
	{
		/* Start DMA Error */
		Error_Handler(ERROR_TOGGLE_TIME_DAC);
	}
}

void DAC_DMA_IRQHandler(void) {
	LED_Toggle(LED2);
        {						
	    DAC_DMA_Done = 1;

            frame_out_flag = !frame_out_flag;
            if (frame_out_flag) LED_On (LED_FRAME_OUT);
            else                LED_Off (LED_FRAME_OUT);

            /* Data in audiobuf[PlayingBufIdx] has been sent out */
    		audiobuf[PlayingBufIdx].Empty = 1;
    		audiobuf[PlayingBufIdx].Size = -1;

		    /* Send the data in next audio buffer */
		    PlayingBufIdx++;
		    if (PlayingBufIdx == MAX_AUDIOBUF_NUM)
			    PlayingBufIdx = 0;
    	#if 1
    		if (audiobuf[PlayingBufIdx].Empty == 1)
    		{
                /* If empty==1, it means read file+decoder is slower than playback
                (it will cause noise) or playback is over (it is ok). */
    			decode_slower_flag ^= 0x1;
    			if (decode_slower_flag) LED_On (LED_DEC_SLOWER);
    			else                    LED_Off (LED_DEC_SLOWER);  

                DMA_Buf_Empty = 1;
				DAC_DeInit();
				Timer_6_Off();
                return;
            }
    	#endif
			DAC_Init();
			DMA_SendData((uint32_t *)audiobuf[PlayingBufIdx].BaseAddr, audiobuf[PlayingBufIdx].Size);
			Timer_6_On();
			DAC_DMA_Done = 0;	
 	    }
}

Timer 6 Config :
Code:
volatile void Timer_6_Off(void) {
	/* Disable TIM peripheral counter */
	HAL_TIM_Base_Stop(&htim);	
}

volatile void Timer_6_On(void) {
	/* Disable TIM peripheral counter */
	HAL_TIM_Base_Start(&htim);	
}

void setMp3Bitrate(uint32_t bitrate)
{
  /*##-1- Configure the TIM peripheral #######################################*/
  /* Time base configuration */
  htim.Instance = TIM6;

  htim.Init.Period               = 0x26A;
  htim.Init.Prescaler           = 0;
  htim.Init.ClockDivision      = 0;
  htim.Init.CounterMode       = TIM_COUNTERMODE_UP;
  htim.Init.RepetitionCounter = 0;
  HAL_TIM_Base_Init(&htim);

  /* TIM6 TRGO selection */
  sMasterConfig.MasterOutputTrigger  = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode      = TIM_MASTERSLAVEMODE_DISABLE;

  HAL_TIMEx_MasterConfigSynchronization(&htim, &sMasterConfig);

  /*##-2- Enable TIM peripheral counter ######################################*/
  HAL_TIM_Base_Start(&htim);	
}

and this is my MP3 playback :
Code:
uint8_t MP3_Playback (void)
{
    /* Init variables */
	bytesLeft = 0;
	outOfData = 0;
	eofReached = 0;
	readPtr = readBuf = (uint8_t *)(READBUF_BASEADDR);
	totalDecTime = 0;
	nFrames = 0;
	nReadFileBlkCnt = 0;
	totalReadFileTime = 0;
	pcmframe_size = 0;

    /* Decode and playback from audio buf 0 */
    DecodeBufIdx = PlayingBufIdx = AUDIOBUF0;    
    audiobuf[AUDIOBUF0].Empty = audiobuf[AUDIOBUF1].Empty = 1;                 

    /* Decode the first frame to get the frame format */
    if (MP3_DecodeFrame() == 0)
    {
        pcmframe_size = (mp3FrameInfo.bitsPerSample / 8) * mp3FrameInfo.outputSamps;            
        MP3_AddAudioBuf (pcmframe_size);
	}
    else
    {
//      DEBUG_MSG("\n\rFailed to decode the 1st frame, try to increase READBUF_SIZE.\n\r");
        return 0x1;  /* fail to decode the first frame */
    }

    /* Display the frame format */
    MP3_DisplayFormat();

    /* configure appropriate bit rate for each mp3, otherwise, 
    the mp3 will be too fast or slow with default setting */    
  	if (MP3_ConfigBitrate(mp3FrameInfo.nChans, mp3FrameInfo.bitsPerSample, mp3FrameInfo.samprate))
 	{
 		//DEBUG_MSG("Unsupported MP3 format!\n\r");
         return 0x2; /* Unsupported MP3 format */
 	}

	setMp3Bitrate(DAC_TXBITRATE);
    /* Start I2S+DMA to send the first decoded frame */
	DAC_Init();
	Timer_6_On();	
        DMA_SendData((uint32_t *)audiobuf[AUDIOBUF0].BaseAddr, audiobuf[AUDIOBUF0].Size);
	DAC_DMA_Done = 0;
	DMA_Buf_Empty = 0;
              
    /* Then deocder all the left frames */
    do 
    { 
        /* wait for playback is complete to release buf */
        while (audiobuf[DecodeBufIdx].Empty == 0)
        {
            /* Decoder is faster than play, all buf is full */
			decode_faster_flag ^= 0x1;
			if (decode_faster_flag)  LED_On (LED_DEC_FASTER);
			else                     LED_Off (LED_DEC_FASTER);
        }

        /* Decoder one frame */
        if (MP3_DecodeFrame() == 0)
        {
			pcmframe_size = (mp3FrameInfo.bitsPerSample / 8) * mp3FrameInfo.outputSamps;
	   		MP3_AddAudioBuf (pcmframe_size);          
        }
    } while ((outOfData == 0) && (DMA_Buf_Empty ==0));
    	
	/* Playback complete */
    WAIFORDMADONE;
	
	BSP_LED_On(LED1);
	BSP_LED_On(LED2);
	BSP_LED_On(LED3);
	HAL_Delay(1000);
	BSP_LED_Off(LED1);
	BSP_LED_Off(LED2);
	BSP_LED_Off(LED3);
	HAL_Delay(1000);
	BSP_LED_On(LED1);
	BSP_LED_On(LED2);
	BSP_LED_On(LED3);
	HAL_Delay(1000);
	BSP_LED_Off(LED1);
	BSP_LED_Off(LED2);
	BSP_LED_Off(LED3);


    if (outOfData == 1 || (DMA_Buf_Empty==1))
    {   

    #if 1
    	MP3GetLastFrameInfo(hMP3Decoder, &mp3FrameInfo);
    	audioSecs = ((float)nFrames * mp3FrameInfo.outputSamps) / ( (float)mp3FrameInfo.samprate * mp3FrameInfo.nChans);
    	//DEBUG_MSG("Statistics: \n\r");
    	//DEBUG_MSG("    Decode: %d frames, %d ms (%d ms/frame)\n\r", nFrames, totalDecTime, totalDecTime/nFrames);
    	//DEBUG_MSG("    Audio duration(min:sec): %02d:%02d\n\r",  (uint32_t)audioSecs/60, (uint32_t)audioSecs%60);
#ifdef SUPPORT_USB
    	if(MP3List[File1.fs? CurrentMP3 : PreviousMP3].drive == 0)
    		//DEBUG_MSG("    File read speed: %d ms/%dkB\n\r",  totalReadFileTime/nReadFileBlkCnt, READBUF_SIZE_USB>>10);
#endif
#ifdef SUPPORT_SD
//     	if(MP3List[File1.fs? CurrentMP3 : PreviousMP3].drive == 1)
//      	DEBUG_MSG("    File read speed: %d ms/%dkB\n\r",  totalReadFileTime/nReadFileBlkCnt, READBUF_SIZE_SD>>10);
#endif
    #endif 
    }

    return 0;
}

uint8_t MP3_DecodeFrame ()
{
    uint8_t word_align, frame_decoded;    
    int nRead, offset, err = 0;

    frame_decoded = 0;
    word_align = 0;
    nRead = 0;
    offset = 0;

	do
	{
		/* somewhat arbitrary trigger to refill buffer - should always be enough for a full frame */
		if (bytesLeft < 2*MAINBUF_SIZE && !eofReached) 
        {
            /* Align to 4 bytes */
    		word_align = (4-(bytesLeft&3)) & 3;

            Timer = 0;
            /* Fill read buffer */
    	    nRead = MP3_FillReadBuffer(readBuf, readPtr, bytesLeft, word_align);
    		totalReadFileTime += Timer;
    		nReadFileBlkCnt++;
    
    		bytesLeft += nRead;
    		readPtr = readBuf + word_align;
    		if (nRead == 0)
    		{
    			eofReached = 1;	/* end of file */
    			outOfData = 1;
    		} 
		}

		/* find start of next MP3 frame - assume EOF if no sync found */
		offset = MP3FindSyncWord(readPtr, bytesLeft);
		if (offset < 0) {
			readPtr = readBuf;
			bytesLeft = 0;
			continue;
		}
		readPtr += offset;
		bytesLeft -= offset;
				
		//simple check for valid header
		if(((*(readPtr+1) & 24) == 8) || ((*(readPtr+1) & 6) != 2) || ((*(readPtr+2) & 240) == 240) || ((*(readPtr+2) & 12) == 12) || ((*(readPtr+3) & 3) == 2))
		{
			readPtr += 1;		//header not valid, try next one
			bytesLeft -= 1;
			continue;
		}

        Timer = 0;
		err = MP3Decode(hMP3Decoder, &readPtr, &bytesLeft, (short *)audiobuf[DecodeBufIdx].BaseAddr, 0);
		
		totalDecTime += Timer;
		nFrames++;
		if(err == -6)
		{
			readPtr += 1;
			bytesLeft -= 1;
			continue;
		}

		if (err) {
			/* error occurred */
			switch (err) {
			case ERR_MP3_INDATA_UNDERFLOW:
				outOfData = 1;
				break;
			case ERR_MP3_MAINDATA_UNDERFLOW:
				/* do nothing - next call to decode will provide more mainData */
				break;
			case ERR_MP3_FREE_BITRATE_SYNC:
			default:
				outOfData = 1;
				break;
			}
		} else {
			/* no error */
			MP3GetLastFrameInfo(hMP3Decoder, &mp3FrameInfo);				 
			frame_decoded = 1; 
		}

	} while (!frame_decoded && !outOfData);

    if (outOfData == 1) return 0x1; /* Decoder terminated */
    else                return 0x0;   /* Decoder success. */

}

In this case i assume that the SampleRate is constant that play mp3 in good timing.
 

as you said, i've checked again and this is what i see in oscop :

Sin wave : Input : Ch1 = blue, Output : Ch2 = yellow

before setting Speaker :
View attachment DS0003.BMP

after setting speaker :
View attachment DS0004.BMP

my amplifier is LM386 with this schem :
amp_schem.jpg
in my board i removed the Gain capacitor.

so is it correct output? does the negative voltage important for audio?
 
Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top