Forum: Mikrocontroller und Digitale Elektronik Programable Sine Wave Osciallator ML2035 Ersatz


von Heinrich (Gast)


Lesenswert?

Hallo,

Ich bin auf der Suche nach einem IC, wie dem ML2035 ( Programable Sine 
Wave Oscillator ), der ja nun leider nichtmehr hergestellt wird.

Ich habe gesucht, und habe nichts vergleichbares gefunden.

Ich würde das ganze ja gerne per µC machen, aber wenn man bedenkt, dass 
man 256 Werte eines Sinus speichert, und das mit 16MHz Takt mit einem µC 
macht, ist die Frequenz nach oben hin irgendwie stark begrenzt, bzw. 
sind zu große Sprünge.

Würde also gerne zb. 17kHz genauso wie 17.5kHz ausgeben können, was so 
nicht möglich wäre.

Brauche also einen programmierbaren Sinus Generator ( wie eben der 
ML2035 )

Freue mich auf eure Antworten

Danke schonmal

von holger (Gast)


Lesenswert?

Such mal nach DDS.

von holger (Gast)


Lesenswert?

>Such mal nach DDS.

Upps, zu viele Treffer;)

Versuch DDS Generator.

von Abdul K. (ehydra) Benutzerseite


Lesenswert?

Kingt nach jemanden der es gerne analog mag: Switched-Capacitor Low-Pass 
Filter. Davor einen Rechteck erzeugen, z.B. mit Controller. Das dann in 
den SCLPF einspeisenm, wobei dessen Takteingang benutzt wird. Raus kommt 
ein super Sinus.

Bausteinchen gibts von LTC und Maxim. Bei LTC ist dafür ne App Note zu 
finden.

von Heinrich (Gast)


Lesenswert?

Hallo!

Danke für die Antworten. Ja DDS hab ich mir eben gerade angeschaut, ich 
versteh es noch nicht 100%ig wie es funktioniert.

Meines erachtens wird zb. bei einer Sinus look up Tabelle mit 2000 
Werten einfach 1 Wert ausgelassen, um damit die nächst höhere Frequenz 
zu erhalten, geh ich da richtig in der Annahme ?

Das ganze funktioniert nun mit einem "Phase Akkumulator" und einem 
"Frequency Increment"

Versteh nur nicht ganz, wie das ganze funktioniert.

Es geht doch darum, dass er je nach Frequency Increment, wohl einen Wert 
von 0-2000 auslässt, oder eben mehr, je nach Frequenz.

Habe mir unzählige Seiten dazu nun angesehen, und verstehe nicht ganz 
die Funktion.

von Abdul K. (ehydra) Benutzerseite


Lesenswert?

Machs dir einfach. Geh zu Analog Devices und kauf dir einen von den ganz 
kleinen DDS-Chips. Dahinter noch ein Primitivfilter und einen kräftigen 
OpAmp-Buffer. BUF634 oder so.
Bei AD gibts auch die ganzen Erklärungen verlinkt.

von ML2036 (Gast)


Lesenswert?

Mit einigen ML2036 könnte ich noch dienen.

von Heinrich (Gast)


Lesenswert?

Hallo!

Ja darauf wird es wohl hinauslaufen, allerdings würde ich es gerne 
verstehen wieso das funktioniert.

Heinrich

von Heinrich (Gast)


Lesenswert?

Heißt das, dass das "tuning word" also, was ich ihn per Seriellen oder 
Parallelen Anschluss zuführe zu der Taktfrequenz hinzugezählt wird.

heißt also liegt 1 an, ist Taktfrequenz die Geschwindigkeit, mit der der 
Counter die Sinus look up table hinaufzählt.

liegt 2 an, ist die Taktfrequenz nur halb so groß?

bei 3 1/3
bei 4 1/4
usw.

Oder verstehe ich hier was falsch ? Wie würde dann über den gesamten 
Frequenzbereich eine gleichbleibende Frequenzänderung delta f von ein 
paar mHz erzeugt werden können?

Heinrich

von Helmut L. (helmi1)


Lesenswert?

Nicht die Frequenz wird hochgezaehlt sondern der Phasenwinkel. Und je 
groesser der Wert wird umso hoeher wird die Frequenz.

