打印
[APM32F4]

APM32F407VET6 -> DMA+DAC播放WAV音频文件

[复制链接]
1686|30
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
LQM568|  楼主 | 2022-5-27 16:28 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本程序为应用级,可以根据实际情况修改后用到你的项目中,本代码例程是运用STM32F407的标准库,可以运用到APM32407上完全兼容运行。
这里采用的是STM32F407VET6标准库 DMA+DAC 的方式播放16Khz 8bit数据,只实现了DAC1 右对齐8位数数据播放方式,使用了TIM6 作为DAC触发基准,由于频率较高,这里使用了双缓冲的方式,加载音频数据 两个buff交替加载
本程序据有音量调节功能,最小音量级数30级(可以自己定义,在.h文件中由宏MAXVolume设置)。
这里播放的是原始wav文件没有经过任何裁剪,具体wav数据头文件原理请参考其他说明。

//Audio.c文件的初始化配置:

#include "Audio.h"
#include "soundlib.h"  //音频库头文件
#include "W25Qxx.h" //读取外置flash内的音频
#include "rs485.h"     //调试用

//DAC数据寄存器地址
#define Audio_DR8R  (0x40007410)
#define AUdio_DR12R (0x40007408)
#define Audio_DHR8R (0x40007428)



Audio_Info *Audio;
void Audio_DMA_8K(u8 *dat,u16 len);

//内部外部播放需要的参数
Play_Flag Inplay={0},Explay={0};
//音频数据指针
static u8 *ExData;
//DMA双缓冲数据buff
static u8 Audiobuf0[mallocLen]={0},Audiobuf1[mallocLen]={0};


//DAC 配置初始化
void Audio_DAC_Init(void)
{            
    DMA_InitTypeDef  DMA_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
  DAC_InitTypeDef  DAC_InitType;   

   
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
   
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;    //模拟输入
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//下拉
    GPIO_Init(GPIOA, &GPIO_InitStructure);          //初始化

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;   
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;   
    GPIO_Init(GPIOA,&GPIO_InitStructure);
   
    TIM_DeInit(TIM6);
    TIM_SetAutoreload(TIM6, 5250); //16KHz=84000000/5250
    TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update);
    TIM_Cmd(TIM6, ENABLE);
   
  //DAC1 12R
  DMA_DeInit(DMA1_Stream5);
  while (DMA_GetCmdStatus(DMA1_Stream5) != DISABLE){}              //等待DMA可配置
  DMA_InitStructure.DMA_Channel = DMA_Channel_7;                            //通道选择
  DMA_InitStructure.DMA_PeripheralBaseAddr =Audio_DR8R ;            //DMA外设地址
  DMA_InitStructure.DMA_Memory0BaseAddr = 0;                                 //DMA 存储器0地址
  DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;          //存储器->外设
  DMA_InitStructure.DMA_BufferSize = 0;                            //数据传输量
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设非增量模式
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;          //存储器增量模式
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设数据长度:8位
  DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;    //存储器数据长度:8位
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                          //使用普通模式
  DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;                //非常高等优先级
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;               //存储器突发单次传输
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;       //外设突发单次传输
  DMA_Init(DMA1_Stream5, &DMA_InitStructure);                               //初始化DMA Stream
    DMA_ITConfig(DMA1_Stream5,DMA_IT_TC|DMA_IT_HT,ENABLE);
  DMA_Cmd(DMA1_Stream5, DISABLE);                                           //关闭DMA传输   
   
    DAC_InitType.DAC_Trigger=DAC_Trigger_T6_TRGO;                        //使用TIM6触发
    DAC_InitType.DAC_WaveGeneration=DAC_WaveGeneration_None;          //不使用波形发生
    DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;//屏蔽、幅值设置
    DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable ;            //DAC1输出缓存关闭 BOFF1=1
    DAC_Init(DAC_Channel_1,&DAC_InitType);
    DAC_DMACmd(DAC_Channel_1,ENABLE);                        
    DAC_Cmd(DAC_Channel_1,ENABLE);             //使能DAC通道1
   
    AudioEN=0; //这里是使能功放
}

