Forum: Mikrocontroller und Digitale Elektronik Verständnisfrage zu Timern in ATSAM4E ARM µC's


von Nico (Gast)


Lesenswert?

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

von gerhard (Gast)


Lesenswert?

>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.
da kann ich dir nur recht geben.
für die sam7/9 gab es eine einfache library samt doklu und für alle 
peripherals div. beispiele.

nun zu deinem problem:

>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.
über die register A, B, C kannst du compare werte einstellen. erreicht 
der timer eine der schwellen kann dies einen ausgang schalten oder eine 
irq auslösen.
unterschied waveform und capture:
im waverform mode zählt der timer hoch und bei erreichen der o.a. 
schwellen kann port schalten oder irq ausgelöst werden.
im capture mode misst der timer pulsweite, ticks an einem eingang o.ä.
das datenblatt, chapter TC Operating Modes lässt eientlich keine fragen 
offen.

gruss
gerhard

von Nico (Gast)


Lesenswert?

Hallo nochmal.

Ich habe mittlerweile herausgefunden, wie man den Timer TC0 grundlegend 
einstellt, dass er normal hochzählt und beim Vergleich mit dem Register 
RC einen Interrupt auslöst. Wenn man im Interrupt-Handler auch das 
Statusregister ausliest, hängt sich der µC in der Interruptroutine auch 
nicht mehr auf.

Ich habe auch in etwa verstanden, wie ich die Timer zusammenschalten 
kann (TIOA und BMR-Block-Register).

Nun habe ich ein neues Problem: Ich kann irgendwie nur den Timer Channel 
0 zum Laufen bringen. Ich habe mal ein Testprogramm geschrieben, bei dem 
alle drei Kanäle (TC0 Channel 0 bis 2) auf die exakt gleiche Weise 
eingestellt werden (Also Timer_Clock1, Waveform mode, Trigger bei 
Erreichen des RC-Wertes, der bei allen gleich ist, keine Interrupts 
aktiv). In der Main-Schleife lese ich die TC_CV-Register aller drei 
Kanäle aus und gebe sie jede Sekunde (per SysTick) per UART in HTerm 
aus.

Die Zahl von Kanal 0 ändert sich, der Timer funktioniert. Die anderen 
beiden Werte sind aber durchgehend bei 0 ! Ich habe auch schon den Sync 
im BCR-Register aktiviert, aber es tut sich trotzdem nichts.

Weiß jemand, woran das liegen kann, dass nur der Kanal 0 von TC0 
funktioniert, nicht aber 1 und 2 ?

LG
Nico

von Nico (Gast)


Lesenswert?

Hat sich erledigt!

Man muss tatsächlich alle Channels der Timer auch nochmal separat via 
PMC aktivieren (gleiches für Interrupts im NVIC-Controller)! Ich dachte 
das müsse man nur für Timer0-2. Das ist so in dem Datasheet nicht 
beschrieben (oder ich hab es nicht gefunden).

GOOOTT!! Sowas kompliziertes hab ich bei einem µC ja noch nie erlebt!

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.