Task hangs semaphore-protected share resource

nobody wrote on Monday, May 30, 2005:

Hello everyone,

I have a problem that I cannot put my finger behind (I am relatively new to FreeRTOS).

My example program has two tasks, plus the idle task, with the preemtive scheduler (it runs on an LPC2138, it is compiled with GCC). One high-priority task sits in a loop waiting for something to do. As there is nothing to do (yet), the task just sits in a loop calling "vTaskDelay(10)". No trouble here.

The other (low priority) task runs in a loop doing continous transfers over an I2C protocol, in small bursts (but no delay between the bursts). As the I2C interface is intended to be shared between several tasks, I folded the function that does a single burst transfer inside xSemaphoreTake() and xSemaphoreGive() calls.

This runs for several hundreds or thousands of "bursts", and then everything stops. xSemaphoreTake() does not time-out either. I put logging (over a serial line) in the code to verify whether xSemaphoreTake() or xSemaphoreGive() hangs, but then the problem "magically" goes away (logging slows things down).

I replaced the semaphore with taskENTER_CRITICAL() and taskEXIT_CRITICAL(), and everything works again.

So… just use a critical section, one would say. However, the high-priority task does not use I2C and it would be unfortunate if it were "held up" by the low-priority task(s).

Oh, another thing that I tested was using the semaphore-protected I2C protocol without ever starting the scheduler (and without logging). This gives no problem either. At first sight, it appears that particular sequence of grabbing/freeing the semaphore and the scheduler causes the task to be blocked, especially if the "take/give" sequence is very quick.

Any help on this is greatly appreciated. Thanks in advance,

rtel wrote on Monday, May 30, 2005:

> As the I2C interface is intended to be shared
> between several tasks

As a slight aside - depending on your application it can be a nice way to share a peripheral to have one task that manages the peripheral.  This task just takes messages that need transmission from a queue and transmits them on the i2c.  No other task is allowed to access the hardware.  If another task wants to use the i2c is send the message on a queue to the manger task - which removes it from the queue and sends it.  It does not matter then how many tasks send messages to the manager task, no mutual exclusion is required.

>This runs for several hundreds or thousands
>of "bursts", and then everything stops.
>xSemaphoreTake() does not time-out either.

Does the high priority task still run even when the i2c task has stopped - or does everything hang?

If everything hangs the most likely cause is a memory corruption somewhere - you might want to try increasing the stack sizes a bit.

Does your i2c interface use interrupts or poll?

> So… just use a critical section, one would say

I wouldn’t :-)  Whenever there is unexplained behaviour it is always best to get to the cause rather than just find a work around.  Otherwise it will always come back an bite you.

The WizNET demo application uses an interrupt driven i2c interface on an LPC2000. 

nobody wrote on Tuesday, May 31, 2005:

This turns out to be a hardware error. The slave in the I2C protocol does not behave as it should (in keeping the SCL low while processing). This apparently confuses the LPC2000 as much as needing a power cycle to recover. The occurence of the error is heavily timing dependent, which is why critical sections gave a so much different result from semaphores.

The flaky chip in question is the microchip MPC23016 (I/O expander). Our temporal solution is to run the I2C protocol at 50 kHz instead of 400 kHz.

Thanks a lot for thinking with us.
Kind regards,