cancel
Showing results for 
Search instead for 
Did you mean: 

connecting audio codec to STM32F4 Discovery using I2S

d_1
Associate II
Posted on July 03, 2013 at 11:52

Can anyone suggest where I'm going wrong? I'm new to this forum and people seem to be posting longish code fragments, but I apologise if I seem to be posting an inappropriately long question.

I can't get I2S comms to an external audio codec from an STM32F4 Discovery board to work.

I've adapted the I2S_DataExchangePolling example from the standard peripherals library as attached (I've cut out the parts of the code that uses I2C to configure the codec as I2S master - that seems to be working since I can see WS and CLK signals and also data from the codec, using a 'scope)

Most fundamentally (as far as I can see) the problems include

1) no signal apparent on output pin (PC2)

2) the program doesn't appear to be held up waiting for RXNE (notionally at 8 kHz) If I run the program for n seconds, counter ends up at approx 100000 x n.

7 REPLIES 7
Posted on July 03, 2013 at 12:06

> 1) no signal apparent on output pin (PC2)

>#define I2Sxext_SD_AF GPIO_AF_SPI2

No; I2S2ext_SD is AF6 rather than AF5 - refer to table 9 in datasheet.

JW

d_1
Associate II
Posted on July 03, 2013 at 14:11

Thanks! I had mistakenly changed that when trying to adapt the original example.

I am now seeing output data on PC2 BUT it's not synchronised with the WS (generated by the I2S master codec) on PB12. So I'm not 'connecting' to this correctly in my configuration?

Could that be the final piece in the puzzle - real-time triggering of everything should be controlled by WS?

Donald

d_1
Associate II
Posted on July 04, 2013 at 08:19

Can anyone help with what looks to be the last remaining issue with my program?

I can see CLK, WS, SDO and SDI signals but the actions of the I2S slave (the STM32F4) do not appear to be synchronised with WS.

Donald

modcan
Associate II
Posted on July 04, 2013 at 19:07

Here's how I did it on a recent project. This is not using DMA for transfer so not as good as it could be but you may be able to see something you are missing from it.

 /* Perform low layer Codec initialization */

  /* Configure the Codec related IOs */

        GPIO_InitTypeDef GPIO_InitStructure;

      NVIC_InitTypeDef NVIC_InitStructure;

      I2S_InitTypeDef I2S_InitStructure;

  /* Enable I2S and I2C GPIO clocks */

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOA, ENABLE);

  /* CODEC_I2S pins configuration: WS, SCK and SD pins -----------------------------*/

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_12;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;

  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;

  GPIO_Init(GPIOC, &GPIO_InitStructure);

  /* Connect pins to I2S peripheral  */

  GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_SPI3);

  GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_SPI3);

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;

  GPIO_Init(GPIOA, &GPIO_InitStructure);

  GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_SPI3);

  /* CODEC_I2S pins configuration: MCK pin */

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;

  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;

  GPIO_Init(GPIOC, &GPIO_InitStructure);

  /* Connect pins to I2S peripheral  */

  GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_SPI3);

  /* Enable the CODEC_I2S peripheral clock */

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);

  /* CODEC_I2S peripheral configuration */

  SPI_I2S_DeInit(SPI3);

  I2S_InitStructure.I2S_AudioFreq = I2S_AudioFreq_48k;

  I2S_InitStructure.I2S_Standard = I2S_STANDARD;

  I2S_InitStructure.I2S_DataFormat = I2S_DataFormat_16b;

  I2S_InitStructure.I2S_CPOL = I2S_CPOL_Low;

  I2S_InitStructure.I2S_Mode = I2S_Mode_MasterTx;

  I2S_InitStructure.I2S_MCLKOutput = I2S_MCLKOutput_Enable;

  /* Initialize the I2S peripheral with the structure above */

  I2S_Init(SPI3, &I2S_InitStructure);

SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Tx, ENABLE);

  SPI_I2S_ITConfig(SPI3, SPI_I2S_IT_TXE, ENABLE);

  /* I2S DMA IRQ Channel configuration */

  NVIC_InitStructure.NVIC_IRQChannel = SPI3_IRQn;

  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = EVAL_AUDIO_IRQ_PREPRIO;

  NVIC_InitStructure.NVIC_IRQChannelSubPriority = EVAL_AUDIO_IRQ_SUBRIO;

  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

  NVIC_Init(&NVIC_InitStructure);

if ((SPI3->I2SCFGR &I2S_ENABLE_MASK) == 0)

  {

    I2S_Cmd(SPI3, ENABLE);

  }

This is the interrupt where output to codec happens

void SPI3_IRQHandler(void)

{

  /* Check on the I2S TXE flag */

  if (SPI3->SR &SPI_I2S_FLAG_TXE)

  {

    if (SPI3->SR &I2S_FLAG_CHSIDE)

      output code for channel 1

      else

     output code for channel 2

}

     }

 

d_1
Associate II
Posted on July 05, 2013 at 11:00

Thanks piker.

One thing that I had missing was

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);

or in my case

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);

although I've tried adding both.

However, still no luck. I think I must be close.

To recap, I am trying to use the codec as I2S master and to run both input and output through it. I have used a 'scope to verify that I have WS, CLK, and SD generated by codec and I have SD (other direction) generated by MCU, all on the pins I expect. The problem seems to be that despite my attempts to check RXNE and TXE, my program seems to be 'free' running and not

controlled by frequency of WS (which I could be wrong about but I am expecting.) Perhaps I am misunderstanding this.

The codec is sending data on each WS edge, hence one 16-bit sample every 62.5us, or 16000 per second. However, although I am waiting for RXNE each time, if I run the program for approx one second, I am counting approx 150000 reads rather than 16000. The data read from the codec comprises a lot of zeros but with 'sensible' values approx every 12th one.

Output data from the MCU is correct (on 'scope) but repeated continuously (24 times per

WS period).

So it appears that the loop is running free (as fast as possible?), when I expected it to hang waiting for RXNE. And I conclude that WS is not 'controlling' data into DR as I anticipated.

The MCU is 'reading' lots of (zero) data samples in between those actually sent by the codec.

Donald

Posted on July 05, 2013 at 13:11

> Output data from the MCU is correct (on 'scope) but repeated continuously (24 times per

WS period).

This sounds like the CK input is not fed properly - perhaps your codec is not properly set up for the 16-bit samples? There should be 16 CK periods within one WS halfperiod. Please check and report back.

JW

d_1
Associate II
Posted on July 05, 2013 at 16:28

JW, thanks!

Your comment (and the previous one) have got me to a stage where I have got audio in and out. I think there is still experimentation to be done but it's a heartening milestone to pass.

As far as I can see, the bit clock frequency on the WM8731 codec is fixed at 64 x 48000.

I was trying to use a 8kHz frame rate but if I change settings on both sides to 48kHz frame rate and 16-bit  data in a 32-bit frame then everything matches up.

Perhaps the bottom line is that using the codec as I2S master is inflexible. Otherwise, I may investigate PCM, or DSP, or whatever other names it goes under data mode/format.

Once again, thanks.

Donald