Pfad:
Home =>
AVR-DE =>
Anwendungen =>
AVR-D/A-Wandler => Sinusgeneratoren
This page in English:
 |
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:
- 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.
- 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.
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.
Das ist die gleiche Kurve für den CA3140.Er arbeitet schon ab 0V
Eingangsspannung linear. Dafür macht er bei etwa 3V schlapp.
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
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,
- ändert die drei grün hinterlegten Einträge nach seinem
Gusto, und
- kopiert die erzeugte Sinustabelle mit Strg-C, und
- pastet die kopierten Werte in seinen Quellcode, und
- assembliert den Quellcode.
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?
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.
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.
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
- die erzeugte Frequenz auf einer kleinen 1x8-Zeichen-LCD in Hz
anzeigt,
- mit zwei Potenziometern die Auflösung und die
Verzögerung einstellbar macht, und
- der die Frequenz nur auf Tastendruck wechselt,
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