Forum: Mikrocontroller und Digitale Elektronik Anfängerfrage STM32


von Walter T. (nicolas)


Angehängte Dateien:

Lesenswert?

Guten Morgen zusammen,
ich bin gerade wieder Anfänger geworden und mache (seit gestern abend) 
meine ersten Gehversuche mit einem STM32F4discovery, CooCoxIDE und 
ARM-GCC. Es interessant, wie kitzlig so ein Cortex im Vergleich zum AVR 
ist.

Dabei hangele ich mich am folgenden Tutorial entlang:
 http://mystm32.de/doku.php?id=arm_interrupts_in_c

Mit dem Timer TIM7 wird eine LED getoggelt. Wie man auf dem Oszi-Bild 
sieht, entstehen dabei riesige zeitliche Abweichungen, als würden 
Interrupts vergessen. Daran ändert sich auch nichts, wenn ich bei
1
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
auf Null setze. Wo liegt mein Fehler?

Viele Grüße
W.T.

von Lutz H. (luhe)


Lesenswert?

Ändert sich etwas, wenn die Interrupts ausgeschaltet werden?
etwa so:
// ISR für Timer 7
void TIM7_IRQHandler() {
__disable_irq();
  GPIO_ToggleBits(GPIOD,GPIO_Pin_15); // blinkt unregelmaessig

  TIM_ClearITPendingBit(TIM7,TIM_IT_Update);
__enable_irq();
}

von (prx) A. K. (prx)


Lesenswert?

Man sollte den Interrupt nicht erst am Ende der ISR zurücksetzen. Man 
riskiert sonst, dass die ISR sofort erneut aufgerufen wird, weil es ein 
paar Takte dauert, bis sich dies bis zum Prozessor rumspricht.

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


Lesenswert?

Stell das Skope mal auf Pulsbreitentrigger unterhalb 10µs ein und schau 
an, ob es bei Pin 15 fündig wird. Könnte mir denken, dass sowas 
mittendrin in den langen Phasen auftritt.

: Bearbeitet durch User
von änge (Gast)


Lesenswert?

> while(1) {
>    GPIO_ToggleBits(GPIOD,GPIO_Pin_13); // blinkt
>  }

Sperrt Deine GIOToggleBits
die Interrupts?

von (prx) A. K. (prx)


Lesenswert?

änge schrieb:
> Sperrt Deine GIOToggleBits die Interrupts?

Yep, es wär sehr gut möglich, dass hier ein Konflikt dank nicht atomarem 
Portzugriff auftritt.

Edit: Typisch ST-Lib. Mal wieder zu kurz gedacht:
  GPIOx->ODR ^= GPIO_Pin;
Das geht natürlich gelegentlich schief.

Besser wär:
  GPIOx->BSRR = (GPIO_Pin << 16) | ((GPIOx->ODR & GPIO_Pin) ^ GPIO_Pin);
Das lässt alle anderen Pins garantiert unberührt.

: Bearbeitet durch User
von holger (Gast)


Lesenswert?

>Yep, es wär sehr gut möglich, dass hier ein Konflikt dank nicht atomarem
>Portzugriff auftritt.

Ein Exor auf ODR ist nun mal nicht atomar.

>entstehen dabei riesige zeitliche Abweichungen, als würden
>Interrupts vergessen.

Es wird kein Interrupt vergessen.
Das schnelle toggeln in der main ist das grösste Problem.
Sonst wäre der Fehler möglicherweise nur sporadisch aufgetreten.

  while(1) {
    GPIO_ToggleBits(GPIOD,GPIO_Pin_13); // blinkt
  }

Wenn GPIO_ToggleBits(GPIOD,GPIO_Pin_13); vom Interrupt
unterbrochen wird und dieser seinerseits am selben Port
Bits ändert, kann es dazu kommen das diese Änderung gleich
nach dem Interrupt von GPIO_ToggleBits(GPIOD,GPIO_Pin_13);
wieder rückgängig gemacht wird.

Man sollte dieses Stück Code am besten gleich als warnendes
Beispiel für andere archivieren;)

von (prx) A. K. (prx)


Lesenswert?

holger schrieb:
> Man sollte dieses Stück Code am besten gleich als warnendes
> Beispiel für andere archivieren;)

Beispielsweise dort:
https://www.mikrocontroller.net/articles/Interrupt#Interruptfeste_Programmierung

Noch Fragen, warum ich die ST-Lib nicht mag? :)

von Walter T. (nicolas)


Lesenswert?

Danke für die zahlreichen Rückmeldungen. Gestärkt von einem guten 
Mittagessen werde ich die mal Stück für Stück abarbeiten:

A. K. schrieb:
> Stell das Skope mal auf Pulsbreitentrigger unterhalb 10µs ein und schau
> an, ob es bei Pin 15 fündig wird. Könnte mir denken, dass sowas
> mittendrin in den langen Phasen auftritt.

