Mikrocontroller STM32 Interrupt (mit HAL_GetTick())?


09.08.2022, 20:54
void testfunktion() 
{
     //Im eigentlichen Code ist hier teilweise noch was mit den       //onboard LEDS, falls das relevant sein sollte
    
     while (1)     //(pushCounter > 0)
     {
        uint32_t x1 = HAL_GetTick(); 
        uint8_t a[8];                                     
        a[0] = 0xAA;                                      
        a[5] = 0x00;
        a[6] = 0x00;
        a[7] = 0xDD;
        int i = 4;
        while(i >= 1) {
           a[i--] = (x1 & 0xFF);
           x1 >>= 8;
        }                                              
        HAL_UART_Transmit(&huart2, a, 8, 50);   
     }
}
Berny96  09.08.2022, 20:31

Kannst du vielleicht die relevante Stelle vom Code posten?

Also: Testfunktion.

AOMkayyy 
Fragesteller
 09.08.2022, 20:33

Ich probiere gerade noch schnell etwas aus (und muss den Code etwas zurechtstutzen), dann poste ich es gleich.

3 Antworten

Vom Fragesteller als hilfreich ausgezeichnet

Ja HAL_GetTick läuft (sofern kein OS genutzt wird) im Systick.

Allerdings läuft das nur im ms Takt, also wenn zwischen zwei UART Interrupts keine Millisekunde verstreicht zählt das auch nicht hoch.

Asonten geht das eigentlich so wie man es sich vorstellt.


AOMkayyy 
Fragesteller
 09.08.2022, 20:36

Erstmal vielen Dank für die Antwort!

Das weniger als eine ms vergeht habe ich auch schon in Betracht gezogen, aber ich habe auch schon eine endlosschleife darin laufen lassen ohne dass sich etwas an der Zeit ändert.

Muss ich an den Prioritäten etwas ändern?

0
Kelec  09.08.2022, 20:57
@AOMkayyy

Wo hast du die Endlosschleife laufen lassen?

Wenn die in der ISR vom UART läuft kommt der Systicl Handler nicht drann.

Du musst an sich nichts an den Prioritäten ändern aber du kannst es machen wenn du befürchtest dass eine ISR zu lange läuft.

Wichtig dabei ist, dass du die Preemption Priorität änders. Nur ein Interrupt mit höherer Preemption Priorität kann ein anderes unterbrechen. Sprich nur weil eine ISR eine höhere Priorität hat muss sie ein anderes Interrupt nicht unterbrechen können wenn die Preemption Priorität nicht höher ist.

Btw bei Cortex M CPUs ist 0 die höchste Priorität. 1 ist damit niedriger als 0.

1
AOMkayyy 
Fragesteller
 09.08.2022, 21:14
@Kelec

Die Endlosschleife (die hier nur exemplarisch ist um sicherzugehen, dass es nicht in weniger als 1ms ausgegeben wird und sich deswegen die ausgegebene Zeit nicht ändert) befindet sich in der UART Rx Callback Funktion (also in der ISR wenn ich das richtig verstehe, oder?)

Weshalb kann der Systick handler nicht zugreifen? Das mit der Priorität hatte ich mir so vorgestellt, dass ich ja bereits in einem Interrupt bin und ich mit HAL_GetTick() wieder (indirekt?) einen Interrupt aufrufe(?) und dieser aber aufgrund der niedrigeren Priorität nicht zum Zug kommt, während der andere noch läuft. deswegen habe ich versucht die Preemption Priority von System Tick timer zu erhöhen, erfolglos. (Es gibt ja nur Preemption und Sub Priority, wenn ich das richtig verstehe und ich habe immer ersteres gewählt)

Ach, der letzte Abschnitt war mir nicht bewusst, vielen Dank, dann probier ich das jetzt auch noch aus!

0
AOMkayyy 
Fragesteller
 09.08.2022, 21:17
@Kelec

Nachdem ich gelernt habe, dass 0 die höchste Prio ist, habe ich es geschafft, dass sich die Werte endlich ändern :D Ich muss zwar noch einiges testen, vor allem für das eigentliche Ziel, aber das ist schonmal ein super Schritt, vielen Dank!

An sowas dummem hängt es dann schon wieder für Stunden :')

0
Kelec  09.08.2022, 21:24
@AOMkayyy

Solange ein Interrupt läuft sollte im Optimalfall kein anderes laufen. Cortex M unterstützen zwar Preemption, aber es sollte eigentlich nur in ganz wichtigen Spezialfällen verwendet werden. Dieser ist aber keiner davon.

HAL_GetTick ruft in dem Sinne kein Interrupt auf sondern verwendet einen Wert welcher in einem Interrupt vergrößert wird. Im wesentlichen:

