Forum: Mikrocontroller und Digitale Elektronik STM32 korrekte Interrupt-Handhabung


von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Und wieder ich :) ich wundere mich, dass sich noch keiner beschwert ;)

Wie handhabt ihr euren Code im Interrupthandler? Laut (u. A.) 
Mikrocontroller.net: "... .Prinzipiell sollte man ISRs möglichst kurz 
halten und schnell beenden. ...", soll man diese ja möglichst kurz 
halten.

In meinem konkreten Fall habe ich einen extern getriggerten Timer, der 
einen Interrupt nach der Triggerung auslöst. Im einfachsten Fall soll 
nach der Triggerung eine Variable gelesen werden. OK, das kann ich ja im 
Interrupt selbst machen. Was ist aber, wenn nach der Triggerung, viel 
mehr passieren muss? Beispielsweise ADC auslesen und in den Speicher 
übergeben. Oder viele Rechenoperationen.

von Esam (Gast)


Lesenswert?

Reginald L. schrieb:
> Beispielsweise ADC auslesen und in den Speicher
> übergeben.

Das benötigt schonmal nicht viel, kann also kein Problem sein.

Reginald L. schrieb:
> Oder viele Rechenoperationen

Wofür benötigst du denn die viele Rechenoperationen? Hinweis: Siehe 
vorige Antwort.

Reginald L. schrieb:
> Und wieder ich :) ich wundere mich, dass sich noch keiner beschwert ;)

Ich sage jetzt mal voraus dass es demnächst Beschwerden geben wird, denn 
das ist jetzt keine konkrete Frage mehr die du hier stellst.

Trotzdem bekommst du eine Pauschale Antwort, kein Problem. Du setzt in 
der ISR ein globales Flag und das Hauptprogramm übernimmt die 
entsprechend verknüpfte Aufgabe sobald es dieses Flag auswertet. 
Zufrieden?

von Esam (Gast)


Lesenswert?

Ach ja, meine Antwort ist nicht STM32-spezifisch. Genau so wenig wie es 
die Frage war, trotz Thread-Titel.

von m.n. (Gast)


Lesenswert?

Durch geschickte Wahl von Interruptprioritäten und Subprioritäten kann 
man schon eine Menge machen; auch sollte man den DMA-Controller nicht 
unterschätzen.
Nächter Beitrag: "Memory-Memory-Tranfer mit dem DMA-Controller beim 
STM32?"
;-)

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Esam schrieb:
> Wofür benötigst du denn die viele Rechenoperationen?

Beispielsweise Matrizenberechnungen oder FFTs. Kann man sozusagen alles 
in den Handler reinscheißen, solange ich meine Interrupts unter 
Kontrolle habe? Macht man das so?


Esam schrieb:
> Ich sage jetzt mal voraus dass es demnächst Beschwerden geben wird, denn
> das ist jetzt keine konkrete Frage mehr die du hier stellst.

Na also, auf Anfrage gehts doch ;) Wobei du mit nicht-STM32-spezifisch 
natürlich Recht hast.


Esam schrieb:
> Trotzdem bekommst du eine Pauschale Antwort, kein Problem. Du setzt in
> der ISR ein globales Flag und das Hauptprogramm übernimmt die
> entsprechend verknüpfte Aufgabe sobald es dieses Flag auswertet.
> Zufrieden?

Verstehe ich das richtig, dass der InterruptHandler dann nur ein Flag 
setzt, mein Code im Hauptprogramm abgearbeitet wird, wenn das Flag 
gesetzt sein sollte und ich danach das Flag wieder resette?


m.n. schrieb:
> Durch geschickte Wahl von Interruptprioritäten und Subprioritäten kann
> man schon eine Menge machen; auch sollte man den DMA-Controller nicht
> unterschätzen.

Ich bin schon tierisch Stolz, dass ich den Timer per externem Trigger 
auf nen Interrupt legen konnte, ohne Examples (so langsam frage ich 
mich, wieviele Schreib-/Lesezyklen ich noch frei habe :>)! Hör mir auf 
mit Prioritäten, was glaubst du wie mir der Kopf raucht ;) Im Ernst: Das 
habe ich mir schon angeschaut, bisher brauche ich das noch nicht. Wenn 
es dann Richtung RS232 geht, nehme ich mal an. Aber soweit bin ich noch 
lange nicht.

von Amateur (Gast)


Lesenswert?

Am einfachsten löst man solche Probleme - geht natürlich nur im 
entsprechenden Umfeld - indem man in der Interruptroutine nur das 
nötigste macht und somit die Erkennung von der Reaktion trennt.
Also z.B.
1. Einen Wert lesen und Zwischenspeichern, so das Nötig ist.
2. Ein Flag setzen.
Das war’s.

Dann kann man in der Hauptschleife seine "Runden" drehen und immer mal 
wieder nachfragen, ob was (Flag) anliegt.

von Esam (Gast)


Lesenswert?