Treffer! Mein Scope hat leider keinen Pulsbreitentrigger, aber es treten 
Pulsereignisse mit nur 1ms Länge auf - ob die in der Mitte der langen 
Phasen liegen kann ich nicht messen.

änge schrieb:
> Sperrt Deine GIOToggleBits
> die Interrupts?

Kann ich auf Anhieb nicht sagen, über die zahlreichen Funktionen, die 
die ST-Library mitbringt habe ich noch keine Übersicht- und alle 
Interrupt-Schalter kenne ich auch noch nicht, könnte also auch nicht aus 
dem List-File mit Sicherheit ausschlißen, daß da eine Interruptsperre 
drin ist.

A. K. schrieb:
> Man sollte den Interrupt nicht erst am Ende der ISR zurücksetzen. Man
> riskiert sonst, dass die ISR sofort erneut aufgerufen wird, weil es ein
> paar Takte dauert, bis sich dies bis zum Prozessor rumspricht.

Das wird der nächste Versuch.

von (prx) A. K. (prx)


Lesenswert?

Walter Tarpan schrieb:
> Das wird der nächste Versuch.

Solltest du tun. Aber die Ursache ist das weder atomare noch 
abgesicherte XOR auf ODR in der Toggle-Funktion. Das ist ein absolutes 
NoGo.

: Bearbeitet durch User
von Walter T. (nicolas)


Lesenswert?

Walter Tarpan schrieb:
> A. K. schrieb:
>> Man sollte den Interrupt nicht erst am Ende der ISR zurücksetzen. Man
>> riskiert sonst, dass die ISR sofort erneut aufgerufen wird, weil es ein
>> paar Takte dauert, bis sich dies bis zum Prozessor rumspricht.
>
> Das wird der nächste Versuch.

Das Rücksetzen an den Anfang halbiert die Länge der sporadischen Peaks 
auf 500ns Länge.

holger schrieb:
> Ein Exor auf ODR ist nun mal nicht atomar.

A. K. schrieb:
> Edit: Typisch ST-Lib. Mal wieder zu kurz gedacht:
>   GPIOx->ODR ^= GPIO_Pin;
> Das geht natürlich gelegentlich schief.
>
> Besser wär:
>   GPIOx->BSRR = (GPIO_Pin << 16) | ((GPIOx->ODR & GPIO_Pin) ^ GPIO_Pin);
> Das lässt alle anderen Pins garantiert unberührt.

Neee, oder? OK, das Dingen ist wirklich deutlich kitzliger als ein AVR. 
Ich habe noch viel zu lernen.

Vielen Dank für die Rückmeldungen!

von (prx) A. K. (prx)


Lesenswert?

Walter Tarpan schrieb:
> Neee, oder? OK, das Dingen ist wirklich deutlich kitzliger als ein AVR.

An dieser Stelle nicht. Wenn du beim AVR
  PORTA ^= 1;
schreibst, dann kriegst du exakt das gleiche Problem. Und müsstest es 
durch
  PINA = 1;
ersetzen, was aber z.B. beim Mega16 noch nicht möglich ist.

Nur die Sache mit dem Rücksetzen vom Int-Flag ist einfacher.

von Walter T. (nicolas)


Lesenswert?

A. K. schrieb:
>
> Besser wär:
>   GPIOx->BSRR = (GPIO_Pin << 16) | ((GPIOx->ODR & GPIO_Pin) ^ GPIO_Pin);
> Das lässt alle anderen Pins garantiert unberührt.

Merkwürdig. Das Reference Manual sagt, der Port hätte ein BSRR-Register, 
der Compiler ist anderer Meinung:

"error: 'GPIO_TypeDef' has no member named 'BSRR'"

Ist da die ST-Lib unvollständig oder will sie mich als Benutzer da nicht 
ranlassen?

Aber das sieht jetzt gut aus mit
GPIO_SetBits(GPIOD,(GPIO_Pin_13 << 16) | ((GPIOD->ODR & GPIO_Pin_13) ^ 
GPIO_Pin_13));

Was aber nach meinem Geschmack ist daß so das Setzen beliebig vieler 
Bits in einem Port atomar ist.

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


Lesenswert?

Walter Tarpan schrieb:
> "error: 'GPIO_TypeDef' has no member named 'BSRR'"

Schönes Beispiel dafür, wie 2 Leute in der gleichen Firma gegeneinander 
arbeiten. Da hat der Hardware-Designer eine gute Idee gehabt, die der 
Autor des Include-Files wieder zunichte machte. Das BSRR Register ergibt 
nämlich in seiner Auslegung sehr viel Sinn, gerade weil man dessen beide 
Hälften zusammen atomar schreiben kann. Eben deshalb sind die Ports der 
STM32 16 Bits breit, nicht 32 Bits.

Nur mit dem vollständigen BSRR kann man mehr als ein Bit in einem Port 
atomar auf einen beliebigen Wert setzen:
  GPIOx->BSRR = mask << 16 | value;
