STM32CubeIDE Audio播放音频,DAC + TIM + DMA

STM32CubeIDE音频播放

随言:

STM32CubeIDE Audio播放音频,DAC + TIM + DMA STM32CubeIDE Audio播放音频,DAC + TIM + DMA_sudaroot的博客-优快云博客
STM32CubeIDE Audio播放音频,PWM + TIM STM32CubeIDE Audio播放音频,PWM + TIM_sudaroot的博客-优快云博客
STM32CbueIDE Audio播放音频 WM8978 + I2S STM32CbueIDE Audio播放音频 WM8978 + I2S_sudaroot的博客-优快云博客_wm8978全双工
STM32CubeIDE Audio播放音频 WM8978 + I2S + DMA双缓存模式 STM32CubeIDE Audio播放音频 WM8978 + I2S + DMA双缓存模式_sudaroot的博客-优快云博客
STM32CubeIDE USB Audio声卡 WM8978 + I2S STM32CubeIDE USB Audio声卡 WM8978 + I2S_sudaroot的博客-优快云博客

官方参考 文档 和 例程:

文档编号 AN3126   网址:意法半导体STM | STM32/STM8微控制器 | MCU单片机

例程编号 UM0891   网址:意法半导体STM | STM32/STM8微控制器 | MCU单片机

官方硬件连接:

文档编号(UM0841)

官方例程实现流程图:

官方例程播放音频代码:

/**
  * @brief  Start wave playing
  * @param  None
  * @retval None
  */
uint8_t WavePlayerMenu_Start(uint8_t *DirName, uint8_t *FileName, uint32_t *FileLen)
{
  DAC_InitTypeDef            DAC_InitStructure;
  DMA_InitTypeDef            DMA_InitStructure;
  uint32_t var;
  uint8_t tmp, KeyState = NOKEY;
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

  WaveFileStatus = Unvalid_RIFF_ID;

  /* TIM6 Configuration */
  TIM_DeInit(TIM6);

  /* DMA1 channel3 configuration */
  DMA_DeInit(DMA1_Channel3);
  DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHLCD_REG_8LCD_REG_1_ADDRESS;
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) & Wavebuffer;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
  DMA_InitStructure.DMA_BufferSize = 512;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  DMA_Init(DMA1_Channel3, &DMA_InitStructure);

  /* Enable DMA1 Channel3 */
  DMA_Cmd(DMA1_Channel3, ENABLE);

  /* DAC deinitialize */
  DAC_DeInit();
  DAC_StructInit(&DAC_InitStructure);

  /* Fill DAC InitStructure */
  DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO;
  DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
  DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable;