von Heinrich (Gast)


Lesenswert?

Hallo!

Das Problem ist, ich versteh den Phasenakkumulator nicht ganz.

Ich weiß in der Sinus look up table, sind für jeden Phasenwinkel der 
Amplitudenwert gespeichert.

Zb. 2000 Werte also zw. 0-360° hab ich 200 verschiedene Amplitudenwerte 
( ohne jetzt eine optimierung zu haben durch 1/4 des sinus speichern, 
usw. )

Eingang für den Phasenakkumulator ist die Frequenz des DDS und 
andererseits der Wert den ich gerne als Ausgangsfrequenz hätte.

Was macht der Phasenakkumulator nun? Ausgang dessen ist, die Adresse des 
Amplitudenwerts in der Sinus look up table ( irgendwas zw. 0 und 1999 )

Nur wie kann der nun eine Frequenz, die ja ständig zw. 0 und 1 ändert, 
und eine konstante, nehmen wir mal 100 an, so vergleichen, addieren, 
multiplizieren, dividieren ( Keine Ahnung eben ? ) um danach die Adresse 
richtig hoch zu zählen. Bin etwas verwirrt.

von Peter R. (pnu)


Lesenswert?

Nimm einmal an, der Phasenakkumulator habe als höchste Zahl die Zahl 360 
000.
die 360 seien dann die Grade einer Sinusschwingung.

Wenn jetzt in einer Sekunde 360-tausendmal die 1 addiert wird, wird in 
den oberen drei Stellen 0 bis 355 in einer Sekunde durchfahren. also 
eine Schwingung je Sekunde erzeugt.

Wird dagegen 2 addiert, dauert es nur die halbe Zeit. also wird 0..355 
zweimal je Sekunde durchfahren.

Die Zahl, die addiert wird, bestimmt also direkt die Dauer der 
Schwingung:
Große Zahl = kurze Schwingungsdauer = hohe Frequenz.

Es gilt die Verhältnisgleichung:

Taktfrequenz / Ausgangsfrequenz = Höchstzahl / Additionskonstante

Bei DDS-IC's hat man z.B. einen sehr schnellen 32-bit Addierer. Von der 
Summe werden dann die oberen 10..14 Bit über eine ROM-Tabelle auf einen 
DA-Wandler gegeben, der einen der momentanen Phase entsprechenden 
Analogwert ausgibt.

von Heinrich (Gast)


Lesenswert?

Hallo Peter!

Ja das habe ich soweit verstanden.

Die "verschaltung" des Addierers für den Phasenakkumulator verstehe ich 
aber nicht.

Der Addierer muss doch die Additionskonstante ( bestimmt die Frequenz ) 
zum derzeitigen Wert der Adresse addieren richtig?

Ist das nun ein Taktflankengesteuerter Addierer der als Eingang einmal 
die Additionskonstante hat, und einmal seinen eigenen Ausgang wieder an 
den Eingang zurückführt.

Heißt also zb. 10001000 am Ausgang ( Daher auch Eingang )  und 1 als 
Additionskonstante, kommt am Ausgang 10001001 heraus, und bei der 
nächsten Taktflanke eben 10001010 usw.

Ist das so richtig ?

von Helmut L. (helmi1)


Lesenswert?

Heinrich schrieb:
> Ist das nun ein Taktflankengesteuerter Addierer der als Eingang einmal
> die Additionskonstante hat, und einmal seinen eigenen Ausgang wieder an
> den Eingang zurückführt.
>
> Heißt also zb. 10001000 am Ausgang ( Daher auch Eingang )  und 1 als
> Additionskonstante, kommt am Ausgang 10001001 heraus, und bei der
> nächsten Taktflanke eben 10001010 usw.

Das ist richtig. So zaehlt er bei einer Konstante von 2 immer in 2 
Schritten hoch bei 3 in 3 Schritten u.s.w.
Also immer um einen entsprechenden Phasenwinkel der Sinusspannung. Und 
so baut sich deine Sinusschwingung Stueckchenweise auf. Weil da 
allerdings auch Stoerkomponenten in dem Signal vorhanden sind (durch die 
Taktung) brauchst du am Ausgang noch einen Tiefpass der alles was 
hoeherfrequent als Fc/2 ist unterdrueckt.

