OpenEdv-开源电子网

 找回密码
 立即注册

扫一扫,访问微社区

正点原子新作:阿波罗STM32F767&F429&探索者STM32F4开发板&赶快来下载资料哦。

查看: 14378|回复: 17

基于STM32的WAV音频格式播放器

[复制链接]

  离线 

6

主题

17

帖子

0

精华

初级会员

Rank: 2

积分
65
金钱
65
注册时间
2014-5-6
在线时间
1 小时
发表于 2014-5-13 16:05:50 | 显示全部楼层 |阅读模式
正点原子公众号

正在做的项目中需要STM32SD卡中读取语音文件进行播放,因此需要对语音进行解码,刚开始就一直使用Speex的音频压缩格式,最近发现,在进行语音格式转换时,我们不能很好地分析spx格式音频文件的文件头,这样就会导致语音的播放出现问题。由于WAV采用PCM编码,音质也十分不错,于是考虑用STM32WAV格式音频文件进行解码,上周末开始找资料和编程,其中也遇到了不少问题,不过功夫不负有心人,最终还是顺利的跑起来了。先将资料和编程过程整理成本文,供大家一起学习和进步。

WAV文件格式是一种重要的用于存放声音文件的文件格式,尽管现在有MP3RAM等压缩效率更高的声音文件格式,并且广泛被音乐文件所采用,但是又很多的应用程序仍然采用WAV文件格式。由于WAV文件没有采用压缩技术,所以它的文件很庞大,一般都在几MB以上。但也正是因为没有采用压缩技术,声音的采样数据很容易被读出来,便于用作其他的处理。

废话不多说了,我们直接去解析WAV文件格式吧。

WAV格式符合RIFFResource interchange File Format)规范。所有的WAV都有一个头文件,这个头文件音频流的编码参数。


1WAV文件的文件头

2WAV声音文件的数据块
接下来我们用已经编好的程序来读取一个WAV文件的文件头和数据块,看看各个内容都表示什么含义。


1WAV源文件



2、用WinHex软件解析WAV


    3STM32读取WAV的信息

头文件样例说明:

?        “52 49 46 46”这个是Ascii字符“RIFF”,这部分是固定格式,表明这是一个WAVE文件头。

?        “24 33 AE 00”这个是我的WAV文件的数据大小,这个大小包括除了前面4个字节的所有字节,也就是等于文件总字节数减去8。得到图3中的1141635611416356+8=11416364Byte=10.88Mb

?        “57 41 56 45 66 6D 74 20”,也是Ascii字符“WAVEfmt”,这部分是固定格式。以后是PCMWAVEFORMAT部分。

?        “10 00 00 00”,这是一个DWORD,对应数字16,这个对应定义中的PCMWAVEFORMAT部分的大小,可以看到后面的这个段内容正好是16个字节。当为16时,最后是没有附加信息的,当为数字18时,最后多了两个字节的附加信息。

?        “01 00”,这是一个WORD,对应定义为编码格式(WAVE_FORMAT_PCM格式用的就是这个)。

?        “01 00”,这是一个WORD,对应数字1,表示声道数为1,是个单声道WAV,当值为2时为立体声WAV

?        “22 56 00 00”对应数字22050,代表的是采样频率220505,采样率(每秒样本数)表示每个通道的播放速度。

?        “44 AC 00 00”对应数字44100,代表的是每秒的数据量,波形音频数据传送数率,其值为通道数×每秒样本数×每个样本的数据位数/8。播放软件利用此值可以估计缓冲区的大小。

?        “02 00:”对应数字是2,表示块对齐的内容。数据块的调整数(按字节算),其值为通道数×每个样本的数据位置/8.播放软件需要一次处理多个改值大小的字节数据,以便将其值用于缓冲区的调整。

?        “10 00”,此数值为16,采样大小为16bits,每样本数据位数,表示每个声道中各个样本的数据位数。如果有多个声道,对每个声道而言,样本大小都一样。

?        “64 61 74 61”,这个是Ascii字符“data”,表示头结束,开始数据区域。

?        “00 33 AE 00”,十六进制数是“0xAE3300”,对应十进制11416320,是数据区的开头以后的数据总数。

 

再往后就是真正的WAV文件数据体了,头文件分析到此。

常见的声音文件主要有两种,分别对应单声道(11.025KHz采样率、8Bit的采样值)和双声道(44.1KHz采样率、16Bit的采样值)。采样率是指:声音信号在“模->数”转换过程中单位时间内采样的次数。采样值是指每一次采样周期内声音模拟信号的积分值。

对于单声道声音文件,采样数据位8位的短整数;而对于双声道立体声声音文件,每次采样数据位一个16位的整数,高8为和低8位分别代表左右两个声道。
WAVE文件数据块包含以脉冲编码调制(PCM)格式表示样本。WAVE文件是由样本组织而成的。在单声道WAVE文件中,声道0代表左声道,声道1代表右声道。在多声道WAVE文件中,样本是交替出现的。

 

