wch-code/ch32v307_mp3_dac/User/audio/audio_hal.c
2024-05-05 22:27:52 +08:00

204 lines
8.3 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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);
}
}