Aus RN-Wissen.de
Wechseln zu: Navigation, Suche
Rasenmaehroboter Test

K (Einige Pinbelegungen der populärsten Controller im Roboternetz)
Zeile 157: Zeile 157:
 
  |[[Pwm]] bzw. Output Compare Ausgang des Timers2
 
  |[[Pwm]] bzw. Output Compare Ausgang des Timers2
 
|}
 
|}
 +
----
 +
 +
== Timer der AVR's ==
 +
Die Mikrocontroller der Firma ATmel (AVR's genannt) besitzen je nach Typ eine unterschiedliche Anzahl an Timern, die sich alle programmieren lassen. Bei den aktuellen ATmega's sind das mindestens ein 8-Bit Timer und bei größeren Ausführungen der Serie auch 16-Bit Timer. Die Timer werden immer Timerx benannt, wobei x für die Timernummer steht (also 0, 1, 2, usw.). Die Konfigurationsmöglichkeiten der Timer schwanken von Timer zu Timer.
 +
 +
''Hinweis: Die folgenden Code-Beispiele (in C programmiert) wurden für einen ATmega32 entwickelt. Sie lassen sich also ohne große Änderungen auch auf anderen Mikrocontrollern der Firma ATmel ausführen.''
 +
 +
=== Allgemeine Funktionsweise ===
 +
Timer funktionieren nach dem allgemeinen Prinzip, dass sie eine Ganzzahl (im weiteren als Zähler bezeichnet) je nach Betriebsmodus auf- oder abwärts erweitern, d.h. inkrementieren bzw. dekrementieren.
 +
Angenommen der Timer arbeitet im einfachsten Betriebsmodus, dem normalen Modus (siehe [[Avr#Normaler Modus|Normaler Modus]]). Die Zählrichtung des Timers ist aufsteigend gerichtet, und je nach Auflösung, also 8-Bit oder 16-Bit, erreicht der Zähler irgendwann einen bestimmten Zustand. Möglich wäre, dass er überläuft, wenn z.B. bei einem 8-Bit Timer der Wert 255 inkrementiert wird (siehe Grafik).
 +
 +
[[Bild:AbstrakterZaehlvorgang.png]]
 +
 +
=== Der Prescaler ===
 +
Der Prescaler (eng. = Vorteiler) kann dazu genutzt werden den Takt, der den Timern zugeführt wird, zu verkleinern. U.a. kann man damit die Timer so konfigurieren, damit diese in den unterschiedlichsten Frequenzen takten. Hier eine Grafik die den Prescaler veranschaulicht:
 +
 +
[[Bild:Prescaler.png]]
 +
 +
Das obere Diagramm zeigt den Betrieb ohne Prescaler der untere mit Prescaler. Die gestrichelte Linie zeigt wann ein Interrupt eintritt.
 +
 +
Im Teil [[Avr#Die Betriebsmodi|Die Betriebsmodi]] wird weiter auf die praktische Verwendung des Prescalers eingegangen.
 +
 +
=== Die Betriebsmodi ===
 +
Die AVR-Controller können in unterschiedlichen Timer-Betriebsmodi ausgeführt werden. Diese wären:
 +
* Normaler Modus
 +
* CTC Modus
 +
* PWM
 +
 +
==== Normaler Modus (Normal Mode) ====
 +
Der einfachste Betriebsmodus ist der normale Modus. Er funktioniert wie im Abschnitt Allgemeine Funktionsweise [[Avr#Allgemeine Funktionsweise|Allgemeine Funktionsweise]] ansatzweise beschrieben. Die Zählrichtung des Timers ist immer aufsteigend gerichtet und irgendwann kommt es zu dem Interrupt Timer-Overflow (welcher in einer passend ISR aufgefangen werden kann). Im einfachsten Fall kann man diesen Modus in folgendem Diagramm darstellen:
 +
 +
[[Bild:NormalerModus_1.png]]
 +
 +
Der Zähler des Timers (im Diagramm oben, die aufsteigende und dann wieder zurückgesetzte Linie) ist in dem Register TCNTx gespeichert, wobei x für eine Zahl steht. Soll z.B. auf den Timer0 (siehe Datenblatt des jeweiligen Controllers) des Controllers zugegriffen werden, so ist an TCNT eine 0 anzuhängen, also TCNT0.
 +
Wie lange es braucht, bis der Zähler einen Overflow auslöst, ist von der Taktfrequenz des Controllers und von der Timerauflösung abhängig. Nun wäre es ja sehr unpraktisch, wenn wir den Zähler nicht anpassen könnten. Denn sonst müssten wir unsere Software die den Timer benutzt evtl. unpassend anpassen und viel rechnen um z.B. für 1000 ms zu schlafen. Deswegen kann auf den Zähler zugreifen und ihn vorladen bevor dieser wieder vom eigentlichen Timer hochgezählt wird. Dies veranschaulicht folgendes Diagramm:
 +
 +
[[Bild:NormalerModus_1_Vorladen.png]]
 +
 +
Dadurch können man den Timer beeinflussen, und damit beeinflussen wie lange es dauert, bis ein Overflow auftritt. [https://mpg.dnsalias.com/~magerlu/rn-wiki/avrtimer_applet/ Um zu berechnen welchen Wert wir vorladen müssen, kann man dieses Java-Applet verwenden].
 +
 +
Natürlich kann man das auch „von Hand“ rechnen. Die Berechnung des Preloader- sowie Prescalerwerts bei Verwendung der Overflow-Interrupts, eines Prescalers von 64 und eines Quarzes mit der Frequenz von 8 MHz sieht folgendermaßen aus (gesuchte Frequenz beträgt 1000 Hz unter der Verwendung des Timer0 eines ATmega32):
 +
# <math>Prescale: Frequenz \cdot 1000000</math>
 +
# Wir definieren den maximalen Zählerwert. Dieser ist bei einem 8-Bit Timer 256, bei einem 16-Bit Timer 65536. In unserem Fall ist der maximale Zählerwert 256, weil Timer0 verwendet wird <math>Max. Zaehler: 256</math>.
 +
# Nun wird der Wert Prescale (s.o.) durch den Prescaler geteilt (<math>{8000000 \over 32}</math>). Das Ergebnis wird abgerundet (<math>=125000</math>).
 +
# Als nächstes wird der im dritten Punkt errechnete Wert durch die gesuchte Frequenz geteilt, also <math>={1000 \over 125000}=125</math>.
 +
# Nun wird mathematisch überprüft, ob der errechnete Wert aus dem vierten Punkt kleiner als die der maximale Zählerwert ist. Trifft dies zu so subtrahiert man den errechneten Wert vom maximalen Zählerwert (<math>=256-125=131</math>).
 +
 +
Damit hätten wir den Wert errechnet, der bei jedem Interrupt, den der Timer0 auslöst, in TCNTx (in diesem Fall TCNT0) nachgeladen werden muss, damit die Interrupts in dem gewünschten Zeitabstand von einer Millisekunde ausgelöst werden.
 +
Allerdings bleibt zu bemerken, dass es bei „ungeraden“ Frequenzen eine gewisse Ungenauigkeit geben würde, würden wir den Timer entsprechend des oben ermittelten Werts nachladen (diese Ungenauigkeit würde z.B. bei einer Quarzfrequenz von 7,3728 MHz gerundet 0,17% betragen). Wird über den Controller über einen langen Zeitraum eine Zeitmessung gemacht, so empfiehlt es sich gerade Quarzfrequenzen zu wählen.
 +
 +
Die Fehlerrate kann natürlich auch ausgerechnet werden. Hier die Rechenschritte (sie sind erweiternd zu der oberen Berechnung):
 +
# Als erstes wird mathematisch überprüft, ob der Preloaderwert (siehe fünften Schritt oben) größer als 1 ist.
 +
# Trifft dies zu, so wird als nächstens die resultierende Frequenz errechnet. Die geschieht folgendermaßen. Der errechnete Preloaderwert aus der Rechnung oben wird vom maximalen Zählerwert subtrahiert, anschließend mit dem Prescaler multipliziert und dann das Ganze durch Prescale geteilt (<math>\left( \frac{(256-131) \cdot 64}{8000000} \right) \cdot 1000000=1000</math>).
 +
# Nun wird die gesuchte Frequenz vom errechneten Wert aus dem dritten Punkt subtrahiert und dann wiederum durch diese geteilt (<math>{1000-1000 \over 1000}=0</math>). Damit läuft dieser Timer genau mit einer Fehlerrate von 0 %.
 +
 +
Betreibt man den Timer im Overflow-Modus, so muss man, wie bereits erwähnt, nach/bei jedem Overflow-Interrupt den Timer nachladen. Der Interrupt heißt in diesem Fall SIG_OVERFLOWx (x steht für die Nummer des Timers). Dieser muss in einer ISR abgefangen werden.
 +
 +
Zusammenfassend ein Beispielprogramm:
 +
 +
<pre>
 +
/* Es wird der Timer2 (8-Bit) eines ATmega32 verwendet, der mit einem Quarz mit 7,3728 MHz
 +
betrieben wird. Im Abstand von 0,001 ms erzeugt der Timer einen Interrupt, also eine
 +
Frequenz von 1000000 Hz (oder 100 kHz). Der Timer wird auf einen Prescaler von 1 und
 +
einem Preloader von  183 konfiguriert.*/
 +
 +
volatile uint8_t countTimer2; // Speichert den aktuellen Zählerwert
 +
 +
// ISR zum auffangen der Interrupts:
 +
SIGNAL(SIG_SIG_OVERFLOW2)
 +
{
 +
countTimer2++;
 +
TCNT2 = 183; // Nachladen
 +
}
 +
 +
// Initialisierung:
 +
TCCR2 = (1<<CS22); // Prescaler von 1
 +
TCNT2  = 183; // Vorladen
 +
TIMSK |= (1<<TOIE2); // Interrupts aktivieren und damit Timer starten
 +
sei();
 +
 +
// Funktionen zum benutzen der Timer:
 +
/** Diese Funktion nicht aufrufen. Wird von sleep_millisec aufgerufen.
 +
Bei t=100 schläft die Funktion 1 ms. */
 +
inline void sleep(uint8_t t)
 +
{
 +
// countTimer2 wird in der ISR oben inkrementiert
 +
countTimer2 = 0;
 +
while (countTimer2 < t);
 +
}
 +
 +
/** Schläft x-Millisekunden. */
 +
inline void sleep_millisec(uint16_t msec)
 +
{
 +
uint16_t i;
 +
for(i=0; i<msec; i++) {
 +
sleep(100);
 +
}
 +
}
 +
</pre>
 +
Allerdings wird auf diese leicht veraltete Technik nun nicht weiter eingegangen. Der Artikel wendet sich nun dem neueren „Compare Output“-Betriebsmodus zu.
 +
 +
Beim „Compare Output“-Betriebsmodus wird genauso ein Zähler hoch gezählt. Allerdings wird der Zählerwert nach jeder Inkrementierung mit einem, vom Benutzer festgelegten, Wert verglichen. Entspricht der Zählerwert dem gespeicherten Wert, so kommt es zu einem Interrupt. Dieser Wert wird in den Register OCRx (x steht für die Timernummer, z.B. bei Timer0 OCR0) gespeichert. Je nach Auflösung des Timers ist dieser Register 8-Bit oder 16-Bit breit. Hinweis: Siehe CTC Modus unten. Dieser wird benötigt um den Timer entsprechend im „Compare Output“-Betriebsmodus vernünftig zu betreiben.
 +
 +
Hier ein typisches Diagramm dieses Betriebsmodus (Hinweis: Das Diagramm wurde unter Verwendung des CTC Modus erstellt. Für Begriffserklärung siehe CTC Modus):
 +
 +
[[Bild:NormalerModus_CompareMatch.png]]
 +
 +
Das ausrechnen des Werts, der in OCRx geschrieben werden muss damit Frequenz x entsteht, ist nicht sonderlich schwer. Man geht wie bei der Berechnung des Werts für den Overflow-Modus vor (s.o.) nur das man das resultierende Ergebnis vom maximalen Zählerwert (bei 8-Bit Auflösung ist dieser 256, bei 16-Bit Auflösung 65536) subtrahiert. Das Ergebnis wird dann einmalig in den Register OCRx geschrieben. Mann muss also nicht wie beim Overflow-Modus den Timer nach jedem Interrupt nachladen. Der enstehende Interrupt heißt in diesem Fall SIG_OUTPUT_COMPAREx (x steht für die Nummer des Timers). Dieser muss in einer ISR abgefangen werden.
 +
 +
Wiederum zusammenfassend ein Beispielprogramm:
 +
 +
<pre>
 +
/* Es wird der Timer2 (8-Bit) eines ATmega32 verwendet, der mit einem Quarz mit 7,3728 MHz
 +
betrieben wird. Im Abstand von 0,001 ms erzeugt der Timer einen Interrupt, also eine Frequenz
 +
von 1000000 Hz (oder 100 kHz). Der Timer wird auf einen Prescaler von 1 und einem OCR2-Wert
 +
von 73 konfiguriert.*/
 +
 +
volatile uint8_t countTimer2; // Speichert den aktuellen Zählerwert
 +
 +
// ISR zum auffangen der Interrupts:
 +
SIGNAL(SIG_OUTPUT_COMPARE2)
 +
{
 +
  countTimer2++;
 +
}
 +
 +
// Initialisierung:
 +
TCCR2 = (1<<CS22) | (1<<WGM21); // Prescaler von 1 | CTC-Modus (siehe unten für Beschreibung)
 +
OCR2  = 73; // Vergleichswert
 +
TIMSK |= (1<<OCIE2); // Interrupts aktivieren und damit Timer starten
 +
sei();
 +
 +
// Funktionen zum benutzen der Timer:
 +
/** Diese Funktion nicht aufrufen. Wird von sleep_millisec aufgerufen.
 +
Bei t=100 schläft die Funktion 1 ms. */
 +
inline void sleep(uint8_t t)
 +
{
 +
// countTimer2 wird in der ISR oben inkrementiert
 +
countTimer2 = 0;
 +
while (countTimer2 < t);
 +
}
 +
 +
/** Schläft x-Millisekunden. */
 +
inline void sleep_millisec(uint16_t msec)
 +
{
 +
uint16_t i;
 +
for(i=0; i<msec; i++) {
 +
sleep(100);
 +
}
 +
}
 +
</pre>
 +
 +
==== CTC Modus (Clear Timer on Compare Match mode) ====
 +
Der CTC Modus ist eine Erweiterung zum „Compare Output“-Betriebsmodus. Der CTC Modus wird z.B. für das obere Beispielprogramm benötigt. Wird der Timer nämlich im normalen Betriebsmodus betrieben, so ist seine Zählergrenze je nach Auflösung 255 oder entsprechend für die 16-Bit Timer. Erst wenn diese Grenze erreicht wurde, wird der Timer zurückgesetzt (also auf 0). Durch den CTC Modus wird der Timer augenblicklich automatisch nachdem ein „Compare Output“-Interrupt auftrat zurückgesetzt. Man kann also die maximalen Zählergrenze selber definieren.
 +
Dieses Diagramm veranschaulicht den CTC Modus. Nach dem Interrupt wird der Timer sofort wieder zurückgesetzt.
 +
 +
[[Bild:NormalerModus_CompareMatch.png]]
 +
 +
==== PWM ====
 +
Für PWM siehe [[Pwm]].
 +
 +
=== Registerübersicht ===
 +
{{FarbigerRahmen|
 +
Hinweis: Dieser Teil ist noch Baustelle und wird von mir in den nächsten Tagen erweitert werden!
 +
}}
 +
 +
 +
--[[Benutzer:Luma|Luma]] 01:17, 10. Dez 2005 (CET)
 +
 
----
 
----
  

Version vom 10. Dezember 2005, 01:17 Uhr

Hersteller der AVR-Controllerserie ist Atmel.

Beispiel eines Atmel Controllers

Es gibt eine ganze Serie von AVR-Controllern. Sie alle werden ähnlich programmiert bieten jedoch unterschiedliche Features (I/O Leitungem, Timer, Pwm-Ports usw.) Es gibt inzwischen Entwicklungssysteme in den Sprachen Basic, C, Pascal und Assembler für diese Controller. AVR steht angeblich für Advanced Virtual RISC (in einem Paper von Alf Egin Bogen und Vegard Wollan)

  • 8 Bit Architektur ist für Hochsprachen (C) optimiert
  • Harvard-Architektur (getrennter Befehls- und Datenspeicher)
  • 32 Register, kein Akkumulator, 3 Pointerregister
  • In-System progammierbar - Das bedeutet der Controller kann sehr einfach über ein Programmierkabel (oft ISP-Kabel genannt ) das mit dem PC verbunden wird programmiert werden, auch dann wenn sich dieser in einer Schaltung befindet
  • JTAG (Debugerinterface)
  • AVR Typen (AT90, ATtiny, ATmega)
  • Viele Entwicklungsboards erhältlich, z.B. das Roboternetzboard RN-Control


Einige Pinbelegungen der populärsten Controller im Roboternetz

(in etwa nach Leistungsfähigkeit sortiert)



Die AVR-Pin-Bezeichnungen und deren Funktion

Die meisten Ports sind doppelt belegt und besitzen neben der normalen Port-Funktion noch eine Sonderfunktion. Die verschiedenen Pinbezeichnungen und Sonderfunktionen werden hier beschrieben:


PA 0 – 7 Port A - Ein 8 Bit breiter, bi-direktionaler I/O Port. Jeder Pin des Ports kann individuell als Eingang oder Ausgang konfiguriert werden.
PB 0 – 7 Port B - Ein 8 Bit breiter, bi-direktionaler I/O Port. Jeder Pin des Ports kann individuell als Eingang oder Ausgang konfiguriert werden.
PC 0 – 7 Port C - Ein 8 Bit breiter, bi-direktionaler I/O Port. Jeder Pin des Ports kann individuell als Eingang oder Ausgang konfiguriert werden.
PD 0 – 7 Port D - Ein 8 Bit breiter, bi-direktionaler I/O Port. Jeder Pin des Ports kann individuell als Eingang oder Ausgang konfiguriert werden.
XCK Externe Takt für UART. Wird nur in Sonderfällen für Baudrate benötigt.

UART ("Universal Asynchronous Receiver and Transmitter"). Das ist die serielle Schnittstelle, die zur Datenübertragung zwischen Mikrocontroller und PC genutzt wird. Zur Übertragung werden zwei Pins am Controller benötigt: TXD und RXD. Über TXD ("Transmit Data") werden Daten gesendet, RXD ("Receive Data") dient zum Empfang.

T0 Timer Eingang. Timer kann gestartet, gestoppt oder getaktet werden
T1 Timer Eingang. Timer kann gestartet, gestoppt oder getaktet werden
AIN0 Erster Eingang des Analog Komperators.

Mit AIN0 und AIN1 kann man zwei Spannungen miteinander vergleichen. Wenn die Spannung an AIN0 höher als bei AIN1 ist, liefert der Komparator "High", wenn umgekehrt ein "Low".

AIN1 Zweiter Teil des Analog Komperators

Mit AIN0 und AIN1 kann man zwei Spannungen miteinander vergleichen. Wenn die Spannung an AIN0 höher als bei AIN1 ist, liefert der Komparator "High", wenn umgekehrt ein "Low".

OC0 PWM bzw. Output Compare Ausgang des Timers 0
SS SPI-Interface – wird beneötigt um den richtigen Slave am Bus zu wählen
MOSI SPI-Interface – Datenausgang (bei Slave Eingang)
MISO SPI-Interface – Dateneingang (bei Slave Ausgang)
SCK SPI-Interface – Bustakt vom Controller
RESET Rücksetz Eingang. Ein log. 0 – Pegel an diesem Pin für die Dauer von mindestens zwei Zyklen des Systemtaktes bei aktivem Oszillator setzt den Controller zurück
VCC Betriebsspannung 5 V

(2,7 Volt bis 6 V bei 8 Mhz, die nächsten AVRs sollen ab 1,8 Volt funktionieren )

GND Masse
XTAL1 Eingang des internen Oszillators zur Erzeugung des Systemtaktes bzw. Eingang für ein externes Taktsignal, wenn der interne Oszillator nicht verwendet werden soll
XTAL2 Ausgang des integrierten Oszillators zur Erzeugung des Systemtaktes
RXD Serielle Schnittstelle Eingang TTL-Pegel
TXD Serielle Schnittstelle Ausgang TTL-Pegel
INT0 Externe Interrupt
INT1 Externe Interrupt
INT2 Externer Interrupt 2
OC1A Ausgang für die Compare-Funktion des integrierten Zeitgeber- / Zählerbausteines

Der erste PWM Ausgang des Timers1. Er wird oft zum Regeln der Bot-Motogeschwindigkeit benutzt.

OC1B Ausgang für die Compare-Funktion des integrierten Zeitgeber- / Zählerbausteines

Der zweite PWM Ausgang des Timers1. Er wird oft zum Regeln der Bot-Motogeschwindigkeit benutzt.

ICP1 Eingang für die Capture-Funktion des integrierten Zeitgebers / Zählerbausteines
ADC0 bis ADC7 Eingänge des Analag nach Digital (AD) Wandlers. Spannungen können hier gemessen werden.
AREF Referenzspannung für Analog-Digitalwandler (wird meist auf 5 V gesetzt)
GND Masse
AVCC Analog Digital Wandler (siehe Beschaltungsskizze)

Die Betriebsspannung für den AD Wandler. Die Pins AVCC, AGND und AREF sollten immer beschaltet werden, da es sonst es passieren kann, dass Port A nicht richtig funktioniert, selbst wenn man den AD Wandler nicht benutzt

TOSC1 TOSC1 und 2 sind Eingänge für den Asyncronen Modus von Timer2. Sie sind vorgesehen für den Anschluss eines externen Uhrenquarzes ( 32.768 kHz ). Damit lässen sich zum Beispiel sehr genaue 1 Sekunden Impulse für eine Uhr generien...
TOSC2 TOSC1 und 2 sind Eingänge für den Asyncronen Modus von Timer2. Sie sind vorgesehen für den Anschluss eines externen Uhrenquarzes ( 32.768 kHz ). Damit lässen sich zum Beispiel sehr genaue 1 Sekunden Impulse für eine Uhr generien...
TDI JTAG-Debug Interface
TDO JTAG-Debug Interface
TMS JTAG-Debug Interface
TCK JTAG-Debug Interface
SDA I2C-Schnittstelle (Bus aus 2 Leitungen) Datenleitung
SCL I2C-Schnittstelle (Bus aus 2 Leitungen) Clockleitung
OC2 Pwm bzw. Output Compare Ausgang des Timers2

Timer der AVR's

Die Mikrocontroller der Firma ATmel (AVR's genannt) besitzen je nach Typ eine unterschiedliche Anzahl an Timern, die sich alle programmieren lassen. Bei den aktuellen ATmega's sind das mindestens ein 8-Bit Timer und bei größeren Ausführungen der Serie auch 16-Bit Timer. Die Timer werden immer Timerx benannt, wobei x für die Timernummer steht (also 0, 1, 2, usw.). Die Konfigurationsmöglichkeiten der Timer schwanken von Timer zu Timer.

Hinweis: Die folgenden Code-Beispiele (in C programmiert) wurden für einen ATmega32 entwickelt. Sie lassen sich also ohne große Änderungen auch auf anderen Mikrocontrollern der Firma ATmel ausführen.

Allgemeine Funktionsweise

Timer funktionieren nach dem allgemeinen Prinzip, dass sie eine Ganzzahl (im weiteren als Zähler bezeichnet) je nach Betriebsmodus auf- oder abwärts erweitern, d.h. inkrementieren bzw. dekrementieren. Angenommen der Timer arbeitet im einfachsten Betriebsmodus, dem normalen Modus (siehe Normaler Modus). Die Zählrichtung des Timers ist aufsteigend gerichtet, und je nach Auflösung, also 8-Bit oder 16-Bit, erreicht der Zähler irgendwann einen bestimmten Zustand. Möglich wäre, dass er überläuft, wenn z.B. bei einem 8-Bit Timer der Wert 255 inkrementiert wird (siehe Grafik).

AbstrakterZaehlvorgang.png

Der Prescaler

Der Prescaler (eng. = Vorteiler) kann dazu genutzt werden den Takt, der den Timern zugeführt wird, zu verkleinern. U.a. kann man damit die Timer so konfigurieren, damit diese in den unterschiedlichsten Frequenzen takten. Hier eine Grafik die den Prescaler veranschaulicht:

Prescaler.png

Das obere Diagramm zeigt den Betrieb ohne Prescaler der untere mit Prescaler. Die gestrichelte Linie zeigt wann ein Interrupt eintritt.

Im Teil Die Betriebsmodi wird weiter auf die praktische Verwendung des Prescalers eingegangen.

Die Betriebsmodi

Die AVR-Controller können in unterschiedlichen Timer-Betriebsmodi ausgeführt werden. Diese wären:

  • Normaler Modus
  • CTC Modus
  • PWM

Normaler Modus (Normal Mode)

Der einfachste Betriebsmodus ist der normale Modus. Er funktioniert wie im Abschnitt Allgemeine Funktionsweise Allgemeine Funktionsweise ansatzweise beschrieben. Die Zählrichtung des Timers ist immer aufsteigend gerichtet und irgendwann kommt es zu dem Interrupt Timer-Overflow (welcher in einer passend ISR aufgefangen werden kann). Im einfachsten Fall kann man diesen Modus in folgendem Diagramm darstellen:

NormalerModus 1.png

Der Zähler des Timers (im Diagramm oben, die aufsteigende und dann wieder zurückgesetzte Linie) ist in dem Register TCNTx gespeichert, wobei x für eine Zahl steht. Soll z.B. auf den Timer0 (siehe Datenblatt des jeweiligen Controllers) des Controllers zugegriffen werden, so ist an TCNT eine 0 anzuhängen, also TCNT0. Wie lange es braucht, bis der Zähler einen Overflow auslöst, ist von der Taktfrequenz des Controllers und von der Timerauflösung abhängig. Nun wäre es ja sehr unpraktisch, wenn wir den Zähler nicht anpassen könnten. Denn sonst müssten wir unsere Software die den Timer benutzt evtl. unpassend anpassen und viel rechnen um z.B. für 1000 ms zu schlafen. Deswegen kann auf den Zähler zugreifen und ihn vorladen bevor dieser wieder vom eigentlichen Timer hochgezählt wird. Dies veranschaulicht folgendes Diagramm:

NormalerModus 1 Vorladen.png

Dadurch können man den Timer beeinflussen, und damit beeinflussen wie lange es dauert, bis ein Overflow auftritt. Um zu berechnen welchen Wert wir vorladen müssen, kann man dieses Java-Applet verwenden.

Natürlich kann man das auch „von Hand“ rechnen. Die Berechnung des Preloader- sowie Prescalerwerts bei Verwendung der Overflow-Interrupts, eines Prescalers von 64 und eines Quarzes mit der Frequenz von 8 MHz sieht folgendermaßen aus (gesuchte Frequenz beträgt 1000 Hz unter der Verwendung des Timer0 eines ATmega32):

  1. [math]Prescale: Frequenz \cdot 1000000[/math]
  2. Wir definieren den maximalen Zählerwert. Dieser ist bei einem 8-Bit Timer 256, bei einem 16-Bit Timer 65536. In unserem Fall ist der maximale Zählerwert 256, weil Timer0 verwendet wird [math]Max. Zaehler: 256[/math].
  3. Nun wird der Wert Prescale (s.o.) durch den Prescaler geteilt ([math]{8000000 \over 32}[/math]). Das Ergebnis wird abgerundet ([math]=125000[/math]).
  4. Als nächstes wird der im dritten Punkt errechnete Wert durch die gesuchte Frequenz geteilt, also [math]={1000 \over 125000}=125[/math].
  5. Nun wird mathematisch überprüft, ob der errechnete Wert aus dem vierten Punkt kleiner als die der maximale Zählerwert ist. Trifft dies zu so subtrahiert man den errechneten Wert vom maximalen Zählerwert ([math]=256-125=131[/math]).

Damit hätten wir den Wert errechnet, der bei jedem Interrupt, den der Timer0 auslöst, in TCNTx (in diesem Fall TCNT0) nachgeladen werden muss, damit die Interrupts in dem gewünschten Zeitabstand von einer Millisekunde ausgelöst werden. Allerdings bleibt zu bemerken, dass es bei „ungeraden“ Frequenzen eine gewisse Ungenauigkeit geben würde, würden wir den Timer entsprechend des oben ermittelten Werts nachladen (diese Ungenauigkeit würde z.B. bei einer Quarzfrequenz von 7,3728 MHz gerundet 0,17% betragen). Wird über den Controller über einen langen Zeitraum eine Zeitmessung gemacht, so empfiehlt es sich gerade Quarzfrequenzen zu wählen.

Die Fehlerrate kann natürlich auch ausgerechnet werden. Hier die Rechenschritte (sie sind erweiternd zu der oberen Berechnung):

  1. Als erstes wird mathematisch überprüft, ob der Preloaderwert (siehe fünften Schritt oben) größer als 1 ist.
  2. Trifft dies zu, so wird als nächstens die resultierende Frequenz errechnet. Die geschieht folgendermaßen. Der errechnete Preloaderwert aus der Rechnung oben wird vom maximalen Zählerwert subtrahiert, anschließend mit dem Prescaler multipliziert und dann das Ganze durch Prescale geteilt ([math]\left( \frac{(256-131) \cdot 64}{8000000} \right) \cdot 1000000=1000[/math]).
  3. Nun wird die gesuchte Frequenz vom errechneten Wert aus dem dritten Punkt subtrahiert und dann wiederum durch diese geteilt ([math]{1000-1000 \over 1000}=0[/math]). Damit läuft dieser Timer genau mit einer Fehlerrate von 0 %.

Betreibt man den Timer im Overflow-Modus, so muss man, wie bereits erwähnt, nach/bei jedem Overflow-Interrupt den Timer nachladen. Der Interrupt heißt in diesem Fall SIG_OVERFLOWx (x steht für die Nummer des Timers). Dieser muss in einer ISR abgefangen werden.

Zusammenfassend ein Beispielprogramm:

/* Es wird der Timer2 (8-Bit) eines ATmega32 verwendet, der mit einem Quarz mit 7,3728 MHz
betrieben wird. Im Abstand von 0,001 ms erzeugt der Timer einen Interrupt, also eine
Frequenz von 1000000 Hz (oder 100 kHz). Der Timer wird auf einen Prescaler von 1 und
einem Preloader von  183 konfiguriert.*/

volatile uint8_t countTimer2;	// Speichert den aktuellen Zählerwert

// ISR zum auffangen der Interrupts:
SIGNAL(SIG_SIG_OVERFLOW2)
{
	countTimer2++;
	TCNT2 = 183;		// Nachladen
}

// Initialisierung:
TCCR2 = (1<<CS22);		// Prescaler von 1
TCNT2  = 183;			// Vorladen
TIMSK |= (1<<TOIE2);		// Interrupts aktivieren und damit Timer starten
sei();

// Funktionen zum benutzen der Timer:
/** Diese Funktion nicht aufrufen. Wird von sleep_millisec aufgerufen.
Bei t=100 schläft die Funktion 1 ms. */
inline void sleep(uint8_t t)
{
	// countTimer2 wird in der ISR oben inkrementiert
	countTimer2 = 0;
	while (countTimer2 < t);
}

/** Schläft x-Millisekunden. */
inline void sleep_millisec(uint16_t msec)
{
	uint16_t i;
	for(i=0; i<msec; i++) {
		sleep(100);
	}
}

Allerdings wird auf diese leicht veraltete Technik nun nicht weiter eingegangen. Der Artikel wendet sich nun dem neueren „Compare Output“-Betriebsmodus zu.

Beim „Compare Output“-Betriebsmodus wird genauso ein Zähler hoch gezählt. Allerdings wird der Zählerwert nach jeder Inkrementierung mit einem, vom Benutzer festgelegten, Wert verglichen. Entspricht der Zählerwert dem gespeicherten Wert, so kommt es zu einem Interrupt. Dieser Wert wird in den Register OCRx (x steht für die Timernummer, z.B. bei Timer0 OCR0) gespeichert. Je nach Auflösung des Timers ist dieser Register 8-Bit oder 16-Bit breit. Hinweis: Siehe CTC Modus unten. Dieser wird benötigt um den Timer entsprechend im „Compare Output“-Betriebsmodus vernünftig zu betreiben.

Hier ein typisches Diagramm dieses Betriebsmodus (Hinweis: Das Diagramm wurde unter Verwendung des CTC Modus erstellt. Für Begriffserklärung siehe CTC Modus):

NormalerModus CompareMatch.png

Das ausrechnen des Werts, der in OCRx geschrieben werden muss damit Frequenz x entsteht, ist nicht sonderlich schwer. Man geht wie bei der Berechnung des Werts für den Overflow-Modus vor (s.o.) nur das man das resultierende Ergebnis vom maximalen Zählerwert (bei 8-Bit Auflösung ist dieser 256, bei 16-Bit Auflösung 65536) subtrahiert. Das Ergebnis wird dann einmalig in den Register OCRx geschrieben. Mann muss also nicht wie beim Overflow-Modus den Timer nach jedem Interrupt nachladen. Der enstehende Interrupt heißt in diesem Fall SIG_OUTPUT_COMPAREx (x steht für die Nummer des Timers). Dieser muss in einer ISR abgefangen werden.

Wiederum zusammenfassend ein Beispielprogramm:

/* Es wird der Timer2 (8-Bit) eines ATmega32 verwendet, der mit einem Quarz mit 7,3728 MHz
betrieben wird. Im Abstand von 0,001 ms erzeugt der Timer einen Interrupt, also eine Frequenz
von 1000000 Hz (oder 100 kHz). Der Timer wird auf einen Prescaler von 1 und einem OCR2-Wert
von 73 konfiguriert.*/

volatile uint8_t countTimer2;	// Speichert den aktuellen Zählerwert

// ISR zum auffangen der Interrupts:
SIGNAL(SIG_OUTPUT_COMPARE2)
{
  countTimer2++;
}

// Initialisierung:
TCCR2 = (1<<CS22) | (1<<WGM21);	// Prescaler von 1 | CTC-Modus (siehe unten für Beschreibung)
OCR2  = 73;			// Vergleichswert
TIMSK |= (1<<OCIE2);		// Interrupts aktivieren und damit Timer starten
sei();

// Funktionen zum benutzen der Timer:
/** Diese Funktion nicht aufrufen. Wird von sleep_millisec aufgerufen.
Bei t=100 schläft die Funktion 1 ms. */
inline void sleep(uint8_t t)
{
	// countTimer2 wird in der ISR oben inkrementiert
	countTimer2 = 0;
	while (countTimer2 < t);
}

/** Schläft x-Millisekunden. */
inline void sleep_millisec(uint16_t msec)
{
	uint16_t i;
	for(i=0; i<msec; i++) {
		sleep(100);
	}
}

CTC Modus (Clear Timer on Compare Match mode)

Der CTC Modus ist eine Erweiterung zum „Compare Output“-Betriebsmodus. Der CTC Modus wird z.B. für das obere Beispielprogramm benötigt. Wird der Timer nämlich im normalen Betriebsmodus betrieben, so ist seine Zählergrenze je nach Auflösung 255 oder entsprechend für die 16-Bit Timer. Erst wenn diese Grenze erreicht wurde, wird der Timer zurückgesetzt (also auf 0). Durch den CTC Modus wird der Timer augenblicklich automatisch nachdem ein „Compare Output“-Interrupt auftrat zurückgesetzt. Man kann also die maximalen Zählergrenze selber definieren. Dieses Diagramm veranschaulicht den CTC Modus. Nach dem Interrupt wird der Timer sofort wieder zurückgesetzt.

NormalerModus CompareMatch.png

PWM

Für PWM siehe Pwm.

Registerübersicht

Hinweis: Dieser Teil ist noch Baustelle und wird von mir in den nächsten Tagen erweitert werden!


--Luma 01:17, 10. Dez 2005 (CET)


Die Fusebits

Fusebits nennt man bestimmte Bits zur Konfigurierung eines AVR-Controllers. Bei der Auslieferung neuer AVR Controller sind die Fusebits bereits vorkonfiguriert. In vielen Fällen kann man die Konfiguration unverändert belassen, je nach Controllertyp. Bei den Typen Mega xxx bestimmen einige Fusebits beispielsweise, dass der interne Taktgeber aktiviert ist. Möchte man dagegen einen externen Quarz anschließen oder die Taktfrequenz ändern, so müssen auch die Fusebits geändert werden. Auch das Deaktivieren des "On Chip Debugging" Modus ist oft notwendig, wenn man alle Ports ausnutzen möchte.

Die Fusebits werden in der Regel über die Software eingestellt, welche auch für das Übertragen des Programmcodes zuständig ist. Besonders einfach geht dies beispielsweise mit der Entwicklungsumgebung Bascom. Aber auch andere Programme wie PonyProg können für die Umstellung der Fusebits genutzt werden. Einmal eingestellte Fusebits bleiben bis zur erneuten Fusebit-Änderung erhalten. Der normale Programmiermodus verändert die Fusebits nicht.

Je nach AVR Controllertyp sind unterschiedliche Fusebits (Einstellungen) vorhanden. Die genaue Beschreibung findet man im jeweiligen Datenblatt. Da aber falsch gesetzte Fusebit-Einstellungen zu den häufigsten Problemen gehören, liste ich hier die Funktion der üblichen Fusebits nochmals genauer auf:

CKSEL0, CKSEL1, CKSEL2, CKSEL3 Die Kombination dieser 4 Fusebits bestimmt die Taktquelle des Controllers. Das kann eine interner Taktgenerator, ein Quarz, Quarzoszillator, RC-Glied und ähnliches sein.
JTAGEN Hiermit wird die "On Chip Debugging" Schnittstelle aktiviert bzw. deaktiviert. Das sind die Bits mit den Bezeichnungen TDI, TDO, TMS und TCK. Möchte man diese Pins als normalen Port nutzen, so muss diese Schnittstelle immer deaktiviert werden.
SUT0, SUT1 Die sogenannte StartUp-Zeit (PowerOn delay). Diese Einstellung muss abhängig von der Art des Taktgenerators eingestellt werden, genaueres im jeweiligen Datenblatt.
SPIEN Hiermit kann die serielle ISP-Programmierung, welche die meisten Programmierkabel nutzen, deaktiviert werden. Dies sollte man lieber vermeiden, denn wenn dieser Programmiermodus deaktiviert wurde, kann nur noch der Parallel-Programmiermodus genutzt werden. Der Parallel-Programmiermodus benötigt jedoch ein spezielles Programmiergerät, das die wenigsten Bastler besitzen. Also Vorsicht!
BODEN Über dieses Bit wird der Brown-out Detector aktiviert bzw. deaktiviert. Dies ist eine Überwachung der Betriebsspannung. Diese Überwachung soll dafür sorgen, dass bei Spannungseinbrüchen ein ordentlicher RESET durchgeführt wird. Dadurch wird verhindert, dass ein Controller in einen undefinierten Zustand gerät (hängen bleibt).
BOOTLEVEL Über dieses Bit kann die Spannung festgelegt werden, ab welcher der Brown-out Detector den Controller neu startet (also RESET ausführt).
BOOTRST Gewöhnlich startet ein Programm im Controller nach einem RESET ab Adresse 0. Durch dieses Fusebit kann der Controller jedoch veranlasst werden, nach einem Reset einen sogenannten Bootloader-Bereich auszuführen. Ein Bootloader kann genutzt werden, um Controller über andere Schnittstellen (z.B. RS232) zu programmieren.
BOOTSZ0, BOOTSZ1 Der zuvor genannte Bootloaderbereich kann bei AVR-Controllern verschieden groß sein. Über diese beiden Bits können vier verschiedene Größen eingestellt werden. Siehe unter Bootloader.
EESAVE Dieses Bit legt fest, ob beim Programmieren des Controllers (man nennt es auch brennen) immer das EEPROM gelöscht werden soll.
CKOPT Abhängig von den Einstellungen von CKSEL kann hier dir Oszillator-Verstärkung eingestellt werden. Genaueres im Datenblatt des jeweiligen Controllers.
WDTON Schaltet den WatchDog-Timer beim Booten ein/aus. Dies ist auch per Software möglich
RSTDISBL Durch dieses Bit kann man den RESET-Pin deaktivieren und dann als normalen I/O-Port nutzen. Aber Vorsicht! Da die RESET-Leitung beim Programmieren (Brennen) des Chips genutzt wird, kann man nach dessen Deaktivierung den Controller mit den üblichen ISP-Adaptern nicht mehr programmieren. In diesem Fall könnte man zwar den Controlle noch mit speziellen Programmiergeräten im Parallelmodus programmieren, aber in der Praxis verfügen nur wenige Bastler über ein Programmiergerät, das dies leistet.
LB1, LB2 Das sind die sogenannten Lockbits, mit denen sich das Auslesen des Flash- als auch EEPROM-Speichers verhindern läßt. Zwar können andere Anwender immer noch Daten lesen, allerdings handelt es sich dabei nicht mehr um den wirklichen Inhalt sondern lediglich um wirre Datenbytefolgen. Programmierer, die den erarbeiteten Code vor Raubkopierern schützen wollen, nutzen diese Lockbits. Das Programmieren ist auch bei gesetzen Lockbits noch möglich. Der Bootloader-Bereich wird nicht durch die Lockbits geschützt.
BLB01, BLB02 Durch diese Bits kann der Code sogar vor dem Zugriff durch den Bootloader geschützt werden
BLB11, BLB12 Diese Bits schützen den Bootloaderbereich selbst

Wie man die Fusebits mit Bascom einstellt, wird im Beitrag Bascom - Erstes Programm in den AVR Controller übertragen erläutert.

--Frank 19:49, 29. Nov 2005 (CET)

Siehe auch

Weblinks

Siehe auch


LiFePO4 Speicher Test