von Heinrich (Gast)


Lesenswert?

Hallo!

Wui jetzt hab ichs endlich verstanden ;)

Danke!
Hab auf die schnelle was programmiert um das nachzuvollziehen, 
funktioniert!
1
int main(void)
2
{ uint16_t x=1;
3
  DDRD=0xFF;
4
  while(1)
5
  {
6
    for(uint16_t i=0;i<65535;i+=x)
7
    {  if(!(i%256))
8
     PORTD=sinus[i>>8];
9
    }
10
  }
11
12
}

Ist alles andere als schön oder genau. Bräuchte einen Timer, aber so 
konnte ich die funktion nachvollziehen.

die Abfrage "if(!(i%256))" zoomt mir die Sinustabelle sozusagen auf, es 
wäre also ob ich 256 mal soviele Werte hätte. Damit komme ich auf eine 
tiefe Frequenz und auf kleine Frequenzschritte.

Wie kann ich das nun besser ausprogrammieren?

Mit einem Timer + Interrupt richtig?

Wieviele Werte sollte ich in der Sinustabelle speichern. Bis 20kHz 
sollte es einigermaßen schönen Sinus liefern.

Danke!

von Peter R. (pnu)


Lesenswert?

google mal nach "poor man's Synthesizer" von einem Jesper...

Bis ca. 20 kHz liefert das einen für viele Zwecke ausreichenden Sinus 
wenn der Quarz 20 MHz hat.

von Heinrich (Gast)


Lesenswert?

Hallo!

Den führe ich mir gerade zu gemüte.

Ist allerdings in asm programmiert.

Geh ich recht in der annahme, dass ich das in c nicht in 9 Zyklen in der 
main Loop schaffe?
1
LOOP1:
2
    add    r28,r24      ; 1
3
    adc    r29,r25      ; 1
4
    adc    r30,r26      ; 1
5
    lpm            ; 3
6
    out    PORTB,r0    ; 1
7
    rjmp  LOOP1      ; 2 => 9 cycles

Das ist seine gesamte schleife.

Er addiert also die 24 Bit des Phasenakkumulators mit den 24 Bit der 
Frequenz die man haben möchte. Soweit so klar.
das wäre ja noch einfach per c möglich. Einfach a=a+b zb. alleine hier, 
sind schon mehr Zyklen nötig, als in asm ( laut der .lss Datei ) warum 
auch immer.

Die Ausgabe versteh ich bei ihm nicht so ganz.
lpm bedeutet irgendwas mit r30 und r31, r30 sind also die obersten 8 Bit 
des Phasenakkumulators, das ist klar soweit. r31 weiß ich nicht.
es scheint als würde lpm die Speicherstelle x(steht in r30) aus der 
sinus wave table nehmen und in r0 hauen.
Danach den r0 auf PORTB ausgeben.

