Forum: Mikrocontroller und Digitale Elektronik XMEGA software ATOMIC_BLOCK(ATOMIC_RESTORESTATE)


von Matthias T. (matthias_199)


Lesenswert?

Hallo alle zusammen,

Ich habe da ein kleines Problem mit meine UART, er verliert beim 
empfangen hin
und da mal Zeichen, Grund dafür ist es gibt einen einen Overflow da ich 
leider nicht manchmal schnell genug die Daten abholen kann.

Grund dafür ist das ich eine Routine mit 
ATOMIC_BLOCK(ATOMIC_RESTORESTATE), blocken musste.

Ich zeige das mal in pseudocode auf

Der RX des UART ist übriges Prio High und der Pin ISR ist Medium

1
ISR(PINx High) // JA ich weiß das stimmt nicht aber es ist 
2
{
3
   dosomething();
4
}
5
6
7
main(void)
8
{
9
   //DO many other things
10
   dosomething();
11
} 
12
13
dosomething(void)
14
{
15
     ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
16
     {
17
       //DO BLOCKED
18
     }
19
}

Der Punkt ist ich habe ein Chip der per high Interrupt sagt das sein 
FIFO voll ist und ich unbedingt jetzt gleich die Daten abholen soll.
Das macht dosomething(void).
Jedoch versuche ich in der mainloop zyklisch das FIFO zu leeren.
Da ich wenn ich es per ISR mache unter Umstände Datenverluste in kauf 
nehmen muss.
Die Idee hinter dem Block des dosomething(void) ist das die ISR nicht 
zuschlägt während dosomething(void) eigentlich gerade abgearbeitet wird.
Das kann ja theoretisch passieren.
Wenn das durch das ATOMIC_BLOCK(ATOMIC_RESTORESTATE) geblockt wird, ist 
das schlimmste was passieren kann, dass die Funktion zwei mal aufgerufen 
wird.
Das ist nicht weiter schlimm in meinem Fall.
Nur das Alle Interrupts geblockt sind wie der UART recv ist ist 
tragisch.

Besteht irgendwie sauber die Möglichkeit das dosomething(void) oder den 
code dessen zu überspringen, wenn sie schon in der Main gerade 
ausgeführt wird.
Also das wenn die ISR zuschlägt sie erkennt das dosomething(void) 
eigentlich schon im Gange ist und wieder die ISR verlassen werden kann.

MFG
Matthias

von c-hater (Gast)


Lesenswert?

Matthias T. schrieb:

Die Konstruktion ist insgesamt etwas krank, aber wenn schon, dann besser 
so:

>
1
> ISR(PINx High) // JA ich weiß das stimmt nicht aber es ist
2
> {
3
>    dosomething();
4
> }
5
> 
6
> 
7
> main(void)
8
> {
9
>    //DO many other things
10
>    DisablePinInterrupt();
11
>    dosomething();
12
>    EnablePinIntterrupt();
13
> }
14
> 
15
> dosomething(void)
16
> {
17
>        //DO BLOCKED
18
> }
19
>

von Anno (Gast)


Lesenswert?

Wie schnell kommen denn die Daten beim FIFO rein? (wieviel Zeit ist bis 
zum Überlauf?)
Wie ist die Baudrate von der USART?

>> Da ich wenn ich es per ISR mache unter Umstände Datenverluste in kauf
nehmen muss.
??? Das sollte gerade anders herum sein! Da ist was faul!

FIFO-Interupt(!) auf High-Level und hier nur die Daten abholen und in 
einen Buffer schreiben, USART-RX-INT auf Med-Level.
Eventuell die Systemfrequenz erhöhen, wenn es nicht reicht.

Du könntest auch die FIFO-Daten mit einer DMA abholen lassen!
FIFO-Int-Leitung vom Portpin ins Eventsystem schicken und DMA-Trigger
darauf einstellen.
Auch die USART-RX könntest du mit einer weiteren DMA in einen Puffer 
schreiben...

Meine Glaskugel sagt mir, dass du woanders was falsch machst.

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

1. Du machst zu viel, zu lange etwas in deinem dosomething(). Zerlege 
das in mehr Teile die problemlos bei aktiven Interrupts laufen können.

2. Der Aufruf von dosomething() im ISR macht wenig Sinn. Die 
Hauptschleife in main() erledigt das doch sowieso beim nächsten 
Durchgang durch die Schleife. Zwar mit ein paar Takten Verspätung, aber 
was soll's?
1
ISR(PINx High)
2
{
3
    queue(char) // Nur schnell empfangenes Zeichen in Puffer schreiben
4
                // Sonst nichts. Keine Berechnungen, keine Updates
5
                // anderer Werte.
6
}
7
8
main() {
9
    while(1) {
10
       int c;
11
       irq_off();
12
       c = dequeue() // Zeichen aus Buffer lesen, -1 = kein Zeichen
13
       irq_on();
14
15
       if(c >= 0) {
16
           do_something_with_char_without_blocking_irq(c);
17
      }
18
19
      //DO NOT SO MANY other things
20
21
    }
22
}

