Skip to main content
leonardo
Associate III
October 5, 2016
Question

How to properly change I2S clock.

  • October 5, 2016
  • 3 replies
  • 1213 views
Posted on October 06, 2016 at 00:37

Hi, I'm using a STM32F411 on a product that uses I2S to stream some sound to an audio codec.

I use this function to configure oscillator:

void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;
__PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 6;
RCC_OscInitStruct.PLL.PLLN = 168;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
RCC_OscInitStruct.PLL.PLLQ = 7;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
//22050 Hz original del final_09_DPOAE_05_Apexar
// PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2S;
// PeriphClkInitStruct.PLLI2S.PLLI2SN = 192;
// PeriphClkInitStruct.PLLI2S.PLLI2SM = 12;
// PeriphClkInitStruct.PLLI2S.PLLI2SR = 2;
// HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
//44100Hz
// PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2S;
// PeriphClkInitStruct.PLLI2S.PLLI2SN = 271;
// PeriphClkInitStruct.PLLI2S.PLLI2SM = 12;
// PeriphClkInitStruct.PLLI2S.PLLI2SR = 2;
// HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
/*
* Esta configuracion es para tener buen % trabajando a 22KHz
*/
// PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2S;
// PeriphClkInitStruct.PLLI2S.PLLI2SN = 429;
// PeriphClkInitStruct.PLLI2S.PLLI2SM = 12;
// PeriphClkInitStruct.PLLI2S.PLLI2SR = 4;
// HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
/*
* Esta configuracion es para tener buen % trabajando a 96KHz
*/
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2S;
PeriphClkInitStruct.PLLI2S.PLLI2SN = 344;
PeriphClkInitStruct.PLLI2S.PLLI2SM = 12;
PeriphClkInitStruct.PLLI2S.PLLI2SR = 2;
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* SysTick_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}

As you can see I have 4 diferent blocks on I2S clock. I need to use 2 of them on my firmware. I need to know how can I change just I2S clock frecuency. Could I write just this:

PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2S;
PeriphClkInitStruct.PLLI2S.PLLI2SN = 344;
PeriphClkInitStruct.PLLI2S.PLLI2SM = 12;
PeriphClkInitStruct.PLLI2S.PLLI2SR = 2;
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);