Kann mir vielleicht jemand erklären was es mit dem lpm auf sich hat? 
Wäre toll, von Assembler hab ich leider keine Ahnung :( Habs versucht 
selbst herauszufinden, aber schaff es leider nicht

von Heinrich (Gast)


Lesenswert?

Ok ich schreib mal auf wie ich mir das denke, bitte berichtigt mich.

lpm geht auf die Adresse die in r30, und r31 steht, und ladet das darin 
enthaltene Byte in r0

am Anfang macht er:
1
    ; set sinewave output as default
2
    
3
    ldi    r31,hi8(sine)  ; setup Z pointer hi
4
    ldi    r30,lo8(sine)  ; setup Z pointer lo

ldi heißt schreibe das rechts davon stehende ( hi8(sine) ) in das linke 
Register.

also hier ladet er hi8(sine) in r31 und lo8(sine) in r30, es liegt nun 
in den beiden 8 Bit Registern die Adresse von dem Feld sine

das heißt also, dadurch dass nur 256 Werte im Feld gespeichert sind, 
bleibt über das gesamte Feld r31 konstant, und r30 wird verändert, heißt 
wenn in r30 0 steht ist es das Feld an der Stelle 0 und wenn in r30 255 
steht, ist es die Stelle 255 also das letzte Byte im sine Feld?

Hab ich das so richtig verstanden?

von Peter R. (pnu)


Lesenswert?

lpm heißt load from program memory (nach r0)

Aus der Adresse des Programmspeichers, die der Z-pointer (r31+r30) 
angibt, wird gelesen und an r0 übergeben.

Hier wird noch getrickst, indem r30 die oberen 8 bit des Phasenakku 
bilden und gleichzeitig das low-Byte des Z-Pointers sind.

Nach dem Lesen steht also der für die aktuelle Phase passende 8-bit-Wert 
in r0. Der wird dann in Spannung umgewandelt.

Mit der Speicheradresse gibt es noch eine Komplikation:
Da der Programmspeicher mit 16-bit Befehlen gefüllt wird, unterscheidet 
sich die Adresse beim Lesen und die Zahl, die im Z-Pointer steht um den 
Faktor zwei.

Beim Initialisieren muss r31 auf 0x04 gesetzt werden, wenn die Tabelle 
bei Adresse 0x200 beginnt.


Leider haben C-Compiler oft die Eigenschaft, dass sie nicht in die 
kürzeste Form umsetzen. Vielleicht hilft es, wenn man beim Compiler eine 
Option wie "Codeoptimierung" aktiviert/deaktiviert ( Ich bin zwar in 
Assembler zuhause, C kann ich aber nicht)

von Peter R. (pnu)


Lesenswert?

Das mit dem ldi stimmt schon weitgehend, nur wird mit hi(sine) nix 
gemacht.
Die Adresse in  r31,r30 ist 16 bit, die Daten umfassen nur 8 bit.
Also landet in r0  lo(sine) in 8-bit Form.

lo(sine) steht in der Tabelle auf die per r31,r30 zugegriffen wird.

von Heinrich (Gast)


Lesenswert?

Sehr schön, also soweit hab ichs verstanden bis auf:

"Mit der Speicheradresse gibt es noch eine Komplikation:
Da der Programmspeicher mit 16-bit Befehlen gefüllt wird, unterscheidet
sich die Adresse beim Lesen und die Zahl, die im Z-Pointer steht um den
Faktor zwei.

Beim Initialisieren muss r31 auf 0x04 gesetzt werden, wenn die Tabelle
bei Adresse 0x200 beginnt."

Beim Initalisieren setzt er doch r31 auf den Wert, der das obere Byte 
der Speicherstelle von sine beschreibt.

Wieso unterscheidet sich die Adresse beim Lesen und die Zahl im 
Z-Pointer um 2 ?

die unteren 8-Bit stehen im r30 die oberen 8-Bit im r31

direkt in r30 kann ich nun die 256 verschiedenen Amplitudenwerte für den 
Sinus abgreifen.

Wenns bei Adresse 0x200 beginnt, sollte im r30 doch 0 stehen ( 0tes 
Element ) und im r31 0x02, aber wieso genau sinds nun doch 0x04? Hab ich 
noch nicht ganz durchschaut

von Peter R. (pnu)


Lesenswert?

Jo, da hab ich etwas Schwierigkeiten mit dem Erklären.

Es liegt daran, dass im Porgrammspeicher eigentlich nur 16-bit Befehle 
erwartet werden, also opcode und Argument immer zusammen gelesen werden.

Befehl 0 steht auf den Plätzen 0 und 1, Befehl 1 steht auf den Plätzen 2 
und 3,....

Wenn im Programm .org 0x200 steht, ist das 0x200-ste 16-bit Wort 
gemeint, das steht aber auf den Adressen 0x0400 und 0x0401.

.db 0x16,0x61,0x65,.... schreibt in Wirklichkeit in den 
Programmspeicher:

0x0016,0x0061,0x0065,... obgleich es nur 8-bit-Angaben sind.

Ich hoffe, das klärt die Sache.

Also:

lpm greift, wenn in r31 und r30 0x0201 steht, auf den Platz ox0403 zu 
liefert dann also 0x61 in R0

von Heinrich (Gast)


Lesenswert?

Hallo!

Danke, aber so ganz verstehe ich es noch immer nicht, ich werde es 
einfach mal so hinnehmen.

Ich wollte gerade probieren, die assembler programmteile in c zu 
integrieren.

Leider nicht wirklich erfolgreich.

Ich kann zwar variablen in die Register speichern, sodass ich weiß wo 
sie liegen und danach per asm Befehle darauf zugreifen, allerdings kann 
ich wohl aus asm heraus nicht auf mein sinus Feld zugreifen.
1
  register uint8_t counter_1 asm("r24");
2
  register uint8_t counter_2 asm("r25");
3
  register uint8_t counter_3 asm("r26"); 
4
  register uint8_t acc_1 asm("r28"); 
5
  register uint8_t acc_2 asm("r29"); 
6
  register uint8_t acc_3 asm("r30"); 
7
8
  asm("ldi r31,hi8(sinus)");
9
  asm("ldi r30,lo8(sinus)");
10
11
  while(1)
12
  {
13
    asm("add r28,r24");
14
  asm("add r29,r25");
15
  asm("add r30,r26");
16
  asm("lpm");
17
  //asm("out PORTD,r0");
18
  }
19
20
}

