avr ersterer schritt
  16-Bit Register (ADC, ICR1, OCR1x, TCNT1, UBRR)
 
16-Bit Register (ADC, ICR1, OCR1x, TCNT1, UBRR)

Einige der Portregister in den AVR-Controllern sind 16 Bit breit. Im Datenblatt sind diese Register üblicherweise mit dem Suffix "L" (LSB) und "H" (MSB) versehen. Die avr-libc definiert zusätzlich die meisten dieser Variablen die Bezeichnung ohne "L" oder "H". Auf diese kann direkt zugewiesen bzw. zugegriffen werden. Die Konvertierung von 16-bit Wort nach 2*8-bit Byte erfolgt intern.

#include <avr/io.h>
#include <stdint.h>
...
uint16_t foo;
 
foo=ADC; /* setzt die Wort-Variable foo auf den Wert der letzten AD-Wandlung */

Falls benötigt, kann eine 16-Bit Variable auch recht einfach manuell in ihre zwei 8-Bit Bestandteile zerlegt werden. Folgendes Beispiel demonstriert dies anhand des pseudo- 16-Bit Registers UBRR.

/* Diese Variante ist normal am effizientesten */
#include <avr/io.h>
#include <stdint.h>
#ifndef F_CPU
#define F_CPU 3686400
#endif
#define UART_BAUD_RATE 9600
 
typedef union {
        uint16_t i16;
        struct {
                uint8_t i8l;
                uint8_t i8h;
        };
} convert16to8;
 
...
convert16to8 baud;
baud.i16 = F_CPU / (UART_BAUD_RATE * 16L) -1;
UBRRH = baud.i8h;
UBRRL = baud.i8l;
...
 
/* Alternative 1:*/
#include <inttypes.h>
 
uint16_t wFoo16;
uint8_t bFooLow, bFooHigh;
 
...
 
wFoo16   = 0xAA55;                 /* zu "zerlegende" 16Bit-Integer */
bFooHigh = (uint8_t)(wFoo16 >> 8); /* MS-Byte */
bFooLow  = (uint8_t)(wFoo16);      /* LS-Byte */
 
/* Alternative 2:*/
 
#define us0(Data) (*((unsigned char *)(&Data)))
#define us1(Data) (*((unsigned char *)((&Data)+1)))
bFooHigh = us1(wFoo16);
bFoolow  = us0(wFoo16);
...

Bei einigen AVR-Typen (z. B. ATmega8, ATMega16) teilen sich UBRRH und UCSRC die gleiche Memory-Adresse. Damit der AVR trotzdem zwischen den beiden Registern unterscheiden kann, bestimmt das Bit7 (URSEL) welches Register tatsächlich beschrieben werden soll. 1000 0011 (0x83) adressiert demnach UCSRC und übergibt den Wert 3 und 0000 0011 (0x3) adressiert UBRRH und übergibt ebenfalls den Wert 3.

Speziell bei den 16-Bit-Timern und auch beim ADC ist es bei allen Zugriffen auf Datenregister erforderlich, dass diese Daten synchronisiert sind. Wenn z. B. bei einem 16-Bit-Timer das High-Byte des Zählregisters gelesen wurde und vor dem Lesezugriff auf das Low-Byte ein Überlauf des Low-Bytes stattfindet, erhält man einen völlig unsinnigen Wert. Auch die Compare-Register müssen synchron geschrieben werden, da es ansonsten zu unerwünschten Compare-Ereignissen kommen kann.

Beim ADC besteht das Problem darin, dass zwischen den Zugriffen auf die beiden Teilregister eine Wandlung beendet werden kann und der ADC ein neues Ergebnis in ADCL und ADCH schreiben will, wodurch High- und Low-Byte nicht zusammenpassen.

