-项目需求要用STM32驱动喇叭发出声音(是啥声音咱也不知道,可能是音乐,也可能是哔哔哔的声音)
在网上查了很多种方式整理一下思路,高手可以右上角X。
1.TIM PWM方式
2.DAC+放大器到喇叭
3.IIS+语音解码芯片

第一种方案已经实现,有时间再写一篇。第三种因为对成本有要求,而且对音质没有很强的需求,所以不使用。主要是记录一下第二种的方案。STM32F103使用TIM DMA DAC实现播放WAVE音乐。

参考《使用 STM32 微控制器系列中的 DAC生成音频和波形》此文档举例介绍了使用数模转换器 (DAC) 外设生成音频输出信号的过程。
STM32 DAC 模块是 12 位字转换器,带有两个支持立体声音频的输出通道。DAC 可用于多种音频应用中,例如:安全警报、蓝牙耳机、发声玩具、答录机、人机接口以及低成本的音乐播放器STM32 DAC 还可实现许多其他模拟用途,如模拟波形产生和控制工程。

实现架构

大致的架构和上图差不多,只是文档介绍的是从TF卡上或者外部存储设备读取数据,这里只是验证DAC功能(只要有音乐能够正常的流淌就可以),数据源就用自己找了一段8KHz_8bit_wav格式音频,直接转成C语言数组存在芯片内部flash。直接让DMA指向flash数据的地址。

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

WAVE格式的音频需要了解,百度。

1.准备想要播放的音频。我这里使用的是GoldWave.exe,随便找了个MP3文件通过软件截取一段(内部FLASH放不了太多的)转换成8bit 8K的wav格式文件。点击 效果 --重取样可改变速率。


PC端播放这个音频文件就是8K速率的了,同理要多少K,就转化能成多少K就是了。
2.用UltraEdit打开这个wav文件,前面的一段是固定格式。
CTRL+A 鼠标右键 选择 十六进制复制选定视图,新建文件,粘贴。


我们想要的只是中间的那一块内容。找到最下面那个字节,鼠标右键 选择范围 输入起始的行号和列号,确定就选中了整个我们需要的内容,有复制粘贴建立新文件。


3.用notepad++打开上面保存文件,编辑–列块编辑–插入文本 ,生成如下文件。
上面3个步骤准备好了我们要的音频文件。
下面是程序分解:
我的程序是基于火牛开发板ADC实验来做的,只看dac就好了
用耳机或者喇叭接到PA4脚就可以听到歌声了。