Aber nein, da musste der Autor natürlich die Hälften wieder trennen, 
weil er diesen Aspekt überhaupt nicht verstanden hat. Siehe
Beitrag "STM32 - Nerviger Bug in "stm32f4xx.h""

Anno STM32F1 konnte er den Fehler noch nicht machen, denn da waren 
16-Bit Zugriffe auf die GPIO Register unzulässig. Kaum bekam er mit dem 
Redesign des GPIO auf AHB statt APB die Chance...

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


Lesenswert?

Walter Tarpan schrieb:
> Aber das sieht jetzt gut aus mit
> GPIO_SetBits(GPIOD,(GPIO_Pin_13 << 16) | ((GPIOD->ODR & GPIO_Pin_13) ^
> GPIO_Pin_13));

Erstaunlich. Denn GPIO_SetBits interessiert sich nur für die unteren 16 
Bits und schreibt nur BSRRL. Pin 13 wird darin also entweder gesetzt, 
oder unverändert gelassen, nirgends aber gelöscht.

Ich gehe davon aus, dass nun zwar Pin 15 in konstantem Zeitraster 
toggelt, nicht aber Pin 13.

Mit der Lib geht eigentlich nur
  if (GPIO_ReadOutputData(GPIOD) & GPIO_Pin_13)
    GPIO_ResetBits(GPIOD, GPIO_Pin_13);
  else
    GPIO_SetBits(GPIOD, GPIO_Pin_13);

Du solltest zudem beachten, dass dieser Konflikt auch zwischen den 
beiden Timer-Interrupts auftreten kann, sofern die nicht in der gleichen 
premption priority liegen. Nur sehr viel unwahrscheinlicher.

: Bearbeitet durch User
von Roland E. (roland0815)


Lesenswert?

A. K. schrieb:
> Man sollte den Interrupt nicht erst am Ende der ISR zurücksetzen.

Warum denn das? Man setzt den erst IRQ dann zurück, wenn man mit der 
Behandlung des IRQ fertig ist. Nicht eher. Zumindest wenn man darauf 
Einfluss hat.

> Man
> riskiert sonst, dass die ISR sofort erneut aufgerufen wird, weil es ein
> paar Takte dauert, bis sich dies bis zum Prozessor rumspricht.

Ebend. Man müsste seine ISR (erst recht bei aktiviertem nested IRQ) 
reentrant bauen.

Wenn die IRQ-Routine zu lang wird, um zwischen zwei Auslösungen die 
main() zuzulassen, hat man an anderer Stelle was falsch gemacht.

von (prx) A. K. (prx)


Lesenswert?

Roland Ertelt schrieb:
> Warum denn das? Man setzt den erst IRQ dann zurück, wenn man mit der
> Behandlung des IRQ fertig ist. Nicht eher.

Wenn man Probleme kriegen will, nur zu.

> Ebend. Man müsste seine ISR (erst recht bei aktiviertem nested IRQ)
> reentrant bauen.

Es geht nicht um verlorene Interrupts, sondern um doppelte 
ISR-Aufrufe für dasselbe Ereignis.

Siehe Beitrag "STM32: Timer-ISR löst 2x aus - Fehler im Flag-Reset bei Optimierung O3"

: Bearbeitet durch User
von Walter T. (nicolas)


Lesenswert?

A. K. schrieb:
> Ich gehe davon aus, dass nun zwar Pin 15 in konstantem Zeitraster
> toggelt, nicht aber Pin 13.

Stimmt. Nachdem die Interrupt-Geschichte so weit verstanden war, habe 
ich das ad acta gelegt und bin zum nächsten Punkt weitergegangen - und 
das war die Ansteuerung meines Grafik-LCDs (was auch mittlerweile 
funktioniert). Jetzt bin ich gerade bei der Suche nach dem Äquivalent zu 
"PROGMEM".

Es geht sozusagen weiter...

von (prx) A. K. (prx)


Lesenswert?

Walter Tarpan schrieb:
> AJetzt bin ich gerade bei der Suche nach dem Äquivalent zu
> "PROGMEM".

const

von Walter T. (nicolas)


Lesenswert?

Gibt es keine Möglichkeit, an die GPIOx->BSRR zu kommen? Ich brauche 
gerade eine unschöne Konstruktition wie
1
  GPIO_ResetBits(GLCD_DATA_GPIO, 0xFF<<GLCD_DATA_OFFSET);
2
  GPIO_SetBits(GLCD_DATA_GPIO,  value<<GLCD_DATA_OFFSET);
obwohl ich eigentlich das hier zu machen will:
1
  GPIOx->BSRR = value<<GLCD_DATA_OFFSET | ~value<<(GLCD_DATA_OFFSET+16)

von (prx) A. K. (prx)


Lesenswert?

Siehe https://www.mikrocontroller.net/topic/goto_post/2700761

Die Set-Bits im BSRR haben übrigens Vorrang vor den Reset-Bits. Es 
reicht also aus, in die obere Hälfte die Maske zu schreiben. Es muss 
nicht das Komplement sein.