PCM数据的存放方式:

                  样本1                    样本2   

8位单声道        0声道                    0声道

8位立体声        0声道(左)1声道(右)    0声道(左) 1声道(右)

16位单声道       0声道低 0声道高          0声道低 0声道高  

16位立体声 0声道(左)低 0声道() 1声道(右)低 1声道(右)高  



    系统硬件组成比较简单,可以分为液晶显示,LED指示,USB输入,SD卡,电源供电,音频功放和按键等,如图3-1所示:


3-1 系统组成框图

SD卡电路:
SD卡采用SPI驱动。


USB电路:


采用SGM7222做转换开关,识别ID的电压值来选择是作为IAP下载还是用于USB接口


音频功放电路:


充电和系统电源:






程序编写主要有三个部分:定时器初始化,DAC初始化,定时器中断服务程序,WAV播放程序。

 

定时器初始化:

void Timerx_Init(u16 arr,u16 psc)

{

       NVIC_InitTypeDef NVIC_InitStructure;

      

       RCC->APB1ENR|=1<<1;//TIM3时钟使能  

      TIM3->ARR=arr;  //设定计数器自动重装值   

       TIM3->SC=psc;  //预分频器7200,得到10KHz的计数时钟

       TIM3->DIER|=1<<0;   //允许更新中断          

       TIM3->DIER|=1<<6;   //允许触发中断

                                                                  

       TIM3->CR1|=0x01;    //使能定时器3

      

       //优先级设置

      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

       NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

       NVIC_Init(&NVIC_InitStructure);

}

 

DAC初始化:

#include "dac.h"

 

extern u16 digital;

 

void MyDAC_Init(void)//DAC channel1 Configuration

{

    unsigned int tmpreg1=0,tmpreg2=0;

      RCC->APB2ENR|=1<<2;//使能PORTA时钟

       RCC->APB1ENR|=RCC_APB1Periph_DAC;//使能DAC时钟

      GPIOA->CRL&=0XFFF0FFFF;

       GPIOA->CRL|=0X00040000;//PA4浮空输入    

 

    tmpreg1=DAC->CR;//Get the DAC CR value 

    tmpreg1&=~(CR_CLEAR_Mask<<DAC_Channel_1);

  tmpreg2=(DAC_Trigger_Software|DAC_WaveGeneration_None|DAC_LFSRUnmask_Bits8_0|DAC_OutputBuffer_Enable);

    tmpreg1|=tmpreg2<<DAC_Channel_1;

    DAC->CR=tmpreg1;//Write to DAC CR

     DAC->CR|=CR_EN_Set<<DAC_Channel_1;//DAC通道1使能,PA4自动连接到DAC

       DAC1_SetData(0x000);

 

       #if 0

    tmpreg1=DAC->CR;//Get the DAC CR value 

    tmpreg1&=~(CR_CLEAR_Mask<<DAC_Channel_2);  tmpreg2=(DAC_Trigger_Software|DAC_WaveGeneration_None|DAC_LFSRUnmask_Bits8_0|DAC_OutputBuffer_Enable);

tmpreg1|=tmpreg2<<DAC_Channel_2;

DAC->CR=tmpreg1;

       DAC->CR|=CR_EN_Set<<DAC_Channel_2;

       DAC2_SetData(0x000);

       #endif

}

void DAC1_SetData(u16 data)

{

       DAC->DHR12R1=data;//通道112位右对齐数据

       DAC->SWTRIGR|=0x01;//软件启动转换

}

void DAC2_SetData(u16 data)

{

       DAC->DHR12R2=data;//通道212位右对齐数据

       DAC->SWTRIGR|=0x02;//软件启动转换

}

定时器中断服务程序:

void TIM3_IRQHandler(void)