Reginald L. schrieb:
> Beispielsweise Matrizenberechnungen oder FFTs. Kann man sozusagen alles
> in den Handler reinscheißen, solange ich meine Interrupts unter
> Kontrolle habe? Macht man das so?

Das kann man nicht allgemeingültig beantworten. Wenn eine Anwendung das 
erfordert, ist es zumindest schon mal nicht verboten.
Ansonsten hoffe ich doch sehr dass du dich nur vertippt hast ;)

Reginald L. schrieb:
> Verstehe ich das richtig, dass der InterruptHandler dann nur ein Flag
> setzt, mein Code im Hauptprogramm abgearbeitet wird, wenn das Flag
> gesetzt sein sollte und ich danach das Flag wieder resette?

Ja, so ist das gemeint.

von Amateur (Gast)


Lesenswert?

> Verstehe ich das richtig, dass der InterruptHandler dann nur ein Flag
> setzt, mein Code im Hauptprogramm abgearbeitet wird, wenn das Flag
> gesetzt sein sollte und ich danach das Flag wieder resette?

Musst Du aber nicht!
Setzt Du dieses nicht zurück, so ergibt die darauffolgende Abfrage 
wieder ein positives Ergebnis.
Eine gute Möglichkeit des thermische Verhalten der CPU im Dauerstress zu 
überprüfen.
Eine schlechte Wahl, wenn Du ein folgendes Ereignis separieren willst.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Vielen Dank an alle! Dem entnehme ich, dass es also schonmal nicht 
"verboten" ist zeitkritische Aufgaben im Handler durchzuführen und alle 
anderen über ein Flag im normalen Programmablauf.


Amateur schrieb:
> Musst Du aber nicht!
> Setzt Du dieses nicht zurück, so ergibt die darauffolgende Abfrage
> wieder ein positives Ergebnis.
> Eine gute Möglichkeit des thermische Verhalten der CPU im Dauerstress zu
> überprüfen.
> Eine schlechte Wahl, wenn Du ein folgendes Ereignis separieren willst.

Danke für den Tipp, so weit bin ich noch lange nicht ;)


Esam schrieb:
> Reginald L. schrieb:
>> Beispielsweise Matrizenberechnungen oder FFTs. Kann man sozusagen alles
>> in den Handler reinscheißen, solange ich meine Interrupts unter
>> Kontrolle habe? Macht man das so?
>
> Das kann man nicht allgemeingültig beantworten. Wenn eine Anwendung das
> erfordert, ist es zumindest schon mal nicht verboten.
> Ansonsten hoffe ich doch sehr dass du dich nur vertippt hast ;)

Upsa :> Sollte natürlich noch ein "m" reingeschiß.. ähm, ...geschmissen 
werden :)

von Steffen R. (steffen_rose)


Lesenswert?

Nun kommen wir doch in den CPU spezifischen Bereich.

Es gibt Interrupte, die dauerhaft auslösen. Hier muss das Flag 
zurückgesetzt werden oder der jeweilige Interrupt gesperrt werden.

Es gibt aber auch Interrupte, welche nur einmalig auslösen.

Es gibt auch Interrupte, die lösen aus, wenn die Peripherie nichts zu 
tun hat.

Und und und.

Daher sind die Lösungsansätze auch vielfältig und müssen an die 
jeweilige CPU und die jeweilige Peripherie anpassen.

von Amateur (Gast)


Lesenswert?

In diesem Bereich gibt es die folgende Problematik:

Oft werden während der Interruptbearbeitung andere Unterbrechungen 
gesperrt.
Hast Du jetzt eine Unterbrechung, die extrem langsam ist 
(rechenintensiv), so besteht die Möglichkeit, dass eine andere 
Unterbrechung zweimal Deine Aufmerksamkeit erhascht.

Mit dem Scenario der einfachen Unterbrechung kommen die meisten Rechner 
klar. Die mehrfache (von einer Quelle) beherrschen nicht alle.

Machst Du aber Deine Arbeit in der Hauptschleife, so kann dieser Ablauf 
ruhig öfter, von einer oder mehreren Quellen, unterbrochen werden.
Hier gilt dann: Die gesamte Rechenzeit darf nicht überschritten werden.

von m.n. (Gast)


Lesenswert?

Reginald L. schrieb:
> Hör mir auf
> mit Prioritäten, was glaubst du wie mir der Kopf raucht ;) Im Ernst: Das
> habe ich mir schon angeschaut, bisher brauche ich das noch nicht.

Mach auch mal einen ganzen Tag Pause - damit löst man manchmal Probleme 
auf die einfachste Art.
Gerade bei Cortex-M4 muß einem der Kopf nicht mehr rauchen. Durch die 
zahlreichen Prioritäten kann man zeitkritischen, kurzen Routinen Vorrang 
vor allen anderen Interrupts geben, während ein langsamer (z.B. 1 ms) 
Timerinterrupt ganz niedrige Priorität bekommt. Das Schöne dabei ist, 
daß ein höher priorisierter Interrupt die darunterliegenden ISRs 
unterbrechen kann und nicht blockiert wird.

