Pfad: Home => AVR-DE => Anwendungen => AVR-D/A-Wandler => Sinusgeneratoren   This page in English: Flag EN Logo
DAC8 Tutorial zum Erlernen der AVR Assembler-Sprache von
AVR-Einchip-Prozessoren
von ATMEL anhand praktischer Beispiele.

Einfacher 8-Bit-Digital-zu-Analog-Wandler mit einem R/2R-Netzwerk
Sinusgeneratoren mit R/2R-Netzwerk

Sinusgeneratoren mit R/2R-Netzwerk

Wenn Du Festfrequenz- oder variable Sinusgeneratoren mit exakter Frequenzeinhaltung brauchst und wenn Du keine Dreiecke oder Sägezähne brauchst, wie bei dem hier, dann bist Du auf dieser Seite richtig.

Zugriff auf die Sinustabelle

Diese Seite bietet einen universellen Algorithmus, um Sinustabellen mit einem R/2R-Netzwerk abzuspielen. Er verwendet eine 256 Bytes lange Tabelle im Flash. Damit können Sinusse mit bis zu 3,676 kHz erzeugt werden, wenn auf jedes Byte der Tabelle zugegriffen wird. Greift man nur auf jeden zweiten/vierten/achten/sechzehnten/zweiunddreißigsten Wert zu, kriegt man das Zwei-, Vier-, Acht-, Sechzehn- bzw. 32-fache dieser Frequenz hin (mithin bis zu 118 kHz (aber erkauft durch eine geringere Auflösung der Einzelwerte).

Der Algorithmus funktioniert folgendermaßen. Er benötigt zwei Werte, die entweder als Konstante oder als variable Größe in einem Registerpaar vorhanden sein müssen:
  1. Die Auflösung (hier als Resolution oder mit Res bezeichnet): sie bestimmt die nächste Adresse in der Tabelle und kann 1/2/4/8/16 oder 32 groß sein.
  2. Die Verzögerung (hier als Delay oder mit Del bezeichnet): sie bestimmt mittels einer Verzögerungsschleife den Wartezeitraum zwischen der Ausgabe zweier Werte und kann zwischen 1 und 65536 groß sein.
Der Algorithmus mit zwei Konstanten geht so. (Der 16-Bit-Schleifenzähler ist dabei mit dem Registerpaar R25:R24 als rCntH:rCntL realisiert).

Sinusschleife:
  nop ; Kurze Verzoegerung, Takte +1 = 4 * cDel + 8 bei Sprung von unten
  ldi ZH,High(2*SineTable) ; Neustart Sinustabelle, +1 = 4 * cDel + 9
  ldi ZL,Low(2*SineTable) ; +1 = 4 * cDel + 10
Sinusschleife1:
  ; Naechster Tabellenwert, 4 * Del + 10
  lpm rmp,Z ; Lese Tabellenwert, +2 = 4 * cDel + 11
  out pR2RO,rmp ; 1 Takt = Referenzpunkt, +1 = 1 oder 4 * cDel + 13
  ; -----------------------------------------------
  ; Starte Verzoegerungsschleife
  ldi rCntH,High(cDel) ; Setze Schleifenzaehler, MSB, +1
  ldi rCntL,Low(cDel) ; dto., LSB, +1
Sinusschleife2:
  sbiw rCntL,1 ; Erniedrige Zaehler, +2 = 2 Takte Schleife
  brne Sinusschleife2 ; +2 wenn Sprung, +1 wenn nicht
  ; Verzoegerung = 1 + 1 + 4 * (cDel - 1) + 3
  ;  = 4 * cDel + 1
  ; -----------------------------------------------
  subi ZL,-Low(cResAdd) ; Naechste Tabellenadresse, +1 = 4 * cDel + 3
  sbci ZH,-High(cResAdd) ; +1 = 4 * cDel + 4
  cpi ZL,Low(2*SineTableEnd) ; +1 = 4 * cDel + 5
  breq Sinusschleife ; +2 wenn Sprung = 4 * cDel + 7
                ; +1 wenn nicht, = 4 * cDel + 6
  nop ; Eine zusaetzliche Verzoegerung, +1 = 4 * cDel + 7
  nop ; Zwei zusaetzliche Verzoegerungen, +1 = 4 * cDel + 8
	rjmp Sinusschleife1 ; +2 = 4 * cCnt + 10

Der gesamte Algorithmus braucht immer die gleiche Anzahl an Takten, egal ob das Lesen aus der Sinustabelle weitergeht oder ob die Tabelle an ihrem Ende angelangt ist und neu gestartet werden muss. Dass dies der Fall ist, wird mit den drei NOP erreicht. Die Anzahl Takte ist in jedem Fall
nTakte = 4 * cDel + 13

Bei einem cDel von 1 ist ein Durchlauf 17 Takte lang, bei einem cDel von 65.536 (cDel = 0) sind das 262.157 Takte. Bei einem Quarztakt von 16 MHz sind das zwischen 1,0625 µs und 16,3848125 ms pro Schleifendurchlauf. Der lange Verzögerungswert dauert bei 256 Sinus-Einzelschritten zu 4,194512 s Dauer pro Sinusschleife, entsprechend 0,2384 Hz. Das ist langsam genug, um auch exotische Ansprüchen zu genügen.

Die generierte Frequenz ergibt sich aus
fSinus = fTakt / Resolution / (4 * delay + 13)

, der Verzögerungswert kann aus der gewünschten Frequenz mit
cDel = (fTakt / Resolution / fSinus - 13) / 4

errechnet werden. In beiden Fällen ist die Resolution = 256 / cResAdd, wobei cResAdd der Schrittweite in der Tabelle entspricht (1, 2, 4 ... 32).
Der Algorithmus eignet sich für jeden AVR, der die 16-Bit-Instruktion ADIW implementiert (nur einige ganz uralte Typen haben das nicht). Wer keine festen sondern variable Grööen verwenden will, um einen einstellbaren Sinus zu machen, packt cDel und cResAdd in zwei Registerpaare und tauscht die vier Instruktionen aus (LDI gegen MOV und SUBI/SBCI gegen ADD und ADC).

Eine Frage ist noch zu klären: wieso beginnt der Algorithmus mit einem NOP? An sich ist der unnötig und kann ersatzlos entfallen (wenn man auch noch den zweiten NOP am Ende streicht). Dann müssen alle 13 in den obigen Formeln durch 12 ersetzt werden und alles stimmt wieder. Nun, wenn man zu Beginn jedes Sinusdurchganges noch mal testen möchte, ob eine Taste gedrückt wurde, dann braucht man eine Instruktion wie BRTS, um beim Vorliegen einer gesetzten T-Flagge aus der Schleife zu springen. Das ist beim Sinusgenerator mit dem ATmega324 so realisiert.

Die Grenzen von Operationsverstärkern

Operationsverstärker, die mit 5V betrieben werden, sind nur in bestimmten Bereichen linear. Die Aussteuerung des R/2R-Netzwerks, und damit die Sinustabelle, muss dies berücksichtigen.

Zum Ausmessen des Linearitätsbereiches habe ich einen ATmega324PA mit angeschlossenem R/2R-Netzwerk schrittweise angesteuert und die Ein- und Ausgangsspannungen bei den Operationsverstärkern 741 und CA3140 gemessen. Zusätzlich und nachträglich habe ich einen TL071 ausgemessen.

Ein- und Ausgangsspannung beim 741 Die gemessenen Spannungen beim 741 sind auf dem Tabellenblatt 741 in der LibreOffice-Calc-Datei hier enthalten.

Die Eingangsspannung (in rot, dick) weicht nur geringfügig vom Sollwert (schwarz, dünn) ab. Die Abweichung bleibt über den gesamten Aussteuerbereich etwa gleich groß. Die 1%-Widerstände des R/2R-Netzwerks arbeiten also sehr linear.

Der Ausgang des 741 folgt im Spannungsbereich unter 2,0V nicht der Spannung am Eingang. Die Spannung bleibt etwa gleich bei knapp 2V.

Ab zwei Volt folgt der Ausgang dem Eingang. Das ändert sich erst bei 4,3V, oberhalb geht der Ausgang in die Sättigung.

Ein- und Ausgangsspannung beim CA3140 Das ist die gleiche Kurve für den CA3140.Er arbeitet schon ab 0V Eingangsspannung linear. Dafür macht er bei etwa 3V schlapp.

Ein- und Ausgangsspannungen beim TL071 Das wäre die dritte Alternative: ein TL071. Ebenfalls ein CMOS-OP, aber ganz, ganz anders als der CA3140. Bei Eingangsspannungen unter 1,9 V schaltet der TL071 auf stur, oberhalb von 4,5 V macht er ebenfalls die Biege. Die Kennlinie ist dem 741 viel näher als dem CA3140.

Beim LM324 ist der Aussteuerbereich noch etwas umfangreicher als beim CA3140. Er kommt nicht ganz bis 0 Volt herab, kann dafür aber bis 3,7 V hinauf ausgesteuert werden.

Da es bei Sinusgeneratoren mit R/2R-Netzwerk gar nicht darauf ankommt, den gesamten Aussteuerbereich auszunutzen, kann man sich teure Spezial-ICs auch sparen, denn man kann den Aussteuerbereich einfach mit der Tabelle auf den linearen Bereich des Operationsverstärkers einengen und gut ist.

Die Sinus-Tabelle

Das Aussehen der Sinus-Tabelle Die Erzeugung der Sinustabelle braucht wegen des eingeschränkten Aussteuerbereichs des Operationsverstärkers etwas Aufmerksamkeit. Abhängig vom verwendeten Operationsverstärker darf dieser nicht von null bis auf fünf Volt ausgesteuert werden. Ich habe daher im Rechenblatt Sinustabelle in dem LibreOffice-Dokument hier eine untere und obere Grenze angesetzt, die mit den beiden Operationsverstärkern 741, CA3140 und TL071 kompatibel ist und eine Amplitude von exakt 0,8 (741) bzw. 1 (CA3140) oder 0,93 Veff am Ausgang liefert. Daher stößt der im Bild dargestellte Sinus beim 741 nicht unten und oben an, sondern bleibt etwas weg.

Wer einen 741 oder CA3140 verwendet, kann zwischen den beiden im Quellcode auswählen. Wer einen TL071 oder noch was anders braucht, Achtung bei Betriebsspannungen weit unterhalb von 5 V, der Operationsverstärker muss die auch noch abkönnen!

LC- oder RC-Filter für den Ausgang?

LC-Filter fuer Sinusgenerator Der 8-Bit-Sinusgenerator produziert Rechtecke mit einer Frequenz von 256 * fSinus, mit allen ungeraden Harmonischen. Die Variante mit jeweils jedem 32-sten Wert produziert 8 * fSinus. Um die und die Harmonischen auszufiltern, kann ein RC- oder, wie hier abgebildet, ein LC-Filter verwendet werden. Seine Dimensionierung kann mit einer weiteren Tabelle im oben referenzierten LibreOffice-Calc-Dokument berechnet werden.

RC-Filter bei 1 kHz RC-Filter bei 17 kHz Ich habe auch ein RC-Filter probiert. Das Rechensheet LC_Filter im LibreOffice-Calc-Dokument hier rechnet solche Filter. Die Dämpfung bei der Grundfrequenz (links, 1 kHz) hält sich in Grenzen, während die R/2R-Vielfachen und ihre Oberwellen (rechts, 17 kHz) schon ordentlich abgeschwächt werden.

Sinussignal roh Sinussignal gefiltert Links ist das Oszillogramm einer Sinusschwingung mit einer Auflösung von 7 Bits (128 Zahlenwerte) vor dem Filter zu sehen. Die eckige Form ist noch klar erkennbar.

Rechts dasselbe, aber nach dem RC-Filter. Sauber, aber etwas verformt.

Ausgangskondensator

Der Ausgangskondensator trennt die Gleichspannung ab, die nach dem R/2R-Netzwerk und dem Operationsverstärker noch auf dem Signal liegt, falls man den Sinus einem Analogverstärker zuführen will. Das gleiche Berechnungsblatt kann auch dazu verwendet werden, um den Ausgangskondensator Cout zu berechnen. Übliche Verstärker haben 10 kΩ Eingangsimpedanz.

Ein Sinusgenerator mit fester Frequenz

Wenn Dein Sinusgenerator eine feste Frequenz haben soll und keinen weiteren Schnickschnack, bist Du mit diesem hier mit einem ATtiny24 in einer schnuckeligen 14-Pin-DIL-Packung gut bedient. Alles was Du dafür brauchst, also auch die Software, gibt es auf dieser Seite.

Ein einstellbarer Sinusgenerator mit LCD

Wenn Du lieber einen Mercedes brauchst, der dann solltest Du das etwas größere Exemplar mit einem ATmega324 hier zum Nachbau auswählen.

Zum Anfang dieser Seite

©2020-2022 by http://www.avr-asm-tutorial.net