Forum: Mikrocontroller und Digitale Elektronik Timer Finder Opta (STM32H747XI)


von Holger (holger_p2)


Lesenswert?

Hallo,
ich versuche auf einer Finder Opta einen Timer zu 
programmieren.Prozessor der Steuerung ist ein STM32H747XI.

Umgebung ist Arduino.

Ich bin ziemlicher Anfaenger und fuer jede Hilfe dankbar.


1. Problem. Wieso brauch ich einen Delay bevor ich was in das ARR bzw. 
PSC Register schreibe.

2. Problem. Wenn ich den Interrupt fuer den Timer aktiviere (Zeile39) 
geht das Programm in die Wueste.Ich bin nicht sicher ob der Name fuer 
den Interrupt Handler korrekt ist. Ich bin auch nicht sicher ob ich den 
irgendwie als ISR Kennzeichnen muss. Wie ich das verstehe wird der im 
Startup in der Interrupt Vektor Tabelle definiert. Wie ist die Verbinung 
von meinem Handler zur Tabelle?

Ohne Interrupt laeuft das Programm. Die While schleife wird durchlaufen, 
Micros sind etwa 1 Million. Im Anschluss geht die LED aus.

1
#include "stm32h747xx.h"
2
#include "timer.h"
3
4
void TIM14_IRQHandler(void) {
5
  if (TIM14->SR & TIM_SR_UIF) {
6
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
7
    TIM14->SR &= ~(TIM_SR_UIF);
8
  }
9
}
10
11
//---------------------------------------------------------
12
void setup()
13
{
14
// configure pin in output mode
15
  pinMode(LED_BUILTIN,  OUTPUT);
16
  pinMode(LED_USER, OUTPUT);
17
  digitalWrite(LED_USER, HIGH);  // laeuft
18
  
19
  Serial.begin(115200);
20
  while (!Serial); // warte bis terminal offen
21
  delay(100);
22
  Serial.print(F("\nStart "));
23
24
  digitalWrite(LED_BUILTIN, LOW);  
25
//---------------------------------------------------------
26
  RCC->APB1LENR |= RCC_APB1LENR_TIM14EN;        // enable timer14 clock
27
  TIM14->CR1 &= ~(TIM_CR1_CEN);                 // Timercount disable
28
  RCC->APB1LRSTR |= (RCC_APB1LRSTR_TIM14RST);   // reset timerblock14
29
  RCC->APB1LRSTR &= ~(RCC_APB1LRSTR_TIM14RST);
30
delay(10);
31
  TIM14->ARR = 10000;                            // 10000 * 0.1 ms = 1sec
32
  TIM14->PSC = 19999;                           // prescale -> frequenz =16000000 / 16000 = 1KHz  so ist es 0.1ms??
33
  Serial.println("--0");
34
  TIM14->EGR |= TIM_EGR_UG;                     // update psc und arr
35
  TIM14->DIER |= TIM_DIER_UIE;                  // enable timer irq
36
  Serial.println("--1");
37
  TIM14->SR &= ~TIM_SR_UIF;                     // clear interrupt flag
38
  Serial.println("--2");
39
//  NVIC_EnableIRQ(TIM14_IRQn);                   // Enable the NVIC interrupt for TIM14
40
  Serial.println("--3");
41
  TIM14->CR1 |= TIM_CR1_CEN;                    // enable timer
42
43
Serial.println(TIM14->PSC);
44
Serial.println(TIM14->ARR);
45
46
  uint32_t t1=0,t2=0;
47
48
  while(t2 < 20){
49
    while ((TIM14->SR & TIM_SR_UIF) == 0);
50
    TIM14->SR &= ~TIM_SR_UIF;
51
    Serial.println(micros() - t1);
52
    t1=micros();
53
    t2++;
54
  }
55
  digitalWrite(LED_USER, LOW);
56
}
57
58
void loop()
59
{
60
61
}

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Holger schrieb:
> Wieso brauch ich einen Delay bevor ich was in das ARR bzw. PSC Register
> schreibe.

Nachdem du den Timer im RCC aktiviert hast, dauert es ein paar Takte bis 
sich das auswirkt, weil die CPU schneller als der Bus ist. Füge 
dazwischen ein
1
__DSB();
2
__ISB();
ein, damit die CPU die Vervollständigung des Schreibzugriffs abwartet.

Holger schrieb:
> ch bin nicht sicher ob der Name fuer den Interrupt Handler korrekt ist

Der korrekte Name lautet "TIM8_TRG_COM_TIM14_IRQHandler". Ist im Startup 
Code definiert:

https://github.com/STMicroelectronics/cmsis_device_h7/blob/13ba9da797a30e1563a8795f974425fb26dd0457/Source/Templates/gcc/startup_stm32h747xx.s#L191