//DMA传输开始函数
void Audio_DMA_8K(unsigned char *dat,u16 len)
{
    DMA_Cmd(DMA1_Stream5, DISABLE);                      //关闭DMA传输
    while (DMA_GetCmdStatus(DMA1_Stream5) != DISABLE){}    //确保DMA可以被设置
    DMA1_Stream5->M0AR=(uint32_t)dat;
    DMA_SetCurrDataCounter(DMA1_Stream5,len);          //数据传输量
    DMA_Cmd(DMA1_Stream5, ENABLE);                      //开启DMA传输
}


//DMA 传输过半,传输完成中断 使用时需要开启DMA中断,这里就不贴代码了
void DMA1_Stream5_IRQHandler(void)
{
     if(DMA_GetITStatus(DMA1_Stream5,DMA_IT_TCIF5)!=RESET)
     {
            DMA_ClearITPendingBit(DMA1_Stream5,DMA_IT_TCIF5); //传输完成
            
          if(Inplay.Alllen>Inplay.ReadAddr)
                Inplay.TCIFFg=1;
            
            if(Explay.Alllen>Explay.ReadAddr)
                Explay.TCIFFg=1;
     }
        
     if(DMA_GetITStatus(DMA1_Stream5,DMA_IT_HTIF5)!=RESET) //传输过半
     {
         DMA_ClearITPendingBit(DMA1_Stream5,DMA_IT_HTIF5);
         if(Inplay.Alllen>Inplay.ReadAddr)
             Inplay.HTIFFg=1;
         
         if(Explay.Alllen>Explay.ReadAddr)
             Explay.HTIFFg=1;
     }   
}


//内置Flash存储传输过半
void Interior_Signal_HTIFx(void)
{
    u16 i;
   
    //传输过半
    if(Inplay.HTIFFg==1)
    {
        Inplay.HTIFFg=0;
        
        if(Inplay.SubCNT>mallocLen)
        {
            Inplay.DMAlen=mallocLen;
            Inplay.SubCNT-=mallocLen;
        }
        else
        {
            Inplay.DMAlen=Inplay.SubCNT;
            Inplay.SubCNT-=Inplay.SubCNT;
        }
        
        if(Inplay.bufSel==0)
        {
            memcpy(Audiobuf1,ExData+Inplay.ReadAddr,Inplay.DMAlen);
            for(i=0;i<Inplay.DMAlen;i++)
            {
                if(sysSrtting.volume==MAXVolume)
                    Audiobuf1=0;
                else
                    Audiobuf1/=sysSrtting.volume;
            }
            Inplay.bufSel=1;
        }
        else
        {
            memcpy(Audiobuf0,ExData+Inplay.ReadAddr,Inplay.DMAlen);
            for(i=0;i<Inplay.DMAlen;i++)
            {
                if(sysSrtting.volume==MAXVolume)
                    Audiobuf0=0;
                else
                    Audiobuf0/=sysSrtting.volume;
            }
            Inplay.bufSel=0;
        }
    }
   
    //传输完成
    if(Inplay.TCIFFg==1)
    {
        Inplay.TCIFFg=0;
        
        if(Inplay.bufSel==0)
            Audio_DMA_8K(Audiobuf0,Inplay.DMAlen);
        else
            Audio_DMA_8K(Audiobuf1,Inplay.DMAlen);
        Inplay.ReadAddr+=Inplay.DMAlen;
        
        if(Inplay.ReadAddr>=Inplay.Alllen) //整段音频播放完成
        {
#if Debug
              logDebugCam("Audio play done\r\n");
#endif            
            Inplay.ReadAddr=0;
            Inplay.Alllen=0;
        }
    }
}

