chVTGetSystemTimeX overflow

Discussions and support about ChibiOS/RT, the free embedded RTOS.
0x3333
Posts: 57
Joined: Thu Mar 07, 2019 10:19 pm
Has thanked: 7 times
Been thanked: 6 times

chVTGetSystemTimeX overflow

Postby 0x3333 » Thu Mar 07, 2019 10:45 pm

Hi guys!

This is my first post!

I'm new to Chibios, so if this is a dumb question, please excuse.

I'm using a STM32F103, I have some functions that need to wait for a specific number of milliseconds.

I'm using chVTGetSystemTimeX to get the start time and check periodically if X milliseconds have passed. The problem is that my system time overflows and goes back to zero in a short period of a time, making my function run much before it should.

I don't have a 32bits timer available, I tried increasing the system tick time, but I'm almost in its limit.

Here is a pseudo code:

Code: Select all

systime_t cycle_start_time = chVTGetSystemTimeX();
systime_t cycle_end_time = cycle_start_time + TIME_MS2I(3000);

...

if(!chVTIsSystemTimeWithinX(cycle_start_time, cycle_end_time) {
     runMyFunction();
}


The problem is that if the cycle_start_time is, for example, 0xFF00, after the system time reaches 0xFFFF it will overflow, and the next check it will not be "WithinX"... thus, running the function before it should.

How do you guys handle it?

User avatar
Giovanni
Site Admin
Posts: 12313
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 594 times
Been thanked: 505 times
Contact:

Re: chVTGetSystemTimeX overflow

Postby Giovanni » Fri Mar 08, 2019 8:36 am

Hi,

The system time is meant to overflow, chVTIsSystemTimeWithinX() also works when end<start.

The size of the counter is HW constrained but if you disable tickless mode in chconf.h then you can use a 32 bits system time, in normal mode the counter becomes a normal variable increased by an interrupt.

Giovanni

electronic_eel
Posts: 38
Joined: Sat Mar 19, 2016 8:07 pm
Been thanked: 6 times

Re: chVTGetSystemTimeX overflow

Postby electronic_eel » Fri Mar 08, 2019 8:40 pm

0x3333 wrote:I have some functions that need to wait for a specific number of milliseconds.

I'm using chVTGetSystemTimeX to get the start time and check periodically if X milliseconds have passed.

This sounds like a busy-loop and is not very efficient. Sometimes you need it when you need a very fast reaction time, but if you mention milliseconds I doubt that this is the case for you.

If you really want a busy loop, have a look at chSysPolledDelayX().

If exact timing isn't that critical, have a look at chThdSleepMilliseconds() and family. This allows allows other threads to run while you are waiting. Resolution depends on the system tick frequency.

0x3333
Posts: 57
Joined: Thu Mar 07, 2019 10:19 pm
Has thanked: 7 times
Been thanked: 6 times

Re: chVTGetSystemTimeX overflow

Postby 0x3333 » Sat Mar 09, 2019 4:02 pm

Giovanni wrote:Hi,

The system time is meant to overflow, chVTIsSystemTimeWithinX() also works when end<start.

The size of the counter is HW constrained but if you disable tickless mode in chconf.h then you can use a 32 bits system time, in normal mode the counter becomes a normal variable increased by an interrupt.

Giovanni


Hi Giovanni,

Well, let me explain a little better my situation.

I have a state machine in the main thread, basically a switch case, with states. In some cases, when I switch to another state, I store 2 variables(cycle_start_time/cycle_end_time), using this function:

Code: Select all

static inline void jumpState(app_state_t new_state, uint16_t interval)
{
    app_state = new_state;
    if (interval > 0)
    {
        cycle_start_time = chVTGetSystemTimeX();
        cycle_end_time = cycle_start_time + TIME_MS2I(interval);
    }
    else
    {
        cycle_start_time = 0;
        cycle_end_time = 0;
    }   
}


Parameter new_state is the state I must switch to, interval is an integer, ranging from 2000 to 5000 milis, or zero, which I'll not check if the timeout has passed.

In my main thread I have the switch case:

Code: Select all

        switch (app_state)
        {
        case ST_APP_INIT:
            if (stButtonGetAndClearState(LINE_BTN_START) == ST_BUTTON_SHORT_PRESSED)
            {
                jumpState(ST_APP_CALIBRATION, 0);
            }
            else if (!isWithinInterval())
            {
                jumpState(ST_APP_READY, 0);
            }
            break;

        case ST_APP_CALIBRATION:
            if (!isWithinInterval())
            {
                   // Basically I switch to the next state when the condition is true(Not within interval).
                   ...
            }
            ... More cases ...
        }


isWithinInterval is a macro:

Code: Select all

#define isWithinInterval()      chVTIsSystemTimeWithinX(cycle_start_time, cycle_end_time)


The switch case is inside a while(true) loop, with a chThdSleepMilliseconds(50); in the end.

I have other threads that do the heavy lift, everything is working great, except that if the process starts with the chVTGetSystemTimeX() in the end of the range(0xFFFF), when I check if it is within the start/end, it returns false, but it should return true:

In my log(I print this to debug the timer) I have:

Code: Select all

... a lot of entries == 1 ...
chVTIsSystemTimeWithinX(56074, 62074) == 1, when chVTGetSystemTimeX() == 62049
chVTIsSystemTimeWithinX(56074, 62074) == 0, when chVTGetSystemTimeX() == 62109

When the last line is printed, my state machine switch state to the next state, and the start/end variables are updated to:

Code: Select all

Start: 63489 (Current chVTGetSystemTimeX())
End: 3953 (chVTGetSystemTimeX() + TIME_MS2I(5000))


The first check of this new start/end configuration I get:

Code: Select all

New State: 5(4)
New Time: 63489, 3953
chVTIsSystemTimeWithinX(63489, 3953) == 0, when chVTGetSystemTimeX() == 63549


So my app switches to the new state immediately, it doesn't wait for the 5000 milis.

If the check would be made after the chVTGetSystemTimeX() overflows, the check would return true.

I'll write a function that checks if the start is greater then the end, if so, if the chVTGetSystemTimeX() returns greater than the end, I'll consider within interval. I belive that it will solve, as my timeouts are much smaller than the system time.

0x3333
Posts: 57
Joined: Thu Mar 07, 2019 10:19 pm
Has thanked: 7 times
Been thanked: 6 times

Re: chVTGetSystemTimeX overflow

Postby 0x3333 » Sat Mar 09, 2019 4:03 pm

EDITED BELOW
Last edited by 0x3333 on Sat Mar 09, 2019 4:30 pm, edited 1 time in total.

0x3333
Posts: 57
Joined: Thu Mar 07, 2019 10:19 pm
Has thanked: 7 times
Been thanked: 6 times

Re: chVTGetSystemTimeX overflow

Postby 0x3333 » Sat Mar 09, 2019 4:28 pm

electronic_eel wrote:
0x3333 wrote:I have some functions that need to wait for a specific number of milliseconds.

I'm using chVTGetSystemTimeX to get the start time and check periodically if X milliseconds have passed.

This sounds like a busy-loop and is not very efficient. Sometimes you need it when you need a very fast reaction time, but if you mention milliseconds I doubt that this is the case for you.

If you really want a busy loop, have a look at chSysPolledDelayX().

If exact timing isn't that critical, have a look at chThdSleepMilliseconds() and family. This allows allows other threads to run while you are waiting. Resolution depends on the system tick frequency.


electronic_eel, thanks for the info, I explained a little better in the previous message, I believe its clear now, I'm not using a busy loop.

Another solution would be to use a GPT, but I think it's overkill, I'm used to program to very limited MCUs in the past, I prefer to keep it simple, as my time constraints are not that tight :)