asm("out PORTD,r0"); mag er auch nicht, er meint es muss eine konstante 
drinstehen.

Gibt es dennoch eine möglichkeit die Schleife bzw. die wichtigen 
Programmteile in asm zu integrieren?

Danke

von Peter R. (pnu)


Lesenswert?

Natürlich gibt die es, asm-Teile lassen sich in C-Programme integrieren.

Nur, ab da muss ich passen, da sind C-Kenner gefragt.

von Heinrich (Gast)


Lesenswert?

Ok schade!

Habs nun soweit geschafft, ob das so funktioniert ? Weiß ich nicht, denn 
hauptproblem ist dass er out PORTD,r0 nicht machen möchte!
1
DDRD=0xFF;
2
  
3
  register uint8_t counter_1 asm("r24");
4
  register uint8_t counter_2 asm("r25");
5
  register uint8_t counter_3 asm("r26"); 
6
  register uint8_t acc_1 asm("r28"); 
7
  register uint8_t acc_2 asm("r29"); 
8
  register uint8_t acc_3 asm("r30"); 
9
10
  register uint8_t R31 asm("r31"); 
11
  register uint8_t R30 asm("r30");
12
  
13
  R31=((uint16_t)(&sinus[0]))>>8;
14
  R30=((uint16_t)(&sinus[0]))&0xFF;
15
  
16
  acc_1=0;
17
  acc_2=0;
18
19
  counter_1=0x55;
20
  counter_2=0x35;
21
  counter_3=0x00;
22
23
  while(1)
24
  {
25
    asm("add r28,r24");
26
  asm("add r29,r25");
27
  asm("add r30,r26");
28
  asm("lpm");
29
  //PORTD=R30;
30
  }

von Heinrich (Gast)


Lesenswert?

Ok ist klar wiesos nicht geht
1
  asm("out 0x12,r0");

so muss es heißen, da er mit PORTD in c was anderes meint als in asm, da 
sonst PORTD=0xFF; oder dergleichen nicht funktionieren würde.

Hab das Programm aufgespielt, funktioniert aber trotzdem aus irgendeinem 
Grund nicht, wäre super, wenn sich das einer anschauen könnte warum!

Danke

von Spess53 (Gast)


Lesenswert?

Hi

>  asm("ldi r31,hi8(sinus)");
>  asm("ldi r30,lo8(sinus)");

Falsch.

>  R31=((uint16_t)(&sinus[0]))>>8;
>  R30=((uint16_t)(&sinus[0]))&0xFF;

Auch nicht besser.


  asm("ldi r31,hi8(sinus<<1)");
  asm("ldi r30,lo8(sinus<<1)");

Die Adresse muss von Word nach Byte umgerechnet werden.

MfG Spess

von Heinrich (Gast)


Lesenswert?

"Error: undefined symbol `sinus' in operation"

Das meldet er als Fehler!

Kann es sein, dass er aus "asm" heraus nicht auf das sinus Feld 
zugreifen kann?

Lg

von Peter R. (pnu)


Lesenswert?

schonmal die Folge add r28,r24...

muss heißen: add r28,r24  adc r29,r25  adc r30,r26  da es sich um eine 
24-bit Addition handelt, die Carry-Übertrag berücksichtigen muss.

