Forum: Mikrocontroller und Digitale Elektronik stm32f103 RTC Interrupt öfter als erwartet


von Stefan F. (Gast)


Lesenswert?

Es geht um folgendes Programm auf einem Nucleo-64 Board mit STM32F103RB 
(ohne HAL):
1
#include "stm32f1xx.h"
2
3
void initRtc()
4
{
5
    // Enable the backup domain
6
    SET_BIT(RCC->APB1ENR, RCC_APB1ENR_BKPEN + RCC_APB1ENR_PWREN);
7
8
    // Enable write access to the backup domain
9
    SET_BIT(PWR->CR, PWR_CR_DBP);
10
11
    // Enable LSE oscillator
12
    SET_BIT(RCC->BDCR, RCC_BDCR_LSEON);
13
14
    // Wait until LSE oscillator is ready
15
    while(!READ_BIT(RCC->BDCR, RCC_BDCR_LSERDY)) {}
16
17
    // Select LSE as clock source for the RTC
18
    MODIFY_REG(RCC->BDCR, RCC_BDCR_RTCSEL, RCC_BDCR_RTCSEL_LSE);
19
20
    // Enable the RTC
21
    SET_BIT(RCC->BDCR, RCC_BDCR_RTCEN);
22
23
    // Wait until RTC is synchronized
24
    while(!READ_BIT(RTC->CRL, RTC_CRL_RSF)) {}
25
26
    // Wait until last write operation is done
27
    while(!READ_BIT(RTC->CRL, RTC_CRL_RTOFF)) {}
28
29
    // Enable second interrupt
30
    SET_BIT(RTC->CRH,RTC_CRH_SECIE);
31
32
    // Wait until last write operation is done
33
    while(!READ_BIT(RTC->CRL, RTC_CRL_RTOFF)) {}
34
35
    // Enter configuration mode
36
    SET_BIT(RTC->CRL,RTC_CRL_CNF);
37
38
    // Divide oscillator frequency by 32767+1 to get seconds
39
    RTC->PRLL=32767;
40
    RTC->PRLH=0;
41
42
    // Leave configuration mode
43
    CLEAR_BIT(RTC->CRL,RTC_CRL_CNF);
44
45
    // Wait until last write operation is done
46
    while(!READ_BIT(RTC->CRL, RTC_CRL_RTOFF)) {}
47
48
    // Enable interrupt in NVIC
49
    NVIC_EnableIRQ(3);
50
}
51
52
void init_io()
53
{
54
    // Enable Port A
55
    SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPAEN);
56
57
    // PA5 = Output
58
    MODIFY_REG(GPIOA->CRL, GPIO_CRL_CNF5 + GPIO_CRL_MODE5, GPIO_CRL_MODE5_0);
59
}
60
61
void RTC_IRQHandler(void)
62
{
63
    // If a second has elapsed, then ...
64
    if (READ_BIT(RTC->CRL,RTC_CRL_SECF))
65
    {
66
        // Toggle LED
67
        GPIOA->ODR ^= GPIO_ODR_ODR5;
68
69
        // Clear interrupt flag
70
        CLEAR_BIT(RTC->CRL,RTC_CRL_SECF);
71
    }
72
}
73
74
int main(void)
75
{
76
    init_io();
77
    initRtc();
78
    while(1){};
79
}

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.

von Jörn (Gast)


Lesenswert?

RTC_IRQHandler() scheint ein Sammelinterrupt zu sein. Mit deiner Zeile 
fragst du das Sekundenflag ab. Ist aber jetzt nur so geraten...

von Stefan F. (Gast)


Lesenswert?

Auch mit dieser Zeile:
1
if (READ_BIT(RTC->CRL,RTC_CRL_SECF) || READ_BIT(RTC->CRL,RTC_CRL_ALRF) || READ_BIT(RTC->CRL,RTC_CRL_OWF))
blinkt die LED schön im Sekundentakt. Ich verstehe das nicht, es gibt 
doch nur diese drei Quellen für den IRQ, oder nicht?

von Jörn (Gast)


Lesenswert?

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...

von Stefan F. (Gast)


Lesenswert?

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>.

von Stefan F. (Gast)


Lesenswert?

Auch wenn ich alle drei Flags lösche, wird der Interrupt doppelt 
aufgerufen:
1
void RTC_IRQHandler(void)
2
{
3
    // Toggle LED
4
    GPIOA->ODR ^= GPIO_ODR_ODR5;
5
6
    // Clear interrupt flags
7
    CLEAR_BIT(RTC->CRL,RTC_CRL_SECF + RTC_CRL_ALRF + RTC_CRL_OWF);
8
}

Es scheint noch eine vierte Quelle/Ursache für die Unterbrechungen zu 
geben.

von noreply@noreply.com (Gast)


Lesenswert?

Ich würde die Anzahl Interrupts mal vor dem if in einer globalen 
Variable zählen und ausgeben.