{                                                      

       u16 temp;

       if(TIM3->SR&0X0001)//溢出中断

       {

              if(CHanalnum==1)//单声道

              {

                     if(Bitnum==8)//8位精度

                     {

                            DAC->DHR12R1=wav_buf[DApc]*10/volume;

                            DAC->DHR12R2=wav_buf[DApc]*10/volume;

                            DAC->SWTRIGR |=0x01;                        

DApc++;

                     }

                     else if(Bitnum==16)                    

{

               temp=(((u8)(wav_buf[DApc+1]-0x80)<<4)|(wav_buf[DApc]>>4))*10/volume;

                        DAC->DHR12L1=temp;

                        DAC->DHR12L2=temp;

                        DAC->SWTRIGR|=0x01;

                        DApc+=2;                     

                     }

              }

              else if(CHanalnum==2)

              {

                     if(Bitnum==8)

                     {

                            DAC->DHR12R1=wav_buf[DApc]*10/volume;

                            DApc++;

                            DAC->DHR12R2=wav_buf[DApc]*10/volume;

                            DApc++;

                            DAC->SWTRIGR|=0x01;                  

}

                     else if(Bitnum==16)

                     {            DAC->DHR12L1=(((u8)(wav_buf[DApc+1]-0x80)<<4)|(wav_buf[DApc]>>4))*10/volume;                       DApc+=2;             DAC->DHR12L2=(((u8)(wav_buf[DApc+1]-0x80)<<4)|(wav_buf[DApc]>>4))*10/volume;

                            DApc+=2;                   

DAC->SWTRIGR|=0x01;           

                     }

              }           

              if(DApc==16384)

        {

        DApc=0;

        DACdone=1;

    }                                                                                                        

       }                            

       TIM3->SR&=~(1<<0);  

}

 

WAV初始化:

u8 WAV_Init(u8* wav_buf)

{

       if(Check_Ifo(wav_buf,"RIFF"))

              return 1; 

       wav1.wavlen=Get_num(wav_buf+4,4);

       printf("\n\rwav1.wavlen = %ld\n\r",wav1.wavlen);

//if(Check_Ifo(wav_buf+8,"WAVE"))return 2;//WAVE错误标志

//if(Check_Ifo(wav_buf+12,"fmt "))return 3;//fmt错误标志

       wav1.formart=Get_num(wav_buf+20,2);//格式类别

       printf("\n\rwav1.formart = %d\n\r",wav1.formart);

      

       wav1.CHnum=Get_num(wav_buf+22,2);//通道数

       printf("\n\rwav1.CHnum = %d\n\r",wav1.CHnum);

       CHanalnum=wav1.CHnum;

      

       wav1.SampleRate=Get_num(wav_buf+24,4);//采样率

       printf("\n\rwav1.SampleRate = %ld\n\r",wav1.SampleRate);

      

       wav1.speed=Get_num(wav_buf+28,4);//音频转换数率

       printf("\n\rwav1.speed = %ld\n\r",wav1.speed);

      

       wav1.ajust=Get_num(wav_buf+32,2);//数据块调速数

       printf("\n\rwav1.ajust = %d\n\r",wav1.ajust);

      

       wav1.SampleBits=Get_num(wav_buf+34,2);//样本数据位数

       printf("\n\rwav1.SampleBits = %d\n\r",wav1.SampleBits);

       Bitnum=wav1.SampleBits;

      

//if(Check_Ifo(wav_buf+36,"data"))return 4;//数据标志错误

      

       wav1.DATAlen=Get_num(wav_buf+40,4);//数据长度

       printf("\n\rwav1.DATAlen = %d\n\r",wav1.DATAlen);

      

       if(wav1.wavlen<0x100000)

       {

              printf("\n\rwav1.wavlen = %dkb\n\r",(wav1.wavlen)>>10);

       }

       else

       {

              printf("\n\rwav1.wavlen = %dMb\n\r",(wav1.wavlen)>>20);

       }

       if(wav1.formart==1)

              printf("\n\rWAV PCM\n\r");

       if(wav1.CHnum==1)

              printf("\n\rsingle\n\r");

       else

              printf("\n\rstereo\n\r");

       printf("\n\rwav1.SampleRate = %dkHz\n\r",(wav1.SampleRate)/1000);

       printf("\n\rwav1.speed = %dbps\n\r",(wav1.speed)/1000);

       printf("\n\rwav1.SampleBits = %dbit\n\r",wav1.SampleBits);

      

       return 0;

}

u8 Check_Ifo(u8* pbuf1,u8* pbuf2)

{

       u8 i;

       for(i=0;i<4;i++)

              if(pbuf1!=pbuf2)

                     return 1;

       return 0;

}

 

u32 Get_num(u8* pbuf,u8 len)

{

  u32 num;

       if(len==2)num=(pbuf[1]<<8)|pbuf[0];

       else if(len==4)num=(pbuf[3]<<24)|(pbuf[2]<<16)|(pbuf[1]<<8)|pbuf[0];

       return num;

}

 

WAV播放:

u8 Playwav(char *file)

