国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

STM32F407使用Helix庫(kù)軟解MP3并通過(guò)DAC輸出,最精簡(jiǎn)的STM32+SD卡實(shí)現(xiàn)MP3播放器

這篇具有很好參考價(jià)值的文章主要介紹了STM32F407使用Helix庫(kù)軟解MP3并通過(guò)DAC輸出,最精簡(jiǎn)的STM32+SD卡實(shí)現(xiàn)MP3播放器。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

只用STM32單片機(jī)+SD卡+耳機(jī)插座,實(shí)現(xiàn)播放MP3播放器!

看過(guò)很多STM32軟解MP3的方案,即不通過(guò)類(lèi)似VS1053之類(lèi)的解碼器芯片,直接用STM32和軟件庫(kù)解碼MP3文件,通常使用了labmad或者Helix解碼庫(kù)實(shí)現(xiàn),Helix相對(duì)labmad占用的RAM更少。但是大多數(shù)參考的方案還是用了外接IIS接口WM98xx之類(lèi)的音頻DAC芯片播放音頻,稍顯復(fù)雜繁瑣。STM32F407Vx本身就自帶了2路12位DAC輸出,最高刷新速度333kHz,除了分辨率差點(diǎn)意思,速度上對(duì)于MP3通常44.1kHz采樣率來(lái)說(shuō),用來(lái)播放音頻綽綽有余了。本文給的方案和源碼,直接用STM32軟解碼MP3并使用自帶的2個(gè)DAC輸出引腳輸出音頻左右聲道。

原理:STM32從SD讀取MP3文件原始數(shù)據(jù),發(fā)送給Helix庫(kù)解碼,Helix解碼后輸出PCM數(shù)據(jù)流,將此數(shù)據(jù)進(jìn)一步處理轉(zhuǎn)換后,按照左右聲道分別存入DAC輸出1和2緩存,通過(guò)定時(shí)器以MP3文件的采樣率的頻率提供DAC觸發(fā)節(jié)拍,通過(guò)DMA取緩存中高12位數(shù)據(jù)給DAC,在DAC1和2引腳產(chǎn)生音頻波形,通過(guò)電容耦合到耳機(jī)的左右聲道上。

MP3源文件是一種經(jīng)過(guò)若干算法,將原始音頻數(shù)據(jù)壓縮得來(lái)的,軟件解碼的過(guò)程是逆過(guò)程,將壓縮的音頻反向轉(zhuǎn)換為記錄了左右聲道、幅值的數(shù)據(jù)流,通常是PCM格式。

PCM:是模擬信號(hào)以固定的采樣頻率轉(zhuǎn)換成數(shù)字信號(hào)后的表現(xiàn)形式。記錄了音頻采樣的數(shù)據(jù),雙通道、16bit的PCM數(shù)據(jù)格式是以0軸為中心,范圍為-32768~32767的數(shù)值,每個(gè)數(shù)據(jù)占用2字節(jié),左聲道和右聲道交替存儲(chǔ),如圖。

STM32F407使用Helix庫(kù)軟解MP3并通過(guò)DAC輸出,最精簡(jiǎn)的STM32+SD卡實(shí)現(xiàn)MP3播放器,STM32開(kāi)發(fā),stm32,嵌入式硬件,單片機(jī)

?軟解碼得到的PCM數(shù)據(jù)到STM32的DAC緩存需要進(jìn)一步處理。STM32的DAC是12位的,其輸入范圍0~4095,而雙通道16位的PCM音頻數(shù)據(jù)是左右聲道交替存儲(chǔ),且數(shù)據(jù)范圍-32768~32767,因此PCM到STM32的DAC緩存要按照順序一拆為二,分為左右聲道,每個(gè)數(shù)據(jù)再加上32768,使其由short int的范圍轉(zhuǎn)換為unsigned short int,即0~65535。由于PCM數(shù)據(jù)是對(duì)音頻的采樣,因此調(diào)節(jié)音量(幅值)可以在此步驟一并處理,即音頻數(shù)據(jù) x 音量 /最大音量。至于DAC是12位,只需將DAC模式設(shè)置為左對(duì)齊12位,舍棄低4位即可。

