Forum: Compiler & IDEs Soft UART und Hardware UART gleichzeitiges empfangen


von Marcel (Gast)


Lesenswert?

Hallo zusammen,
ich habe eine wie ich finde besondere Herausforderung an meinen µC.

Ich habe ein Gerät1, dass alle 20 Sekunden einen String über COM (8N1) 
ausgibt und welcher dann von meinem µC eingelesen und gespeichert wird.

Auf der anderen Seite meines µC steht ebenfalls ein Gerät2, was bei 
Knopfdruck am Gerät2 einen String an meinen µC schickt.

Wenn der String von Gerät2 kommt, so wird dieser eingelesen und darauf 
hin werden Teile von dem von Gerät1 gespeicherten String gesendet. (Je 
nach Art des Befehls)

Meine Software UART geht über das INT0 Ereignis und nutzt dann einen 
8-Bit Timer.
Die Hardware-UART habe ich über das ISR(USART_RX_vect) implementiert.

Mein Programm funktioniert tadellos.
Die Ausnahme ist, wenn beide Geräte gleichzeitig einen String an den µC 
senden. Habt ihr irgendwelche Tipps wie ich nen optimales Timing machen 
könnte, damit mein µC gleichzeitig zwei Strings empfangen kann?

Merci beaucoup :-)
Grüße Marcel

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

> Habt ihr irgendwelche Tipps wie ich nen optimales Timing machen
> könnte, damit mein µC gleichzeitig zwei Strings empfangen kann?
Das wait() in Zeile 42 sollte raus...

Im Ernst: zeig doch mal deinen Quellcode. Vermutlich brauchst du zuviel 
Zeit beim Bearbeiten der Interrupts...

von Gast (Gast)


Lesenswert?

>Habt ihr irgendwelche Tipps wie ich nen optimales Timing machen
>könnte, damit mein µC gleichzeitig zwei Strings empfangen kann?

Ja, das Wort 'gleichzeitig' streichen.
Der PC schickt abwechselnd Anfragen an G1 und G2; nur das angefragte Gx 
darf mit seinem Wert antworten.

von Karl H. (kbuchegg)


Lesenswert?

Höchst wahrscheinloich hast du Grundregel 1 verletzt:
In einem Interrupt wird auf nichts und niemanden gewartet.
Auch nicht auf einen Timer.

von Peter D. (peda)


Lesenswert?


von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Karl heinz Buchegger schrieb:
> Höchst wahrscheinlich hast du Grundregel 1 verletzt:
> In einem Interrupt wird auf nichts und niemanden gewartet.
Und vermutlich auch Grundregel 2:
Ein Interrupt hat schnellstmöglich abgearbeitet zu sein.
Verwaltungsaufgaben und Datenverarbeitung werden woanders gemacht.

von Marcel (Gast)


Lesenswert?

Wow vielen Dank für die supi schnellen Antworten :-).

Das stimmt, das hatte ich noch gar nicht bedacht, dass wenn man sich in 
einem Interrupt befindet, der µC wartet bis dieses beendet wurde und 
dann ins nächste springt.

Das Problem ist, dass die beiden Geräte nicht auf die Befehle meines µC 
hören, sondern Gerät1 spricht dauerhaft alle 20 Sekunden und Gerät2 
bringt den Befehl raus (vielleicht 1x in der Stunde) und wartet 1,5 
Sekunden lang auf eine Antwort.

Ich hab mir noch was anderes überlegt. Wenn ich Gerät2 an meine 
Soft-UART lege und wenn dann beim Empfang von Zeichen von Gerät2 (sprich 
fallenede Flanke an INT0) sofort die Hardware-UART deaktiviert wird und 
ich Gerät2 dann einfach die Werte gebe, die noch vor 20 Sekunden aktuell 
waren. So kommt wennigstens kein Müll heraus.
Das setzt aber auch vorraus, dass ich die von Gerät1 empfangenen Strings 
immer 2x im µC deponiere.

Der erste Zeichen-Array wird von Gerät1 immer wieder fleißig 
überschrieben und wenn dann gerade zum Empfangszeitpunkt von Gerät1 eben 
Gerät2 seinen Befehl schickt, wird die Zeichenaufnahme von Gerät1 
gestoppt (dann ist ja der String von Gerät1 nicht vollstädig übertragen 
worden.).

