fix: 32bit tick timer based on TC0 and TC1

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.
This commit is contained in:
Andreas Dröscher 2018-08-11 21:53:29 +02:00
commit 9d330dde87
2 changed files with 53 additions and 44 deletions

View file

@ -169,59 +169,70 @@ uint32_t RAMFUNC GetCountSspClk(void) {
} }
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// 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 // 1us = 1.5ticks
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
void StartTicks(void){ void StartTicks(void){
//initialization of the timer // initialization of the timer
// tc1 is higher 0xFFFF0000
// tc0 is lower 0x0000FFFF
AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1); 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; 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_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_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_WAVE | AT91C_TC_WAVESEL_UP_AUTO |
AT91C_TC_ACPC_SET | AT91C_TC_ASWTRG_SET; AT91C_TC_ACPA_CLEAR | // RA comperator clears TIOA (carry bit)
AT91C_BASE_TC0->TC_RA = 1; AT91C_TC_ACPC_SET | // RC comperator sets TIOA (carry bit)
AT91C_BASE_TC0->TC_RC = 0; 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
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; // timer disable // synchronized startup procedure
AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_XC1; // from TC0 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)
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // return to zero
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG;
AT91C_BASE_TCB->TCB_BCR = 1; AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
while (AT91C_BASE_TC0->TC_CV > 0);
// 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;
}
// Wait - Spindelay in ticks. // Wait - Spindelay in ticks.
// if called with a high number, this will trigger the WDT... // if called with a high number, this will trigger the WDT...
void WaitTicks(uint32_t ticks){ void WaitTicks(uint32_t ticks){
if ( ticks == 0 ) return; if ( ticks == 0 ) return;
ticks += GET_TICKS; ticks += GetTicks();
while (GET_TICKS < ticks); while (GetTicks() < ticks);
} }
// Wait / Spindelay in us (microseconds) // Wait / Spindelay in us (microseconds)
// 1us = 1.5ticks. // 1us = 1.5ticks.
void WaitUS(uint16_t us){ void WaitUS(uint16_t us){
if ( us == 0 ) return; WaitTicks( (uint32_t)us * 3/2 );
WaitTicks( (uint32_t)us * 3/2 );
} }
void WaitMS(uint16_t ms){ void WaitMS(uint16_t ms){
if (ms == 0) return;
WaitTicks( (uint32_t)ms * 1500 ); 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);
}
void ResetTimer(AT91PS_TC timer){
timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
while(timer->TC_CV > 0) ;
}
// stop clock // stop clock
void StopTicks(void){ void StopTicks(void){
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS;

View file

@ -19,7 +19,7 @@
#include "proxmark3.h" #include "proxmark3.h"
#ifndef GET_TICKS #ifndef GET_TICKS
# define GET_TICKS (uint32_t)((AT91C_BASE_TC1->TC_CV << 16) | AT91C_BASE_TC0->TC_CV) #define GET_TICKS GetTicks()
#endif #endif
void SpinDelay(int ms); void SpinDelay(int ms);
@ -32,17 +32,15 @@ void StartCountUS(void);
uint32_t RAMFUNC GetCountUS(void); uint32_t RAMFUNC GetCountUS(void);
void ResetUSClock(void); void ResetUSClock(void);
void SpinDelayCountUs(uint32_t us); void SpinDelayCountUs(uint32_t us);
//uint32_t RAMFUNC GetDeltaCountUS(void);
void StartCountSspClk(); void StartCountSspClk();
void ResetSspClk(void); void ResetSspClk(void);
uint32_t RAMFUNC GetCountSspClk(); uint32_t RAMFUNC GetCountSspClk();
extern void StartTicks(void); void StartTicks(void);
extern void WaitTicks(uint32_t ticks); uint32_t GetTicks(void);
extern void WaitUS(uint16_t us); void WaitTicks(uint32_t ticks);
extern void WaitMS(uint16_t ms); void WaitUS(uint16_t us);
extern void ResetTicks(); void WaitMS(uint16_t ms);
extern void ResetTimer(AT91PS_TC timer); void StopTicks(void);
extern void StopTicks(void);
#endif #endif