到此,STM32的DAC輸出引腳上應(yīng)該已經(jīng)有音頻信號(hào)了,通常DAC引腳上串聯(lián)一個(gè)1~10uF的電容用來(lái)耦合音頻信號(hào),電容越大音質(zhì)越好,電容另一端接耳機(jī)插座的左聲道/右聲道,插上耳機(jī)就可以欣賞音樂(lè)啦!音質(zhì)嘛,反正我是聽(tīng)不出來(lái)好不好,跟商品MP3播放器差不多。如果不串聯(lián)電容,DAC引腳直連耳機(jī)插座左右聲道也能聽(tīng)到聲音,就是有些數(shù)字信號(hào)噪聲也會(huì)傳進(jìn)來(lái)。如果希望噪聲小一些,DAC引腳輸出端加一個(gè)下圖的低通濾波電路也是可以的。

STM32F407使用Helix庫(kù)軟解MP3并通過(guò)DAC輸出,最精簡(jiǎn)的STM32+SD卡實(shí)現(xiàn)MP3播放器,STM32開(kāi)發(fā),stm32,嵌入式硬件,單片機(jī)

?STM32F407使用Helix庫(kù)軟解MP3并通過(guò)DAC輸出,最精簡(jiǎn)的STM32+SD卡實(shí)現(xiàn)MP3播放器,STM32開(kāi)發(fā),stm32,嵌入式硬件,單片機(jī)

??

Helix移植:

Helix源碼的官網(wǎng)我沒(méi)找到,直接用了野火的例程里面的代碼,移植也很簡(jiǎn)單,不用改任何代碼,只需要將Helix文件夾拷貝到工程目錄里,然后在Keil中添加好文件,以及添加頭文件途徑,編譯即可。工程目錄如圖。

STM32F407使用Helix庫(kù)軟解MP3并通過(guò)DAC輸出,最精簡(jiǎn)的STM32+SD卡實(shí)現(xiàn)MP3播放器,STM32開(kāi)發(fā),stm32,嵌入式硬件,單片機(jī)

源碼:dac配置

dac.c

/**
  ******************************************************************************
  * @file    dac.c
  * @author  ZL
  * @version V0.0.1
  * @date    September-20-2019
  * @brief   DAC configuration.
  ******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "dac.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define   CNT_FREQ          84000000      // TIM6 counter clock (prescaled APB1)

/* DHR registers offsets */
#define DHR12R1_OFFSET             ((uint32_t)0x00000008)
#define DHR12R2_OFFSET             ((uint32_t)0x00000014)
#define DHR12RD_OFFSET             ((uint32_t)0x00000020)

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
uint32_t DAC_DHR12R1_ADDR = (uint32_t)DAC_BASE + DHR12R1_OFFSET + DAC_Align_12b_L;
uint32_t DAC_DHR12R2_ADDR = (uint32_t)DAC_BASE + DHR12R2_OFFSET + DAC_Align_12b_L;

uint16_t DAC_buff[2][DAC_BUF_LEN]; //DAC1、DAC2輸出緩沖

/* Private function prototypes -----------------------------------------------*/
static void TIM6_Config(void);

/* Private functions ---------------------------------------------------------*/
/**
  * @brief  DAC初始化
  * @param  none
  * @retval none
*/
void DAC_Config(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	DAC_InitTypeDef  DAC_InitStructure;
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
		
	DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
	DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO;
	DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
	DAC_Init(DAC_Channel_1, &DAC_InitStructure);
	DAC_Init(DAC_Channel_2, &DAC_InitStructure);
	
	//配置DMA
	DMA_InitTypeDef DMA_InitStruct;
	DMA_StructInit(&DMA_InitStruct);
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
	
	DMA_InitStruct.DMA_PeripheralBaseAddr = (u32)DAC_DHR12R1_ADDR;
	DMA_InitStruct.DMA_Memory0BaseAddr = (u32)&DAC_buff[0];//DAC1
	DMA_InitStruct.DMA_DIR = DMA_DIR_MemoryToPeripheral;
	DMA_InitStruct.DMA_BufferSize = DAC_BUF_LEN;
	DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
	DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
	DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;
	DMA_InitStruct.DMA_Priority = DMA_Priority_High;
	DMA_InitStruct.DMA_Channel = DMA_Channel_7;
	DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;
	DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
  DMA_InitStruct.DMA_MemoryBurst   = DMA_MemoryBurst_Single;
  DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
	
	DMA_Init(DMA1_Stream5, &DMA_InitStruct);
		
	DMA_InitStruct.DMA_PeripheralBaseAddr = (u32)DAC_DHR12R2_ADDR;
	DMA_InitStruct.DMA_Memory0BaseAddr = (u32)&DAC_buff[1];//DAC2
	DMA_Init(DMA1_Stream6, &DMA_InitStruct);
		
	//開(kāi)啟DMA傳輸完成中斷
	NVIC_InitTypeDef NVIC_InitStructure;
	
  NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream6_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
	
	DMA_ClearITPendingBit(DMA1_Stream6, DMA_IT_TCIF6);
	DMA_ClearITPendingBit(DMA1_Stream6, DMA_IT_HTIF6);
	DMA_ITConfig(DMA1_Stream6, DMA_IT_TC, ENABLE);
	DMA_ITConfig(DMA1_Stream6, DMA_IT_HT, ENABLE);

//	DMA_Cmd(DMA1_Stream5, ENABLE);
//	DMA_Cmd(DMA1_Stream6, ENABLE);
	DAC_Cmd(DAC_Channel_1, ENABLE);
  DAC_Cmd(DAC_Channel_2, ENABLE);
	
	DAC_DMACmd(DAC_Channel_1, ENABLE);
	DAC_DMACmd(DAC_Channel_2, ENABLE);
	
	TIM6_Config();
}

