Skip to main content
Matti Sillanpaa
Associate III
March 8, 2017
Solved

STMF469I as I2S slave with external MEMS microphone

  • March 8, 2017
  • 18 replies
  • 2995 views
Posted on March 08, 2017 at 10:03

Hi.

I'm trying to understand the I2S configuration in STMF469I-Discovery. I've got external MEMS microphone that's outputting 16khz mono output. I can see with oscilloscope that the output is OK however I cannot seem to get the I2S configured correctly.

I've connected the mic like this and generated code with cubemx as I2S2 as slave with microphone outputting the clock signal (1Mhz), so no as SAI I2S input:

/**I2S2 GPIO Configuration

PB13 ------> I2S2_CK

PB12 ------> I2S2_WS

PC1 ------> I2S2_SD

I'm trying to read the output in task context like this:

 status = HAL_I2S_Receive(&hi2s2, buf, time * hi2s2.Init.AudioFreq, time*1000);

However the I2S_WaitFlagStateUntilTimeout expires as the while(__HAL_I2S_GET_FLAG(hi2s, Flag) == RESET) stays in RESET.

The HAL init is not the _weak definition stmf4xx_hal_i2s.c, but one _i2s_ex.c. Is this the problem or is this clock related?

I'm really having a hard time understanding the clock configuration and the audio pipelines as general. Is there any documentation about this? Below is the simple intialization generated by CubeMX and read function added by me.

/* I2S2 init function */

void MX_I2S2_Init(void)

{

hi2s2.Instance = SPI2;

hi2s2.Init.Mode = I2S_MODE_SLAVE_RX;

hi2s2.Init.Standard = I2S_STANDARD_LSB;

hi2s2.Init.DataFormat = I2S_DATAFORMAT_16B;

hi2s2.Init.MCLKOutput = I2S_MCLKOUTPUT_DISABLE;

//hi2s2.Init.AudioFreq = I2S_AUDIOFREQ_44K;

hi2s2.Init.AudioFreq = I2S_FREQ;

hi2s2.Init.CPOL = I2S_CPOL_LOW;

hi2s2.Init.ClockSource = I2S_CLOCK_PLL;

hi2s2.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_DISABLE;

if (HAL_I2S_Init(&hi2s2) != HAL_OK)

{

Error_Handler();

}

}

void HAL_I2S_MspInit(I2S_HandleTypeDef* i2sHandle)

{

GPIO_InitTypeDef GPIO_InitStruct;

if(i2sHandle->Instance==SPI2)

{

/* USER CODE BEGIN SPI2_MspInit 0 */

/* USER CODE END SPI2_MspInit 0 */

/* Peripheral clock enable */

__HAL_RCC_SPI2_CLK_ENABLE();

/**I2S2 GPIO Configuration

PB13 ------> I2S2_CK

PB12 ------> I2S2_WS

PC1 ------> I2S2_SD

*/

GPIO_InitStruct.Pin = GPIO_PIN_13|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_1;

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

/* Peripheral DMA init*/

hdma_spi2_rx.Instance = DMA1_Stream3;

hdma_spi2_rx.Init.Channel = DMA_CHANNEL_0;

hdma_spi2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;

hdma_spi2_rx.Init.PeriphInc = DMA_PINC_DISABLE;

hdma_spi2_rx.Init.MemInc = DMA_MINC_ENABLE;

hdma_spi2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;

hdma_spi2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;

hdma_spi2_rx.Init.Mode = DMA_NORMAL;

hdma_spi2_rx.Init.Priority = DMA_PRIORITY_LOW;

hdma_spi2_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;

if (HAL_DMA_Init(&hdma_spi2_rx) != HAL_OK)

{

Error_Handler();

}

__HAL_LINKDMA(i2sHandle,hdmarx,hdma_spi2_rx);

/* USER CODE BEGIN SPI2_MspInit 1 */

/* USER CODE END SPI2_MspInit 1 */

}

}

void HAL_I2S_MspDeInit(I2S_HandleTypeDef* i2sHandle)