/* DAC Channel1: 8bit right---------------------------------------------------*/
  /* DAC Channel1 Init */
  DAC_Init(DAC_Channel_1, &DAC_InitStructure);
  /* Enable DAC Channel1: Once the DAC channel1 is enabled, PA.04 is
     automatically connected to the DAC converter. */
  DAC_Cmd(DAC_Channel_1, ENABLE);
  /* Enable DMA for DAC Channel1 */
  DAC_DMACmd(DAC_Channel_1, ENABLE);

  /* Read the Speech wave file status */
  WaveFileStatus = WavePlayer_WaveParsing(DirName, FileName, &wavelen);

  /* TIM6 TRGO selection */
  TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update);
  TIM_SetAutoreload(TIM6, TIM6ARRValue);

  if (WaveFileStatus == Valid_WAVE_File)  /* the .WAV file is valid */
  {
    /* Set WaveDataLenght to the Speech wave length */
    WaveDataLength = WAVE_Format.DataSize;
  }
  else
  {
    return NOKEY;
  }
  
  /* Start TIM6 */
  TIM_Cmd(TIM6, ENABLE);

  LCD_SetTextColor(LCD_COLOR_MAGENTA);
  /* Set the Back Color */
  LCD_SetBackColor(LCD_COLOR_BLUE);
  LCD_DrawRect(LCD_LINE_7, 310, 16, 300);

  while (WaveDataLength)
  {
    DFS_ReadFile(&fiwave, sector, Wavebuffer2, &var, SECTOR_SIZE);
    if (WaveDataLength) WaveDataLength -= 512;
    if (WaveDataLength < 512) WaveDataLength = 0;
    while (DMA_GetFlagStatus(DMA1_FLAG_TC3) == RESET)
    {
    }
    DMA1->IFCR = DMA1_FLAG_TC3;
    DMA1_Channel3->CCR = 0x0;

    DMA1_Channel3->CNDTR = 0x200;
    DMA1_Channel3->CPAR = 0x40007410;
    DMA1_Channel3->CMAR = (uint32_t) & Wavebuffer2;
    DMA1_Channel3->CCR = 0x2091;

    DFS_ReadFile(&fiwave, sector, Wavebuffer, &var, SECTOR_SIZE);
    if (WaveDataLength) WaveDataLength -= 512;
    if (WaveDataLength < 512) WaveDataLength = 0;
    
    while (DMA_GetFlagStatus(DMA1_FLAG_TC3) == RESET)
    {
    }
    DMA1->IFCR = DMA1_FLAG_TC3;
    DMA1_Channel3->CCR = 0x0;

    DMA1_Channel3->CNDTR = 0x200;
    DMA1_Channel3->CPAR = 0x40007410;
    DMA1_Channel3->CMAR = (uint32_t) & Wavebuffer;
    DMA1_Channel3->CCR = 0x2091;
  }
  DMA1_Channel3->CCR = 0x0;
  
  /* Disable TIM6 */
  TIM_Cmd(TIM6, DISABLE);
  WaveDataLength = 0;
  
  return NOKEY;
}

随言:

建议下载该例程看看源码,但是由于官方使用的是外部TF卡存储音频,有一个读取外部数据拷贝到SRAM的延时问题,故官方使用了双缓存区方式。

而我只想简单播放音频,故我找了一段15秒的16KHz_8bit_wav格式音频,直接转成C语言数组存在芯片内部flash。由于是放在内部flash,故不用担心数据拷贝的速度问题,所以我使用单缓冲区就可以了。甚至可以不需要把内部flash数据拷贝到缓存区,直接让DMA指向flash数据的地址。

音频的采样位数为8bit 16bit 24bit 32bit,采样位数越高当然音质越好,但是相对的存储也急剧增加。

注意:STM32F4的DAC最大分辨率为12bit,故我们只能使用8bit的音频。还有就是一般高采样位数音频转低采样位数音频的一定要适当加入抖动(噪声)。

硬件要求:一定要带功放模块。

开发板:STM32F407ZG,功放PAM8403,一个喇叭4Ω 3W。

STM32CubeIDE配置:

首先配置TIM6。使用TIM6触发DAC的将音频数字数据转换为模拟波形。由于我使用的音频为16KHz,

计算  1s / 16K = 0.0000625s = 62.5us。即每62.5us触发一次DAC转换。

TIM6的时钟是84MHz,分频系数42,故当前时钟为2MHz,向上计数一次为0.5us,计数值设为125,即可满足62.5us触发一次DAC转换。

DAC设置,PA4引脚,TIM6触发,打开DMA.

准备工作:

1、下载一首十几秒的歌曲。

2、准备一下音频编辑工具,我自己用Adobe Audition CS6。

这个包含两部分:专辑图和专辑信息(曲名、发行年份、流派等等)和 Au专用的数据5261信息,用于下次编辑时使用。
取消勾选,我们不需要这部分数据,否则音频播放会出现结尾爆破音。

3、使用winhex打开编辑好的音频文件,复制成C语言数组,新建一个文件粘贴即可。

代码:

把保存音频数据数组导入工程。但是wav文件前44个字节是格式头,故应该跳过,若没有了解过wav格式的,必须自己百度一下。