von Walter T. (nicolas)


Lesenswert?

A. K. schrieb:
> Die Set-Bits im BSRR haben übrigens Vorrang vor den Reset-Bits. Es
> reicht also aus, in die obere Hälfte die Maske zu schreiben. Es muss
> nicht das Komplement

Das verstehe ich nicht. Wenn ich die Pins von z.B. 4-12 mit einem Byte 
beschreiben will, ohne die anderen Pins zu beeinflussen muß ich doch 
sowohl die 1en setzen als auch die 0en löschen?

von (prx) A. K. (prx)


Lesenswert?

Walter Tarpan schrieb:
> Das verstehe ich nicht. Wenn ich die Pins von z.B. 4-12 mit einem Byte
> beschreiben will, ohne die anderen Pins zu beeinflussen muß ich doch
> sowohl die 1en setzen als auch die 0en löschen?

Wenn du als Beispiel für 4 Datenbits ins BSRR den Wert 0x00F000F0 
reinschreibst, also die gleichen 4 Bits sowohl setzt als auch löschst, 
dann wird gesetzt. Also reicht
  BSRR = 0x00F00000 | value << 4;
Wo ein Bit in value 0 ist wird gelöscht, bei 1 gesetzt. .

: Bearbeitet durch User
von Walter T. (nicolas)


Lesenswert?

OK, verstehe, ich habe dann die Veroderung mit einer Konstanten und 
spare mir die Veroderung mit einer geshifteten Inversen.

von Gerd E. (robberknight)


Lesenswert?

A. K. schrieb:
> Wenn du als Beispiel für 4 Datenbits ins BSRR den Wert 0x00F000F0
> reinschreibst, also die gleichen 4 Bits sowohl setzt als auch löschst,
> dann wird gesetzt. Also reicht
>   BSRR = 0x00F00000 | value << 4;
> Wo ein Bit in value 0 ist wird gelöscht, bei 1 gesetzt. .

Und das ganze atomic und auch per DMA ansteuerbar. Das hat ST gut 
umgesetzt. Schon ein deutlicher Fortschritt gegenüber dem Atmega.

Nur muss man sich das halt einmal genau anschauen und sich dazu 
durchreißen, diese dämlichen ST-Libs wegzuwerfen...

von Thorsten S. (thosch)


Lesenswert?

ich häng mich hier mal dran,
habe zwar langjährige Erfahrung im Programmieren von Firmware in C auf 
8-Bit CPUs (Z180 und AVR), bin aber in Sachen ARM ebenfalls Anfänger mit 
'nem STM32F407 unter CooCox IDE und ARM GCC in einem eigenen 
Hardware-Design.

gibts da nicht auch 'ne Lösung, wie man das mit der ST-Lib hinbekommt?

Insbesondere vermisse ich beim ARM GCC ein Äquivalent zu den 
ATOMIC-Makros, insbesondere ATOMIC_BLOCK(ATOMIC_RESTORESTATE) die es 
beim AVR-GCC gibt...

gibt's da nichts Vergleichbares oder hab ich es nur nicht gefunden?

Die ST-Lib finde ich als ARM-Einsteiger an sich gar nicht schlecht, da 
sie einem eine standardisierte Schnittstelle zur Peripherie liefert, 
ohne daß man sich mit jedem einzelnen Bit in jedem Config-Register zu 
Fuß beschäftigen muß.

von (prx) A. K. (prx)


Lesenswert?

Thorsten S. schrieb:
> gibts da nicht auch 'ne Lösung, wie man das mit der ST-Lib hinbekommt?

Mit der unveränderten Lib m.E. nur so, wie ich oben beschrieb.
Also per if...else...

> Insbesondere vermisse ich beim ARM GCC ein Äquivalent zu den
> ATOMIC-Makros, insbesondere ATOMIC_BLOCK(ATOMIC_RESTORESTATE) die es
> beim AVR-GCC gibt...

Es ist ein Unterschied, ob man eine Lib aus der Erfahrung heraus 
schreibt und weiterpflegt, oder als Anfängerpraktikum. Dieses 
ATOMIC-Zeug war ausserdem in der avrlibc auch nicht von Anfang an drin.

Es halbwegs Gesunder wäre auch nicht auf die Idee gekommen, bei einem 
Prozessor mit exzellenter Parametrisierung von Funktionen über Register 
ausgerechnet zeilenschindend per Struct zu parametrisieren. Sowas macht 
jemand, der als Vorbild die Lib eines Akkumulator-Prozessors wie STM8 
hat.

Für mangelndes Verständnis spricht auch, dass im Code für die UART 
anfangs eine Konstante 0x64 stand, erkennbar aber 100 gemeint war und 
ausserdem eine Zweierpotenz besser gewesen wäre. Das ist eigentlich nur 
erklärbar, wenn der Code ohne jedes Verständnis aus disassemblierten 
Objektcode rausgelesen wurde.

> gibt's da nichts Vergleichbares oder hab ich es nur nicht gefunden?

