Hallo Forum! Ich arbeite mit WinAVR auf einem ATmega8 und realisiere gerade eine MIDI-Anwendung. Über einen Sendepuffer und den UART möchte ich meine Messages versenden. Leider sendet der UART nichts, wenn ich ihn nutze. Das ist mein Puffer: BYTE UART_tx_buffer[16] = {0}; //UART Sendepuffer Hier hab ich mir noch einen "Descriptor" dazu gebaut: struct buffer_desc { volatile BYTE head:4; volatile BYTE tail:4; } RX_desc = {0},TX_desc = {0}; So beschreibe ich den Puffer: void UART_tx(BYTE data) { cli(); UART_tx_buffer[TX_desc.head++] = data; //Wert in den Puffer schreiben if(TX_desc.head == TX_desc.tail + 1) //Erster Wert im Puffer? UCSRB |= (1<<TXCIE); //Enable TX-INT sei(); } Und hier die Int-Routine: SIGNAL(SIG_UART_TRANS) { UDR = UART_tx_buffer[TX_desc.tail++]; if(TX_desc.head == TX_desc.tail) //Puffer leer? UCSRB &= ~(1<<TXCIE); //Disable TX-INT } Wenn ich die Standard-Sendefunktion (warten auf das UCSRA, ohne Puffer und INT) verwende funktioniert alles wunderbar. Fällt euch was auf? Hab ich nen Denkfehler? Grüße Laplace
Ich hab das noch nie gemacht, aber was mir auffällt: Wozu schaltest du den TX Interrupt aus und nicht wieder ein? Der Interrupt wird nur dann ausgelöst wenn das Zeichen gesendet wurde, dann sende einfach das nächste Zeichen und gut is.
Sobald der letzt Wert im Puffer gesendet wurde (head == tail) wird der TX-INT deaktiviert. (Wenn es nichts zu senden gibt, interessiert es ja nicht, ob er wieder bereit ist) Wenn ich jetzt ein neues Zeichen in den Puffer schreibe (head == tail + 1) wird der TX-Int wieder aktiviert. Jetzt sollte wieder ein TX-INT ausgelöst werden, weil der UART ja nichts zu senden hat. Er bleibt so lange aktiviert, bis der Puffer leer ist. Allerdings bin ich mir nicht sicher, ob allgemein bei den AVRs auch dann ein TX-INT ausgelöst wird, wenn schon länger nichts zum senden ansteht und das Transmit Compete Interrupt Enable Bit gesetzt wird. Weiß das jemand?
Der Interrupt löst erst dann wieder aus, wenn du ihm ein Zeichen zum senden gegeben hast und er damit fertig ist. Also müsstest du in UART_tx das erste Zeichen manuell ins UDR schieben, der Rest läuft dann wieder im Interrupt.
Das scheint auch nicht das Problem zu sein. Ich erzeuge jetzt den ersten Interrupt manuell aber er sendet trotzdem nicht: void UART_tx(BYTE data) { cli(); UART_tx_buffer[TX_desc.head++] = data; //Wert in den Puffer schreiben if(TX_desc.head == TX_desc.tail + 1) //Erster Wert im Puffer? { UCSRB |= (1<<TXCIE); //Enable TX-INT UCSRA |= (1<<TXC); //Ersten Interrupt manuell erzeugen } sei(); }
UCSRA |= (1<<TXC); //Ersten Interrupt manuell erzeugen Das funktioniert nicht. Damit löschst du das Flag. Dieser Interrupt lässt sich nicht ,,manuell'' erzeugen. Für deine Vorgehensweise empfiehlt es sich, den UDRE-Interrupt stattdessen zu benutzen.
Hast recht, hab ich auch grad gelesen...:] Ich werd morgen mal versuchen, das ganze mit dem UDRE-INT zu realisieren.
Ein kleiner Hinweis:
1 | void UART_tx(BYTE data) |
2 | {
|
3 | cli(); |
4 | UART_tx_buffer[TX_desc.head++] = data; //Wert in den Puffer |
5 | schreiben
|
6 | if(TX_desc.head == TX_desc.tail + 1) //Erster Wert im Puffer? |
7 | {
|
8 | UCSRB |= (1<<TXCIE); //Enable TX-INT |
9 | UCSRA |= (1<<TXC); //Ersten Interrupt manuell erzeugen |
10 | }
|
11 | sei(); |
12 | }
|
Damit versaust Du Dir übrigens die Möglichkeit, 'UART_tx' aus einer Funktion aufzurufen, die bereits das globale Interruptflag gelöscht hat. Besser so:
1 | void UART_tx(BYTE data) |
2 | {
|
3 | unsigned char sreg_buf = SREG; |
4 | cli(); |
5 | |
6 | UART_tx_buffer[TX_desc.head++] = data; //Wert in den Puffer |
7 | schreiben
|
8 | if(TX_desc.head == TX_desc.tail + 1) //Erster Wert im Puffer? |
9 | {
|
10 | UCSRB |= (1<<TXCIE); //Enable TX-INT |
11 | UCSRA |= (1<<TXC); //Ersten Interrupt manuell erzeugen |
12 | }
|
13 | |
14 | SREG = sreg_buf; |
15 | }
|
Damit wird das globale Interruptfalg nur wieder gesetzt, wenn es vor dem 'cli' schon gesetzt war.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.