#define		BUFFER_SIZE		(1024)

uint8_t Buffer0[BUFFER_SIZE] = {0};

void Music_Player(void)
{
	uint32_t DataLength = 0;
	uint8_t* DataAddress = NULL;

	DataLength = sizeof(data) - 0x2c;
	DataAddress = (unsigned char *)(data + 0x2c);
	memset(Buffer0, 0, BUFFER_SIZE);
	memset(Buffer1, 0, BUFFER_SIZE);
	HAL_TIM_Base_Start(&htim6);

	while(1)
	{
		if(DataLength >= BUFFER_SIZE)
		{
			memcpy(Buffer0, DataAddress, BUFFER_SIZE);
			DataLength -= BUFFER_SIZE;
			DataAddress += BUFFER_SIZE;
			HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)Buffer0, BUFFER_SIZE, DAC_ALIGN_8B_R);
			while(HAL_DAC_GetState(&hdac) != HAL_DAC_STATE_READY);
		}
		else break;
	}
	HAL_TIM_Base_Stop(&htim6);
	HAL_DAC_Stop_DMA(&hdac, DAC_CHANNEL_1);
}

主函数:

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_DAC_Init();
  MX_TIM6_Init();
  MX_USART1_UART_Init();
  Music_Player();

  while (1)
  {
	  HAL_Delay(3000);
  }
}

STM32CubeIDE Audio播放音频,DAC + TIM + DMA

源码:DACAudio3.rar_stm32cubedacdma-其它文档类资源-优快云下载

效果演示B站链接:STM32CubeIDE Audio播放音频,DAC + TIM + DMA_哔哩哔哩_bilibili

  全篇完。

本人是一个嵌入式未入门小白,博客仅仅代表我个人主观见解,记录成长笔记。
笔记是以最简单的方式,只展示最核心的原理。
若有与 大神大大 见解有歧义,我绝对坚信 大神大大 见解是对的,我的是错的。
若无积分等无法下载源码,可加入QQ群657407920下载交流经验。感谢~!

 

 