In der Lib nicht. Im Prozessor ist es relativ elegant über 
BASEPRI/BASEPRI_MAX realisierbar.

> ohne daß man sich mit jedem einzelnen Bit in jedem Config-Register zu
> Fuß beschäftigen muß.

Das musst du spätestens dann doch tun, wenns nicht gleich funktioniert 
und du per Debugger in die Register siehst.

von Roland E. (roland0815)


Lesenswert?

A. K. schrieb:
> Roland Ertelt schrieb:
>> Warum denn das? Man setzt den erst IRQ dann zurück, wenn man mit der
>> Behandlung des IRQ fertig ist. Nicht eher.
>
> Wenn man Probleme kriegen will, nur zu.
>

Bisher hatte ich da nie Probleme. Bis auf, dass man uU einen IRQ 
"verschläft", weil er erneut auslösen würde, bevor der Erste fertig ist. 
Dann ist aber die ISR zu lang.

>> Ebend. Man müsste seine ISR (erst recht bei aktiviertem nested IRQ)
>> reentrant bauen.
>
> Es geht nicht um verlorene Interrupts, sondern um /doppelte/
> ISR-Aufrufe für dasselbe Ereignis.
>

"Doppelte" Interrupts gibt es nicht. Zumindest wenn man keinen Mist mit 
Back-Calls macht. Wie soll das auch gehen, wenn der Prozessor noch im 
privilegierten IRQ-Modus ist, und man ihn (noch) nicht wieder mit 
"return from interrupt" (reti) freigegeben hat. Im schlimmsten Fall wird 
die ISR sofort nach dem reti wieder aufgerufen.

> Siehe Beitrag "STM32: Timer-ISR löst 2x aus - Fehler im Flag-Reset bei Optimierung O3"

Das dort zitierte Phänomen ist ein Kompiler(optimierungs)fehler. Dafür 
kann der ARM nix, wenn der Kompiler ein "reti" setzt, und vorher das 
"clear flag" wegoptimiert. Der ARM macht in dem Fall alles richtig, nur 
der Anwender dort hat offenbar nie ins Kompilat (assembler) geschaut 
oder das Projekt simuliert. Sonst hätte er den Mist bemerkt.

Wenn der dortige OP das Projekt in asm geschrieben hätte, wäre sein 
"Problem" gar nicht auftreten.

PS: Wer denkt, einen ARM (oder ein ähnliches MuC-System) programmieren 
zu können, ohne sich mit Assembler und den Prozessorregistern 
beschäftigen zu müssen, irrt schwer.

: Bearbeitet durch User
von Markus M. (Firma: EleLa - www.elela.de) (mmvisual)


Lesenswert?

Roland Ertelt schrieb:
> Bisher hatte ich da nie Probleme. Bis auf, dass man uU einen IRQ
> "verschläft", weil er erneut auslösen würde, bevor der Erste fertig ist.
> Dann ist aber die ISR zu lang.

Ist es nicht so, wenn man schon zu Beginn der ISR Routine das ISR Flag 
löscht und dann wenn die Hardware dieses Flag während der ISR 
Bearbeitung erneut setzt, dann würde doch nach beenden der aktuellen 
Bearbeitung diese gleiche ISR Rourtine erneut gestartet werden.
So verstehe ich zumindest das ISR Handling der Prioritäten.
Damit sollte auch nichts verloren gehen.

von Walter T. (nicolas)


Lesenswert?

Roland Ertelt schrieb:
> Wenn der dortige OP das Projekt in asm geschrieben hätte, wäre sein
> "Problem" gar nicht auftreten.

Hmmm....also ich habe auf dem AVR vor der Migration 4627 Zeilen C-Code. 
Wenn ich den wirklich komplett in ASM neu implementieren müßte, hätte 
ich tatsächlich nicht das Problem mich über Interrupts zu ärgern- dann 
wäre ich noch daran beschäftigt, beide ARM-Codes zu lernen.

Ich will mich ja nicht davor drücken Register und 
Implementierungsdetails zu lernen - aber bei der Prozessorgröße ist für 
mich die Grenze, wo eine Hochsprache geeigneter als Assembler erscheint 
deutlich überschritten.

von W.S. (Gast)


Lesenswert?

Walter Tarpan schrieb:
> ich bin gerade wieder Anfänger geworden und mache (seit gestern abend)
> meine ersten Gehversuche mit einem STM32F4discovery, CooCoxIDE und
> ARM-GCC...

...UND mit der ST-Lib. Das hattest du nicht erwähnt.

Glaub mir, diese Kombination ist zum Einsteigen nicht wirklich die beste 
Wahl. Leg dir zum absoluten Anfang lieber ne Demo-Version vom Keil zu 
und verzichte auch erstmal auf die gesamte ST-Lib. Da weißt du, daß der 
Compiler wirklich keinen Mist baut und du merkst schon bald, daß es in 
fast allen Fällen leichter ist, aus eigenr Kraft mit dem µC klar zu 
kommen, als wenn du zwischen deine Ideen und Hardware noch so eine 
aufgebauschte Pseudo-Abstraktionsschicht zwischenschaltest.