{

       FIL fwav;

       FRESULT Res;

       UINT BR;

       unsigned char i;

       unsigned int times;

       Res = f_open(&fwav, file, FA_OPEN_EXISTING | FA_READ);

       if(Res != FR_OK)

  {

              printf("\n\ropen file error : %d\n\r",Res);

       }

       else

  {

         Res = f_read(&fwav, wav_buf, sizeof(wav_buf), &BR);     /* Read a chunk of src file */

    if(Res==FR_OK)

    {

                     WAV_Init(wav_buf);

                     DACdone=0;

                     DApc=44; //跳过头信息             

                     Timerx_Init(1000000/wav1.SampleRate,72); //定时器初始化

                     times=(wav1.DATAlen>>10)-1; //计算数据大小              

                    

                     for(i=0;i<times/32;i++)//循环一次转换32KB数据

                     {    

                            while(!DACdone);//等待前面16384字节转换完成                                                   DACdone=0;

                            Res = f_read(&fwav, wav_buf, 16384, &BR);

                            while(!DACdone);// 等待前面16384字节转换完成

                            DACdone=0;

                            Res = f_read(&fwav, wav_buf, 16384, &BR);//读取数据

                     }

              }

              else

              {

                     printf("\n\rread file error : %d\n\r",Res);

              }

              f_close(&fwav);

       }

       return 0;

}


回复

使用道具 举报

  离线 

482

主题

8万

帖子

30

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
117247
金钱
117247
注册时间
2010-12-1
在线时间
900 小时
发表于 2014-5-13 22:40:02 | 显示全部楼层
不错,谢谢分享.
要是能上传工程就更好了.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
微信公众平台:正点原子   点击扫码添加
回复 支持 反对

使用道具 举报

  离线 

45

主题

248

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
448
金钱
448
注册时间
2013-5-18
在线时间
0 小时
发表于 2014-5-15 08:28:55 | 显示全部楼层
有代码就更好了
回复 支持 反对

使用道具 举报

  离线 

1

主题

18

帖子

0

精华

初级会员

Rank: 2

积分
65
金钱
65
注册时间
2013-6-2
在线时间
4 小时
发表于 2014-10-28 10:35:49 | 显示全部楼层
应经很棒的资料了。谢谢!很有用!
回复 支持 反对

使用道具 举报

  离线 

118

主题

953

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1909
金钱
1909
注册时间
2014-5-21
在线时间
202 小时
发表于 2014-12-25 11:17:24 | 显示全部楼层
回复【2楼】正点原子:
---------------------------------
看了这个帖子,可惜没工程,我自己摸索着也快做出来了,回头我发一个分享帖的,应该比他详细
彼高丽者,边夷贱类,不足待以仁义,不可责以常礼。古来以鱼鳖畜之,宜从阔略。若必欲绝其种类,恐兽穷则搏。
回复 支持 反对

使用道具 举报

  离线 

2

主题

11

帖子

0

精华

新手上路

Rank: 1

积分
39
金钱
39
注册时间
2015-5-5
在线时间
0 小时
发表于 2015-5-7 10:25:19 | 显示全部楼层
回复【5楼】cornrn:
---------------------------------
那你现在还有这个例子吗?有的话发我一份啊....这是我邮箱:453251337@qq.com
回复 支持 反对

使用道具 举报

  离线 

1

主题

5

帖子

0

精华

新手上路

Rank: 1

积分
38
金钱
38
注册时间
2015-6-27
在线时间
3 小时
发表于 2015-6-27 16:47:51 | 显示全部楼层
能解释下
temp=(((u8)(wav_buf[DApc+1]-0x80)<<4)|(wav_buf[DApc]>>4))*10/volume;
这句将16bit转为12bit的具体思想吗,看了好久也不明白。
回复 支持 反对

使用道具 举报

  离线 

1

主题

5

帖子

0

精华

新手上路

Rank: 1

积分
38
金钱
38
注册时间
2015-6-27
在线时间
3 小时
发表于 2015-6-27 16:48:14 | 显示全部楼层
回复【2楼】正点原子:
---------------------------------
能解释下
temp=(((u8)(wav_buf[DApc+1]-0x80)<<4)|(wav_buf[DApc]>>4))*10/volume;
这句将16bit转为12bit的具体思想吗,看了好久也不明白。
回复 支持 反对

使用道具 举报

  离线 

482

主题

8万

帖子

30

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
117247
金钱
117247
注册时间
2010-12-1
在线时间
900 小时
发表于 2015-6-27 17:55:35 | 显示全部楼层
回复【8楼】好见不坏:
---------------------------------
我也不知道。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
微信公众平台:正点原子   点击扫码添加
回复 支持 反对

使用道具 举报

  离线 

7

主题

22

帖子

0

精华

初级会员

Rank: 2

积分
109
金钱
109
注册时间
2015-11-16
在线时间
26 小时
发表于 2016-3-10 18:31:23 | 显示全部楼层
能把工程文件传上来分享一下吗,正在需要这方面的
回复 支持 反对

使用道具 举报

  离线 

1

主题

18

帖子

0

精华

初级会员

Rank: 2

积分
88
金钱
88
注册时间
2016-6-1
在线时间
26 小时
发表于 2016-6-1 20:08:27 | 显示全部楼层
cornrn 发表于 2014-12-25 11:17
回复【2楼】正点原子:
---------------------------------
看了这个帖子,可惜没工程,我自己摸索着也快做 ...

