cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F4Discovery read I2S2 data, delay it, and send to I2S3

megahercas6
Senior
Posted on July 09, 2016 at 17:01

Hello

I am trying to delay audio sygnal before it is send to I2S dac. DAC is working, can get Sinus waveform without problem. Question is, what is the correct way to take 48Ksps 16b data from I2S2, and send it to I2S3? This is my code, i hear lot of metallic sound, and it looks like only SD and CK (PB15 and PB13) does effect sound, and LRCK, BCK does nothing to sound (connected or unconnected PC9 and PB12) If any one can spot the problem, please help me 🙂

#include ''main.h''
#include ''math.h''
float NOTEFREQUENCY =0.015; //frequency of saw wave: f0 = 0.5 * NOTEFREQUENCY * 48000 (=sample rate)
float NOTEAMPLITUDE =18000.0; 
int16_t sinusas[2400];
void PINS(void);
uint32_t cnt = 0;
int16_t data = 0;
int main(void)
{
SystemInit();
PINS();
int i = 0;
while(i<
2400
)
{
sinusas[i]=(int)(NOTEAMPLITUDE*sinf((2.0f*3.14f*i)/(20f)));
i++;
}
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
GPIO_InitStructure.GPIO_Pin
= 
GPIO_Pin_15
;
GPIO_InitStructure.GPIO_Mode
= 
GPIO_Mode_OUT
;
GPIO_InitStructure.GPIO_OType
= 
GPIO_OType_PP
;
GPIO_InitStructure.GPIO_Speed
= 
GPIO_Speed_50MHz
;
GPIO_Init(GPIOD, &GPIO_InitStructure);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
codec_init();
codec_ctrl_init();
I2S_Cmd(CODEC_I2S, ENABLE);
I2S_Cmd(SPI2, ENABLE);
while(1)
{
if (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE))
{
if(cnt & 0x00000001)
{
data = (int16_t)SPI_I2S_ReceiveData(SPI2);
}
if (SPI_I2S_GetFlagStatus(CODEC_I2S, SPI_I2S_FLAG_TXE))
{
SPI_I2S_SendData(CODEC_I2S, data/10);
}
}
cnt++;
}
while(1)
{
if (SPI_I2S_GetFlagStatus(CODEC_I2S, SPI_I2S_FLAG_TXE))
{
SPI_I2S_SendData(CODEC_I2S, (int16_t)sinusas[i]);
//only update on every second sample to insure that L & R ch. have the same sample value
/*if (sampleCounter & 0x00000001)
{
sawWave += NOTEFREQUENCY;
if (sawWave > 1.0)
sawWave -= 2.0;
filteredSaw = updateFilter(&filt, sawWave);
sample = (int16_t)sinusas[sampleCounter];//[(NOTEAMPLITUDE*filteredSaw);
}*/
sampleCounter++;
}
i++;
if(i>2399)
i=0;
if (sampleCounter==48000)
{
LED_BLUE_OFF;
}
else if (sampleCounter == 2399)
{
LED_BLUE_ON;
sampleCounter = 0;
}
}
}
void PINS(void)
{
GPIO_InitTypeDef PinInitStruct;
GPIO_StructInit(&PinInitStruct);
I2S_InitTypeDef I2S_InitType;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOB, ENABLE);
RCC_I2SCLKConfig(RCC_I2S2CLKSource_Ext);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
RCC_PLLI2SCmd(ENABLE);
PinInitStruct.GPIO_Mode = GPIO_Mode_AF;
PinInitStruct.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_15;
GPIO_Init(GPIOB, &PinInitStruct);
PinInitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOC, &PinInitStruct);
//prepare output ports for alternate function
GPIO_PinAFConfig(GPIOB, GPIO_PinSource12, GPIO_AF_SPI2); //WS LR CLK
GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_SPI2); //CK
GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_SPI2); //SDIN
GPIO_PinAFConfig(GPIOC, GPIO_PinSource9, GPIO_AF_SPI2); //I2S2_CKIN 
// configure I2S port
SPI_I2S_DeInit(SPI2);
I2S_InitType.I2S_AudioFreq = I2S_AudioFreq_48k;
I2S_InitType.I2S_MCLKOutput = I2S_MCLKOutput_Disable;
I2S_InitType.I2S_DataFormat = I2S_DataFormat_16b;
I2S_InitType.I2S_Mode = I2S_Mode_SlaveRx;
I2S_InitType.I2S_Standard = I2S_Standard_Phillips;
I2S_InitType.I2S_CPOL = I2S_CPOL_High;
I2S_Init(SPI2, &I2S_InitType);
I2S_Cmd(SPI2, ENABLE);
}

3 REPLIES 3
mark239955_stm1
Associate II
Posted on July 10, 2016 at 09:02

When you say that you want to delay the data received at I2S2 before you send it via I2S3, what do you mean?  How much delay?  Is the delay constant?  How precise does the delay have to be?

Re the other part of your question, forwarding data from one I2S peripheral to another should be reasonably straightforward to do, as long as no transcoding is required (i.e. no changes in sample rate or resolution).  Use two DMA streams and two circular buffers, A & B.  One DMA stream transfers received data into buffer A while the other DMA stream transfers data from buffer B into the transmitter.  When DMA has finished with either buffer, it starts on the other.  You should be able to make the entire process almost completely automatic, requiring CPU intervention only to start it.  If you use this approach then the forwarding delay would be set by the size of the buffers, the sample rate and the amount of time between starting the receive DMA and transmit DMA.

If you don't understand the above then I suggest you start by reading the 407's reference manual, the DMA and SPI/I2S sections in particular.

megahercas6
Senior
Posted on July 10, 2016 at 09:39

Delay will be created by filling buffer from one side, and reading from another, and shifting it as soon as sample comes in. since this is the only job CPU will going to do, i figure don't use DMA.

Yes, it would be possible to make it with circular DMA buffer, and circular buffer size would give delay,but for testing, it should be possible to make it without DMA. In this forum i read that I2S2 ignores word sync, it is error in the silicon :( Since i have no control on I2S stream, when i start debug session, it locks in different part of the frame, and i can't understand how i can re-sync it.

In I2S slave mode, WS level must be set by the external master when enabling the I2S
Description
In slave mode, the WS signal level is used only to start the communication. If the I2S (in
slave mode) is enabled while the master is already sending the clock and the WS signal
level is low (for I2S protocol) or is high (for the LSB or MSB-justified mode), the slave starts
communicating data immediately. In this case, the master and slave will be desynchronized
throughout the whole communication.
Workaround
The I2S peripheral must be enabled when the external master sets the WS line at:
• High level when the I2S protocol is selected.
• Low level when the LSB or MSB-justified mode is selected.

mark239955_stm1
Associate II
Posted on July 12, 2016 at 00:58

Some fairly compelling (imo) reasons to use the hardware peripherals (DMA, timers) are that you get fixed timing behaviour, and you can slow the CPU core clock down if the CPU isn't busy.

It sounds like you need to use an interrupt (probably an EXTI) to start the I2S slave capture on detection of an edge on your WS signal.  You might also be able to use DMA2 and input capture to get tighter timing on servicing an edge on WS, but that's not something that ST have documented afaia.