cancel
Showing results for 
Search instead for 
Did you mean: 

SAI with blocking mode

DYann.1
Senior II

Hello,

I have this code

 

 

  // Initialiser le bloc A
  fresult= HAL_SAI_Init(&hsai_BlockA1);
  if (fresult != HAL_OK)
  	{
  	return HAL_ERROR;
  	}
  // Initialiser le bloc B
  fresult= HAL_SAI_Init(&hsai_BlockB1);
  if (fresult != HAL_OK)
	{
  	return HAL_ERROR;
	}

  // Transmission
  fresult = HAL_SAI_Transmit(&hsai_BlockB1, (uint8_t *)playbuf, (sizeof(playbuf))/4, 0xFF);
  if (fresult != HAL_OK)
  	{
  	return HAL_ERROR;
  	}
  // Reception
  fresult = HAL_SAI_Receive(&hsai_BlockA1, (uint8_t *)playbuf_RX, (sizeof(playbuf_RX))/4, 0xFF);
  if (fresult != HAL_OK)
  	{
  	return HAL_ERROR;
  	}

 

 

I am not receiving some of the data, do you know where I can find the documentation to implement SAI blocking mode? thanks in advance.

DYann1_0-1704728511248.png

In my reception array I have only a part of data, and yet my TX table is well initialized like this :

DYann1_1-1704728708060.png

28 REPLIES 28

Yes you're right, but when I don't know where to start then it seems a little confusing. Thank you for this verification method.

Hi,

I tried to set up the circular buffer. Here is my code :

  while (1)
  {
    /* USER CODE END WHILE */
	  Reset_playbuf_RX();
	  HAL_Delay(1);
	  if (dataReadyFlag)
	  {
		  processData();
	  }
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

 To be sure that my RX table is cleared with each call, I reset it all to 0. In my callback I set a flag to 1 and then I proceed with the transfer.

void HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef *hsai)
 {
	dataReadyFlag=1;
 }

void processData ()
{for (int32_t n=0; n<8192; n++)
	{playbuf_RX[n]=playbuf[n];}
	HAL_Delay(1);
}
void Reset_playbuf_RX()
{static int32_t j;
 for (j=0;j<8192;j++)
  {playbuf_RX[j]=0;}
 HAL_Delay(1);
}

But something is wrong, in my playbuf_RX array is never reset to 0 yet I enter it in the void Reset_playbuf_RX function.

DYann1_0-1705408720019.png

Probably it runs continuously via the DMA and I don't have control of the playbuf_RX?

fresult = HAL_SAI_Receive_DMA(&hsai_BlockA1, (uint8_t *)playbuf_RX, (sizeof(playbuf_RX))/4);
 if (fresult != HAL_OK)
	{return HAL_ERROR;}

When I look at my LRCLK :

DYann1_1-1705409210744.png

And on the TX signal, I have signals that run continuously

DYann1_2-1705409250901.png

How to recover the transmitted signals ? I certainly forgot some functions but what ? Thank you for your advice

LCE
Principal

1) So TX is running continuously? If yes, great!
Now better check that with both LRCK and TX data at the scope, with trigger on LRCK, just to make sure...

2) Your code:

a) dataReadyFlag is set at HALF complete. Why?
b) dataReadyFlag is reset where?

c) I wonder why you ever see playbuf_RX[] not = 0, because your while loop makes no sense.

What your while loop does:

- START: reset playbuf_RX[] = 0
- wait 1 ms
- if dataReadyFlag is set in HALF complete, then you copy playbuf (the DAC buffer?) into playbuf_RX, basically overwriting what the DMA put in there
- and then, 1 ms later you go to START and reset playbuf_RX again

This makes NO sense at all and you're really molesting the DMA buffer by constantly resetting and copying - or I just don't get it.

 

Here's my suggestion:

1) set dataReadyFlag in RX full complete callback, not in half complete

2) in while loop start only with this, reset the flag, count, toggle an LED

uint32_t cntRxTest = 0;
while(1)
{
    if( dataReadyFlag != 0 )
    {
        dataReadyFlag = 0;
        cntRxTest++;
        toggle_LED;
    }
}

If that is working, maybe reset the playbuf_RX to zero within the if statement. 

Maybe make the buffer smaller so that the resetting is done within one sample duration.

 

Hi,

Thank you for your comments

DYann1_0-1705478298550.png

I don't understand where the DMA data is stored ? In my configuration SAI1_A (the slave : CODEC)