//配置DAC采樣率和DMA數(shù)據(jù)長(zhǎng)度,并啟動(dòng)DMA DAC
void DAC_DMA_Start(uint32_t freq, uint16_t len)
{
	//設(shè)置DMA緩沖長(zhǎng)度需要停止DMA
	DAC_DMA_Stop();
	//設(shè)置DMA DAC緩沖長(zhǎng)度
	DMA_SetCurrDataCounter(DMA1_Stream5, len);
	DMA_SetCurrDataCounter(DMA1_Stream6, len);
	
	//設(shè)置定時(shí)器
	TIM_SetAutoreload(TIM6, (uint16_t)((CNT_FREQ)/freq));
	
	//啟動(dòng)
	DMA_Cmd(DMA1_Stream5, ENABLE);
	DMA_Cmd(DMA1_Stream6, ENABLE);
}

//停止DMA DAC
void DAC_DMA_Stop(void)
{
	DMA_Cmd(DMA1_Stream5, DISABLE);
	DMA_Cmd(DMA1_Stream6, DISABLE);
}

//定時(shí)器6用于設(shè)置DAC刷新率
static void TIM6_Config(void)
{
  TIM_TimeBaseInitTypeDef TIM6_TimeBase;

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
  TIM_TimeBaseStructInit(&TIM6_TimeBase); 
	
  TIM6_TimeBase.TIM_Period        = (uint16_t)((CNT_FREQ)/44100);
  TIM6_TimeBase.TIM_Prescaler     = 0;
  TIM6_TimeBase.TIM_ClockDivision = 0;
  TIM6_TimeBase.TIM_CounterMode   = TIM_CounterMode_Up;
  TIM_TimeBaseInit(TIM6, &TIM6_TimeBase);
	
  TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update);
  TIM_Cmd(TIM6, ENABLE);
}

/**
  * @brief  DAC out1 PA4輸出電壓
  * @param  dat:dac數(shù)值:,0~4095
  * @retval none
*/
void DAC_Out1(uint16_t dat)
{
	DAC_SetChannel1Data(DAC_Align_12b_R,  dat);
	DAC_SoftwareTriggerCmd(DAC_Channel_1, ENABLE);
}

/**
  * @brief  DAC out2 PA5輸出電壓
  * @param  dat:dac數(shù)值:,0~4095
  * @retval none
*/
void DAC_Out2(uint16_t dat)
{
	DAC_SetChannel2Data(DAC_Align_12b_R,  dat);
	DAC_SoftwareTriggerCmd(DAC_Channel_2, ENABLE);
}

/********************************************* *****END OF FILE****/

源碼:MP3播放流程 (原創(chuàng)野火,參考了野火的例程,本人進(jìn)行整理和修改)

MP3player.c

/*
******************************************************************************
* @file    mp3Player.c
* @author  fire
* @version V1.0
* @date    2023-08-13
* @brief   mp3解碼
******************************************************************************
*/
#include <stdio.h>
#include <string.h>
#include "ff.h" 
#include "mp3Player.h"
#include "mp3dec.h"
#include "dac.h"
#include "led.h"

/* 推薦使用以下格式mp3文件:
 * 采樣率:44100Hz
 * 聲  道:2
 * 比特率:320kbps
 */

/* 處理立體聲音頻數(shù)據(jù)時(shí),輸出緩沖區(qū)需要的最大大小為2304*16/8字節(jié)(16為PCM數(shù)據(jù)為16位),
 * 這里我們定義MP3BUFFER_SIZE為2304
 */