朋友,能分享一下你的工程给我么?谢谢。我的邮箱:1498873933@qq.com
回复 支持 反对

使用道具 举报

  离线 

1

主题

18

帖子

0

精华

初级会员

Rank: 2

积分
88
金钱
88
注册时间
2016-6-1
在线时间
26 小时
发表于 2016-6-1 20:09:28 | 显示全部楼层
china043 发表于 2016-3-10 18:31
能把工程文件传上来分享一下吗,正在需要这方面的

朋友,这个工程你做的怎样了
回复 支持 反对

使用道具 举报

  离线 

1

主题

18

帖子

0

精华

初级会员

Rank: 2

积分
88
金钱
88
注册时间
2016-6-1
在线时间
26 小时
发表于 2016-6-7 09:56:07 | 显示全部楼层
正点原子 发表于 2015-6-27 17:55
回复【8楼】好见不坏:
---------------------------------
我也不知道。

原子兄,能帮我看看这段代码么?谢谢
#define  SAMPLE_RATE_8000                            8000
#define  SAMPLE_RATE_11025                           11025
#define         SAMPLE_RATE_16000                                                                                16000
#define  SAMPLE_RATE_22050                           22050
#define  SAMPLE_RATE_44100                           44100
#define  SAMPLE_RATE_48000                           48000
#define RCC_APB2Periph_GPIOA             ((uint32_t)0x00000004)//zhuyi
#define RCC_APB2Periph_AFIO              ((uint32_t)0x00000001)
#define RCC_APB2Periph_GPIOC             ((uint32_t)0x00000010)//zhuyi

void RCC_Init(void);
void RCC_Configuration(void);
void GPIOA_Configuration(void);
void TIM6_Configuration(void);
void DAC1_Configuration(void);
void SystemInit(void);
void DMA2_Configuration(void);        //PA->DAC??3
void NVIC_Configuration(void);

uint32_t wavecount;

#define                WAVELENGTH                64044
const uint8_t wavedata[64044]={
xxxxxxxxxxxxxxxxxxxx
};
u16 GetARRValue(u16 sample)
{
        u16 arrValue;
        /* 更新OCA值以符合.WAV文件采样率 */
        switch (sample)
        {
                case SAMPLE_RATE_8000 :
                        arrValue = (uint16_t)(72000000/8000);
                        break; /* 8KHz = 2x36MHz / 9000 */
                case SAMPLE_RATE_11025:
                        arrValue = (uint16_t)(72000000/11025);
                        break; /* 11.025KHz = 2x36MHz / 6531 */
                case SAMPLE_RATE_16000:
                        arrValue = (uint16_t)(72000000/16000);
                        break; /* 16KHz = 2x36MHz / 4500 */
                case SAMPLE_RATE_22050:
                        arrValue = (uint16_t)(72000000/22050);
                        break; /* 22.05KHz = 2x36MHz / 2365 */
                case SAMPLE_RATE_44100:
                        arrValue = (uint16_t)(72000000/44100);
                        break; /* 44.1KHz = 2x36MHz / 1633 */
                case SAMPLE_RATE_48000:
                        arrValue = (uint16_t)(72000000/48000);
                        break; /* 48KHz = 2x36MHz / 1500 */
                default:
                        arrValue = 0;
                        break;
        }
        return arrValue;
}
void TIM6_Int_Init(u16 arr,u16 psc)
{
        TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
        NVIC_InitTypeDef NVIC_InitStructure;

        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); /???
      
        /??TIM2???
        TIM_TimeBaseStructure.TIM_Period = arr; /??????????????????????????      
        TIM_TimeBaseStructure.TIM_Prescaler =psc; /?????TIMx???????????
        TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; /?????:TDTS = Tck_tim
        TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM??????
        TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure); /?????????TIMx???????
  
        TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE ); /????TIM2??,??????

        /????NVIC??
        NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;  //TIM2??
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  /????0?
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  /???3?
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ?????
        NVIC_Init(&NVIC_InitStructure);  /??NVIC???

//         TIM_Cmd(TIM6, ENABLE);  /?TIMx                                         
}


