From 8ff31e935d19bc53dd8f27877b7450cafba4a942 Mon Sep 17 00:00:00 2001 From: AntiCat Date: Tue, 14 Aug 2018 09:24:02 +0200 Subject: [PATCH] fix: 32bit tick timer based on TC0 and TC1 (#653) TC1 counts the number of TC0 overflows (carry bits). In random conditions TC1 would return or stay at zero, instead of counting up. This due to the behavior of the reset signal. SAM7S Series Datasheet, 33.5.6 Trigger: Regardless of the trigger used, it will be taken into account at the following active edge of the selected clock. This means that the counter value can be read differently from zero just after a trigger, especially when a low frequency signal is selected as the clock. The new code first prepares TC1 and asserts TC1 trigger and then prepares TC0 and asserts TC0 trigger. The TC0 start-up will reset TC1. --- armsrc/util.c | 65 +++++++++++++++++++++++++++++++++------------------ armsrc/util.h | 3 ++- 2 files changed, 44 insertions(+), 24 deletions(-) diff --git a/armsrc/util.c b/armsrc/util.c index 59dfe07b..8a8d6657 100644 --- a/armsrc/util.c +++ b/armsrc/util.c @@ -448,31 +448,52 @@ uint32_t RAMFUNC GetCountSspClk(){ // ------------------------------------------------------------------------- -// Timer for bitbanging, or LF stuff when you need a very precis timer +// Timer for bitbanging, or LF stuff when you need a very precis timer // 1us = 1.5ticks // ------------------------------------------------------------------------- void StartTicks(void){ - //initialization of the timer - // tc1 is higher 0xFFFF0000 - // tc0 is lower 0x0000FFFF + // initialization of the timer AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1); AT91C_BASE_TCB->TCB_BMR = AT91C_TCB_TC0XC0S_NONE | AT91C_TCB_TC1XC1S_TIOA0 | AT91C_TCB_TC2XC2S_NONE; + + // disable TC0 and TC1 for re-configuration AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + + // first configure TC1 (higher, 0xFFFF0000) 16 bit counter + AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_XC1; // just connect to TIOA0 from TC0 + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // re-enable timer and wait for TC0 + + // second configure TC0 (lower, 0x0000FFFF) 16 bit counter AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK | // MCK(48MHz) / 32 - AT91C_TC_WAVE | AT91C_TC_WAVESEL_UP_AUTO | AT91C_TC_ACPA_CLEAR | - AT91C_TC_ACPC_SET | AT91C_TC_ASWTRG_SET; - AT91C_BASE_TC0->TC_RA = 1; - AT91C_BASE_TC0->TC_RC = 0; + AT91C_TC_WAVE | AT91C_TC_WAVESEL_UP_AUTO | + AT91C_TC_ACPA_CLEAR | // RA comperator clears TIOA (carry bit) + AT91C_TC_ACPC_SET | // RC comperator sets TIOA (carry bit) + AT91C_TC_ASWTRG_SET; // SWTriger sets TIOA (carry bit) + AT91C_BASE_TC0->TC_RC = 0; // set TIOA (carry bit) on overflow, return to zero + AT91C_BASE_TC0->TC_RA = 1; // clear carry bit on next clock cycle + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // reset and re-enable timer + + // synchronized startup procedure + while (AT91C_BASE_TC0->TC_CV > 0); // wait until TC0 returned to zero + while (AT91C_BASE_TC0->TC_CV < 2); // and has started (TC_CV > TC_RA, now TC1 is cleared) + + // return to zero + AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; + AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + while (AT91C_BASE_TC0->TC_CV > 0); +} - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; // timer disable - AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_XC1; // from TC0 - - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - AT91C_BASE_TCB->TCB_BCR = 1; - - // wait until timer becomes zero. - while (AT91C_BASE_TC1->TC_CV > 0); + +uint32_t GetTicks(void) { + uint32_t hi, lo; + + do { + hi = AT91C_BASE_TC1->TC_CV; + lo = AT91C_BASE_TC0->TC_CV; + } while(hi != AT91C_BASE_TC1->TC_CV); + + return (hi << 16) | lo; } @@ -480,30 +501,28 @@ void StartTicks(void){ // if called with a high number, this will trigger the WDT... void WaitTicks(uint32_t ticks){ if ( ticks == 0 ) return; - ticks += GET_TICKS; - while (GET_TICKS < ticks); + ticks += GetTicks(); + while (GetTicks() < ticks); } // Wait / Spindelay in us (microseconds) // 1us = 1.5ticks. void WaitUS(uint16_t us){ - if ( us == 0 ) return; WaitTicks( (uint32_t)us * 3 / 2 ) ; } void WaitMS(uint16_t ms){ - if (ms == 0) return; WaitTicks( (uint32_t)ms * 1500 ); } // Starts Clock and waits until its reset void ResetTicks(void){ - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - while (AT91C_BASE_TC1->TC_CV > 0); + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + while (AT91C_BASE_TC0->TC_CV > 0); } diff --git a/armsrc/util.h b/armsrc/util.h index 0148e1dd..58dcdd52 100644 --- a/armsrc/util.h +++ b/armsrc/util.h @@ -44,7 +44,7 @@ void FormatVersionInformation(char *dst, int len, const char *prefix, void *vers //iceman's ticks.h #ifndef GET_TICKS -# define GET_TICKS (uint32_t)((AT91C_BASE_TC1->TC_CV << 16) | AT91C_BASE_TC0->TC_CV) +# define GET_TICKS GetTicks() #endif void SpinDelay(int ms); @@ -62,6 +62,7 @@ void ResetSspClk(void); uint32_t RAMFUNC GetCountSspClk(); extern void StartTicks(void); +extern uint32_t GetTicks(void); extern void WaitTicks(uint32_t ticks); extern void WaitUS(uint16_t us); extern void WaitMS(uint16_t ms); -- 2.39.5