
Tiny3216 USART, TX und RX
Elektronik-Labor Projekte AVR

TinyAVR Serie 0 und 1 im Einsatz
Weil ich gerade an einer PicoBasic- und TestLab-Version
für den Tiny3216 arbeite, musste ich mal wieder tiefer in die Hardware
einsteigen. Im ersten Ansatz war alles auf dem Arduino-Framework
aufgebaut. Um meine Firmware etwas schneller zu machen, wollte ich an
einigen Stellen auf natives C wechseln. Hier ging es speziell um die
serielle Datenübertragung. Die Arduino-Funktionen Serial.begin,
Serial.available(), Serial.read, Serial.parseInt und
Serial.println sollten ersetzt werden.
Zuerst habe ich in meinem TinyAVR-Buch nachgeschaut, was zu dem Thema
schon vorhanden war. Aber leider wurden da immer nur Daten gesendet,
aber nie empfangen. Also musste ich noch mal etwas nachrüsten.
#define USART0_BAUD_RATE(BAUD_RATE) ((float)(F_CPU * 64 /(16 * (float)BAUD_RATE)) + 0.5)
void USART0_init(void){
PORTB.DIRSET = 0x04; //TXD an PB2
PORTA.DIRCLR = 0x08; //RXD an PB3
USART0.BAUD = (uint16_t)USART0_BAUD_RATE(1000000);
USART0.CTRLB |= USART_RXEN_bm | USART_TXEN_bm;
}
Tatsächlich kann man extreme Baudraten bis 1 MBaud verwenden. Bei der
Initialisierung musste ich in USART0.CTRLB zusätzlich das Empfangsbit
RXEN setzen, weil bisher nur TXEN gesetzt war.
void USART0_sendChar(char c){
while (!(USART0.STATUS & USART_DREIF_bm));
USART0.TXDATAL = c;
}
void USART0_sendString(char *str){
for(size_t i = 0; i < strlen(str); i++){
USART0_sendChar(str[i]);
}
}
Die beiden Funktionen zum Senden eines Zeichens und eines Textes gab es
ja schon. Aber Serial.println kann mehr, nämlich auch Zahlen in
Zeichenketten umwandeln und senden. Für mein Projekt brauche ich nur
Bytes in ASCII umwandeln und senden. Dazu habe ich die Funktion
USART0_printByte geschrieben. Ein Byte kann als Dezimalzahl ein-, zwei-
oder dreistellig sein. Ich wollte führende Nullen unterdrücken. Zur
Unterscheidung der Fälle wird abgefragt, ob die Zahl größer als 99 bzw.
größer als 9 ist. Um eine dezimale Ziffer in ASCII umzuwandeln, wird 48
addiert. Am Ende wird noch 13 (CR) und 10 (LF) gesendet.
void USART0_printByte(uint8_t n){
if (n>99){
while (!(USART0.STATUS & USART_DREIF_bm));
USART0.TXDATAL = 48+n/100;
n=n-100*(n/100);
while (!(USART0.STATUS & USART_DREIF_bm));
USART0.TXDATAL = 48+n/10;
n=n-10*(n/10);
while (!(USART0.STATUS & USART_DREIF_bm));
USART0.TXDATAL = 48+n;
}
else{
if (n>9){
while (!(USART0.STATUS & USART_DREIF_bm));
USART0.TXDATAL = 48+n/10;
n=n-10*(n/10);
}
while (!(USART0.STATUS & USART_DREIF_bm));
USART0.TXDATAL = 48+n;
}
while (!(USART0.STATUS & USART_DREIF_bm));
USART0.TXDATAL = 13;
while (!(USART0.STATUS & USART_DREIF_bm));
USART0.TXDATAL = 10;
}
Ein einzelnes Zeichen wird mit USART0_readChar empfangen. Allerdings
blockiert diese Funktion des Ablauf, bis tatsächlich ein Zeichen
eingetroffen ist. Um das zu verhindern, hatte ich mit
Serial.available() abgefragt, ob ein Zeichen empfangen wurde. Nur dann
sollte es auch abgeholt werden. Mit demselben Effekt frage ich jetzt
direkt das RXCIF-Bit im USART-Statusregister ab.
if (USART0.STATUS & USART_RXCIF_bm){
…
}
char USART0_readChar(void){
while (!(USART0.STATUS & USART_RXCIF_bm));
return USART0.RXDATAL;
}
Die Funktion USART0_flush
kann verwendet werden, um irgendwelchen Datenmüll zu löschen. Wenn ich z.B.
nicht sicher bin, ob ein Programm nur CR oder CR + LF sendet, kann ich ein
eventuelles LF löschen, damit es im weiteren Verlauf nicht stört.
void USART0_flush(void){
char c;
delay(1);
if (USART0.STATUS & USART_RXCIF_bm){c=USART0.RXDATAL;}
if (USART0.STATUS & USART_RXCIF_bm){c=USART0.RXDATAL;}
if (USART0.STATUS & USART_RXCIF_bm){c=USART0.RXDATAL;}
}
In meinem Projekt werden nicht nur Einzelzeichen empfangen, sondern
auch Dezimalzahlen in der Größe Byte oder Word. Dazu wurde die Funktion
USART0_readInt geschrieben. Sie reagiert nur auf die ASCII-Zeichen 48
bis 57 (0…9) und fügt sie zu einer Zahl zusammen. Das Ende der
Übertragung wird an einem CR (ASCII 13) erkannt. Die Funktion kann also
alles zwischen einstelligen Zahlen (0, 1, 2…) bis zu fünfstelligen
Zahlen (…65535) empfangen, also auch Bytes im Bereich 0 bis 255.
uint16_t USART0_readInt(void){
char c;
uint16_t n=0;
while (!(USART0.STATUS & USART_RXCIF_bm));
c= USART0.RXDATAL;
while (c > 13){
if (c>47 && c<58){
n=n*10;
n+=c-48;
}
while (!(USART0.STATUS & USART_RXCIF_bm));
c= USART0.RXDATAL;
}
return n;
}
Elektronik-Labor Projekte AVR