von . . (Gast)


Lesenswert?

@Stefanus F.
Kannst Du mal das nicht funktionierende Binary oder Hexfile hochladen, 
ich könnte es mir mal anschauen.

von Stefan F. (Gast)


Lesenswert?

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:
1
void RTC_IRQHandler(void)
2
{
3
    static volatile uint8_t cnt=0;
4
    cnt++;
5
6
    // If the Interrupt came from the RTC
7
    if (READ_BIT(RTC->CRL,RTC_CRL_SECF + RTC_CRL_ALRF + RTC_CRL_OWF))
8
    {
9
        // Toggle LED
10
        GPIOA->ODR ^= GPIO_ODR_ODR5;
11
12
        // Clear interrupt flags
13
        CLEAR_BIT(RTC->CRL,RTC_CRL_SECF + RTC_CRL_ALRF + RTC_CRL_OWF);
14
    }
15
}

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.

von Stefan F. (Gast)


Angehängte Dateien:

Lesenswert?

.                                                . schrieb im Beitrag 
#5757293:
> Kannst Du mal das nicht funktionierende Binary oder Hexfile hochladen,
> ich könnte es mir mal anschauen.

Gerne.

von noreply@noreply.com (Gast)


Lesenswert?

Mach mal erst Clear Interrupt und dann Toggle.

von Stefan F. (Gast)


Lesenswert?

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?

von noreply@noreply.com (Gast)


Lesenswert?

Danke.

Nach intensiver Studium des VHDL-Designs, ... war nur Spaß.

Ich schätzte, das irgendwo Race-Conditions existieren.

von Stefan F. (Gast)


Lesenswert?

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.

von Stefan F. (Gast)


Lesenswert?

Jetzt wird es interessant:

Das läuft gut:
1
void RTC_IRQHandler(void)
2
{
3
    // Toggle LED
4
    GPIOA->ODR ^= GPIO_ODR_ODR5;
5
6
    // Clear interrupt flags
7
    CLEAR_BIT(RTC->CRL,RTC_CRL_SECF);
8
9
    // Toggle something else
10
    GPIOA->ODR ^= GPIO_ODR_ODR4;
11
}

Das läuft gut:
1
void RTC_IRQHandler(void)
2
{
3
    // Toggle something else
4
    GPIOA->ODR ^= GPIO_ODR_ODR4;    
5
6
    // Clear interrupt flags
7
    CLEAR_BIT(RTC->CRL,RTC_CRL_SECF);
8
9
    // Toggle LED
10
    GPIOA->ODR ^= GPIO_ODR_ODR5;
11
}

Das läuft gut:
1
void RTC_IRQHandler(void)
2
{
3
    // Clear interrupt flags
4
    CLEAR_BIT(RTC->CRL,RTC_CRL_SECF);
5
6
    // Toggle LED
7
    GPIOA->ODR ^= GPIO_ODR_ODR5;
8
}

Das funktioniert nicht (doppel-Interrupts):
1
void RTC_IRQHandler(void)
2
{
3
    // Toggle LED
4
    GPIOA->ODR ^= GPIO_ODR_ODR5;
5
6
    // Toggle something else
7
    GPIOA->ODR ^= GPIO_ODR_ODR4;
8
9
    // Clear interrupt flags
10
    CLEAR_BIT(RTC->CRL,RTC_CRL_SECF);
11
}

Das funktioniert nicht (doppel-Interrupts):
1
void RTC_IRQHandler(void)
2
{
3
    // Toggle LED
4
    GPIOA->ODR ^= GPIO_ODR_ODR5;
5
6
    // Toggle something else
7
    GPIOA->ODR ^= GPIO_ODR_ODR4;
8
9
    // Clear interrupt flags
10
    CLEAR_BIT(RTC->CRL,RTC_CRL_SECF);
11
}

Dann hat es in meinem Kopf "klick" gemacht. Die vermutlich saubere 
Lösung ist:
1
void RTC_IRQHandler(void)
2
{
3
    // Toggle LED
4
    GPIOA->ODR ^= GPIO_ODR_ODR5;
5
6
    // Clear interrupt flags
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
}

von noreply@noreply.com (Gast)


Lesenswert?

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.

von Stefan F. (Gast)


Lesenswert?

noreply@noreply.com schrieb:
> Interrupt-Grund Abfragen
> Interrupt-Grund Zurücksetzen
> restliche Arbeit erledigen

Meinst du so?:
1
void RTC_IRQHandler(void)
2
{
3
    // if a second has elapsed, then ...
4
    if (READ_BIT(RTC->CRL,RTC_CRL_SECF))
5
    {
6
        // Clear interrupt flag
7
        CLEAR_BIT(RTC->CRL,RTC_CRL_SECF);
8
9
        // Toggle LED
10
        GPIOA->ODR ^= GPIO_ODR_ODR5;
11
    }
12
13
    // Wait until last write operation is done
14
    while(!READ_BIT(RTC->CRL, RTC_CRL_RTOFF)) {}
15
}

