Hallo zusammen,
ich habe gerade ein Problem mit dem Uart vom Atmega128 (avr-gcc). Und
zwar habe ich ein Programm geschrieben, das einen 256 byte langen TX und
einen 256 byte langen RX Puffer verwendet.
Damit das Senden halbwegs im Hintergrund passiert, soll jedes mal, wenn
das Senderegister leer ist, im Interrupt ein neues Byte aus dem Puffer
hineingeschrieben werden.
Um nun beim aller ersten Byte, das gesendet werden soll, einen Interrupt
zu generieren, habe ich gedacht, ich kann einfach das Interruptflag
setzen und der Interrupt wird dann angesprungen. Das ist aber nicht so.
Offenbar kann man das TXC0 Bit in UCSR0A nicht setzen.
Ich habe schon einiges probiert, z.B.
cli();
UCSR0A = (1<<TXC0); //Set TxComplete Interrupt flag
led_green(1);
//if ((UCSR0A & 0x40) == 0x40) led_yellow(1); //Check if TXC0 is set
sei();
Man würde hier erwarten, dass das bit gesetzt wird und zumindest gesetzt
bleibt, bis die Interrupts freigegeben werden. Ist aber nicht so.
led_yellow(1) wird nicht ausgeführt.
Das Problem ist, dass der Interrupt auch nicht angesprungen wird.
Das Assembler listing habe ich schon angeschaut und meiner Meinung nach
verhält sich der Compiler korrekt.
1
300 0172 80E4 ldi r24,lo8(64)
2
301 0174 8BB9 out 43-0x20,r24
3
302 .LM42:
4
303 0176 81E0 ldi r24,lo8(1)
5
304 0178 0E94 0000 call led_green
6
305 .LVL9:
7
306 .LM43:
8
307 017c 5E9B sbis 43-0x20,6
9
308 017e 00C0 rjmp .L38
10
309 0180 81E0 ldi r24,lo8(1)
11
310 0182 0E94 0000 call led_yellow
Ich habe testweise alle Interrupts definiert, aber keiner wird
angesprungen.
Was mache ich denn da falsch?
Das angehängte Programm sollte einfach immer Test\n senden, es passiert
aber nichts.
Ich muss dazusagen, dass der Puffercode auf einem MSP430 läuft, das ist
also nur ein Thema der Interrupts beim Atmega128.
Grüße,
Peter
>Offenbar kann man das TXC0 Bit in UCSR0A nicht setzen.
Genaueres sollte man in Datenblatt finden. Manche Register/Flags sind
nur lesbar.
Sobald du ein Zeichen in den Puffer geschoben hast, solltest du das
UDRIE setzen. Dann springt der Controller von alleine in die ISR.
Da fragst du dann ab, ob der Puffer leer ist, und setzt es zurück.
Das TXC-Flag braucht man eigentlich nur bei Übertragungen, die
halbduplex sind, wie RS485-2wire, wo sich mehrere Sender und Empfänger
die Datenübertragungsleitung teilen.
Für die Datenübertragung musst du die UDR-ISR nehmen.
Hi,
das Datenblatt sagt:
Writing this bit to one enables interrupt on the TXCn flag. A USARTn
Transmit Complete interrupt will be generated only if the TXCIEn bit is
written to one, the global interrupt flag in SREG is written to one and
the TXCn bit in UCSRnA is set.
Also der Interrupt wird nur ausgelöst wenn das TXCIEn bit UND im SREG
die Interrupts global aktiviert sind (sei) UND das TXCn bit gesetzt ist.
Grüße,
Michael
@Michael:
Genau das habe ich gemacht (siehe initialisierungsroutine des uart
USART0_Init).
Uart konfiguriert, TXCIE0 gesetzt
und dann per Software TXC0 gesetzt.
Aber der Interrupt wird nicht angesprungen. Das Bit lässt sich nicht mal
schreiben, laut Datenblatt ist es aber schreibbar. Ich habe sogar extra
das ganze Register gesetzt und nicht nur das Bit, genau so wie es im
Datenblatt geschrieben ist (dass man alle anderen Bits auf 0 setzen
soll, wenn man auf UCSR0A zugreift).
Mit dem Data Register empty Flag ist es ganz genauso, nur mit dem
Unterschied, dass sogar im Datenblatt steht, dass man es nicht in
Software setzen kann.
Wie löse ich also in Software einer der folgenden Interrupts aus:
TX Complete Interrupt
USART Data Register Empty Interrupt
?
Grüße,
Peter
Hallo,
Du kannst ein Interruptflag nicht setzen, das wird nur von der
jeweiligen Hardware gesetzt. Mit Schreiben einer 1 in die Position wird
ein gesetztes Interruptflag gelöscht, beim Schreiben einer 0 passiert
garnichts.
Gruß aus Berlin
Michael
@ STK500-Besitzer:
Jetzt verstehe ich, was du meinst. Das Data register empty Flag ist ja
automatisch beim Reset gesetzt...
Ich probier das mal eben, wie du es beschrieben hast.
Peter
Hallo Ralf,
ich häng das einfach nochmal als kompletten Quellcode an.
Wichtig ist scheinbar, dass man das Bit TXCIE0 mit dem Befehl cbi
zurücksetzt. Ich hatte das vorher in C reinundiert in das Register
UCSR0B, nur das funktioniert nicht, weil es ein Read-Modify-Write war.
Ich hab das jetzt einfach unkompliziert mit einem Stück Inlineassembler
erledigt:
asm volatile ("cbi 10, 5"); //clear bit UDRIE0 in register UCSR0B
Wenn jemand zufällig noch weiß, wie man im Inlineassembler unkompliziert
die Registernamen und Bitnamen anstatt der Adressen verwenden kann,
würde ich mich über einen kurzen Tip freuen. Ich habe im Moment nur
nicht die Zeit, das noch zu bereinigen, wichtig ist gerade nur, dass es
so läuft wie es soll.
Grüße,
Peter