Auch wenn Du es jetzt nicht brauchst, es gibt Auswege, falls das Timing 
mal zu eng werden sollte.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Ich sollte nächstes mal wirklich spezifischer fragen:
Mein Interrupt löst zwischen 1-50Hz aus ;) Mir gings hierbei nur um die 
Behandlung eines einzigen Interrupts. Aber jetzt weiß ich für die 
Zukunft, dass ich nur in diesen Thread schauen muss, wenns intensiver 
wird. Danke nochmals :)

von Irgendwer (Gast)


Lesenswert?

Amateur schrieb:
> Oft werden während der Interruptbearbeitung andere Unterbrechungen
> gesperrt.
> Hast Du jetzt eine Unterbrechung, die extrem langsam ist
> (rechenintensiv), so besteht die Möglichkeit, dass eine andere
> Unterbrechung zweimal Deine Aufmerksamkeit erhascht.

Warum sollte man das tun? Genau dafür wurden schon vor Uhrzeit 
Interruptprioritäten erfunden. Auch wenn sich das noch nicht bis zu den 
kleinen AVR herumgesprochen hat (bzw. manche meinen immer noch so 
programmieren zu müssen), so können viele µC mittlerweile sowas:-)

von Klaus (Gast)


Lesenswert?

Amateur schrieb:
> indem man in der Interruptroutine nur das
> nötigste macht und somit die Erkennung von der Reaktion trennt.
> Also z.B.
> 1. Einen Wert lesen und Zwischenspeichern, so das Nötig ist.
> 2. Ein Flag setzen.

Warum dann eigentlich einen Interrupt? Da kann man doch einfach den 
Interrupt disablen und das Interruptflag direkt in der Mainloop auslesen 
und sich den ganzen Interruptoverhead sparen.

MfG Klaus

von Steffen R. (steffen_rose)


Lesenswert?

Irgendwer schrieb:
> Genau dafür wurden schon vor Uhrzeit
> Interruptprioritäten erfunden. Auch wenn sich das noch nicht bis zu den
> kleinen AVR herumgesprochen hat (bzw. manche meinen immer noch so
> programmieren zu müssen), so können viele µC mittlerweile sowas:-)

Prioritäten reichen hier nicht. Häufig bestimmt die Priorität nur, wer 
bei gleichzeitigen Anliegen von Interrupts der Gewinner ist.
Wenn Du längere Zeit im Interrupt verweilen willst, brauchst Du auch 
noch nested Interrupts.

von m.n. (Gast)


Lesenswert?

Steffen R. schrieb:
> Wenn Du längere Zeit im Interrupt verweilen willst, brauchst Du auch
> noch nested Interrupts.

Das hat 'Irgendwer' sicher auch so gemeint, da er den AVR als 
Negativbeispiel genannt hatte.

Anders war es seinerzeit bei µPs, die nur einen /INT-Eingang für ext. 
Peripherie hatten und damit keine Hardwarepriorität möglich war, es sein 
denn, man hatte einen vorgeschalteten Dekoder ...

von Steffen R. (steffen_rose)


Lesenswert?

m.n. schrieb:
> Das hat 'Irgendwer' sicher auch so gemeint, da er den AVR als
> Negativbeispiel genannt hatte.

Ich beziehe mich hier z.B. auf den Unterschied ARM7 und Cortex-M3.
'Irgendwer' schien sich eher auf ältere Prozessoren zu beziehen. Ich 
eher auf den Unterschied Microprozessor <> Microcontroller.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Und wieder ein kleines Erfolgserlebnis, das ich unbedingt mit euch 
teilen möchte, passt auch zum Thema :>

Seit gestern habe ich vergeblich versucht, eine Variable, die sich im 
IRQHandler in der "stm32f4xx_it.h" ändert, der "main.c" zu übergeben. 
Heute habe ich es endlich geschafft. Der Umstieg von MatLab auf C ist 
wirklich immens hart. Könntet ihr mir sagen, ob das so korrekt übergeben 
wird, da ich noch nicht so fit mit den Deklarationen in C bin:

stm32f4xx_it.c:
1
#include "stm32f4xx_it.h"
2
...
3
...
4
long int TriggerValue;
5
6
void TIM2_IRQHandler(void)
7
{
8
9
    if(TIM_GetITStatus(TIM2, TIM_IT_Trigger) != RESET)
10
    {
11
12
        TriggerValue = TIM_GetCounter(TIM2);                // Get Timer2 Value
13
        TIM_SetCounter(TIM2, 0);                            // Reset Timer2
14
15
        TIM_ClearITPendingBit(TIM2, TIM_IT_Trigger);        // Clear IRQ Timer 15
16
17
    }
18
}

stm32f4xx_it.h:
1
extern long int TriggerValue;

main.c:
1
#include "stm32f4xx_it.h"
2
...
3
...
4
int main(void)
5
{
6
...
7
...
8
while(1)
9
    {
10
long int a=TriggerValue;
11
printf("%li",a);
12
...
13
...
14
}
15
}