Wenn dann deine Projekte mal größer werden, kannst du mit dem dann 
angesammelten Wissen immer noch auf GCC umsteigen. Aber dann hast du 
hoffentlich bereits deine eigenen ausgetesteten Sourcen und wenn es dann 
Späne macht, weißt du, daß es am GCC und/oder der ST-Lib liegt - sofern 
du sowas dann überhaupt noch benutzen willst.

> Es interessant, wie kitzlig so ein Cortex im Vergleich zum AVR
> ist.

Nö. Die Cortexe sind überhaupt nicht kitzlig, man sollte sie bloß ein 
bissel verstehen. Und nochwas: Verzichte lieber auf den Gedanken, sowas 
wie EnableInt() oder DisableInt() benutzen zu wollen. Genau dann, wenn 
du glaubst, sowas jetzt mal dringend zu benötigen, hast du nen schweren 
Entwurfsfehler gemacht.

W.S.

von holger (Gast)


Lesenswert?

>Verzichte lieber auf den Gedanken, sowas
>wie EnableInt() oder DisableInt() benutzen zu wollen. Genau dann, wenn
>du glaubst, sowas jetzt mal dringend zu benötigen, hast du nen schweren
>Entwurfsfehler gemacht.

Zeig uns ein Beispiel grosser Meister!

von (prx) A. K. (prx)


Lesenswert?

@Roland: Jetzt geht das Märchen mit dem vermeintlichen Compilerfehler 
wieder los. Der IF-Reset Befehl wird nicht wegoptimiert. Er ist nur zu 
dicht am Return. Wegoptimiert würde die ISR in eine Dauerschleife 
laufen, statt 2x ausgeführt.

@MM: Korrekt. Für die Funktion der ISR selbst ist nicht relevant, ob das 
IF am Anfang oder am Ende zurück gesetzt wird.

von Roland E. (roland0815)


Lesenswert?

Markus Müller schrieb:
> Roland Ertelt schrieb:
>> Bisher hatte ich da nie Probleme. Bis auf, dass man uU einen IRQ
>> "verschläft", weil er erneut auslösen würde, bevor der Erste fertig ist.
>> Dann ist aber die ISR zu lang.
>
> Ist es nicht so, wenn man schon zu Beginn der ISR Routine das ISR Flag
> löscht und dann wenn die Hardware dieses Flag während der ISR
> Bearbeitung erneut setzt, dann würde doch nach beenden der aktuellen
> Bearbeitung diese gleiche ISR Rourtine erneut gestartet werden.
> So verstehe ich zumindest das ISR Handling der Prioritäten.
> Damit sollte auch nichts verloren gehen.

Das ist korrekt. Zumindest wenn man bei dem ARM den "nested Interrupt" 
nicht nutzt wird es so passieren.

Daher verstehe ich ehrlich gesagt das Gesabbel über "doppelte 
Interrupts" nicht.

Dass man die Flags erst am Ende der Routine löscht, ist mehr ein Zeichen 
des Programmierers:
Hier bin ich damit fertig, jetzt kommt das Nächste.

Und es ist (m)eine Angewohnheit aus Software-IRQs von rtOS-en. Damit 
umgeht man wie schon gesagt das Problem, dass man reentrant schreiben 
müsste.

Es gibt noch die Unwarscheinlichkeit, dass irgend ein Bug im Kern beim 
löschen des Flags andere Teile des Subsystems ändert. Daher fasse ich zu 
Ändernde Werte immer erst dann an, wenn es wirklich nötig wird.
Man muss nur mal di diversen Errata zu einigen Kernen lesen. Da wird 
einem Übel...

von (prx) A. K. (prx)


Lesenswert?

@holger: BASEPRI und sinnvolle Prioritäten ist i.d.R. besser, als ganz 
abschalten.

von Roland E. (roland0815)


Lesenswert?

A. K. schrieb:
> @Roland: Jetzt geht das Märchen mit dem vermeintlichen Compilerfehler
> wieder los. Der IF-Reset Befehl wird nicht wegoptimiert. Er ist nur zu
> dicht am Return.

Hm. Das musst du jetzt mal genauer erklären. Wie kann eine Befehl "zu 
dicht" an einem andern stehen? Eine CPU ist kein Azubi, der früh immer 
etwas länger braucht bis ers schnallt.

Und selbst wenn der Befehl "zu dicht" am Return steht, ist es trotzdem 
ein Kompilerfehler. Denn wenn es ein "zu dicht[1]" gibt, muss der 
Kompiler das berücksichtigen.

Roland
[1]Auch in einer echten Multi-Tread-Umgebung muss der Kompiler korrekt 
berücksichtigen, dass ein Flag löschen uU mehrere Taktzyklen dauert und 
darf die Ausführung des "reti" unter keinen Umständen vor der korrekten 
Ausführung der letzten Instruktion umsetzen.

