打印
[APM32F0]

APM32F072CBT6进行DAC模拟播放声音(二)

[复制链接]
580|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
继上篇烧录语音bin文件进外部FLASH后,进行WAV文件解析、
以下是解析WAV格式代码
typedef struct WAV_RIFF {
    char ChunkID[4];   /* "RIFF" */
    uint32_t ChunkSize; /* 36 + Subchunk2Size */
    char Format[4];    /* "WAVE" */
} RIFF_t;    //给WAV_RIFF取别名为RIFF_t
typedef struct WAV_FMT {
    /* sub-chunk "fmt" */
    char Subchunk1ID[4];   /* "fmt " */
    /* sub-chunk-size */
    uint32_t Subchunk1Size; /* 16 for PCM */
    /* sub-chunk-data */
    uint16_t AudioFormat;   /* PCM = 1*/
    uint16_t NumChannels;   /* Mono = 1, Stereo = 2, etc. */
uint32_t SampleRate;    /* 8000, 44100, etc. */
    uint32_t ByteRate;  /* = SampleRate * NumChannels * BitsPerSample/8 */
    uint16_t BlockAlign;    /* = NumChannels * BitsPerSample/8 */
    uint16_t BitsPerSample; /* 8bits, 16bits, etc. */
} FMT_t;
typedef struct WAV_data {
    char Subchunk2ID[4];   /* "data" */
    uint32_t Subchunk2Size; /* data size */
} Data_t;    //给WAV_data取别名为Data_t
typedef struct WAV_fotmat {
   RIFF_t riff;
   FMT_t fmt;
   Data_t data;
} Wav;    //给WAV_fotmat取别名为Wav
void Load_Wav_Info(Wav *mywav)
{
    RIFF_t riff;
    FMT_t fmt;
    Data_t data;
    W25qxx_ReadSector((uint8_t *)mywav, 0x000000, 0x00, sizeof(Wav));
    riff = mywav->riff;  //调用结构体,附给riff
    fmt = mywav->fmt;
    data = mywav->data;
    printf("ChunkID \t%c%c%c%c\n", riff.ChunkID[0], riff.ChunkID[1], riff.ChunkID[2], riff.ChunkID[3]);
    printf("ChunkSize \t%d\n", riff.ChunkSize);
    printf("Format \t\t%c%c%c%c\n", riff.Format[0], riff.Format[1], riff.Format[2], riff.Format[3]);
     
    printf("wav size %d\n",sizeof(Wav));
     
    //输出格式子块信息
    printf("Subchunk1ID \t%c%c%c%c\n", fmt.Subchunk1ID[0],         fmt.Subchunk1ID[1], fmt.Subchunk1ID[2], fmt.Subchunk1ID[3]);
    printf("Subchunk1Size \t%d\n", fmt.Subchunk1Size);
    printf("AudioFormat \t%d\n", fmt.AudioFormat);
    printf("NumChannels \t%d\n", fmt.NumChannels);
    printf("SampleRate \t%d\n", fmt.SampleRate);
    printf("ByteRate \t%d\n", fmt.ByteRate);
    printf("BlockAlign \t%d\n", fmt.BlockAlign);
    printf("BitsPerSample \t%d\n", fmt.BitsPerSample);
     
    printf("\n");
    //输出数据块信息
    printf("blockID \t%c%c%c%c\n", data.Subchunk2ID[0],          data.Subchunk2ID[1], data.Subchunk2ID[2], data.Subchunk2ID[3]);
    printf("blockSize \t%d\n", data.Subchunk2Size);
     
    printf("\n");
    //输出wav文件的时长
printf("duration \t%d\n", data.Subchunk2Size / fmt.ByteRate);
}


已经可以识别出该WAV文件时 16bit数据,单通道,22050hz采样率的 格式数据了。总数据长度808480.有效DAC数据 808444个字节,总时间 36秒
硬件方案主要就是选用夕力杰语音功放IC或者富满功放IC,几毛钱。APM32F072CBT6通过DAC输出,进行功放输出。
软件实现流程:
考虑到CPU的效率问题,采用TIMER+DAC DMA的方式输送音频数据
DAC配置





