cancel
Showing results for 
Search instead for 
Did you mean: 

I2S + DMA configuration

JusbeR
Associate II

I have NUCLEO-F756ZG board with STM32F765ZG and I have hooked up ICS43434 mic into I2S1 bus. Now I have been scratching my head for a day to figure out how to get data out from that puppy. Or actually getting data out is simple, but to make it sane is not. I have configured I2S and DMA like below

JusbeR_0-1746793869493.pngJusbeR_1-1746793913913.png

Then I allocate some buffers and write DMA callbacks and send the data to freeRTOS task for processing. Now if I play 1kHz sound and dump the raw data from DMA buffers I get something like this

113,58880,0,0,116,43008,0,0,123,4608,0,0,134,42496,0,0

If I draw the 113, 166 so every 4th sample to graph it is the sine wave what I am playing. My friend the AI said that the data format is left_high, left_low, right_high, righ_low. That might very well be, but I don't want that. If the mic sends 24 bit of data in 32 bit frames it feels insane that I have to spend 2*32bits of memory for it.

I just can't figure out what am I missing. It does not help HAL_I2S_Receive_DMA is one big red herring, but I believe it probably isn't the problem. I did try to configure DMA to half word in hope to get the 24 bits into 2*16bit, but that didn't fly. I might have missed something in this though.

Any ideas anyone?

1 ACCEPTED SOLUTION

Accepted Solutions

The DMA should be set up for half-word access on the peripheral side since the DR register is 16-bits.

TDK_0-1746814515501.png

You can keep the memory side at 32 bits, but the bytes might not be in the right order.

 

When it's set up as a word, it's reading 32 bits but only half of them are relevant, so you're getting double the data.

 

> I did try to configure DMA to half word in hope to get the 24 bits into 2*16bit, but that didn't fly.

Explain what is not flying.

If you feel a post has answered your question, please click "Accept as Solution".

View solution in original post

8 REPLIES 8
Saket_Om
ST Employee

Hello @JusbeR 

The microphone you are using is mono with output in sterio format. 

Saket_Om_0-1746797501271.png

According to the datasheet we have a pin LR that should be set to select the output on the right or the left chanel.

So it is normal that you receive the half of data as 0. 

 

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.
Saket_Om
TDK
Guru

Looks like these are meant to be in stereo, with one device outputting on the left channel, and the other on the right channel. With only one device, half of the data will be 0, as you see.

TDK_0-1746797384628.png

 

If you only have one device, you could set it up to output on the left channel and use SPI to read it instead and use WS as the CS signal. Don't think there's an I2S setting to ignore half of the data or capture "mono" audio.

 

You could look into using SAI instead.

 

I2S mono audio capture - STMicroelectronics Community

SAI I2S Mono Mode Not An Option - STMicroelectronics Community

 

If you feel a post has answered your question, please click "Accept as Solution".
JusbeR
Associate II

Thanks for the answers. Yes. I understand that I have only left channel there, but the question is that why the 24bit sample is divided into two 32 bit memory blocks. And to be precise, I am not sure if this is happening or are the other values just rubbish or what. So to clarify. I read this from DMA buffer:

113,58880,0,0,116,43008,0,0,123,4608,0,0,134,42496,0,0

and each number here is uint32_t, and every 4th item produces a sine wave if I draw it. So this is maybe something like:

113 58880 0 0 116 0x00000071 0x0000E600 0x00000000 0x00000000 0x00000074 0x... 0x71E600 maybe? 64 bits right chan? Another 64 bits for left?

I would want that there is something like

Left Right Left 0x0071E600 0x00000000 0x...

That would make more sense and consumes 50% less memory

Those look like uint16_t values. Not a single one is above 65536. Are you sure they are uint32_t?

You have "word" configured in the DMA, but the actual array being uint16_t would explain everything you see here.

If you feel a post has answered your question, please click "Accept as Solution".
JusbeR
Associate II

Looks like 16 bits in one and 8 bits in another yes. I can't understand why the data is like that in the buffer. I have stared at these for hours and finally came here to see if anyone else is less blind than me. To me they look like 32bit values

#define SINGLE_SAMPLE_SIZE sizeof(uint32_t) #define I2S_DMA_SAMPLE_COUNT 128 #define I2S_DMA_SAMPLE_SIZE_BYTES (SINGLE_SAMPLE_SIZE * I2S_DMA_SAMPLE_COUNT) #define I2S_DMA_BUF_COUNT (2 * I2S_DMA_SAMPLE_COUNT) #define CHANNEL_COUNT 3 ... static uint32_t dmabuf[CHANNEL_COUNT][I2S_DMA_BUF_COUNT]; ... HAL_I2S_Receive_DMA(I2SHandles[i], (uint16_t *)dmabuf[i], I2S_DMA_BUF_COUNT/2);

Then if I send, print or check the dmabuf content after e.g. `HAL_I2S_RxHalfCpltCallback` I see the numbers above in the dmabuf

>Looks like 16 bits in one and 8 bits in another yes. I can't understand why the data is like that in the buffer.

Right, that's "normal" . (The "problem" is the way of representing data : big or little endian.)

from ds ICS43434    :

The output data word length is 24 bits/channel. The data word format is 2’s complement, MSB-first.

So to put your data "together" : 

113,58880,0,0,116,43008,0,0,123,4608,0,0,134,42496,0,0

58880 (MSB word, bytes swapped)  , 113 (LSB in lower byte) , 0,0 right ch. on zero ; and so on...

check in debug -> in memory view : different ways to show the memory, maybe you "see" what you expect, on a certain way (classic...or byte wise)

Here example , from my audio player, reading audio data from sd-card , CD/wav format (2’s complement, MSB-first): inbuf is bytes stream, playbuf is  int32_t (what i need)

if(wav_header.waveheader.bits_sample==24) playbuf[a]=(inbuf[(a*2)+a+2]<<24)|(inbuf[a*2+a+1]<<16)|(inbuf[a*2+a])<<8 ; // move inbuf ->playbuf(int32_t)

Here 24bit is shifted together, to get a 32bit word , int32_t , as needed. (or expected... :)  )

 

btw 24bits in 32b frame is really the most "difficult" , i needed some time to find out, what to shift and why ...

If you feel a post has answered your question, please click "Accept as Solution".

The DMA should be set up for half-word access on the peripheral side since the DR register is 16-bits.

TDK_0-1746814515501.png

You can keep the memory side at 32 bits, but the bytes might not be in the right order.

 

When it's set up as a word, it's reading 32 bits but only half of them are relevant, so you're getting double the data.

 

> I did try to configure DMA to half word in hope to get the 24 bits into 2*16bit, but that didn't fly.

Explain what is not flying.

If you feel a post has answered your question, please click "Accept as Solution".
JusbeR
Associate II

All right. I think I got it now by combining all the hints you guys gave me. The final clue was @TDKs half word hint. So the culprit was

- Use half word DMA setting even if you use word memory block

- Swap the half words (as in not Little/Big endian swap, just swap the half words)

- Give HAL_I2S_Receive_DMA the actual count of words in the buf not half words

Here some screenshots and code for future reference

JusbeR_0-1747033818693.png

static inline int raw_to_sample(uint32_t dma_raw) { return ((dma_raw & 0xFFFF) << 8) | (dma_raw >> 24); }
static uint32_t dmabuf[128] ... HAL_I2S_Receive_DMA(h, (uint16_t *)dmabuf, 128);

This way you when you get the half full interrupt you will have data in 0-63 where left in even and right in odd numbers. After full interrupt data is on 64-127.