<think>好的,我现在需要帮助用户解决如何在STM32 HAL库中通过SD卡播放音频并使用DAC输出的问题。首先,我需要回顾用户提供的引用内容,确保信息准确。用户引用了四个关于STM32DAC模块和HAL库的资料,这些将作为基础。 首先,用户的问题涉及两个主要部分:SD卡读取和DAC输出音频。我需要结合这两个部分来设计一个解决方案。根据引用[3],HAL库提供了硬件抽象层,方便跨STM32产品的移植性,这意味着代码结构可能较为统一。DAC模块的配置部分,引用[2]提到STM32F1有两个DAC,12位分辨率,而引用[4]指出DAC的参考电压范围是2.4-3.3V,这些是硬件相关的要点,需要注意。 接下来,我需要考虑SD卡的读取。虽然用户提供的引用中没有直接提到SD卡,但HAL库通常支持FatFS文件系统来管理SD卡文件。因此,可能需要使用FatFS来读取音频文件。音频文件格式方面,常见的如WAV格式容易解析,所以可以建议用户将音频转换为PCM格式的WAV文件,这样可以直接提取数据通过DAC输出。 然后是DAC配置。根据引用[1]和[4],需要配置DAC的时钟、引脚,并设置触发方式。如果音频需要定时更新,可能需要使用定时器触发DAC转换,确保采样率正确。引用[4]提到的参考电压范围也需要确认,确保输出电平符合音频设备的要求。 接着是主程序逻辑。需要初始化SD卡、文件系统、DAC和定时器。主循环中读取音频数据到缓冲区,并通过DMA或中断将数据传输到DACDMA方式更高效,可以减少CPU负担,但需要正确配置DMA通道。同时,需要注意音频的采样率,定时器的触发频率需要匹配,以避免声音失真。 可能遇到的问题包括SD卡读取速度不够导致数据流中断,或者DAC输出速度不匹配。这时,使用双缓冲区机制可能有助于缓解问题,即一个缓冲区填充数据时,另一个缓冲区被DAC使用。此外,音频数据的位数可能需要调整,因为DAC是12位,而音频文件可能是16位,需要进行适当的移位或缩放。 最后,用户可能需要具体的代码示例,包括初始化部分、SD卡读取、DAC配置和中断处理函数。需要确保代码正确使用HAL库函数,并处理好错误情况,如SD卡初始化失败或文件读取错误。同时,要提醒用户检查硬件连接,如SD卡接口和DAC输出引脚的配置是否正确。 总结步骤:配置SD卡和文件系统,初始化DAC和定时器,读取音频数据,定时触发DAC转换,处理数据传输。需要参考HAL库的文档,确保各模块正确初始化和协同工作。此外,测试时建议使用简单的音频文件,逐步调试各部分功能,确保每一步都正常工作后再整合。</think>在STM32 HAL库中通过SD卡播放音频并使用DAC输出,需要结合SD卡文件系统读取、音频数据解码和DAC定时输出三个核心步骤。以下是具体实现流程: --- ### **1. 硬件及软件准备** - **硬件要求**:STM32支持DAC的型号(如STM32F4/F7系列)、SD卡接口(SDIO或SPI)、音频文件(建议16位PCM WAV格式)[^2][^4] - **软件依赖**:STM32CubeMX配置工具、FatFS文件系统库、HAL库DAC模块 --- ### **2. STM32CubeMX配置** 1. **SD卡接口配置**: - 选择SDIO或SPI模式(根据硬件设计) - 启用DMA传输以提高读取效率 2. **DAC配置**: - 启用DAC通道(如DAC1_CH1) - 设置触发源为定时器(如TIM6),用于控制采样率 - 输出缓冲关闭以降低延迟[^4] 3. **定时器配置**: - 定时器频率计算公式: $$ \text{Timer频率} = \frac{\text{系统时钟}}{\text{Prescaler} \times (\text{Counter Period} + 1)} $$ 例如:44.1kHz采样率需设置Timer频率为44,100Hz --- ### **3. 代码实现步骤** #### **3.1 SD卡初始化与文件读取** ```c // 初始化SD卡和FatFS FATFS fs; FIL audio_file; uint8_t buffer[4096]; // 双缓冲区建议 if (f_mount(&fs, "", 1) != FR_OK) { Error_Handler(); } if (f_open(&audio_file, "audio.wav", FA_READ) != FR_OK) { Error_Handler(); } ``` #### **3.2 DAC与定时器初始化** ```c // 启动DAC HAL_DAC_Start(&hdac, DAC_CHANNEL_1); // 启动定时器触发 HAL_TIM_Base_Start(&htim6); ``` #### **3.3 音频数据处理** ```c // 定时器中断中更新DAC值 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { static uint32_t data_index = 0; if (data_index < buffer_size) { HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, buffer[data_index++]); } else { // 重新填充缓冲区 f_read(&audio_file, buffer, sizeof(buffer), &bytes_read); data_index = 0; } } ``` --- ### **4. 关键优化点** 1. **双缓冲技术**:使用两个缓冲区交替填充数据,避免音频断流 2. **DMA传输**:SD卡读取和DAC输出均可配置DMA,减少CPU占用率[^3] 3. **位宽匹配**:将16位音频数据右移4位适配12位DAC(或使用缩放算法) --- ### **5. 完整流程示意图** $$ \begin{array}{ccc} \text{SD卡} & \xrightarrow{\text{FatFS读取}} & \text{音频缓冲区} \\ & & \downarrow\text{定时器触发} \\ \text{DAC输出} & \xleftarrow{\text{HAL\_DAC\_SetValue}} & \text{PCM数据} \end{array} $$ ---
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值