Forum: Mikrocontroller und Digitale Elektronik DDS mit Mega8 und Bascom


von Thomas S. (zeilentrafo)


Lesenswert?

Habe einem mit 14,7456 Mhz (Baudratenquarz) getakteten ATMega8 auf Bus D 
ein R2R Netzwerk verpasst und möchte damit einen abstimmbaren Oszillator 
für Klangerzeugung programmieren. Zur Veranschaulichung wird eine 
Sinuskurve in einer indizierten Variablen abgespeichert und dann in der 
IRQ-Routine abgerufen. Mich verwundert, dass Bascom anscheinend doch 
wesentlich mehr Takte benötigt als für eine Klangerzeugung vom IRQ 
vorgegeben. So würde ich gern eine Hüllkurve mit reinprogrammieren oder 
bspw. einen Ringmodulator. Aber da kommt immer der nächste IRQ während 
der vorige noch abgearbeitet wird. Brauche ein paar Tipps.

Unten der Bascom Quellcode.

Meine Fragen: (1) grundsätzlich: Habe ich DDS richtig verstanden ?
(2) Gibt es eine Methode ganzzahlig, im Rahmen von 8 Bit zu 
multiplizieren (für die Lautstärkenkontrolle und für DDS) ? Wenn ich 
bspw. in der IRQ Routine schreibe "IF Versatz > 255 then ..." dann 
reichen die 8 * 256 Takte bereits nicht mehr aus, auf die Timer0 
eingestellt ist !

~~~~~~~~~~~~~

$regfile = "m8def.dat"
$crystal = 14745600
$hwstack = 32                                               ' default 
use 32 for the hardware stack
$swstack = 10                                               ' default 
use 10 for the SW stack
$framesize = 40                                             ' default 
use 40 for the frame space

Config Timer0 = Timer , Prescale = 8
On Timer0 Tmr0

Config Portd = Output

Dim A As Byte , B As Byte , Sinus(256) As Byte
Dim C As Single , Laut As Single , Amplitude As Single , Freq As Byte , 
Versatz As Single

B = 0
For A = 0 To 255
C = A * 360
C = C / 256
C = Deg2rad(c)
C = Sin(c)
C = C * 128
C = C + 128
Sinus(b) = Int(c)
B = B + 1
Next

Enable Timer0
Enable Interrupts
Do
Laut = 1
For Freq = 1 To 255
Waitms 5
Next
For Freq = 255 To 1 Step -1
Waitms 5
Next
Loop

Tmr0:
C = Freq \ 100
C = C + 1
Versatz = Versatz + C
A = Int(versatz)
If A > 255 Then Versatz = 0
Portd = Sinus(a)
Return

~~~~~~~~~~

Danke und Gruß

von Ulrich (Gast)


Lesenswert?

Der BAscom Compiler ist nicht gerade der Schnellste. Bei ISRs kommt da 
noch ein relativ langer Teil zum retten und wiederherstellend er 
Register dazu. Es könnte nötig sein die ISR als ganzes als inline ASM zu 
schreiben. Immerhin geht das relativ einfach.

Wenn man mutig ist, und in Kauf nimmt, dass das Programm eventuell nicht 
mehr so mit neuere Compilerversionen läuft, könnte man sogar ausnutzen 
das der Compiler einige Register gar nicht nutzt.

Unabhäng davon sollte die ISR nicht mit Fließkomma arbeiten, und auch 
keine Division enthalten.  Das allein kann die ISR schon über 10 mal 
schneller machen.
Und auch so ist die ISR auch noch logisch falsch, beim Überlauf fängt 
man nicht wieder genau bei 0 an.

von Thomas S. (zeilentrafo)


Lesenswert?

Ulrich schrieb:

> Und auch so ist die ISR auch noch logisch falsch, beim Überlauf fängt
> man nicht wieder genau bei 0 an.

Danke Dir. Stimmt, bei Freq > 100 wird Sinus(0) nicht mehr aufgerufen. 
Komischerweise sieht es auf dem Oszi noch recht sauber aus.

Man kann in Bascom bei den IRQ "nosave" angeben, dann werden die 
Register nicht auf den Stack geschoben. Als ich es probiert habe, ist 
nur Müll rausgekommen. Komischerweise hatte ich auch Müll, wenn ich die 
DDS-Division aus der IRQ Routine rausgenommen habe.

Problematisch wird es bei der Hüllkurveneinstellung. Da sehe ich keine 
Möglichkeit, die Division aus der IRQ Routine zu entfernen. Daher 
nochmal meine Frage, wie man eine Division (oder Multiplikation mit dem 
Kehrwert) auf ganzzahlige Werte im 8-Bit-Raum beschränken kann ?

von Ulrich (Gast)


Lesenswert?

Die Option NOSAVE bei der SIR ist nur brauchbar, wenn man die ISR in 
inline ASM schreibt, und da die wrklich benötigsten Register von Hand 
rettet. Ohne geht es nicht (bis auf extreme Ausnahmen).

So wie die ISR oben ist, könnte man die Division ganz einfach nach außen 
ziehen und eine Freq/100 als extra Variable speichern.
Für eine DDS Funktion sollte die Variable für die Phase (versatz) vom 
Typ Word (oder ggf. dem 32 Bit equivalent) sein. Die addition dann auch 
mit einen Word Typen für die Frequenz, wobei der Wert ausherlalb der ISR 
berechent wird. Für die Sinustabelle wird dann nur das High-byte der 
Phase benutzt. Für das High byte kann man bei der Deklaration eine extra 
Varabel an der gleichen Postion wie die Phase extra definieren.

Also C und versatz als WORD. und die ISR etwa so:

versatz = verstatz + c
A = versatz / 256             ( hier besser spez. Varablenzugriff)
portd = sinus (A)

von Maik W. (werner01)


Lesenswert?

Division durch 256 geht auch mit MSB auf LSB kopieren

von Peter R. (pnu)


Lesenswert?

Für die Nutzung eines Kontrollers als DDS-IC muss die Grundoperation 
(Addierschleife) ohne Eingriff durch interupts ablaufen.Also als 
inline-ASM-Routine. Der Kontroller sollte NUR die DDS-Schleife arbeiten.

Dann läuft diese Schleife mit etwa 6 bis 8 Takten ab und man kommt mit 
14,..MHz bis etwa 50 kHz auf eine einigermaßen gute Kurvenform, kann 
also den NF-Bereich überdecken.

Zusätzliche Operationen, wie Multiplikation wegen der Amplitude, sollte 
man nicht machen. Nicht nur,dass dann die DDS-Schleife verlangsamt wird 
und man nur maximal einige kHz erzeugen kann, auch die Auflösung von 256 
Schritten vertikal wird zerstört.

Die Steuerung, wie Eingabe der Sollfrequenz, Berechnung der Steuerzahl 
usw. sollte von einem zweiten Kontroller ausgeführt werden, der nach 
Änderungen die fertig berechnete Additionszahl an das "DDS-IC" übergibt, 
z.B über eine serielle Schnittstelle.

google mal "poor man's DDS-synthesizer".

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.