//内置Flash音频函数播放索引 需要播放时调用此函数一次并输入索引
void Audioplay_index(u8 index)
{
    u16 i;   
    memset((u8 *)&Inplay,0,sizeof(Play_Flag));
   
    if(sysSrtting.volume==0)
            sysSrtting.volume=1;
   
    DMA_Cmd(DMA1_Stream5, DISABLE);                      //关闭DMA传输
    while (DMA_GetCmdStatus(DMA1_Stream5) != DISABLE){}    //确保DMA可以被设置
   
    switch(index)
  {
     case Person: //播放1号音频
             Audio=(Audio_Info *)AudioPerson; //这里是将WAV文件转成的C语言数组,并设置为const unsigned char数据类型
#if Debug
              logDebugCam("Audio play Person\r\n");
#endif
       break;
     case Car: //播放2号音频
             Audio=(Audio_Info *)AudioCars;
#if Debug
              logDebugCam("Audio play Car\r\n");
#endif
       break;
         case Volume: //播放3号音频            

             Audio=(Audio_Info *)voldata;
             break;
     default:
       break;
  }
   
    Inplay.Alllen=Audio->SubChunck2Size.Vu32;
    Inplay.SubCNT=Audio->SubChunck2Size.Vu32;
    ExData=&Audio->data;
   
    if(Inplay.SubCNT>mallocLen)
    {
        Inplay.DMAlen=mallocLen;
        Inplay.SubCNT-=mallocLen;
    }
    else
    {
        Inplay.DMAlen=Inplay.SubCNT;
        Inplay.SubCNT-=Inplay.SubCNT;
    }
   
    memcpy(Audiobuf0,ExData,Inplay.DMAlen);
    Inplay.ReadAddr+=Inplay.DMAlen;
   
    for(i=0;i<Inplay.DMAlen;i++)
    {
        if(sysSrtting.volume==MAXVolume)
            Audiobuf0=0;
        else
            Audiobuf0/=sysSrtting.volume;
    }
   
    Audio_DMA_8K(Audiobuf0,Inplay.DMAlen);
}

/********************************************************************************************************************************************/
//外部存储Flash传输过半信号
void External_Signal_HTIFx(void)
{   
    u16 i;
   
    //传输过半
    if(Explay.HTIFFg==1)
    {
        Explay.HTIFFg=0;
        
        if(Explay.SubCNT>mallocLen)
        {
            Explay.DMAlen=mallocLen;
            Explay.SubCNT-=mallocLen;
        }
        else
        {
            Explay.DMAlen=Explay.SubCNT;
            Explay.SubCNT-=Explay.SubCNT;
        }
        
        if(Explay.bufSel==0)
        {
            W25QXX_Read(Audiobuf1,Explay.EXAddres+Explay.ReadAddr,Explay.DMAlen);
            for(i=0;i<Explay.DMAlen;i++)
            {
                if(sysSrtting.volume==MAXVolume)
                    Audiobuf1=0;
                else
                    Audiobuf1/=sysSrtting.volume;
            }
            Explay.bufSel=1;
        }
        else
        {
            W25QXX_Read(Audiobuf0,Explay.EXAddres+Explay.ReadAddr,Explay.DMAlen);
            for(i=0;i<Explay.DMAlen;i++)
            {
                if(sysSrtting.volume==MAXVolume)
                    Audiobuf0=0;
                else
                    Audiobuf0/=sysSrtting.volume;
            }
            Explay.bufSel=0;
        }
    }
   
    //传输完成
    if(Explay.TCIFFg==1)
    {
        Explay.TCIFFg=0;
        
        if(Explay.bufSel==0)
            Audio_DMA_8K(Audiobuf0,Explay.DMAlen);
        else
            Audio_DMA_8K(Audiobuf1,Explay.DMAlen);
        Explay.ReadAddr+=Explay.DMAlen;
        
        if(Explay.ReadAddr>=Explay.Alllen)
        {
            if(Showx[Person].TIMFg==True)
                Showx[Person].TIMFg=Done;
            if(Showx[Car].TIMFg==True)
                Showx[Car].TIMFg=Done;
            Explay.ReadAddr=0;
            Explay.Alllen=0;
        }
    }
}