: Bearbeitet durch User
von holger (Gast)


Lesenswert?

>Hm. Das musst du jetzt mal genauer erklären. Wie kann eine Befehl "zu
>dicht" an einem andern stehen? Eine CPU ist kein Azubi, der früh immer
>etwas länger braucht bis ers schnallt.

Das zurücksetzen des Interruptflags dauert unter Umständen
etwas länger. Wenn man die Interruptroutine verlässt
bevor das Flag zurückgesetzt ist wird der Interrupt noch
einmal aufgerufen. Das ist das Problem.

von (prx) A. K. (prx)


Lesenswert?

@Roland: Er kann es nicht berücksichtigen, weil das keine Eigenschaft 
des Prozessors ist, sondern des Gesamtsystems. Und weil er nicht wissen 
kann, was der Store bedeutet. Compiler sind nicht allwissend.

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


Lesenswert?

@Roland: zu [1]: Das abzuwarten wäre allenfalls der CPU möglich, nicht 
aber dem Compiler.

von Roland E. (roland0815)


Lesenswert?

A. K. schrieb:
> @Roland: Er kann es nicht berücksichtigen, weil das keine Eigenschaft
> des Prozessors ist, sondern des Gesamtsystems. Und weil er nicht wissen
> kann, was der Store bedeutet. Compiler sind nicht allwissend.

Doch, das kann (muss) er wissen. Zumindest wenn er nach Zeit optimieren 
können will, muss er sehr genau die Ausführungszteiten der einzelnen 
Maschinen/Assemblerbefehle kennen. Wie sonst will er ein Loop-Unrolling 
beurteilen?

Das Problem ist dort wie überall: Es muss ausgiebig getestet werden. Bei 
gcc macht dass der "Kunde". Dort ist das Feedback, gelinde gesagt, 
mangelhaft. Zumal 75% der Anwender nicht mal in der Lage sind einen 
Kompilerfehler zu erkennen und korrekt ans Entwicklerteam zu melden.

Deswegen bekommen es ja auch einige Kompiler sauber hin, während andere 
dabei scheitern.

von holger (Gast)


Lesenswert?

>Doch, das kann (muss) er wissen. Zumindest wenn er nach Zeit optimieren
>können will, muss er sehr genau die Ausführungszteiten der einzelnen
>Maschinen/Assemblerbefehle kennen. Wie sonst will er ein Loop-Unrolling
>beurteilen?

Der Compiler kennt aber nicht die Zeiten bis ein Befehl an der
Peripherie angekommen ist. Die Peripherie ist kein Bestandteil der CPU.

von (prx) A. K. (prx)


Lesenswert?

Der Compiler weiss auch nicht, dass der Store das IF zurücksetzt. Woher 
denn?

von Roland E. (roland0815)


Lesenswert?

holger schrieb:
>>Doch, das kann (muss) er wissen. Zumindest wenn er nach Zeit optimieren
>>können will, muss er sehr genau die Ausführungszteiten der einzelnen
>>Maschinen/Assemblerbefehle kennen. Wie sonst will er ein Loop-Unrolling
>>beurteilen?
>
> Der Compiler kennt aber nicht die Zeiten bis ein Befehl an der
> Peripherie angekommen ist. Die Peripherie ist kein Bestandteil der CPU.

Urks. Was rauchen manche Leute..?

- Es ist ganz genau bekannt, wie lange eine Operation dauert.
- Es ist ganz genau bekannt, wie lange welcher Zugriff auf welche 
Register dauert.

Daraus lässt sich korrekt errechnen, wie lange eine beliebige Operation 
auf ein beliebiges Register der CPU/Peripherie (und beim ARM sogar auf 
externe Speicherzellen im Nachbarchip) dauert.

Ich streite gar nicht ab, dass bei einigen Operationen Kombinationen 
entstehen, welche selten (bei einigen Kompilern aus Zeitgründen gar 
nicht) verifiziert werden. Das ist aber ein Problem des Kompilers, nicht 
des Kerns.

Und ja, die Optimierungsstufe 3 "auf Zeit" ist bei allen Kompilern eine 
Wundertüte.

von Markus M. (Firma: EleLa - www.elela.de) (mmvisual)


Lesenswert?

Nein, es ist kein GCC Bug, der macht was ihm programmiert wurde.

Der GCC hat das Flag "-mcpu=cortex-m3" vom Cortex-M3 aber damit hat der 
noch lange keine Ahnung ob es ein STM, NXP oder sonst was ist. Und der 
müsste vor jedem "reti" noch "nop" hinzufügen, was auch nicht wirklich 
sinnvoll ist. Woher soll GCC denn wissen dass der Befehl vor dem reti 
das IF löscht?

Wenn ich z.B. zu Beginn die IF lösche und ich will/muss den ISR schnell 
beenden, dann wären die "nop"s eher störend.