#define MP3BUFFER_SIZE  2304
#define INPUTBUF_SIZE   3000

static HMP3Decoder		Mp3Decoder;			/* mp3解碼器指針	*/
static MP3FrameInfo		Mp3FrameInfo;		/* mP3幀信息  */
static MP3_TYPE mp3player;            /* mp3播放設(shè)備 */
volatile uint8_t Isread = 0;          /* DMA傳輸完成標(biāo)志 */
volatile uint8_t dac_ht = 0;          //DAC dma 半傳輸標(biāo)志

uint32_t led_delay = 0;

uint8_t inputbuf[INPUTBUF_SIZE]={0};     /* 解碼輸入緩沖區(qū),1940字節(jié)為最大MP3幀大小  */
static short outbuffer[MP3BUFFER_SIZE];  /* 解碼輸出緩沖區(qū)*/

static FIL file;			/* file objects */
static UINT bw;       /* File R/W count */
FRESULT result; 

//從SD卡讀取MP3源文件進(jìn)行解碼,并傳入DAC緩沖區(qū)
int MP3DataDecoder(uint8_t **read_ptr, int *bytes_left)
{
	int err = 0, i = 0, outputSamps = 0;

	//bufflag開(kāi)始解碼 參數(shù):mp3解碼結(jié)構(gòu)體、輸入流指針、輸入流大小、輸出流指針、數(shù)據(jù)格式
	err = MP3Decode(Mp3Decoder, read_ptr, bytes_left, outbuffer, 0);
	
	if (err != ERR_MP3_NONE)	//錯(cuò)誤處理
	{
		switch (err)
		{
			case ERR_MP3_INDATA_UNDERFLOW:
							printf("ERR_MP3_INDATA_UNDERFLOW\r\n");
							result = f_read(&file, inputbuf, INPUTBUF_SIZE, &bw);
							*read_ptr = inputbuf;
							*bytes_left = bw;
				break;		
			case ERR_MP3_MAINDATA_UNDERFLOW:
							/* do nothing - next call to decode will provide more mainData */
							printf("ERR_MP3_MAINDATA_UNDERFLOW\r\n");
				break;		
			default:
							printf("UNKNOWN ERROR:%d\r\n", err);		
							// 跳過(guò)此幀
							if (*bytes_left > 0)
							{
								(*bytes_left) --;
								read_ptr ++;
							}
				break;
		}
		return 0;
	}
	else		//解碼無(wú)錯(cuò)誤,準(zhǔn)備把數(shù)據(jù)輸出到PCM
	{
		MP3GetLastFrameInfo(Mp3Decoder, &Mp3FrameInfo);		//獲取解碼信息				
		/* 輸出到DAC */
		outputSamps = Mp3FrameInfo.outputSamps;						//PCM數(shù)據(jù)個(gè)數(shù)
		if (outputSamps > 0)
		{
			if (Mp3FrameInfo.nChans == 1)	//單聲道
			{
				//單聲道數(shù)據(jù)需要復(fù)制一份到另一個(gè)聲道
				for (i = outputSamps - 1; i >= 0; i--)
				{
					outbuffer[i * 2] = outbuffer[i];
					outbuffer[i * 2 + 1] = outbuffer[i];
				}
				outputSamps *= 2;
			}//if (Mp3FrameInfo.nChans == 1)	//單聲道
		}//if (outputSamps > 0)
					
		//將數(shù)據(jù)傳送至DMA DAC緩沖區(qū)
		for (i = 0; i < outputSamps/2; i++)
		{
			if(dac_ht == 1)
			{
				DAC_buff[0][i] = outbuffer[2*i] * mp3player.ucVolume /100 + 32768;
				DAC_buff[1][i] = outbuffer[2*i+1] * mp3player.ucVolume /100 + 32768;
			}
			else
			{
				DAC_buff[0][i+outputSamps/2] = outbuffer[2*i] * mp3player.ucVolume /100 + 32768;
				DAC_buff[1][i+outputSamps/2] = outbuffer[2*i+1] * mp3player.ucVolume /100 + 32768;
			}
		}
		
		return 1;
	}//else 解碼正常
}

