Fix time accounting on msp430 Series 1 and Series 2 MCU based platforms.

The problem with the current version of the code was that the condition at the end of the do...while loop at Timer A1 interrupt:
 while((TACCR1 - TAR) > INTERVAL);
evaluates to false whenever TACCR1 == TAR.
Not incrementing TACCR1 in this case leads to Timer A1 interrupt not being called for 2 seconds, until TAR counter reaches TACCR1 again after an overflow.

The patch avoids this problem by changing the condition of the loop, and using CLOCK_LT macro to compare between time values.

The patch also attempts to fix another problem: a read of TAR register while it is being updated may return a lower value than the actual contents. To avoid that, the "read twice and compare results" idiom should be used. As the TAR register is updated by the actual hardware, it is of no importance whether it is read with interrupts disabled or enabled; the problem can occur in both contexts.
This commit is contained in:
Atis Elsts 2014-06-17 15:44:08 +02:00
parent ed87b928c5
commit 705587cdb7
1 changed files with 19 additions and 5 deletions

View File

@ -41,12 +41,26 @@
#define MAX_TICKS (~((clock_time_t)0) / 2)
#define CLOCK_LT(a, b) ((int16_t)((a)-(b)) < 0)
static volatile unsigned long seconds;
static volatile clock_time_t count = 0;
/* last_tar is used for calculating clock_fine */
static volatile uint16_t last_tar = 0;
/*---------------------------------------------------------------------------*/
static inline uint16_t
read_tar(void)
{
/* Same as clock_counter(), but can be inlined */
uint16_t t1, t2;
do {
t1 = TAR;
t2 = TAR;
} while(t1 != t2);
return t1;
}
/*---------------------------------------------------------------------------*/
ISR(TIMERA1, timera1)
{
ENERGEST_ON(ENERGEST_TYPE_IRQ);
@ -57,10 +71,11 @@ ISR(TIMERA1, timera1)
/* HW timer bug fix: Interrupt handler called before TR==CCR.
* Occurs when timer state is toggled between STOP and CONT. */
while(TACTL & MC1 && TACCR1 - TAR == 1);
while(TACTL & MC1 && TACCR1 - read_tar() == 1);
last_tar = read_tar();
/* Make sure interrupt time is future */
do {
while(!CLOCK_LT(last_tar, TACCR1)) {
TACCR1 += INTERVAL;
++count;
@ -76,9 +91,8 @@ ISR(TIMERA1, timera1)
++seconds;
energest_flush();
}
} while((TACCR1 - TAR) > INTERVAL);
last_tar = TAR;
last_tar = read_tar();
}
if(etimer_pending() &&
(etimer_next_expiration_time() - count - 1) > MAX_TICKS) {