2

I have a problem with vTaskDelayUntil() function not making a delay but finishing immediately. Here is the code:

TickType_t xLastWakeTime = xTaskGetTickCount();
while(1){
    if (xSemaphoreTake(xSemaphoreRS485, portMAX_DELAY) == pdTRUE) {
        printf("S display data %d\n", xTaskGetTickCount());
        sendDisplayData();
        printf("E display data %d\n", xTaskGetTickCount());
        xSemaphoreGive(xSemaphoreRS485);
        printf("W display data %d\n", xLastWakeTime);
        vTaskDelayUntil(&xLastWakeTime, 2000);
    }
}

From this I get following output:

S display data 29928
E display data 30534
W display data 3919
S display data 30534
E display data 31140
W display data 5919
S display data 31140
E display data 31746
W display data 7919
S display data 31746
E display data 32352
W display data 9919

The function sendDisplayData() takes about 670 ms to execute and xTaskGetTickCount() confirms it. Then the task should wait around 1230 ms so the whole iteration could take 2000 ms. But vTaskDelayUntil() finishes immediately. First execution ended at 30534 and the second one starts at 30534 too. Value returned by xTaskGetTickCount() proves there was no delay introduced by vTaskDelayUntil(). I can also see it by frequency of output of sendDisplayData().

The second funny thing is that xLastWakeTime shows totally different values and those values are in fact incremented by 2000. Shouldn't it store similar value as returned by xTaskGetTickCount()?

grzegorz
  • 331
  • 3
  • 16
  • 1
    Do you have #define INCLUDE_vTaskDelayUntil 1 in your config file? And are you sure you not redefined TickType_t to let's say signed 16 bit? Try moving TickType_t xLastWakeTime = xTaskGetTickCount(); after if with semaphore take, will it now show proper values? (I know it'll not make what you want just for plain checking if this is caused that in first time getting to vTaskDelayUntil you already exceeded xLastWakeTime+2000. – koper89 Jan 14 '17 at 16:36
  • Yes, I have this define and TickType_t is 32-bit type. Moving declaration and initialization of xLastWakeTime helps! I wonder how. Of course my original code wasn't perfect because taking a semphore can take up to 10 seconds and first vTaskDelayUntil() would exit immediately. But I still don't know why in the second iteration that variable was set to 5919 not 30534. – grzegorz Jan 14 '17 at 18:21

2 Answers2

6

In your first iteration xLastWakeTime has value 3919, and you request an increment of 2000, so a delay until 5919, but you have called it at time 30534.

From the documentation of vTaskDelayUntil()

It should be noted that vTaskDelayUntil() will return immediately (without blocking) if it is used to specify a wake time that is already in the past.

Your task spent 26009 ticks (29928 - 3919) blocked on the initial semaphore. Your target 2000 tick increment has long past.

I would suggest the following is nearer at least to what you intended

for(;;)
{
    if (xSemaphoreTake(xSemaphoreRS485, portMAX_DELAY) == pdTRUE)  // Lock
    {
        TickType_t xLastWakeTime = xTaskGetTickCount();
        sendDisplayData();
        xSemaphoreGive(xSemaphoreRS485); // Unlock

        vTaskDelayUntil(&xLastWakeTime, 2000);
}

This will make the loop iteration take 2000 ticks in total including the time taken to execute sendDisplayData() plus the time waiting for the RS485 resource to become available, which is I think what you intended.

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • The semaphore here works as a mutex for synchronizing access to RS485 link. The task needs to obtain the semaphore before it executes sendDisplayData(). When the RS485 link is not used by other task, I want this one to execute sendDisplayData() every two seconds. And please take a look at subsequent iterations. How 5919 and 7919 can be explained in relation to 30534? – grzegorz Jan 14 '17 at 17:54
  • 1
    @grzegorz : Fair enough, but after the initial synchronisation you are not synchronising anything since you are giving the semaphore immediately before and in the same context as you are taking it. Jour "justification" is more an indication that you have not understood my answer than a reason for the code being the way it is. Possibly you still need the semaphore wait, but you need to remove the sem give, since that defeats your synchronisation - that is a different issue and not within the scope of your question - this answer will fix the problem you described. – Clifford Jan 14 '17 at 19:17
  • 1
    Regarding teh other iterations - yes they are explained in this answer. You start off 26000 ticks behind, it will take a number of iterations incrementing the target time by 2000 before it catches up with the current time. It is clear what is happening from your debug output. – Clifford Jan 14 '17 at 19:19
  • This semaphore works as a mutex and it is handled in a very usual way: taking before using a resource and giving back when the resource is no longer used by this task. During the delay there is a context switch, second task takes the semaphore and uses the resource (RS485 link). I can't hold the mutex constantly. Thanks for clarification about vTaskDelayUntil(). I thought it sets xLastWakeTime with current number of ticks but in reality it just adds second parameter in order to catch up. In my project it can't happen because semaphore is taken by other task too often and for too long time. – grzegorz Jan 14 '17 at 19:38
  • @grzegorz : OK; I did not realise that the same sem give/take API calls are used for both regular semaphores and mutexes in FreeRTOS. Just one more reason to dislike FreeTROS to add to my list. – Clifford Jan 14 '17 at 19:53
  • Yes, that's how it looks like in FreeRTOS :) – grzegorz Jan 14 '17 at 19:58
  • If `vTaskDelayUntil()` to set `xLastWakeTime` to the current time, and that is what you intended then the solution is simple assign `xLastWakeTime` in the loop. – Clifford Jan 14 '17 at 20:00
  • Yes, that's what koper89 suggested and it solved the problem. Thanks for help. – grzegorz Jan 14 '17 at 20:02
  • @grzegorz : Too bad he did not post an answer then - you'll have to accept mine! ;-) Apart from that he may have given a solution, bu the did not explain the cause. Updated BTW. – Clifford Jan 14 '17 at 20:08
  • Your updated answer can be definitly marked as solution :) – grzegorz Jan 14 '17 at 20:15
  • @grzegorz : But your question was about why it returned immediately - you did not ask for a solution; that was a free bonus! – Clifford Jan 14 '17 at 20:17
1

As per above, vTaskDelayUntil() will return immediately if the specified wake time is already in the past. Suggest using vTaskDelay() instead.

Richard
  • 3,081
  • 11
  • 9