Systick_Interrupt()
   halTick++;

HAL_GetTick()
   return halTick;

In einem Interrupt sollte nicht gewartet werden.

Ein Interrupt sollte so schnell wie möglich fertig sein. Daher keine Wartezeiten einfügen und schon gar keine Endlosschleifen.

Die einzige Endlosschleife sollte in der main Funktion sein.

1
AOMkayyy 
Fragesteller
 09.08.2022, 21:43
@Kelec

Also meinst du, dass man Interrupts nicht verschachteln sollte?

Der Interrupt ist eigentlich so das Kernstück meines Programms, daher geht es mir eigentlich nicht so um den restlichen Programmcode. Wartezeiten habe ich im Interrupt indirekt und die Endlosschleife ist nicht drin, die war nur temporär da um sicherzugehen, dass nicht immer die selbe Zeit ausgegeben wird, weil die Ausführungdauer zu kurz war.

Um es mal aufs minimalste herunterzubrechen: Ich benutze einen UART Interrupt und sobald der Eingang einer Nachricht erkannt wurde (diese enthält einen counter 0 bis 255 und ein paar Zeitdaten), dann reagiere ich damit ein paar Pins zu gewissen Zeiten count mal zu stimulieren. Das ist ja praktisch nur über gewisse Verzögerungen und das Ausrufen von HAL_GetTick() mögich. Bin ich also eigentlich gezwungen Polling zu verwenden? So scheint es jetzt aber eigentlich ganz gut zu funktionieren.

0
AOMkayyy 
Fragesteller
 09.08.2022, 21:47
@AOMkayyy

Bearbeitungszeit war abgelaufen, daher P.S: Ich habe das selbe Programm auch mit Polling implementiert und merke erstmal keine Nachteile.

0
Kelec  09.08.2022, 21:59
@AOMkayyy

Nein solange du im Interrupt nicht wartest passt das so.

HAL_GetTick wartet ja nicht du solltest nur kein HAL_Delay verwenden.

Was meinst du mit Interrupts verschachteln? Man sollte Preemption nur im Notfall verwenden. Aber dein Programm sollte eigentlich auch ohne Preemption Problemlos laufen. Wenn du im Interrupt Handler keine länger dauernden Funktionen hast.

1
AOMkayyy 
Fragesteller
 09.08.2022, 22:21
@Kelec

Also einen Delay habe ich nicht drin, aber ich verzögere praktisch künstlich, weil ich die GPIO_Pins zu bestimmten Zeiten triggern möchte.

Ich bin ziemlicher Novize, ist Preemption mit Verschachteln gleichzusetzten? Mit Verschachteln meine ich, dass in einem Interrupt ein Interrupt mit höherer Priorität ausgeführt wird und danach der vorangegangene Interrupt weiterläuft. Das wäre hier doch der Fall, indem ich in meinem UART Interrupt Callback HAL_GetTick() aufrufe, oder?

Die Funktion dauert etwas länger (im absoluten Extremfall, der zwar praktisch nicht angewendet wird aber möglich wäre, wären das glaube ich 130 Sekunden).

Ich weiß nicht ob das mit dem Code sharing so funktioniert, aber ich versuche es mal: https://codeshare.io/8pozQZ Das sollte die etwas aufgeräumte Funktion sein, die durch den UART_Rx Callback aufgerufen wird.

Vielen Dank für die Hilfe!

Alternativ hier: https://ctxt.io/2/AADgTwYTFw

0
Kelec  09.08.2022, 23:39
@AOMkayyy

Wenn du künstlich etwas verzögerst kannst du das in die Main auslagern, also die UART routine setzt nur ein Flag dass ein GPIO gesetz werden soll. Die Main oder ein Timer führt die Aktion dann aus wenn sie ausgeführt werden soll.

Ja Preemption ist in diesem Sinne verschachteln.

HAL_GetTick() führt nicht zur verschachtelung von Interrupts. HAL_GetTick liest lediglich einen Zahlenwert aus dem Ram aus. Dieser Zahlenwert im Ram wird aber vom Systick Interrupt erhöht.

Das ist zB genau das was ich meine der Systick macht am Ende nichts anderes als einen Zahl im Ram zu erhöhen. Er wartet auf nichts und hat minimale laufzeit. Wenn du nun die Information brauchst liest du diese aus dem Ram und wartest nicht auf den Systick oder ähnliches.

130s sind ein ganz schönes Stück. Kurz ist im Sinne von Interrupts im Bereich von 100us angesiedelt.

1
AOMkayyy 
Fragesteller
 10.08.2022, 14:20
@Kelec

Wäre es dann so, dass ich im Interrupt nur eine Flag setze und die dann wieder (praktisch durch Polling) in der Main abfrage? Besteht denn dann noch ein großer Vorteil zu normalem Polling?

