Die Zeile macht in der Anwendung nur dann Sinn, wenn hinten ein
Semikolon steht:
while (!(UCSRA & (1<<UDRE)));
Damit wird die (leere) Schleife so lange ausgeführt, wie die Bedingung
in der Klammer wahr ist, also so lange das Bit UDRE im UCSRA nicht
gesetzt ist.
En Détail: Das Ausrufezeichen ist eine logische (boolesche) Negation.
Der dahinter stehende Ausdruck wird also negiert (ist er 0, dann ist
das Eregbnis 1, andernfalls ist es 0). (UCSRA & (1<<UDRE)) ist eine
bitweise UND-Verknüpfung des Registers UCSRA mit einer um UDRE Stellen
nach links verschobenen binären 1 (in 8 Bit also 00000001). UDRE ist
ein in der Headerdatei definiertes Makro, das die Bitnummer im Register
einsetzt. Hab grad net im Kopf, welche Nummer das UDRE hat, aber mal
angenommen, es ist das Bit Nr. 3 im UCSRA, also wird die binäre 1
(00000001) um 3 Stellen nach links verschoben (gibt also 00001000). Das
Ding wird jetzt mit UCSRA verUNDet. Wenn das Bit Nr. 3 im UCSRA gesetzt
ist, ist der Ausdruck (UCSRA & (1<<UDRE)) 1, also logisch wahr. Durch
das "!" wird er negiert, der gesamte Inhalt der Klammer wird also in
dem Fall "0", also logisch falsch, so dass die Schleife abgebrochen
wird, sobald das UDRE-Bit im UCSRA gesetzt ist.
Die Schleife dient dazu, zu warten, bis nach dem Schreiben von Daten in
das UART-Datenregister UDR diese Daten übertragen wurden, also das UDR
leer ist. In dem Fall (also nach abgeschlossener Übertragung) wird das
UDRE gesetzt und die UART zeigt so an, dass sie bereit ist, neue Daten
aufzunehmen. Das Semikolon (alternativ ein paar {}) ist dabei elementar
wichtig, da es eine leere Schleife sein soll.
Hoffe, das war jetzt nicht zu viel Information...