//讀取一段MP3數(shù)據(jù),并把讀取的指針賦值read_ptr,長(zhǎng)度賦值bytes_left
uint8_t read_file(const char *mp3file, uint8_t **read_ptr, int *bytes_left)
{
	result = f_read(&file, inputbuf, INPUTBUF_SIZE, &bw);
	
	if(result != FR_OK)
	{
		printf("讀取%s失敗 -> %d\r\n", mp3file, result);
		return 0;
	}
	else
	{
		*read_ptr = inputbuf;
		*bytes_left = bw;
		
		return 1;
	}
}

/**
  * @brief  MP3格式音頻播放主程序
  * @param  mp3file MP3文件路徑
  * @retval 無(wú)
  */
void mp3PlayerDemo(const char *mp3file)
{
	uint8_t *read_ptr = inputbuf;
	int	read_offset = 0;				/* 讀偏移指針 */
	int	bytes_left = 0;					/* 剩余字節(jié)數(shù) */	
	
	mp3player.ucStatus = STA_IDLE;
	mp3player.ucVolume = 15; //音量值,100滿(mǎn)
	
	//嘗試打開(kāi)MP3文件
	result = f_open(&file, mp3file, FA_READ);
	if(result != FR_OK)
	{
		printf("Open mp3file :%s fail!!!->%d\r\n", mp3file, result);
		result = f_close (&file);
		return;	/* 停止播放 */
	}
	printf("當(dāng)前播放文件 -> %s\n", mp3file);
	
	//初始化MP3解碼器
	Mp3Decoder = MP3InitDecoder();	
	if(Mp3Decoder == 0)
	{
		printf("初始化helix解碼庫(kù)設(shè)備失??!\r\n");
		return;	/* 停止播放 */
	}
	else
	{
		printf("初始化helix解碼庫(kù)完成\r\n");
	}
	
	//嘗試讀取一段MP3數(shù)據(jù),并把讀取的指針賦值read_ptr,長(zhǎng)度賦值bytes_left
	if(!read_file(mp3file, &read_ptr, &bytes_left))
	{
		MP3FreeDecoder(Mp3Decoder);
		return;	/* 停止播放 */
	}
	
	//嘗試解碼成功
	if(MP3DataDecoder(&read_ptr, &bytes_left))
	{
		//打印MP3信息
		printf(" \r\n Bitrate       %dKbps", Mp3FrameInfo.bitrate/1000);
		printf(" \r\n Samprate      %dHz",   Mp3FrameInfo.samprate);
		printf(" \r\n BitsPerSample %db",    Mp3FrameInfo.bitsPerSample);
		printf(" \r\n nChans        %d",     Mp3FrameInfo.nChans);
		printf(" \r\n Layer         %d",     Mp3FrameInfo.layer);
		printf(" \r\n Version       %d",     Mp3FrameInfo.version);
		printf(" \r\n OutputSamps   %d",     Mp3FrameInfo.outputSamps);
		printf("\r\n");
		
		//啟動(dòng)DAC,開(kāi)始發(fā)聲
		if (Mp3FrameInfo.nChans == 1)	//單聲道要將outputSamps*2
		{
			DAC_DMA_Start(Mp3FrameInfo.samprate, 2 * Mp3FrameInfo.outputSamps);
		}
		else//雙聲道直接用Mp3FrameInfo.outputSamps
		{
			DAC_DMA_Start(Mp3FrameInfo.samprate, Mp3FrameInfo.outputSamps);
		}
	}
	else //解碼失敗
	{
		MP3FreeDecoder(Mp3Decoder);
		return;
	}
	
	/* 放音狀態(tài) */
	mp3player.ucStatus = STA_PLAYING;
	
	/* 進(jìn)入主程序循環(huán)體 */
	while(mp3player.ucStatus == STA_PLAYING)
	{
			//尋找?guī)?,返回第一個(gè)同步字的位置
			read_offset = MP3FindSyncWord(read_ptr, bytes_left);
			if(read_offset < 0)					//沒(méi)有找到同步字
			{
				if(!read_file(mp3file, &read_ptr, &bytes_left))//重新讀取一次文件再找
				{
					continue;//回到while(mp3player.ucStatus == STA_PLAYING)后面
				}
			}
			else//找到同步字
			{			
				read_ptr   += read_offset;	//偏移至同步字的位置
				bytes_left -= read_offset;	//同步字之后的數(shù)據(jù)大小	
				
				if(bytes_left < 1024)				//如果剩余的數(shù)據(jù)小于1024字節(jié),補(bǔ)充數(shù)據(jù)
				{
					/* 注意這個(gè)地方因?yàn)椴捎玫氖荄MA讀取,所以一定要4字節(jié)對(duì)齊  */
					u16 i = (uint32_t)(bytes_left)&3;	//判斷多余的字節(jié)
					if(i) i=4-i;						//需要補(bǔ)充的字節(jié)
					memcpy(inputbuf+i, read_ptr, bytes_left);	//從對(duì)齊位置開(kāi)始復(fù)制
					read_ptr = inputbuf+i;										//指向數(shù)據(jù)對(duì)齊位置
					result = f_read(&file, inputbuf+bytes_left+i, INPUTBUF_SIZE-bytes_left-i, &bw);//補(bǔ)充數(shù)據(jù)
					if(result != FR_OK)
					{
						printf("讀取%s失敗 -> %d\r\n",mp3file,result);
						break;
					}
					bytes_left += bw;		//有效數(shù)據(jù)流大小
				}
			}
			
			//MP3數(shù)據(jù)解碼并送入DAC緩存
			if(!MP3DataDecoder(&read_ptr, &bytes_left))
			{//如果播放出錯(cuò),Isread置1,避免卡住死循環(huán)
				Isread = 1;
			}
			
			//mp3文件讀取完成,退出
			if(file.fptr == file.fsize)
			{
				printf("單曲播放完畢\r\n");
				break;
			}	

			//等待DAC發(fā)送一半或全部中斷
			while(Isread == 0)
			{
				led_delay++;
				if(led_delay == 0xffffff)
				{
					led_delay=0;
					LED1_TROG;
				}
				//Input_scan();		//等待DMA傳輸完成,此間可以運(yùn)行按鍵掃描及處理事件
			}
			Isread = 0;
	}

	//運(yùn)行到此處,說(shuō)明單曲播放完成,收尾工作
	DAC_DMA_Stop();//停止喂DAC數(shù)據(jù)	
	mp3player.ucStatus = STA_IDLE;
	MP3FreeDecoder(Mp3Decoder);//清理緩存
	f_close(&file);	
}