: Bearbeitet durch User
von Adam P. (adamap)


Lesenswert?

Wenn du ein "richtigen" FIFO hast, dann brauchst du kein ATOMIC_BLOCK.

Bevor jetzt alle aufschreien:

Wenn du dafür sorgst, dass deine ISR() NUR schreibend auf den FIFO 
zugreift und du in deiner restlichen Software NUR lesend auf den FIFO 
zugreifst,
dann kann da nichts passieren.

Deine ISR verändert den Schreibzeiger vom FIFO und du in deiner Software 
nur den Lesezeiger.

Falls du dieses Zugriffverhalten nicht garantieren kannst, dann wirst du 
ATOMIC_BLOCK nutzen müssen und wie bereits erwähnt:

Hannes J. schrieb:
> 1. Du machst zu viel, zu lange etwas in deinem dosomething(). Zerlege
> das in mehr Teile die problemlos bei aktiven Interrupts laufen können.

von Falk B. (falk)


Lesenswert?

Adam P. schrieb:

> Wenn du ein "richtigen" FIFO hast, dann brauchst du kein ATOMIC_BLOCK.

Stimmt nicht.

> Bevor jetzt alle aufschreien:

;-)

> Wenn du dafür sorgst, dass deine ISR() NUR schreibend auf den FIFO
> zugreift und du in deiner restlichen Software NUR lesend auf den FIFO
> zugreifst,
> dann kann da nichts passieren.

Falsch!

> Deine ISR verändert den Schreibzeiger vom FIFO und du in deiner Software
> nur den Lesezeiger.

Ist totzdem unsauber! Denn im Normalfall muss die Software vor dem 
Zugriff prüfen, ob genug Platz im FIFO ist bzw. ob überhaupt Daten drin 
sind. Dazu muss man den Datenpointer der "Gegenseite" lesen. Das muss 
atomar erfolgen -> ATOMIC BLOCK

Mit einem gescheiten FIFO geht das auch mit sehr kurzer Interruptsperre. 
Been there, done that.

https://www.mikrocontroller.net/articles/FIFO#FIFO_als_Bibliothek

von Adam P. (adamap)


Lesenswert?

Falk B. schrieb:
> Denn im Normalfall muss die Software vor dem
> Zugriff prüfen, ob genug Platz im FIFO ist bzw. ob überhaupt Daten drin
> sind. Dazu muss man den Datenpointer der "Gegenseite" lesen.

Ja und nun...kannst doch ruhig machen in der ISR.

Selbst wenn die Software gtrad Liest, dann bekomsmt halt das zurück, was 
grad Stand der Dinge ist, aber du veränderst nichts am Lesestatus.

Ich habe nie gesagt, dass es "sauber" gelöst ist, aber es kann nichts 
passieren, außer dass du halt kein Platz im FIFO hast, aber dann liegt 
das Probelm wo anders.

Falk B. schrieb:
> Mit einem gescheiten FIFO geht das auch mit sehr kurzer Interruptsperre.

Ja da hast du recht,
der FIFO muss halt richtig implementiert sein.

edit:
OK, ich hab grad überlesen dass es ja ein 8-Bit µC ist...
Alles Gut. War grad nicht ganz bei der Sache)

Sorry Falk
:-(

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Adam P. schrieb:
> Falk B. schrieb:
>> Denn im Normalfall muss die Software vor dem
>> Zugriff prüfen, ob genug Platz im FIFO ist bzw. ob überhaupt Daten drin
>> sind. Dazu muss man den Datenpointer der "Gegenseite" lesen.
>
> Ja und nun...kannst doch ruhig machen in der ISR.

Und was ist im Hauptprogramm?

> Selbst wenn die Software gtrad Liest, dann bekomsmt halt das zurück, was
> grad Stand der Dinge ist, aber du veränderst nichts am Lesestatus.

Falsch!

> Ich habe nie gesagt, dass es "sauber" gelöst ist, aber es kann nichts

Jaja, immer die Ausreden! Man kann sich auch mit einem Rasiermesser 
rasieren, ohne sich zu schneiden. Aber die meisten, vor allem Anfänger, 
können es NICHT, denn es ist NICHT allgemein sicher!

> passieren, außer dass du halt kein Platz im FIFO hast, aber dann liegt
> das Probelm wo anders.

Es geht gar nicht darum allein!

> OK, ich hab grad überlesen dass es ja ein 8-Bit µC ist...
> Alles Gut. War grad nicht ganz bei der Sache)

Auch bei einem 32 Bitter, der die meisten 32 Bit Zugriffe atomar macht, 
sollte man das so machen. Die paar us Interruptsperre schaden nicht, 
anders herum im Einzelfall schon!

Das Problem liegt im falschen Konzept des OPs, nicht in der 
Interruptsperre im Allgemeinen.