Es geht hier lediglich um optische Rückmeldung, der TriggerValue wird im 
Handler später noch benötigt.

von Steffen R. (steffen_rose)


Lesenswert?

Ans 'volatile' denken.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Ah, vergessen :)
Also in der "stm32f4xx_it.c" anstatt "long int TriggerValue;":
1
volatile long int TriggerValue;

EDIT: ? :)

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Steffen R. schrieb:
> Ich beziehe mich hier z.B. auf den Unterschied ARM7 und Cortex-M3.

Die Knackpunkte sind:
- lässt die CPU ein Nesting prinzipiell zu,
- gibt es einen Interrupt-Controller mit Prioritäten.

In den Cortex-M ist stets einer drin. Bei dem ARM7 ist das Sache 
desjenigen, der den Core mit Peripherie zusammen in einen Chip giesst. 
Meistens ist einer drin. Damit geht das auch beim ARM7.

> 'Irgendwer' schien sich eher auf ältere Prozessoren zu beziehen.

Priorisierte Interrupts mit Nesting sind kein sonderlich neues Konzept. 
Das war schon anno Z80 ziemlich verbreitet, weil fertig drin. Dass 
andererseits µCs ohne Interrupt-Controller mit Nesting dennoch 
irgendwelche Prioritäten für ihre Interrupt-Quellen haben liegt in der 
Natur der Sache, wenn man keinen Zufallsgenerator einbaut.

Insofern ist alt/neu hier kein Kriterium. Und die Prioritäten bei AVRs 
sind selten das, was man üblicherweise mit Interrupt-Prioritäten 
verbindet.

: Bearbeitet durch User
von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Hm, also so wie ich mir das gedacht habe, gehts jedenfalls nicht. Wenn 
ich das volatile allerdings in meine main() Funktion reinmache, dann 
bringt er mir keinen Fehler, wenn es über der main() Funktion steht aber 
schon. Komplett anderes Konzept hier in C :/

von m.n. (Gast)


Lesenswert?

Nimm besser "volatile uint32_t TriggerValue", weil hier der Wert 
eindeutig eine 32-Bit Variable ohne Vorzeichen ist.

In Deinem IRQHandler teste bitte IMMER auf ein aktives Ereignis, dessen 
Flag dann gelöscht wird. Dann lasse bitte noch Timer2 durchlaufen (nicht 
löschen), wenn das Timing exakt sein soll, oder lösche ihn per Hardware.
Anderfalls könnte er zum Zeitpunkt des Löschens schon einen 
nennenswerten Zählerstand haben (ein anderer Interrupt höherer Priorität 
hat 'dazwischen gefunkt'). Aber gut, das sind Feinheiten für die 
Zukunft.

Dein Programmierstil entwickelt sich ja ;-), aber lasse bitte leere 
Zeilen weg. Wenn man viel Code hat, hat man dadurch weniger Luft und 
mehr Substanz im Blick.

Reginald L. schrieb:
> Der Umstieg von MatLab auf C ist
> wirklich immens hart.

Aber miss mal die Zeit für Deine Interruptroutine. Ich schätze mal rund 
100 - 150 ns. Das ist richtig schnell!

von m.n. (Gast)


Lesenswert?

Reginald L. schrieb:
> printf("%li",a);
> ...
> ...
> }
> }
>
> Es geht hier lediglich um optische Rückmeldung, der TriggerValue wird im
> Handler später noch benötigt.

Vielleicht testest Du doch noch einmal IAR-Kickstart. Mit 'Live Watch' 
kannst Du dir die Variable zur Laufzeit anzeigen lassen und nicht nur, 
wenn gerade main() ein printf() veranstaltet: zum Beispiel, wenn das 
Programm an irgendeiner Stelle hängen bleibt, die Interrupts aber 
weiterhin bedient werden.
Gerade für die ersten Schritte ist das sehr hilfreich.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

m.n. schrieb:
> Nimm besser "volatile uint32_t TriggerValue", weil hier der Wert
> eindeutig eine 32-Bit Variable ohne Vorzeichen ist.

Tatsächlich, mach ich.


m.n. schrieb:
> In Deinem IRQHandler teste bitte IMMER auf ein aktives Ereignis, dessen
> Flag dann gelöscht wird. Dann lasse bitte noch Timer2 durchlaufen (nicht
> löschen), wenn das Timing exakt sein soll, oder lösche ihn per Hardware.
> Anderfalls könnte er zum Zeitpunkt des Löschens schon einen
> nennenswerten Zählerstand haben (ein anderer Interrupt höherer Priorität
> hat 'dazwischen gefunkt'). Aber gut, das sind Feinheiten für die
> Zukunft.

Was meinst du mit dem Flag löschen genau, also den Sinn dahinter?
Der Timer soll direkt nach dem Trigger ausgelesen werden, dann sofort 
neu starten. Dies eben jede Runde (Frequenz max 50Hz). Ob dazwischen 
weitergezählt wird, stört mich nicht (ausser du weißt etwas, was ich 
nicht weiß :) ).Ungern würde ich den Timer durchlaufen lassen und das 
Value dann aus den durchlaufenen Schleifen ziehen.
Per Hardware löschen wäre super, aber ich habe bisher nur diese 
Möglichkeit gefunden, den Timer zurückzusetzen, kannst du mir da einen 
Tipp geben?


m.n. schrieb:
> Dein Programmierstil entwickelt sich ja ;-), aber lasse bitte leere
> Zeilen weg. Wenn man viel Code hat, hat man dadurch weniger Luft und
> mehr Substanz im Blick.

Hey, ich programmiere ja schon seit Jahren mit MatLab, da ist die Welt 
nur mathematischer :P Und nein, die Luft bleibt drin, da kann ich besser 
gucken :)


Dankeschön mal wieder :)

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Reginald L. schrieb:
> m.n. schrieb:
>> Nimm besser "volatile uint32_t TriggerValue", weil hier der Wert
>> eindeutig eine 32-Bit Variable ohne Vorzeichen ist.
>
> Tatsächlich, mach ich.

Na danke, jetzt geht gar nichts mehr^^

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

m.n. schrieb:
> Vielleicht testest Du doch noch einmal IAR-Kickstart. Mit 'Live Watch'
> kannst Du dir die Variable zur Laufzeit anzeigen lassen und nicht nur,
> wenn gerade main() ein printf() veranstaltet: zum Beispiel, wenn das
> Programm an irgendeiner Stelle hängen bleibt, die Interrupts aber
> weiterhin bedient werden.

Ich werds grad mal runterziehen. Welche Version soll ich benutzen, blick 
da nicht durch mit C-Run und Co :)

von Luis (Gast)


Lesenswert?

Füge volatile auch im Header ein. Sonst weiss der Compiler beim 
compilieren der Main nicht, dass er den Zugriff auf die Variable nicht 
optimieren darf.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Luis schrieb:
> Füge volatile auch im Header ein. Sonst weiss der Compiler beim
> compilieren der Main nicht, dass er den Zugriff auf die Variable nicht
> optimieren darf.

Aber es reicht die Variable mit volatile jeweils einmal im Header und 
einmal im SourceCode zu modifizieren? Wird die Variable dann im 
kompletten Programm als volatile behandelt?

von Luis (Gast)


Lesenswert?

Jepp, wenn du den Header korrekt in jedem genutzten c-File includiert 
hast, reicht das.

 Vorsicht, wenn du innerhalb einer Funktion nochmals eine Angabe 
"volatile uint32_t TriggerValue" einbaust. Der Compiler legt dann ggf 
eine zweite Variable gleichen Namens auf dem Stack an. Prüfe mal den 
Warnung level deines Compilers, ob er meckert wenn er namens gleiche 
Variablen erstellen soll.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Luis schrieb:
> Vorsicht, wenn du innerhalb einer Funktion nochmals eine Angabe
> "volatile uint32_t TriggerValue" einbaust. Der Compiler legt dann ggf
> eine zweite Variable gleichen Namens auf dem Stack an. Prüfe mal den
> Warnung level deines Compilers, ob er meckert wenn er namens gleiche
> Variablen erstellen soll.

Wäre das die Option "Warn if anything is declared more than once in the 
same scope"?

Komischerweise funktioniert "uint32_t" nicht, obwohl es im 
CodeCompletion auftaucht. Allerdings geht "unsigned long int". Weisst du 
woran das liegt?


Und wieder mal ein herzliches Dankeschöne an alle, hilft ungemein, wenn 
es um solche Kleinigkeiten geht, deren Lösung schwerlichst im Internet 
zu finden sind. Zumindest als Anfänger in C.



EDIT: Wenn ich "volatile unsigned long int TriggerValue;" jeweils im 
Header und in der Source angebe, sagt er mir aber:
1
"src\stm32f4xx_it.c|166|warning: redundant redeclaration of 'TriggerValue' [-Wredundant-decls]|
2
.\inc\stm32f4xx_it.h|28|note: previous declaration of 'TriggerValue' was here|
3
bin\Debug\Test.map|1|Program size (bytes):   15128|
4
||Data size    (bytes):     440|
5
||BSS size     (bytes):    1888|
6
||             ----------------|
7
||Total size   (bytes):   17456   (R/W Memory: 2328)|
8
|||
9
||=== Build finished: 0 errors, 1 warnings (0 minutes, 0 seconds) ===|

: Bearbeitet durch User
von Luis (Gast)


Lesenswert?

>Wenn ich "volatile unsigned long int TriggerValue;" jeweils im ...

long ist ein Datentyp UND int ist ein Datentyp. verwende entweder long 
oder int.


>Komischerweise funktioniert "uint32_t" nicht...

Inkludier den Header <stdint.h>
#include <stdint.h>

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Luis schrieb:
> long ist ein Datentyp UND int ist ein Datentyp. verwende entweder long
> oder int.