Thanks!

0x3333
Posts: 57
Joined: Thu Mar 07, 2019 10:19 pm
Has thanked: 7 times
Been thanked: 6 times

Re: chVTGetSystemTimeX overflow

Postby 0x3333 » Sat Mar 09, 2019 4:38 pm

I changed the method chTimeIsInRangeX, added a check, now it is working:

Code: Select all

static inline bool chTimeIsInRangeX(systime_t time, systime_t start, systime_t end)
{
    // If the end has overflowed and the current time is greater than start, we're good.
    if (end < start && time > start)
    {
        return true;
    }
    return (bool)((time - start) < (end - start));
}

User avatar
Giovanni
Site Admin
Posts: 12313
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 594 times
Been thanked: 505 times
Contact:

Re: chVTGetSystemTimeX overflow

Postby Giovanni » Sat Mar 09, 2019 5:58 pm

In your example above:

start = 63489
end = 3953
time = 63549

end - start = 6000 (considering the overflow over 16 bits).
time - start = 60

The expression (bool)((time - start) < (end - start)) returns true if I have done it right, which is correct, time is greater than end and within the interval.

I need to verify if there is a problem when systime_t is 16 bits, it is possible there is a type promotion problem. Could you try it as follow?

Code: Select all

return (bool)((systime_t)(time - start) < (systime_t)(end - start));


Giovanni

0x3333
Posts: 57
Joined: Thu Mar 07, 2019 10:19 pm
Has thanked: 7 times
Been thanked: 6 times

Re: chVTGetSystemTimeX overflow

Postby 0x3333 » Sat Mar 09, 2019 9:43 pm

Hi!

With your patch, it worked!

To be honest, I thought about it, but I thought it was too basic to be the truth. Well, it wasn't.

Thanks.

0x3333
Posts: 57
Joined: Thu Mar 07, 2019 10:19 pm
Has thanked: 7 times
Been thanked: 6 times

Re: chVTGetSystemTimeX overflow

Postby 0x3333 » Sat Mar 09, 2019 11:19 pm

Will this be upstreamed or should I work with a local patch? Thanks again.


Return to “ChibiOS/RT”

Who is online

Users browsing this forum: No registered users and 1 guest