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