on any part of my code and it will change I2S clock? Thank
    This topic has been closed for replies.

    3 replies

    slimen
    Visitor II
    October 11, 2016
    Posted on October 11, 2016 at 12:42

    Hello,

    Review I2S working example under STM32CubeF4, this may help you to develop your project:

    STM32Cube_FW_F4_V1.13.0\Projects\STM324xG_EVAL\Examples\I2S\I2S_Audio

    Regards

    leonardo
    leonardoAuthor
    Associate III
    October 17, 2016
    Posted on October 18, 2016 at 01:09

    I saw the example code and it is a good starting point.

    But it is not clear to me the correct way to properly do this:
    1. Configure I2S peripheral for working with 22050 Hz
    2. Configure DMA channels
    3. Configure AudioCodec
    4. Start I2S stream using DMA (full duplex)
    5. Stop I2S stream
    6. Re Configure I2S peripheral for working with 22050 Hz
    7. re Configure DMA Channels
    8. re Configure Audio Codec
    9. Start I2S stream using DMA (F D )
    10. Stop I2S

    I'm using CubeMx generated code for some parts of that flowchart.

    So, for point 1 I have:

    /* I2S2 init function */
    void MX_I2S2_Init(uint32_t SampleFreq)
    {
     hi2s2.State = HAL_I2C_STATE_RESET; // To be sure that HAL_I2S_MspInit is executed on point 6 
    hi2s2.Instance = SPI2;
    hi2s2.Init.Mode = I2S_MODE_MASTER_TX;
    hi2s2.Init.Standard = I2S_STANDARD_PHILLIPS;
    hi2s2.Init.DataFormat = I2S_DATAFORMAT_24B;
    hi2s2.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE;
    hi2s2.Init.AudioFreq = SampleFreq;
    hi2s2.Init.CPOL = I2S_CPOL_LOW;
    hi2s2.Init.ClockSource = I2S_CLOCK_PLL;
    hi2s2.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_ENABLE;
    HAL_I2S_Init(&hi2s2);
    }

    HAL_I2S_MspInit has part of point 2 and is called by HAL_I2S_Init

    void HAL_I2S_MspInit(I2S_HandleTypeDef* hi2s)
    {
    GPIO_InitTypeDef GPIO_InitStruct;
    if(hi2s->Instance==SPI2)
    {
    /* Peripheral clock enable */
    __SPI2_CLK_ENABLE();
    /**I2S2 GPIO Configuration 
    PB12 ------> I2S2_WS
    PB13 ------> I2S2_CK
    PB14 ------> I2S2_ext_SD
    PB15 ------> I2S2_SD
    PC6 ------> I2S2_MCK 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_15;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = GPIO_PIN_14;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF6_I2S2ext;
    HAL_GPIO_Init(GPIOB, &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_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
    /* Peripheral DMA init*/
    hdma_i2s2_ext_rx.Instance = DMA1_Stream3;
    hdma_i2s2_ext_rx.Init.Channel = DMA_CHANNEL_3;
    hdma_i2s2_ext_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_i2s2_ext_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_i2s2_ext_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_i2s2_ext_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_i2s2_ext_rx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_i2s2_ext_rx.Init.Mode = DMA_CIRCULAR;
    hdma_i2s2_ext_rx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_i2s2_ext_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    hdma_i2s2_ext_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
    hdma_i2s2_ext_rx.Init.MemBurst = DMA_MBURST_SINGLE;
    hdma_i2s2_ext_rx.Init.PeriphBurst = DMA_PBURST_SINGLE;
    HAL_DMA_Init(&hdma_i2s2_ext_rx);
    __HAL_LINKDMA(hi2s,hdmarx,hdma_i2s2_ext_rx);
    hdma_spi2_tx.Instance = DMA1_Stream4;
    hdma_spi2_tx.Init.Channel = DMA_CHANNEL_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;
    hdma_spi2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_spi2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_spi2_tx.Init.Mode = DMA_CIRCULAR;
    hdma_spi2_tx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_spi2_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    hdma_spi2_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
    hdma_spi2_tx.Init.MemBurst = DMA_MBURST_SINGLE;
    hdma_spi2_tx.Init.PeriphBurst = DMA_PBURST_SINGLE;
    HAL_DMA_Init(&hdma_spi2_tx);
    __HAL_LINKDMA(hi2s,hdmatx,hdma_spi2_tx);
    }
    }

    for point 2 I have:

    void MX_DMA_Init(void) 
    {
    __DMA2_CLK_ENABLE();
    __DMA1_CLK_ENABLE();
    /* DMA interrupt init */
    HAL_NVIC_SetPriority(DMA1_Stream3_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(DMA1_Stream3_IRQn);
    HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn);
    HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
    }

    Then I config my codec. Here there is no problem. I already test my codec initialization and output/input sound and it is ok. Then for pint 4 I write:

    HAL_I2SEx_TransmitReceive_DMA(&hi2s2, (uint16_t *)OutputBuffer, (uint16_t *)InputBuffer, SOUND_BUFF_SIZE);

    with output/input buffers configured. Then after a few seconds I call HAL_I2S_DMAStop(&hi2s2); It is something like this:

    MX_DMA_Init(); // Canales de comunicacion DMA
    MX_I2S2_Init(); // I2S para comunicacion de sonidos con Codec de Audio
    Codec_Init_DPEOAE();
    HAL_Delay(2000);
    // Reseteo la bandera de mitad de buffer lleno
    f_mitad_buffer=0;
    HAL_I2SEx_TransmitReceive_DMA(&hi2s2, (uint16_t *)OutputBuffer, (uint16_t *)InputBuffer, SOUND_BUFF_SIZE);
    HAL_Delay(2000);
    HAL_I2S_DMAStop(&hi2s2);

    All of this is working well. But the problem is that I need to execute previouse code several times. And the second time I run that code the sound is not well. So, I would like to know what should I do to put I2S and DMA as it was when uC run for the first time after reset. Thank
    slimen
    Visitor II
    October 18, 2016
    Posted on October 18, 2016 at 18:19

    Hi,

    I suggest you calling I2S_DeInit to deInitializes the I2S peripheral.

    Regards