Unterrichts- und Lernmaterial für Mikrocontroller
Unterrichts- und Lernmaterial fürMikrocontroller

Arbeiten mit AVR Studio - Teil 4

Alle hier dargestellten Vorgänge und Erklärungen am Beispiel eines ATmega8515 lassen sich auf alle anderen Atmel-Controller (ATtiny, ATmega, ATxmega) übertragen. Im Einzelnen muss immer das Datenblatt hinzugezogen werden; die Bezeichnungen unterscheiden sich - wenn überhaupt - nur geringfügig voneinander.

Theorie - Der 8-Bit Timer0/Counter

Nach Datenblatt verfügt der ATmega8515 über einen 8-Bit und einen 16-Bit Timer0/Counter. Beide mit Vorteilern und zwei unabhängig voneinander arbeitenden Compare Ausgängen A und B. Damit lässt sich die Ausführungszeit des Controllers genau einstellen.

Der Timer/Counter hat ein 8-Bit Register TCNT0, den eigentlichen Zähler, der bei jedem CLK um 1 erhöht wird. Alle 256 CLKs ist der maximale Wert

  • MAX = 255

erreicht, der Timer/Counter wird auf

  • BOTTOM = 0

zurückgesetzt und das Timer/Overflow Flag TOV0 wird gesetzt, später aber nicht gelöscht. Wird zusätzlich das Timer Overflow Interrupt  mit TOIE0 = 1 im TIMSK0-Register und I = 1 (I-Bit) im Statusregister gesetzt, wird ein Interrupt ausgelöst und das TOV0-Flag anschließend auf 0 zurückgesetzt.

  • Mit dem Timer0 kann ein Interrupt ausgelöst werden.

Der höchste angenommene Wert beim Hochzählen wird als TOP bezeichnet.

  • TOP

ist normalerweise 0xFF oder MAX. Hinterlegt man aber in einem 8-Bit Output Compare-Register OCR0 einen Wert TOP, der kleiner als 255 ist, kann damit das Hochzählen verkürzt werden.

Immer dann, wenn der Inhalt von TCNT0 und OCR0 identisch ist, wird dies über das Output Compare Flag (OCF0) beim nächsten CLK-Signal angezeigt.

Ist der Interrupt aktiviert, dann erzeugt das Output Compare Flag einen Output Compare Interrupt (OCI). Das Flag wird nach Ausführung des Interrupts automatisch zurückgesetzt oder manuell per Software. Auf diese Art kann eine Interrupt Service Routine (ISR) abgerufen werden.

Praktisch bedeutet das, dass bei 256 Zähltakten und einem Arbeitstakt von 1 MHz ungefähr 3906 Overflow Ereignisse pro Sekunde eintreten. Wollte man eine Verzögerungszeit von 1s erzeugen, müsste eine Variable bis 3906 hochgezählt werden, bevor eine Sekunde herum ist.

Für eine 8-Bit Variable ist das nicht darstellbar; die zählt nur bis 255. Das würde pro Sekunde 15-mal einen Overflow erzeugen. Also nehmen wir eine 16-Bit Variable, die 65536 Zahlen darstellen kann.

Die Zahl 3906 passt ca. 17-mal in 65536 hinein; damit lassen sich dann Blinkzeiten von bis zu 17s realisieren.

Eine weitere Möglichkeit ist, den Timer über einen Prescaler (Vorteiler) herunterzutakten. Der maximale Prescaler-Wert liegt bei 1024, wenn man die Bits CS02 und CS0 im Register TCCR0 auf 1 setzt. Mit dieser Einstellung ergibt sich:

  • 1,0 MHz / 1024 ~ 976 pro Sekunde

oder in Worten: das Timer Register wird 976-mal pro Sekunde um 1 erhöht. und erzeugt damit 3,8-mal pro Sekunde einen Overflow. Mit einer 8-Bit Variablen lassen sich diese Ereignisse zählen und man erreicht so Zeiten von maximal ca. 67 Sekunden.

