davidbrown wrote on Monday, April 06, 2009:
I haven’t actually started using FreeRTOS yet - I’m currently wandering the forum to gauge the level of community interest and support. However, I can still give some general advice.
You might not need to use a semaphore or critical section at all here, depending on your task priorities, counter speeds, etc. Consider the function:
// Assumes time_variable and actual_timer_register_count
// are both volatile uint16_t
uint32_t getTimestamp(void) {
uint16_t a1, a2, b;
a1 = time_variable;
while (1) {
b = actual_timer_register_count;
a2 = time_variable;
if (a1 == a2) break;
a1 = a2;
}
return ((uint32_t) a2 << 16) | b;
}
There are a couple of points about this code. First, it is theoretically unbounded if you can’t be sure that it will not be continuously pre-empted during the loop. Secondly, it will only be correct if interrupts are enabled - if they are not, then a roll-over of the timer between reading time_variable and then reading actual_timer_register_count will be lost - you’ll get the old value of time_variable, followed by the rolled-over value of actual_timer_register_count.
Note also that MEdward’s suggestion of using a critical section will suffer from the same flaw, as the incrementing of actual_timer_register_count is not stopped by the critical section.
An alternative method if your are confident that your task will not be pre-empted for longer than half a counter period (you can use a critical section to be sure) is:
// Assumes time_variable and actual_timer_register_count
// are both volatile uint16_t
uint32_t getTimestamp(void) {
uint16_t a, b1, b2;
b1 = actual_timer_register_count;
while (1) {
a = time_variable;
b2 = actual_timer_register_count;
if (b1 <= b2) break;
b1 = b2;
}
return ((uint32_t) a << 16) | b2;
}
These can be combined to give a single function that is safe from rollovers regardless of the state of interrupts, or how long it is pre-empted, and does not need any critical sections:
uint32_t getTimestamp(void) {
uint16_t a1, a2, b1, b2;
a1 = time_variable;
b1 = actual_timer_register_count;
while (1) {
a2 = time_variable;
b2 = actual_timer_register_count;
if ((a1 == a2) && (b1 <= b2)) break;
a1 = a2;
b1 = b2;
}
return ((uint32_t) a2 << 16) | b2;
}
This will still suffer from the possibility that a pre-emption could occur between reading time_variable for a2 and reading actual_timer_register_count for b2 and last longer than a timer period. However, the value returned will still be a valid timestamp for a point in time between the entry to and exit from the function - that’s all you can ask of a timestamp function.