Private Website von Niklas Menke

Elektrotechnik und mehr...

Netzfrequenz: - Hz
-

Netzfrequenz


(Etwa alle 30s wird ein neuer Messwert ermittelt. Dieses Diagramm gibt nicht den exakten Verlauf der Netzfrequenz wieder.)


Vorwort

Einige Mikrocontroller verfügen heutzutage über keine in Hardware ausgeführte UART-Schnittstelle mehr. Stattdessen wird eine sogenannte "USI" bereitgestellt. Das Kürzel steht für "Universal Serial Interface", also "Universelle serielle Schnittstelle". Mit einer USI können unterschiedliche serielle Schnittstellen aufgebaut werden. Hauptsächlich genannt sind SPI, I2C oder eben UART.

Auf dieser Seite präsentiere ich nun meine kleine Bibliothek, welche eine UART-Schnittelle auf USI-Basis bereitstellt.

Detailliertere Informationen stellt Microchip mit einer Application Note und einem Beispielcode selber bereit.

Wichtige Informationen:
1) Die Codierung 8N2 (8 Datenbits, Keine Parität, 2 Stoppbits) ist fest eingestellt.
2) Über die USI kann nur ein Halbduplex UART aufgebaut werden. Es ist also kein paralleles Senden und Empfangen von Daten möglich.
3) Der nachfolgende Code wurde nur mit einem ATtiny861A bei einer CPU-Frequenz von 8MHz getestet! Bei anderen Bedingungen ist ggf. eine Anpassung des Codes notwendig.

Funktionen

Meine geschriebene Bibliothek stellt folgende Funktionen bereit. Die einzelnen Funktionen werden im weiteren Verlauf dieser Seite genauer erklärt.

  1. uart_listen: Aktiviert den "Pin change interrupt" des DI-Pins. So können Bytes empfangen werden.
  2. uart_stop: Deaktiviert den "Pin change interrupt" des DI-Pins. Es werden also keine Bytes empfangen.
  3. uart_getBaudrate: Fragt den Logiklevel zweier IO-Pins ab und gibt so die eingestellte Baudrate zurück.
  4. uart_TimerConfig: Stellt den Sollwert und den Prescaler des Timers in Abhängigkeit von der Baudrate und der CPU-Frequenz ein.
  5. uart_receive: Startet den Timer und das USI zum einlesen eines eingehenden Bytes.
  6. uart_transmit: Sendet ein Byte.
  7. uart_transmitArray: Sendet ein Array von Bytes.
  8. uart_available: Gibt die Anzahl der empfangenden Bytes, welche noch im Puffer liegen, zurück.
  9. uart_readByte: Gibt das erste Byte aus dem Puffer zurück, löscht es aber nicht daraus.
  10. uart_getByte: Gibt das erste Byte aus dem Puffer zurück und löscht es daraus.
  11. uart_delete: Leert den Puffer.
  12. rev_byte: Dreht die Reihenfolge der Bits eines Bytes.
Globale Variablen

Zum Betrieb des UART werden ein paar globale Variablen deklariert/definiert:

uart_listen

Diese Funktion konfiguriert den DI und DO Pin als Eingänge und setzt sie auf einen H-Pegel. Der DO Pin wird ebenfalls als Eingang definiert, da er sonst mit dem Schieberegister der USI verbunden wäre und so den Zustand der letzten Stelle des Schieberegisters annehmen würde. So wäre der DO Pin beim empfangen eines Bytes nicht dauerhaft auf einem H-Pegel.

uart_stop

Diese Funktion deaktiviert den "pin change interrupt" des DI Pins, sodass keine Bytes mehr empfangen werden. Des Weiteren wird dafür gesorgt, dass der DI und DO Pin garantiert einen H-Pegel (Den Ruhepegel) haben.

uart_getBaudrate

Diese Funktion fragt den Logikpegel von zwei IO Pins (Hier: PA6 und PA7) ab und gibt die so eingestellte Baudrate zurück. Vorgesehen ist 9600, 19200 und 38400.

uart_TimerConfig

Die Dauer eines Bits ist der Kehrwert der Baudrate. Die Dauer bis der der Timer den Sollwert n erreicht, ergibt sich aus der CPU-Frequenz und dem Prescaler. So erhält man folgende Gleichung:

Formel 1

Der maximale Sollwert des Timers beträgt 255. Da der berechnete Sollwert n nachher immer aufgerundet werden soll, darf dieser maximal 254 betragen. Stellt man nun die Formel nach PRESCALER um, dann ergibt sich:

Formel 2

Diese Formel liefert den Wert, den der Prescaler mindestens haben muss. Die if-Bedingung wählt den nächst höheren Prescaler aus.
Zum Schluss wird der notwendige Sollwert berechnet, indem man die erste Formel nach n umstellt. Da der berechnete Sollwert auf jeden Fall aufgerundet werden soll, wird der berechnete Wert noch um 1 erhöht:

Formel 3
uart_receive