da oben r26 Null ist, bleibt im Moment r30 auf konstantem Wert.

versuchs also mit irgendeiner Zahl >0  in r26 und gib erst einmal mit 
"out 0x12,r30" r30 aus, um zu sehen ob sich r30 ändert.

Für eine Erfolg fehlen dann noch folgende Aktionen:

Erstens: Eine Tabelle mit den Sinuswerten im Programmspeicher an 
definiertem Ort anlegen, beim Einschreiben des Programms in den 
Programmspeicher.

assembler macht das mit .org 0x0200 (Anfang der Tabelle)
und folgendem .db ...... (Aufreihung der einzuschreibenden Werte)

auf diese Tabelle in Abhängigkeit von r30 zugreifen und das Ergebnis 
ausgeben

von Heinrich (Gast)


Lesenswert?

Hallo!

das mit adc hab ich auch schon gemerkt, habs oben nur noch falsch 
stehen. Danke

Wieso sollte sich r30 nicht ändern weil r26=0 ist, nachdem x-ten 
Schleifendurchlauf ändert sich r30 ja trotzdem, da ja ganze zeit was von 
r25 und r26 dazuaddiert wird, und daher auch nach einiger Zeit r30 
verändert wird. Wenn ich das richtig sehe?

von Heinrich (Gast)


Lesenswert?

r30 ändert sich!

Fehlt also nurmehr das Feld.

von Heinrich (Gast)


Lesenswert?

r30 bildet ein Sägezahn ab. Also soweit richtig wie ich das sehe.

Problem bleibt allerdings mit dem Array, weiß nicht wie ich das mit asm 
machen kann. Er meldet dabei immer Fehler wenn ich es so versuche 
einfach mit asm(" ... "); und das originale einzufügen.

von Heinrich (Gast)


Lesenswert?

Dashier funktioniert nicht:
1
asm(".org 0x100");
2
asm("sine:");
3
asm(".byte  0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae");
4
asm(".byte  0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8");
5
asm(".byte  0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5");
6
asm(".byte  0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff");
7
asm(".byte  0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7");
8
asm(".byte  0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc");
9
asm(".byte  0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3");
10
asm(".byte  0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83");
11
asm(".byte  0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51");
12
asm(".byte  0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27");
13
asm(".byte  0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a");
14
asm(".byte  0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00");
15
asm(".byte  0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08");
16
asm(".byte  0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23");
17
asm(".byte  0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c");
18
asm(".byte  0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c");

Er legt die Daten zumindest nicht im Data ab.

von Spess53 (Gast)


Lesenswert?

Hi

In Assembler heißt das auch .db (Flash) und nicht .byte ( reserviert 
RAM).

MfG Spess

von Heinrich (Gast)


Lesenswert?