代码流程
Wav wav;  //使用wve.h中的结构体重命名为wav
uint16_t  DMA_BUFF1[1024] = {0};
uint16_t  SPI_BUFF1[1024] = {0};
uint32_t  blockSize = 0;
uint32_t  blocklent = 1024;
uint8_t   start_flag = 2;
uint8_t   end_flag = 0;
uint8_t   get_flag = 0;
uint32_t   bytes_count = 0;
/* USER CODE END 0 */
void GetChanelDate(uint16_t *Date,uint32_t len)
{
  int i = 0;
  int ch1_pos = 0;
  int temp;
for(i= 0; i < len/2; i++)
  {
//    if ((i % 8 == 0) && (i > 2))
//    {
//      printf("\r\n");
//    }
//    printf("0x%04X,", Date);
    temp = Date;
    temp = (temp+0x8000) >> 1;
    Date = temp;
  }
  );
}
/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
   uint32_t  blockAddr = sizeof(Wav);
  /* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
  /* Configure the system clock */
  SystemClock_Config();
/* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_DAC_Init();
  MX_I2C1_Init();
  MX_SPI1_Init();
  MX_SPI2_Init();
  MX_TIM2_Init();
  MX_USART3_UART_Init();
MX_USART4_UART_Init();
  UART_InitDMAReceive();
  /* USER CODE BEGIN 2 */
  printf("hello \n");
   W25qxx_Init();
  Load_Wav_Info(&wav);
  blockSize = wav.data.Subchunk2Size;
  HAL_TIM_Base_Start(&htim2);//?????**
    get_flag = 1;
  /* USER CODE BEGIN WHILE */
  while (1)
  {
/* USER CODE END WHILE */
   
   
   
    if((end_flag == 0) && (get_flag == 1))
    {
      if(start_flag == 0)
      {
        get_flag = 0;
      }
     W25qxx_ReadBytes((uint8_t *)SPI_BUFF1, blockAddr, blocklent);
      bytes_count += blocklent;
GetChanelDate(SPI_BUFF1,blocklent);
      if(start_flag > 0)
      {
  
        if(start_flag == 2)
        {
          memcpy(&DMA_BUFF1[0],SPI_BUFF1,blocklent);
         }
        else if(start_flag == 1)
        {
          memcpy(&DMA_BUFF1[512],SPI_BUFF1,blocklent);
          HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*)DMA_BUFF1,  blocklent, DAC_ALIGN_12B_L);//填充完第一批数据后直接开启DMA传输。  
          get_flag = 0;//停止spi数据刷新缓冲区。等待传输完成中断触发再更新
         
        }
         start_flag -= 1;
      }
      if(blockSize - bytes_count > 0)
      {
        if((blockSize - bytes_count) == (blockSize%1024))
        {
          blocklent = blockSize%1024;
          printf("last lent blocklent %d\n",blocklent);
        }
        blockAddr += 1024;
     }
   
      if(bytes_count == blockSize)
      {
        end_flag = 1;
      }
    }
  }
  /* USER CODE END 3 */
}
void HAL_DAC_ConvHalfCpltCallbackCh1(DAC_HandleTypeDef *hdac)
{
  
   if((blockSize - bytes_count) == (blockSize%1024))
   {
     blocklent = blockSize%1024;
      memcpy(&DMA_BUFF1[0],SPI_BUFF1,blocklent);
     printf("HAL_DAC_ConvHalfCpltCallbackCh1 blocklent%d\n",blocklent);
    }
    else
    {
      memcpy(&DMA_BUFF1[0],SPI_BUFF1,blocklent);
    }
   get_flag = 1;
}
void HAL_DAC_ConvCpltCallbackCh1(DAC_HandleTypeDef *hdac)
{
  
    if((blockSize - bytes_count) == (blockSize%blocklent))
    {
      blocklent = blockSize%blocklent;
      memcpy(&DMA_BUFF1[blocklent/2],SPI_BUFF1,blocklent);
}
    else
    {
      memcpy(&DMA_BUFF1[blocklent/2],SPI_BUFF1,blocklent);
}
//   
    if(end_flag)
    {
      HAL_DAC_Stop_DMA(hdac, DAC_CHANNEL_1);
      printf("HAL_DAC_Stop_DMA \n");
    }
    get_flag = 1;
}
功放波形

使用特权

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

本版积分规则

7

主题

12

帖子

1

粉丝