cancel
Showing results for 
Search instead for 
Did you mean: 

Nucleo-F439ZI: SPI DMA normal mode and TIM6 running at background does not work

Hello,

I wanna read an external ADC.

The ADC has following serial process conditions:
1. Get a CNV signal by µC
2. Convert and send BUSY=HIGH to µC
3. After Convert send BUSY=LOW to µC
4. µC send SCK to ADC, then ADC send data and is running as long as it gets SCK signals

That causes, that I cannot run my SPI DMA communication in circular mode, because I have to run the CNV and BUSY process first and always de-/activate SPI (the SCK signal) after it. So I have choosen the normal mode.

But the thing is I want to run the SPI communication fully at the background, also the CNV and BUSY process, that my main while function does not block the regular SPI and Convert process. This is what should be fast always. And at the main while loop I can choose, if I output the data at 100 ms rate or whatever.

So I had the idea to use TIM6´s Callback function to run the CNV/BUSY/DMA-Receive-call at the background, wile the DMA-call also runs at the background.

(I have tested the DMA Communication at the main while loop before, it works, the tim callback also works without the DMA SPI function call)

I use UART outputs with PUTTY.

Here is my Code:

... //Over the main while loop: #ifdef SPI_MODE_DMA cplt_callback_ready = true; // At the beginning the callback was never processing, so it is ready HAL_TIM_Base_Start_IT(&htim6); // Starting the Timer TIM6 to let the callback process the CNV/BUSY and SPI DMA receive function call #endif while(1) { ... } ... // Non blocking function to process the serial procedure with adc and µC (CNV High, then Low, then SPI and so on...) // It functions continously like a while(1) loop void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM6) // TIM1 interrupt trigger check { if (hspi1.State == HAL_SPI_STATE_READY && cplt_callback_ready == true) // Only process, when spi DMA communication is ready already for next communication { if (pinstate(BUSY) == LOW) // When the adc is ready to start a new convert { set_pin(CNV, HIGH); // Give Start signal to convert while (pinstate(BUSY) == HIGH); // Wait the whole converting process set_pin(CNV, LOW); // Now set CNV back to low for the next signal sending test_message("\n \n\nHere starting DMA function.\n"); cplt_callback_ready = false; HAL_SPI_Receive_DMA(&hspi1, (uint8_t*)spi_buffer, sizeof(spi_buffer)); // Now asking adc out for the digital value } } } } // ___________________________________________________________________________________________________ //SPI DMA functions // Callback for Half-Transfer void HAL_SPI_RxHalfCpltCallback(SPI_HandleTypeDef *hspi) { if (hspi->Instance == SPI1) { processing stuff... test_message("\n\nHALF!\n"); } } // Callback for Complete-Transfer void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) { if (hspi->Instance == SPI1) { processing stuff... test_message("\n\FULL!\n"); cplt_callback_ready = true; } }
View more


It runs 2 times and prints "Here starting DMA..." the third time. Then there is no output any longer.
I added the bool "cplt_callback_ready" to be sure, that a callback processing is ready first, before the TIM6 callback calls the next DMA receive. But it does not change anything.

Somehow I dont understand anything, what causes my error, it seems that the interrupts of TIM6 and the SPI DMA interrupt has time issues???

By the way, the TIM6 callback runs perfectly without calling the receive DMA SPI function.

Please help me.

5 REPLIES 5
TDK
Guru

Blocking calls within interrupts can cause issues if the callback doesnt complete in a timely manner. How quickly are these callbacks supposed to happen? And how long does your "processing" and printf take?

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

Hi TDK,

to print I use the UART Function, for example an output suceed like this

 
voltage_value = adc_value * 9.5e-6; // 9,5µV is one step, @20Bit @5V reference voltage (datasheed ADC LTC2378-20 page 9 “Transfer Function�?) sprintf(uart_buffer, "ADC voltage Value: %.6f V\r\n", voltage_value); // Save the value of "voltage_value" as a string "uart_buffer" and again UART output it HAL_UART_Transmit(&huart3, (uint8_t*)uart_buffer, strlen(uart_buffer), HAL_MAX_DELAY);

 This is my function test_message():