Dann habe ich ja noch den "alten" String den ich dann schicke.
Das kopieren der Strings kann ich ja zu einem unkritischen Zeitpunkt 
machen. Damit mir da auch nichts wieder bei flöten geht, deaktiveire ich 
zu diesem Zeitpunkt die Hardware-UART und wenn dann gerade von Gerät2 
nen String kommt (Soft-UART), dann wird der erst in nen FIFO geschoben 
und anschließend verarbeitet.
Somit müssten alle zeitkritischen Punkte abgefedert sein und seht ihr 
noch einen?

Das wäre doch ne Möglichkeit. - oder habt ihr ne bessere?

Danke
Marcel

von (prx) A. K. (prx)


Lesenswert?

Die Hardware-UART hat mindestens 2 Bytes Zeit, die Soft-UART ist viel 
viel zeitkritischer. Wenn der Handler der Hardware-UART länger als ein 
paar Befehle ist, dann dort sofort den UART-Interrupt abschalten und die 
Interrupts wieder freigeben. Dann kommt der kritischere Soft-Int wieder 
durch (aber jeder andere auch, Vorsicht Stack-Grösse).

Annahme dabei: Soft-UART via externem Interrupt für das Rx-Startbit und 
via Timer-Interrupt für den Rest.

von Marcel (Gast)


Lesenswert?

Genau so hab ich auch meine Soft-UART programmiert. ;-)

Von der Interruptlänge - sind die zu lange?:
PS: Indem UART Receive Interrupt wird eine Funktion aufgerufen, die 
wartet, bis das Register ausgelsen werden kann (---  while ( !(UCSR0A & 
(1<<RXC0)) )----)

Danke im Vorraus
Marcel

FALLENDE FLANKE AN INT0:


ISR(INT0_vect)
{

  sparkline_sending++;

  //Disable Hardware Uart if nothing received at the moment
  if(meter_sending==0)
  {
  //Disable Hardware UART
  UCSR0B &= ~(1<<RXEN0);
  }

  state = RECEIVE;                  // Change state
  DISABLE_EXTERNAL0_INTERRUPT( );   // Disable interrupt during the data 
bits.

  DISABLE_TIMER_INTERRUPT( );       // Disable timer to change its 
registers.
  TCCR_P &= ~( 1 << CS01 );         // Reset prescaler counter.
  TCCR_P &= ~( 1 << CS00 );

  TCNT0 = INTERRUPT_EXEC_CYCL;      // Clear counter register. Include 
time to run interrupt rutine.

  TCCR_P |= ( 1 << CS01 );          // Start prescaler clock.
  TCCR_P |= ( 1 << CS00 );

  OCR = TICKS2WAITONE_HALF;         // Count one and a half period into 
the future.

  SwUartRXBitCount = 0;             // Clear received bit counter.
  CLEAR_TIMER_INTERRUPT( );         // Clear interrupt bits
  ENABLE_TIMER_INTERRUPT( );        // Enable timer0 interrupt on again

}


ISR(INT0_vect)
{

  sparkline_sending++;

  //Disable Hardware Uart if nothing received at the moment
  if(meter_sending==0)
  {
  //Disable Hardware UART
  UCSR0B &= ~(1<<RXEN0);
  }

  state = RECEIVE;                  // Change state
  DISABLE_EXTERNAL0_INTERRUPT( );   // Disable interrupt during the data 
bits.

  DISABLE_TIMER_INTERRUPT( );       // Disable timer to change its 
registers.
  TCCR_P &= ~( 1 << CS01 );         // Reset prescaler counter.
  TCCR_P &= ~( 1 << CS00 );

  TCNT0 = INTERRUPT_EXEC_CYCL;      // Clear counter register. Include 
time to run interrupt rutine.

  TCCR_P |= ( 1 << CS01 );          // Start prescaler clock.
  TCCR_P |= ( 1 << CS00 );

  OCR = TICKS2WAITONE_HALF;         // Count one and a half period into 
the future.

  SwUartRXBitCount = 0;             // Clear received bit counter.
  CLEAR_TIMER_INTERRUPT( );         // Clear interrupt bits
  ENABLE_TIMER_INTERRUPT( );        // Enable timer0 interrupt on again

}



