Forum: Mikrocontroller und Digitale Elektronik Ring-LookUp-Table für 2 unabhängige Sinussignale


von Christian B. (Gast)


Lesenswert?

Hallo Leute,

wie jeder hier benötige auch ich Hilfe. Ich habe schon einige
"kleine" AVR-Spielereien gemacht und wollte jetzt mal etwas
anspruchsvolleres versuchen.

Und zwar möchte ich mit einem ATmega einen Mehrkanal-DAC füttern,
dieser soll zunächst
                     - ein Sinus mit variabler Frequenz erzeugen
                     - ein zweiter Kanal soll ein identschisches
                       Signal, aber mit variabler Phasenverschiebung
                       (0-90°) erzeugen

Nach einigem stöbern hier in diesem Forum habe ich auch viele
interessante anregungen bekommen. Nur fehlt mir eindeutig das knowhow
über eine LookUp-Table, die wie ein Kreis ausgelesen werden kann. Sehr
interessant wird dies, wenn das zweite phasenverschobene Signal erzeugt
werden soll.

Wenn mir jemand helfen könnte wäre ich sehr dankbar.

von Hagen (Gast)


Lesenswert?

suche mal nach DDS, direct digital synthesis und dem Phase Accumulator.
Deinen "Kreis" einer Lookuptable kannst ganz einfach erreich indem du
diese Tabelle aus 256 Werten definierst und an einer Speichergrenze die
durch 256 teilbat ist positionisert. Dein Zeiger auf den akteullen Wert
wird also bei einem Inkrement nach exakt 255 Zyklen überlaufen und dann
wieder bei dem Index 0 der Tabelle von vorne beginnen. Du darfst halt
nur diesen 16 Bit Zeiger nicht im Highbyte modizieren, due
inkrementierst also nur die untersten 8 Bits dieses Zeigers.

Gruß Hagen

von Christian B. (Gast)


Lesenswert?

@ Hagen

Danke für deine Hilfe. Mit der größe der Tabelle habe ich es verstanden
nur nicht dass  ich die noch zwingend an einer Speicheradresse (n*0xff)
positionieren muss.?

Genau so wie du es sagst, hab ich mir das auch gedacht, ich möchte dass
ein Zeiger nach einem Überlauf automatisch wider bei Adresse NULL
beginnt. Entschuldige, mir fehlt einfach die Erfahrung mit
Zeigeroperationen, doch jetzt ist es wichtig geworden.

Das mit dem 16bit Zeiger und der 8Bit-Adresse hab ich soweit auch
verstanden.

Wäre für eine Hilfe im Bezug auf die genannte Zeigeroperation dankbar.
Kannst du mir da vielleicht weiterhelfen Hagen ... oder auch gern
jemand anderes, der damit erfahrung hat.

von Hagen (Gast)


Lesenswert?

Deine Tabelle beginnt im Flash zb an Addresse 0x1200 -> ergo 0x1200 mod
256 == 0. Dein Zeiger wird also mit 0x1200 initialisiert und nur
jeweils +1 inkrementiert, nach exakt 256 Schritten beginnt er wieder
von vorne WENN du nur die untersten 8 Bits dieses Zeiger um +1
inkrementierst. Somit wird eine 8 Bit Addition um+1 auf einen 16 Bit
zeiger durchgeführt.

Gruß hagen

von Christian B. (Gast)


Lesenswert?

@ Hagen

ich glaug ich habs, ... man beim schreiben meines zweiten Beitrags kam
mir ein Blitzgedanke.

-Position des Datenfeldes mit 256 Werten an Bsp-Adresse 0x0300
-Pointer auf diese stelle Als Startadresse zeigen lassen
-Pointer(Low-Byte) hochzählen max.Point-Wert=letzter Datenwert
-Überlauf -> Pointer automatisch bei NULL und zeigt wieder auf
Datenfeld Startwert

... @Hagen ist das so korrekt von mir verstanden worden?
Das scheint mir jedenfalls einleuchtend.

Wenn es jetzt aber statt 2^8 2^(10 od 12) werte sind, was dann... ist
es dann etwas schwieriger oder wesentlich komplizierter?


Ein oder zwei Codebeispiele ASM oder/und C wären aber trotzdem noch
ganz hilfreich für die Pointerprogrammierung. Wäre wirklich echt super

best regards littleCB

von Christian B. (Gast)


Lesenswert?

Oh krass @Hagen

du bist ja schnell wie nix ...


THX @ You

von Hagen (Gast)


Lesenswert?

Am besten du suchst im WEB oder sogar hier im Wiki nach DDS und Phase
Accumulator, ich bin mir 100% sicher das dort auch fertige Sourcen
drinnen sind.