DYann1_1-1705478474244.png

Peripheral : SAI_RX 

Memory : ? Where and how to read this datas when the Flag RX =1 (HAL_SAI_RxCpltCallback, I make a mistake here).   

For the Delay (1), it's just being able to make a breakpoint and see if the playbuf_RX table is indeed reset to 0 for the next acquisition, and it's probably not a good method because I'm 'really molesting the DMA buffer' but how to make sure if the data is transmitted correctly when Flag =1 (RxCpltCallback) ? you may lose a frame of data but then you will have a complete frame, right ?

I have 2 signals (blue : data (probe x10). yellow : LRCLK)

DYann1_0-1705491476053.png

LCE
Principal

> I don't understand where the DMA data is stored ?

You should at least read the HAL source function descriptions.

Then you would find for HAL_SAI_Receive_DMA

HAL_SAI_Receive_DMA(&hsai_BlockA1, (uint8_t *)playbuf_RX, (sizeof(playbuf_RX))/4);

playbuf_RX (= memory) is the data buffer you hand over to the DMA, and that's where DMA writes the data it gets from the SAI RX peripheral.
That's why I said it does not make sense to constantly "work" on this buffer.

 

Scope:

Your scope does NOT show LRCK ( = FS in STM lingo), but probably SCLK ( = CK or so, the bit clock).
LRCK frequency equals the sampling rate, and within one half period you should see one data sample.

If you have 3 channels, then simply add LRCK, and trigger on that.

 

For the scope you're right about FS (yellow) Data (blue) now I have this :

DYann1_0-1705506158398.png

Now I understand better what DMA and memory are. A confusion on my part however, I think that we need a way to check that what we have received corresponds to the transmission. Maybe a pointer to this playbuf_RX array so as not to slow down DMA reception ?

LCE
Principal

Scope: now that looks better!

I guess that there's some up-counting going on, because only the LSBs are changing.

LRCK ~= 36 kHz - is that what you expected from your SAI TX settings?

At 36 kHz, one sampling period is about 27 µs. Check how many buffers you could read or reset within that time, depends on your CPU clock. As a rule of thumb, take factor 4 for each for loop iteration.

To get more "time" to read the buffer, you could now use both the HALF and the complete interrupts:
- at HALF you set half flag within ISR
- in the loop check half flag, if set copy 1st half of the buffer
- at complete set another flag within ISR
- in the loop check complete flag, if set copy 2nd half of the buffer

A pointer is basically an address, so if you want to check what's the content of that address, it doesn't really make a difference concerning the buffer access.

 

Hi, 

Thank you very much for this very useful information (at least for me). I think after my configuration the LRCL is correct (Real Audio Frequency ?) : 

DYann1_0-1705571858437.png

I don't know how long it will take to read the receive memory but for this loop for example :

void Reset_playbuf_RX()
{static int32_t j;
 for (j=0;j<8192;j++)
  {playbuf_RX[j]=0;}
}

I think my CPU clock is 32.768 kHz about 30 µs. 

DYann1_1-1705572576791.png

2 instructions x 8192 x 27 µs = 0.44 s x 4 (factor) = 1.7s. I think the best method is to do is to read 2 halfs buffers, I'll modify the configuration via '.ioc' to do it. I'll see how to deal with interruptions.

LCE
Principal

I think my CPU clock is 32.768 kHz about 30 µs. 

Hopefully not!

Maybe find some basic tutorial about STM32 / clock setup.

What you show is the LSE, which stands for Low Speed External clock, that should only / mostly be used for a real time clock (RTC).

Your CPU clock should at least be in the MHz range, which STM32 type + board are you using?
CPU clock source should be somewhere from HSI (not good for audio due to jitter) or HSE.

So again, at HALF complete interrupt:

- set half flag within ISR
- in the loop check half flag, if set:
    - copy 1st half of playbuf_RX to checkbuf_RX, or simply compare to the TX buffer
    - reset 1st half of playbuf_RX

at full complete interrupt same with 2nd half of buffer

Hi,

After reading some information about the clock I understand a little better. My CPU clock is 16 MHz

DYann1_0-1705583947425.png

For the moment my objective is just communication by SAI, after of course I need to optimize my electronic board. I have an evaluation board : STM32L552E-EV with STM32L552ZET6QU microcontroller. I understand the 'HALF complete interrupt' after your explanation and I'll implement with this method. You're right, I've to reduce the size of my array just to see at first, for example an array of 100 values.