LQM568 发表于 2022-5-27 16:28

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

本程序为应用级,可以根据实际情况修改后用到你的项目中,本代码例程是运用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={0},Audiobuf1={0};


//DAC 配置初始化
void Audio_DAC_Init(void)
{            
    DMA_InitTypeDefDMA_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
DAC_InitTypeDefDAC_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.TIMFg==True)
                Showx.TIMFg=Done;
            if(Showx.TIMFg==True)
                Showx.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;
    unsigned intVu32;
}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;   //音频格式
    unsigned char Channels;    //声道数
      Byte_Word SampleRate;   //采样率
      Byte_Word ByteRate;       //每秒数据字节数
    unsigned char BlockAlign;//数据块对齐
    unsigned char BitsPSample; //采样位数   
   
      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;//发送长度
    u8bufSel;//缓存选择 buf0 或buf1
    u8TCIFFg;//传输完成
    u8HTIFFg;//传输过半
}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

可以做音频的识别吗

pl202 发表于 2022-5-28 14:24

APM32F407性能怎么样?

myiclife 发表于 2022-5-28 15:00

WAV音频文件如何直接读取的?

dzfansman 发表于 2022-6-2 10:05

这个代码在哪来?

cashrwood 发表于 2022-6-2 10:14

有工程可以参考吗?

phoenixwhite 发表于 2022-6-2 11:32

DMA+DAC可行吗?

wwppd 发表于 2022-6-2 15:35

如何驱动后端的呢?

jflahdink09 发表于 2022-8-16 11:15

最小音量级数30级,这个极限是在多少?

macpherson 发表于 2022-8-16 20:32

代码可以移植吗

mmbs 发表于 2022-8-16 20:58

如何驱动外部音频播放呢?

maudlu 发表于 2022-8-16 21:31

使用的是外部的存储音频文件吗

jstgotodo 发表于 2022-8-17 16:12

如何解码wav文件?   

typeof 发表于 2022-8-18 17:17

这个性能怎么样   

iamaiqiyi 发表于 2022-8-18 18:38

使用的是DMA采样?

adolphcocker 发表于 2022-9-3 17:08

可以控制VS1003解码芯片

xietingfeng 发表于 2022-9-3 17:35

用单片机怎么装入.wav文件

jstgotodo 发表于 2022-9-3 18:15

为什么不使用语音模块呢   
页: [1] 2
查看完整版本: APM32F407VET6 -> DMA+DAC播放WAV音频文件