Oder willst du die Sinus Schwingungen immer mit der gleichen Frequenz
erzeugen ?

Ansonsten hast du mich schon richtig verstanden :-)

Gruß hagen

von Clemens (Gast)


Lesenswert?

Hallo,

Beispiel für (fast) beliebig viele bits:

.equ nBits = 10
.equ nMask = (1<<nBits)-1   ; Erzeugt Einsen bis zur letzten

                            ; verwendeten Bitstelle
.equ nBaseAddr = 0x10000    ; Achtung: die niedrigen nBits müssen Null
                            ; sein!

.def rTemp0 = r16

ringInit:                   ; Initialisiert den Z-Pointer
    ldi zl, low(nBaseAddr)  ; dieser darf nicht verändert werden
    ldi zh, high(nBaseAddr)
    ret

ringGetNext:
    lpm                     ; Wert in r0 kopieren

    inc zl                  ; diese und die folgenden drei Zeilen
    brcc ringGetNext_0      ; können, sofern es der AVR unterstützt
        inc zh              ; auch durch addiw zl, 1 ersetzt werden
ringGetNext_0:              ; nun ist zeigt z auf nächste Stelle

    mov rTemp0, zh          ; noch auf überlauf testen
    andi rTemp0, high(nBaseAddr)
    breq ringGetNext_1      ; in hohem AddressIndex steht eine null
        ret                 ; sonst (<>0) ist kein überlauf

                            ; stattgefunden
ringGetNext_1:
    mov rTemp0, zl
    andi rTemp0, low(nBaseAddr)
    breq ringInit           ; in niedrigem AddressIndex steht auch
                            ; eine null -> Addresse neu laden
        ret                 ; kein überlauf

vielleicht habe ich den Überlauf-Test ein wenig zu kompliziert
gestaltet... leider habe ich auch grad keinen Testaufbau, um den Code
zu testen, aber eigentlich sollte es schon gehen, etwa so.
also:
- lesen
- erhöhen
- überlauf testen

AddressIndex ist der Index innerhalb der LookUp-Table.

schöne Grüße, Clemens

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?

Code & Erklärung für DDS mit dem AVR gibt's hier:
http://www.mikrocontroller.net/articles/Digitaler_Funktionsgenerator

von Philipp Sªsse (Gast)


Lesenswert?

Sofern des der AVR unterstützt, gibt es auch das fertig LPM rd, Z+, das
macht dann alles auf einmal.

Falls nicht, ist es einfacher, rückwärts zu zählen, also mit dem
letzten Wert anfangen und dann decrementieren, bis alles null ist. Dann
entfällt die ganze Vergleicherei.

Falls Speicherplatz ein Thema ist, reicht natürlich auch ein Viertel
der Tabelle, die anderen Viertel lassen sich über Symmetrieen
ausrechnen.

von R2D2 (Gast)


Lesenswert?

in C:

uint8_t sin_table[COUNT]={...}

int8_t sin(uint16_t angle)
{
  uint8_t tmp = sin_table[angle % COUNT]; //Wird bei COUNT=2^x vom
Compiler in ein AND optimiert.

  if ((angle % 4*COUNT) > 2*COUNT)
    return -tmp;
  else
    return tmp;
}

Ich hoffe das ich mich jetzt nicht irgendwie vertippt habe, oder nen
denkfehler drin hab.

von Philipp Sªsse (Gast)


Lesenswert?

Doch, Denkfehler, fürchte ich.

Wenn Angle zwischen COUNT und 2*COUNT liegt, darfst Du nicht tmp
zurückgeben, sondern sin_table[COUNT - (angle % COUNT)]
(Spiegelsymmetrie bei pi/2).

Also:

angle %= 4 * COUNT;
switch (angle / COUNT)
 {
  case 0: return sin_table[angle];
  case 1: return sin_table[2 * COUNT - angle];
  case 2: return -sin_table[angle - 2 * COUNT];
  case 3: return -sin_table[4 * COUNT - angle];
 }

von Christian B. (Gast)


Lesenswert?

Ey echt super von euch Leute,

das ist zunächst mehr Feedback als ich erwartet habe. Falls Ihr mal was
habt, und ich was helfen kann, dann werde ich es gern tun, doch ich
befürchte dass ich mit meinem derzeitigen Wissen noch sehr weit am
Anfang stehe.

So ich werde jetzt erstmal alle Beiträge nacheinander auf mich wirken
lassen und vresuchen nachzuvollziehen. wird wohl etwas dauern ... ;-)


special THX @all unpaid helper ;)
littleCB

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.