Um diese Datenmüllproduktion zu verhindern, gibt es in beiden Fällen eine Synchronisation, die jeweils durch den Zugriff auf das Low-Byte ausgelöst wird:

  • Bei den Timer-Registern (das gilt für alle TCNT-, OCR- und ICR-Register bei den 16-Bit-Timern) wird bei einem Lesezugriff auf das Low-Byte automatisch das High-Byte in ein temporäres Register, das ansonsten nach außen nicht sichtbar ist, geschoben. Greift man nun anschließend auf das High-Byte zu, dann wird eben dieses temporäre Register gelesen.
  • Bei einem Schreibzugriff auf eines der genannten Register wird das High-Byte in besagtem temporären Register zwischengespeichert und erst beim Schreiben des Low-Bytes werden beide gleichzeitig in das eigentliche Register übernommen.

Das bedeutet für die Reihenfolge beim Lesezugriff: Erst Low-Byte, dann High-Byte und für den Schreibzugriff: Erst High-Byte, dann Low-Byte.

Des weiteren ist zu beachten, dass es für all diese 16-Bit-Register nur ein einziges temporäres Register gibt, so dass das Auftreten eines Interrupts, in dessen Handler ein solches Register manipuliert wird, bei einem durch ihn unterbrochenen Zugriff i.d.R. zu Datenmüll führt. 16-Bit-Zugriffe sind generell nicht atomar! Wenn mit Interrupts gearbeitet wird, kann es erforderlich sein, vor einem solchen Zugriff auf ein 16-Bit-Register die Interrupt-Bearbeitung zu deaktivieren.

Beim ADC-Datenregister ADCH/ADCL ist die Synchronisierung anders gelöst. Hier wird beim Lesezugriff (ADCH/ADCL sind logischerweise Read-only) auf das Low-Byte ADCL beide Teilregister für Zugriffe seitens des ADC so lange gesperrt, bis das High-Byte ADCH ausgelesen wurde. Dadurch kann der ADC nach einem Zugriff auf ADCL keinen neuen Wert in ADCH/ADCL ablegen, bis ADCH gelesen wurde. Ergebnisse von Wandlungen, die zwischen einem Zugriff auf ADCL und ADCH beendet werden, gehen verloren!

Nach einem Zugriff auf ADCL muss grundsätzlich ADCH gelesen werden!

In beiden Fällen (also sowohl bei den Timern als auch beim ADC) werden vom C-Compiler 16-Bit-Pseudo-Register zur Verfügung gestellt (z. B. TCNT1H/TCNT1L -> TCNT1, ADCH/ADCL -> ADC bzw. ADCW), bei deren Verwendung der Compiler automatisch die richtige Zugriffsreihenfolge regelt. In C-Programmen sollten grundsätzlich diese 16-Bit-Register verwendet werden. Sollte trotzdem ein Zugriff auf ein Teilregister erforderlich sein, sind obige Angaben zu berücksichtigen.

Es ist darauf zu achten, dass auch ein Zugriff auf die 16-Bit-Register vom Compiler in zwei 8-Bit-Zugriffe aufgeteilt wird und dementsprechend genauso nicht-atomar ist wie die Einzelzugriffe. Auch hier gilt, dass u.U. die Interrupt-Bearbeitung gesperrt werden muss, um Datenmüll zu vermeiden.

Beim ADC gibt es für den Fall, dass eine Auflösung von 8 Bit ausreicht, die Möglichkeit, das Ergebnis "linksbündig" in ADCH/ADCL auszurichten, so dass die relevanten 8 MSB in ADCH stehen. In diesem Fall muss bzw. sollte nur ADCH ausgelesen werden.

ADC und ADCW sind unterschiedliche Bezeichner für das selbe Registerpaar. Üblicherweise kann man in C-Programmen ADC verwenden, was analog zu den anderen 16-Bit-Registern benannt ist. ADCW (ADC Word) existiert nur deshalb, weil die Headerdateien auch für Assembler vorgesehen sind und es bereits einen Assembler-Befehl namens adc gibt.


Im Umgang mit 16-Bit Registern siehe auch:

  • Dokumentation der avr-libc Abschnitt Related Pages/Frequently Asked Questions/Nr. 8
  • Datenblatt Abschnitt Accessing 16-bit Registers
 
  Heute waren schon 1 Besucher (13 Hits) hier!  
 
Diese Webseite wurde kostenlos mit Homepage-Baukasten.de erstellt. Willst du auch eine eigene Webseite?
Gratis anmelden