bei .db sagt er Error: unknown pseudo-op: `.db'

von Peter R. (pnu)


Lesenswert?

Hallo, da bin ich wieder, hatte gerade Anderes zu tun.

Also, da sind wir ja schon so weit, dass die Phasenschritte in r30 
durchlaufen werden. Jetzt bleibt noch das Problem mit der Tabelle und 
dem lookup.

Man kann die Tabelle (mit .byte ?) auch im RAM installieren, es sind ja 
nur 256 byte.
Dann kann man mit LD R0,Z indiziert aus der Tabelle lesen und so den 
lookup ausführen.

Da wird die Schleife sogar um einen Takt kürzer.

Das Problem für mich, der C nicht beherrscht, besteht darin, diesen 
RAM-Bereich freizuhalten, denn C benutzt ja recht viel RAM für den Stack 
und diverse Variablen.

von Heinrich (Gast)


Lesenswert?

Also alles in asm oder es wird nix draus?

Er legt momentan nichtmal das Array an, wieso auch immer ?

von Spess53 (Gast)


Lesenswert?

Hi

>Er legt momentan nichtmal das Array an, wieso auch immer ?

Wie kann man etwas permanent im RAM anlegen? Hirn einschalten. Viel 
Spass noch.

MfG Spess

von Heinrich (Gast)


Lesenswert?

Stimmt.

Aber wie dann .db funktioniert auch nicht ? was bleibt übrig ;) alles 
ausm fenster schmeißen und hoffen dass sich die sine table während des 
fluges eingebrannt hat?

von Peter R. (pnu)


Lesenswert?

Natürlich kann man im RAM eine Tabelle anlegen:

Das Programm beginnt dann eben nach einem reset damit, die Werte ins RAM 
zu schreiben, im Extremfall mit 256 LDI R16,K und anschließendem STX,R16 
bis 256 Werte des Laufindex X durchgearbeitet sind.

Anschließend führt dann die DDS-Schleife mit diesem RAM-Bereich per LD 
R0,Z den lookup durch.

von Heinrich (Gast)


Lesenswert?

Nur wie schreibe ich das nun so, dass er es auch übernimmt?

So wie oben geschrieben macht es das schonmal nicht! Irgendeine Idee?

von Peter R. (pnu)


Lesenswert?

Also Hallo,

irgendjemand, der C soweit beherrscht, wird doch die Zeilen liefern 
können, wie man eine page im Programmspeicher mit einer Tabelle belegt 
und wie man diese per eingebundenes Assemblerprogramm :add, adc, adc, 
lpm, out, rjmp  (wie oben) benutzt.

Oder wie man eine page im RAM mit einer Tabelle füllt, sie vom Stack 
frei hält und sie für lookup benutzt.

So weit weg ist Heinrich doch wirklich nicht weg von der Lösung.

Sonst mault doch jeder, dass nur unqualifizierte Fragen gestellt werden 
und sich niemand selbst Mühe gibt. - Was bei Heinrich doch 
offensichtlich nicht der Fall ist.

von Heinrich (Gast)


Lesenswert?

Hallo Peter!

So ich hab mir jetzt was überlegt, sieht so aus als würde es 
funktionieren.

Ich mache ein 512 Byte Array auf, und schreibe 2x einen kompletten Sinus 
rein, somit ist auf der Adresse der oberen 8 Bit des Arrays + 1 sicher 
ein kompletter Sinus abgebildet ( ob der bei Phase 0 oder Phase x 
beginnt ist ja egal )

in R31 schreib ich danach das:

R31=(((uint16_t)&sinus[0])>>8)/2;

Warum es /2 heißt weiß ich nicht, aber es könnte doch mit deinem 
zusammenhängen, wie du mir vorhin erzählt hast, mit dem 0x200 bzw. 0x400 
usw. liegt daran richtig ?

Auf jedenfall funktioniert es so, ein Sinus bekomm ich am PORTD raus.

Will trotzdem noch wissen ob das so stimmt :)

von Heinrich (Gast)


Lesenswert?

Ok, also das haut so nicht hin.

Wenn ich mit &sinus[0]>>8 das high byte der Adresse auslese, kommt 0x06 
raus, aber nur wenn ich r31 auf 2 setze, funktioniert es perfekt. ist 
r31 auf 1, fehlt ein Teil vom sinus, bei 3 fehlt auch ein Teil ( ist ja 
logisch, bei 512 Bytes sinus ist ein Teil vor und ein Teil danach )

Aber wieso kommt die Adresse 0x06 raus, und in wirklichkeit muss ich auf 
0x02 zugreifen?

von Peter R. (pnu)


Lesenswert?

Bei diesen Termen:        R31 = ((uint16... usw.

muss ich dann passen, denn das sind C- spezifische Formulierungen.

Offensichtlich hast Du ja schon einen Sinus, der mit r31 = 2 richtig 
gelesen wird.

Mir kommts einigermaßen richtig vor:

mit org 0x100 ist der Beginn der Tabelle auf word 0x0100 gesetzt, dessen 
erstes byte auf Speicherplatz 0x0200 liegt. und 256 words umfasst.also 
bis Speicherplatz 3ff geht.
mit r31 = 2 liest Du die Tabelle komplett, aber immer nur die geraden 
bytes davon wegen des Halbierens in dem komischen Term.

mit r31 = 1 bekommst Du eine Verschiebung um 256 bytes nach unten, das 
laufende Term mit r30 erwischt also nur die Hälfte der Tabelle. bei r31 
= 3 läuft es entsprechend oberen Bereich der Tabelle

So, jetzt mach ich aber Schluss, Viel Erfolg beim Weitermachen und Gute 
Nacht.

von Heinrich (Gast)


Lesenswert?

Hallo!

Ich hab das ganze jetzt auch nochmals simuliert.

Auch per simulation meint er es liegt an 0x065C das erste Element, aber 
jetzt muss ich trotzdem in r31 1 hineinschreiben, damit es funktioniert.
Kann sich das mal ein c-geschulter ansehen?

Danke!

von Peter R. (pnu)


Lesenswert?

Ich habe mir einmal im instruction set den befehl lpm angesehen (S.91), 
was ich schon längst hätte tun sollen:

Der LESEBEFEHL lpm arbeitet BYTEweise, adressiert mit r31,r30 und 
übergibt 8bit an r0.

Das ANLEGEN DER TABELLE muss WORDweise (16bit) geschehen.

-Man muss also den Befehl .dw0x5876 verwenden.
-Der Platzierungsbefehl .org muss das Halbe des Z-Pointers r31,r30 
betragen, sonst erwischt man beim Lesen die Tabelle nicht

direkt nach dem .org200 schreibt man also mit .dw0x5876:

die 0x58 in den byteplatz Nr. 0x0401  und die 0x76 in den byteplatz 
0x400, was dem wordplatz 0x200 entspricht.

Wenn r31 4 enthält und r30 die 0 liest man 0x76, wenn r31 die 4 und r30 
die 1 enthält liest man die 0x58.

von Spess53 (Gast)


Lesenswert?

Hi

Das ist Unsinn. Deine Tabelle liegt immer auf einer Wordadresse.

Da lpm aber byteweise adressiert muss Z den doppelten Wert der Adresse 
der Tabelle bekommen.

Daher:

ldi r31,high(sinus<<1)
ldi r30,low(sinus<<1)

Und damit kann man problemlos .db verwenden. Die Anzahl der Werte in 
einer Zeile muss geradzahlig sein.

MfG Spess

von Heinrich (Gast)


Lesenswert?

Das Problem ist, momentan erstelle ich das Array ganz normal in c

also uint8_t sinus[]={..............};

weil wenn ich es mit inline assembler mache und .db verwenden möchte, 
sagt er dass er den Befehl nicht kennt ?

von Heinrich (Gast)


Lesenswert?

Es gibt wieder Neuigkeiten.

Mit Avr Studio simuliert meint er er legt aus auf die Adresse 0x06.. ab.

Ich muss aber im R31 immer die letzte Adresse in der Daten im SRAM 
vorhanden sind eintragen. Das heißt wenn der Code länger wird, muss ich 
auch R31 ändern.

Was mir noch aufgefallen ist, es wäre wohl besser wenn ich erreichen 
kann, dass ich lediglich in der Assembler Hauptschleife, Assembler 
Befehle brauche.

Dazu müsste ich aber sachen wie zb.:
1
asm volatile(
2
"lds r21,counter_1 \n\t"
3
"lds r22,counter_2 \n\t"
4
"lds r23,counter_3 \n\t"
5
"lds r24,acc_1 \n\t"
6
"lds r25,acc_2 \n\t"
7
"lds r30,RDN \n\t"
8
"lds r31,RDE \n\t"
9
"lds r20,run \n\t"
10
);

Wegbringen.

Nur wie mache ich das, kann ich in C variable definieren, die er immer 
im Register haben soll, damit C auch weiß dass er diese Register für 
nichts anderes verwenden darf? Ich denk damit wäre schon einiges 
geschafft, dass sich Asembler und C nicht gegenseitig "beißen"

Grüße
Heinrich

von Abdul K. (ehydra) Benutzerseite


Lesenswert?

Schau mal unter 'volatile' nach.

von Peter Ullrich (Gast)


Lesenswert?

Hallo Heinrich!

MSI hat einen Chip, der dem ML2035 ähnlich ist: MSLOSC

http://www.mix-sig.com/index.php?option=com_content&view=article&id=136:mslosc&catid=52

Demo Schaltplan ist hier: http://i.imgur.com/E2nIN.jpg

Ciao
Peter

http://www.ullrich.at.tf

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.