Wie benutze ich die Timer vom AM64x von TI

OP #7464772
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!

#7464872
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.

OP #7464917
Lesenswert?

Hallo Rolf,

vielen Dank für Deine Antworten.

Rolf schrieb:

Flevi schrieb im Beitrag #7464772: 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!

#7465482
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.

OP #7466854
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...

#7467067
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
...

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren