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.
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?
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?"
;-)
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.
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.
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.
> 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.
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 :)
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.
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.
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.
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 :)
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:-)
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
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.
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 ...
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.
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
longintTriggerValue;
5
6
voidTIM2_IRQHandler(void)
7
{
8
9
if(TIM_GetITStatus(TIM2,TIM_IT_Trigger)!=RESET)
10
{
11
12
TriggerValue=TIM_GetCounter(TIM2);// Get Timer2 Value
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.
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 :/
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!
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.
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 :)
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^^
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 :)
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.
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?
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.
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|
>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>
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".
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.
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
volatileuint32_tTriggerValue;
2
3
voidTIM2_IRQHandler(void)
4
{
5
if(TIM_GetITStatus(TIM2,TIM_IT_Trigger)!=RESET)
6
{
7
8
TriggerValue=TIM_GetCounter(TIM2);// Get Timer2 Value
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.
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:
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.
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.
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
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 ....
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.
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!
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?
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.
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
;)
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.
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 ;)