STM32CubeIDE Audio播放音频,DAC + TIM + DMA https://blog.csdn.net/sudaroot/article/details/106337717
STM32CubeIDE Audio播放音频,PWM + TIM https://blog.csdn.net/sudaroot/article/details/106373388
STM32CbueIDE Audio播放音频 WM8978 + I2S https://blog.csdn.net/sudaroot/article/details/106528371
STM32CubeIDE Audio播放音频 WM8978 + I2S + DMA双缓存模式 https://blog.csdn.net/sudaroot/article/details/106660351
STM32CubeIDE USB Audio声卡 WM8978 + I2S https://blog.csdn.net/sudaroot/article/details/106834893
文档编号 AN3126 网址:https://www.stmcu.org.cn/document/detail/index/id-200187
例程编号 UM0891 网址:https://www.stmcu.org.cn/document/detail/index/id-200294
/**
* @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。
首先配置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);
}
}
源码:https://download.csdn.net/download/sudaroot/12461656
效果演示B站链接:https://www.bilibili.com/video/BV1GZ4y1W76T/
全篇完。
本人是一个嵌入式未入门小白,博客仅仅代表我个人主观见解方便记录成长笔记。 若有与大神大大见解有冲突,我坚信大神大大见解是对的,我的是错的。 感谢~!