{

if(i2sHandle->Instance==SPI2)

{

/* USER CODE BEGIN SPI2_MspDeInit 0 */

/* USER CODE END SPI2_MspDeInit 0 */

/* Peripheral clock disable */

__HAL_RCC_SPI2_CLK_DISABLE();

/**I2S2 GPIO Configuration

PB13 ------> I2S2_CK

PB12 ------> I2S2_WS

PC1 ------> I2S2_SD

*/

HAL_GPIO_DeInit(GPIOB, GPIO_PIN_13|GPIO_PIN_12);

HAL_GPIO_DeInit(GPIOC, GPIO_PIN_1);

/* Peripheral DMA DeInit*/

HAL_DMA_DeInit(i2sHandle->hdmarx);

}

/* USER CODE BEGIN SPI2_MspDeInit 1 */

/* USER CODE END SPI2_MspDeInit 1 */

}

/* USER CODE BEGIN 1 */

HAL_StatusTypeDef get_i2s_data(uint16_t *buf, uint16_t time) {

   HAL_StatusTypeDef status;

   HAL_I2S_MspDeInit(&hi2s2);

   MX_I2S2_Init();

   status = HAL_I2S_Receive(&hi2s2, buf, time * hi2s2.Init.AudioFreq, time*1000);

   return status;

}