void DACInit(void)
{
        GPIO_InitTypeDef                         GPIO_InitStructure;
        DAC_InitTypeDef                           DAC_InitStructure;
        TIM_TimeBaseInitTypeDef         TIM_TimeBaseStructure;
        NVIC_InitTypeDef                         NVIC_InitStructure;
      
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);          //使能PORTA通道时钟
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);          //使能DAC通道时钟
      
        /***********************************************************************************
        问:PA4为什么要设置为模拟输入?
        答:STM32F103ZET6 的 DAC 通道 1 在 PA4 上,所以,我们先要使能 PORTA 的时钟, 然后设
                置 PA4 为模拟输入。 DAC 本身是输出,但是为什么端口要设置为模拟输入模式呢?因为一但
                使能 DACx 通道之后,相应的 GPIO 引脚( PA4 或者 PA5)会自动与 DAC 的模拟输出相连,设
                置为输入,是为了避免额外的干扰。
        ***********************************************************************************/
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;                                 // 端口配置
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;                  //模拟输入
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
        GPIO_SetBits(GPIOA,GPIO_Pin_4)        ;//PA.4 输出高
      
        TIM6_Int_Init(8999,0);
      
        DAC_DeInit();
//        DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO;
        DAC_InitStructure.DAC_Trigger = DAC_Trigger_None;
        DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
        DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bits8_0;
        DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable;
        DAC_Init(DAC_Channel_1, &DAC_InitStructure);
        DAC_Cmd(DAC_Channel_1, ENABLE);

        TIM_Cmd(TIM6, ENABLE);
}

void NVIC_Configuration(void)
{

        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);        //设置NVIC中断分组2:2位抢占优先级,2位响应优先级

}

/*DMA通道3中断-------------------------*/

void DMAChannel3_IRQHandler(void)
{
        if(DMA_GetFlagStatus(DMA2_IT_GL3)!=RESET)
        {
                DMA_ClearFlag(DMA2_IT_TC3);
                DMA_Cmd(DMA2_Channel3, ENABLE);
                DMA2_Channel3->CNDTR = 32;         
                DMA_Cmd(DMA2_Channel3, ENABLE);
        }
}
/*RCC_Configuration---------------------------------*/
void RCC_Configuration(void)
{
        /*开启相关通道的时钟使能*/
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC|RCC_APB2Periph_AFIO, ENABLE);
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);
}

/*DMA2_Channle配置----------------------------------*/
void DMA2_Configuration(void)
{
        rw_DMA2_CCR3&=0xffff8000;//寄存器清0
        rw_DMA2_CPAR3=0x40007410;
        rw_DMA2_CMAR3=(u32)&wavedata;
        rw_DMA2_CNDTR3=37040;

        rw_DMA2_CCR3|=(0<<14);//启动非存储器到存储器模式
        rw_DMA2_CCR3|=(0x03<<12);//设置通道优先级为高
        rw_DMA2_CCR3|=(0x00<<10);//设置存储数据宽度为8位
        rw_DMA2_CCR3|=(0x00<<8);//设置外设数据宽度为8位
        rw_DMA2_CCR3|=(1<<7);//启动存储器增量模式
        rw_DMA2_CCR3|=(0<<6);//关闭外存储器增量模式
        rw_DMA2_CCR3|=(1<<5);//开启循环模式
        rw_DMA2_CCR3|=(1<<4);//从存储器读
        rw_DMA2_CCR3|=(0<<1);//关闭传输完成中断

//        rw_DMA2_CCR3|=(1<<0);//开启通道
//        rw_DAC_CR|=(1<<12);//开启DAC->DMA通道
}


int main(void)
{               
        delay_init();                     //延时函数初始化         
        NVIC_Configuration();          //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
        uart_init(9600);         //串口初始化为9600
        DACInit();
        while(1)
        {
               
        };
}

void TIM6_IRQHandler(void)
{
        u16 tmpCap;         
        if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)
        {
                if( wavecount < 48474)
                {
                        tmpCap = wavedata1[wavecount];
                        wavecount++;
                        /* Set DAC Channel1 DHR register */
                        DAC_SetChannel1Data(DAC_Align_8b_R,tmpCap);                        
                }
                else
                {
                        // 完成传输,关闭中断
                        wavecount = 0;
//                        TIM_ITConfig(TIM6, TIM_IT_Update, DISABLE);
//                        TIM_Cmd(TIM6, DISABLE);
//                         // 需要关闭DAC,不然在没有声音的时候会有杂音
//                        DAC_Cmd(DAC_Channel_1, DISABLE);
                }
        }
        TIM_ClearITPendingBit(TIM6, TIM_IT_Update);
}
回复 支持 反对

使用道具 举报

  离线 

1

主题

18

帖子

0

精华

初级会员

Rank: 2

积分
88
金钱
88
注册时间
2016-6-1
在线时间
26 小时
发表于 2016-6-7 16:46:03 | 显示全部楼层
cornrn 发表于 2014-12-25 11:17
回复【2楼】正点原子:
---------------------------------
看了这个帖子,可惜没工程,我自己摸索着也快做 ...