void DMA1_Stream6_IRQHandler(void)
{
	if(DMA_GetITStatus(DMA1_Stream6, DMA_IT_HTIF6) != RESET) //半傳輸
	{	
		dac_ht = 1;		
		Isread=1;
		
    DMA_ClearITPendingBit(DMA1_Stream6, DMA_IT_HTIF6);
  }
	
	if(DMA_GetITStatus(DMA1_Stream6, DMA_IT_TCIF6) != RESET) //全傳輸
	{
		dac_ht = 0;
		Isread=1;
		
    DMA_ClearITPendingBit(DMA1_Stream6, DMA_IT_TCIF6);
  }
}

/***************************** (END OF FILE) *********************************/

源碼:main.c

/**
  ******************************************************************************
  * @file    ../User/main.c 
  * @author  ZL
  * @version V1.0
  * @date    2015-12-26
  * @brief   Main program body
  ******************************************************************************
**/

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "hw_includes.h"
#include "ff.h"  
#include "exfuns.h"  
#include "mp3Player.h"

//遍歷目錄文件并打印輸出
u8 scan_files(u8 * path)
{
	FRESULT res;
	char buf[512] = {0};	
  char *fn;
	
#if _USE_LFN
 	fileinfo.lfsize = _MAX_LFN * 2 + 1;
	fileinfo.lfname = buf;
#endif
 
	res = f_opendir(&dir,(const TCHAR*)path);
	if (res == FR_OK) 
	{	
		printf("\r\n"); 
		
		while(1){
			
			res = f_readdir(&dir, &fileinfo);                
			if (res != FR_OK || fileinfo.fname[0] == 0) break;  
 
#if _USE_LFN
			fn = *fileinfo.lfname ? fileinfo.lfname : fileinfo.fname;
#else							   
			fn = fileinfo.fname;
#endif	    

			printf("%s/", path);			
			printf("%s\r\n", fn);			
		} 
  }	  
 
  return res;	  
}

/**
  * @brief  Main program
  * @param  None
  * @retval None
  */
int main(void)
{	
	delay_init(168);
	usart1_Init(115200);
	LED_Init();
	DAC_Config();

	if(!SD_Init())
 	{
		exfuns_init();							//為fatfs相關(guān)變量申請(qǐng)內(nèi)存				 
		f_mount(fs[0],"0:",1); 					//掛載SD卡 
	}

	//打印SD目錄和文件
	scan_files("0:");
	
	LED0_ON;
	 		
	while (1)
	{
		mp3PlayerDemo("0:/斷橋殘雪.MP3");
		mp3PlayerDemo("0:/張國(guó)榮-玻璃之情.MP3");

		delay_ms(50);
	}
}