//自定义音频信号开始传输
void Person_Audio_Udefined(void)
{   
    u16 i;
   
    memset((u8 *)&Explay,0,sizeof(Play_Flag));
   
    if(sysSrtting.volume==0)
            sysSrtting.volume=1;
   
    DMA_Cmd(DMA1_Stream5, DISABLE);                      //关闭DMA传输
    while (DMA_GetCmdStatus(DMA1_Stream5) != DISABLE){}    //确保DMA可以被设置
   
    Explay.EXAddres=AddrAudioPerson;  //外部Flash 音频基地址   
    W25QXX_Read(Audiobuf0,Explay.EXAddres,44);
   
    Audio=(Audio_Info *)Audiobuf0;
   
    Explay.Alllen=Audio->SubChunck2Size.Vu32;    //获取音频有效数据总长
    Explay.SubCNT=Audio->SubChunck2Size.Vu32; //获取音频有效数据总长   
    if(Explay.SubCNT>mallocLen)
    {
        Explay.DMAlen=mallocLen;
        Explay.SubCNT-=mallocLen;
    }
    else
    {
        Explay.DMAlen=Explay.SubCNT;
        Explay.SubCNT-=Explay.SubCNT;
    }
    Explay.EXAddres+=44;   
    W25QXX_Read(Audiobuf0,Explay.EXAddres+Explay.ReadAddr,Explay.DMAlen);
    Explay.ReadAddr+=Explay.DMAlen;
     
    for(i=0;i<Explay.DMAlen;i++)  //设置音量
    {
        if(sysSrtting.volume==MAXVolume)
            Audiobuf0=0;
        else
            Audiobuf0/=sysSrtting.volume;
    }
   
    Audio_DMA_8K(Audiobuf0,Explay.DMAlen); //开启传输
}


//自定义音频信号开始传输
void Car_Audio_Udefined(void)
{
    u16 i;
   
    Explay.Alllen=0;
    Explay.bufSel=0;
    Explay.DMAlen=0;
    Explay.ReadAddr=0;
   
    if(sysSrtting.volume==0)
            sysSrtting.volume=1;
   
    DMA_Cmd(DMA1_Stream5, DISABLE);                      //关闭DMA传输
    while (DMA_GetCmdStatus(DMA1_Stream5) != DISABLE){}    //确保DMA可以被设置
        
    Explay.EXAddres=AddrAudioCar; //外部Flash 音频基地址
   
    W25QXX_Read(Audiobuf0,Explay.EXAddres,44);
   
    Audio=(Audio_Info *)Audiobuf0;
   
    Explay.Alllen=Audio->SubChunck2Size.Vu32;
    Explay.SubCNT=Audio->SubChunck2Size.Vu32;
   
    if(Explay.SubCNT>mallocLen)
    {
        Explay.DMAlen=mallocLen;
        Explay.SubCNT-=mallocLen;
    }
    else
    {
        Explay.DMAlen=Explay.SubCNT;
        Explay.SubCNT-=Explay.SubCNT;
    }
    Explay.EXAddres+=44;   
    W25QXX_Read(Audiobuf0,Explay.EXAddres+Explay.ReadAddr,Explay.DMAlen);
    Explay.ReadAddr+=Explay.DMAlen;
   
    for(i=0;i<Explay.DMAlen;i++) //设置音量
    {
        if(sysSrtting.volume==MAXVolume)
            Audiobuf0=0;
        else
            Audiobuf0/=sysSrtting.volume;
    }
   
    Audio_DMA_8K(Audiobuf0,Explay.DMAlen); //开启传输
}

/*************************************************************************************分隔符*************************************************************************************************************/
//Audio.h 头文件
#ifndef __AUDIO_H
#define __AUDIO_H


#include "system.h"

typedef union
{
    unsigned char Vu8[4];
    unsigned int  Vu32;
}Byte_Word;


//WAV 文件数据头信息为文件开头0-43字节的数据
typedef struct
{
        Byte_Word ChunkID;    //'RIFF' (0x52494646)  大端
        Byte_Word ChunkSize;  //fileSize - 8
        Byte_Word Format;     //'WAVE'(0x57415645)   大端
     
        Byte_Word SchunkID;       //'fmt ' (0x666D7420)  大端
        Byte_Word SchunkSize;     //16
    unsigned char Aformat[2];     //音频格式
    unsigned char Channels[2];    //声道数
        Byte_Word SampleRate;     //采样率
        Byte_Word ByteRate;       //每秒数据字节数
    unsigned char BlockAlign[2];  //数据块对齐
    unsigned char BitsPSample[2]; //采样位数   
   
        Byte_Word SubChunck2ID;   //'data' (0x64617461) 大端
        Byte_Word SubChunck2Size; //有效数据长度
    unsigned char data;           //音频数据     
}Audio_Info;