我的工程代码不能让喇叭放出声音。能帮我看看么?
#define  SAMPLE_RATE_8000                            8000
#define  SAMPLE_RATE_11025                           11025
#define         SAMPLE_RATE_16000                                                                                16000
#define  SAMPLE_RATE_22050                           22050
#define  SAMPLE_RATE_44100                           44100
#define  SAMPLE_RATE_48000                           48000
#define RCC_APB2Periph_GPIOA             ((uint32_t)0x00000004)//zhuyi
#define RCC_APB2Periph_AFIO              ((uint32_t)0x00000001)
#define RCC_APB2Periph_GPIOC             ((uint32_t)0x00000010)//zhuyi

void RCC_Init(void);
void RCC_Configuration(void);
void GPIOA_Configuration(void);
void TIM6_Configuration(void);
void DAC1_Configuration(void);
void SystemInit(void);
void DMA2_Configuration(void);        //PA->DAC??3
void NVIC_Configuration(void);

uint32_t wavecount;

#define                WAVELENGTH                64044
const uint8_t wavedata[64044]={
xxxxxxxxxxxxxxxxxxxx
};
u16 GetARRValue(u16 sample)
{
        u16 arrValue;
        /* 更新OCA值以符合.WAV文件采样率 */
        switch (sample)
        {
                case SAMPLE_RATE_8000 :
                        arrValue = (uint16_t)(72000000/8000);
                        break; /* 8KHz = 2x36MHz / 9000 */
                case SAMPLE_RATE_11025:
                        arrValue = (uint16_t)(72000000/11025);
                        break; /* 11.025KHz = 2x36MHz / 6531 */
                case SAMPLE_RATE_16000:
                        arrValue = (uint16_t)(72000000/16000);
                        break; /* 16KHz = 2x36MHz / 4500 */
                case SAMPLE_RATE_22050:
                        arrValue = (uint16_t)(72000000/22050);
                        break; /* 22.05KHz = 2x36MHz / 2365 */
                case SAMPLE_RATE_44100:
                        arrValue = (uint16_t)(72000000/44100);
                        break; /* 44.1KHz = 2x36MHz / 1633 */
                case SAMPLE_RATE_48000:
                        arrValue = (uint16_t)(72000000/48000);
                        break; /* 48KHz = 2x36MHz / 1500 */
                default:
                        arrValue = 0;
                        break;
        }
        return arrValue;
}

void TIM6_Int_Init(u16 arr,u16 psc)
{
        TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
        NVIC_InitTypeDef NVIC_InitStructure;

        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); /???
      
        /??TIM2???
        TIM_TimeBaseStructure.TIM_Period = arr; /??????????????????????????      
        TIM_TimeBaseStructure.TIM_Prescaler =psc; /?????TIMx???????????
        TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; /?????:TDTS = Tck_tim
        TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM??????
        TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure); /?????????TIMx???????
  
        TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE ); /????TIM2??,??????

        /????NVIC??
        NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;  //TIM2??
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  /????0?
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  /???3?
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ?????
        NVIC_Init(&NVIC_InitStructure);  /??NVIC???

//         TIM_Cmd(TIM6, ENABLE);  /?TIMx                                         
}


void DACInit(void)
{
        GPIO_InitTypeDef                         GPIO_InitStructure;
        DAC_InitTypeDef                           DAC_InitStructure;
        TIM_TimeBaseInitTypeDef         TIM_TimeBaseStructure;
        NVIC_InitTypeDef                         NVIC_InitStructure;
      
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);          //使能PORTA通道时钟
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);          //使能DAC通道时钟
      
        /***********************************************************************************
        问:PA4为什么要设置为模拟输入?
        答:STM32F103ZET6 的 DAC 通道 1 在 PA4 上,所以,我们先要使能 PORTA 的时钟, 然后设
                置 PA4 为模拟输入。 DAC 本身是输出,但是为什么端口要设置为模拟输入模式呢?因为一但
                使能 DACx 通道之后,相应的 GPIO 引脚( PA4 或者 PA5)会自动与 DAC 的模拟输出相连,设
                置为输入,是为了避免额外的干扰。
        ***********************************************************************************/
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;                                 // 端口配置
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;                  //模拟输入
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
        GPIO_SetBits(GPIOA,GPIO_Pin_4)        ;//PA.4 输出高
      
        TIM6_Int_Init(8999,0);
      
        DAC_DeInit();
//        DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO;
        DAC_InitStructure.DAC_Trigger = DAC_Trigger_None;
        DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
        DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bits8_0;
        DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable;
        DAC_Init(DAC_Channel_1, &DAC_InitStructure);
        DAC_Cmd(DAC_Channel_1, ENABLE);

        TIM_Cmd(TIM6, ENABLE);
}

void NVIC_Configuration(void)
{

        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);        //设置NVIC中断分组2:2位抢占优先级,2位响应优先级

}

/*DMA通道3中断-------------------------*/

