继上篇烧录语音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; } 功放波形
|