Forum: Mikrocontroller und Digitale Elektronik Wie benutze ich die Timer vom AM64x von TI


von Flevi (florian_o)


Lesenswert?

Hallo,

ich habe eine Frage bezüglich der Timer beim AM64x von TI.
Laut Datenblatt 
(https://www.ti.com/lit/ug/spruim2g/spruim2g.pdf?ts=1690449545593&ref_url=https%253A%252F%252Fwww.ti.com%252Fproduct%252FAM6442%253FkeyMatch%253DAM6442%2526tisearch%253Dsearch-everything%2526usecase%253DGPN) 
ab Seite 9691 kann ich die Timer in verschiedenen Modi betreiben 
(overflow, compare, und capture).

Im Benutzerhandbuch vom SDK ist nur ein kleines Beispiel für die Timer 
angegeben 
(https://software-dl.ti.com/mcu-plus-sdk/esd/AM64X/latest/exports/docs/api_guide_am64x/KERNEL_DPL_TIMER_PAGE.html) 
und die Doku zur Timer API erwähnt keine Funktionen für das Einstellen 
des Modus.

Meine Frage ist nun, wie konfiguriere ich die Timer mit all den 
Einstellungen, die im Datenblatt genannt werden? Direkt über die 
Register? Wenn ja, wie greife ich darauf zu?

Und wie verbinde ich einen Timer mit einem Interrupt? In der API 
(https://software-dl.ti.com/mcu-plus-sdk/esd/AM64X/latest/exports/docs/api_guide_am64x/group__KERNEL__DPL__TIMER.html) 
finde ich so etwas, wie
1
uint32_t TimerP_isOverflowed(uint32_t baseAddr)
. Um das zu benutzen, müsste ich ja selber aktiv auf den Overflow 
prüfen, z.B. so:
1
while(true) {
2
  if(TimerP_isOverflowed(baseAddr)) {
3
    // Interrupt...
4
    TimerP_clearOverflowInt(baseAddr);
5
  }
6
}
Das würde mir aber den Prozess blockieren, bzw. das ständige Prüfen wäre 
doch dann kein "echter" Interrupt mehr.

Für einen Schubs in die richtige Richtung wäre ich hier sehr dankbar!

von Rolf (rolf22)


Lesenswert?

Flevi schrieb:

> Im Benutzerhandbuch vom SDK ist nur ein kleines Beispiel für die Timer
> angegeben
> 
(https://software-dl.ti.com/mcu-plus-sdk/esd/AM64X/latest/exports/docs/api_guide_am64x/KERNEL_DPL_TIMER_PAGE.html)

Ich arbeite nicht mit diesem µP, aber im Prinzip läuft es immer ähnlich.

In dem Beispiel siehst du
"timerParams.oneshotMode = 0;"

IntelliSense-Funktion der IDE? Ich vermute mal stark, dass man so auch 
die anderen (in dem Beispiel nicht benutzten) Einstellungen machen kann. 
Bei einer vernünftigen IDE schreibst du timerParams., und die IDE bietet 
dir dann nach dem Punkt innerhalb einer Sekunde alle 
Auswahlmöglichkeiten an. Anhand der Namen kannst/musst du dann die 
Funktion erraten
Geht das denn nicht?

> "timerParams.enableOverflowInt = 1;

Siehe oben. Mit dieser Einstellung aktiviert du den Overflow-Interrupt 
des Timers, d. h. die von dir zu schreibende Interruptroutine wird beim 
Interrupt angesprungen. Dafür haben µPs in der Regel eine feste 
Interrupttabelle, in der ein Sprung zu deiner Interruptroutine 
eingetragen werden muss. Wo die Tabelle liegt, weiß ich nicht, in dem 
von dir zitierten Datenblatt sehe ich aber Tabellen dieser Art. Nicht 
(nur) unter "Timer" nachlesen, sondern (auch) unter "Interrupts".

> Direkt über die Register?

Wenn es keine passende API bzw. Lib gibt, dann über die Registernamen. 
Deine IDE sollte die Namen kennen, sodass du keine numerische 
Registeradressen benutzen musst.

> Das würde mir aber den Prozess blockieren, bzw. das ständige Prüfen wäre
> doch dann kein "echter" Interrupt mehr.

Richtig. Aber es geht ganz sicher auch mit echten Interrupts. 1000 
Seiten Datenblatt wollte ich jetzt aber nicht studieren.

: Bearbeitet durch User
von Flevi (florian_o)


Lesenswert?

Hallo Rolf,

vielen Dank für Deine Antworten.

Rolf schrieb:
> Flevi schrieb:
> IntelliSense-Funktion der IDE? Ich vermute mal stark, dass man so auch
> die anderen (in dem Beispiel nicht benutzten) Einstellungen machen kann.
> Bei einer vernünftigen IDE schreibst du timerParams., und die IDE bietet
> dir dann nach dem Punkt innerhalb einer Sekunde alle
> Auswahlmöglichkeiten an. Anhand der Namen kannst/musst du dann die
> Funktion erraten
> Geht das denn nicht?

IntelliSense funktioniert, wie von Dir beschrieben. Es gibt aber nur 
eine Auswahl von den 8 Funktionen, die im Handbuch vom SDK beschrieben 
sind. Der eingebundene Header "TimerP.h" bietet ebenfalls nur diese 8 
erwähnten Funktionen an.
(zur Info: als IDE benutze ich Visual Studio Code zusammen mit dem 
C++-Compiler von TI)

Rolf schrieb:
>> "timerParams.enableOverflowInt = 1;
>
> Siehe oben. Mit dieser Einstellung aktiviert du den Overflow-Interrupt
> des Timers, d. h. die von dir zu schreibende Interruptroutine wird beim
> Interrupt angesprungen. Dafür haben µPs in der Regel eine feste
> Interrupttabelle, in der ein Sprung zu deiner Interruptroutine
> eingetragen werden muss. Wo die Tabelle liegt, weiß ich nicht, in dem
> von dir zitierten Datenblatt sehe ich aber Tabellen dieser Art. Nicht
> (nur) unter "Timer" nachlesen, sondern (auch) unter "Interrupts".
>
Danke für den Hinweis. Ich versuche da mal etwas zu finden.
Kurz zum Verständnis: Der Interrupt vom Overflow wird ausgelöst, wenn 
der 32bit-Wert überläuft, oder der Timer über den Wert der eingestellten 
Periode zählt?


Rolf schrieb:
>> Direkt über die Register?
>
> Wenn es keine passende API bzw. Lib gibt, dann über die Registernamen.
> Deine IDE sollte die Namen kennen, sodass du keine numerische
> Registeradressen benutzen musst.
Wenn ich direkt auf die Register muss, über welche Funktion geht das 
normalerweise? Es müsste dann irgendwie sowas geben:
1
auto readRegister(addr);
2
void writeRegister(addr);
Aber ich habe bisher nirgends etwas gefunden, wie diese Art Funktionen 
für den AM64x von TI benannt, bzw. benutzt werden sollen.

Rolf schrieb:
>> Das würde mir aber den Prozess blockieren, bzw. das ständige Prüfen wäre
>> doch dann kein "echter" Interrupt mehr.
>
> Richtig. Aber es geht ganz sicher auch mit echten Interrupts. 1000
> Seiten Datenblatt wollte ich jetzt aber nicht studieren.
Verständlich! Vielen Dank für Deine Mühe bisher!

von Rolf (rolf22)


Lesenswert?

Flevi schrieb:
> IntelliSense funktioniert, wie von Dir beschrieben. Es gibt aber nur
> eine Auswahl von den 8 Funktionen, die im Handbuch vom SDK beschrieben
> sind. Der eingebundene Header "TimerP.h" bietet ebenfalls nur diese 8
> erwähnten Funktionen an.
> (zur Info: als IDE benutze ich Visual Studio Code zusammen mit dem
> C++-Compiler von TI)

Vielleicht musst du eine andere IDE benutzen? Hier z. B. finden sich 
Timer-Beispiele mit mehr Funktionen:
https://developer.arm.com/documentation/102379/0101/Example-using-Arm-Development-Studio
Wie gesagt, ich kenne diesen speziellen µP nicht, weiß aber Einiges über 
die gängigen Techniken.

> Kurz zum Verständnis: Der Interrupt vom Overflow wird ausgelöst, wenn
> der 32bit-Wert überläuft, oder der Timer über den Wert der eingestellten
> Periode zählt?

Vermutlich das zweite. Damit könnte man das erste ja als Sonderfall 
erschlagen.

> Wenn ich direkt auf die Register muss, über welche Funktion geht das
> normalerweise? Es müsste dann irgendwie sowas geben:
>
1
> auto readRegister(addr);
2
> void writeRegister(addr);
3
>

Das könnte so sein, muss aber nicht. Der Compiler kann ja mithilfe von 
.h-Dateien o. Ä. wissen, wie TI die Register benannt hat. Dann könnte 
man die Registernamen im C++ Quelltext wie Variablennamen behandeln und 
der Compiler würde den Unterschied auf Assemblerebene berücksichtigen.

Beim Avr-Compiler für den ATMega geht das zum Beispiel, da kann man etwa
1
 REG_NAME |= 00000100b;
schreiben, um das Bit 2 in dem 8-Bit-Register REG_NAME zu setzen. Mit 
C-Funktionen wäre das aufwendiger und u. U. nicht so gut optimierbar.

von Flevi (florian_o)


Lesenswert?

Hallo Rolf,

danke für den Tipp bezüglich der Register. Das scheint so zu klappen, 
wie Du es vorgeschlagen hast. Laut Datenblatt muss ich zum Benutzen des 
Timers wie folgt vorgehen:

1. Execute software reset. (Register[bit] TIMER_TIOCP_CFG[0] == 1)
2. Wait until reset release? (Register[bit] TIMER_TIOCP_CFG[0] == 0)
3. ...
(Tabelle 12-5483 Timer Module Global Initialization)

In der Beschreibung steht dazu folgendes:
"TIMER_TIOCP_CFG[0] SOFTRESET bit can initiate a software reset of the 
timer. This bit is autocleared to 0 when the reset is complete.
Before accessing or using the timer, the local host must ensure that 
internal reset is released by reading the TIMER_TIOCP_CFG[0] SOFTRESET 
bit. This bit monitors the internal reset status."

Meine Idee wäre wie folgt gewesen:
1
void DMTimer_init(uint32_t timerBaseAddr) {
2
  uint32_t TIMER_TIOCP_CFG = timerBaseAddr + 0x10;
3
  //initialize software reset
4
  TIMER_TIOCP_CFG |= 0x1;
5
  //wait until software reset is completed
6
  while(TIMER_TIOCP_CFG & 0x1) {
7
    (void)0;
8
  }
9
  ...
10
}

Allerdings bleibe ich so in der while-Schleife hängen. Ich glaub, ich 
hab hier irgendwie einen Denkfehler...

von Rolf (rolf22)


Lesenswert?

Flevi schrieb:
> Ich glaub, ich
> hab hier irgendwie einen Denkfehler...

Stimmt. Wenn der µP Speicheradressen für die Register hat (und das hat 
er anscheinend) und wenn du die Registernamen selbst als uint 
definierst, dann geht es so (zum besseren Verständnis etwas 
umständlich):
1
const uint32_t basisadresse = <irgendwas>;
2
const uint32_t offset = <irgendwas>;
3
 
4
uint32_t *REG_adresse; // Hier mit *
5
REG_adresse = basisadresse + offset; // Hier ohne *
6
7
*REG_adresse |= 0x01;
8
9
while (*REG_adresse != 0x01)
10
...

: Bearbeitet durch User
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.