von Stefan F. (Gast)


Lesenswert?

Ich gehe jetzt duschen.

Falls wir uns nicht mehr sehen: Vielen Dank für eure Anregungen zur 
Fehleranalyse. Hat mir sehr geholfen.

von Martin (Gast)


Lesenswert?

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.

von noreply@noreply.com (Gast)


Lesenswert?

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.

von noreply@noreply.com (Gast)


Lesenswert?

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)) {}
> }

...

von . . (Gast)


Lesenswert?

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.

von Stefan F. (Gast)


Lesenswert?

.                                                . 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.

von Jörn (Gast)


Lesenswert?

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...

von Stefan F. (Gast)


Lesenswert?

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.

von neuer PIC Freund (Gast)


Lesenswert?

Vielleicht 18.3.4@RM0008
1
In addition, writing to any RTC register is only enabled if the previous write operation is
2
finished. To enable the software to detect this situation, the RTOFF status bit is provided in
3
the RTC_CR register to indicate that an update of the registers is in progress. A new value
4
can be written to the RTC registers only when the RTOFF status bit value is ’1’.

Also mit
1
1. Poll RTOFF, wait until its value goes to ‘1’
2
...
verfahren?

von Ingo Less (Gast)


Lesenswert?

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.

von Ingo Less (Gast)


Lesenswert?

Das lässt sich übrigens auch mit Timer-Ints ganz easy reproduzieren. Ich 
habe ewig gebraucht um das zu finden...

von Jan (Gast)


Lesenswert?

Es gab genau dazu auch mal einen Thread hier. Ich suche mal.

von Jan (Gast)


Lesenswert?

Mist, nicht angemeldet..

hier ein paar Threads ;) Es gibt noch mehr.

Beitrag "STM32: Timer Interrupt: Löschen des Pending Flags"
Beitrag "Re: STM32: Timer-ISR löst 2x aus - Fehler im Flag-Reset bei Optimierung O3"
Beitrag "STM32F4: EXTI-Interrupt-Flag löschen" <- kurz aber hilfreich, man 
könnte memory barriers benutzen

von Stefan F. (Gast)


Lesenswert?

SO langsam kommt Licht ins Dunkel. Ihr seid echt gut!

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

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

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

.. das thema ist interessant, da es auch im context mit interrupts/dma 
steht und wie hier schon erwähnt sind dsb/dmb die keywords

einer von vielen links dazu,
https://stackoverflow.com/questions/15491751/real-life-use-cases-of-barriers-dsb-dmb-isb-in-arm

ein while() contruct ist dann wohl eher die "ahnunglose" variante das 
problem zu beherrschen.


mt

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Apollo M. schrieb:
> ein while() contruct ist dann wohl eher die "ahnunglose" variante das
> problem zu beherrschen.

Hast du das obige Zitat aus dem Referenzhandbuch gesehen, wo genau dies 
empfohlen wird?

> Beitrag "Re: stm32f103 RTC Interrupt öfter als erwartet"

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

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!?

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

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

: Bearbeitet durch User
von noreply@noreply.com (Gast)


Lesenswert?

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.

von Stefan F. (Gast)


Lesenswert?

Bei 8MHz reicht zo siemlich alles, was ein paar Takte verbraucht. Zum 
Beispiel zwei oder mehr NOP.
1
void RTC_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
    asm volatile ("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
void RTC_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
    asm volatile ("ISB");
11
}

Aber das klappt nur mit 8MHz Systemtakt. Bei 72MHz reicht das wieder 
nicht, da muss ich doch dies machen:
1
void RTC_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.

von Stefan F. (Gast)


Lesenswert?

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.

von noreply@noreply.com (Gast)


Lesenswert?

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.

von Stefan F. (Gast)


Lesenswert?

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.

Beitrag #5758602 wurde von einem Moderator gelöscht.
von Stefan F. (Gast)


Lesenswert?

Bei 72MHz brauche ich vier NOPs.
1
void RTC_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
    asm volatile ("NOP; NOP; NOP; NOP;");
10
}

Oder zuerst das Flag löschen, geht auch.
1
void RTC_IRQHandler(void)
2
{
3
    // Clear interrupt flag
4
    CLEAR_BIT(RTC->CRL,RTC_CRL_SECF);
5
6
    // Toggle LED
7
    GPIOA->ODR ^= GPIO_ODR_ODR5;
8
}

Ich mache das jetzt so und schreibt noch einen kleinen Kommentar dazu, 
dass nach den Clear ein Paar Takte Verzögerung nötig sind.

von Jörn (Gast)


Lesenswert?

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...

von Stefan F. (Gast)


Lesenswert?

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.

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

... 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.

von Stefan F. (Gast)


Lesenswert?

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.

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.