Ahhh, eieiei, aber es ist doch ein int ;)


Luis schrieb:
> Inkludier den Header <stdint.h>
> #include <stdint.h>

Ganz vergessen, dass ich irgendwo wortwörtlich gelesen habe "C kann ohne 
Bibliotheken fast gar nichts".

von Steffen R. (steffen_rose)


Lesenswert?

A. K. schrieb:
> In den Cortex-M ist stets einer drin. Bei dem ARM7 ist das Sache
> desjenigen, der den Core mit Peripherie zusammen in einen Chip giesst.
> Meistens ist einer drin. Damit geht das auch beim ARM7.

Der Interrupt Controller stößt immer den IRQ oder den FIRQ an.
Ist der IRQ gerade aktiv nützt es auch nichts, wenn der Interrupt 
Controller einen Interrupt höherer Priorität anstoßen will.
(per Software kann man da was drehen, aber das war ja auch die Aussage 
-> IRQ Behandlung ja nach Umgebung)

Reginald Leonczuk
1
"src\stm32f4xx_it.c|166|warning: redundant redeclaration of 'TriggerValue' [-Wredundant-decls]|
2
.\inc\stm32f4xx_it.h|28|note: previous declaration of 'TriggerValue' was here|

Wie soll man etwas prüfen, wenn man nicht weiß, was du angestellt hast?
Schau Dir die beiden angegebenen Zeilen an.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Steffen R. schrieb:
> Wie soll man etwas prüfen, wenn man nicht weiß, was du angestellt hast?
> Schau Dir die beiden angegebenen Zeilen an.

Steht doch eigentlich alles in den obigen Beiträgen von mir.

source:
1
volatile uint32_t TriggerValue;
2
3
void TIM2_IRQHandler(void)
4
{
5
    if(TIM_GetITStatus(TIM2, TIM_IT_Trigger) != RESET)
6
    {
7
8
        TriggerValue = TIM_GetCounter(TIM2);                // Get Timer2 Value
9
        TIM_SetCounter(TIM2, 0);                            // Reset Timer2
10
11
        TIM_ClearITPendingBit(TIM2, TIM_IT_Trigger);        // Clear IRQ Timer 15
12
13
    }
14
}

header:
1
volatile uint32_t TriggerValue;

Wenn du einen guten Link hast mit klaren Informationen für Anfänger zu 
dem Thema, ziehe ich das natürlich vor, als euch mit solchen stupiden 
Fragen zu bombardieren. Auf allen Seiten, die ich mir zu dem Thema 
angeschaut habe, finde ich nur unzureichende Informationen, vor allem, 
wenn es um volatile geht.

von Steffen R. (steffen_rose)


Lesenswert?

Im Header steht normalerweise nur die Deklaration => extern fehlt
Du hast zweimal definiert == zwei Variable angelegt. Beim gcc dürfte 
dies gut gehen, da er nach meiner Erfahrung diese zusammenlegt.

Header:
1
extern volatile uint32_t TriggerValue;

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Aaaah, man darf extern noch zum volatile dazulegen? Supi, tausend Dank!

von Steffen R. (steffen_rose)


Lesenswert?

Reginald L. schrieb:
> Wenn du einen guten Link hast mit klaren Informationen für Anfänger zu
> dem Thema,

https://www.mikrocontroller.net/articles/FAQ#Was_hat_es_mit_volatile_auf_sich

Naja, ein Einsteiger C Kurs im Netz oder als Buch solltest Du als 
Umsteiger doch mal überfliegen. Dass du C nicht von Pike auf lernen 
willst ist mir klar.

von (prx) A. K. (prx)


Lesenswert?

Steffen R. schrieb:
> Der Interrupt Controller stößt immer den IRQ oder den FIRQ an.
> Ist der IRQ gerade aktiv nützt es auch nichts, wenn der Interrupt
> Controller einen Interrupt höherer Priorität anstoßen will.

Funktioniert. Der Handler muss natürlich Nesting unterstützen, d.h. 
Stack wechseln, Interrupts wieder einschalten und Vektor auslesen (nicht 
zwingend in dieser Reihenfolge). Das kann u.U. bereits im 
Runtime-Evironment des Compilers enthalten sein, so dass der 
Programmierer davon nichts sieht.

Entscheidend ist bei Interrupts nicht, ob das Verfahren hin hin zum 
Prolog/Epilog der Funktionen in der Hardware steckt (Cortex M), sondern 
ob priorisiertes Nesting mit geringem Aufwand überhaupt möglich ist. Und 
das ist bei ARM7 mit Interrupt-Controller der Fall, bei AVR hingegen 
nicht.

> (per Software kann man da was drehen, aber das war ja auch die Aussage
> -> IRQ Behandlung ja nach Umgebung)