/* USER CODE END 1 */
    This topic has been closed for replies.
    Best answer by waclawek.jan
    Posted on March 08, 2017 at 12:09

    Then its clock in RCC is probably off.

    I don't Cube (as you might have guessed).

    JW

    18 replies

    ST Technical Moderator
    March 8, 2017
    Posted on March 08, 2017 at 11:07

    Hi,

    I propose that you refer to the working I2S example within STM32CubeF4 firmware package. The provided example will help you to have more idea on how implementation is done:  

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

    Refer to your reference manual in 'I2S functional description' section this may help you on your setting and more understanding the clock configuration.

    Imen

    In order to give better visibility on the answered topics, please click on 'Best answer' on the reply which solved your issue or answered your question. Thanks
    Matti Sillanpaa
    Associate III
    March 8, 2017
    Posted on March 08, 2017 at 11:20

    Thanks Imen!

    The example is about playback. Is there anything about I2S slave input?

    Also regarding the reference manual. Where do I get one that's applicable for 469i? I've found one that's for f43x.
    waclawek.jan
    Super User
    March 8, 2017
    Posted on March 08, 2017 at 11:17

    hi2s2.Init.Mode = I2S_MODE_SLAVE_RX;

    So the mic is the source of clocks? Do you see both SCK and WS on PB13/PB12 with an oscilloscope/LA?

    JW

    Matti Sillanpaa
    Associate III
    March 8, 2017
    Posted on March 08, 2017 at 11:24

    Yes, there's valid activity on all lines. And the mic is outputting the as long as it's on.

    waclawek.jan
    Super User
    March 8, 2017
    Posted on March 08, 2017 at 11:44

    And what is that flag Cube waits for? SPI_SR.RXNE? Do you observe the SPI/I2S registers in a debugger?

    JW

    Matti Sillanpaa
    Associate III
    March 8, 2017
    Posted on March 08, 2017 at 11:48

    I2S_FLAG_RXNE

    From stm32f4xx_hal_i2s.c -> HAL_I2S_Receive

    if (I2S_WaitFlagStateUntilTimeout(hi2s, I2S_FLAG_RXNE, RESET, Timeout) != HAL_OK)

    {

    return HAL_TIMEOUT;

    }

    My main confusion is the stm32f4xx_hal_i2sex -interface. Why the initialization is calling init there? What I understood from the code is that it's only for the full duplex stuff and not just for receiving.

    waclawek.jan
    Super User
    March 8, 2017
    Posted on March 08, 2017 at 11:57

    Do you observe the SPI/I2S registers in a debugger?

    Matti Sillanpaa
    Associate III
    March 8, 2017
    Posted on March 08, 2017 at 12:06

    All the SPI2 registers seem to be 0.

    From debugger after timeout:

    SPI2    SPI_TypeDef *    0x40003800    

        CR1    volatile uint32_t    0    

        CR2    volatile uint32_t    0    

        SR    volatile uint32_t    0    

        DR    volatile uint32_t    0    

        CRCPR    volatile uint32_t    0    

        RXCRCR    volatile uint32_t    0    

        TXCRCR    volatile uint32_t    0    

        I2SCFGR    volatile uint32_t    0    

        I2SPR    volatile uint32_t    0    
    waclawek.jan
    waclawek.janBest answer
    Super User
    March 8, 2017
    Posted on March 08, 2017 at 12:09

    Then its clock in RCC is probably off.

    I don't Cube (as you might have guessed).

    JW

    Matti Sillanpaa
    Associate III
    March 8, 2017
    Posted on March 08, 2017 at 12:13

    I would love not to either .

    ST has wonderful HW with fantastic devboards, but the SW configuration & projects are a little bit a confusing. Way too complex interfaces and configurations with very hard to find documentation. Also simple makefile projects would be nice.

    Matti Sillanpaa
    Associate III
    March 8, 2017
    Posted on March 08, 2017 at 12:27

    If I understood correctly the cube clock tree, the I2S gets I2S_APB1_CLK (160Mhz). And RCC registers say that the APB1ENR has value 4143892991 (0xf6fec9ff). From stm32f469xx.h:

    #define RCC_APB1RSTR_SPI2RST_Pos (14U)

    #define RCC_APB1RSTR_SPI2RST_Msk (0x1U << RCC_APB1RSTR_SPI2RST_Pos) /*!< 0x00004000 */

    #define RCC_APB1RSTR_SPI2RST RCC_APB1RSTR_SPI2RST_Msk

    The bit is 1. Dunno about the rest of the clock config.

    RCC    RCC_TypeDef *    0x40023800   

    CR    volatile uint32_t    1057194115    

    PLLCFGR    volatile uint32_t    624960527    

    CFGR    volatile uint32_t    0    

    CIR    volatile uint32_t    0    

    AHB1RSTR    volatile uint32_t    0    

    AHB2RSTR    volatile uint32_t    0    

    AHB3RSTR    volatile uint32_t    0    

    RESERVED0    uint32_t    0    

    APB1RSTR    volatile uint32_t    0    

    APB2RSTR    volatile uint32_t    0    

    RESERVED1    uint32_t [2]    0x40023828    

    AHB1ENR    volatile uint32_t    1050623    

    AHB2ENR    volatile uint32_t    128    

    AHB3ENR    volatile uint32_t    3    

    RESERVED2    uint32_t    0    

    APB1ENR    volatile uint32_t    270532608    

    APB2ENR    volatile uint32_t    134234113    

    RESERVED3    uint32_t [2]    0x40023848    

    AHB1LPENR    volatile uint32_t    2129631231    

    AHB2LPENR    volatile uint32_t    241    

    AHB3LPENR    volatile uint32_t    3    

    RESERVED4    uint32_t    0    

    APB1LPENR    volatile uint32_t    4143892991    

    APB2LPENR    volatile uint32_t    209157939    

    RESERVED5    uint32_t [2]    0x40023868    

    BDCR    volatile uint32_t    0    

    CSR    volatile uint32_t    234881024    

    RESERVED6    uint32_t [2]    0x40023878    

    SSCGR    volatile uint32_t    0    

    PLLI2SCFGR    volatile uint32_t    536883200    

    PLLSAICFGR    volatile uint32_t    570432768    

    DCKCFGR    volatile uint32_t    0    

    EDIT: The clock config in main.c seems to be OK from what I understood from other examples.

    waclawek.jan
    Super User
    March 8, 2017
    Posted on March 08, 2017 at 13:32

    Okay, so now STOP watching the variables, as reading SPI_DR clears the RXNE flag.

    (TXE is okay - it IS empty).

    JW

    Matti Sillanpaa
    Associate III
    March 9, 2017
    Posted on March 09, 2017 at 11:27

    Ok, now the I2S works. The remaining issue was rather embarassing.

    I had connected the lines wrong. With other connectors the name is marked at the top. I didn't re-check that and just assumed that that's the chase here also. But no, here it was at the bottom :/. Damn I'm blind. The pin number is right next to that name...

    waclawek.jan
    Super User
    March 9, 2017
    Posted on March 09, 2017 at 12:21

    But no, here it was at the bottom

    Oh, all hw and sw designers I know of (including me of course) has been caught at least once by some variant of this '3D'/symmetry/etc. problem... We call this problem internally the 'left/right' problem, after a prominently painful incident with stereo audio... (with two such mistakes compensating each other in a particular prototype setup).

    However, recall, what was my first question:

    Do you see both SCK and WS on PB13/PB12 with an oscilloscope/LA?

    I forgot to write this down precisely, but by 'on PB13/PB12' I *always* mean 'as measured DIRECTLY on the mcu pins'. I noted here and elsewhere that I am not the only one who takes this implication as granted...

    JW