Diese Funktion wird von dem "Pin change interrupt" des DI Pins aufgerufen. Dadurch wird der Timer gestartet und die USI passend konfiguriert.

Wurde ein volles Byte empfangen, dann wird der "USI overflow interrupt" ausgelöst.

uart_transmit

Diese Funktion setzt die richtige Konfiguration der Pins, parametriert die USI und schaltet den Timer ein.

Ein gesamte Übertragung besteht aus 11 Bits (1x Startbit, 8x Datenbits, 2x Stoppbits). Mit dieser Funktion werden erst das Startbit und die ersten sieben Bits des zu sendenden Bytes übertragen.

Wurde der erste Teil übertragen, dann wird der "USI overflow interrupt" ausgelöst.

uart_transmitArray

Hierbei handelt es um eine einfache for-Schleife, die für jedes Byte die "uart_transmit"-Funktion aufruft:

  1. void uart_transmitArray(const uint8_t *byte, uint8_t length) {
  2. for(uint8_t i = 0; i < length; i++) uart_transmit(byte[i]);
  3. }
uart_available

Diese Funktion gibt nur den Wert der globalen Variable "data_avail_rx" zurück:

  1. uint8_t uart_available(void) {
  2. return data_avail_rx;
  3. }
uart_readByte

Diese Funktion gibt das erste Element aus dem Puffer zurück. Allerdings bleibt das Byte im Puffer stehen. Sollten keine Bytes verfügbar sein, dann wird einfach eine Null zurück gegeben.

  1. uint8_t uart_readByte(void) {
  2. return data_rx[0];
  3. }
uart_getByte

Diese Funktion gibt das erste Element aus dem Puffer zurück. Dabei wird das abgefragte Byte aus dem Puffer gelöscht, indem alle anderen Bytes um Eins nach vorne geschoben werden. Sollte kein Byte verfügbar sein, dann wird einfach eine Null zurückgegeben.

uart_delete

Diese Funktion setzt die Anzahl der im Puffer verfügbaren Bytes auf Null zurück. So sind alle gespeicherten Bytes nicht mehr über "uart_readByte", bzw. "uart_getByte" abrufbar.
Ein neu eingehendes Byte wird nun wieder vom Index 0 ausgehend im Puffer gespeichert.

  1. void uart_delete(void) {
  2. data_avail_rx = 0;
  3. }
rev_byte

Da alle Bytes mit dem LSB zuerst empfangen und gesendet werden, muss die Reihenfolge der Bits gedreht werden. Diese Aufgabe übernimmt diese Funktion:

  1. uint8_t rev_byte(uint8_t rbyte) {
  2. rbyte = ((rbyte >> 1) & 0x55) | ((rbyte << 1) & 0xaa);
  3. rbyte = ((rbyte >> 2) & 0x33) | ((rbyte << 2) & 0xcc);
  4. rbyte = ((rbyte >> 4) & 0x0f) | ((rbyte << 4) & 0xf0);
  5. return rbyte;
  6. }
USI overflow interrupt

Diese ISR (Interrupt Service Routine) wird aufgerufen, wenn

  1. Ein volles Byte (8 Bit) empfangen wurde.
  2. Der erste oder der zweite Teil eines Bytes (1x Startbit, 8x Datenbits, 8x Stoppbits) gesendet wurde.

Wurde ein Byte empfangen, dann wird der Timer und die USI abgeschaltet und das empfangende Byte in den Puffer geschrieben. Danach ist der Controller bereit, ein weiteres Byte zu empfangen.

Wurde der erste Teil eines Bytes (Startbit und die sieben ersten Datenbits) gesendet, dann wird das letzte Datenbit in das Schieberegister der USI geschrieben. Der Rest wird mit Einsen aufgefüllt.
Der Zählwert des 4-Bit-Zählers wird auf 13 gesetzt. So werden drei Zyklen durchlaufen, bis der Zähler überläuft. Dadurch wird das letzte Datenbit und zwei Stoppbits gesendet.

Wurde der zweite Teil eines Bytes (Letztes Datenbit und zwei Stoppbits) gesendet, dann wird der Timer und die USI abgeschaltet und der Rx-Modus wieder aktiviert, falls er vorher auch aktiviert war.

Beispiel

Dieser Abschnitt ist noch nicht Verfügbar.

Downloads
Creative Commons Lizenzvertrag
Dieses Werk von Niklas Menke ist lizenziert unter einer Creative Commons Namensnennung 4.0 International Lizenz.

Header-Datei und Programmcode (zip)

Diese Website verwendet zur Darstellung einiger Inhalte externe Dienste und Cookies.

Nachfolgend können Sie die einzelnen Dienste ein- bzw. ausschalten.
Diese Einstellungen können jederzeit im Footer der Website angepasst werden.

Technisch notwendig (Cookie-Präferenzen, Design)
Font Awesome (Symbole)
Google Fonts (Schriftart)
Google reCAPTCHA (Formularschutz)
Sketchfab (3D-Inhalte)

Weitere Informationen finden Sie in der Datenschutzerklärung.

Datenschutzerklärung