Der Name kommt daher, dass sich manchmal mehrere Peripherals den selben 
Interrupt teilen. Im Handler musst du dann prüfen, ob der gewünschte 
Interrupt aufgetreten ist.

Schreibe den Interrupt besser so:
1
void TIM14_IRQHandler(void) {
2
  if (TIM14->SR & TIM_SR_UIF) {
3
    TIM14->SR = ~(TIM_SR_UIF);
4
5
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
6
  }
7
}

Man sollte das UIF Flag so schnell wie möglich löschen, weil auch dieser 
Zugriff etwas dauert; macht man das am Ende der ISR, kann die ISR 
eventuell direkt noch einmal auslösen. Außerdem ist kein 
Read-Modify-Write ( "&=" ) nötig, es reicht einfach der direkte 
Schreibzugriff, weil die Bits im Register als "rc_w0" definiert sind (1 
schreiben = keine Wirkung, 0 schreiben = löschen).

: Bearbeitet durch User
von Holger (holger_p2)


Lesenswert?

Hallo,
danke fuer die schnelle Antwort.

Das mit dem Delay habe ich mir so halb gedacht.

Den Handler habe ich jetzt geaendert zu
1
void TIM8_TRG_COM_TIM14_IRQHandler(void) {
2
  if (TIM14->SR & TIM_SR_UIF) {
3
    TIM14->SR = ~(TIM_SR_UIF);
4
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
5
  }
6
}

aber das Programm haengt immer noch. Startup werde ich mir jetzt 
anschauen.

Ich verstehe den Zusammenhang von der Vektortabelle und meinem Handler 
nicht.
Zu DOS Zeiten war in der Vektortabelle direkt die Einsprungadresse des 
Handlers

von J. S. (jojos)


Lesenswert?

Im Startup Code sind dummy Handler in der Vektortabelle. Diese dummy 
Handler sind als 'weak' definiert, d.h. sie werden überschrieben (vom 
Linker ersetzt) wenn sie im Code vorhanden sind.

von Holger (holger_p2)


Lesenswert?

Ok. Das habe ich verstanden.

Dann muss ich bei mir auf dem Rechner irgendwo ein FILExxx.s heben, dass 
der Linker verwendet. Ich hab bis jetzt nur eins gefunden mit 0-15 was 
ja jeder ARM gleich hat.

Oder muss ich das oben verlinkte File irgendwohin kopieren?

von J. S. (jojos)


Lesenswert?

Der startup code steckt kompiliert in einer library drin, den kannst du 
nicht einfach ersetzen oder verändern. Es wird auch nicht der aus dem 
CMSIS Beispiel verwendet, sondern der steckt im ArduinoCore-mbed drin.

Wichtig ist aber den Handler als 'extern "C"' zu definieren, jetzt wird 
der als C++ Unit übersetzt und durch das Name mangeling passt der 
tatsächliche Funktionsname nicht zu der Definition im Startup Code.

von Holger (holger_p2)


Lesenswert?

Herzlichen Dank

und so
1
extern "C"{
2
  void TIM8_TRG_COM_TIM14_IRQHandler(void) {
3
4
    if (TIM14->SR & TIM_SR_UIF) {
5
      TIM14->SR = ~(TIM_SR_UIF);
6
      digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
7
    }
8
  }
9
}
funktioniert das jetzt einwandfrei.

Kann ich mir morgen mal das extern "C" zu Gemuete ziehen

Danke
Holger

von J. S. (jojos)


Lesenswert?

C++ name mangeling ist das Stichwort.
In C++ sind ja mehre Signaturen für Funktionen mit gleichen Namen 
erlaubt, z.B.
1
void foo(int a);
2
void foo(float a);
Dadurch würde der Linker bei C meckern, in C++ werden die Argumente in 
den Funktionsnamen reinkodiert und damit sind die wieder eindeutig.
Mit 'extern "C"' verhindert man dieses name mangeling um es C kompatibel 
zu machen.

Aber auch obwohl ich das weiß habe ich an dem blöden fehlenden extern 
"C" auch schon lange den Fehler gesucht :)
Und deshalb ich das 'weak' nicht besonders, der Linker kann solche 
Fehler nicht erkennen weil er ja nicht weiß ob die Funktion ersetzt 
werden soll oder nicht. Aber es ist eben ein Mittel um eine generisches 
Modul zu haben wo die Funktion beim Linken ersetzt werden kann.

: Bearbeitet durch User
von Holger (holger_p2)


Lesenswert?

Ja, ich habe mir den Artikel auf Wikipedia quer gelesen. Hat halt alles 
Vor- und Nachteile.

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.