Das Register des Timer0, mit dem wir in Übung 1 arbeiten werden, heißt:

  • TCCR0 (Timer Counter Control Register)
  • Über dieses 8-Bit Register wird über CS0[2:0] (Clock select) der Vorteiler (s. Datenblatt des Controllers), über COM0[1:0] der Modus Compare Output und mit WGM0[1:0] der Modus Waveform Generation eingestellt.
Abbildung 1 - Timer/Counter Control Register
Abbildung 2 - Einstellungsmöglichkeiten im TCCR0-Register. Gezeigt werden die Einstellungen für Übung 1 in binärer Form unter Einstellung1 und Einstellung 2 sowie in hexadezimaler Form. CM -> Compare Mode; Clk -> Clock; PWM -> Puls-Weiten-Modulation; phk

Übung 1 - Blinkerschaltung mit Timer0 im Normalmodus

Nach so viel Theorie erfolgt jetzt die praktische Umsetzung einer Blinkerschaltung in Assembler. In der ersten Übung blinkt die LED mit unterschiedlicher Frequenz; dazu wird der Frequenzteiler im Register TCCR0 nacheinander auf

  • 100 (fo DIV 256) und
  • 101 (fo DIV 1024)

gesetzt und die Blinkfrequenz beobachtet. Mit einem Oszilloskop wird die Schaltfrequenz am Ausgang von PB0 gemessen.

Anschließend wird der Modus Compare Output über die TCCR0-Pin COM0[1:0] nacheinander auf

  • 01 (PB0 -> TOGGLE)
  • 11 (PB0 auf 1)
  • 10 (PB0 auf 0)

gesetzt und das jeweilige Ergebniss im Versuch beobachtet. Im ersten Fall sollte LED0 blinken, im zweiten permanent auf 1 oder HIGH sein und im dritten Fall nicht leuchten.

Das Signal des Timers wird am OC0-Pin (s. Datenblatt: PB0 des ATmega8515) erzeugt und direkt an die LED weitergeleitet.

Blinkerschaltung mit Timer0 im Normalmodus
Material
  • 1x  STK200 mit ATmega 8515-16PU
  • 1x  Schaltnetzteil 9V DC, 1000 mA
  • 1x  ISP2 Programmer
  • 1x  AVR Studio 4.19 auf PC
  • 1x  USB-Oszilloskop (optional)
Aufgaben
  • Teil 1: LED0 wird so programmiert, dass sie blinkt.
  • Lege ein neues Projekt led_timer0 an
  • Gib das Assemblerprogramm led_timer0.asm in den Editor ein und speichere es ab.
  • Brenne das Programm in den Controller und starte es anschließend.
  • Überprüfe, ob LED0 blinkt.
  • Bestimme mit einem USB-Oszilloskop die Blinkfrequenz.
  • Teil 2: Verändere jetzt das Assemblerprogramm led_timer0.asm so, dass der Vorteiler auf 256 eingestellt ist. Mit welcher Frequenz blinkt jetzt LED0? Miss mit einem USB-Oszilloskop nach.
  • Teil 3: Verändere das Assemblerprogramm über die Einstellungen im Register TCCR0 so, dass LED0 permanent leuchtet. Beschreibe mit eigenen Worten, wo und wie die Programmänderung vorgenommen wird.
  • Teil 4: Verändere das Assemblerprogramm so, dass LED0 über das Register TCCR0 ausgeschaltet ist. An welcher Stelle im Programm wird die Änderung vorgenommen?

Schaltungsaufbau

Abbildung 3 - Schaltungsaufbau zu Übung 1

Das Programm LED_timer0.asm

Wie arbeitet das Programm LED_timer0.asm?

  • SBI DDRB, DDB0

SBI -> Set Bit Immediate. DDB0 im Datenrichtungsregister wird auf Ausgang gesetzt.

  • LDI R16, 0x15

In das Register R16 wird die Hexadezimalzahl 15 (binär: 0001_0101) geladen. Die Bitfolge beschreibt die Flags im Register TCCR0. TCCR0[2:0] = 101 setzt den Vorteiler laut Datenblatt auf 1024, während TCCR0[5:4] den Modus Compare Output festlegt. Die Bitfolge 01 toggelt Ausgang OC0 - PB0, an dem LED0 hängt.

  • OUT TCCR0, R16