Anders als bei AVRs ist das dann aber ein vollständiges Nesting mit 
echten Prioritäten, d.h. Unterbrechung nur durch höher priorisierte 
Interrupts.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Steffen R. schrieb:
> https://www.mikrocontroller.net/articles/FAQ#Was_hat_es_mit_volatile_auf_sich
>
> Naja, ein Einsteiger C Kurs im Netz oder als Buch solltest Du als
> Umsteiger doch mal überfliegen. Dass du C nicht von Pike auf lernen
> willst ist mir klar.

Hihi, dieser Link war meine erste Anlaufstelle ;) Allerdings steht da 
nicht die Information, die ich gerade von dir bekommen habe. Ich bin 
davon ausgegangen, dass volatile und extern sich gegenseitig 
ausschließen. So bin ich auf mehreren Seiten darauf gestoßen, dass 
volatile, extern, static und co in einer Tabelle als Speicherklassen 
aufgelistet werden. Nun kann ich ja nicht ahnen, dass eine Variable 
mehreren Speicherklassen zugeordnet werden kann. Zumindest in C.

Seit ich den µC habe lese ich ziemlich viel in Bezug auf C. Muss ich 
auch, sonst kann ich aus dem "Geschäft" hier aussteigen. Ich poste hier 
auch nicht jede Frage rein die mir in den Sinn kommt. Wenn ich 
allerdings den halben Tag das Internet nach Header, volitale und 
Variablenübergabe durchsuche und dann einen Satz finde der da lautet: 
"Variablen allgemein verfügbar zu machen stellt ein besonderes Problem 
dar, das besonders für Anfänger schwer verständlich ist. Grundsätzlich 
sollte man den Variablen in Header-Dateien das Schlüsselwort extern 
voranstellen. Damit erklärt man dem Compiler, dass es die Variable 
meineVariable gibt, diese jedoch an anderer Stelle definiert ist." In 
Verbindung mit meiner oben genannten Fehlinterpretation, bin ich somit 
wieder bei euch gelandet.

Das mit dem Buch, ist aber gar keine so schlechte Idee, hast du eine 
Empfehlung? Darf ruhig dicker sein.

von Steffen R. (steffen_rose)


Lesenswert?

A. K. schrieb:
> Der Handler muss natürlich Nesting unterstützen,

Gut. Der Handler, wo auch immer er sitzt, gehört bei mir zur Software. 
Du hast es aber gut auch die Position des Programmierers dargestellt.

Reginald L. schrieb:
> extern, static und co in einer Tabelle als Speicherklassen

Ich glaub nicht, dass man extern zur Speicherklasse zählt.

Reginald L. schrieb:
> Hihi, dieser Link war meine erste Anlaufstelle ;) Allerdings steht da
> nicht die Information, die ich gerade von dir bekommen habe.

Soll ich sagen, dass du scheinbar die falschen Fragen stellst ;-)

Der Link war für das Thema 'volatile'. Der Verweis auf Anfängerliteratur 
bezog sich auf die Nutzung der Schlüsselwörter.

Reginald L. schrieb:
> Das mit dem Buch, ist aber gar keine so schlechte Idee, hast du eine
> Empfehlung? Darf ruhig dicker sein.

Weiß nicht. Sollte eher dünner sein und sich "nur" mit C beschäftigen. 
Die restlichen Seiten sind meißt Nutzung unter Linux oder Windows.

Vielleicht:
Programmieren in C: Mit dem C-Reference Manual in deutscher Sprache
von Kerninghan und Ritchie
ISBN:3446154973

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Steffen R. schrieb:
> Ich glaub nicht, dass man extern zur Speicherklasse zählt.

Laut diversen C-Guides, ja. Beispielsweise 
http://www2.informatik.uni-halle.de/lehre/c/c_storag.html


Steffen R. schrieb:
> Programmieren in C: Mit dem C-Reference Manual in deutscher Sprache
> von Kerninghan und Ritchie

Das empfiehlst du auch nur, weil da zwei Gartenzwerge aufm Einband sind!

von Steffen R. (steffen_rose)


Lesenswert?

Reginald L. schrieb:
> Steffen R. schrieb:
>> Ich glaub nicht, dass man extern zur Speicherklasse zählt.
>
> Laut diversen C-Guides, ja. Beispielsweise

Ja, ok. Hast recht.

(6.7.1) storage-class-specifier:
typedef
extern
static
auto
register

(6.7.3) type-qualifier:
const
restrict
volatile


> Steffen R. schrieb:
>> Programmieren in C: Mit dem C-Reference Manual in deutscher Sprache
>> von Kerninghan und Ritchie
>
> Das empfiehlst du auch nur, weil da zwei Gartenzwerge aufm Einband sind!

;-)
Ich habs damit gelernt. Ist aber schon ewig her.
C11 wirst Du dort auch nicht finden. Das Basiswissen über die Sprache 
aber schon.

https://de.wikipedia.org/wiki/The_C_Programming_Language
"Das Buch sorgte auch für die Bekanntheit des Hallo-Welt-Programms."