為方便調(diào)試測(cè)試,使用usart1打印數(shù)據(jù)。實(shí)測(cè)效果:

STM32F407使用Helix庫(kù)軟解MP3并通過(guò)DAC輸出,最精簡(jiǎn)的STM32+SD卡實(shí)現(xiàn)MP3播放器,STM32開(kāi)發(fā),stm32,嵌入式硬件,單片機(jī)

STM32F407使用Helix庫(kù)軟解MP3并通過(guò)DAC輸出,最精簡(jiǎn)的STM32+SD卡實(shí)現(xiàn)MP3播放器,STM32開(kāi)發(fā),stm32,嵌入式硬件,單片機(jī)

程序源碼與原理圖,測(cè)試音頻:

鏈接:https://pan.baidu.com/s/10hYXkrqnuBQgs0DWKLUUOA?pwd=iatt?
提取碼:iatt

知道這里下載要積分登錄什么的麻煩得很,所以程序放到百度網(wǎng)盤(pán)了,假如連接失效,記得在評(píng)論區(qū)喊我更新!

理論上STM32F1或者其他系列也能用這個(gè)方案,要自己改改測(cè)試嘍,本文把思路分享出來(lái)拋磚引玉。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-650614.html

到了這里,關(guān)于STM32F407使用Helix庫(kù)軟解MP3并通過(guò)DAC輸出,最精簡(jiǎn)的STM32+SD卡實(shí)現(xiàn)MP3播放器的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶(hù)投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • RT-Thread使用PWM實(shí)現(xiàn)燈亮度調(diào)節(jié)——STM32F407

    RT-Thread使用PWM實(shí)現(xiàn)燈亮度調(diào)節(jié)——STM32F407

    提示:文章寫(xiě)完后,目錄可以自動(dòng)生成,如何生成可參考右邊的幫助文檔 RT-Thread使用PWM實(shí)現(xiàn)燈亮度調(diào)節(jié)——STM32F407ZG 作為新入門(mén)的嵌入式選手,最近在學(xué)習(xí)RT-Thread操作系統(tǒng),鑒于自己健忘的記性,打算記錄下來(lái)后面好回顧學(xué)習(xí)。 今天要總結(jié)的是RT-Thread使用PWM實(shí)現(xiàn)燈亮度調(diào)節(jié)

    2024年02月15日
    瀏覽(16)
  • STM32F407——串口通信

    STM32F407——串口通信

    本文將對(duì)串口通信的分類(lèi)和基于 stm32 的串口配置進(jìn)行介紹,以及如何使用串口調(diào)試助手進(jìn)行串口收發(fā)功能的調(diào)試,旨在幫助還不會(huì)使用 stm32 單片機(jī)串口資源進(jìn)行通信的家人們快速學(xué)會(huì)如何使用串口來(lái)進(jìn)行通信。 (純干貨、快速上手、零基礎(chǔ)也能會(huì)?。。。?(1)串口,即串

    2023年04月08日
    瀏覽(44)
  • STM32F407的時(shí)鐘

    STM32F407的時(shí)鐘

    時(shí)鐘源用來(lái)為環(huán)形脈沖發(fā)生器提供頻率穩(wěn)定且電平匹配的方波時(shí)鐘脈沖信號(hào)。它通常由石英 晶體振蕩器和與非門(mén)組成的正反饋振蕩電路組成,其輸出送至環(huán)形脈沖發(fā)生器。 F4開(kāi)發(fā)指南P107 F4開(kāi)發(fā)指南P108 HSI高速內(nèi)部時(shí)鐘源 High Speed Internal。RC 振蕩器,頻率為 16MHz??梢灾苯幼鳛?/p>

    2024年02月10日
    瀏覽(38)
  • STM32F407的介紹

    STM32F407的介紹

    內(nèi)核 32位 高性能ARM Cortex-M4處理器 時(shí)鐘: 高達(dá)168MHz,實(shí)際還可以超頻一點(diǎn)點(diǎn) stm32f407的主頻通過(guò)PLL倍頻后能夠達(dá)到168MHz,而且芯片內(nèi)置一個(gè)16MHz的晶振和一個(gè)32KHz的晶振,可以滿(mǎn)足不同功耗的需求。 支持FPU(浮點(diǎn)運(yùn)算)和DSP指令 144引腳 114個(gè)IO口 存儲(chǔ)器容量: 1024K FLASH, 192K

    2024年02月10日
    瀏覽(31)
  • 初識(shí) STM32和STM32F407簡(jiǎn)介

    初識(shí) STM32和STM32F407簡(jiǎn)介

    2007 年 6 月,ST 在北京發(fā)布了全球第一款基于 ARM Cortex M3 內(nèi)核的 32 位通用微控制 器芯片:STM32F103,以?xún)?yōu)異的性能,豐富的資源,超高的性?xún)r(jià)比,迅速占領(lǐng)市場(chǎng),從此一鳴 驚人,一發(fā)不可收拾,截止到 2020 年 6 月,STM32 累計(jì)出貨量超過(guò) 45 億顆。 戰(zhàn)艦開(kāi)發(fā)板使用的 STM32F103ZET6

    2023年04月08日
    瀏覽(22)
  • STM32F407的PWM

    STM32F407的PWM

    泉水 STM32 的定時(shí)器除了 TIM6 和 7。其他的定時(shí)器都可以用來(lái)產(chǎn)生 PWM 輸出。 高級(jí)定時(shí)器 TIM1 和 TIM8 可以同時(shí)產(chǎn)生多達(dá) 7 路的 PWM 輸出。 通用定時(shí)器也能同時(shí)產(chǎn)生多達(dá) 4路的 PWM 輸出 STM32F407 最多可以同時(shí)產(chǎn)生 30 路 PWM 輸出! 這里我們僅利用 TIM14的 CH1 產(chǎn)生一路 PWM 輸出。 如上所

    2024年02月17日
    瀏覽(20)
  • STM32F407 移植 FreeRTOS

    STM32F407 移植 FreeRTOS

    本實(shí)驗(yàn)是基于正點(diǎn)原子 STM32F407ZG 探索者開(kāi)發(fā)板完成的,所以需要一個(gè)STM32F407ZG 探索者開(kāi)發(fā)板 用于移植的基礎(chǔ)工程(下面會(huì)講) FreeRTOS源碼(下面會(huì)講) 本實(shí)驗(yàn)所有用到的代碼:基于正點(diǎn)原子STM32F407的FreeRTOS移植工程.zip 1.1 移植前準(zhǔn)備 1.1.1 基礎(chǔ)工程 由于后續(xù)需要用到 LED、

    2024年02月08日
    瀏覽(28)
  • STM32F407 滴答定時(shí)器

    STM32F407 滴答定時(shí)器

    介紹STM32F407滴答定時(shí)器配置方法、使用方式,封裝延時(shí)函數(shù)得到精確的時(shí)間。 STM32F407參考手冊(cè)中第10章介紹了滴答定時(shí)器的校準(zhǔn)值。 M4權(quán)威指南介紹滴答定時(shí)器的章節(jié),M3權(quán)威指南中與M4權(quán)威指南中的介紹一樣。 在sys.c文件中增加滴答定時(shí)器的代碼 在delay.c文件增加以下代碼

    2024年02月11日
    瀏覽(30)
  • STM32F407添加DSP庫(kù)

    STM32F407添加DSP庫(kù)

    編譯程序出現(xiàn)以下報(bào)錯(cuò) 出現(xiàn) “error: #5: cannot open source input file “arm_const_structs.h”: No such file or directory” 錯(cuò)誤的原因是編譯器無(wú)法找到名為 “arm_const_structs.h” 的頭文件。 頭文件路徑錯(cuò)誤 頭文件未安裝或丟失 編譯器配置問(wèn)題 添加DSP庫(kù) 添加DSP庫(kù)可以參考這篇博客: STM32CubeMX關(guān)

    2024年02月05日
    瀏覽(34)
  • 基于STM32F407的智能門(mén)鎖

    基于STM32F407的智能門(mén)鎖

    ????????在消費(fèi)升級(jí)滲透在各個(gè)領(lǐng)域的今天,國(guó)民消費(fèi)發(fā)生著巨大的變化,與每個(gè)人息息相關(guān)的家居行業(yè)也是如此?,F(xiàn)今,越來(lái)越多的智能家居產(chǎn)品出現(xiàn)在普通老百姓的生活中,智能照明、智能窗簾、智能掃地機(jī)器人等各種智能產(chǎn)品都給人們的生活帶來(lái)了極大的便利。智能

    2024年02月11日
    瀏覽(21)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包