Counters/Timers

Counter, welche man auch in einigen Fällen Taktgeber nennen kann, sind einer der wichtigsten Unterfunktionen eines Microcontrollers. Diese ermöglichen es Prozesse exakt zu timen, Signale zu generieren und Ereignisse zu zählen. Ein Zähler konvertiert die Nummer der Inputzyklen in einen binären Wert mit Hilfe eines Arrays an Triggern. Die maximale Nummer der gezählten Zyklen hängt von der Länge des Arrays ab und diese ist markiert von der Länge des binären Codes. Der AVR hat 8- und 16- Bit Counter. Wenn ein Timer seinen maximalen Wert erreicht hat(255 bei 8-Bit und 65535 bei 16-Bit), generiert der nächste Zyklus ein Overflow und der Zähler macht ein Reset auf 0. Das Taktsignal eines Counters kann vom Taktsignal des Microcontrollers kommen und in diesem Fall ist es möglich den Wert mit einem Prescaler herunter zusetzen. Einige AVRs haben einen internen unabhängigen Taktgeber, welcher mit einem Frequenzmultiplikator modifiziert werden kann, um schneller zu laufen. Counter unterscheiden sich auch in Applikationsfällen und Arbeitsmodi.

Counter's default mode

Im Default-Modus macht ein Counter nichts anderes, als fortlaufend Nummer zu zählen Sein wert kann natürlich jederzeit ausgelesen, und von einem Programm verändert werden. Die einzige zusätzliche Funktion im Default-Modus ist, dass er ein Interrupt beim Counter-Overflow verursacht. Der Default-Modus wird normalerweise benutzt, um eine Sektion eines Programms in bestimmten Intervallen laufen zulassen.

Example

Aufgabe: Ein 8 MHz ATmega128 soll einen Interrupt alle 10ms (100 Hz) auslösen. Dafür ist der 8-Bit Counter 0 brauchbar.

#include <avr/interrupt.h>
 
ISR(TIMER0_OVF_vect)
{
	// Gibt den Counter einen Wert,
	// dass der nächste Overflow in 10Hz passiert.
	// Formel: 256 - 8 MHz / 1024 / 100 Hz = 177,785 = ~178
	TCNT0 = 178;
}
 
int main()
{
	// Um den ersten Interrupt in 10ms auszulösen,
	// muss der Counter initialisiert werden.
	TCNT0 = 178;
 
	// Prescaler Wert 1024
	TCCR0 = 0x07;
 
	// Erlaubt Overflow Interrupts
	TIMSK |= (1 << TOIE0);
 
	// Erlaubt global Interrupts
	sei();
 
	// Endlosschleife
	while (1) continue;
}

Der Counter in diesem Beispiel wird den Interrupt nicht in exakt 10 ms generieren, dafür müsste man dem Counter einen Dezimalwert geben, das ist aber nicht möglich. Um einen präzisen Intervall zwischen den Interrupts zu verwirklichen, wird sowohl der Prescaler Wert, als auch der Wert des Counter müssen so ausgesucht werden, dass ihre Division in einer exakten Nummer endet. Das ist nicht immer möglich, vor allem mit 8-Bit Countern, da ihr Wertebereich sehr klein ist. Um einen präziseren oder größeren Intervall zu bekommen, kann ein 16-Bit Counter gewählt werden.

Externer Taktgeber

Es ist auch möglich einen externen Taktgeber als das Taktsignals eines Counters zu nutzen. Der AVR hat einen Pin “Tn” dafür. Das n ist Platzhalter für die Nummer des Counters. Das Externe Taktsignal und die Polarität kann man im Prescaler Register auswählen.

Timing events

Da die Counter zeitlich abgestimmte Operationen ermöglichen, haben komplexe AVR Microcontroller eine Option auf Hardware-Level für zeit spezifische Ereignisse. Der Teil des Counter heißt “Input Capture Unit”. Es gibt eine Auswahl zwischen zwei Ereignissen: Die logische Änderung eines Werts an einem speziellen Input Pins oder im Wert des analogen Komparator Ergebnisses. Wenn das gewählte Ereignis eintritt wird der Wert des Counters in ein spezielles Register geschrieben, welches man jederzeit auslesen kann. Wenn das Event länger ist als die Overflowzeit des Counter, muss das Programm die Overflow mit zählen und beim Endergebnis mitberechnen.

Example

Aufgabe: Miss die Frequenz eines Externen 122Hz - 100kHz logischen Rechtecksignals mit einem 8MHz ATmega128. Die Messung soll mit einer 1 Hz-Präzision erfolgen. Das Programm nutzt einen 16-Bit Counter mit einer Input Capture Unit.

#include <avr/interrupt.h>
 
unsigned long frequency;
 
// Interrupt für das Ereignis
ISR(TIMER1_CAPT_vect)
{
	// Counter to 0
	TCNT1 = 0;
 
	// Das Ergebnis ist nur gültig wenn der Counter
	// noch kein Overflow hatte.
	if (!(TIFR & (1 << TOV1)))
	{
		// Berechnen der Frequenz von der Periode
		frequency = (unsigned long)8000000 /
		            (unsigned long)ICR1;
	}
	else
	{
		// Frequenz ist weniger als 122Hz
		frequency = 0;
 
		// Setzt die Counter Overflow Flagge auf 0
		TIFR &= ~(1 << TOV1);
	}
}
 
