How to use taskENTER_CRITICAL()

jeanalban wrote on Tuesday, March 03, 2015:

Hello everybody,

I created two tasks: vCore_Loop (uxPriority = 4, launched every 5ms) and vScreen_Loop (uxPriority = 3, launched every 1ms).

vScreen_Loop communicates with a graphical screen via I2C. This low-priority task is often swapped by vCore_Loop task. My problem happens when vScreen_Loop is swapped during an I2C communication: my I2C driver creates an error because the communication is badly interrupted.

To solve this trouble, I added taskENTER_CRITICAL(); and taskEXIT_CRITICAL(); between the beggining and the end of the I2C communication function, to be sure that this region will not be swapped by the RTOS scheduler.

To my surprise it did not affect the task swapping, vScreen_Loop is still swapped by vCore_Loop during the I2C communication. Do you have any ideas of why?

note: if the I2C communication function is called by the high-priority task vCore_Loop I have no troubles.

Thanks a lot for your help!

Jean

rtel wrote on Wednesday, March 04, 2015:

To solve this trouble, I added taskENTER_CRITICAL(); and taskEXIT_CRITICAL();

That might not be a good solution if the I2C communication takes a long time as it will [excepting the rest of your post] prevent context switches or interrupts occuring while the I2C communication is in process. That is a particular problem if the I2C communication is itself using interrupts - which it probably is - as the interrupts will not execute. A neater and more real time friendly solution would be to have a DMA handle the I2C communication.

To my surprise it did not affect the task swapping, vScreen_Loop is still swapped by
vCore_Loop during the I2C communication. Do you have any ideas of why?

My guess would be that the I2C driver is enabling interrupts directly, without using the taskENTER/EXIT_CRITICAL() macros. The macros will count interrupt enable/disable nesting, but the nesting count will only work if the macros are used in all cases that interrupts are disabled/enabled. If the I2C driver enables interrupts directly then they will become enabled even though the nesting count is not 0.

  • NOTE - many FreeRTOS ports do not disable interrupts globally, as my reply would infer, but just disables a subset of interrupts.

Regards.

jeanalban wrote on Wednesday, March 04, 2015:

“That might not be a good solution if the I2C communication takes a long time as it will”

Yes I know, but for the moment I want something without interrupt. So I did not set any I2C interrupts in my code. Each time the microprocessor communicate via I2C it waits for the slave with something like while(!i2c_message_is_received). I know it’s not real-time friendly but I have to do that to achieve my tests.

“My guess would be that the I2C driver is enabling interrupts directly”

I think that my I2C driver don’t generates interrupts because I don’t enable them. So it’s strange that the task is swapped between taskENTER_CRITICAL() macros. But maybe it comes from low-end I2C interruptions I can’t manage…

(note: I use a STM32F427)

heinbali01 wrote on Thursday, March 05, 2015:

Hi Jean,

Are you using an i2c library from ST, like e.g. stm32f4xx_hal_i2c.c ?
And are you using the DMA-variants of those functions?

Remember that a lighter way of preventing task switching is vTaskSuspend/vTaskResume.

I don’t know why you still observe task-switches after calling taskENTER_CRITICAL. But what I do know is that i2c is a robust protocol that is resistant to delays.

You wrote:

because the ( i2c ) communication is badly interrupted

What exactly do you mean with that?
Maybe some data corruption takes place when you get an interrupt at the wrong moment?

Or is your STM32F4xx a slave in the protocol? In that case I could understand that you don’t want any interruption.

Regards.

jeanalban wrote on Friday, March 06, 2015:

Hi Hein,

Yes I use the i2c library stm32f4xx_i2c.c but i’m not using any DMA variant.

My i2c communication is badly interrupted because I send a message to the slave, then I wait for the slave multiple answers, and during this messages reception the task is swapped. When the task returns in the i2c function after the swap, the function still wait for the slave answer but this answer is already send: this is my problem with the while(!i2c_message_is_received)

My I2C is configured as the master.

I hope it’s clear!
Thanks

richard_damon wrote on Friday, March 06, 2015:

I am not familiar with that part, but I am very surprised at your claimed data loss, as the master controls the timing of the I2C bus, and every I2C Controller I know will hold the cycle until you take the data. (If it is SMD compliant and you get swapped out for a long time, then it would abort the cycle).

Holding up the processor while you do the I2C transaction is VERY inefficient for the processor, this would be an ideal use of interrupts and letting everything else run while you are getting the results and then resuming. If the rest of your system is really that relaxed in requirements, then you probably don’t really need an RTOS, but can just turn your program into the simple big while loop type of implementation.

One solution to your problem, if you can’t figure out where you are getting swapped out, is to boost your priority to be above every other task, then nothing can take over.

jeanalban wrote on Tuesday, March 10, 2015:

Hi Richard,

When I set a higher priority it’s working fine.

I don’t understand why I loss datas wy the task is a low priority. I was holding the processor during the I2C transaction in order to quickly test the I2C communication and make a robust configuration the slave, but now I have to switch to a real time communication and use interrupts.

Thanks for all your help and see you soon!
Jean