//数据播放期间需要的一些参数
typedef struct
{
    u32 Alllen;  //数据总长
    u32 ReadAddr; //读取地址
    u32 EXAddres; //基地址外部地址
    u32 SubCNT;  //剩余长度
    u32 DMAlen;  //发送长度
    u8  bufSel;  //缓存选择 buf0 或buf1
    u8  TCIFFg;  //传输完成
    u8  HTIFFg;  //传输过半
}Play_Flag;



#define mallocLen 4096  //双缓冲数据长度 单位:字节

#define MAXVolume 30 //最大音量级数

extern Audio_Info *Audio;

//DAC初始化
void Audio_DAC_Init(void);
//DAC播放索引,运行一次自动播放
void Audioplay_index(u8 index);


//内置 在main while中运行

void Interior_Signal_HTIFx(void);
//外置 在main while中运行
void External_Signal_HTIFx(void);

//自定义音频信号开始传输
void Person_Audio_Udefined(void);
//自定义音频信号开始传输
void Car_Audio_Udefined(void);

//下面是播放示例:
int main(void)
{
/*

你自己的其他初始化函数

*/

//音频初始化函数
Audio_DAC_Init();


Audioplay_index(0);//开始播放索引0的音频文件,只需调用一次

  while(1)
{
        External_Signal_HTIFx();
        Interior_Signal_HTIFx();
  }
}

使用特权

评论回复
沙发
WoodData| | 2022-5-27 17:03 | 只看该作者
不错不错

使用特权

评论回复
板凳
hellosdc| | 2022-5-28 13:06 | 只看该作者
这个兼容stm32f107吗?

使用特权

评论回复
地板
jstgotodo| | 2022-5-28 13:25 | 只看该作者
可以做音频的识别吗

使用特权

评论回复
5
pl202| | 2022-5-28 14:24 | 只看该作者
APM32F407性能怎么样?

使用特权

评论回复
6
myiclife| | 2022-5-28 15:00 | 只看该作者
WAV音频文件如何直接读取的?

使用特权

评论回复
7
dzfansman| | 2022-6-2 10:05 | 只看该作者
这个代码在哪来?

使用特权

评论回复
8
cashrwood| | 2022-6-2 10:14 | 只看该作者
有工程可以参考吗?

使用特权

评论回复
9
phoenixwhite| | 2022-6-2 11:32 | 只看该作者
DMA+DAC可行吗?

使用特权

评论回复
10
wwppd| | 2022-6-2 15:35 | 只看该作者
如何驱动后端的呢?

使用特权

评论回复
11
jflahdink09| | 2022-8-16 11:15 | 只看该作者
最小音量级数30级,这个极限是在多少?

使用特权

评论回复
12
macpherson| | 2022-8-16 20:32 | 只看该作者
代码可以移植吗  

使用特权

评论回复
13
mmbs| | 2022-8-16 20:58 | 只看该作者
如何驱动外部音频播放呢?

使用特权

评论回复
14
maudlu| | 2022-8-16 21:31 | 只看该作者
使用的是外部的存储音频文件吗  

使用特权

评论回复
15
jstgotodo| | 2022-8-17 16:12 | 只看该作者
如何解码wav文件?   

使用特权

评论回复
16
typeof| | 2022-8-18 17:17 | 只看该作者
这个性能怎么样   

使用特权

评论回复
17
iamaiqiyi| | 2022-8-18 18:38 | 只看该作者
使用的是DMA采样?

使用特权

评论回复
18
adolphcocker| | 2022-9-3 17:08 | 只看该作者
可以控制VS1003解码芯片

使用特权

评论回复
19
xietingfeng| | 2022-9-3 17:35 | 只看该作者
用单片机怎么装入.wav文件

使用特权

评论回复
20
jstgotodo| | 2022-9-3 18:15 | 只看该作者
为什么不使用语音模块呢   

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

3

主题

3

帖子

0

粉丝