0
Kelec  10.08.2022, 16:45
@AOMkayyy

Wenn du nur ein Flag setzt nein. Wenn du im Interrupt das machst was schnell gehen muss und das was auch länger Zeit hat in die Main legst hat es einen Vorteil.

Wenn du erst auf einen Zeitpunkt warten musst bevor du den Pin setzt dann is das was für die Main. Wenn du diese Aktion nur bei einem bestimmten Byte auslösen willst welches auf der UART daher kommt ist die Verarbeitung dieses Bytes selbst etwas fürs Interrupt denn da wartest du ja auf nichts.

1

Also hier noch der heruntergekürzte Code (ist alles relevante, den Rest habe ich sowieso gerade auskommentiert), der durch void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) aufgerufen wird. Die Werte werden endlos in der Konsole ausgegeben, aber eben immer die selben. An den Prioritäten der Interrupts habe ich momentan nichts geändert, alle stehen bei 0.

void buttonPress() 
{
     //Im eigentlichen Code ist hier teilweise noch was mit den       //onboard LEDS, falls das relevant sein sollte
    
     while (1)     //(pushCounter > 0)
     {
        uint32_t x1 = HAL_GetTick(); 
        uint8_t a[8];                                     
        a[0] = 0xAA;                                      
        a[5] = 0x00;
        a[6] = 0x00;
        a[7] = 0xDD;
        int i = 4;
        while(i >= 1) {
           a[i--] = (x1 & 0xFF);
           x1 >>= 8;
        }                                              
        HAL_UART_Transmit(&huart2, a, 8, 50);   
     }
}

Ach und a[1] bist a[4] ist sozusagen die Zeit als aufgetrennte 1 Byte Segmente.

Wenn ich es richtig sehe darfst du an der Stelle keine Endlosschleife nehmen.

Ich gebe mal einen Lösungsanschlag der aus meiner eigenen Erfahrung eher funktionieren dürfte ich aber nicht sicher bin ob es auch hier zu trifft.

Also:

  1. Endlosschleife nach Main verschieben
  2. Dort einen Boolean abfragen ob gesendet werden soll oder nicht (Also ob gedrückt wurde)
  3. In button_click lediglich den Boolean negieren (damit es als Schalter agiert, falls so gewünscht)

So befindet sich der langlaufende Code (also dass Senden der Hexwerte)

an der richtigen Stelle und nicht im interrupt.

Woher ich das weiß:Berufserfahrung – Beruf, Schule, Hobby und eigene Erfahrungen

AOMkayyy 
Fragesteller
 09.08.2022, 21:06

Vorab, vielen Dank für die Antwort!

Also die Endlosschleife ist eigentlich nicht da, normalerweise ist da eine Schleife mit maximal 255 (normalerweise eher < 30) Iterationen, ich habe aber eine Endlosschleife genutzt um sicherzugehen, dass die Aktionen nicht in unter 1ms stattfinden und sich die ausgegebene Zeit deswegen nicht ändert.

Du meinst einfach Polling in der Main anstatt Interrupts, verstehe ich das so richtig? Ich habe die Lösung schon mit Polling gemacht, würde aber gerne den Umgang und die Probleme die ich mit Interrupts habe besser verstehen.

0
Berny96  09.08.2022, 21:12
@AOMkayyy

Mein Ansatz kommt daher dass ich aus anderen Umgebungen (z.b. Spieleengines oder so) es kenne dass so Framework Funktionen gerne mal vom Zyklus der Umgebung abhängig ist. Sprich wenn ich eine bestimmte Funktion aufrufe die z.b. die vergangene Zeit aufruft dann wird diese Zeit in jedem Aufruf derselben Zyklus gleich sein. Dort gibt es eben entsprechende Callbacks die genau solche Vorraussetzungen (Dass die Systemzeit weiter gezählt wird) erfüllen und manche eben nicht. (Zum Beispiel kann es sein dass dein Interupt den Thread aufhält und deshalb die Zeit immer gleich bleibt weil im Hintergrund nichts passiert)

1
AOMkayyy 
Fragesteller
 09.08.2022, 21:21
@Berny96

Was du sagst das stimmt im Grunde, ich habe oben auch schon gemutmaßt, dass es an der Interrupt Priorität liegt (der HAL_GetTick() den aktuell laufenden nicht unterbrechen kann), nur leider war mir nicht bewusst, dass standardmäßig alle auf höchster Priorität sind und höhere Zahl niedrigere Priorität bedeutet :') Das hat @Kelec klargestellt und nun scheint es zu funktionieren :D

Dir auch vielen Dank für die Unterstützung!

0