Timer und Counter: Projekt-2
Idee
BearbeitenMit einer Kombination aus einem Widerstand und einem Kondensator können wir ein PWM-Signal in ein quasi analoges Signal umwandeln. Eine solche Kombination aus einem Widerstand und einem Kondensator wird auch RC-Glied genannt. In der Konfiguration, in der wir das RC-Glied verwenden, wird es auch Tiefpass genannt.
Mit zwei Timern können wir ein (quasi-)analoges Sinussignal erzeugen.
Für die Erzeugung des Ausgabepegels wird ein PWM-Signal mit einer festen Frequenz verwendet. Das Signal wird dann mit einem Tiefpass gefiltert. Der (quasi-)analoge Ausgabepegel des Signals kann über den Tastgrad festgelegt werden.
Die zeitliche Veränderung der Ausgabewerte wird über einen zweiten Timer gesteuert.
Anstelle einer Berechnung können wir den jeweils gewünschten Ausgabepegel einer Tabelle mit vorberechneten Werten entnehmen.
Konfiguration Timer 0
BearbeitenFür die Ausgabe der gewünschten analogen Pegel wird der Timer TC0 verwendet. Die Ausgabe selbst soll über den Pin OC1A erfolgen. Zu diesem Zweck muss der Timer aktiviert werden und der gewünschte Pin als Ausgabepin konfiguriert werden.
t0_a_out();
t0_power(1);
Das Produkt aus der Freqzenz des Timers und der bit-Tiefe der Samples legt eine obere Grenze für die maximale Samplerate fest, die wir erreichen können. Der Timer soll aus diesem Grund mit der höchstmöglichen Frequenz arbeiten. Als Taktquelle wird daher der CPU Takt mit Prescaler Wert 1 direkt abgegriffen. Die Taktquelle muss hierfür mit dem Wert 1 angegeben werden.
t0_cs(1);
Die bit-Tiefe der Samples, also die Stufen, mit der die Amplitude aufgelöst werden kann, entsprechen dem maximalen Zählerwert. Für 8-bit Auflösung muss der maximalen Wert 0xFF betragen. Der Timer TC0 wird deshalb im Modus 0x3 (Fast PWM Mode mit Maximalwert 0xFF) betrieben. Damit der Pin bei Erreichen des Zählestands OCRA auf HIGH gesetzt wird und bei Erreichen des Zählerstands 0 auf LOW zurückgesetzt wird, muss der Vergleichsmodus 3 gewählt werden.
t0_wgm(3);
t0_coma(3);
Konfiguration Timer 2
BearbeitenDie Aufgabe von Timer 2 ist es, im Takt der gewünschten Samplerate den gewünschten analogen Ausgabewert in das OCRA Register von Timer 0 zu schreiben. Um überhaupt eine Aufgabe übernehmen zu können, muss er zunächst aktiviert werden. Er muss aktiviert werden
t2_power(1);
Um eine Samplereate von 31.25 kHz zu erreichen, kann der Timer im Modus 0x7 (Fast PWM Mode mit Maximalwert OCRA) betrieben werden.
t2_wgm(7)
Für die Taktquelle und den Maximalwert des Timers kann die Kombination CLK/256 und OCRA=1 gewählt werden. Der Wert, der bei Timer 2 für einen Takt von CLK/256 angegeben werden muss ist 6.
t2_cs(6);
t2_ocra(1);
Bei Erreichen des Maximalen Zählerstands muss ein Interrupt ausgelöst werden, damit der nächste Samplewert in das OCRA Register von Timer 0 geschrieben wird.
t2_interrupt_ocra();
Interrupt Service Routine
BearbeitenDie Interrupt Service Routine soll den nächsten Samplewert aus der Sinus-Tabelle ausgeben.
ISR(TIMER2_COMPA_vect) {
t0_ocra(sin_table3[i]);
++i;
if (i >= 71)
i = 0;
}
Sinus-Tabelle
BearbeitenUm die Werte nicht berechnen zu müssen, werden sie in einer Tabelle vorgegeben. Um bei der gewählten Samplerate von 31.25 kHz ein Signal von ungefähr 440 Hz ausgzugeben muss die Tabelle 71 Werte enthalten.
uint8_t sin_table3[] = {
128, 139, 150, 161, 172, 182, 192, 202, 210, 219,
226, 233, 239, 244, 248, 251, 253, 255, 255, 254,
252, 250, 246, 241, 236, 230, 223, 215, 206, 197,
187, 177, 166, 155, 144, 133, 122, 111, 100, 89,
78, 68, 58, 49, 40, 32, 25, 19, 14, 9,
5, 3, 1, 0, 0, 2, 4, 7, 11, 16,
22, 29, 36, 45, 53, 63, 73, 83, 94, 105,
116
};
Quellcode
BearbeitenAbschließend der vollständige Quellcode des Projekts. Um ein lauffähiges Programm daraus zu machen nicht vergessen ihn mit den Timerfunktionen zu linken.
#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include "timer.h"
uint8_t wave_table[] = {
128, 139, 150, 161, 172, 182, 192, 202, 210, 219,
226, 233, 239, 244, 248, 251, 253, 255, 255, 254,
252, 250, 246, 241, 236, 230, 223, 215, 206, 197,
187, 177, 166, 155, 144, 133, 122, 111, 100, 89,
78, 68, 58, 49, 40, 32, 25, 19, 14, 9,
5, 3, 1, 0, 0, 2, 4, 7, 11, 16,
22, 29, 36, 45, 53, 63, 73, 83, 94, 105,
116
};
// Forward Declarations
void setup(void);
void loop(void);
#define sbi(port, bit) (port) |= (uint8_t) (1 << (bit))
#define cbi(port, bit) (port) &= (uint8_t) ~(1 << (bit))
void setup() {
// TIMER 0:
t0_power(1); // disable power reduction
t0_cs(1); // clock source 1: clk/1
t0_wgm(3); // wave generation mode 3: fast pwm max=0xFF
t0_coma(3); // output compare A mode 3: B:clear M:set
t0_a_out();
// TIMER 2:
t2_power(1); // disable power reduction
t2_cs(2); // clock source 6: clk/8
t2_wgm(7); // wave generation mode 7: fast pwm max=OCR2A
t2_ocra(63); // => 31.25 kHz
t2_interrupt_ocra(); //
sei();
}
uint8_t i;
ISR(TIMER2_COMPA_vect) {
t0_ocra(wave_table[i]);
++i;
if (i >= 71)
i = 0;
}
void loop() {
// do nothing
}
#ifndef ARDUINO
int main(void) {
setup();
while(1)
loop();
}
#endif
Ergebnis
BearbeitenFußnoten
Bearbeiten