Ethernut Home Hardware Firmware Tools Download Community
 
Ethernuts  /  Ethernut 3  /  Timer  /  Flexibler Rechteckgenerator
Suche | Impressum | English

Flexibler Rechteckgenerator

Im vorangegangenen Artikel wurde beschrieben, wie man mit Hilfe weniger Befehle eine Rechteckschwingung an einem freien Pin des Ethernut 3 Expansionports erzeugen kann. Die erzeugte Schwingung war aber nur innerhalb enger Grenzen zu variieren. Im folgenden werden diese Möglichkeiten erweitert.

Tast-/Pausenverhältnis ändern

Mit

outr(TC1_CMR, TC_WAVE | TC_ACPA_TOGGLE_OUTPUT | TC_CLKS_MCK2);
outr(TC1_RA, 0x8000);

wird der Ausgang jedesmal umgeschaltet, wenn der Zähler den Wert in TC1_RA erreicht.

Nimmt man einen zweiten Vergleichswert zu Hilfe, lässt sich der Ausgang beim ersten Wert (in TC1_RA) auf High und beim zweiten Wert (in TC1_RC) wieder zurück auf Low schalten.

outr(TC1_CMR, TC_WAVE | TC_ACPA_SET_OUTPUT | TC_ACPC_CLEAR_OUTPUT | TC_CLKS_MCK2);
outr(TC1_RA, 0x4000);
outr(TC1_RC, 0x8000);

Das Bit TC_ACPA_SET_OUTPUT im Channel Mode Register bestimmt, dass der Ausgang auf High gesetzt wird, wenn der Zähler den Wert in TC1_RA erreicht. Entsprechend sorgt TC_ACPC_CLEAR_OUTPUT dafür, dass bei Erreichen des Werts in TC1_RC der Ausgang auf Low geschaltet wird.

Durch Variation der beiden Werte lässt sich das Verhältnis von Low und High in sehr feinen Stufen einstellen.

Frequenz ändern

Durch zusätzliches Setzen von TC_CPCTRG im Channel Mode Register wird erreicht, dass der Zähler nicht mehr bis zum Überlauf durchläuft, sondern beim Erreichen des Werts in TC1_RC wieder von Null beginnt.

Je kleiner der Wert in TC1_RC gewählt wird, desto höher die ausgegebene Frequenz. Die niedrigste Frequenz (etwa 1,1 HZ) lässt sich mit

outr(TC1_CMR, TC_WAVE | TC_CPCTRG | TC_ACPA_SET_OUTPUT | TC_ACPC_CLEAR_OUTPUT | TC_CLKS_MCK1024);
outr(TC1_RA, 0x8000);
outr(TC1_RC, 0xFFFF);

und die höchste Frequenz (etwa 18,5 MHz) mit

outr(TC1_CMR, TC_WAVE | TC_CPCTRG | TC_ACPA_SET_OUTPUT | TC_ACPC_CLEAR_OUTPUT | TC_CLKS_MCK2);
outr(TC1_RA, 1);
outr(TC1_RC, 2);

erzeugen. Es wird aber auch schnell klar, dass die Variationsmöglichkeit des Puls-/Pausenverhältnisses mit zunehmender Frequenz abnimmt. Bei der höchsten Frequenz liegt der Wert fest bei 50%.

Zusätzlicher Ausgang

Wird ein weiterer Ausgang benötigt, lässt sich natürlich der dritte Timer, TC2 verwenden. Insbesondere bei PWM-Steuerungen wird ein weiteren Ausgang mit gleicher Frequenz benötigt, lediglich das Tastverhältnis soll variieren. Dies lässt sich mit Ethernut 3 realisieren, ohne einen zusätzlichen Timer in Anspruch zu nehmen. Statt dessen wird ein weiterer Vergleichswert in TC1_RB zu Hilfe genommen. Dieser hat die gleiche Funktion wie der Wert in TC1_RA, wirkt aber auf einen eigenen Pin, nämlich Port Bit P5/TIOB1.

Damit lässt sich die Frequenz über TC1_RC, das Tastverhältnis am Ausgang P4 mit TC1_RA und das Tastverhältnis am Ausgang P5 mit TC1_RB einstellen.

Allerdings ist TIOB1 im Grundzustand als Ereigniseingang konfiguriert und wird erst dann zum Ausgang, wenn eines der internen Signale XC0, XC1 oder XC2 als Ereigniseingang konfiguriert wird. Das ist grundsätzlich kein Problem, da ohne Festlegung der Flanke EEVTEDG kein ungewolltes Ereignis ausgelöst wird. Die Konfiguration des Timers für die zwei Ausgänge P4/TIOA1 und P4/TIOB1 sieht dann so aus:

outr(TC1_CMR, TC_WAVE | TC_EEVT_XC0 | TC_BCPB_CLEAR_OUTPUT | TC_ACPA_CLEAR_OUTPUT |
              TC_BCPC_SET_OUTPUT | TC_ACPC_SET_OUTPUT | TC_CPCTRG | TC_CLKS_MCK2);

Logischerweise muss für beide Pins der PIO-Modus abgeschaltet werden:

outr(PIO_PDR, _BV(4) | _BV(5));

Die folgende Funktion initialisiert Timer 1 für beide Ausgänge P4 und P5, wobei der Parameter cs den Teilerfaktor des CPU-Takts und der Parameter fs den maximalen Zählerwert bestimmt. Zusammen ergeben diese die Frequenz der Rechteckschwingung.

void InitWave1AB(uint32_t cs, uint16_t fs)
{
    outr(PIO_PDR, _BV(4) | _BV(5));
    outr(TC1_CMR, TC_WAVE | TC_EEVT_XC0 | TC_BCPB_CLEAR_OUTPUT | TC_ACPA_CLEAR_OUTPUT |
                  TC_BCPC_SET_OUTPUT | TC_ACPC_SET_OUTPUT | TC_CPCTRG | cs);
    outr(TC1_RC, fs);
    PulseWave1A(50);
    PulseWave1B(50);
    outr(TC1_CCR, TC_CLKEN);
    outr(TC1_CCR, TC_SWTRG);
}

Die Pulsdauer lässt sich dann mit den beiden folgenden Funktion getrennt für jeden Ausgang einstellen. Man beachte, dass die Funktionen bereits in der Initialisierungsroutine aufgerufen werden, um ein Verhältnis von 50% einzustellen. Dies ist ggf. auf die zu realisierende Anwendung zu anzupassen.

void PulseWave1A(int percent)
{
    outr(TC1_RA, ((uint16_t) inr(TC1_RC) * percent) / 100);
}

void PulseWave1B(int percent)
{
    outr(TC1_RB, ((uint16_t) inr(TC1_RC) * percent) / 100);
}

Die folgende Schleife ruft die Initialisierung auf und variiert das Puls-/Pausverhältnis der beiden Ausgänge kontinuierlich zwischen 1 und 99%. Sie eignet sich z.B. zur Helligkeitssteuerung von LEDs.

InitWave1AB(TC_CLKS_MCK2, 1000);
for (;;) {
    int p;
    for (p = 1; p < 100; p++) {
        PulseWave1A(p);
        PulseWave1B(100 - p);
        NutSleep(10);
    }
    for (p = 1; p < 100; p++) {
        PulseWave1A(100 - p);
        PulseWave1B(p);
        NutSleep(10);
    }
}