TIMER INTERRUPT

ISR(TIMER0_COMPA_vect)
{

  switch (state) {
  // Transmit Byte.
  case TRANSMIT:
    // Output the TX buffer.
    if( SwUartTXBitCount < 8 ) {
      if( SwUartTXData & 0x01 ) {           // If the LSB of the TX 
buffer is 1:
        SET_TX_PIN();                       // Send a logic 1 on the 
TX_PIN.
      }
      else {                                // Otherwise:
        CLEAR_TX_PIN();                     // Send a logic 0 on the 
TX_PIN.
      }
      SwUartTXData = SwUartTXData >> 1;     // Bitshift the TX buffer 
and
      SwUartTXBitCount++;                   // increment TX bit counter.
    }

    //Send stop bit.
    else {
      SET_TX_PIN();                         // Output a logic 1.
      state = TRANSMIT_STOP_BIT;
    }
  break;

  // Go to idle after stop bit was sent.
  case TRANSMIT_STOP_BIT:
    DISABLE_TIMER_INTERRUPT( );           // Stop the timer interrupts.
    state = IDLE;                         // Go back to idle.
    ENABLE_EXTERNAL0_INTERRUPT( );        // Enable reception again.
  break;

  //Receive Byte.
  case RECEIVE:
    OCR = TICKS2WAITONE;                  // Count one period after the 
falling edge is trigged.
    //Receiving, LSB first.
    if( SwUartRXBitCount < 8 ) {
        SwUartRX        ENABLE_EXTERNAL0_INTERRUPT( );      // Enable 
interrupt to receive more bytes.
    }
  break;

  // Unknown state.
  default:
    state = IDLE;                           // Error, should not occur. 
Going to a safe state.
  }
}




HARDWARE-UART RECEIVE

ISR(USART_RX_vect)
{
//Es werden Zeichen auf der Hardware UART empfangen
meter_buffer[counter_meter]=USART_Recieve();
counter_meter++;
}

von Stefan E. (sternst)


Lesenswert?

> PS: Indem UART Receive Interrupt wird eine Funktion aufgerufen, die
> wartet, bis das Register ausgelsen werden kann (---  while ( !(UCSR0A &
> (1<<RXC0)) )----)

Der Interrupt wird ja aber genau dann ausgelöst, wenn ein Zeichen 
empfangen wurde (das Register kann also auf jeden Fall sofort ausgelesen 
werden). Und RXC wird beim Eintritt in die ISR automatisch gelöscht, das 
heißt, deine Schleife wartet dann also, bis noch ein Zeichen empfangen 
wurde.

von (prx) A. K. (prx)


Lesenswert?

Die Hard-UART abzuschalten heisst, dass du schon verdammt sicher sein 
solltest, dass in der fraglichen Zeit nix kommt. Wobei du sie dann auch 
nicht abschalten musst ;-).

Denn wenn grad was reinkommt während du wieder einschaltest, dann gibt 
es Schrott.

Wenn schon, dann den Int abschalten, nicht die ganze Unit. Gibt hier 
aber keinen Grund, im Ext-Int überhaupt was von der UART abzuschalten.

Den Timer-Int in der ISR anfangs abzuschalten bringt nix, weil in einer 
ISR sowieso alle Interrupts unbearbeitet liegen bleiben.

Cut-and-Paste-Fehler im Timer-Int?

Ein echter Funktionsaufruf in kleiner ISR ist vergleichsweise teuer, 
weil der Compiler dann nicht nur diejenigen Regs sichert, die er selber 
wirklich verwendet, sondern auch alle von der Funktion prinziell 
verwendbaren Regs.

von Marcel (Gast)


Lesenswert?

Ne in der Timer INT hab ich keine Copy Paste Fehler drinn - was ist dir 
denn an der Routine aufgefallen?

LG
Marcel

von Karl H. (kbuchegg)


Lesenswert?

zb, dass du die Hardware UART dort drinnen abschaltest

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.