#include "audio_hal.h" #include "debug.h" #include "ff.h" #include "tlsf.h" #include "bsp_led.h" struct { uint32_t sample_count; /* 每个缓冲区的采样数(若存在多声道 则为一个声道的采样数) */ uint32_t sample_rate; /* 音频采样率 */ uint8_t sample_size; /* 单个采样的位数 */ uint8_t channel_count; /* 声道数 */ uint8_t *audio_buffer; /* 用于DMA的音频缓冲区地址 */ uint32_t audio_buffer_length; /* 用于DMA的音频缓冲区的长度,此变量的大小为单个缓冲区的长度 */ volatile uint8_t audio_buffer_status; /* 指示当前可用的缓冲区。0表示缓冲区均不可用,1表示缓冲区前半部分可用,2表示后半部分可用 */ } audio_hal_info; void DMA2_Channel3_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast"))); void audio_hal_init_dac(void) { RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC | RCC_APB1Periph_TIM2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOA, &GPIO_InitStructure); DAC_InitTypeDef DAC_InitStructure; DAC_InitStructure.DAC_Trigger = DAC_Trigger_T2_TRGO; //使用定时器2作为DAC的触发源 DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None; DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0; DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable; //使能输出缓冲器 若不使能则输出无法驱动电阻分压网络 DAC_Init(DAC_Channel_1, &DAC_InitStructure); DAC_Init(DAC_Channel_2, &DAC_InitStructure); DAC_SetDualChannelData(DAC_Align_12b_L, 0x8000, 0x8000); //输出中点电压 DAC_DualSoftwareTriggerCmd(ENABLE); DAC_Cmd(DAC_Channel_1, ENABLE); DAC_Cmd(DAC_Channel_2, ENABLE); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = DMA2_Channel3_IRQn; //使能DAC1中断 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_Init(&NVIC_InitStructure); } /** * @brief 开始播放音频 * * @param[in] sample_count 每个缓冲区的采样数 * @param[in] sample_size 每个采样的位数 * @param[in] sample_rate 音频采样率 * @param[in] channel_count 音频声道数 * * @return audio_hal_result_t * - audio_hal_ok: 成功 * - audio_hal_error_unsupported_parameter: 不支持的音频文件参数,例如采样率过高、采样位数过高等 * - audio_hal_error_insufficient_memory: 内存不足,无法申请DMA缓冲区 * * @note 若有多个声道,则sample_count为单个声道的采样数 */ audio_hal_result_t audio_hal_start(uint32_t sample_count, uint8_t sample_size, uint32_t sample_rate, uint8_t channel_count) { if (sample_count == 0 || sample_count > 1152) { LOG_E("sample count too large(%d)", sample_count); return audio_hal_error_unsupported_parameter; } audio_hal_info.sample_count = sample_count; if (sample_size != 16 || channel_count != 2) { //16位立体声采样 LOG_E("unsupported sample size(%d) or channel count(%d)", sample_size, channel_count); return audio_hal_error_unsupported_parameter; } audio_hal_info.sample_size = sample_size; audio_hal_info.channel_count = channel_count; if (sample_rate > 48000 || sample_rate == 0) { LOG_E("unsupported sample rate: %d", sample_rate); return audio_hal_error_unsupported_parameter; } audio_hal_info.sample_rate = sample_rate; LOG_I("sample_count=%d, sample_size=%d, channel_count=%d, sample_rate=%d", audio_hal_info.sample_count, audio_hal_info.sample_size, audio_hal_info.channel_count, audio_hal_info.sample_rate); /* 申请DAC输出双缓冲区 */ audio_hal_info.audio_buffer_length = audio_hal_info.sample_count * (audio_hal_info.sample_size / 8) * audio_hal_info.channel_count; //单个缓冲区的大小 LOG_D("allocating %d bytes of dma buffer", audio_hal_info.audio_buffer_length * 2); audio_hal_info.audio_buffer = tlsf_malloc(audio_hal_info.audio_buffer_length * 2); //双缓冲 if (audio_hal_info.audio_buffer == NULL) { return audio_hal_error_insufficient_memory; } for (uint32_t i = 0; i < audio_hal_info.audio_buffer_length / sizeof(uint16_t) * 2; i ++) { ((uint16_t*)audio_hal_info.audio_buffer)[i] = 32768; //将音频缓冲区设置为中点电压 } /* 配置用于生成采样率的定时器 */ TIM_TimeBaseInitTypeDef TimeBase_InitStruct; TIM_DeInit(TIM2); //首先反初始化定时器 TimeBase_InitStruct.TIM_Prescaler = 0; TimeBase_InitStruct.TIM_Period = SystemCoreClock / audio_hal_info.sample_rate - 1; //根据采样率计算定时器溢出时间 TimeBase_InitStruct.TIM_ClockDivision = TIM_CKD_DIV1; TimeBase_InitStruct.TIM_CounterMode = TIM_CounterMode_Up; TimeBase_InitStruct.TIM_RepetitionCounter = 0x0000; TIM_TimeBaseInit(TIM2, &TimeBase_InitStruct); //初始化定时器2的时基作为DAC转换触发器 TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); //选择定时器溢出事件为触发源输出 TIM_Cmd(TIM2, ENABLE); //使能定时器2 /* 配置DAC输出 */ DMA_InitTypeDef DMA_InitStruct; DMA_DeInit(DMA2_Channel3); DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&(DAC->LD12BDHR); //左对齐 丢弃低4位数据 DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)audio_hal_info.audio_buffer; DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStruct.DMA_BufferSize = audio_hal_info.sample_count * 2; //双缓冲 DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; DMA_InitStruct.DMA_Priority = DMA_Priority_High; DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA2_Channel3, &DMA_InitStruct); //双通道同步转换时 可选任意DAC通道产生DMA请求 DMA_Cmd(DMA2_Channel3, ENABLE); DMA_ITConfig(DMA2_Channel3, DMA_IT_TC | DMA_IT_HT, ENABLE); //启用DMA传输完成中断与半传输中断 DAC_DMACmd(DAC_Channel_1, ENABLE); //开始播放 return audio_hal_ok; } void *audio_hal_get_free_buffer(void) { if (audio_hal_info.audio_buffer_status != 0) { //获取缓冲区时没有等待 说明当前解码速度较慢 可能出现卡顿 LOG_W("buffer is ready before getting free buffer, playback may get laggy"); } while (audio_hal_info.audio_buffer_status == 0) { //等待当前缓冲区播放完毕 // __WFI(); } void *free_buffer = NULL; if (audio_hal_info.audio_buffer_status == 1) { free_buffer = audio_hal_info.audio_buffer; } else if (audio_hal_info.audio_buffer_status == 2) { free_buffer = audio_hal_info.audio_buffer + audio_hal_info.audio_buffer_length; } audio_hal_info.audio_buffer_status = 0; return free_buffer; } void audio_hal_dac_postprocess(int16_t* buffer) { for(uint16_t i = 0; i < audio_hal_info.sample_count * audio_hal_info.channel_count; i ++) { buffer[i] = buffer[i] + 32768; //转为无符号 } } void audio_hal_pause(void) { DAC_DMACmd(DAC_Channel_1, DISABLE); } void audio_hal_resume(void) { DAC_DMACmd(DAC_Channel_1, ENABLE); } void audio_hal_stop(void) { DAC_DMACmd(DAC_Channel_1, DISABLE); DAC_SetDualChannelData(DAC_Align_12b_L, 0x8000, 0x8000); //输出中点电压 if (audio_hal_info.audio_buffer) { tlsf_free(audio_hal_info.audio_buffer); } memset(&audio_hal_info, 0, sizeof(audio_hal_info)); } void DMA2_Channel3_IRQHandler(void) //DMA中断服务函数 { if(DMA_GetITStatus(DMA2_IT_TC3)) { //传输完成中断 audio_hal_info.audio_buffer_status = 2; bsp_led_set(bsp_led_blue, true); DMA_ClearITPendingBit(DMA2_IT_TC3); } else if(DMA_GetITStatus(DMA2_IT_HT3)) { //半传输中断 audio_hal_info.audio_buffer_status = 1; bsp_led_set(bsp_led_blue, false); DMA_ClearITPendingBit(DMA2_IT_HT3); } }