I don't know if it is an issue specific to Atmel SAMC21 Cortex-M0+ devices or not.
I wrote a simple timer driver: a 32-bits hw counter clocked at 875kHz (14MHz/16) that triggers an interrupt on overflow (every 1h 21'). In the interrupt I increment the 32-bits global variable _ticks_high. The 64-bits number composed by _ticks_high (upper 32-bits) and the hw counter (lower 32-bits) is my system tick. This 64-bits software counter, incremented at 875kHz, will never overflow during my life, so it's good :-)
In timer.h I have:
--- Start of timer.h --- #include #include // Atmel specific
#define TIMER_FREQ 875000
typedef uint64_t Timer; extern uint32_t _ticks_high;
#define volatileAccess(v) *((volatile typeof((v)) *) &(v))
static inline uint64_t ticks(void) { uint32_t h1 = volatileAccess(_ticks_high); TC0->COUNT32.CTRLBSET.reg = TC_CTRLBSET_CMD(TC_CTRLBSET_CMD_READSYNC_Val); uint32_t l1 = TC0->COUNT32.COUNT.reg; uint32_t h2 = volatileAccess(_ticks_high); if (h2 != h1) return ((uint64_t)h2 COUNT32.INTFLAG.reg & TC_INTFLAG_OVF) { ++_ticks_high; TC0->COUNT32.INTFLAG.reg = TC_INTFLAG_OVF; } } ...
--- End of timer.c ---
The idea is simple (and stolen from a post appeared on this newsgroup). At first, the 64-bits software counter must be calculated disabling interrupts, because if a timer interrupt triggers during calculation, the overall software counter could be wrong. By reading _ticks_high before and after reading hw counter, we can avoid to disable the interrupts.
Now I have a code that uses timers. It's a simple state-machine that manages communication over a bus.
--- Start of bus.c --- ... int bus_task(void) { switch(bus.state) { case BUS_IDLE: if (TimerExpired(&bus.tmr_answer)) { /* Send new request on the bus */ ... TimerSet(&bus.tmr_answer, timeout_answer); bus.state = BUS_WAITING_ANSWER; } break;
case BUS_WAITING_ANSWER: if (TimerExpired(&bus.tmr_answer)) { /* No reply */ bus.state = BUS_IDLE; TimerSet(&bus.tmr_answer, 0); } else { if (reply_received() == true) { /* Analyze the reply */ bus.state = BUS_IDLE; TimerSet(&bus.tmr_answer, 0); } } break; } return 0; } ...
--- End of bus.c ---
I don't think I need to explain the code in bus.c. The only thing to specify is that bus_task() is called continuously in the main loop.
99% of the time this code works well. Unfortunately I have seen some strange events. Rarely (very rarely, one time in a week) the bus seems frozen for a time. After that it restarts the normal activity magically. There's a thing that relates those strange events to driver of timers: the bus stall time lasts exactly 1h 21', the overflow period of hw counter.I suspect there's a problem in my low-level driver and sometimes, maybe near the overflow, the code doesn't work as I expect. Maybe the TimerSet() function sometimes sets a wrong value to the uint64_t timer, maybe a tick value that will happen only at the next overflow of the hw counter.
Do you see where is the problem?