int main()
{
	// Registriert eine "rising front", Prescaler Wert auf 1
	TCCR1B = (1 << ICES1) | (1 << CS10);
 
	// Erlaubt Ereignis-Interrupts
	TIMSK = (1 << TICIE1);
 
	// Erlaubt global Interrupts
	sei();
 
	// Endlosschleife
	while (1) continue;
}

Das Programm löst jedes mal bei einer “Rising Front” im externen Signal einen Interrupt aus. Während des Interrupts, überprüft das Programm den Counter auf einen Overflow - dass kann passieren wenn die Frequenz des Signals unter 122Hz (8 MHz / 216) ist und in so einem Fall spiegelt der Wert des Counters die echte Periode nicht mehr wieder. Die Frequenz wird berechnent indem man mit 32-Bit Nummern die Inverse der Periode bekommt. Als erstes setzt man den Counter auf 0, weil der Timer mit dem gleichen Taktsignal wie der Prozessors arbeitet und jede Befehlsausführung nach dem externen Event die zu messende Periode verkürzt und damit das Ergebnis verfälscht. Die maixmale gemessene Frequenz wird durch die Zeit die das Interrupt benötigt limitiert.

Events erfassen und die Zeit registrieren die es brauchte damit das Event auftrat kann auf Software Level abgehandelt werden. Es ist möglich einen externen oder andere Interrupts zu nutzen und den Wert des Counters während des Events auszulesen. Das Erfassen auf Hardwarelevel wird genutzt um unabhängig vom Hauptprogramm zu arbeiten und sehr kurze (oder häufige) Events zu timen.

Taktsignal generieren

Komplexere Counter können ein Taktsignal generieren, zusätzlich zum Timen eines Signals. Dafür hat der Counter eine “Output Compare Unit” und eine “Compare Match Output Unit”. Die Output-Compare-Unit hat Register mit der gleichen Bit-Weite wie der Counter und die Werte dieser Register werden mit den Werten des Counters verglichen während dieser läuft. Ein Interrupt kann generiert werden und spezielle Pin Werte können geändert werden wenn der Counter Wert gleich der des Compare Unit Registers ist. In diesem Moment kann ein Pin entweder High oder low gesetzt oder invertiert werden. Das Signal wird durch Änderungen im Wert des Output-Pins generiert.

In einigen Signal-generierenden Modi, kann der maximale Wert des Counters verändert werden. Die physikalische Größe bleibt die gleiche, aber ein Vergleichsregister wird benutzt um den Counter bei einem bestimmten Wert zu resetten. Das vorherige Beispiel kann auch mit dieser Methode gelöst werden, aber die Funktion ist eher für das Ändern der Periode des Signals gedacht. Zusätzlich kann ein Counter so eingestellt werden, dass er mit auf- oder absteigenden Werten arbeitet.

Der Counter und die Signal-generierenden Modi die diese nutzten sind einer der komplexesten Peripheriemodule in einem AVR. Über jeden hier zu schreiben würde eine große Zeitverschwendung sein, normalerweise gibt es keinen Grund alles zu wissen, um sie zu nutzen. Der folgende Absatz beschreibt einer der üblichen PWM Signale in den Robotics. Der Rest kann den AVR Dokumentationen entnommen werden.

Pulse Width Modulation

Pulsweitenmodulation (PWM) ist ein Typ eines Signals, wo die Frequenz und die Periode(normalerweise) konstant sind, aber die Länge der Halb-Periode ändert sich. PWM Signale werden benutzt um elektromechanische, optische und andere Geräte zu kontrollieren. Zum Beispiel, ein Servomotor nutzt ein ein 50Hz PWM Signal und haben einen hohe Halbperiode von 1 - 2 ms.

Example

Aufgabe: Generiere mit einem 8Mhz ATmega128, zwei Geschwindigkeits-regulierende Servomotor Signale. Nutze Pin PB5 (OC1A) um eine Pulsweite von 1ms zu generieren und Pin PB6 (OC1B) um eine Pulsweite von 2ms zu generieren.

#include <avr/io.h>
 
int main()
{
	// Set pins as outputs
	DDRB |= (1 << PIN5) | (1 << PIN6);
 
	// Set outputs A and B low for comparison,
	// "Fast PWM" mode, prescaler value 8
	TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11);
	TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11);
 
	// Maximum value of the counter. Formula:
	//   TOP = 8 MHz / 8 / 50 Hz
	ICR1 = 20000;
 
	// Half-period of the first motor is 1 ms, and second 2 ms
	OCR1A = 1000;
	OCR1B = 2000;
 
	// Endless loop
	while (1) continue;
}
de/avr/timers.txt · Last modified: 2020/07/20 09:00 by 127.0.0.1
CC Attribution-Share Alike 4.0 International
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0