void DMAChannel3_IRQHandler(void)
{
        if(DMA_GetFlagStatus(DMA2_IT_GL3)!=RESET)
        {
                DMA_ClearFlag(DMA2_IT_TC3);
                DMA_Cmd(DMA2_Channel3, ENABLE);
                DMA2_Channel3->CNDTR = 32;         
                DMA_Cmd(DMA2_Channel3, ENABLE);
        }
}
/*RCC_Configuration---------------------------------*/
void RCC_Configuration(void)
{
        /*开启相关通道的时钟使能*/
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC|RCC_APB2Periph_AFIO, ENABLE);
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);
}

/*DMA2_Channle配置----------------------------------*/
void DMA2_Configuration(void)
{
        rw_DMA2_CCR3&=0xffff8000;//寄存器清0
        rw_DMA2_CPAR3=0x40007410;
        rw_DMA2_CMAR3=(u32)&wavedata;
        rw_DMA2_CNDTR3=37040;

        rw_DMA2_CCR3|=(0<<14);//启动非存储器到存储器模式
        rw_DMA2_CCR3|=(0x03<<12);//设置通道优先级为高
        rw_DMA2_CCR3|=(0x00<<10);//设置存储数据宽度为8位
        rw_DMA2_CCR3|=(0x00<<8);//设置外设数据宽度为8位
        rw_DMA2_CCR3|=(1<<7);//启动存储器增量模式
        rw_DMA2_CCR3|=(0<<6);//关闭外存储器增量模式
        rw_DMA2_CCR3|=(1<<5);//开启循环模式
        rw_DMA2_CCR3|=(1<<4);//从存储器读
        rw_DMA2_CCR3|=(0<<1);//关闭传输完成中断

//        rw_DMA2_CCR3|=(1<<0);//开启通道
//        rw_DAC_CR|=(1<<12);//开启DAC->DMA通道
}


int main(void)
{               
        delay_init();                     //延时函数初始化         
        NVIC_Configuration();          //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
        uart_init(9600);         //串口初始化为9600
        DACInit();
        while(1)
        {
               
        };
}

void TIM6_IRQHandler(void)
{
        u16 tmpCap;         
        if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)
        {
                if( wavecount < 48474)
                {
                        tmpCap = wavedata1[wavecount];
                        wavecount++;
                        /* Set DAC Channel1 DHR register */
                        DAC_SetChannel1Data(DAC_Align_8b_R,tmpCap);                        
                }
                else
                {
                        // 完成传输,关闭中断
                        wavecount = 0;
//                        TIM_ITConfig(TIM6, TIM_IT_Update, DISABLE);
//                        TIM_Cmd(TIM6, DISABLE);
//                         // 需要关闭DAC,不然在没有声音的时候会有杂音
//                        DAC_Cmd(DAC_Channel_1, DISABLE);
                }
        }
        TIM_ClearITPendingBit(TIM6, TIM_IT_Update);
}
回复 支持 反对

使用道具 举报

  离线 

6

主题

27

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
362
金钱
362
注册时间
2015-12-1
在线时间
71 小时
发表于 2016-6-29 10:26:46 | 显示全部楼层
1498873933@qq.c 发表于 2016-6-7 16:46
我的工程代码不能让喇叭放出声音。能帮我看看么?
#define  SAMPLE_RATE_8000                          ...

哥们,你这是在做wav播放器吗?用没用到外部芯片? 现在可以出声音了吗?
回复 支持 反对

使用道具 举报

  离线 

0

主题

1

帖子

0

精华

新手上路

Rank: 1

积分
11
金钱
11
注册时间
2016-7-28
在线时间
2 小时
发表于 2016-7-28 15:25:14 | 显示全部楼层
本帖最后由 阿波罗先生 于 2016-7-28 15:27 编辑

谢谢分享,请问楼主volume的值是多少?
回复 支持 反对

使用道具 举报

  离线 

1

主题

18

帖子

0

精华

初级会员

Rank: 2

积分
88
金钱
88
注册时间
2016-6-1
在线时间
26 小时
发表于 2016-10-14 11:19:02 | 显示全部楼层
Oser_Man 发表于 2016-6-29 10:26
哥们,你这是在做wav播放器吗?用没用到外部芯片? 现在可以出声音了吗?

之前做的没有做成功,没有用外部芯片,不过加了外部功放芯片。
回复 支持 反对

使用道具 举报

  离线 

3

主题

15

帖子

0

精华

初级会员

Rank: 2

积分
192
金钱
192
注册时间
2016-8-19
在线时间
21 小时
发表于 2017-11-14 23:40:33 | 显示全部楼层
这个资料很不错,wav 硬件都有了,再来个pcm解析,就完美了
回复 支持 反对

使用道具 举报

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

本版积分规则




QQ|联系我们|手机版|官方淘宝店|新浪微博|微信公众平台|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2017-11-22 07:11

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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