Hi,
ich wollte bei einem ATTIny 4313 vor dem sleep_mode() sicherstellen,
dass die Datenübertragung des USART fertig ist.
Hierzu hatte ich folgende Funktion geschrieben:
Diese Funktion wird im Hauptprogramm vor dem Schlafmodus aufgerufen:
1
if(huart_tx_empty()){
2
_delay_ms(1);
3
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
4
sleep_mode();
5
};
Leider funktioniert das nur, wenn ich den oben erwähnten _delay_ms(1)
mache. Ansonsten kommen beim Empfänger unsinnige Zeichen an. Wie kann
man denn nun darauf warten, dass wirklich die Übertragung fertig ist?
LG Matthias
Matthias T. schrieb:> Ansonsten kommen beim Empfänger unsinnige Zeichen an.
zusätzlicher oder statt der erwarteten Daten?
Was kommt genau an, und was wird erwartet?
Es wird alle 4 sec. eine Zahl abgeschlossen mit CR LF gesendet.
Beim Delay kommt die auch gut an, ohne das delay kommt im Terminal nur
Zeichensalat (=offenbar Frameerror) an.
Die komplette Routine ist:
Der UARt Sender des AVR hat einen zweistufigen Buffer. Erste Stufe ist
das ausgehenden UDR Register (hat das eigentlich einen eindeutigen
Namen?). Dessen Status fragst du ab.
Dahinter kommt ein Schieberegister, dass die Bits nacheinander über den
TxD Pin raus schiebt. Dieses kontrollierst du nicht.
Wenn du ohne Delay in den Sleep Modus gehst, brichst du das Senden
dieses letzten Bytes ab.
Der Delay ist also definitiv nötig. Wie lange er wirklich sein muss,
kannst du ja mal selbst anhand der Baudrate ausrechnen.
Stefan U. schrieb:> Der Delay ist also definitiv nötig
in der Doku steht aber:
Bit 6 – TXC: USART Transmit Complete
This flag bit is set when the entire frame in the Transmit Shift
Register has been shifted out a
außerdem würde es nur das letzte Zeichen betreffen, was defekt ist. Wenn
aber mehre falsche Zeichen ankommen muss woanders noch ein fehler sein.
@Matthias Taube
Lass das Programm doch mal im Simulator laufen.
Heinz schrieb:> Ich hab leider nur das Datenblatt von einem ATMega, aber bei dem ist> UDRIE auch im UCSRA, nicht UCSRB
Nein, beim ATTINY 4313 ist das im UCSRB.
Ich habe inzwischen vor dem sleep dne USART explizit ausgeschaltet und
danach wieder ein, aber das hilft auch nichts.
Matthias T. schrieb:> Nein, beim ATTINY 4313 ist das im UCSRB.> Ich habe inzwischen vor dem sleep dne USART explizit ausgeschaltet und> danach wieder ein, aber das hilft auch nichts.
ich bin auch der Meinung es müsste gehen. Ich habe auch Atmels wo ich
das brauche weil ich damit von senden auf empfangen bei RS485 umschalte
und ein sleep habe ich dort nicht verwendet.
Wie schon oben geschrieben, darf dadurch aber nicht die komplette
Übertragung gestört werden, maximal das letzte Zeichen. Deswegen vermute
ich den Fehler woanders. Aber dafür gibt es ja den Simulator, bei den
kurzen code ist das doch einfach möglich.
Nachtrag:
Da du kein Interupt hast, muss du das TXC flag auch noch löschen, sonst
bleibt es ja auf "1".
> or it can be cleared by writing a one to its bit location.
Das TXC-Bit ist ein Flag-Bit.
Das heißt, wenn irgendwann mal Transmit Shift Register und
Transmit Buffer (UDR) leer geworden sind, ist es gesetzt.
Und es bleibt gesetzt, wenn kein "transmit complete interrupt"
ausgeführt wurde.
The TXC flag bit is automatically cleared when a transmit
complete interrupt is executed, or it can be cleared by
writing a one to its bit location.
Abhilfe:
Wenn du mit dem UDRE-Interrupt arbeitest, musst du vor dem
Senden jeder Zahl mit CR, LF das TXC-Bit löschen.
Sonst musst du vor jedem zu sendenden Byte das TXC-Bit löschen.
- Dann zeigt das TXC-Bit den AKTUELLEN Zustand.
Ansonsten, wenn das Programm eh nicht viel zu tun hat, nimm
halt den passenden (!) Delay-Aufruf...
Jakob schrieb:> Wenn du mit dem UDRE-Interrupt arbeitest, musst du vor dem> Senden jeder Zahl mit CR, LF das TXC-Bit löschen.> Sonst musst du vor jedem zu sendenden Byte das TXC-Bit löschen.> - Dann zeigt das TXC-Bit den AKTUELLEN Zustand.
nein. Es muss es nur einmal auf 0 setzen, wenn er es abgefragt hat und
es auf 1 steht.
Da niemand Deine huart_xxx Funktionen kennt, ist wohl Hellsehen nötig.
Das TXC muß irgendwie gelöscht werden, sonst bleibt es auf ewig gesetzt.
Z.B. direkt nach dem Schreiben auf UDR.
Hier mal ein Beispiel für die UART als SPI:
1
voidmax7221_out(uint8_tval)
2
{
3
while((UCSR1A&1<<UDRE1)==0);
4
UDR1=val;
5
}
6
7
voidmax7221_last_out(uint8_tval)
8
{
9
while((UCSR1A&1<<UDRE1)==0);
10
ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
11
UDR1=val;
12
UCSR1A=1<<TXC1;// clear tx complete flag
13
}
14
while((UCSR1A&1<<TXC1)==0);// until tx completed
15
}
Man beachte die atomare Kapselung, damit nicht die Daten schon gesendet
wurden, weil ein langer Interrupt dazwischen kommt.
Auch kann ja bei jedem vorherigen Byte durch einen langen Interrupt der
Puffer leer gelaufen sein, d.h. TXC gesetzt werden.
Nur Löschen nach dem Test ist also nicht sicher.
> Bit 6 – TXC: USART Transmit Complete> This flag bit is set when the entire frame in the Transmit Shift> Register has been shifted out a
Das ist eindeutig. Dann hatte ich es falsch in Erinnerung.
Jakob schrieb:> Sonst musst du vor jedem zu sendenden Byte das TXC-Bit löschen.> - Dann zeigt das TXC-Bit den AKTUELLEN Zustand.
Vielen Dank. Ich lösche nun das TXC-Bit beim Schreiben auf das
Datenregister und damit funktioniert alles wie es soll.
Es ist ein wenig verwirrend, dass man das Bit durch schreiben von 1 auf
den Zustand 0 setzt, aber das wird schon seinen Grund haben.
LG
Matthias
Matthias T. schrieb:> Es ist ein wenig verwirrend, dass man das Bit durch schreiben von 1 auf> den Zustand 0 setzt, aber das wird schon seinen Grund haben.
Das ist ein sog. "Strobe-Bit". Das findest du in vieler Hardware.
Dir fehlt halt einfach nur die Erfahrung und die Hardware-Kenntnisse...
Wäre es anders, würde dir die Sache völlig logisch erscheinen...
Die Sache beruht darauf, das die Hardware weiss, ob es sich um einen
Schreib- oder Lesezugriff handelt. Beim Lesezugriff wird der Status des
Ausgangs eines Flipflops geliefert, beim Schreibzugriff hingegen für
einen Takt das übergebene Signal auf den Reset-Eingang desselben
Flipflops ausgegen.
Beide Zugriffe erfolgen über das gleiche Registerbit, hantieren mit
demselben Flipflop, betreffen aber eben nicht das gleiche Signal dieses
Flipflops.
Man hätte statt dessen natürlich auch ein TCXRST-Bit in irgendeinem
zusätzlichen IO-Register definieren können. Aber wozu? Hätte nur den
Aufwand für die Hardware erhöht...
c-hater schrieb:> Man hätte statt dessen natürlich auch ein TCXRST-Bit in irgendeinem> zusätzlichen IO-Register definieren können.
Man hätte es auch einfach logisch machen können, wie es z.B. beim 8051
der Fall ist. D.h. 0 setzen löscht das Bit, 1 setzen setzt es. Man kann
daher beim 8051 Interrupts auch per Befehl auslösen. Ich hab das auch
schonmal benötigt.
Peter D. schrieb:> Man hätte es auch einfach logisch machen können, wie es z.B. beim 8051> der Fall ist. D.h. 0 setzen löscht das Bit, 1 setzen setzt es. Man kann> daher beim 8051 Interrupts auch per Befehl auslösen. Ich hab das auch> schonmal benötigt.
Das ist natürlich auch möglich, erfordert aber signifikant zusätzliche
Logik. Der Schreibzugriff muss dann nämlich abhängig vom Pegel entweder
auf RESET oder SET des FF gerouted werden. Das ist in synchroner Logik
in einem Takt nur mit relativ hohem Zusatzaufwand zu schaffen.
Und: es könnte blöde Nebeneffekte geben. Ungewolltes Abwürgen eines noch
nicht bearbeiteten IRQ z.B... Das könnte man nur dadurch vermeiden, die
Zahl der Hardwareregister massiv zu erhöhen. Im Prinzip wäre dann wohl
ein Register pro Strobe-Bit fällig.