780 lines
26 KiB
C
780 lines
26 KiB
C
|
#include "audio_hw.h"
|
||
|
#include "audio_common.h"
|
||
|
|
||
|
#include "fr30xx.h"
|
||
|
#include "FreeRTOS.h"
|
||
|
|
||
|
#define AUDIO_HW_PDM_RX_INT_LEVEL 16
|
||
|
#define AUDIO_HW_STORE_FRAME_COUNT 40
|
||
|
|
||
|
struct audio_hw_env_t {
|
||
|
struct co_list hw_list;
|
||
|
};
|
||
|
|
||
|
struct audio_hw_handle_t {
|
||
|
audio_hw_t *audio_hw;
|
||
|
void *hw_handle;
|
||
|
};
|
||
|
|
||
|
static struct audio_hw_env_t audio_hw_env = {0};
|
||
|
static struct audio_hw_handle_t i2s_hw_table[3];
|
||
|
static struct audio_hw_handle_t pdm_hw_table[3];
|
||
|
|
||
|
static struct audio_hw_handle_t codec_hw;
|
||
|
|
||
|
static void i2s_rx_callback(I2S_HandleTypeDef *i2s_handle)
|
||
|
{
|
||
|
uint8_t index;
|
||
|
audio_hw_t *hw;
|
||
|
|
||
|
for (index=0; index<3; index++) {
|
||
|
if (i2s_hw_table[index].hw_handle == i2s_handle) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (index < 3) {
|
||
|
hw = i2s_hw_table[index].audio_hw;
|
||
|
|
||
|
/* store data into internal buffer */
|
||
|
if (hw->channels == AUDIO_CHANNELS_MONO) {
|
||
|
int16_t *pcm = (void *)&hw->pcm[hw->channels * sizeof(int16_t) * hw->wr_ptr];
|
||
|
for (uint32_t i=0; i<I2S_FIFO_HALF_DEPTH;) {
|
||
|
pcm[i++] = i2s_handle->I2Sx->DATA_L;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
uint32_t *pcm = (void *)&hw->pcm[hw->channels * sizeof(int16_t) * hw->wr_ptr];
|
||
|
for (uint32_t i=0; i<I2S_FIFO_HALF_DEPTH;) {
|
||
|
pcm[i++] = i2s_handle->I2Sx->DATA_L;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
hw->wr_ptr += I2S_FIFO_HALF_DEPTH;
|
||
|
if (hw->wr_ptr >= hw->pcm_samples) {
|
||
|
hw->wr_ptr = 0;
|
||
|
}
|
||
|
|
||
|
/* notify receivers new data are available */
|
||
|
audio_hw_output_t *output;
|
||
|
output = (void *)co_list_pick(&hw->output_list);
|
||
|
while (output) {
|
||
|
if (output->handler) {
|
||
|
output->handler(I2S_FIFO_HALF_DEPTH);
|
||
|
}
|
||
|
|
||
|
output = (void *)output->hdr.next;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void i2s_tx_callback(I2S_HandleTypeDef *i2s_handle)
|
||
|
{
|
||
|
uint8_t index;
|
||
|
audio_hw_t *hw;
|
||
|
|
||
|
for (index=0; index<3; index++) {
|
||
|
if (i2s_hw_table[index].hw_handle == i2s_handle) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (index < 3) {
|
||
|
hw = i2s_hw_table[index].audio_hw;
|
||
|
|
||
|
/* request new data to send through I2S */
|
||
|
if (hw->request_handler) {
|
||
|
hw->request_handler(hw->pcm_out, I2S_FIFO_HALF_DEPTH, hw->channels);
|
||
|
}
|
||
|
else {
|
||
|
memset(hw->pcm_out, 0, hw->channels * sizeof(int16_t) * I2S_FIFO_HALF_DEPTH);
|
||
|
}
|
||
|
if (hw->channels == AUDIO_CHANNELS_MONO) {
|
||
|
for (uint32_t i=0; i<I2S_FIFO_HALF_DEPTH;) {
|
||
|
i2s_handle->I2Sx->DATA_L = hw->pcm_out[i++];
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
uint32_t *pcm = (void *)&hw->pcm_out[0];
|
||
|
for (uint32_t i=0; i<I2S_FIFO_HALF_DEPTH;) {
|
||
|
i2s_handle->I2Sx->DATA_L = pcm[i++];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void codec_irq(void)
|
||
|
{
|
||
|
uint32_t status;
|
||
|
audio_hw_t *hw;
|
||
|
|
||
|
status = __CODEC_GET_INT_STATUS();
|
||
|
|
||
|
if (status & (CODEC_INT_ADCFF_L_AFULL | CODEC_INT_ADCFF_L_FULL)) {
|
||
|
// fputc('R', &__stdout);
|
||
|
hw = codec_hw.audio_hw;
|
||
|
|
||
|
/* store data into internal buffer */
|
||
|
if (hw->channels == AUDIO_CHANNELS_MONO) {
|
||
|
int16_t *pcm = (void *)&hw->pcm[hw->channels * sizeof(int16_t) * hw->wr_ptr];
|
||
|
for (uint32_t i=0; i<CODEC_FIFO_HALF_DEPTH;) {
|
||
|
pcm[i++] = CODEC->ADCDataL;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
uint16_t *pcm = (void *)&hw->pcm[hw->channels * sizeof(int16_t) * hw->wr_ptr];
|
||
|
for (uint32_t i=0; i<CODEC_FIFO_HALF_DEPTH;) {
|
||
|
uint16_t data = CODEC->ADCDataL;
|
||
|
*pcm++ = data;
|
||
|
*pcm++ = data;
|
||
|
i++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
hw->wr_ptr += CODEC_FIFO_HALF_DEPTH;
|
||
|
if (hw->wr_ptr >= hw->pcm_samples) {
|
||
|
hw->wr_ptr = 0;
|
||
|
}
|
||
|
|
||
|
/* notify receivers new data are available */
|
||
|
audio_hw_output_t *output;
|
||
|
output = (void *)co_list_pick(&hw->output_list);
|
||
|
while (output) {
|
||
|
if (output->handler) {
|
||
|
output->handler(CODEC_FIFO_HALF_DEPTH);
|
||
|
}
|
||
|
|
||
|
output = (void *)output->hdr.next;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (status & (CODEC_INT_DACFF_L_AEMPTY | CODEC_INT_DACFF_L_EMPTY)) {
|
||
|
// fputc('T', &__stdout);
|
||
|
hw = codec_hw.audio_hw;
|
||
|
|
||
|
/* request new data to send through Codec */
|
||
|
if (hw->request_handler) {
|
||
|
hw->request_handler(hw->pcm_out, CODEC_FIFO_HALF_DEPTH, hw->channels);
|
||
|
}
|
||
|
else {
|
||
|
memset(hw->pcm_out, 0, hw->channels * sizeof(int16_t) * CODEC_FIFO_HALF_DEPTH);
|
||
|
}
|
||
|
|
||
|
#if 1
|
||
|
if (hw->channels == AUDIO_CHANNELS_MONO) {
|
||
|
for (uint32_t i=0; i<CODEC_FIFO_HALF_DEPTH;) {
|
||
|
CODEC->DACLRDataL = hw->pcm_out[i++];
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
uint32_t *pcm = (void *)&hw->pcm_out[0];
|
||
|
for (uint32_t i=0; i<CODEC_FIFO_HALF_DEPTH;) {
|
||
|
CODEC->DACLRDataL = pcm[i++];
|
||
|
}
|
||
|
}
|
||
|
#else
|
||
|
const int16_t sin_pcm[] = {0, 4000, 8000, 12000, 16000, 20000, 24000, 28000, 32000, 28000, 24000, 20000, 16000, 12000, 8000, 4000,
|
||
|
0, -4000, -8000, -12000, -16000, -20000, -24000, -28000, -32000, -28000, -24000, -20000, -16000, -12000, -8000, -4000};
|
||
|
for (uint32_t i=0; i<CODEC_FIFO_HALF_DEPTH;) {
|
||
|
CODEC->DACLRDataL = sin_pcm[i];
|
||
|
CODEC->DACLRDataR = sin_pcm[i++];
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void pdm_rx_callback(PDM_HandleTypeDef *pdm_handle)
|
||
|
{
|
||
|
uint8_t index;
|
||
|
audio_hw_t *hw;
|
||
|
|
||
|
for (index=0; index<3; index++) {
|
||
|
if (pdm_hw_table[index].hw_handle == pdm_handle) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (index < 3) {
|
||
|
hw = pdm_hw_table[index].audio_hw;
|
||
|
|
||
|
/* store data into internal buffer */
|
||
|
pdm_read_data(pdm_handle, (void *)&hw->pcm[hw->channels * sizeof(int16_t) * hw->wr_ptr]);
|
||
|
hw->wr_ptr += AUDIO_HW_PDM_RX_INT_LEVEL;
|
||
|
if (hw->wr_ptr >= hw->pcm_samples) {
|
||
|
hw->wr_ptr = 0;
|
||
|
}
|
||
|
|
||
|
/* notify receivers new data are available */
|
||
|
audio_hw_output_t *output;
|
||
|
output = (void *)co_list_pick(&hw->output_list);
|
||
|
while (output) {
|
||
|
if (output->handler) {
|
||
|
output->handler(AUDIO_HW_PDM_RX_INT_LEVEL);
|
||
|
}
|
||
|
|
||
|
output = (void *)output->hdr.next;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void i2s0_irq(void)
|
||
|
{
|
||
|
// fputc('I', NULL);
|
||
|
i2s_IRQHandler(i2s_hw_table[0].hw_handle);
|
||
|
}
|
||
|
|
||
|
void i2s1_irq(void)
|
||
|
{
|
||
|
i2s_IRQHandler(i2s_hw_table[1].hw_handle);
|
||
|
}
|
||
|
|
||
|
void i2s2_irq(void)
|
||
|
{
|
||
|
i2s_IRQHandler(i2s_hw_table[2].hw_handle);
|
||
|
}
|
||
|
|
||
|
void pdm0_irq(void)
|
||
|
{
|
||
|
pdm_IRQHandler(pdm_hw_table[0].hw_handle);
|
||
|
}
|
||
|
|
||
|
void pdm1_irq(void)
|
||
|
{
|
||
|
pdm_IRQHandler(pdm_hw_table[1].hw_handle);
|
||
|
}
|
||
|
|
||
|
void pdm2_irq(void)
|
||
|
{
|
||
|
pdm_IRQHandler(pdm_hw_table[2].hw_handle);
|
||
|
}
|
||
|
|
||
|
audio_hw_t *audio_hw_create(audio_hw_type_t type, audio_hw_request_pcm_t handler, uint32_t base_addr, audio_hw_dir_t dir, uint32_t sample_rate, uint8_t channels)
|
||
|
{
|
||
|
audio_hw_t *tmp;
|
||
|
bool reject = false;
|
||
|
|
||
|
/* check duplication */
|
||
|
tmp = (void *)co_list_pick(&audio_hw_env.hw_list);
|
||
|
while (tmp) {
|
||
|
if (type == tmp->type) {
|
||
|
reject = true;
|
||
|
}
|
||
|
tmp = (void *)tmp->hdr.next;
|
||
|
}
|
||
|
if (reject) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
tmp = pvPortMalloc(sizeof(audio_hw_t));
|
||
|
if (tmp == NULL) {
|
||
|
goto __err;
|
||
|
}
|
||
|
tmp->type = type;
|
||
|
tmp->dir = dir;
|
||
|
tmp->channels = channels;
|
||
|
tmp->sample_rate = sample_rate;
|
||
|
tmp->base_addr = base_addr;
|
||
|
tmp->hw_handle = NULL;
|
||
|
tmp->request_handler = handler;
|
||
|
tmp->pcm_out = NULL;
|
||
|
tmp->wr_ptr = 0;
|
||
|
co_list_init(&tmp->output_list);
|
||
|
tmp->pcm = NULL;
|
||
|
|
||
|
switch (type) {
|
||
|
case AUDIO_HW_TYPE_I2S:
|
||
|
{
|
||
|
I2S_HandleTypeDef *i2s_handle = pvPortMalloc(sizeof(I2S_HandleTypeDef));
|
||
|
if (i2s_handle == NULL) {
|
||
|
goto __err;
|
||
|
}
|
||
|
tmp->hw_handle = i2s_handle;
|
||
|
|
||
|
if (sample_rate == 44100) {
|
||
|
__SYSTEM_I2S_CLK_SELECT_AUPLL();
|
||
|
__SYSTEM_I2S_CLK_DIV(8);
|
||
|
}
|
||
|
else {
|
||
|
__SYSTEM_I2S_CLK_SELECT_COREH();
|
||
|
__SYSTEM_I2S_CLK_DIV(1);
|
||
|
}
|
||
|
|
||
|
switch (base_addr) {
|
||
|
case I2S0_BASE:
|
||
|
__SYSTEM_I2S0_CLK_ENABLE();
|
||
|
i2s_handle->I2Sx = I2S0;
|
||
|
i2s_hw_table[0].hw_handle = i2s_handle;
|
||
|
i2s_hw_table[0].audio_hw = tmp;
|
||
|
break;
|
||
|
case I2S1_BASE:
|
||
|
__SYSTEM_I2S1_CLK_ENABLE();
|
||
|
i2s_handle->I2Sx = I2S1;
|
||
|
i2s_hw_table[1].hw_handle = i2s_handle;
|
||
|
i2s_hw_table[1].audio_hw = tmp;
|
||
|
break;
|
||
|
case I2S2_BASE:
|
||
|
__SYSTEM_I2S2_CLK_ENABLE();
|
||
|
i2s_handle->I2Sx = I2S2;
|
||
|
i2s_hw_table[2].hw_handle = i2s_handle;
|
||
|
i2s_hw_table[2].audio_hw = tmp;
|
||
|
break;
|
||
|
default:
|
||
|
goto __err;
|
||
|
}
|
||
|
|
||
|
i2s_handle->Init.Mode = I2S_MODE_MASTER;
|
||
|
i2s_handle->Init.Standard = I2S_STANDARD_PHILIPS;
|
||
|
i2s_handle->Init.DataFormat = I2S_DATA_FORMAT_16BIT;
|
||
|
if (sample_rate == 48000) {
|
||
|
i2s_handle->Init.BCLKDIV = 5;
|
||
|
i2s_handle->Init.ChannelLength = 50; /* nearly 42K based on 3M BCLK, just for test */
|
||
|
i2s_handle->Init.AudioFreq = I2S_AUDIOFREQ_48000;
|
||
|
}
|
||
|
else if (sample_rate == 44100) {
|
||
|
i2s_handle->Init.BCLKDIV = 8;
|
||
|
i2s_handle->Init.ChannelLength = 35; /* nearly 42K based on 3M BCLK, just for test */
|
||
|
i2s_handle->Init.AudioFreq = I2S_AUDIOFREQ_44100;
|
||
|
}
|
||
|
else if (sample_rate == 16000) {
|
||
|
i2s_handle->Init.BCLKDIV = 15;
|
||
|
i2s_handle->Init.ChannelLength = 50; /* nearly 42K based on 3M BCLK, just for test */
|
||
|
i2s_handle->Init.AudioFreq = I2S_AUDIOFREQ_16000;
|
||
|
}
|
||
|
else {
|
||
|
goto __err;
|
||
|
}
|
||
|
|
||
|
/* insert new audio hw to list before enable interrupt */
|
||
|
co_list_push_back(&audio_hw_env.hw_list, &tmp->hdr);
|
||
|
|
||
|
/* Initialize and start I2S */
|
||
|
i2s_init(i2s_handle);
|
||
|
if (dir & AUDIO_HW_DIR_IN) {
|
||
|
tmp->pcm_samples = I2S_FIFO_HALF_DEPTH * AUDIO_HW_STORE_FRAME_COUNT;
|
||
|
tmp->pcm = (void *)pvPortMalloc(sizeof(int16_t) * channels * tmp->pcm_samples);
|
||
|
if (tmp->pcm == NULL) {
|
||
|
goto __err;
|
||
|
}
|
||
|
|
||
|
i2s_handle->RxIntCallback = i2s_rx_callback;
|
||
|
i2s_receive_IT(i2s_handle);
|
||
|
}
|
||
|
if (dir & AUDIO_HW_DIR_OUT) {
|
||
|
tmp->pcm_out = (void *)pvPortMalloc(sizeof(int16_t) * channels * I2S_FIFO_HALF_DEPTH);
|
||
|
if (tmp->pcm_out == NULL) {
|
||
|
goto __err;
|
||
|
}
|
||
|
|
||
|
i2s_handle->TxIntCallback = i2s_tx_callback;
|
||
|
i2s_transmit_IT(i2s_handle);
|
||
|
}
|
||
|
|
||
|
switch (base_addr) {
|
||
|
case I2S0_BASE:
|
||
|
NVIC_EnableIRQ(I2S0_IRQn);
|
||
|
break;
|
||
|
case I2S1_BASE:
|
||
|
NVIC_EnableIRQ(I2S1_IRQn);
|
||
|
break;
|
||
|
case I2S2_BASE:
|
||
|
NVIC_EnableIRQ(I2S2_IRQn);
|
||
|
break;
|
||
|
default:
|
||
|
goto __err;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case AUDIO_HW_TYPE_CODEC:
|
||
|
{
|
||
|
codec_hw.hw_handle = pvPortMalloc(sizeof(CODEC_HandleTypeDef));
|
||
|
CODEC_HandleTypeDef *CODEC_h = codec_hw.hw_handle;
|
||
|
codec_hw.audio_hw = tmp;
|
||
|
tmp->hw_handle = CODEC_h;
|
||
|
if (dir & AUDIO_HW_DIR_IN) {
|
||
|
tmp->pcm_samples = CODEC_FIFO_HALF_DEPTH * AUDIO_HW_STORE_FRAME_COUNT;
|
||
|
tmp->pcm = (void *)pvPortMalloc(sizeof(int16_t) * channels * tmp->pcm_samples);
|
||
|
if (tmp->pcm == NULL) {
|
||
|
goto __err;
|
||
|
}
|
||
|
}
|
||
|
if (dir & AUDIO_HW_DIR_OUT) {
|
||
|
tmp->pcm_out = (void *)pvPortMalloc(sizeof(int16_t) * channels * CODEC_FIFO_HALF_DEPTH);
|
||
|
if (tmp->pcm_out == NULL) {
|
||
|
goto __err;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
__SYSTEM_CODEC_CLK_ENABLE();
|
||
|
CODEC->CodecAna0 = 0x00004080;
|
||
|
CODEC->CodecAna1 = 0x47628100;
|
||
|
CODEC->CodecAna2 = 0x000d1FCF;
|
||
|
CODEC->CodecAna3 = 0x04000000;
|
||
|
CODEC_h->Init.Codec_Input_sel = INPUT_SELECT_ADC;
|
||
|
CODEC_h->Init.Codec_Input_Routing_sel = INPUT_ROUTING_ASRC0_GPF0_ALC_ADCFF;
|
||
|
|
||
|
CODEC_h->Init.Codec_Output_sel = OUTPUT_SELECT_DAC;
|
||
|
CODEC_h->Init.Codec_Output_Routing_sel = OUTPUT_ROUTING_DACFFLR_ASRC1_GPF1_DRC;
|
||
|
CODEC_h->Init.ADC_DataFormat = CODEC_FIFO_FORMAT_16BIT;
|
||
|
CODEC_h->Init.DAC_LR_DataFormat = CODEC_FIFO_FORMAT_16BIT;
|
||
|
CODEC_h->Init.DAC_01_DataFormat = CODEC_FIFO_FORMAT_16BIT;
|
||
|
|
||
|
if (dir & AUDIO_HW_DIR_IN) {
|
||
|
CODEC_h->Init.Codec_Input_sel = INPUT_SELECT_ADC;
|
||
|
CODEC_h->InputConfig.ADC_Oversampling_Level = CODEC_OVERSAMPLING_HIGH;
|
||
|
if (sample_rate == 48000) {
|
||
|
CODEC_h->InputConfig.ADC_SampleRate = CODEC_SAMPLE_RATE_48000;
|
||
|
CODEC_h->InputConfig.ADC_ClockSource = CODEC_CLOCK_SOURCE_24M_MODE;
|
||
|
}
|
||
|
else if (sample_rate == 44100) {
|
||
|
CODEC_h->InputConfig.ADC_SampleRate = CODEC_SAMPLE_RATE_44100;
|
||
|
CODEC_h->InputConfig.ADC_ClockSource = CODEC_CLOCK_SOURCE_24M_MODE;
|
||
|
}
|
||
|
else if (sample_rate == 16000) {
|
||
|
CODEC_h->InputConfig.ADC_SampleRate = CODEC_SAMPLE_RATE_16000;
|
||
|
CODEC_h->InputConfig.ADC_ClockSource = CODEC_CLOCK_SOURCE_24M_MODE;
|
||
|
}
|
||
|
else if (sample_rate == 8000) {
|
||
|
CODEC_h->InputConfig.ADC_SampleRate = CODEC_SAMPLE_RATE_8000;
|
||
|
CODEC_h->InputConfig.ADC_ClockSource = CODEC_CLOCK_SOURCE_24M_MODE;
|
||
|
}
|
||
|
else {
|
||
|
goto __err;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
CODEC_h->Init.Codec_Input_sel = INPUT_SELECT_BYPASS;
|
||
|
}
|
||
|
|
||
|
if (dir & AUDIO_HW_DIR_OUT) {
|
||
|
CODEC_h->Init.Codec_Output_sel = OUTPUT_SELECT_DAC;
|
||
|
CODEC_h->OutputConfig.DAC_Oversampling_Level = CODEC_OVERSAMPLING_HIGH;
|
||
|
if (sample_rate == 48000) {
|
||
|
CODEC_h->OutputConfig.DAC_SampleRate = CODEC_SAMPLE_RATE_48000;
|
||
|
CODEC_h->OutputConfig.DAC_ClockSource = CODEC_CLOCK_SOURCE_24M_MODE;
|
||
|
}
|
||
|
else if (sample_rate == 44100) {
|
||
|
CODEC_h->OutputConfig.DAC_SampleRate = CODEC_SAMPLE_RATE_44100;
|
||
|
CODEC_h->OutputConfig.DAC_ClockSource = CODEC_CLOCK_SOURCE_24M_MODE;
|
||
|
}
|
||
|
else if (sample_rate == 16000) {
|
||
|
CODEC_h->OutputConfig.DAC_SampleRate = CODEC_SAMPLE_RATE_16000;
|
||
|
CODEC_h->OutputConfig.DAC_ClockSource = CODEC_CLOCK_SOURCE_24M_MODE;
|
||
|
}
|
||
|
else if (sample_rate == 8000) {
|
||
|
CODEC_h->OutputConfig.DAC_SampleRate = CODEC_SAMPLE_RATE_8000;
|
||
|
CODEC_h->OutputConfig.DAC_ClockSource = CODEC_CLOCK_SOURCE_24M_MODE;
|
||
|
}
|
||
|
else {
|
||
|
goto __err;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
CODEC_h->Init.Codec_Output_sel = OUTPUT_SELECT_BYPASS;
|
||
|
}
|
||
|
|
||
|
codec_init(CODEC_h);
|
||
|
__CODEC_ADCFF_L_FULL_THRESHOLD(32);
|
||
|
__CODEC_ADCFF_R_FULL_THRESHOLD(32);
|
||
|
if (dir & AUDIO_HW_DIR_IN) {
|
||
|
codec_int_enable(CODEC_INT_ADCFF_L_FULL | CODEC_INT_ADCFF_L_AFULL);
|
||
|
}
|
||
|
if (dir & AUDIO_HW_DIR_OUT) {
|
||
|
codec_int_enable(CODEC_INT_DACFF_L_EMPTY | CODEC_INT_DACFF_L_AEMPTY);
|
||
|
}
|
||
|
codec_Set_ADC_Volume(0x8000);
|
||
|
codec_Set_DAC_Volume(0x0800);
|
||
|
codec_mute_output(1);
|
||
|
|
||
|
/* insert new audio hw to list before enable interrupt */
|
||
|
co_list_push_back(&audio_hw_env.hw_list, &tmp->hdr);
|
||
|
|
||
|
NVIC_EnableIRQ(CODEC_IRQn);
|
||
|
}
|
||
|
break;
|
||
|
case AUDIO_HW_TYPE_PDM:
|
||
|
{
|
||
|
if (dir & AUDIO_HW_DIR_INOUT) {
|
||
|
goto __err;
|
||
|
}
|
||
|
|
||
|
PDM_HandleTypeDef *pdm_handle = pvPortMalloc(sizeof(PDM_HandleTypeDef));
|
||
|
if (pdm_handle == NULL) {
|
||
|
goto __err;
|
||
|
}
|
||
|
tmp->hw_handle = pdm_handle;
|
||
|
tmp->pcm_samples = AUDIO_HW_PDM_RX_INT_LEVEL * AUDIO_HW_STORE_FRAME_COUNT;
|
||
|
tmp->pcm = (void *)pvPortMalloc(sizeof(int16_t) * channels * tmp->pcm_samples);
|
||
|
if (tmp->pcm == NULL) {
|
||
|
goto __err;
|
||
|
}
|
||
|
|
||
|
switch (base_addr) {
|
||
|
case PDM0_BASE:
|
||
|
__SYSTEM_PDM0_CLK_ENABLE();
|
||
|
pdm_handle->PDMx = PDM0;
|
||
|
pdm_hw_table[0].hw_handle = pdm_handle;
|
||
|
pdm_hw_table[0].audio_hw = tmp;
|
||
|
break;
|
||
|
case PDM1_BASE:
|
||
|
__SYSTEM_PDM1_CLK_ENABLE();
|
||
|
pdm_handle->PDMx = PDM1;
|
||
|
pdm_hw_table[1].hw_handle = pdm_handle;
|
||
|
pdm_hw_table[1].audio_hw = tmp;
|
||
|
break;
|
||
|
case PDM2_BASE:
|
||
|
__SYSTEM_PDM2_CLK_ENABLE();
|
||
|
pdm_handle->PDMx = PDM2;
|
||
|
pdm_hw_table[2].hw_handle = pdm_handle;
|
||
|
pdm_hw_table[2].audio_hw = tmp;
|
||
|
break;
|
||
|
default:
|
||
|
goto __err;
|
||
|
}
|
||
|
|
||
|
if (sample_rate == 48000) {
|
||
|
pdm_handle->Init.SampleRate = PDM_SAMPLE_RATE_48000;
|
||
|
}
|
||
|
else if (sample_rate == 44100) {
|
||
|
pdm_handle->Init.SampleRate = PDM_SAMPLE_RATE_44100;
|
||
|
}
|
||
|
else if (sample_rate == 16000) {
|
||
|
pdm_handle->Init.SampleRate = PDM_SAMPLE_RATE_16000;
|
||
|
}
|
||
|
else {
|
||
|
goto __err;
|
||
|
}
|
||
|
pdm_handle->Init.OverSampleMode = PDM_OSM_0;
|
||
|
if (channels == AUDIO_CHANNELS_MONO) {
|
||
|
pdm_handle->Init.ChannelMode = PDM_MONO_LEFT;
|
||
|
}
|
||
|
else if (channels == AUDIO_CHANNELS_STEREO) {
|
||
|
pdm_handle->Init.ChannelMode = PDM_STEREO;
|
||
|
}
|
||
|
else {
|
||
|
goto __err;
|
||
|
}
|
||
|
pdm_handle->Init.Volume = 0;
|
||
|
pdm_handle->Init.FIFO_FullThreshold = AUDIO_HW_PDM_RX_INT_LEVEL;
|
||
|
|
||
|
pdm_handle->p_RxData = NULL;
|
||
|
pdm_handle->RxCallback = pdm_rx_callback;
|
||
|
|
||
|
/* insert new audio hw to list before enable interrupt */
|
||
|
co_list_push_back(&audio_hw_env.hw_list, &tmp->hdr);
|
||
|
|
||
|
pdm_init(pdm_handle);
|
||
|
pdm_start_IT(pdm_handle, NULL);
|
||
|
switch (base_addr) {
|
||
|
case PDM0_BASE:
|
||
|
NVIC_EnableIRQ(PDM0_IRQn);
|
||
|
break;
|
||
|
case PDM1_BASE:
|
||
|
NVIC_EnableIRQ(PDM1_IRQn);
|
||
|
break;
|
||
|
case PDM2_BASE:
|
||
|
NVIC_EnableIRQ(PDM2_IRQn);
|
||
|
break;
|
||
|
default:
|
||
|
goto __err;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
goto __err;
|
||
|
}
|
||
|
|
||
|
return tmp;
|
||
|
|
||
|
__err:
|
||
|
if (tmp->hw_handle) {
|
||
|
vPortFree(tmp->hw_handle);
|
||
|
}
|
||
|
if (tmp->pcm) {
|
||
|
vPortFree(tmp->pcm);
|
||
|
}
|
||
|
if (tmp->pcm_out) {
|
||
|
vPortFree(tmp->pcm_out);
|
||
|
}
|
||
|
co_list_extract(&audio_hw_env.hw_list, &tmp->hdr);
|
||
|
vPortFree(tmp);
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
void audio_hw_destroy(audio_hw_t *hw)
|
||
|
{
|
||
|
if (hw == NULL) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
GLOBAL_INT_DISABLE();
|
||
|
if (co_list_extract(&audio_hw_env.hw_list, &hw->hdr)) {
|
||
|
switch (hw->type) {
|
||
|
case AUDIO_HW_TYPE_I2S:
|
||
|
i2s_deinit(hw->hw_handle);
|
||
|
switch (hw->base_addr) {
|
||
|
case I2S0_BASE:
|
||
|
__SYSTEM_I2S0_CLK_DISABLE();
|
||
|
NVIC_DisableIRQ(I2S0_IRQn);
|
||
|
break;
|
||
|
case I2S1_BASE:
|
||
|
__SYSTEM_I2S1_CLK_DISABLE();
|
||
|
NVIC_DisableIRQ(I2S1_IRQn);
|
||
|
break;
|
||
|
case I2S2_BASE:
|
||
|
__SYSTEM_I2S2_CLK_DISABLE();
|
||
|
NVIC_DisableIRQ(I2S2_IRQn);
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
case AUDIO_HW_TYPE_CODEC:
|
||
|
codec_deinit(hw->hw_handle);
|
||
|
__SYSTEM_CODEC_CLK_DISABLE();
|
||
|
NVIC_DisableIRQ(CODEC_IRQn);
|
||
|
break;
|
||
|
case AUDIO_HW_TYPE_PDM:
|
||
|
pdm_stop(hw->hw_handle);
|
||
|
switch (hw->base_addr) {
|
||
|
case PDM0_BASE:
|
||
|
__SYSTEM_PDM0_CLK_DISABLE();
|
||
|
NVIC_DisableIRQ(PDM0_IRQn);
|
||
|
break;
|
||
|
case PDM1_BASE:
|
||
|
__SYSTEM_PDM1_CLK_DISABLE();
|
||
|
NVIC_DisableIRQ(PDM1_IRQn);
|
||
|
break;
|
||
|
case PDM2_BASE:
|
||
|
__SYSTEM_PDM2_CLK_DISABLE();
|
||
|
NVIC_DisableIRQ(PDM2_IRQn);
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
GLOBAL_INT_RESTORE();
|
||
|
|
||
|
audio_hw_output_t *output;
|
||
|
output = (void *)co_list_pop_front(&hw->output_list);
|
||
|
while (output) {
|
||
|
vPortFree(output);
|
||
|
output = (void *)co_list_pop_front(&hw->output_list);
|
||
|
}
|
||
|
|
||
|
if (hw->hw_handle) {
|
||
|
vPortFree(hw->hw_handle);
|
||
|
}
|
||
|
if (hw->pcm) {
|
||
|
vPortFree(hw->pcm);
|
||
|
}
|
||
|
if (hw->pcm_out) {
|
||
|
vPortFree(hw->pcm_out);
|
||
|
}
|
||
|
vPortFree(hw);
|
||
|
}
|
||
|
|
||
|
audio_hw_output_t *audio_hw_output_add(audio_hw_t *hw, audio_hw_receive_pcm_ntf_t handler)
|
||
|
{
|
||
|
audio_hw_output_t *output;
|
||
|
|
||
|
if (hw == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
output = pvPortMalloc(sizeof(audio_hw_output_t));
|
||
|
if (output) {
|
||
|
GLOBAL_INT_DISABLE();
|
||
|
output->audio_hw = hw;
|
||
|
output->handler = handler;
|
||
|
output->rd_ptr = hw->wr_ptr;
|
||
|
co_list_push_back(&hw->output_list, &output->hdr);
|
||
|
GLOBAL_INT_RESTORE();
|
||
|
}
|
||
|
|
||
|
return output;
|
||
|
}
|
||
|
|
||
|
void audio_hw_output_remove(audio_hw_t *hw, audio_hw_output_t *output)
|
||
|
{
|
||
|
if (hw == NULL) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
GLOBAL_INT_DISABLE();
|
||
|
co_list_extract(&hw->output_list, &output->hdr);
|
||
|
GLOBAL_INT_RESTORE();
|
||
|
|
||
|
vPortFree(output);
|
||
|
}
|
||
|
|
||
|
static void *copy_pcm(audio_hw_output_t *output, void *pcm, uint32_t samples, uint8_t channels)
|
||
|
{
|
||
|
audio_hw_t *hw = output->audio_hw;
|
||
|
|
||
|
if (channels == AUDIO_CHANNELS_MONO) {
|
||
|
if (hw->channels == AUDIO_CHANNELS_MONO) {
|
||
|
int16_t *src = (int16_t *)hw->pcm + output->rd_ptr;
|
||
|
int16_t *dst = pcm;
|
||
|
for (uint32_t i=0; i<samples; i++) {
|
||
|
*dst++ = *src++;
|
||
|
}
|
||
|
pcm = dst;
|
||
|
}
|
||
|
else {
|
||
|
int16_t *src = (int16_t *)hw->pcm + (output->rd_ptr << 1);
|
||
|
int16_t *dst = pcm;
|
||
|
for (uint32_t i=0; i<samples; i++) {
|
||
|
*dst++ = *src;
|
||
|
src += 2;
|
||
|
}
|
||
|
pcm = dst;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if (hw->channels == AUDIO_CHANNELS_MONO) {
|
||
|
int16_t *src = (int16_t *)hw->pcm + output->rd_ptr;
|
||
|
int16_t *dst = pcm;
|
||
|
for (uint32_t i=0; i<samples; i++) {
|
||
|
*dst++ = *src;
|
||
|
*dst++ = *src++;
|
||
|
}
|
||
|
pcm = dst;
|
||
|
}
|
||
|
else {
|
||
|
uint32_t *src = (uint32_t *)hw->pcm + output->rd_ptr;
|
||
|
uint32_t *dst = pcm;
|
||
|
for (uint32_t i=0; i<samples; i++) {
|
||
|
*dst++ = *src++;
|
||
|
}
|
||
|
pcm = dst;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return pcm;
|
||
|
}
|
||
|
|
||
|
uint32_t audio_hw_read_pcm(audio_hw_output_t *output, void *pcm, uint32_t samples, uint8_t channels)
|
||
|
{
|
||
|
audio_hw_t *hw = output->audio_hw;
|
||
|
uint32_t tail_samples;
|
||
|
|
||
|
tail_samples = hw->pcm_samples - output->rd_ptr;
|
||
|
if (tail_samples <= samples) {
|
||
|
pcm = copy_pcm(output, pcm, tail_samples, channels);
|
||
|
output->rd_ptr = 0;
|
||
|
samples -= tail_samples;
|
||
|
}
|
||
|
if (samples) {
|
||
|
pcm = copy_pcm(output, pcm, samples, channels);
|
||
|
output->rd_ptr += samples;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|