STM32: I²S Callbacks get called but UART doesn't show it - Page 1
Forum section: electronics / general

author: schwukas
author_rank: Newbie
timestamp: November 12, 2020, 06:47:10 am
content: Hi there!Long time listener, first time caller.I'm currently working on a project involving a digital I²S MEMS microphone on a STM32G031. I got receiving data with DMA working reasonably well but ran into a small understanding issue.At the initialization stage I'm starting the DMA Reception like this:Code:if (HAL_OK == HAL_I2S_Receive_DMA(&hi2s1, (uint16_t*)i2s_dma_buf, 2)) {sprintf(infomsg, "i2s dma ok\r\n");HAL_UART_Transmit(&huart1, (uint8_t*)infomsg, strlen(infomsg), 100);}I'm flashing an LED in the main while loop just to see if things are working.Also I have defined these two callbacks that are supposed to fire when the buffer is half or completely filled up:Code:void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef* hi2s1) {sprintf(msg, "half\r\n");HAL_UART_Transmit(&huart1, (uint8_t*)msg, strlen(msg), 100);}void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef* hi2s1) {sprintf(msg2, "cplt\r\n");HAL_UART_Transmit(&huart1, (uint8_t*)msg2, strlen(msg2), 100);}My problem is, that I never see that "i2s dma ok" message appear on reset. Also, I only ever see thousands of "half" messages in the serial console but no "cplt"s. I think, however, that both callbacks are firing properly, because if I exchange those UART transmission with LED toggles, they light up.LukasWhen I'm trying to run the debugger on that piece of code it stops right at the "sprintf(infomsg, 'i2s dma ok\r\n')" line, but the serial console is showing "half"s, so the DMA seems to be running and firing callbacks. Also the LED that is supposed to be blinking only lights up once and never goes out.I'm pretty sure I have some misunderstanding about the DMA and UART, since it's just chugging along without any governing by the CPU. Maybe someone can enlighten me.

author: newbrain
author_rank: Super Contributor
timestamp: November 12, 2020, 08:17:44 am
content: A small set of hints to nudge you in the right direction:Why use sprintf to just copy a fixed string? It's a very slow function (even for this simple case).You don't see the the initial message "i2s dma ok", why is that?Consider what happens inside the HAL_I2S_Receive_DMA() function, and your I2S sampling frequency. What will happen before, the DMA half-done interrupt or the printout?Also, consider how small a buffer you are handling with each DMA transaction with respect to the speed of UART: do you even have the time to print a message every half- and full- DMA interrupts, even ignoring the byte shuffling mentioned above?GPIO is many orders of magnitude faster than UART.

author: schwukas
author_rank: Newbie
timestamp: November 12, 2020, 09:49:02 am
content: Well that helped quite a lot. I had some suspicions about why it's behaving like it is, but since I'm very new to this I could never be quite sure whether I'm assuming the right things.I replaced the sprintf with just a variable. That definitely makes sense, I just never really thought about it.Getting rid of sprintf() also made "i2s dma ok" appear. Coming from python I'm not used to such high speeds.That I actually took into account and tried other buffer sizes, but since it changed nothing I just assumed my conception was wrong. I never set it higher than 1024, maybe that's why.I still cannot see any "cplt" debug messages but at least I now know why. I feel like other than external means (logic analyzer, oscilloscope) there isn't any good way of debugging DMA stuff? UART is too slow, flashing LEDs for every callback slows down everything and turning an LED on for every half callback, then off for the complete one, I can only see it with a highspeed camera.I'd like to see the actual content of the dma buffer but now I feel like using UART might lead to false conclusions due to it being slower that the actual DMA transfer.

author: Psi
author_rank: Super Contributor
timestamp: November 12, 2020, 10:02:53 am
content: If you want to send some text to the UART if a interrupt/callback fires.Make a global volatile variable to use as a flag, set the flag in the interrupt and do the sending in main().In the interrupt check the state of the flag and if =3 set it 1In void Main() check if the flag is =1 (message to be sent), if so, set it to 2 and send the actual UART messageIn a systick/timer block that runs less often, maybe every 50ms, check the flag and if it's =2 set it back to 3.There will be a few instruction cycle delay as the message wont sent right inside the interrupt, it will wait until after the interrupt/callback ends (when execution passes back to main). However this is usually not an issue.The main advantage is it allows you to control the repeat rate and prevent it trying to send it again while it's still sendingYou can play with the delays to make it as fast as you need.-Post has been edited to fix a bug in my first post-

author: newbrain
author_rank: Super Contributor
timestamp: November 12, 2020, 11:29:44 am
content: Quote from: schwukas on November 12, 2020, 09:49:02 amGlad to see that!Also good advice from psi (though I would clear the variable after the printing, yes, messages might be missed but that can't be helped in any case).Yes, the UART is (usually, depends on bit rate and I2S bit clock of course!) much too slow to "see" all he samples on an I2S flow.With a long enough buffer, you probably have the time to print out a short subset of the samples, you need to make some calculations.Another couple of points to keep in mind if you are new to this:The printf family is usually large and slow*One of the most common programming pattern is to try to avoid any heavy lifting (printf, UART) in interrupt routines: just let the main loop know what's happened (e.g. flag** that a buffer is ready) and handle that. In case you want to use a real time OS this even more recommended.Of course, if you don't mind interrupting the program, you can have a look at the buffers using a debugger.* A quite lightweight and functional printf implementation which I often use isthis one.** Remember to use volatile for the communication variables, otherwise the compiler is allowed to optimize your checks away in many cases!

author: schwukas
author_rank: Newbie
timestamp: November 23, 2020, 09:35:39 am
content: Thanks to both of you. Your answers really helped me to understand that whole DMA thing a bit better. It's just a very new concept to me and background stuff and interrupts aren't all that intuitive anyways. I got some meaningful output which has been a big relief. Yet, there are some things I will need to find solutions for, so I might reuse this thread if anything comes up.For now, I just wanted to provide some feedback and "closure". Leaving threads open-ended always bugs me whenever I replied to them.

author: bson
author_rank: Unknown
timestamp: December 01, 2020, 10:56:23 pm
content: You're using a common buffer for all messages. Try:Code:if (HAL_OK == HAL_I2S_Receive_DMA(&hi2s1, (uint16_t*)i2s_dma_buf, 2)) {static const char infomsg[] = "i2s dma ok\r\n";HAL_UART_Transmit(&huart1, (uint8_t*)infomsg, sizeof infomsg - 1, 100);}Also, what does HAL_UART_Transmit() do? If it abandons any existing in-progress output, then that could be lost.