"Es stammt von den Autoren Brian W. Kernighan und Dennis M. Ritchie, 
wovon Ritchie die Programmiersprache entwickelt hat."
Also, wenn die nicht wissen, wie es gemeint ist ....

: Bearbeitet durch User
von Luis (Gast)


Lesenswert?

Steffen R. schrieb:
> Im Header steht normalerweise nur die Deklaration => extern fehlt

Das müsste anders herum sein:
Im Header (File xyz.h) steht die Deklaration MIT storage-class-specifier 
"extern".

extern volatile uint32_t TriggerValue;

In der zugehörigen xyz.c Datei "sollte" man den Specifier "extern" nicht 
nutzen. Dann legt der Compiler die Variable an.

volatile uint32_t TriggerValue;
// oder
volatile uint32_t TriggerValue = Initvalue;


Nutzt man "extern" im C File und initialisiert die Variable nicht, gibt 
es eine Fehlermeldung, da der Compiler die Variable dann nicht angelegt 
hat.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Steffen R. schrieb:
> "Es stammt von den Autoren Brian W. Kernighan und Dennis M. Ritchie,
> wovon Ritchie die Programmiersprache entwickelt hat."
> Also, wenn die nicht wissen, wie es gemeint ist ....

Ah OK, dann werde ich schauen, dass ich in die Richtung nach einem Buch 
schaue.


Luis schrieb:
> Das müsste anders herum sein

War wohl nur ein Schreibfehler ;)


Abermals ein Dankeschön an alle!

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Hallo ihr!

So langsam klappts bei mir jetzt auch mit C, Compiler und Co. Ich 
beginne zu begreifen. Inzwischen bin ich dabei, die Frequenz des Rotors 
über USB VCP an MatLab zu übergeben. Es macht richtig Fun :)
Bin wieder zu CrossWorks gewechselt, EMBlocks bringt mich zur Weißglut 
mit seinen Fenstern und der vermeintlichen "MultiMonitor"-Unterstützung 
:/

Allerdings stellt sich mir hier eine weitere Frage zu volatile, in Bezug 
auf die obigen Posts:
Wenn ich irgendwo einer Variable einen konkreten Wert zuweise, oder nur 
deklariere, und ich diese Variable anderswo verändere, muss ich bei der 
ersten Deklaration noch ein volatile davorhängen. Sonst kann es sein, 
dass der Compiler, falls Optimierungen aktiviert, nur diese erste 
konkrete Zuweisung beachtet.
Nun habe ich aber der Variablen keinen konkreten Wert zugewiesen (siehe 
obiges Beispiel), dieser hängt ja von der Funktion 
"TIM_GetCounter(TIM2)" ab.
Muss man hier nicht tiefer graben?

von Steffen R. (steffen_rose)


Lesenswert?

volatile unterbindet einfach gesagt nur die Optimierung.
Das merkst Du speziell, wenn Du innerhalb einer Schleife auf die 
Veränderung der Variablen wartest. Ohne volatile würde der Compiler 
meinen, dass der Wert im Register immer aktuell ist. Der Interrupt würde 
aber nicht das Register, sondern den Speicher verändern.

Und zu Deklaration und Definition. Bis auf wenige Ausnahmen solltest Du 
diese immer gleich machen (+ 'extern' natürlich). Also keine 
Variationen, wenn du nicht explizit weißt, dass es in dem speziellen 
Fall anders ist.

: Bearbeitet durch User
von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Achso, also im Falle eines Timers ohne Interruptroutine, würde sowieso 
auf das Register zugegriffen werden und man bräuchte hier kein 
volantile, habe ich das richtig verstanden?

Klick hat es noch nicht gemacht, aber ich glaube es geht in die richtige 
Richtung. Muss mich wohl ein wenig in die Hardware reinarbeiten.

PS: Morgen müsste übrigens das von dir empfohlene Buch ankommen, dazu 
habe ich noch dieses "Lösungsbuch" bestellt. Gebraucht, war nicht teuer 
;)

von Steffen R. (steffen_rose)


Lesenswert?

Reginald L. schrieb:
> Achso, also im Falle eines Timers ohne Interruptroutine, würde sowieso
> auf das Register zugegriffen werden und man bräuchte hier kein
> volantile, habe ich das richtig verstanden?

Habe ich es wohl doch zu stark vereinfacht.

Für die Variable, in der Du den Zählerwert speicherst, bräuchtest du es 
nicht. Die Register des Timers sind im allgemeinen jedoch volatile. 
Diese ändern sich ja ohne Kontrolle des Programms selbsttätig.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Steffen R. schrieb:
> Für die Variable, in der Du den Zählerwert speicherst, bräuchtest du es
> nicht. Die Register des Timers sind im allgemeinen jedoch volatile.
> Diese ändern sich ja ohne Kontrolle des Programms selbsttätig.

Bin da immer noch nicht durchgestiegen. Spätestens, wenn mein Programm 
nicht das tut, was es meiner Meinung nach tun sollte, werde ich 
dahintergestiegen sein. Learning-by-doing ;)

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.