von Falk B. (falk)


Lesenswert?

Matthias T. schrieb:
> Hallo alle zusammen,
>
> Ich habe da ein kleines Problem mit meine UART, er verliert beim
> empfangen hin
> und da mal Zeichen, Grund dafür ist es gibt einen einen Overflow da ich
> leider nicht manchmal schnell genug die Daten abholen kann.

Wie hoch ist deine Baudrate? Wie sieht den Konzept im Detail aus?

> Grund dafür ist das ich eine Routine mit
> ATOMIC_BLOCK(ATOMIC_RESTORESTATE), blocken musste.

Wie lange dauert die? Was macht sie?

> Der Punkt ist ich habe ein Chip der per high Interrupt sagt das sein
> FIFO voll ist

Welcher? Wie ist der angeschlossen?

> und ich unbedingt jetzt gleich die Daten abholen soll.

Wie lange dauert das?

> Das macht dosomething(void).
> Jedoch versuche ich in der mainloop zyklisch das FIFO zu leeren.

Welches? Das Hardware-FIFO des UARTs?

> Da ich wenn ich es per ISR mache unter Umstände Datenverluste in kauf
> nehmen muss.

Glaub ich nicht.

> Die Idee hinter dem Block des dosomething(void) ist das die ISR nicht
> zuschlägt während dosomething(void) eigentlich gerade abgearbeitet wird.
> Das kann ja theoretisch passieren.

Auch praktisch.

> Wenn das durch das ATOMIC_BLOCK(ATOMIC_RESTORESTATE) geblockt wird, ist
> das schlimmste was passieren kann, dass die Funktion zwei mal aufgerufen
> wird.

Welche Funktion?

> Besteht irgendwie sauber die Möglichkeit das dosomething(void) oder den
> code dessen zu überspringen, wenn sie schon in der Main gerade
> ausgeführt wird.

Nö. Außerdem ruft man so eine Funktion nur einmal auf. Entweder in einer 
ISR oder im Hauptprogramm. Alles andere ist zu 99% ein falsches Konzept.

von Matthias T. (matthias_199)


Lesenswert?

c-hater schrieb:
>>    //DO many other things
>>    DisablePinInterrupt();
>>    dosomething();
>>    EnablePinIntterrupt();

Das scheint fürs erste mal Abhilfe zu schaffen.

Super danke, der xmega läuft auf 32mhz und das Uart läuft auf 500000 
Baud,
vielleicht ist die Baudrate einfach zu hoch gewählt, da fehlt mir etwas 
die Erfahrung leider.....

Im UART Interrupt passiert nicht viel.
es wird nur die uartReceiveInterrupt() aufgerufen, und diese zieht das 
Zeichen aus dem Uart Buffer und schreibt es in einen Ringbuffer der 
größer als 255 Zeichen sein kann.
1
void uartReceiveInterrupt(UartBuffer *buffer)
2
{
3
    if (buffer == NULL)
4
    {
5
        return;
6
    }
7
8
    uint8_t temp_data = buffer->usart_hardware_adress_->DATA;
9
    uint8_t temp_status = buffer->usart_hardware_adress_->STATUS & (UART_PARITY_ERROR | UART_BUFFER_OVERFLOW | UART_FRMAE_ERROR);
10
    uint16_t rRX_BUFFER_SIZE = buffer->rx_buffer_size_;
11
12
    buffer->status_ = buffer->status_ | temp_status ;
13
14
    uint16_t temp_head;
15
16
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
17
    {
18
        temp_head =  buffer->rx_head_;
19
    }
20
    if( ( temp_head + 1 == buffer->rx_tail_ ) || ( buffer->rx_tail_ == 0 && temp_head + 1 == rRX_BUFFER_SIZE) )
21
    {
22
        buffer->rx_buffer_overflow_ = true;
23
    }
24
    else
25
    {
26
        buffer->rx_buffer_[temp_head] = temp_data;
27
        temp_head++;
28
29
        if (temp_head >= rRX_BUFFER_SIZE)
30
            temp_head = 0;
31
32
        ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
33
        {
34
            buffer->rx_head_ = temp_head;
35
        }
36
    }
37
}

MFG
Matthias

von Peter D. (peda)


Lesenswert?

Adam P. schrieb:
> Wenn du ein "richtigen" FIFO hast, dann brauchst du kein ATOMIC_BLOCK.

Man kann ohne Atomic auskommen, wenn die FIFO max 255 Byte groß ist.
Es ist aber überhaupt kein Problem 2Byte-Zugriffe atomar zu kapseln. Man 
darf nur nicht komplette Funktionen kapseln, sondern immer nur den 
relevaten Zugriff (~8 Zyklen Sperre).

Atomic im Interrupt ist natürlich Unsinn, das Main kann keine Interrupts 
unterbrechen. Nur die Main-Zugriffe benötigen Atomic.

: Bearbeitet durch User
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.