Das Programm funktioniert, die LED Blinkt im Sekundentakt. Aber wenn ich
die Zeile
1
if(READ_BIT(RTC->CRL,RTC_CRL_SECF))
weg lasse, dann blitzt die LED jede Sekunde nur ganz kurz auf. Man kann
es so gerade eben mit blossem Auge erkennen. Daraus schliesse ich, dass
nach dem Sekunden-Interrupt immer noch ein zweiter Interrupt auftritt.
Aber warum? Ich bin mir nicht dessen bewusst, eine andere
Interrupt-Quelle aktiviert zu haben.
Ich sehe nichts im Reference Manual, dass die Flags beim lesen
automatisch gelöscht werden. Das muss per SW gemacht werden. Keine
Ahnung was es ist, aber lass doch die IF Abfrage einfach drin, wenn es
damit funktioniert...
Jörn schrieb:> RTC_IRQHandler() scheint ein Sammelinterrupt zu sein. Mit deiner Zeile> fragst du das Sekundenflag ab. Ist aber jetzt nur so geraten...
Ja schon, aber da ich nur den Sekunden-Interrupt aktiviert habe, sollte
es keine Rolle spielen, ob ich das Flag teste, oder nicht.
Aus irgend einem Grund wird der IRH Handler scheinbar immer genau
zweimal aufgerufen. Einmal wegen dem Sekunden-Flag und dann nochmal
wegen <keine Ahnung>.
noreply@noreply.com schrieb:> Ich würde die Anzahl Interrupts mal vor dem if in einer globalen> Variable zählen und ausgeben.
Guter Tipp. Habe ich gemacht:
Das habe ich im Debugger ausgeführt, mit einem Watch auf die cnt
Variable und einem Breakpoint auf der CLEAR_BIT Zeile. Sie wird so
hochgezählt:
1,3,5,7,9,11,13,15,17, usw.
noreply@noreply.com schrieb:> Mach mal erst Clear Interrupt und dann Toggle.
Du bist gut! Jetzt wird der Interrupt nur noch einmal pro Sekunde
aufgerufen.
Warum?
Ich mag es nicht, wenn ich ein Problem gelöst habe ohne die Lösung zu
verstehen. Solche Sachen kommen oft irgendwann wieder hoch und bereiten
dann ernsthafte Probleme.
Einfaches Softwaredesign, das ich bei Interruptbearbeitung beachten
würden.
Interrupt-Grund Abfragen
Interrupt-Grund Zurücksetzen
restliche Arbeit erledigen
Es gibt vielleicht auch noch Hinweise in der Dokumentation, wieviele
Takte vergangen sein müssen, bis der Interrupt zuverlässig gelöscht
wird. Aber man kann nur mit der Hardware arbeiten.
Hi, das Problem ist, dass nach dem Clear Interrupt direkt der return
Befehl kommt. Da ist die Pipeline noch nicht fertig. Dann liegt der
Request noch an und der Interrupt kommt nochmal. Wenn du sicher sein
willst, schreibe ein ISB() dahinter. Dann ist es unabhängig von der
Position.
Daran habe ich auch schon mal gerätselt.
Stefanus F. schrieb:> // Wait until last write operation is done> while(!READ_BIT(RTC->CRL, RTC_CRL_RTOFF)) {}
Könnte sein, kann aber auch nur der Interruptverwaltung Zeit geben, sich
ordentlich zurückzusetzen.
Man könnte auch eine statische Variable hochzählen.
Stefanus F. schrieb:> noreply@noreply.com schrieb:>>> Meinst du so?:> void RTC_IRQHandler(void)> {
Interrupt-Grund Abfragen
> // if a second has elapsed, then ...> if (READ_BIT(RTC->CRL,RTC_CRL_SECF))> {
Interrupt-Grund Zurücksetzen
> // Clear interrupt flag> CLEAR_BIT(RTC->CRL,RTC_CRL_SECF);
restliche Arbeit erledigen
> // Toggle LED> GPIOA->ODR ^= GPIO_ODR_ODR5;> }
nachfolgendes könnte nur zusätzliche Zyklen erzeugen,
die der Interrupt-Verwaltung Zeit geben, sich zurückzusetzen
siehe Beitrag von Martin. Der Einwand mit den Verarbeitungsstufen
der CPU hat was.
> // Wait until last write operation is done> while(!READ_BIT(RTC->CRL, RTC_CRL_RTOFF)) {}> }
...
Meine Vorredner haben wohl recht, mit dem Zeitproblem.
Hier liegt das seltene Problem vor, dass im Interrupt keine Register
gesichert und wieder restauriert werden müssen, weil die Hauptschleife
leer ist, und der Compiler das wegoptimiert hat.
Lass mal irgendwas in der Hauptschleife machen, dann wird es sehr
wahrscheinlich wie gewünscht laufen.
. . schrieb im Beitrag
#5757407:
> Hier liegt das seltene Problem vor, dass im Interrupt keine Register> gesichert und wieder restauriert werden müssen, weil die Hauptschleife> leer ist, und der Compiler das wegoptimiert hat.
Das klingt plausibel. Vor allem erklärt es, warum ich vorher noch nie
auf den Fehler gestoßen bin. In den vorherigen Programmen hatten die ISR
tatsächlich etwas mehr gemacht.
> Lass mal irgendwas in der Hauptschleife machen, dann wird es sehr> wahrscheinlich wie gewünscht laufen.
Ich wüsste nicht, warum dadurch das Verhalten der ISR anders werden
sollte.
Ok, das mit dem Zeitproblem habe ich schon mal gehört. Also bei
Sammelinterrupts erst die Quelle abfragen, Flag löschen, dann was
machen.
Aber warum klappt dann die Variante aus dem Eröffnungspost mit dem IF
welches die Quelle ermittelt, aber ohne das IF geht es nicht? Obwohl
danach jeweils die "ungünstige" Reihenfolge erst LED togglen dann Flag
löschen kam...
Jörn schrieb:> Aber warum klappt dann die Variante aus dem Eröffnungspost mit dem IF> welches die Quelle ermittelt, aber ohne das IF geht es nicht?
Weil der if() Ausdruck sicher stellt, dass die LED nur getoggelt wird,
wenn eine Sekunde verstrichen ist.
Der Fehler war zwar, dass die ISR doppelt aufgerufen wurde, bis dahin
war das Sekunden-Flag aber asynchron zurück gesetzt.
Das Problem betrifft die Pipelines des Controllers, der Core ist
schneller als der Speicher und die Peripherie. Ich habe mir pauschal
angewöhnt erst die Flags zu löschen, dann etwas machen und danach ISR
beenden.
Stefanus F. schrieb:> // Wait until last write operation is done> while(!READ_BIT(RTC->CRL, RTC_CRL_RTOFF)) {}
mit dem gelernten hier würde ich das jetzt ändern, was du hättest gleich
machen können/sollen ...
// wait until interrupt flag is cleared
...
dann bist du IMMER auf der sicheren seite.
mt
Stefanus F. schrieb:> Hast du das obige Zitat aus dem Referenzhandbuch gesehen, wo genau dies> empfohlen wird?
jetzt ja, :-), das bedeutet aber für mich es gibt hier zwei topics!?
Stefanus F. schrieb:> Hast du das obige Zitat aus dem Referenzhandbuch gesehen, wo genau dies> empfohlen wird?
so, jetzt habe ich die details im reference hb nochmals gelesen und sehe
keinen zusammenhang zu deinem mehrfach interrupt problem und
Beitrag "Re: stm32f103 RTC Interrupt öfter als erwartet"
da das interrupt flag reg nicht in der rtc domain liegt.
ich würde sogar eher ein isb() cmd setzen anstatt dsb/dsm?!
probier mal aus!
ich denke, dass while() funktioniert nur weil es als "delay" wirkt.
mt
Stefanus F. schrieb:> Hast du das obige Zitat aus dem Referenzhandbuch gesehen, wo genau dies> empfohlen wird?>>> Beitrag "Re: stm32f103 RTC Interrupt öfter als erwartet"
Ich würde mir nochmal das Referenzmanual genau durchlesen. Insbesondere
18.3.4
Das Register RTC_CRH wurde hier nicht erwähnt.
Bei 8MHz reicht zo siemlich alles, was ein paar Takte verbraucht. Zum
Beispiel zwei oder mehr NOP.
1
voidRTC_IRQHandler(void)
2
{
3
// Toggle LED
4
GPIOA->ODR^=GPIO_ODR_ODR5;
5
6
// Clear interrupt flag
7
CLEAR_BIT(RTC->CRL,RTC_CRL_SECF);
8
9
// Delay a little
10
asmvolatile("NOP; NOP;");
11
}
Apollo M. schrieb:> ich würde sogar eher ein isb() cmd setzen anstatt dsb/dsm?!> probier mal aus!
isb(), dsb(), dsm() kennt der Compiler nicht, auch nicht in der
Schreibweise __isb(). Ich habe es daher mit Assembler versucht:
1
voidRTC_IRQHandler(void)
2
{
3
// Toggle LED
4
GPIOA->ODR^=GPIO_ODR_ODR5;
5
6
// Clear interrupt flag
7
CLEAR_BIT(RTC->CRL,RTC_CRL_SECF);
8
9
// Flushes the pipeline
10
asmvolatile("ISB");
11
}
Aber das klappt nur mit 8MHz Systemtakt. Bei 72MHz reicht das wieder
nicht, da muss ich doch dies machen:
1
voidRTC_IRQHandler(void)
2
{
3
// Toggle LED
4
GPIOA->ODR^=GPIO_ODR_ODR5;
5
6
// Clear interrupt flag
7
CLEAR_BIT(RTC->CRL,RTC_CRL_SECF);
8
9
// Wait until last write operation is done
10
while(!READ_BIT(RTC->CRL,RTC_CRL_RTOFF)){}
11
}
Ich denke, der Hinweis im Referenzhandbuch passt schon.
noreply@noreply.com schrieb:> Das Register RTC_CRH wurde hier nicht erwähnt.
Doch, im Eröffnungspost. In diesem Register schalte ich den
Sekunden-Interrupt ein.
Stefanus F. schrieb:> noreply@noreply.com schrieb:>> Das Register RTC_CRH wurde hier nicht erwähnt.>> Doch, im Eröffnungspost. In diesem Register schalte ich den> Sekunden-Interrupt ein.
Sorry. Ich meinte RTC_CRL
Verkürzt. RTOFF Abfragen macht nur Sinn in Verbindung der Register
RTC_PRL, RTC_CNT und RTC_ALR. RTC_CRL ist normales Register im
Addressbereich der CPU. Somit ist
// Wait until last write operation is done
while(!READ_BIT(RTC->CRL, RTC_CRL_RTOFF)) {}
nur eine Verzögerung.
18.3.4 Configuring RTC registers
To write in the RTC_PRL, RTC_CNT, RTC_ALR registers, the peripheral must
enter
Configuration Mode. This is done by setting the CNF bit in the RTC_CRL
register.
In addition, writing to any RTC register is only enabled if the previous
write operation is
finished. To enable the software to detect this situation, the RTOFF
status bit is provided in
the RTC_CR register to indicate that an update of the registers is in
progress. A new value
can be written to the RTC registers only when the RTOFF status bit value
is ’1’.
Configuration procedure
1. Poll RTOFF, wait until its value goes to ‘1’
2. Set the CNF bit to enter configuration mode
3. Write to one or more RTC registers
4. Clear the CNF bit to exit configuration mode
5. Poll RTOFF, wait until its value goes to ‘1’ to check the end of the
write operation.
The write operation only executes when the CNF bit is cleared; it takes
at least three
RTCCLK cycles to complete.
Warum pollen dann alle Tutorials RTC_CRL_RTOFF nach jedem Schreibzugriff
auf RTC>CRL?
Die Beschreibung von RTOFF sagt:
If its value is ‘0’ then it is not possible to write to any
of the RTC registers.
Also nach meinem Verständnis ist RTC->CRL ein RTC Register, sonst würde
es wohl kaum so heissen. Es ist auch Bestandteil der RTC Register Map.
Was mich ärgert: Es gibt für jeden Kack eine Application Note, nur dafür
nicht. In den Quelltexten der HAL habe ich auch nichts passenden
gefunden.
Stefanus F. schrieb:> Oder zuerst das Flag löschen, geht auch.void RTC_IRQHandler(void)> {> // Clear interrupt flag> CLEAR_BIT(RTC->CRL,RTC_CRL_SECF);>> // Toggle LED> GPIOA->ODR ^= GPIO_ODR_ODR5;> }
Krass. Braucht das Togglen wirklich so lang, dass das schon reicht?
Togglen = 4 NOPS? Ich dachte der Registerzugriff fürs Togglen dauert
einen Takt...
Jörn schrieb:> Braucht das Togglen wirklich so lang, dass das schon reicht?> Togglen = 4 NOPS? Ich dachte der Registerzugriff fürs Togglen dauert> einen Takt...
Offensichtlich ja. Mit nur 3 NOPS funktioniert es (bei 72MHz) nicht.
Das ist der Assembler-Code von der Variante, wo ich erst das Flag lösche
und dann die LED toggle:
1
188 RTC_IRQHandler:
2
189 @ args = 0, pretend = 0, frame = 0
3
190 @ frame_needed = 0, uses_anonymous_args = 0
4
191 @ link register save eliminated.
5
192 0000 0549 ldr r1, .L34
6
193 0002 064A ldr r2, .L34+4
7
194 0004 4B68 ldr r3, [r1, #4]
8
195 0006 23F00103 bic r3, r3, #1
9
196 000a 4B60 str r3, [r1, #4]
10
197 000c D368 ldr r3, [r2, #12] <---- 2 Cycles
11
198 000e 83F02003 eor r3, r3, #32 <---- 1 Cyle
12
199 0012 D360 str r3, [r2, #12] <---- 2 Cycles
13
200 0014 7047 bx lr
Ich hoffe ich habe die Anzahl der Zyklen richtig ermittelt.
> Ich dachte der Registerzugriff fürs Togglen dauert einen Takt...
Vielleicht verwechselst du das mit dem Setzen/Löschen von I/O Pins über
das GPIOx->BSRR Register.
... das hat irgendwie was esoterisches.
ich glaube das alles nicht wirklich, weil das ist mir zu
undeterministisch und damit nicht logisch.
ich werde mal irendwann meine hw in stellung bringen und rumspielen.
Apollo M. schrieb:> ... das hat irgendwie was esoterisches.> ich glaube das alles nicht wirklich, weil das ist mir zu> undeterministisch und damit nicht logisch.>> ich werde mal irendwann meine hw in stellung bringen und rumspielen.
Ich sehe da nichts undeterministisches.
Nach dem Clearen des Interrupts Flags RTC_CRL_SECF muss die ISR noch
mindestens 4 CPU Takte warten, bevor sie zurück kehrt. Andernfalls steht
das Interrupt-Signal danach noch an, so dass die ISR erneut ausgeführt
wird. Bei 8MHz genügen zwei CPU Takte.
Das Einzige, was mir dazu fehlt, ist der entsprechende Hinweis in der
Doku des Herstellers.