void test_message(char* test_msg) { HAL_UART_Transmit(&huart3, test_msg, strlen(test_msg), HAL_MAX_DELAY); }

 

The "processing" are just to write the content of two 16 Bit buffers into one integer 32 bit buffer (because the µC can only have a max block size of 16 Bit and I want to read 20 bit in total). And some bit shifting, that the 32 Bit buffer has the first and second blocks of bits at the right place and to realize the 2´s complement. I guess that should be nothing special so I have called it just "processing" to have a better clear sight.

I have set the TIM6 with a prescaler of 83, and it runs max at 84 MHz, so it scales to 84MHz / (83+1) = 1 MHz.
Additionally I have set the counter period (ARR) to 1, because Want one cycle period of 1 µs, I mean just 1/f = 1 µs period. So it counts only 1 time and reload then, to really reach the f = 1MHz with period of 1µs.

The SPI communication runs with 21MBit/s (Prescaler=4), 16 Bits as data size (already mentioned). But I guess that is unimportant, due to the situation that an SPI communication process happens serially after the CNV-BUSY-process, and at the callback function of TIM6 I added the if statement, that it only does something, when the spi  ready state is active.
Also the bool variable I added to ensure, the cplt callback function of SPI also finished does not change anything.

> TIM6 [...] f = 1MHz with period of 1µs.

That's unrealistically high interrupt rate, especially with Cube/HAL.

And I reiterate what @TDK said above: don't use blocking functions in ISR. Just don't. Even worse, if you use the same function (HAL_UART_Transmit()) both in main() and interrupt context, who knows if they are reentrant, you may run into atomicity issues.

JW

Hi Waclawek,

I was wondering you said that f=1MHz With period of 1 µs is unrealistically high interrupt rate. Because at the clock settings I see that the timers of APB1 and APB2 (TIM1 to TIM13) are working with 84MHz to 168MHz normally. I even used the prescaler to turn it down to 1MHz. This is confusing me, why ST make them working with such a high frequenzy at normal condition, if 1MHz is already unrealistic.

Then I have read your article. It is difficult for me to understand it 100%, but if I got it right, the µC is not able to suceed with more than some hundreds of kHz as frequenzy, right?

So I am thinking that maybe a background SPI communication like I want it (Fully with CNV and BUSY processing) is maybe not possible. Im binded to what I do at the main function because the ADC first needs that CNV and BUSY stuff and only after this I can start the SPI communication really. But then I can only work polling like, altough I use SPI DMA with the interrupts.

Do anybody has a better idea than mine maybe??

Oh and Waclawek, you said blocking functions, can you explain that more detailed? Do you mean functions like a while? And the UART function I use only in the testing context at the callback functions. Normally I wanna use UART outputs only at the main while function.

A thank nevertheless already for you two first of all: TDK and Waclawek

Timer as such can run at any frequency. It can for example generate PWM at some of its pins at more than 1MHz.

It's the interrupts which are problematic, as it takes quite a couple of machine cycles to execute them.

Consider, for example, that somebody leaves you a message on your phone, to go from your home to a shop and bring some goods home from there. It takes some time to get there, buy the goods, and then get back. If for example it takes more than 168 minutes, then if that somebody will command you to go there every 168 minutes, no time remains to you to do something at home. However, the phone has no problem receiving the messages every 168 minutes, regardless of whether you cope with them or not.

And yes, a blocking function is mostly something containing a while. Sending a character through UART takes considerable time - for example, sending one byte at 115200 Bauds takes approximately 8.68us, if the machine clock is 168MHz this means almost 1500 cycles wasted waiting only for one character - and you want to send many. printf() is a wasteful function, too, and non-reentrant too. You may want to find other ways to debug fast events.

JW