Pfad:
Home =>
AVR-DE =>
Anwendungen =>
Sinusgeneratoren => Sinusgenerator m324
This page in English:
 |
Tutorial zum Erlernen der AVR Assembler-Sprache von
AVR-Einchip-Prozessoren
von ATMEL anhand praktischer Beispiele.
Sinusgeneratoren mit einem R/2R-Netzwerk
Sinusgenerator mit ATmega324 |
Sinusgenerator mit ATmega324
Wenn Du einen Sinusgenerator mit in weiten Grenzen einstellbaren Frequenzen
brauchst, bist Du hier richtig. Er hat die folgenden Eigenschaften:
- 1x8-LCD für Anzeige der Frequenz in Hz,
- zwei Potentiometer für die separate Einstellung der Auflösung
und der Zeitverzögerung = maximale Flexibilität,
- aktive Taste für den Update der vorgenommenen Einstellungen, keine
automatische Verstellung,
- 8-Bit-R/2R-Netzwerk,
- wählbarer und konfigurierbarer Quarz als Taktgeber,
- entweder 256, 128, 64, 32, 16 oder 8 Auflösungsschritte,
- großer 16-Bit-Verzögerungszähler, für sehr niedrige
Frequenzen geeignet,
- sehr schneller Algorithmus (Minimum Zyklendauer = 17 Takte).
Hardware
Das hier ist das Schaltbild.
Der Generator hat
- einen Quarz für einen exakten Takt, hier: 16 MHz,
- das übliche R/2R-Netzwerk mit 8 Bit Auflösung mit 10 und
20kΩ, angeschlossen an Port D,
- einen Trennverstärker, der das R/2R-Netzwerk vor Belastung schützt
und genügend Ausgangsleistung liefert - entweder ein 741, ein CA3140 oder
ein LM357
Operationsverstärker (ein CA3140 oder TL071 täte es auch, würde
aber etwas weniger Ausgangsleistung liefern als ein 741),
- ein LC-Tiefpass-Filter, das die restlichen Rechtecke ausfiltert, hier mit
L=3,3mH und C=150nF,
- ein Standard-6-Pin-ISP-Interface, um den Controller in der
fertigen Schaltung programmieren zu können.
Der ATmega324 wurde deswegen ausgewählt, weil er
- zwei komplette Ports mit je 8 Bits hat, einen für den
bidirektionalen LCD-Datenbus und einen für das R/2R-Netzwerk,
und
- die Möglichkeit für den Quarzbetrieb hat, und
- er AD-Wandler-Eingänge bereithält, und
- weil er an geeigneter Stelle auch einen externen INT-Pin hat,
dessen Interrupt auf fallende Flanken eingestellt werden kann
(INT2).
Der PA-Untertyp des ATmega324 wurde wegen seines niedrigen Preises
ausgewählt, zumindest bei meinem bevorzugten Händler.
Das LC-Filter filtert die rechteckigen Reste des R/2R-Netzwerks. Es
wird so ausgelegt, dass es bei der niedrigsten zu erzeugenden Frequenz
wirksam wird und auch die höchste Frequenz noch ungedämpft
passieren kann. Wer die große mögliche Bandbreite des
Generators nutzen will, muss mehrere Filter umschaltbar anordnen.
Platinenlayout
Das ist das Platinenlayout der Schaltung auf 80x100 mm. Dargestellt
sind 2:1 verkleinerte Kopien der Originale. Originalgröße
kriegt man durch Klicken auf die Bilder, Download mit Rechtsklick und
Speichern unter ....
Die roten Punkte im Bestückungsplan sind 0,8mm-Bohrungen, die
violetten sind 1,0mm. Statt der 10µH-Drossel im Schaltplan habe
ich 22µH verwendet. Mein L ist 3,3mH, mein C ist 150nF. Das Cout
habe ich mit 470nF bestückt.
Wer Linux kann, kann mit tgif die Originale bearbeiten. Die
Kupferseite und alle Komponenten mit Farblayern sind
hier, der Bestückungsplan
hier verfügbar.
Achtung! In der ersten Version des Schaltplans waren die
LCD-Kontrollanschlüsse anders belegt.
Konfiguration
Wenn der Controller startet, startet er mit der im Programm vorgegebenen
Frequenz. Wenn dann die Taste an INT2 gedrückt wird, bestimmen die
zwei Potis an ADC0 und ADC1 die Frequenz.
Frequenz beim Start
Um die Startfrequenz festzulegen, gibt es folgende Möglichkeiten:
- die Frequenz in mHz wird als Konstante fest vorgegeben, der
Assembler ermittelt daraus die Auflösung cRes und die
Verzögerungskonstante cDel, oder
- die Frequenz und die Auflösung cResol werden fest
vorgegeben (256/128/64/32/16/8), daraus ermittelt der Assembler
die Verzögerungskonstante cDel, oder
- die Frequenz und die Verzögerungskonstante (1 bis 65536,
65536 entspricht cDel=0!), wofür der Assembler dann die
Auflösung ermittelt, oder
- die Auflösung cResol und die Verzögerungskonstante
cDelay werden festgelegt, woraus der Assembler dann die Frequenz
errechnet.
Das heißt: aus den gewünschten Kombinationen im Kopf des
Quellcodes, im Abschnitt Einstellbar bestimmt der Assembler
die erzeugte Frequenz in mHz. Unterschiede zwischen der gewünschten
Frequenz und der effektiven können daraufhin beurteilt werden,
ob die Abweichung noch akzeptabel ist. Dazu muss man nur im Listing des
Assemblers an dessen Ende im Abschnitt Symbols nachsehen,
was da als Frequenz herauskommt. Das kriegt man aber nur, wenn man
mit gavrasm oder mit avr_sim assembliert, andere Assembler bieten
diese Informationen nicht an. Hier kriegt man die Angaben nur mit
Tricks, von hinten durch die Brust ins Auge, heraus.
Bitte beachten, dass die Namen der Konstanten cFreq und
cFrequency, cDel und cDelay sowie cRes
und cResol absichtlich doppelt gewählt sind. cResol
und cDelay behalten die Auswahl des Users bei und die beiden
kürzeren Namen sind daraus berechnet. Das stellt sicher, dass
mit .IFDEF und .IFNDEF auch beim zweiten
Assemblierdurchlauf richtig gerechnet wird.
Ändern der Frequenz mit den Potentiometern
Wenn der User die Taste drückt, wird die Sinusausgabe beim
nächsten Nulldurchgang unmittelbar unterbrochen und die Spannung
an den beiden Analogeingängen wird gemessen. Die Spannung am
AD-Eingabe-Pin ADC0 wird linear in die sechs Sektionen der
Auflösung (Grundfrequenz mal 1, mal 2, mal 4, mal 8, mal 16,
mal 32) eingeordnet. Dazu wird das 16-Bit-Resultat zunächst zwei mal
nach rechts geschoben, um einen 8-Bit-Wert zu erhalten. Dann wird der
Addierer auf 1 gesetzt. Vom AD-Resultat werden dann immerzu 44 abgezogen.
Wenn das Carry nicht gesetzt ist wird der Addierer um eine Position nach
links geschoben. Bei gesetztem Carry ist der Algorithmus fertig.
Die Wandlung des ADC1-Resultats in den Delay-Wert ist etwas
aufwändiger. Um die AD-Werte in Verzögerungswerte umzurechnen,
gibt es die Tabelle DelayTable:. Diese hat Einträge für
alle 1.024 AD-Werte.
Das sind die Frequenzen, die im Basisfall (R/2R mit 8 Bit, 256 Stufen)
einstellbar sind. Der Frequenzbereich umfasst vier Dekaden, mit dem
Vervielfältiger noch eine bis zwei Dekaden mehr.
Die etwa logarithmische Kennlinie kriegt man mit zwei Methoden in den
Griff:
- Mit einem logarithmischen Potenziometer für die
Feineinstellung. Die größten Änderungen treten
am rechten Anschlag auf, daher entspricht ein Log-Potenziometer
dieser Charakteristik.
- Mit einem 10-Gang-Poti zur Einstellung.
Nachdem das Einlesen und Auswerten der ADC-Werte erfolgt ist, wird
die Frequenz berechnet und angezeigt. Abschließend wird der
Tasteneingang für 250 ms lang abgefragt und gewartet, bis
dieser inaktiv ist.
Frequenz-Anzeige
Da ich eine sehr kleine LCD mit acht Stellen in einer einzigen
Zeile vorgesehen habe, braucht diese Wahl etwas erhöhten
Aufwand bei der Zahlenausgabe.
Das ist die Pinbelegung der LCD EA8081-A3N, von oben in der Draufsicht
gesehen. Der Pinabstand ist 2,54mm. Dafür ist auch das Layout
der Platine ausgelegt.
Berechnung der Frequenz
Die Berechnung erfolgt nach dieser Formel
fSinus = fTakt / Auflösung / (4*Verzögerung + 13)
Da der Term Takt / Aufl&aouml;sung mit 20 MHz / 8 am
größten wird, müssen Zahlen bis 160.000.000
im Zähler verarbeitet werden. Das sind bis zu 32 Bits.
Um das Ergebnis in mHz zu erhalten, braucht der Divisor
mindestens 24 Bits, effektiv ebenfalls 32 Bits. Um alle
Zahlen durch Division verarbeiten zu können,
benötigt der Divident 32 + 32 = 64 Bits und muss 32
Divisionsschritte ausführen.
Die Berechnung geht so vor sich. Zuerst wird die Taktfrequenz,
multipliziert mit 1000 (damit mHz herauskommen) und geteilt
durch 256 (8-Bit-Ausgabe) in einen 64 Bit umfassenden Registerbereich
geschrieben. Der Addierer, der über die nach jeder Ausgabe
erfolgende Addition der Leseadresse bestimmt, wird so oft nach
rechts in das Carry geschoben, bis dieses Eins wird. Für
jeden erfolgreichen Schiebevorgang wird der Registerbereich
um eine Stelle nach links geschoben. Damit ist der Dividend
komplett.
Der Verzögerungswert wird in einen 32 Bit breiten Registerbereich
geschrieben. Dieser wird mit vier multipliziert (zwei mal links
schieben) und 13 wird addiert. Dies ist dann der Divisor.
Die Division liefert ein 32 Bit breites Resultat in mHz. Dieses
wird dann in eine Dezimalzahl umgewandelt und an der drittletzten
Stelle das Komma eingefügt. Das sieht dann beispielsweise
so aus:
0001234567.123 ; Alle drei Dezimalziffern sind nicht Null
0001234567.120 ; Die letzte Dezimalziffer ist Null
0001234567.100 ; Die letzten beiden Dezimalziffern sind Null
0001234567.000 ; Alle drei Dezimalziffern sind Null
0123456789.123 ; Die Zahl ist größer als 99.999
Formatieren der anzuzeigenden Frequenz
Im ersten Schritt werden alle Null-Zeichen hinter dem Komma
entfernt. Sind alle drei Null, wird auch das Komma entfernt.
Das Entfernen erfolgt, indem alle Ziffern aus der nächsten,
übernächsten oder aus der vierten Stelle davor
überschrieben werden und der verbleibende Speicherbereich
mit ASCII-Nullen beschrieben wird. Die Zahlen sehen jetzt so aus:
0001234567.123 ; Alle drei Dezimalziffern sind nicht Null
00001234567.12 ; Die letzte Dezimalziffer war Null
000001234567.1 ; Die letzten beiden Dezimalziffern waren Null
00000001234567 ; Alle drei Dezimalziffern waren Null
0123456789.123 ; Die Zahl ist größer als 99.999
Dann werden alle führenden Nullen in Leerzeichen umgewandelt.
Wenn das erste Zeichen, das nicht in ein Leerzeichen umgewandelt
wurde, an den Positionen 1 bis 6 der Zahl liegt (die Zahl hat
mehr als acht Zeichen), dann werden von rechts her Ziffern entfernt.
Handelt es bei der höchsten entfernten Ziffer um eine 5 oder
mehr, wird die rechtliche Zahl um Eins aufgerundet.
Die acht fertigen Ziffern werden danach auf die LCD geschrieben.
Inbetriebnahme-Hilfe
Für diverse Hilfszwecke habe ich eine Software geschrieben,
mit der die Einzelteile der Schaltung stufenweise und separat
getestet und in Betrieb genommen werden können. Den
Quellcode dafür gibt es
hier. Wenn auch die LCD
getestet werden soll, braucht man noch die Include-Datei
lcd.inc im gleichen Ordner.
Im Abschnitt Debugging tests des Quellcodes kann man
einstellen, welche Tests ausgeführt werden sollen (den
entsprechenden Test mit Yes einstellen).
Durchgeführt werden können die folgenden Tests:
- testR2R: Auf dem R2R-Port wird diejenige Zahl
ausgegeben, die mit dem Parameter testR2ROut
eingestellt ist.
- testSine: Der mit den beiden Parametern
testSineRes und testSineDly eingestellte
Sinus wird auf dem R/2R-Port ausgegeben.
- testKey: Das testet die angeschlossene Taste.
Ist sie offen (PB2 ist high), dann gibt das R/2R-Netzwerk
eine Spannung von 3 V ab. Im geschlossenen Zustand (PB2
ist low) sind es 2 V.
- testAdc0, testAdc1: Die R/2R-Spannung folgt dem
AD-Wandler-Ergebnis am Eingang.
- testPortC: Der Datenport zur LCD wird als Ausgang
geschaltet und alle Pins auf High gestellt.
- testLcd: Die LCD wird initiiert und der Text
"SineM324" ausgegeben.
In der englischen Version sind noch weitere Testarten zum
Debuggen realisiert. Die kann man aber nur gebrauchen, wenn
man wesentliche Teile des Quellcodes ändern will.
Für die englische Version einfach dem Link im Kopf folgen.
Noch ein Hinweis: Bei meiner Inbetriebnahme weigerte sich die
LCD auch nur einen Mucks von sich zu geben. Nach etlichen
Stunden erfolgloser Suche in der Software habe ich die Ursache
schließlich gefunden: die verwendete Buchsenleiste und
die LCD-Pins passten nicht zusammen, immer hatte mindestens
einer der Pins keinen Kontakt. Ich habe dann die
Schott-Buchsenleiste ausgelötet und eine gedrehte Fassung
eingelötet. Kaum macht man's richtig, schon geht's
(Robert Müller, Rodau).
Die Software
Die Software ist in Assembler-Quellcode-Format
hier verfügbar.
Lade diese herunter und lade in das gleiche Verzeichnis auch
die LCD-Include-Datei
lcd.inc.
Nun muss der Operationsverstärker ausgewählt werden,
denn dessen Charakteristika sind entscheidend für die
verwendete Sinustabelle. Falls ein 741 oder ein CA3140 verwendet
werden soll, muss nur der betreffende Parameter im Kopf des
Quellcodes im Abschnitt Einstellbar ausgewählt
werden.
Falls ein anderer Operationsverstärker zum Einsatz kommen
soll, muss im Rechenblatt Sinustabelle im LibreOffice-Calc
Dokument hier
- ein entsprechender Eintrag in den Spalten G bis I
hinzugefügt werden (wie man die Linearitätsgrenzen
des OpAmp ermittelt ist weiter unten beschrieben),
- diesen Eintrag in der Zelle B2 mit dem Dropdown-Feld
einstellen,
- die daraus generierte Sinustabelle im Zellbereich von F9
bis F42 markieren und mit Strg-C in die Zwischenablage
kopieren,
- die kopierte Tabelle am Ende des Quellcodes mit einem
weiteren Auswahlparameter (wie z. B. mit .ifdef
cMeinOpAmp einfügen, und
- diesen Auswahlparameter anstelle von cOpAmp741 und
cOpAmpCA3140 im Abschnitt Einstellbar des Quellcodes
aktivieren.
Damit ist sichergestellt, dass die Grenzen des eigenen OpAmp
eingehalten werden.
Auch die anderen Einstellungen im Abschnitt Einstellbar
sollten noch mal kontrolliert werden, auch diejenigen für
die LCD (falls da ein anderer Typ oder eine geänderte
Pinzuordnung eingestellt werden muss).
Dann assembliere man den Quellcode und brenne die Hexdaten in
das Memory des Controllers. Nicht vergessen, die
- JTAG-Fuse auszuschalten (sonst geht die LCD nicht),
- die CLKDIV8-Fuse zu deaktivieren, und
- die Taktfuses auf den externen Quarz umzustellen.
Das ist der Sinus, der bei 32-er Auflösung und bei
einer Verzögerung von 122 produziert wird. Nicht
wirklich 1.000 Hz, aber nahe daran.
Mit ein bisschen Glück, der Rechentabelle oder dem
Assemblerprogramm kriegt man aber genauere Sinusse auch
noch hin. Hier: Resol = 128 und Delay = 28.
Zum Anfang dieser Seite
©2020 by http://www.avr-asm-tutorial.net