I’m developing a small circuit that includes uC and external accelerometer and ADC, both connected to same i2c. Right now i use mutex to assign i2c bus for my external ICs, but I’m wondering, if it is possible, that sheduler can disturb this communication (mostly during read from external IC to uC)? If yes, should I use extra: 1. taskENTER_CRITICAL() and taskEXIT_CRITICAL() or 2. vTaskSuspendAll() and vTaskResumeAll()?
It is possible that the task currently reading some data from an I2C slave is preempted by another task. Given that you enabled preemption by setting configUSE_PREEMPTION to 1 and an other task with a higher priority got ready to run.
If you also enabled time slicing by setting configUSE_TIME_SLICING to 1 tasks with equal priority might get preempted every SysTick if there are multiple tasks with the same priority are ready to run. See also the FreeRTOS Configuration documentation.
So depending on your I2C driver/peripheral this might cause problems e.g. generating an appropriate STOP condition. Is this the issue you have sometimes ?
You can either improve the driver (by ensuring that STOP is always set appropriately even in multitasking applications) or enclose the I2C read in vTaskSuspend/ResumeAll as you proposed. Note that this leaves interrupts enabled (usually desired) allowing that ISRs might still preempt the task. If the ISRs are short enough your I2C read sequence might work (more) reliable.
Using a critical section (which disables interrupts) during the rather slow I2C read sequence will work but might cause a severe impact to all other tasks/drivers of your application.
Thank You for your long and substantive answer.
In my project I use preemtion but not time slicing. Right now i got 2 different tasks:
T1. Read from adc current data , it’s done every 50ms (using vTaskDelayUntil),
T2. Read data from FIFO of accelerometer to own ring buffer, it’s done every 100ms (using vTaskDelayUntil).
Right now both tasks have same priority. Idea of them: T2 try to take I2Cmutex and if it’s done it read accelerometer FIFO current capacity, then in loop read every single FIFO node to own ring buffer and after singe read check status of T1 (with eTaskGetState), if T1 is eReady → unlock I2Cmutex and break loop. So T1 can always take I2Cmutex and perform its action (frequency of T1 is most crucial in this application, so i don’t want to leave idea of using vTaskDelayUntil in this task).
To be clear, I didn’t noticed any interference of i2c bus and sheduler right now. But I was just wondering about performance of all this stuff because of upcoming two task, both with priority lower than T1 and T2:
T3: apply fir filtering for accelerometer data and store filered output somewhere else
T4: send ADC output by UART in some free time.
Mostly this issue I described would be noticeable in T4, so I would like to plan all this before implementation. I think that blocking sheduler will be not proper because of vTaskDelayUntil() but no idea about this performance right now. that’s why i was asking.
Well, in this case I’d think about reading the ADC and the accelerometer in 1 task with reading the accelerometer just every 2nd 50 ms loop. You’d save task resources and also get rid of the mutex overhead. Since the sensor task has the highest prio you’ll get a pretty precise 50 ms timing. But if the FIFO reading takes too much time i.e. delaying the ADC read cycle too much, I’d give the ADC read task the highest prio and just mutex-protect every I2C read call (in ADC and ACCEL task). I think that matches your requirements better and avoids the (kind of) workaround checking the task status of the ADC task. Once the delay of the ADC task expires and is waiting for the mutex to do the I2C read and if the ACCEL task is currently reading the FIFO the next mutex unlock immediately switches to the ADC task.
With this priority scheme using time slicing wouldn’t introduce any unwanted effects at least regarding ADC and ACCEL task behavior.
Edit: When using the single sensor task approach you could also hide the FIFO read time by e.g. using an adjusted delay every 2nd loop of e.g. only 40 ms, flush the FIFO (taking 10 ms) and continue with reading the ADC just in time.
Edit2: Alternatively you could use
vTaskDelayUntil to ensure a fixed 50 ms delay reading the ADC and flush the ACCEL FIFO after every 2nd ADC read. Given that it takes less than 50ms.
Thanks for answer.
I’m gonna test both cases later, but probably I’m more interested in keeping both i2c tasks and mutex. Fortunately, accelerometer has different registers for each byte of data, so 1 task (with reading ADC every 50ms and reading just most significant bytes(to save i2c time usage) for each axis ACCEL FIFO every 100ms) might be very possible to successfull implement.
Gonna response there with results.