Die Bitfolge wird in das Register TCCR0 geschrieben.

  • loop:
             RJMP loop

Das Programm verbleibt in der Endlosschleife, in der überhaupt nichts passiert, während im Hintergrund der Timer0/Counter weiter läuft. Der Ausgang OC0 wird immer dann, wenn der Zähler bei 255 ist, umgeschaltet, so dass LED0 fröhlich weiterblinkt.

Oszillogramm für TCCR0[2:0] = 101 (Vorteiler: 1024)

Abbildung 4 - Ausgangsfrequenz an PB0. LED0 blinkt mit ca. 1,9 Hz bei einem Vorteiler von 1024..

Oszillogramm für TCCR0[2:0] = 100 (Vorteiler: 256)

Abbildung 5 - Ausgangsfrequenz an PB0. LED0 blinkt mit ca. 7,6 Hz bei einem Vorteiler von 256.

Die gemessene Frequenz der am Ausgang PB0 anliegenden Schwingung beträgt

  • 1,9 Hz (Abb. 4) bei einem Vorteiler 1024
    und
  • 7,6 Hz (Abb. 5) bei einem Vorteiler 256.

Da sich die Frequenz des Taktgebers bei kleinerem Vorteiler vervierfacht hat, muss sich auch die Blinkfrequenz vervierfachen.

Mit diesem Programm haben wir den Überlauf des Zählers nach 256 Clocks direkt an PB0 weitergegeben und mit der Einstellung COM0[1:0] = 01 dafür gesorgt, dass der Ausgang PB0 bei jedem Overflow umgeschaltet (toggel) wird und dadurch die LED anfängt zu blinken. Leider lässt sich in dieser Konfiguration die Blinkfrequenz nur durch eine Änderung des Vorteilers verändern; Feinjustierungen der Blinkfrequenz sind nicht möglich.

 

Dies kann man aber dadurch erreichen, dass man den Timer0 in den sogenannten CTC-Modus schaltet und in das Output Compare Register OCR0 einen Wert einträgt, bis zu dem der Timer/Counter hoch zählt, bevor er wieder bei 0 anfängt. Das heißt, es wird dann nicht mehr von 0 bis 255 hochgezählt, sondern von 0 bis zu dem im Register OCR0 eingestellten Wert.

8-Bit Register OCR0

Übung 2 - Timer0 im CTC-Modus

Bei einer Blinkerschaltung, die eine LED an PB0 zum Beispiel mit f = 5 Hz blinken lassen soll, beträgt die Periodendauer T = 0,2 s = 200 ms. Bei vorgegebener Taktfrequenz des Controllers von f0 = 1,... MHz (wegen des hohen Fehlers von +/- 10% (bei internem Taktgeber) habe ich hier und im Folgenden die Punkte hinter dem Komma gesetzt) beträgt die Periodendauer Tf0 = 1,... µs.

Mit einem Vorteilerwert 1024 erreicht man 1,... ms; lässt man nach jedem Overflow des 8-Bit timer0/counter den Ausgang PB0 toggeln, erreicht man eine Periodendauer von 256 ms. Dieser Wert ist etwas zu hoch gegenüber den angestrebten 200ms für exakt 5 Hz.

Für eine Feinjustierung der Periodendauer versetzen wir den Timer0 in den CTC-Modus. Bevor das aber geschieht, messen wir am Beispiel eines BASCOM Programms, wie genau dort die Verzögerungsbefehle waitms, wait und waitus bei einer bestimmten Zeitvorgabe arbeiten. Aus praktischen Gründen wird hierzu ein Controller ATtiny13A eingesetzt.

Vorversuch - 5 Hz Blinkerschaltung mit einem BASCOM Programm

Über das folgende BASCOM Programm wird versucht die Blinkfrequenz der LED an PB0 auf 5 Hz einzustellen. Dazu wird der BASIC-Befehl waitms benutzt. Das USB-Oszilloskops zeigt anschließend die tatsächlich anliegende Frequenz.

