Ein Rechteck Signal mit einstellbarer Frequenz ausgeben

Nachdem ich nun die Obergrenze des Rechtecksignals getestet habe, will ich mir jetzt mal die unteren Frequenzen ansehen. Da bieten sich die delay(ms) und delayMicroseconds(us) Funktionen an.

Ein Prozessortakt dauert bei 16 MHz Takt 62,5 Nano Sekunden. Meine while schleife mit den nops und Portausgaben benötigt 6 Takte. delayMicroseconds() lässt sich in 1 Mikrosekunden Stufen einstellen bis hin zu 16383, was hier einer Frequenz von ca. 31 Hz entspricht.

delayMicroseconds()

Eine genauere Betrachtung von delayMicroseconds() wird mit diesem Programm vorgenommen.

for(int i=0;i< 100;i++) {
  t1=micros();
  delayMicroseconds(i);
  t2=micros();
  Serial.print(i);
  Serial.print(";");
  Serial.println(t2-t1);
  delay(10);
}

Ich erhalte folgende Ausgaben.

delayMicroseconds Grafik

delayMicroseconds()

Die Eingabewerte i für delayMicrosecond(i) sind blau eingetragen und die Zeitdifferenzmessung mit micros() ist rot eingetragen. Wenn eine micros() Anweisung 3 Mikrosekunden braucht, ergibt sich die gelbe Linie (Eingabe + 3).
Die Funktion delayMicroseconds() arbeitet nicht linear und wir sehen hier 6 bis zu 8 Mikrosekunden Abweichung der roten Kurve gegenüber der gelben Ideallinie.

Um den Frequenzfehler kleiner 1% zu halten, sollte die Funktion nicht unter 800 Mikro Sekunden eingesetzt werden. Das entspricht einer Frequenz von 625 Hz.

Ergebnis: Damit ist delayMicroseconds() für Frequenzen von 31Hz bis 625 Hz einsetzbar.

Eine eigene for schleife

long d,l,i;
d=round(i/0.0625);
for(l=0;l

Delay For Schleife

Diese for Schleife dauert (i*11,08)+3 Mikro Sekunden und ist von 14 bis 800 Mikrosekunden einsetzbar. Das entspricht einer Frequenz von 35,7 kHz bis runter zu 625 Hz mit einem Fehler kleiner als 1%. Die einstellbare minimale Schrittweite der Frequenzen liegt bei ca. 2 x 11 = 22 Mikrosekunden.

Ergebnis: Damit ist for(,,,) für Frequenzen von 625Hz bis 35 kHz einsetzbar

Eine eigene do while Schleife

long d,l,i;
d=round(i/0.0625); 
do {d--;} while(d>0);

Gibt dasselbe Ergebnis wie die oben untersuchte eigene for Schleife.

Mit dem Timer Interrupt

Also teste ich den Timer Interrupt, der immer ausgelöst wird wenn der Timer überläuft. Die Library TimerOne wird installiert und in das Programm eingebunden. Hier ist die Beschreibung.

Der Timer Interrupt hat zusätzlich den großen Vorteil, dass er unabhängig vom Hauptprogramm arbeitet. Man muss sich darum dann nicht mehr kümmern, ausser in zeitkritischen Routinen.

Bei jedem Timer Interrupt wird diese Funktion aufgerufen. Die DA-Wandler Eingänge werden umschaltet. In status steckt der letzte Wert.

void timerOutput() {
// Timer Ausgabe  
  if(status==1) {
    PORTB=B00111100;
    status=0;
  }
  else {
    PORTB=B00000000;
    status=1;
  }
}

Dann kommt dieses Programm zum Einsatz.

#include 
long mikrosekunden=500;
volatile byte status = 0;

void setup() {
  Serial.begin(38400);
  pinMode(10,OUTPUT);
  pinMode(11,OUTPUT);
  pinMode(12,OUTPUT);
  pinMode(13,OUTPUT);
  Timer1.initialize(mikrosekunden);
  Timer1.attachInterrupt(timerOutput);
}

void loop() {
int f;

  while(1) {
    for(f=1000;f< 30000;f*=2) {
      mikrosekunden = round(500000/f);
      timerNeu();
      delay(3000);
    }
  }
}

void timerNeu() {
  Timer1.detachInterrupt(); 
  Timer1.initialize(mikrosekunden);
  Timer1.attachInterrupt(timerOutput);
}

Im setup wird der Timerinterrupt eingestellt. Danach kann er vom Programm geändert werden indem die Variable mikrosekunden gesetzt wird und timerNeu() aufgerufen wird. Das macht die for Schleife und verdoppelt die Frequenz f dabei jedes mal.

Das ganze funktioniert für die Frequenzen von 1 Hz bis 10000 Hz. Auch noch etwas höher bis max. 83.000 Hz, hier aber nur in grossen Schrittweiten.

Da immer 500.000/f geteilt wird und das ganze auf integer gerundet wird, entstehen oben Lücken in den Frequenzen, die nicht ausgegeben werden können.

500.000 / 10.000 Hz = 50 uSek
nächster Wert 500.000 / 9.804 Hz = 51 uSek
nächster Wert 500.000 / 9.615 Hz = 52 uSek

Das sind 6 mögliche Frequenzen von 9 – 10 kHz, oder anders herum ausgedrückt Frequenzlücken von ca 200 Hz.

500.000 / 1.000 Hz = 500 uSek
nächster Wert 500.000 / 998 Hz = 501 uSek
Frequenzlücken von 2 Hz

500.000 / 100 Hz = 5000 uSek
nächster Wert 500.000 / 99,98 Hz = 5001 uSek
Frequenzlücken von 0,02 Hz

Ergebnis: Damit ist der Timer Interrupt für Frequenzen von unter 1Hz bis 1 kHz einsetzbar.

Zusammenfassung
Von 31Hz bis 625 Hz ist delayMicroseconds verwendbar.

Von unter 1 Hz bis 1 kHz oder sogar 10kHz, je nach Anforderung, ist die Timer Interrupt Routine gut zu verwenden.

Von 1 kHz bis 35 kHz sind die for und die while Schleife einsetzbar.

Bei höheren Frequenzen bis 2 MHz bleiben dann noch die „nops“ aus dem letzten Beitrag. Da heisst es dann ordentlich copy-and-paste oder auch eine Schleife einfügen.

von Matthias Busse

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.