/******DAC寄存器地址声明*******/
#define DAC_DHR12R1    (u32)&(DAC->DHR12R1)   //DAC通道1输出地址
#define DAC_DHR12R2    (u32)&(DAC->DHR12R2)   //DAC通道2输出地址#define DAC_DHR8R1_Address      0x40007410 //用的这个#define DAC_DHR8R2_Address      0x4000741Cconst u8 WaveData[] = {0x52 ,0x49 ,0x46 ,0x46 ,0x88 ,0x2F ,0x01 ,0x00 ,0x57 ,0x41 ,0x56 ,0x45 ,0x66 ,0x6D ,0x74 ,0x20,0x10 ,0x00 ,0x00 ,0x00 ,0x01 ,0x00 ,0x01 ,0x00 ,0x40 ,0x1F ,0x00 ,0x00 ,0x40 ,0x1F ,0x00 ,0x00,0x01 ,0x00 ,0x08 ,0x00 ,0x64 ,0x61 ,0x74 ,0x61 ,0x0D ,0x2F ,0x01 ,0x00 ,0x7B ,0x86 ,0x88 ,0x7C,0x7D ,0x83 ,0x76 ,0x6E ,0x74 ,0x73 ,0x6A ,0x6F ,0x76 ,0x74 ,0x72 ,0x7A ,0x7A ,0x76 ,0x7C ,0x81,0x79 ,0x79 ,0x80 ,0x80 ,0x7E ,0x7B ,0x7E ,0x84 ,0x7A ,0x80 ,0x8F ,0x83 ,0x81 ,0x94 ,0x8C ,0x80,0x93 ,0x8C ,0x80 ,0x89 ,0x8C ,0x83 ,0x81 ,0x82 ,0x80 ,0x79 ,0x7D ,0x81 ,0x7C ,0x79 ,0x80 ,0x75,0x6F ,0x78 ,0x76 ,0x6C ,0x76 ,0x78 ,0x71 ,0x7F ,0x7D ,0x71 ,0x81 ,0x8C ,0x7E ,0x8C ,0x97 ,0x88,0x89 ,0x97 ,0x8D ,0x89 ,0x93 ,0x8E ,0x83 ,0x87 ,0x85 ,0x7D ,0x7C ,0x80 ,0x7E ,0x7E ,0x7F ,0x7E,0x7B ,0x79 ,0x7C ,0x7A ,0x74 ,0x77 ,0x79 ,0x6D ,0x73 ,0x75 ,0x73 ,0x76 ,0x75 ,0x7B ,0x85 ,0x81,0x86 ,0x98 ,0x91 ,0x90 ,0x9E ,0x9A ,0x90 ,0x98 ,0x9A ,0x8E ,0x90 ,0x94 ,0x86 ,0x80 ,0x84 ,0x83,0x80 ,0x83 ,0x81 ,0x7D ,0x78 ,0x79 ,0x75 ,0x73 ,0x73 ,0x73 ,0x73 ,0x74 ,0x70 ,0x6F ,0x79 ,0x77,0x78 ,0x89 ,0x8C ,0x81 ,0x91 ,0x9B ,0x8E ,0x96 ,0xA6 ,0x98 ,0x93 ,0x9C ,0x92 ,0x88 ,0x8B ,0x87,0x82 ,0x80 ,0x82 ,0x80 ,0x7B ,0x7E ,0x7F ,0x7B ,0x7B ,0x7F ,0x72 ,0x72 ,0x77 ,0x6E ,0x6A ,0x71,0x6A ,0x68 ,0x71 ,0x6D ,0x74 ,0x82 ,0x7D ,0x80 ,0x8F ,0x90 ,0x8B ,0x98 ,0x9C ,0x96 ,0x9D ,0xA0,........//太长了 没有都贴上来
}
void GPIO_Configuration(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO , ENABLE);                        /***  LED1 -> PD8 , LED2 -> PD9 , LED3 -> PD10 , LED4 -> PD11*/GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;       GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_4|GPIO_Pin_5; //选择引脚GPIO_Init(GPIOA, &GPIO_InitStructure);      //初始化
}
void WavResolve(void)
{RiffBlock.szRiffID[0] = WaveData[MusicPosite++];    // 'R'RiffBlock.szRiffID[1] = WaveData[MusicPosite++]; // 'I'RiffBlock.szRiffID[2] = WaveData[MusicPosite++]; // 'F'RiffBlock.szRiffID[3] = WaveData[MusicPosite++]; // 'F'RiffBlock.dwRiffSize = WaveData[MusicPosite++] + (WaveData[MusicPosite++] << 8) +(WaveData[MusicPosite++] << 16) + (WaveData[MusicPosite++] << 24);RiffBlock.szRiffFormat[0] = WaveData[MusicPosite++];    // 'W'RiffBlock.szRiffFormat[1] = WaveData[MusicPosite++]; // 'A'RiffBlock.szRiffFormat[2] = WaveData[MusicPosite++]; // 'V'RiffBlock.szRiffFormat[3] = WaveData[MusicPosite++]; // 'E'FmtBlock.szFmtID[0] = WaveData[MusicPosite++];       // 'f'FmtBlock.szFmtID[1] = WaveData[MusicPosite++];       // 'm'FmtBlock.szFmtID[2] = WaveData[MusicPosite++];       // 't'FmtBlock.szFmtID[3] = WaveData[MusicPosite++];       // ' 'FmtBlock.dwFmtSize = WaveData[MusicPosite++] + (WaveData[MusicPosite++] << 8) +(WaveData[MusicPosite++] << 16) + (WaveData[MusicPosite++] << 24);FmtBlock.wavFormat.wFormatTag = WaveData[MusicPosite++] + (WaveData[MusicPosite++] << 8);FmtBlock.wavFormat.wChannels = WaveData[MusicPosite++] + (WaveData[MusicPosite++] << 8);FmtBlock.wavFormat.dwSamplesPerSec = WaveData[MusicPosite++] + (WaveData[MusicPosite++] << 8) +(WaveData[MusicPosite++] << 16) + (WaveData[MusicPosite++] << 24);FmtBlock.wavFormat.dwAvgBytesPerSec = WaveData[MusicPosite++] + (WaveData[MusicPosite++] << 8) +(WaveData[MusicPosite++] << 16) + (WaveData[MusicPosite++] << 24);FmtBlock.wavFormat.wBlockAlign = WaveData[MusicPosite++] + (WaveData[MusicPosite++] << 8);FmtBlock.wavFormat.wBitsPerSample = WaveData[MusicPosite++] + (WaveData[MusicPosite++] << 8);if(WaveData[MusicPosite] != 'f')goto dt;FactBlock.szFactID[0] = WaveData[MusicPosite++];    // 'f'FactBlock.szFactID[1] = WaveData[MusicPosite++]; // 'a'FactBlock.szFactID[2] = WaveData[MusicPosite++]; // 'c'FactBlock.szFactID[3] = WaveData[MusicPosite++]; // 't'FactBlock.dwFactSize = WaveData[MusicPosite++] + (WaveData[MusicPosite++] << 8) +(WaveData[MusicPosite++] << 16) + (WaveData[MusicPosite++] << 24);MusicPosite +=FactBlock.dwFactSize;dt:   DataBlock.szDataID[0] = WaveData[MusicPosite++]; // 'd'DataBlock.szDataID[1] = WaveData[MusicPosite++]; // 'a'DataBlock.szDataID[2] = WaveData[MusicPosite++]; // 't'DataBlock.szDataID[3] = WaveData[MusicPosite++]; // 'a'DataBlock.dwDataSize = WaveData[MusicPosite++] + (WaveData[MusicPosite++] << 8) +(WaveData[MusicPosite++] << 16) + (WaveData[MusicPosite] << 24);}
void Wave_Play_Config(void)
{DMA_InitTypeDef            DMA_InitStructure;TIM_TimeBaseInitTypeDef    TIM_TimeBaseStructure;DAC_InitTypeDef            DAC_InitStructure;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);//开启DMA2时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);//开时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);//开DAC时钟#if 1TIM_DeInit(TIM6);TIM_TimeBaseStructure.TIM_Period = 72000000/FmtBlock.wavFormat.dwSamplesPerSec;                      TIM_TimeBaseStructure.TIM_Prescaler = 0;                  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;         TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);TIM_ClearFlag(TIM6, TIM_FLAG_Update);                                TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update);//设置TIME输出触发为更新模式  #endifDAC_StructInit(&DAC_InitStructure);    DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;//不产生波形DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable; //不使能输出缓存 驱动增大DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO;//DAC触发为定时器6触发DAC_Init(DAC_Channel_1, &DAC_InitStructure);//初始化#if 1DMA_StructInit( &DMA_InitStructure);        //DMA结构体初始化DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//从内存到外设DMA_InitStructure.DMA_BufferSize = 6213440;//;//47857;//66056;//47857;//2484;//寄存器大小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_Priority = DMA_Priority_VeryHigh;//优先级非常高DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//关闭内存到内存模式DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//循环发送模式DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR8R1_Address;//外设地址为DAC通道1的地址DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&WaveData+44;//波形数据表内存地址DMA_Init(DMA2_Channel3, &DMA_InitStructure);//初始化DMA_Cmd(DMA2_Channel3, ENABLE); //使能DMA通道3 #endifDAC_DMACmd(DAC_Channel_1, ENABLE); //使能DAC通道1的DMADAC_Cmd(DAC_Channel_1, ENABLE);    //使能DAC的通道1TIM_Cmd(TIM6, ENABLE); 
}int main(void)
{RCC_Config();#if 1GPIO_Configuration();USART_Configuration();WavResolve();Wave_Play_Config();#endif//DAC_OUTv();//Dac1_Set_Vol(1500);delay_init();#if 0printf("\r\n");printf("*******************************************************************\r\n");printf("*                                                                 *\r\n");printf("*   Thank you for using HY-FireBull V1.0 Development Board !^_^  *\r\n");printf("*                                                                 *\r\n");printf("*******************************************************************\r\n");#endif/* Infinite loop */while (1){#if 0/* Printf message with AD value to serial port every 1 second */AD_value = ADC_ConvertedValue;AD_value = (AD_value/4096)*3.3;printf("The current AD value = %4.2fV \r\n", AD_value);/*====LED-ON=======*/GPIO_SetBits(GPIOD , GPIO_Pin_8);GPIO_SetBits(GPIOD , GPIO_Pin_9);GPIO_SetBits(GPIOD , GPIO_Pin_10);GPIO_SetBits(GPIOD , GPIO_Pin_11);delay_ms(500);   /* delay 500ms *//*====LED-OFF=======*/ GPIO_ResetBits(GPIOD , GPIO_Pin_8);GPIO_ResetBits(GPIOD , GPIO_Pin_9);GPIO_ResetBits(GPIOD , GPIO_Pin_10);GPIO_ResetBits(GPIOD , GPIO_Pin_11);delay_ms(500);   /* delay 500ms */  #endif//Dac1_Set_Vol(3300);}}

最后放一个波形图,我也不知道这个波形好不好,有什么问题,程序里面还有个DMAbuffer 这个值我设置成总数-44,声音不能够播放的完整,只播放前面的一部分。我还没有找到原因。还有利用DAC1 DAC2双通道解码,声音的清晰度等一些问题还没有去试,有时间再试。

这篇文章很有用,值得借鉴

本人是一个嵌入式未入门小白,博客仅仅代表我个人主观见解方便记录成长笔记。 若有与大神大大见解有冲突,我坚信大神大大见解是对的,我的是错的。如有指教或需要代码请联系QQ:420795936 感谢~!

STM32F103使用TIM DMA DAC实现播放WAV音乐相关推荐

  1. arduino下载库出错_【arduino】DIY音乐播放器,arduino播放wav音乐,TRMpcm库测试及使用...

    微信关注 "DLGG创客DIY"设为"星标",重磅干货,第一时间送达. arduino特点库超多,想必大家都领教了,今天来分享一下之前玩过的TRMpcm库. 这 ...

  2. 【arduino】DIY音乐播放器,arduino播放wav音乐,TRMpcm库测试及使用

    微信关注 "DLGG创客DIY" 设为"星标",重磅干货,第一时间送达. arduino特点库超多,想必大家都领教了,今天来分享一下之前玩过的TRMpcm库. ...

  3. 【arduino】DIY音乐播放器,arduino音箱播放wav音乐

    目标:给儿子做一个音乐播放器玩具. 材料: Arduino USB供电音箱 Micro SD卡模块 SD卡 3.5mm音频座模块 模块连接: 测试程序: 步骤: 将音乐转换成对应格式(MP3文件转换成 ...

  4. java wav文件_java播放wav文件

    STM32播放WAV程序_计算机软件及应用_IT/计算机_专业资料.此程序配置的采样率为16k,PWM频率144k; 此代码可以实现16位单声道的WAV文件解码,通过PWM 或者DAC输出音频.... ...

  5. android java 打开snd,使用SndPlaySound从内存中播放WAV

    有些时候,我们写的程序需要播放音乐,一般大家会采用以下几种方式:1.把音乐文件放置在某个特定的目录,例如Sound,WAV,Music供程序读取播放:2.使用资源来存放音乐.有些人估计也是使用这个方法 ...

  6. 使用pyaudio播放无损音乐(wav)

    安装pyaudio sudo apt-get install python-pyaudio python3-pyaudio pip3 install pyaudio 执行第二步可能会遇到如下错误: 根 ...

  7. STM32F767 音乐播放器 SAI DMA双缓冲 可播放WAV、MP3、FLAC文件

    以下为在单缓冲程序基础上修改 /*** Enable DMA controller clock*/ static void MX_DMA_Init(void) {/* DMA controller c ...

  8. STM32cubemx教程 DAC+TIM+DMA

    DAC+TIM+DMA 前言 本文主要讲解定时器触发DAC去输出任意信号,DMA负责数据数据搬移. 所需工具: 开发板:STM32F103RCT6 STM32CubeMX IDE: Keil-MDK ...

  9. stm32 pwm播放wav格式音乐移植遇到的问题

    前言 初识pwm的时候就想做音乐播放器,奈何51单片机速度慢,现在入手stm32,可玩性一下提高了很多,重拾当年挖的坑.既然要造轮子,那肯定先看看大牛是怎么做的.传送门:https://github. ...

  10. 基于STM32的flash读写和DAC音频播放

    一.STM32的flash读写 1.利用STM32CUBEMX创建工程 之前有过很多次创建工程的例子,这里大致过程如下: 2.keil中添加代码及修改配置 在flash.c中添加如下代码, 在main ...

最新文章

  1. NFS v3 RPC
  2. Spring和SpringMVC的总结
  3. 《系统集成项目管理工程师》必背100个知识点-20项目管理计划的主要用途
  4. adcsr图像超分代码_图像超分:RealSR
  5. c++ primer plus中文版_如何成为一名厉害的C/C++程序员?
  6. 机器人局部避障的动态窗口法(dynamic window approach)
  7. UI设计实用干货素材|引导页模板
  8. c语言自定义double函数例子,C语言的那些事——sqrt()函数 跟 double型数据的“%lf”...
  9. CSS层叠样式表的理解(一)
  10. 数据通信基础(面试必备)
  11. android音频格式,适用于iOS和Android的音频格式
  12. 高数_第1章空间解析几何与向量代数__直线方程
  13. java读取文件是乱码_java读取txt文件乱码解决方案
  14. 关于DE405星历表的调用
  15. matlab如何将二进制转换为十进制,matlab – 如何在不使用循环的情况下将二进制转换为十进制?...
  16. java具名参数_Spring框架笔记(二十五)——NamedParameterJdbcTemplate与具名参数
  17. Android一键清理原理
  18. 学习新的计算机语言的方法
  19. sift特征检测与匹配
  20. C# 调用 速印标签标准版 的模板lbl文件

热门文章

  1. excel制作表格的详细步骤
  2. 魔兽争霸——《冰封王座》2007魔兽比赛背景音乐下载
  3. 紫薇在线排盘php源码,灵匣网紫微斗数在线排盘系统
  4. DataGridView 获取当前行数据
  5. 【SD自动记账】SD与FI的集成部分
  6. [NOI2015]小园丁与老司机,洛谷P2304,恶心的建图+有源汇上下界最小流
  7. Python 交通仿真建模(1)
  8. 【FPGA】组合逻辑 3-8译码器实现
  9. matlab 开启并行,Matlab并行(持续更新)
  10. QQ浏览器 不支持html5,浏览器常见问题_浏览器高级功能_浏览器在线帮助-QQ浏览器官网...