Vorversuch LED Blinker mit 5 Hz
Material
  • 1x  Brenner mit Nullkraftsockel
  • 1x  Steckbrett
  • 1x  Steckernetzteil, 5V, 1000mA
  • 1x  Widerstand, 220 Ohm
  • 1x  LED, rot
  • diverse Steckdrähte
  • 1x  USB-Oszilloskop
Aufgaben
  • Trenne die USB-Verbindung zwischen Brenner und PC.
  • Setze einen ATtiny13A in den Nullkraftsockel des Brenners und stelle die USB-Verbindung zum PC wieder her.
  • Lade das Programm BASCOM und lege das neue Programm timer0_1.bas im Projekt timer an.
  • Übertrage das Programm timer0_1.bas in den BASCOM Editor und speichere es ab.
  • Starte das Programm und bestimme mit dem Oszilloskop die Taktfrequenz am Ausgang PB0 des Controllers. Notiere den Messwert.

Schaltungsaufbau

Das Programm timer0_1.bas

Abbildung 6 - Das Programm timer0_1.bas mit zugehörigem Oszillogramm. Aufgenommen an PB0. Die Wartezeit für den HIGH- und den LOW-Pegel beträgt jeweils 100ms. Die gemessene Blinkfrequenz liegt bei 4,6.. Hz, statt wie vorgegeben bei 5,0 Hz.

Bewertung des Messergebnisses

Das vorliegende BASIC Programm mit seinem Befehl Waitms 100 setzt die entsprechende Zeit nur ungenau um. Die Ursachen dafür liegen im ungenauen Taktgeber (+/- 10%) und möglicherweise bei der maschinencodierten Umsetzung des Befehls WAITMS.

Der LOW und HIGH Zustand dauert nicht 100 ms sondern 107,5 ms. Zugegeben, mit unserer Sensorik merken wir den Unterschied von 7,5ms nicht, aber es gibt auch Situationen, in denen eine präzise Zeitumsetzung in einer Schaltung wichtig ist. Dafür benötigt man eine präzisere Programmierung. Mit dem folgenden Assembler-Programm sollte das besser gelingen. Dazu wird der timer0 im sogenannten CTC-Modus (clear-timer-on-compare) betrieben.

Im Modus Clear-Timer on Compare wird ein Vergleichswert in ein Compare-Register OCR0 geschrieben. Sobald der timer0 diesen Wert erreicht (TOP-Wert) wird ein Flag gesetzt. Der timer0/counter zählt also nicht mehr stur von 0 bis 255 (MAX-Wert), wie im Normalmodus, sondern kann irgendwo zwischen 0 und 255 zum Zählabbruch gezwungen werden, um dann wieder bei 0 anzufangen. Eine zweite Variante im Normalmodus, die noch nicht angesprochen wurde, besteht darin, dass man den timer0/counter nicht bei 0 sondern bei einem Wert k zwischen 0 und 255 starten lässt. Damit lassen sich ebenfalls die Zählzeiten verkürzen.

Abbildung 7 - timer0/counter im Normal- und CTC-Modus. Im Normalmodus lässt sich über einen Startwert k ebenfalls die Zählzeit verkürzen.

Übung 3 - Blinkerschaltung mit Timer0 im CTC-Modus

Ein 5-Hz-Blinker muss alle 100ms den Pegel am Ausgang ändern (toggeln); eine Periode dauert 200ms. Das folgende Assemblerprogramm soll das möglichst genau leisten.

Vorgegeben ist eine Taktfrequenz am Controller von 1,... MHz, die vom inneren Taktgeber des ATmega8515 kommt. Arbeitet der timer0/counter mit Vorteiler VT = 1024, dann verlängert sich die Taktzeit durch den 8-Bit Zähler auf

In dieser Übung werden die Register TCCR0 und OCR0 angesprochen.

Im OCR0-Register (Output Compare Register) steht ein 8-Bit Zahlenwert der permanent mit dem Wert im TCNT0-Register verglichen wird. Herrscht Übereinstimmung (match), kann ein

  1. Output Compare Interrupt oder
  2. ein bestimmtes Wellenformverhalten am Pin OC0 (PB0)

ausgelöst werden. Weitere Informationen findet man im Datenblatt zum Controller.

Material

wie in Übung 1

 

Schaltungsaufbau

wie in Abb. 3

