204 lines
8.3 KiB
C
204 lines
8.3 KiB
C
#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);
|
||
}
|
||
}
|