Hallo,
ich versuche gerade, den ATSAM4E von Atmel kennenzulernen. Ich habe mit
ARM Cortex-M3 µC's zwar schon zu tun gehabt (STM32, TI Stellaris), aber
im Vergleich zum ATSAM3/4 gibt es bei denen eine schöne übersichtliche
und einfach zu benutzende DriverLib. Atmel hat zwar das ASF, aber ich
denke es ist nicht neu wenn ich sage, dass das total be******** ist!
Schlecht bis gar nicht dokumentiert, extrem verstrickt und komplex und
die Beispiele sind größtenteils unbrauchbar.
Ich versuche, den SAM4E bare-metal mit Hilfe des Datenblattes zu
programmieren. Auf diese Weise habe ich immerhin schon das externe Quarz
mit PLL und einen UART zum Laufen gebracht.
Als nächstes stehen die Timer an. Da fangen die Probleme allerdings auch
an.
Mein aktuelles Projekt hat mit Zeitsynchronisation über Bluetooth LE zu
tun. Dafür brauche ich einen 64-Bit timer, der in 1µs-Schritten zählt.
Ich hatte das Projekt vorher auf einem ATxMega128A, da war das
programmiertechnisch zwar nicht schwierig, wegen fehlender FPU war aber
die Berechnung der Zeitunterschiede extrem langsam (mit 64-Bit
float-Bibliothek aus diesem Forum brauchte eine Berechnung zwischen 200
ms und 1 s !) und wegen fehlender Hardware-Flow Control die
Kommunikation mit dem Bluetooth-Modul sehr unzuverlässig. Daher habe ich
mich für einen Cortex-M4 entschieden.
Ich würde gerne einen Timer haben (nicht SysTick, der wird unabhängig
für andere Aufgaben verwendet), der in 1ms-Schritten (später dann in
1µs-Schritten) bestenfalls einen anderen Timer triggert. Es geht aber
auch, wenn er erstmal einfach nur in diesem Intervall einen Interrupt
auslöst.
Als nächstes würde ich gerne zwei 32-Bit Timer zu einem 64-Bit Timer
zusammenschalten.
Beim xMega ging das alles einfach über Events. Beim SAM4E habe ich
absolut keine Ahnung ob das überhaupt möglich ist !
Ich habe mal folgende Initialisierung geschrieben:
1 | if(!(PMC->PMC_PCSR0 & (1u <<ID_TC0))) //Enable TC0
|
2 | PMC->PMC_PCER0 = (1u << ID_TC0);
|
3 |
|
4 | NVIC_DisableIRQ(TC0_IRQn);
|
5 | NVIC_GetPendingIRQ(TC0_IRQn);
|
6 |
|
7 | TC0->TC_CHANNEL[0].TC_CCR = TC_CCR_CLKDIS; //Disable Timer
|
8 | TC0->TC_CHANNEL[1].TC_CCR = TC_CCR_CLKDIS;
|
9 | TC0->TC_CHANNEL[2].TC_CCR = TC_CCR_CLKDIS;
|
10 |
|
11 | TC0->TC_CHANNEL[0].TC_IDR = 0xFFFFFFFF; //Disable interrupts
|
12 | TC0->TC_CHANNEL[0].TC_SR;
|
13 |
|
14 | TC0->TC_CHANNEL[0].TC_IER = TC_IER_CPAS; //Timer Channel0 RA Compare interrupt
|
15 | TC0->TC_CHANNEL[0].TC_RC = 0x2EDF; // 11999 counts = 1ms
|
16 | TC0->TC_CHANNEL[0].TC_RA = 0x2EDF;
|
17 |
|
18 | TC0->TC_CHANNEL[0].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 | TC_CMR_WAVE | TC_CMR_CPCTRG;
|
19 |
|
20 | //TC0->TC_CHANNEL[1]//C_RC = 0xFFFFFFFF;
|
21 |
|
22 | TC0->TC_CHANNEL[0].TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG;
|
23 |
|
24 | NVIC_SetPriority(TC0_IRQn, 6);
|
25 | NVIC_EnableIRQ(TC0_IRQn); //enable Timer0 interrupt
|
Ich verstehe nicht ganz, wie das mit den Registern A, B und C genau
funktioniert und ob ich den Timer in Waveform oder Capture-Modus
betreiben muss.
Wenn ich den TC_IER auf TC_IER_CPCS stelle, wird der Interrupt mit jedem
Clock-Zyklus ausgelöst, was praktisch den gesamten Prozessor lahmlegt.
Bei TC_IER_COVFS zählt der Timer ewig lange, daher habe ich noch nicht
getestet ob der Interrupt beim Overflow auslöst. Jedenfalls ist das eh
nicht das, was ich möchte.
Bei TC_IER_CPAS passiert garnichts, ich kann aber durch einfaches
Auslesen des TC_CV-Registers sehen, dass der Timer sich nach 11999
Counts zurücksetzt. Immerhin das geht !
Kennt sich jemand mit den Timern aus und kann mir sagen, ob mein
Vorhaben überhaupt mit dem SAM4E umsetzbar ist und wenn ja, wie ? Ein
paar Tipps oder ein kleiner Beispielcode wären super!
Also zumindest das Auslösen eines Interrupts alle 1ms wie beim SysTick
Timer wäre für den Anfang schon sehr hilfreich!
LG
Nico