Das Programm ctcmodus1.asm

Abbildung 8 - Das Programm ctcmodus1.asm. timer0 wird im CTC-Modus betrieben, in das Vergleichsregister OCR0 ist der Wert 0x6C eingetragen. Der Zähler zählt bis zu diesem Wert, setzt sich auf 0 zurück und beginnt von vorne zu zählen.

Die Oszillogramme

Abbildung 9 - Das Signal ist an PB0 abgenommen. Der Oszillator schwingt mit 1,... MHz. Gemessen wird mit dem Oszilloskop eine Toggle-Frequenz fblink von 4,45 Hz.

Mit einer zweiten Messung wird versucht den Frequenzwert fblink stärker an 5 Hz anzunähern. Der Frequenzwert muss um ca. 0.55 Punkte, von 4,45 auf 5,0 erhöht und der OCR0-Wert dafür erniedrigt werden. Probieren wir es mit einem neuen Wert von OCR0 = 0x60.

Abbildung 10 - Die Blinkfrequenz stellt sich für OCR0 = 0x60 auf ca. 5 Hz ein.

Wie arbeitet das Programm ctcmodus1.asm?

SBI     DDRB, DDB0

Bit 0 im DDRB wird auf 1 und damit auf Ausgang gesetzt. Die an PB0 angeschlossene LED ist aus (low active).

 

LDI    R16, 0x1D

OUT   TCCR0, R16

In das Austauschregister R16 wird der Code für einen Vorteilerwert 1024 und den CTC-Modus geladen und anschließend mit dem OUT-Befehl in das Register TCCR0 übertragen.

 

LDI   R16, 0x6C bzw. 0x60

OUT  OCR0, R16

In das Austauschregister R16 wird der durch mehrfaches Probieren gefundene hexadezimale Wert 0x6C bzw. im zweiten Versuch 0x60 geladen. Anschließend wird dieser Wert in das Vergleichsregister OCR0 übertragen und die Taktzeit verändert.

 

Die letzten beiden Zeilen beschreiben eine Endlosschleife, in der sich die Programmausführung letztendlich bewegt.

Theorie - Stack, Unterprogramme und Interrupts

Ein Unterprogramm ist nichts anderes als ein Block von Programmzeilen, der sich über einen selbst gewählten Namen aufrufen lässt (RCALL <Name Unterprogramm>).

Der Programmzählerinhalt (PC) wird auf einem sogenannten STACK gesichert, der PC mit der Einsprungadresse für das Unterprogramm <Name Unterprogramm> geladen, das Unterprogramm ausgeführt und anschließend zum Hauptprogramm zurückgekehrt.

Die Rückkehr aus einem Unterprogramm wird mit dem OP-Code RET eingeleitet. Anschließend wird die auf dem Stack abgelegte Rücksprungadresse in den PC geladen und das Hauptprogramm weiter ausgeführt.

Unterbrechungen des Hauptprogramms treten auch bei sogenannten Interrupts auf. Sie können durch interne oder externe Signale ausgelöst werden. Der ATmega 8515 kann laut Datenblatt zwischen 17 verschiedenen Interrupts (incl. Reset) unterscheiden. Einige davon werden wir in den kommenden Übungen besprechen und praktisch ausprobieren.

Tritt ein Interrupt auf, dann wird

  1. der AVR sein Hauptprogramm in einer beliebigen Programmzeile beenden und den Inhalt des PC auf dem Stack retten.
  2. die Adresse einer Interrupt-Service-Routine (ISR) in den PC geladen.
  3. die ISR ausgeführt, bis das Programm auf die RETI-Anweisung (return from Interrupt) trifft.
  4. die Rücksprungadresse vom Stackgeholt und in den PC geladen.
  5. die Programmausführung im Hauptprogramm fortgesetzt.

Mit den OP-Codes CLI und SEI wird die Interruptfähigkeit eines Controllers ab- bzw. eingeschaltet.

Damit Interrupt, Interrupt Service Routine (ISR), TIMSK und TIFR keine inhaltlosen Fremdworte bleiben, sollte es hier weitergehen.

Druckversion | Sitemap
© Reinhard Rahner - Gettorf