Das ist wohl auch kein Bug vom Cortex-Kern, denn der hat auch keine 
Ahnung von der Peripherie die ST/NXP/... ran hängen

Da müssten ST/NXP/... schon her gehen und wissen, wenn ein IF gelöscht 
wird, und der nächste Befehl ist ein reti, dass dann der reti so lange 
pausiert bis das IF auch wirklich weg ist.

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


Lesenswert?

Optimierungen wie unrolling gehen von Annahmen aus, nicht von Fakten. 
Worst case ist dabei eine höhere Laufzeit oder mehr Code als nötig. Das 
Zeitverhalten von Interrupts, Peripherie oder DMA ist ausserhalb seiner 
Sicht und es einzurechnen ist Sache des Programmierers.

von Roland E. (roland0815)


Lesenswert?

A. K. schrieb:
> Der Compiler weiss auch nicht, dass der Store das IF zurücksetzt. Woher
> denn?

Muss er auch nicht. Er muss nur wissen wie lange es dauert bzw (und) 
dass ein "store" auf Adresse "0xblab1a"[1] eine atomische Operation ist, 
welche nicht durch ein "reti" (und ggf weitere Operationen) unterbrochen 
werden darf.

Offenbar weiss das aber der gcc nicht, weil es ihm bisher niemand gesagt 
hat.

Roland
[1]der jetzt zu faul ist, rauszusuchen an welcher Adresse das "IF" 
tatsächlich liegt.

von Lutz H. (luhe)


Lesenswert?

Das Laufzeitproblem tritt oft bei "toggle" auf.

Was wird programmiert: Ändere mal was, was am Ausgang rauskommt ist mir 
egal.
Bei Systemen mit mehreren Task und Laufzeiten, ist  das Verhalten so 
doppelt nicht bestimmt.

Durch Auswertung einer Uhr ist es dann nicht mehr so schädlich, wenn das 
Ausgangssignal doppelt gesetzt oder rückgesetzt wird.

von Roland E. (roland0815)


Lesenswert?

A. K. schrieb:
> Optimierungen wie unrolling gehen von Annahmen aus, nicht von Fakten.
> Worst case ist dabei eine höhere Laufzeit oder mehr Code als nötig. Das
> Zeitverhalten von Interrupts, Peripherie oder DMA ist ausserhalb seiner
> Sicht und es einzurechnen ist Sache des Programmierers.

Die Zeiten sind schon lange vorbei. Das war Stand 2000 so...

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


Lesenswert?

Sorry Roland, aber dir hier eine Vorlesung über Compiler und 
Rechnerarchitekturen zu halten überschreitet grad die Kapazität  meines 
Tabs. Glaub es oder lass es, aber von beidem habe ich etwas Ahnung.

von holger (Gast)


Lesenswert?

>> Der Compiler kennt aber nicht die Zeiten bis ein Befehl an der
>> Peripherie angekommen ist. Die Peripherie ist kein Bestandteil der CPU.
>
>Urks. Was rauchen manche Leute..?
>
>- Es ist ganz genau bekannt, wie lange eine Operation dauert.

Es ist bekannt wie lange ein Assembler Befehl in der CPU dauert.
Wobei das bei ARM wohl auch noch gelogen ist.

>- Es ist ganz genau bekannt, wie lange welcher Zugriff auf welche
>Register dauert.

Nein, eben nicht. Der Hersteller weiss wie lange das dauert.
Der Compiler kennt aber die Peripherie nicht.
Die Register der Peripherie hängen an unterschiedlichen
Bussen. AHBx, APBx. Der Compiler weiss nicht wie lange es dauert bis
dort Bits gesetzt, gelöscht wurden.

Genau genommen weiss der gar nicht das er gerade ein Interruptflag
löscht. Das ist für ihn nur ein Bit in einem Register das irgendwo
im RAM gemappt ist.

von W.S. (Gast)


Lesenswert?

Roland Ertelt schrieb:
> Wie kann eine Befehl "zu
> dicht" an einem andern stehen? Eine CPU ist kein Azubi, der früh immer
> etwas länger braucht bis ers schnallt.

Du vergißt ganz und gar die dahintersteckende Architektur:
Ein Peripherie-Core erzeugt ein Interruptsignal, das zum 
Interrupt-Controller gelangt. Der verarbeitet es und interruptiert 
seinerseits die CPU. Also muß nacheinander folgendes passieren:
1. der Peripherie-Core muß sein Signal wegnehmen
2. der Interrupt-Controller muß kapieren, daß die Sache erledigt ist
3. die CPU muß kapieren, daß die Behandlungsroutine zu Ende ist.

Zumeist tut man gut daran, 1. und 2, deutlich vor 3. zu erledigen. 
Reentrant brauchr da GARNIX programmiert zu werden, denn die CPU weiß 
ganz genau, ob der "RETI" dieses Interrupts schon gekommen ist oder 
nicht und wird diesen Interrupthandler nicht ein zweites Mal starten, 
solange er noch läuft.

W.S.

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.