OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 14632|回复: 31

PWM 播放flash中WAV文件

[复制链接]

34

主题

283

帖子

0

精华

高级会员

Rank: 4

积分
739
金钱
739
注册时间
2015-11-15
在线时间
226 小时
发表于 2017-6-20 11:01:27 | 显示全部楼层 |阅读模式
10金钱
本帖最后由 xueshawu 于 2017-6-20 11:07 编辑

功能需求:
     利用TIM1_CH1的PWM,将flash中的WAV输出到喇叭,
我目前的做法,将8KHz,8位数据的WAV通过串口烧录到flash中,
然后,同时使能 TIM1的定时器中断与PWM功能;在定时器中断中更新PWM占空比数据;
因为要保证每8KHz,输出一次wav数据;如果只有TIM1,那在中断里面更新数据的同时,这个周期的PWM已经输出了,


TIM_TimeBaseInitTypeDef   TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef     TIM_OCInitStructure;
GPIO_InitTypeDef     GPIO_InitStructure;
NVIC_InitTypeDef     NVIC_InirStructure;

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //GPIOA.8
GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);

GPIO_PinAFConfig(GPIOA,GPIO_PinSource8,GPIO_AF_2);  //GPIO_AF_2

NVIC_InirStructure.NVIC_IRQChannel = TIM1_BRK_UP_TRG_COM_IRQn;
NVIC_InirStructure.NVIC_IRQChannelPriority = 3;
NVIC_InirStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InirStructure);

//TIM1 Config
//wav采样率 24KHz = 48000KHz/(999+1)(1+1);
//      16KHz = 48000KHz/(999+1)(2+1)
  //          11KHz  = 48000KHz/(2180+1)(1+1)
  //      8KHz  = 48000KHz/(999+1)(5+1)
TIM_TimeBaseInitStructure.TIM_ClockDivision  = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
//TIM_TimeBaseInitStructure.TIM_Period = 999;
// TIM_TimeBaseInitStructure.TIM_Prescaler = 2; //16K
// TIM_TimeBaseInitStructure.TIM_Period = 2180;
// TIM_TimeBaseInitStructure.TIM_Prescaler = 1;  //11K
TIM_TimeBaseInitStructure.TIM_Period = 999;
TIM_TimeBaseInitStructure.TIM_Prescaler = 5;    //8K
TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStructure);

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 500;  //测试


// TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
// TIM_TimeBaseInitStructure.TIM_Period =

TIM_OC1Init(TIM1,&TIM_OCInitStructure);
TIM_ITConfig(TIM1,TIM_IT_Update,ENABLE);
TIM_Cmd(TIM1,ENABLE);
TIM_CtrlPWMOutputs(TIM1,ENABLE);







void TIM1_BRK_UP_TRG_COM_IRQHandler(void)
{
uint8_t data;
uint16_t Speak_data;
//uint8_t mode =0;
float duty;
float  temp;
if(TIM_GetITStatus(TIM1,TIM_IT_Update)!=RESET) //跟新占空比数据
{
  //printf("\r\n 1");//tp
  W25QXX_Read(&data,t_addr,1);
  temp = data;
  duty = temp/255;
  Speak_data = duty*1000;
  TIM1->CCR1 = Speak_data;
  Speak_counter++;
  t_addr++;
  if(Speak_counter>15360)
  {
   //printf("\r\n 2");
   Speak_counter =0;
   t_addr = 0x000D0000;
  }
  TIM_ClearITPendingBit(TIM1,TIM_IT_Update);
}

}


突然想到,就算更新CCR1寄存器滞后了一个周期,应该没有影响的,只要按照固定 频率更新数据就行了;
但是,目前还是没有出来。得到的都是杂音,没有 清晰的语音出来


最佳答案

