使用
STM32F4的I2S接口,直接找的
论坛的I2S代码 drv_i2s代码,发现能够正常播放音频,但是播放速度慢,有频率很高的哒哒哒的噪声,求助原因,代码如下:
drv_i2s:
I2S_HandleTypeDef hi2s2;
DMA_HandleTypeDef hdma_spi2_tx;
struct stm32_audio
{
struct rt_i2c_bus_device *i2c_bus;
struct rt_audio_device audio;
struct rt_audio_configure replay_config;
int replay_volume;
rt_uint8_t *tx_fifo;
rt_bool_t startup;
};
struct stm32_audio _stm32_audio_play = {0};
void i2s_samplerate_set(rt_uint32_t freq)
{
if(freq == I2S_AUDIOFREQ_8K || freq == I2S_AUDIOFREQ_11K ||
freq == I2S_AUDIOFREQ_16K || freq == I2S_AUDIOFREQ_22K ||
freq == I2S_AUDIOFREQ_32K || freq == I2S_AUDIOFREQ_44K ||
freq == I2S_AUDIOFREQ_48K || freq == I2S_AUDIOFREQ_96K)
{
hi2s2.Init.AudioFreq = freq;
}
else
{
return;
}
__HAL_I2S_DISABLE(&hi2s2);
HAL_I2S_Init(&hi2s2);
__HAL_I2S_ENABLE(&hi2s2);
}
void i2s_channels_set(rt_uint16_t channels)
{
}
void i2s_samplebits_set(rt_uint16_t samplebits)
{
switch (samplebits)
{
case 16:
hi2s2.Init.DataFormat = I2S_DATAFORMAT_16B;
break;
case 24:
hi2s2.Init.DataFormat = I2S_DATAFORMAT_24B;
break;
case 32:
hi2s2.Init.DataFormat = I2S_DATAFORMAT_32B;
break;
default:
hi2s2.Init.DataFormat = I2S_DATAFORMAT_16B;
break;
}
__HAL_I2S_DISABLE(&hi2s2);
HAL_I2S_Init(&hi2s2);
__HAL_I2S_ENABLE(&hi2s2);
}
void i2s_config_set(struct rt_audio_configure config)
{
i2s_channels_set(config.channels);
i2s_samplerate_set(config.samplerate);
i2s_samplebits_set(config.samplebits);
}
rt_err_t I2S_config_init(void)
{
hi2s2.Instance = SPI2;
hi2s2.Init.Mode = I2S_MODE_MASTER_TX;
hi2s2.Init.Standard = I2S_STANDARD_PHILIPS;
hi2s2.Init.DataFormat = I2S_DATAFORMAT_16B;
hi2s2.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE;
hi2s2.Init.AudioFreq = I2S_AUDIOFREQ_8K;
hi2s2.Init.CPOL = I2S_CPOL_LOW;
hi2s2.Init.ClockSource = I2S_CLOCK_PLL;
hi2s2.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_ENABLE;
if (HAL_I2S_Init(&hi2s2) != HAL_OK)
{
rt_kprintf("HAL_I2S_Init error\n");
}
SET_BIT(hi2s2.Instance->CR2, SPI_CR2_TXDMAEN);
__HAL_I2S_ENABLE(&hi2s2);
return RT_EOK;
}
void I2S_DMAConvCplt(DMA_HandleTypeDef *hdma)
{
rt_audio_tx_complete(&_stm32_audio_play.audio);
}
void I2S_DMAError(DMA_HandleTypeDef *hdma)
{
// rt_kprintf("DMAError\n");
}
void DMA1_Stream4_IRQHandler(void)
{
rt_interrupt_enter();
HAL_DMA_IRQHandler(&hdma_spi2_tx);
rt_interrupt_leave();
}
void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s)
{
rt_kprintf("HAL_I2S_TxHalfCpltCallback pk 1\n");
rt_audio_tx_complete(&_stm32_audio_play.audio);
}
void I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s)
{
rt_audio_tx_complete(&_stm32_audio_play.audio);
}
void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s)
{
rt_kprintf("HAL_I2S_TxCpltCallback pk 2\n");
rt_audio_tx_complete(&_stm32_audio_play.audio);
}
rt_err_t I2S_tx_dma(void)
{
__HAL_DMA_ENABLE_IT(&hdma_spi2_tx,DMA_IT_TC);//开启DMA传输完成中断
__HAL_DMA_ENABLE_IT(&hdma_spi2_tx, DMA_IT_HT); //开启DMA半传输传输完成中断
__HAL_DMA_CLEAR_FLAG(&hdma_spi2_tx, DMA_FLAG_TCIF0_4);
//注册回调函数,读取数据等操作在这里面处理
hdma_spi2_tx.XferCpltCallback = I2S_DMAConvCplt;
hdma_spi2_tx.XferM1CpltCallback = I2S_DMAConvCplt;
hdma_spi2_tx.XferErrorCallback = I2S_DMAError;
hdma_spi2_tx.XferHalfCpltCallback=I2S_TxHalfCpltCallback;
HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn);
return RT_EOK;
}
rt_err_t i2s_tx_init()
{
/* set I2S_TX DMA */
I2S_config_init();
I2S_tx_dma();
return RT_EOK;
}
sta
tic rt_err_t stm32_player_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
rt_err_t result = RT_EOK;
struct stm32_audio *st_audio = (struct stm32_audio *)audio->parent.user_data;
LOG_D("%s:main_type: %d, sub_type: %d", __FUNCTION__, caps->main_type, caps->sub_type);
switch (caps->main_type)
{
case AUDIO_TYPE_QUERY: /* query the types of hw_codec device */
{
switch (caps->sub_type)
{
case AUDIO_TYPE_QUERY:
caps->udata.mask = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_MIXER;
break;
default:
result = -RT_ERROR;
break;
}
break;
}
case AUDIO_TYPE_OUTPUT: /* Provide capabilities of OUTPUT unit */
{
switch (caps->sub_type)
{
case AUDIO_DSP_PARAM:
caps->udata.config.channels = st_audio->replay_config.channels;
caps->udata.config.samplebits = st_audio->replay_config.samplebits;
caps->udata.config.samplerate = st_audio->replay_config.samplerate;
break;
case AUDIO_DSP_SAMPLERATE:
caps->udata.config.samplerate = st_audio->replay_config.samplerate;
break;
case AUDIO_DSP_CHANNELS:
caps->udata.config.channels = st_audio->replay_config.channels;
break;
case AUDIO_DSP_SAMPLEBITS:
caps->udata.config.samplebits = st_audio->replay_config.samplebits;
break;
default:
result = -RT_ERROR;
break;
}
break;
}
case AUDIO_TYPE_MIXER: /* report the Mixer Units */
{
switch (caps->sub_type)
{
case AUDIO_MIXER_QUERY:
caps->udata.mask = AUDIO_MIXER_VOLUME | AUDIO_MIXER_LINE;
break;
case AUDIO_MIXER_VOLUME:
caps->udata.value = st_audio->replay_volume;
break;
case AUDIO_MIXER_LINE:
break;
default:
result = -RT_ERROR;
break;
}
break;
}
default:
result = -RT_ERROR;
break;
}
return result;
}
static rt_err_t stm32_player_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps)
{
rt_err_t result = RT_EOK;
struct stm32_audio *st_audio = (struct stm32_audio *)audio->parent.user_data;
LOG_D("%s:main_type: %d, sub_type: %d", __FUNCTION__, caps->main_type, caps->sub_type);
switch (caps->main_type)
{
case AUDIO_TYPE_MIXER:
{
switch (caps->sub_type)
{
case AUDIO_MIXER_MUTE:
{
/* set mute mode */
wm8978_mute_enabled(_stm32_audio_play.i2c_bus, RT_FALSE);
break;
}
case AUDIO_MIXER_VOLUME:
{
int volume = caps->udata.value;
st_audio->replay_volume = volume;
/* set mixer volume */
wm8978_set_volume(_stm32_audio_play.i2c_bus, volume);
break;
}
default:
result = -RT_ERROR;
break;
}
break;
}
case AUDIO_TYPE_OUTPUT:
{
switch (caps->sub_type)
{
case AUDIO_DSP_PARAM:
{
struct rt_audio_configure config = caps->udata.config;
st_audio->replay_config.samplerate = config.samplerate;
st_audio->replay_config.samplebits = config.samplebits;
st_audio->replay_config.channels = config.channels;
i2s_config_set(config);
break;
}
case AUDIO_DSP_SAMPLERATE:
{
st_audio->replay_config.samplerate = caps->udata.config.samplerate;
i2s_samplerate_set(caps->udata.config.samplerate);
break;
}
case AUDIO_DSP_CHANNELS:
{
st_audio->replay_config.channels = caps->udata.config.channels;
i2s_channels_set(caps->udata.config.channels);
break;
}
case AUDIO_DSP_SAMPLEBITS:
{
st_audio->replay_config.samplebits = caps->udata.config.samplebits;
i2s_samplebits_set(caps->udata.config.samplebits);
break;
}
default:
result = -RT_ERROR;
break;
}
break;
}
default:
break;
}
return result;
}
static rt_err_t stm32_player_init(struct rt_audio_device *audio)
{
/* initialize wm8978 */
_stm32_audio_play.i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(CODEC_I2C_NAME);
i2s_tx_init();
wm8978_init(_stm32_audio_play.i2c_bus);
return RT_EOK;
}
static rt_err_t stm32_player_start(struct rt_audio_device *audio, int stream)
{
if (stream == AUDIO_STREAM_REPLAY)
{
rt_kprintf("HAL_I2S_Transmit_DMA start\n");
wm8978_player_start(_stm32_audio_play.i2c_bus);
HAL_DMAEx_MultiBufferStart_IT(&hdma_spi2_tx, (uint32_t)_stm32_audio_play.tx_fifo, (uint32_t) & (SPI2->DR), (uint32_t)(_stm32_audio_play.tx_fifo + TX_DMA_FIFO_SIZE / 2), TX_DMA_FIFO_SIZE / 2);
//HAL_I2S_Transmit_DMA(&hi2s2,(uint16_t *)_stm32_audio_play.tx_fifo,TX_DMA_FIFO_SIZE);
}
return RT_EOK;
}
static rt_err_t stm32_player_stop(struct rt_audio_device *audio, int stream)
{
if (stream == AUDIO_STREAM_REPLAY)
{
HAL_I2S_DMAStop(&hi2s2);
rt_kprintf("HAL_I2S_Transmit_DMA stop\n");
}
return RT_EOK;
}
static void stm32_player_buffer_info(struct rt_audio_device *audio, struct rt_audio_buf_info *info)
{
/**
* TX_FIFO
* +----------------+----------------+
* | block1 | block2 |
* +----------------+----------------+
* \ block_size /
*/
info->buffer = _stm32_audio_play.tx_fifo;
info->total_size = TX_DMA_FIFO_SIZE;
info->block_size = TX_DMA_FIFO_SIZE / 2;
info->block_count = 2;
}
static struct rt_audio_ops _p_audio_ops =
{
.getcaps = stm32_player_getcaps,
.configure = stm32_player_configure,
.init = stm32_player_init,
.start = stm32_player_start,
.stop = stm32_player_stop,
.transmit = NULL,
.buffer_info = stm32_player_buffer_info,
};
int rt_hw_sound_init(void)
{
rt_uint8_t *tx_fifo;
/* player */
tx_fifo = rt_malloc(TX_DMA_FIFO_SIZE);
if (tx_fifo == RT_NULL)
{
return -RT_ENOMEM;
}
rt_memset(tx_fifo, 0, TX_DMA_FIFO_SIZE);
_stm32_audio_play.tx_fifo = tx_fifo;
_stm32_audio_play.tx_fifo = tx_fifo;
/* init default configuration */
{
_stm32_audio_play.replay_config.samplerate = 48000;
_stm32_audio_play.replay_config.channels = 1;
_stm32_audio_play.replay_config.samplebits = 16;
_stm32_audio_play.replay_volume = 15;
}
/* register sound device */
_stm32_audio_play.audio.ops = &_p_audio_ops;
rt_audio_register(&_stm32_audio_play.audio, "sound0", RT_DEVICE_FLAG_WRONLY, &_stm32_audio_play);
return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_sound_init);
使用的模拟i2c控制,msp初始化如下:
extern DMA_HandleTypeDef hdma_spi2_tx;
void HAL_I2S_MspInit(I2S_HandleTypeDef *hi2s) {
if (hi2s->Instance == SPI2) {
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2S;
PeriphClkInitStruct.PLLI2S.PLLI2SN = 271;
PeriphClkInitStruct.PLLI2S.PLLI2SR = 2;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}
GPIO_InitTypeDef GPIO_InitStruct;
/* Peripheral clock enable */
__HAL_RCC_SPI2_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOI_CLK_ENABLE();
/**I2S2 GPIO Configuration
PC2 ------> I2S2_ext_SD
PC3 ------> I2S2_SD
PB12 ------> I2S2_WS
PB13 ------> I2S2_CK
PC6 ------> I2S2_MCK
*/
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF6_I2S2ext;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
HAL_GPIO_Init(GPIOI, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
__HAL_RCC_DMA1_CLK_ENABLE();
hdma_spi2_tx.Instance = DMA1_Stream4; //DMA1数据流4
hdma_spi2_tx.Init.Channel = DMA_CHANNEL_0; //通道0
hdma_spi2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; //存储器到外设模式
hdma_spi2_tx.Init.PeriphInc = DMA_PINC_DISABLE; //外设非增量模式
hdma_spi2_tx.Init.MemInc = DMA_MINC_ENABLE; //存储器增量模式
/*todo 这里按照道理应该根据数据位宽来配置 ?*/
hdma_spi2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; //外设数据长度:16位
hdma_spi2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; //存储器数据长度:16位
hdma_spi2_tx.Init.Mode = DMA_CIRCULAR; //使用循环模式
hdma_spi2_tx.Init.Priority = DMA_PRIORITY_VERY_HIGH; //高优先级
hdma_spi2_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; //不使用FIFO
hdma_spi2_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; // 使用FIFO阈值完全配置
hdma_spi2_tx.Init.MemBurst = DMA_MBURST_SINGLE; //存储器单次突发传输
hdma_spi2_tx.Init.PeriphBurst = DMA_MBURST_SINGLE; //外设突发单次传输
if (HAL_DMA_Init(&hdma_spi2_tx) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(hi2s,hdmatx,hdma_spi2_tx);
}
}
其实一开始的播放速度很慢很慢,后面我修改了DMAFIFO的大小,调大了,速度就快了些,但是总的来说还是慢,且噪声非常大
#define TX_DMA_FIFO_SIZE (4096)
#define I2S_SOUND_NAME "sound0"
#define CODEC_I2C_NAME ("i2c1")
卡了很久了,求助