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
> 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...
>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.
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.
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
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.
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++;
}
> 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.
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.