查看完整内容[请看2#楼]

其实最大的问题在于,我这里PWM的输出频率也是8K;然而,我的滤波电路的截止频率大概是33k;昨晚,我已经输出了正确的音频流;这里需要两个定时器,PWM输出I定时器,这个频率大于滤波电路的截止频率2倍,占空比更新定时器,定时8khz中断更新PWM占空比;大致就是这样
与其感慨路难行,不如马上出发
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

34

主题

283

帖子

0

精华

高级会员

Rank: 4

积分
739
金钱
739
注册时间
2015-11-15
在线时间
226 小时
 楼主| 发表于 2017-6-20 11:01:28 | 显示全部楼层
正点原子 发表于 2017-6-20 21:13
要根据你的输出精度来确定
你总共才8位,最高位还用来表示静音那就只有7位,最大是128
那你对应的音频数 ...

其实最大的问题在于,我这里PWM的输出频率也是8K;然而,我的滤波电路的截止频率大概是33k;昨晚,我已经输出了正确的音频流;这里需要两个定时器,PWM输出I定时器,这个频率大于滤波电路的截止频率2倍,占空比更新定时器,定时8khz中断更新PWM占空比;大致就是这样
与其感慨路难行,不如马上出发
回复

使用道具 举报

34

主题

283

帖子

0

精华

高级会员

Rank: 4

积分
739
金钱
739
注册时间
2015-11-15
在线时间
226 小时
 楼主| 发表于 2017-6-20 11:01:44 | 显示全部楼层
本帖最后由 xueshawu 于 2017-6-20 11:06 编辑

貌似字体有点大,下面是我滤波部分的图
1.png
与其感慨路难行,不如马上出发
回复

使用道具 举报

34

主题

283

帖子

0

精华

高级会员

Rank: 4

积分
739
金钱
739
注册时间
2015-11-15
在线时间
226 小时
 楼主| 发表于 2017-6-20 11:08:36 | 显示全部楼层
有做过 的大佬指点下么
与其感慨路难行,不如马上出发
回复

使用道具 举报

34

主题

283

帖子

0

精华

高级会员

Rank: 4

积分
739
金钱
739
注册时间
2015-11-15
在线时间
226 小时
 楼主| 发表于 2017-6-20 14:09:34 | 显示全部楼层
8位数据格式,0x80表示没有声音,那对应到电压,怎么计算,是0么?
与其感慨路难行,不如马上出发
回复

使用道具 举报

34

主题

283

帖子

0

精华

高级会员

Rank: 4

积分
739
金钱
739
注册时间
2015-11-15
在线时间
226 小时
 楼主| 发表于 2017-6-20 15:56:19 | 显示全部楼层
如果,0x80表示50%的占空比的话,喇叭却能输出声音,SOS
与其感慨路难行,不如马上出发
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165516
金钱
165516
注册时间
2010-12-1
在线时间
2116 小时
发表于 2017-6-20 21:13:01 | 显示全部楼层
要根据你的输出精度来确定
你总共才8位,最高位还用来表示静音那就只有7位,最大是128
那你对应的音频数据流,也只能是7位的,这个没人用。
一般至少也是8位,好一点16位。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复

使用道具 举报

2

主题

22

帖子

0

精华

初级会员

Rank: 2

积分
78
金钱
78
注册时间
2012-10-20
在线时间
7 小时
发表于 2017-7-24 18:07:18 | 显示全部楼层
可以分享一下你的代码吗?最近也在研究PWM播放wav
回复

使用道具 举报

34

主题

283

帖子

0

精华

高级会员

Rank: 4

积分
739
金钱
739
注册时间
2015-11-15
在线时间
226 小时
 楼主| 发表于 2017-7-24 19:13:27 | 显示全部楼层
pgf017979 发表于 2017-7-24 18:07
可以分享一下你的代码吗?最近也在研究PWM播放wav

可以,但是代码局限性很大,WAV 的格式必须是16KHZ,8位 单声道;并且WAV要转换成BIN文件
与其感慨路难行,不如马上出发
回复

使用道具 举报

34

主题

283

帖子

0

精华

高级会员

Rank: 4

积分
739
金钱
739
注册时间
2015-11-15
在线时间
226 小时
 楼主| 发表于 2017-7-24 19:15:50 | 显示全部楼层
pgf017979 发表于 2017-7-24 18:07
可以分享一下你的代码吗?最近也在研究PWM播放wav

驱动 代码在附件 里面,频率跟 你的滤波电路有关系,所以不一定要是这个配置

SPEAK.zip

2.78 KB, 下载次数: 970

与其感慨路难行,不如马上出发
回复

使用道具 举报

2

主题

22

帖子

0

精华

初级会员

Rank: 2

积分
78
金钱
78
注册时间
2012-10-20
在线时间
7 小时
发表于 2017-7-25 11:56:18 | 显示全部楼层
我也做出来了,开始声音很怪,后来发现我的wav文件数据有点不一样。读取的时候地址要+2.我用酷狗音乐播放器转的格式。Wav编码输出,8Bits。
我的是44100采样率,声音还是挺不错的。
回复

使用道具 举报

0

主题

7

帖子

0

精华

新手上路

积分
45
金钱
45
注册时间
2017-12-2
在线时间
3 小时
发表于 2017-12-2 21:02:25 | 显示全部楼层
您好,想请教一下代码中断函数里的
duty = temp/255;
  Speak_data = duty*1000;
这两句是什么意思,非常感谢!
回复

使用道具 举报

34

主题

283

帖子

0

精华

高级会员

Rank: 4

积分
739
金钱
739
注册时间
2015-11-15
在线时间
226 小时
 楼主| 发表于 2017-12-5 15:17:54 | 显示全部楼层
tso9002 发表于 2017-12-2 21:02
您好,想请教一下代码中断函数里的
duty = temp/255;
  Speak_data = duty*1000;

?你截图上来看看,我在代码里面没看到这个
与其感慨路难行,不如马上出发
回复

使用道具 举报

0

主题

3

帖子

0

精华

初级会员

Rank: 2

积分
81
金钱
81
注册时间
2013-11-29
在线时间
14 小时
发表于 2017-12-5 17:34:45 | 显示全部楼层
我有不明白的:1,怎么把声音文件烧录flash中。2,怎么做到随意播放flash中任意声音文件。请指导下,谢谢
回复

使用道具 举报

0

主题

7

帖子

0

精华

新手上路

积分
45
金钱
45
注册时间
2017-12-2
在线时间
3 小时
发表于 2017-12-13 19:34:33 | 显示全部楼层
xueshawu 发表于 2017-12-5 15:17
?你截图上来看看,我在代码里面没看到这个

已经明白啦,谢谢回复
回复

使用道具 举报

34

主题

283

帖子

0

精华

高级会员

Rank: 4

积分
739
金钱
739
注册时间
2015-11-15
在线时间
226 小时
 楼主| 发表于 2017-12-14 08:45:17 | 显示全部楼层
妞叫我改么 发表于 2017-12-5 17:34
我有不明白的:1,怎么把声音文件烧录flash中。2,怎么做到随意播放flash中任意声音文件。请指导下,谢谢

1: 可以通过flash烧录器烧录进去,或者通过串口助手发送给MCU,MCU写入到flash。
2:你在烧录到flash中,需要确定每一个语音文件的地址和大小,通过地址播放任意文件,或者通过文件系统fafts 通过文件名访问
与其感慨路难行,不如马上出发
回复

使用道具 举报

5

主题

34

帖子

0

精华

初级会员

Rank: 2

积分
107
金钱
107
注册时间
2014-3-24
在线时间
19 小时
发表于 2017-12-26 10:57:01 | 显示全部楼层
pgf017979 发表于 2017-7-25 11:56
我也做出来了,开始声音很怪,后来发现我的wav文件数据有点不一样。读取的时候地址要+2.我用酷狗音乐播放器 ...

播放过程中有“咦”的声音吗?我播放的时候有个持续的“咦”的声音
回复

使用道具 举报

4

主题

30

帖子

0

精华

初级会员

Rank: 2

积分
195
金钱
195
注册时间
2016-12-28
在线时间
50 小时
发表于 2017-12-26 14:40:06 | 显示全部楼层
楼主音效怎么样
回复

使用道具 举报

34

主题

283

帖子

0

精华

高级会员

Rank: 4

积分
739
金钱
739
注册时间
2015-11-15
在线时间
226 小时
 楼主| 发表于 2017-12-26 16:21:57 | 显示全部楼层
huangbo265419 发表于 2017-12-26 10:57
播放过程中有“咦”的声音吗?我播放的时候有个持续的“咦”的声音

没有,如果是播放开始有点破音只需要 在播放之前 TIM1->CCR1 =0x80;即可,0x80代表没有声音
与其感慨路难行,不如马上出发
回复

使用道具 举报

0

主题

3

帖子

0

精华

新手上路

积分
20
金钱
20
注册时间
2018-5-30
在线时间
3 小时
发表于 2018-5-30 17:20:30 | 显示全部楼层
你好,可以把你的wav文件和wav转的数据共享一下吗?
回复

使用道具 举报

0

主题

3

帖子

0

精华

新手上路

积分
20
金钱
20
注册时间
2018-5-30
在线时间
3 小时
发表于 2018-5-30 18:06:44 | 显示全部楼层
你好方便加你QQ吗?你发的<PWM 播放flash中WAV文件>很多地方不明白,想请教一下,我qq:511097180
回复

使用道具 举报

0

主题

3

帖子

0

精华

新手上路

积分
20
金钱
20
注册时间
2018-5-30
在线时间
3 小时
发表于 2018-6-5 23:23:35 | 显示全部楼层
你好,你共享的“SPEAK.zip”中“data=Voice_Adjust(data);”Voice_Adjust是什么?
回复

使用道具 举报

4

主题

98

帖子

0

精华

初级会员

Rank: 2

积分
195
金钱
195
注册时间
2018-6-27
在线时间
45 小时
发表于 2018-8-2 15:51:12 | 显示全部楼层
感谢,正在尝试输出声音
回复

使用道具 举报

0

主题

36

帖子

0

精华

初级会员

Rank: 2

积分
141
金钱
141
注册时间
2018-9-23
在线时间
21 小时
发表于 2019-8-17 18:03:38 | 显示全部楼层
pgf017979 发表于 2017-7-25 11:56
我也做出来了,开始声音很怪,后来发现我的wav文件数据有点不一样。读取的时候地址要+2.我用酷狗音乐播放器 ...

可以分享一下程序吗
回复

使用道具 举报

2

主题

46

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
208
金钱
208
注册时间
2016-1-26
在线时间
40 小时
发表于 2019-8-19 09:27:01 | 显示全部楼层
扣成本啊。。
回复

使用道具 举报

9

主题

40

帖子

0

精华

初级会员

Rank: 2

积分
93
金钱
93
注册时间
2019-5-24
在线时间
21 小时
发表于 2021-2-19 13:19:56 | 显示全部楼层
tso9002 发表于 2017-12-2 21:02
您好,想请教一下代码中断函数里的
duty = temp/255;
  Speak_data = duty*1000;

这些兄弟,这两句话的意思,麻烦说下,谢谢
回复

使用道具 举报

0

主题

8

帖子

0

精华

初级会员

Rank: 2

积分
52
金钱
52
注册时间
2014-10-17
在线时间
8 小时
发表于 2021-8-8 14:58:55 | 显示全部楼层
我也想知道WAV怎么生成bin文件烧录FLASH中
回复

使用道具 举报

2

主题

22

帖子

0

精华

初级会员

Rank: 2

积分
78
金钱
78
注册时间
2012-10-20
在线时间
7 小时
发表于 2022-1-10 12:08:54 | 显示全部楼层
最简单就是串口助手发送文件呀。
回复

使用道具 举报

2

主题

22

帖子

0

精华

初级会员

Rank: 2

积分
78
金钱
78
注册时间
2012-10-20
在线时间
7 小时
发表于 2022-1-10 12:11:55 | 显示全部楼层
这个项目好久以前做的。我找下,发出这部分代码。
  1.     case CMD_UPDATE_WAV:
  2.     {
  3.       uint32_t wav_addr = 184*4096;
  4.       uint32_t wav_size = 8*1024*1024 - wav_addr;
  5.       Serial.println("擦除中...");
  6.       flash.erasaMemory(wav_addr,wav_size);
  7.       Serial.println("擦除完毕");
  8.       Serial.println("wav更新,请发送文件...");
  9.       uint32_t ms  = millis();
  10.       while(1)//死循环执行
  11.       {
  12.         if(Serial.available())
  13.         {
  14.           flash.writeByte(wav_addr, Serial.read());
  15.           wav_addr++;
  16.           ms  = millis();
  17.         }
  18.         if(millis() - ms > 10000 )break;
  19.       }
  20.       Serial.println("更新完毕");
  21.       Serial.println(wav_addr);
  22.     }
复制代码
回复

使用道具 举报

2

主题

22

帖子

0

精华

初级会员

Rank: 2

积分
78
金钱
78
注册时间
2012-10-20
在线时间
7 小时
发表于 2022-1-10 12:17:27 | 显示全部楼层
wavplay.cpp文件
  1. #include "wavplay.h"
  2. #include <stdlib.h>
  3. #include "HardwareSerial.h"
  4. #include "SPIFlash.h"
  5. extern SPIFlash flash;

  6. uint32_t WAV_ADDR[2] =
  7. {
  8.   WAV_BASE + WAV1_OFFSET,
  9.   WAV_BASE + WAV2_OFFSET
  10. };

  11. WAV_CTR_t wavctrl;                //WAV控制结构体

  12. //WAV解析初始化
  13. //fname:文件路径+文件名
  14. //wavx:wav 信息存放结构体指针
  15. //返回值:0,成功;1,打开文件失败;2,非WAV文件;3,DATA区域未找到.
  16. uint8_t wav_decode_init(WAV_CTR_t *wavx , uint32_t addr)
  17. {
  18.   //        FIL*ftemp;
  19.   uint8_t *buf;
  20.   uint8_t res = 0;
  21.   ChunkRIFF *riff;
  22.   ChunkFMT *fmt;
  23.   ChunkFACT *fact;
  24.   ChunkDATA *data;
  25.   buf = (uint8_t *)malloc(64);
  26.   if(buf)        //内存申请成功
  27.   {
  28.     flash.readBytes(addr, buf, 64);
  29.     riff = (ChunkRIFF *)buf;                //获取RIFF块
  30.     if(riff->Format == 0X45564157) //是WAV文件
  31.     {
  32.       fmt = (ChunkFMT *)(buf + 12);        //获取FMT块
  33.       fact = (ChunkFACT *)(buf + 12 + 8 + fmt->ChunkSize); //读取FACT块
  34.       if(fact->ChunkID == 0X74636166 || fact->ChunkID == 0X5453494C)wavx->datastart = 12 + 8 + fmt->ChunkSize + 8 + fact->ChunkSize; //具有fact/LIST块的时候(未测试)
  35.       else wavx->datastart = 12 + 8 + fmt->ChunkSize;
  36.       data = (ChunkDATA *)(buf + wavx->datastart);        //读取DATA块
  37.       if(data->ChunkID == 0X61746164) //解析成功!
  38.       {
  39.         wavx->audioformat = fmt->AudioFormat;                //音频格式
  40.         wavx->nchannels = fmt->NumOfChannels;                //通道数
  41.         wavx->samplerate = fmt->SampleRate;                //采样率
  42.         wavx->bitrate = fmt->ByteRate * 8;                        //得到位速
  43.         wavx->blockalign = fmt->BlockAlign;                //块对齐
  44.         wavx->bps = fmt->BitsPerSample;                        //位数,16/24/32位
  45.         wavx->datasize = data->ChunkSize;                        //数据块大小
  46.         wavx->datastart = wavx->datastart + 8;                //数据流开始的地方.
  47.         wavx->addr = addr;
  48.         wavx->data_offset = 0;
  49.         Serial.printf("wavx->audioformat:%d\r\n", wavx->audioformat);
  50.         Serial.printf("wavx->nchannels:%d\r\n", wavx->nchannels);
  51.         Serial.printf("wavx->samplerate:%d\r\n", wavx->samplerate);
  52.         Serial.printf("wavx->bitrate:%d\r\n", wavx->bitrate);
  53.         Serial.printf("wavx->blockalign:%d\r\n", wavx->blockalign);
  54.         Serial.printf("wavx->bps:%d\r\n", wavx->bps);
  55.         Serial.printf("wavx->datasize:%d\r\n", wavx->datasize);
  56.         Serial.printf("wavx->datastart:%d\r\n", wavx->datastart);
  57.         Serial.printf("wavx->addr:%d\r\n", wavx->addr);
  58.       }
  59.       else res = 3; //data区域未找到.
  60.     }
  61.     else res = 2; //非wav文件
  62.   }
  63.   free(buf);
  64.   return res;
  65. }

  66. void Speaker_Config()
  67. {
  68. #define WAV_SAMPLERATE 12000
  69.   NVIC_InitTypeDef NVIC_InitStructure;
  70.   GPIO_InitTypeDef GPIO_InitStructure;
  71.   TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  72.   TIM_OCInitTypeDef  TIM_OCInitStructure;
  73.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM16, ENABLE);
  74.   RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14, ENABLE); //使能TIMx外设
  75.   RCC_AHBPeriphClockCmd( RCC_AHBPeriph_GPIOB, ENABLE);
  76.   //设置该引脚为复用输出功能,输出TIM4 CH1的PWM脉冲波形
  77.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //TIM_CH1
  78.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;  //复用功能输出
  79.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  80.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  81.   GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化GPIO
  82.   TIM_TimeBaseStructure.TIM_Period = 0xFF; //设置自动重装载周期值
  83.   TIM_TimeBaseStructure.TIM_Prescaler = 0; //设置预分频值 不分频
  84.   TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
  85.   TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
  86.   TIM_TimeBaseInit(TIM14, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx
  87.   TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //CH1 PWM2模式
  88.   TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
  89.   TIM_OCInitStructure.TIM_Pulse = 0x80; //设置待装入捕获比较寄存器的脉冲值
  90.   TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //OC1 低电平有效
  91.   TIM_OC1Init(TIM14, &TIM_OCInitStructure);  //根据指定的参数初始化外设TIMx
  92.   TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable);  //CH1 预装载使能
  93.   TIM_ARRPreloadConfig(TIM14, ENABLE); //使能TIMx在ARR上的预装载寄存器
  94.   TIM_Cmd(TIM14, ENABLE);  //使能TIMx
  95.   
  96.   
  97.   
  98.   
  99.   
  100.   TIM_TimeBaseStructure.TIM_Period = (uint16_t)(SystemCoreClock / WAV_SAMPLERATE - 1);         //设定计数器自动重装值3273 中断频率=72M/3273=22Khz
  101.   TIM_TimeBaseStructure.TIM_Prescaler = 0; //预分频器为0.
  102.   TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
  103.   TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
  104.   TIM_TimeBaseInit(TIM16, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
  105.   TIM_ITConfig(TIM16, TIM_IT_Update, DISABLE ); //使能或者失能指定的TIM中断
  106.   TIM_Cmd(TIM16, ENABLE);  //使能TIMx外设
  107.   //NVIC中断配置
  108.   NVIC_InitStructure.NVIC_IRQChannel = TIM16_IRQn;        //组2,优先级次之
  109.   NVIC_InitStructure.NVIC_IRQChannelPriority = 1;
  110.   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  111.   NVIC_Init(&NVIC_InitStructure);
  112. }

  113. //uint32_t Out_Data_Offset = 0;
  114. extern "C" void TIM16_IRQHandler(void)
  115. {
  116.   uint32_t addr;
  117.   if (TIM_GetITStatus(TIM16, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源
  118.   {
  119.     if(wavctrl.status == 0)
  120.     {
  121.       //停止
  122.       TIM14->CCR1 = 0x80;
  123.       wavctrl.data_offset = 0;
  124.       TIM_ITConfig(TIM16, TIM_IT_Update, DISABLE ); //使能或者失能指定的TIM中断
  125.     }
  126.     else if(wavctrl.status == 1)
  127.     {
  128.       //播放
  129.       if(wavctrl.data_offset < wavctrl.datasize)
  130.       {
  131.         addr = wavctrl.addr + wavctrl.datastart + wavctrl.data_offset;//Out_Data_Offset;
  132.         uint8_t data = flash.readByte(addr);
  133.         TIM14->CCR1 = data; //改变PWM DAC的值
  134.         wavctrl.data_offset++;
  135.         //        Out_Data_Offset++;//地址数据要加2.这个可以跟转换出来的wav文件有关。
  136.       }
  137.       else
  138.       {
  139.         //播放完毕
  140.         TIM14->CCR1 = 0x80;
  141.         wavctrl.data_offset = 0;
  142.         TIM_ITConfig(TIM16, TIM_IT_Update, DISABLE ); //使能或者失能指定的TIM中断
  143.       }
  144.     }
  145.     else if(wavctrl.status == 2)
  146.     {
  147.       //暂停
  148.       TIM_ITConfig(TIM16, TIM_IT_Update, DISABLE ); //使能或者失能指定的TIM中断
  149.     }
  150.   }
  151.   TIM_ClearITPendingBit(TIM16, TIM_IT_Update  );  //清除TIMx的中断待处理位:TIM 中断源
  152. }

  153. void WavPause()
  154. {
  155.   wavctrl.status = 2;
  156. }

  157. void WavResume()
  158. {
  159.   wavctrl.status = 1;
  160.   TIM_ITConfig(TIM16, TIM_IT_Update, ENABLE ); //使能或者失能指定的TIM中断
  161. }

  162. void WavStop()
  163. {
  164.   wavctrl.status = 0;
  165. }

  166. void WavPlay(uint8_t num )
  167. {
  168.   wavctrl.status = 0;
  169.   uint32_t wav_info_addr = 183*4096;
  170.   uint32_t addr = wav_info_addr + num*4;
  171.   
  172.   uint32_t wav_addr = flash.readByte(addr);
  173.   wav_addr = (wav_addr<<8) + flash.readByte(addr+1);
  174.   wav_addr = (wav_addr<<8) + flash.readByte(addr+2);
  175.   wav_addr = (wav_addr<<8) + flash.readByte(addr+3);
  176.   Serial.printf("wav_addr=%d\r\n",wav_addr);
  177.   
  178.   uint8_t res = wav_decode_init(&wavctrl, wav_addr); //得到文件的信息
  179.   Serial.printf("res=%d\r\n", res);;
  180.   if(res == 0)
  181.   {
  182.     wavctrl.status = 1;
  183.     TIM_ITConfig(TIM16, TIM_IT_Update, ENABLE ); //使能或者失能指定的TIM中断
  184.   }
  185. }
复制代码


头文件wavplay.h
  1. #ifndef __WAVPLAY_H
  2. #define __WAVPLAY_H
  3. #include "stm32f0xx.h"

  4. #define WAV_BASE      184*4096

  5. #define WAV1_OFFSET   0
  6. #define WAV2_OFFSET   486398
  7. #define TIME_LEN  353266



  8. //RIFF块
  9. typedef __packed struct
  10. {
  11.   uint32_t ChunkID;                           //chunk id;这里固定为"RIFF",即0X46464952
  12.   uint32_t ChunkSize ;                           //集合大小;文件总大小-8
  13.   uint32_t Format;                                   //格式;WAVE,即0X45564157
  14. } ChunkRIFF ;
  15. //fmt块
  16. typedef __packed struct
  17. {
  18.   uint32_t ChunkID;                           //chunk id;这里固定为"fmt ",即0X20746D66
  19.   uint32_t ChunkSize ;                           //子集合大小(不包括ID和Size);这里为:20.
  20.   uint16_t AudioFormat;                  //音频格式;0X01,表示线性PCM;0X11表示IMA ADPCM
  21.   uint16_t NumOfChannels;                //通道数量;1,表示单声道;2,表示双声道;
  22.   uint32_t SampleRate;                        //采样率;0X1F40,表示8Khz
  23.   uint32_t ByteRate;                        //字节速率;
  24.   uint16_t BlockAlign;                        //块对齐(字节);
  25.   uint16_t BitsPerSample;                //单个采样数据大小;4位ADPCM,设置为4
  26.   //        uint16_t ByteExtraData;                //附加的数据字节;2个; 线性PCM,没有这个参数
  27. } ChunkFMT;
  28. //fact块
  29. typedef __packed struct
  30. {
  31.   uint32_t ChunkID;                           //chunk id;这里固定为"fact",即0X74636166;
  32.   uint32_t ChunkSize ;                           //子集合大小(不包括ID和Size);这里为:4.
  33.   uint32_t NumOfSamples;                  //采样的数量;
  34. } ChunkFACT;
  35. //LIST块
  36. typedef __packed struct
  37. {
  38.   uint32_t ChunkID;                           //chunk id;这里固定为"LIST",即0X74636166;
  39.   uint32_t ChunkSize ;                           //子集合大小(不包括ID和Size);这里为:4.
  40. } ChunkLIST;

  41. //data块
  42. typedef __packed struct
  43. {
  44.   uint32_t ChunkID;                           //chunk id;这里固定为"data",即0X5453494C
  45.   uint32_t ChunkSize ;                           //子集合大小(不包括ID和Size)
  46. } ChunkDATA;

  47. //wav头
  48. typedef __packed struct
  49. {
  50.   ChunkRIFF riff;        //riff块
  51.   ChunkFMT fmt;          //fmt块
  52.   //        ChunkFACT fact;        //fact块 线性PCM,没有这个结构体
  53.   ChunkDATA data;        //data块
  54. } __WaveHeader;

  55. //wav 播放控制结构体
  56. typedef  struct
  57. {
  58.   uint16_t audioformat;                        //音频格式;0X01,表示线性PCM;0X11表示IMA ADPCM
  59.   uint16_t nchannels;                                //通道数量;1,表示单声道;2,表示双声道;
  60.   uint16_t blockalign;                                //块对齐(字节);
  61.   uint32_t datasize;                                //WAV数据大小

  62.   uint32_t totsec ;                                //整首歌时长,单位:秒
  63.   uint32_t cursec ;                                //当前播放时长

  64.   uint32_t bitrate;                                   //比特率(位速)
  65.   uint32_t samplerate;                                //采样率
  66.   uint16_t bps;                                        //位数,比如16bit,24bit,32bit

  67.   uint32_t datastart;                                //数据帧开始的位置(在文件里面的偏移)
  68.   uint32_t addr;
  69.   uint32_t data_offset;
  70.   uint32_t status;//0 停止,1 播放 2,暂停
  71. } WAV_CTR_t;

  72. uint8_t wav_decode_init(WAV_CTR_t* wavx , uint32_t addr);
  73. void Speaker_Config();
  74. void WavStop();
  75. void WavPause();
  76. void WavResume();
  77. void WavPlay(uint8_t num );

  78. extern uint32_t WAV_ADDR[];
  79. extern WAV_CTR_t wavctrl;
  80. #endif
复制代码


声音 12KHZ 8bit 单声道 wav文件。
这个项目好久了用c++写的代码,具体分析代码吧。当初测试是能工作的。
回复

使用道具 举报

2

主题

22

帖子

0

精华

初级会员

Rank: 2

积分
78
金钱
78
注册时间
2012-10-20
在线时间
7 小时
发表于 2022-1-10 12:24:20 | 显示全部楼层
附件是声音文件。
回复

使用道具 举报

2

主题

22

帖子

0

精华

初级会员

Rank: 2

积分
78
金钱
78
注册时间
2012-10-20
在线时间
7 小时
发表于 2022-1-10 14:51:41 | 显示全部楼层
前面的忘记点上传了。wav声音文件

声音.zip

444.81 KB, 下载次数: 50

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-5-20 17:59

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表