stm32 DAC输出音频
#define DAC_DHR8R1_Address 0x40007410 // Init Structure definition DAC_InitTypeDef DAC_InitStructure; DMA_InitTypeDef DMA_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; void RCC_Configuration(void); void GPIO_Configuration(void); void NVIC_Configuration(void); u16 GetARRValue(u16 sample); //rcc 配置时钟频率 void RCC_Configuration(void) { /* RCC system reset(for debug purpose) */ RCC_DeInit(); /* Enable HSE */ RCC_HSEConfig(RCC_HSE_ON); /* Wait till HSE is ready */ HSEStartUpStatus = RCC_WaitForHSEStartUp(); if(HSEStartUpStatus == SUCCESS) { /* Enable Prefetch Buffer */ FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); /* Flash 2 wait state */ FLASH_SetLatency(FLASH_Latency_2); /* HCLK = SYSCLK */ RCC_HCLKConfig(RCC_SYSCLK_Div1); /* PCLK2 = HCLK */ RCC_PCLK2Config(RCC_HCLK_Div1); /* PCLK1 = HCLK/2 */ RCC_PCLK1Config(RCC_HCLK_Div2);//36MHz /* PLLCLK = 8MHz * 9 = 72 MHz */ RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); /* Enable PLL */ RCC_PLLCmd(ENABLE); /* Wait till PLL is ready */ while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) { } /* Select PLL as system clock source */ RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); /* Wait till PLL is used as system clock source */ while(RCC_GetSYSCLKSource() != 0x08) { } } // Enable peripheral clocks -------------------------------------------------- //dma dac sinewave // DMA clock enable RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // AFIO and GPIOA Periph clock enable RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA, ENABLE); // DAC Periph clock enable RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC | RCC_APB1Periph_TIM6, ENABLE); // TIM8 Periph clock enable RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE); } void GPIO_Configuration(void) { //init gpio GPIO_InitTypeDef GPIO_InitStructure; //dma dac sinewave GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4| GPIO_Pin_5; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//DAC输出 必须要设置成 模拟输入 GPIO_Init(GPIOA, &GPIO_InitStructure); } int main(void) { RCC_Configuration(); NVIC_Configuration(); GPIO_Configuration(); //DAC output SineWave (TIM8) /* //1>.This example describes how to use DAC dual channel mode with DMA to generate sine //waves on both DAC channels outputs. TIM_DeInit(TIM8); // TIM8 Configuration // Time base configuration TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); TIM_TimeBaseStructure.TIM_Period = GetARRValue(44100);//72MHz 的CPU,输出44.1KHz 的音频,设置周期为1633 TIM_TimeBaseStructure.TIM_Prescaler = 0;//分频 TIM_TimeBaseStructure.TIM_ClockDivision = 0x0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure); // TIM8 TRGO selection TIM_SelectOutputTrigger(TIM8, TIM_TRGOSource_Update); // DAC channel1 Configuration DAC_InitStructure.DAC_Trigger = DAC_Trigger_T8_TRGO; DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None; DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable; DAC_Init(DAC_Channel_1, &DAC_InitStructure); // DAC channel2 Configuration DAC_Init(DAC_Channel_2, &DAC_InitStructure); #if 1 // DMA2 channel4 configuration DMA_DeInit(DMA2_Channel4); #else // DMA1 channel4 configuration DMA_DeInit(DMA1_Channel4); #endif DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR8R1_Address;//DAC_DHR12RD_Address;// DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&DualSine12bit; //方向:外设是目的地,还是来源 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //DMA_DIR_PeripheralDST 外设是目的地 //DMA_DIR_PeripheralSRC 外设是来源 DMA_InitStructure.DMA_BufferSize = 512;// DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //DMA_PeripheralInc_Enable 外设地址寄存器递增 //DMA_PeripheralInc_Disable 外设地址寄存器不变 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //DMA_MemoryInc_Enable 内存地址寄存器递增 //DMA_MemoryInc_Disable 内存地址寄存器不变 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设数据宽度 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//内存数据宽度 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//设置CAN SPI 的DMA 模式 //DMA_Mode_Circular 工作在循环缓存模式 //DMA_Mode_Normal 工作在正常缓存模式 DMA_InitStructure.DMA_Priority = DMA_Priority_High;//DMA 通道优先级 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //enable 内存到内存输出 //disable 内存到内存输出 #if 1 DMA_Init(DMA2_Channel4, &DMA_InitStructure); //Enable DMA2 Channel4 DMA_Cmd(DMA2_Channel4, ENABLE); #else DMA_Init(DMA1_Channel4, &DMA_InitStructure); // Enable DMA1 Channel4 DMA_Cmd(DMA1_Channel4, ENABLE); #endif // Enable DAC Channel1 DAC_Cmd(DAC_Channel_1, ENABLE); // Enable DAC Channel2 DAC_Cmd(DAC_Channel_2, ENABLE); // Enable DMA for DAC Channel2 DAC_DMACmd(DAC_Channel_2, ENABLE); // TIM8 enable counter TIM_Cmd(TIM8, ENABLE); */ //2>.DAC output SineWave (TIM) single sinewave 单通道 TIM_DeInit(TIM6); /* TIM6 Configuration */ TIM_PrescalerConfig(TIM6, 0x0, TIM_PSCReloadMode_Update); TIM_SetAutoreload(TIM6, 1633); /* TIM6 TRGO selection */ TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update); /* DAC channel1 Configuration */ DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO; DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None; DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable; DAC_Init(DAC_Channel_1, &DAC_InitStructure); #if 1 /* DMA2 channel3 configuration */ DMA_DeInit(DMA2_Channel3); #else /* DMA1 channel3 configuration */ DMA_DeInit(DMA1_Channel3); #endif DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR8R1_Address; DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&thx; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = 20222; 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_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; #if 1 DMA_Init(DMA2_Channel3, &DMA_InitStructure); /* Enable DMA2 Channel3 */ DMA_Cmd(DMA2_Channel3, ENABLE); #else DMA_Init(DMA1_Channel3, &DMA_InitStructure); /* Enable DMA1 Channel3 */ DMA_Cmd(DMA1_Channel3, ENABLE); #endif // Enable DAC Channel1 DAC_Cmd(DAC_Channel_1, ENABLE); DAC_DMACmd(DAC_Channel_1, ENABLE); // TIM8 enable counter TIM_Cmd(TIM6, ENABLE); while(1); } // 根据采样率获得定时器自动 // 摘自waveplayer.c u16 GetARRValue(u16 sample) { u16 arrValue; //更新OCA值以符合.WAV文件采样率 switch (sample) { case SAMPLE_RATE_8000 : arrValue = (u16)(72000000/8000); break; // 8KHz = 2x36MHz / 9000 case SAMPLE_RATE_11025: arrValue = (u16)(72000000/11025); break; // 11.025KHz = 2x36MHz / 6531 case SAMPLE_RATE_16000: arrValue = (u16)(72000000/16000); break; // 16KHz = 2x36MHz / 4500 case SAMPLE_RATE_22050: arrValue = (u16)(72000000/22050); break; // 22.05KHz = 2x36MHz / 2365 case SAMPLE_RATE_44100: arrValue = (u16)(72000000/44100); break; // 44.1KHz = 2x36MHz / 1633 case SAMPLE_RATE_48000: arrValue = (u16)(72000000/48000); break; // 48KHz = 2x36MHz / 1500 default: arrValue = 0; break; } return arrValue; }
上面是 双通道,单通道的DAC 音频输出
需要注意的是数据,一定要是wave的格式, 下面就有检测wave格式是否正确的代码
/** @defgroup WAVEPLAYER_Private_Defines * @{ */ #define CHUNK_ID 0x52494646 /* correspond to the letters 'RIFF' */ #define FILE_FORMAT 0x57415645 /* correspond to the letters 'WAVE' */ #define FORMAT_ID 0x666D7420 /* correspond to the letters 'fmt ' */ #define DATA_ID 0x64617461 /* correspond to the letters 'data' */ #define FACT_ID 0x66616374 /* correspond to the letters 'fact' */ #define WAVE_FORMAT_PCM 0x01 #define FORMAT_CHNUK_SIZE 0x10 #define CHANNEL_MONO 0x01 #define SAMPLE_RATE_8000 8000 #define SAMPLE_RATE_11025 11025 #define SAMPLE_RATE_22050 22050 #define SAMPLE_RATE_44100 44100 #define BITS_PER_SAMPLE_8 8 #define WAVE_DUMMY_BYTE 0xA5 #define DAC_DHLCD_REG_8LCD_REG_1_ADDRESS 0x40007410 static ErrorCode WavePlayer_WaveParsing() { uint32_t Temp = 0x00; uint32_t ExtraFormatBytes = 0; __IO uint32_t err = 0; memcpy(Wavebuffer,0,20250-1);//在这里固定了数组,实际中再用其他的buffer memcpy(Wavebuffer,thx,20222); // Read chunkID, must be 'RIFF' ---------------------------------------------- Temp = ReadUnit(Wavebuffer, 0, 4, BigEndian); if (Temp != CHUNK_ID) { return(Unvalid_RIFF_ID); } // Read the file length ---------------------------------------------------- WAVE_Format.RIFFchunksize = ReadUnit(Wavebuffer, 4, 4, LittleEndian); // Read the file format, must be 'WAVE' ------------------------------------ Temp = ReadUnit(Wavebuffer, 8, 4, BigEndian); if (Temp != FILE_FORMAT) { return(Unvalid_WAVE_Format); } // Read the format chunk, must be'fmt ' -------------------------------------- Temp = ReadUnit(Wavebuffer, 12, 4, BigEndian); if (Temp != FORMAT_ID) { return(Unvalid_FormatChunk_ID); } // Read the length of the 'fmt' data, must be 0x10 ------------------------- Temp = ReadUnit(Wavebuffer, 16, 4, LittleEndian); if (Temp != 0x10) { ExtraFormatBytes = 1; } // Read the audio format, must be 0x01 (PCM) ------------------------------- WAVE_Format.FormatTag = ReadUnit(Wavebuffer, 20, 2, LittleEndian); if (WAVE_Format.FormatTag != WAVE_FORMAT_PCM) { return(Unsupporetd_FormatTag); } // Read the number of channels, must be 0x01 (Mono) ------------------------ WAVE_Format.NumChannels = ReadUnit(Wavebuffer, 22, 2, LittleEndian); if (WAVE_Format.NumChannels != CHANNEL_MONO) { return(Unsupporetd_Number_Of_Channel); } // Read the Sample Rate ---------------------------------------------------- WAVE_Format.SampleRate = ReadUnit(Wavebuffer, 24, 4, LittleEndian); // Update the OCA value according to the .WAV file Sample Rate switch (WAVE_Format.SampleRate) { case SAMPLE_RATE_8000 : TIM6ARRValue = 4500; break; // 8KHz = 36MHz / 4500 case SAMPLE_RATE_11025: TIM6ARRValue = 3265; break; // 11.025KHz = 36MHz / 3265 case SAMPLE_RATE_22050: TIM6ARRValue = 1632; break; // 22.05KHz = 36MHz / 1632 case SAMPLE_RATE_44100: TIM6ARRValue = 816; break; // 44.1KHz = 36MHz / 816 default: return(Unsupporetd_Sample_Rate); } // Read the Byte Rate ------------------------------------------------------ WAVE_Format.ByteRate = ReadUnit(Wavebuffer, 28, 4, LittleEndian); // Read the block alignment ------------------------------------------------ WAVE_Format.BlockAlign = ReadUnit(Wavebuffer, 32, 2, LittleEndian); // Read the number of bits per sample -------------------------------------- WAVE_Format.BitsPerSample = ReadUnit(Wavebuffer, 34, 2, LittleEndian); if (WAVE_Format.BitsPerSample != BITS_PER_SAMPLE_8) { return(Unsupporetd_Bits_Per_Sample); } SpeechDataOffset = 36; // If there is Extra format bytes, these bytes will be defined in "Fact Chunk" if (ExtraFormatBytes == 1) { // Read th Extra format bytes, must be 0x00 ------------------------------ Temp = ReadUnit(Wavebuffer, 36, 2, LittleEndian); if (Temp != 0x00) { return(Unsupporetd_ExtraFormatBytes); } // Read the Fact chunk, must be 'fact' ----------------------------------- Temp = ReadUnit(Wavebuffer, 38, 4, BigEndian); if (Temp != FACT_ID) { return(Unvalid_FactChunk_ID); } // Read Fact chunk data Size --------------------------------------------- Temp = ReadUnit(Wavebuffer, 42, 4, LittleEndian); SpeechDataOffset += 10 + Temp; } // Read the Data chunk, must be 'data' --------------------------------------- Temp = ReadUnit(Wavebuffer, SpeechDataOffset, 4, BigEndian); SpeechDataOffset += 4; if (Temp != DATA_ID) { return(Unvalid_DataChunk_ID); } // Read the number of sample data ------------------------------------------ WAVE_Format.DataSize = ReadUnit(Wavebuffer, SpeechDataOffset, 4, LittleEndian); SpeechDataOffset += 4; wavecounter = SpeechDataOffset; return(Valid_WAVE_File); }
在处理wave 音频数据的时候
实际数据20222 byte
RIFFchunksize 是20214
Read the number of sample data 20108 (SpeechDataOffset为44 )
前面offset 是44, 而总sample data 是20108,
20222 -44 -20108 = 70
70就是不要的数据尾, 为了DAC 不发出杂音(爆音是接近 0或255的曲线, 128是中音)
实际处理数据的时候 前offset +44, 后offset -(114 -44) 就可以了
这样就没有爆音了,哈哈
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
2014-06-17 linux cross toolsChain 交叉编译 ARM(转)