Forum: Analoge Elektronik und Schaltungstechnik AD9102 und 30MHz Sinus


von Marcel D. (diablokiller999)


Lesenswert?

Hi Leutz!
Ich habe hier einen AD9102 und möchte einen Sinus zwischen 10 und 30MHz 
mit ihm generieren. Die Kommunikation mit dem Chip klappt einwandfrei, 
leider habe ich keine Idee was ich in den Einstellungen übersehe um 
einen Sinus am Oszi zu sehen.

Ich gebe dem IC sechs 14Bit Werde in den SRAM, stelle als Config DDS 
Output und Waveform von RAM zwischen Start und Stop-Adresse ein (0x6000 
bis 0x6005), ein Tuning Word von 0x400000 und den Analog/Digital Gain. 
Hat hier jemand etwas Beispiel-Quelltext um die Funktionalität dieser 
DDS etwas besser zu verstehen oder kann mir sagen wo es bei meiner 
Umsetzung hakt?

Hier mal mein Arduino Sourcecode
1
#include <SPI.h>
2
uint16_t ramvalues[6] = { 8192,16384,16384,8192,0,0};
3
#define DAC_AGAIN_REG       0x0007
4
#define RAM_UPDATE_REG      0x001D
5
#define PAT_STATUS_REG      0x001E
6
#define PAT_DELAY_REG       0x0020
7
#define WAV_CONFIG_REG      0x0027
8
#define DAC_DGAIN_REG       0x0035
9
#define DDS_TW32_REG        0x003E
10
#define DDS_TW1_REG         0x003F
11
#define STOP_ADDR_REG       0x005E
12
#define SRAM_LOW_ADDR       0x6000
13
#define SRAM_HIGH_ADDR      0x6FFF
14
//#########################     COMMANDS #########################
15
#define PAT_WRITE           0x0004
16
#define WAV_CONFIG_RAM      0x0030
17
#define PAT_RUN             0x0001
18
#define TUNING_WORD_L       0x0000
19
#define TUNING_WORD_H       0x4000
20
#define DDS_STOP_ADDR       0x0050
21
#define DACAGAIN            0x4000
22
#define DACDGAIN            0x4000
23
#define START_DELAY         0x0000
24
25
26
void write_lookup();
27
uint16_t write_register(uint16_t reg, uint16_t value);
28
29
const int SPICS = 10;
30
const int SPIMISO = 12;
31
const int SPIMOSI = 11;
32
const int SPISCK = 13;
33
34
uint16_t adreg = 0; 
35
uint16_t adval = 0;
36
uint16_t answer = 0;
37
38
39
void setup() {
40
  Serial.begin(115200);
41
  digitalWrite(SPICS, HIGH);
42
  // start the SPI library:
43
  SPI.begin();
44
  // reduce speed
45
  SPI.setClockDivider(SPI_CLOCK_DIV8); 
46
  // initalize the  data ready and chip select pins:
47
  pinMode(SPIMISO, INPUT);
48
  pinMode(SPICS, OUTPUT);
49
  pinMode(SPIMOSI, OUTPUT);
50
  pinMode(SPISCK, OUTPUT);
51
52
  delay(100);
53
  write_lookup();  
54
}
55
void loop() {
56
  //Wait for command
57
  while(Serial.available() < 4);
58
  
59
  adreg = Serial.read() <<8;
60
  adreg += Serial.read();
61
  adval = Serial.read() << 8;
62
  adval += Serial.read();
63
  answer = write_register(adreg,adval);
64
  Serial.write(answer>>8);
65
  Serial.write(answer);
66
67
}
68
69
void write_lookup()
70
{
71
    write_register(PAT_STATUS_REG,PAT_WRITE);
72
    for( int i = 0; i <= 5 ; i++)
73
    {
74
      write_register(SRAM_LOW_ADDR+i,ramvalues[i]);
75
    }    
76
    
77
    // Select DDS Output / Wave from RAM
78
    write_register(WAV_CONFIG_REG,WAV_CONFIG_RAM);
79
    // Set stop address at 6 values
80
    write_register(STOP_ADDR_REG,DDS_STOP_ADDR);
81
    // Set tuning word to 0x4000000
82
    write_register(DDS_TW32_REG,TUNING_WORD_H);
83
    // No Start Delay
84
    write_register(PAT_DELAY_REG,START_DELAY);
85
    // Set analog gain
86
    write_register(DAC_AGAIN_REG,DACAGAIN);
87
    // Set digital gain
88
    write_register(DAC_DGAIN_REG,DACDGAIN);
89
    // Update Registers
90
    write_register(RAM_UPDATE_REG,0x0001);
91
    // Start pattern generation
92
    write_register(PAT_STATUS_REG,PAT_RUN);
93
}
94
95
96
uint16_t write_register(uint16_t reg, uint16_t value)
97
{   uint16_t answer;
98
    digitalWrite(SPICS,LOW);
99
    SPI.transfer16(reg);
100
    answer = SPI.transfer16(value);
101
    digitalWrite(SPICS,HIGH);
102
    return answer;
103
}

von Christoph db1uq K. (christoph_kessler)


Lesenswert?

Wenn ich das Datenblatt im kurzen Überflug richtig verstanden habe, kann 
man einen Sinus unabhängig vom SRAM erzeugen, dazu muss noch irgendwo 
ein Sinus-ROM drinstecken. Mit dem "Wahlschalter" in der Mitte des 
Blockschaltbilds schaltet man zwischen Sinus, Arbitrary (aus dem RAM) 
oder Sägezahn um.

"Die Kommunikation mit dem Chip klappt einwandfrei" wie äußert sich das? 
Ich hatte beim ersten Ansteuern eines DDS erst mal garkeine Reaktion, 
der schluckte alle Daten, aber nichts kam heraus. Mein erster Erfolg 
war, dass ich den Taktoszillatorausgang einschalten konnte (es lebt...), 
ab da war es relativ einfach.

von Marcel D. (diablokiller999)


Lesenswert?

Christoph db1uq K. schrieb:
> Mit dem "Wahlschalter" in der Mitte des
> Blockschaltbilds schaltet man zwischen Sinus, Arbitrary (aus dem RAM)
> oder Sägezahn um.
Mit dem Command
1
write_register(WAV_CONFIG_REG,WAV_CONFIG_RAM);
setze ich den Schalter auf Arbitrary (Register 0x27).

> "Die Kommunikation mit dem Chip klappt einwandfrei" wie äußert sich das?
Ich kann die per SPI geschriebenen Werte auch wieder auslesen bzw. die 
Übernahme der Schattenregister
1
write_register(RAM_UPDATE_REG,0x0001);

triggern.
Leider sieht mir meine Ausgabe weder nach einem Sinus aus, noch hört er 
auf das eingestellte Tuning Word.

: Bearbeitet durch User
von Axel S. (a-za-z0-9)


Lesenswert?

Marcel D. schrieb:

> Mit dem Command
>
1
write_register(WAV_CONFIG_REG,WAV_CONFIG_RAM);
> setze ich den Schalter auf Arbitrary (Register 0x27).

Aha. Und warum machst du das? Warum verwendest du nicht die 
Sinus-Tabelle aus dem ROM? Oder hast du einen Sinus ins RAM geladen?

> Leider sieht mir meine Ausgabe weder nach einem Sinus aus, noch hört er
> auf das eingestellte Tuning Word.

Sondern? Warum muß man dir eigentlich jedes Wort aus der Nase ziehen? 
Eine sinnvolle Problembeschreibung würde enthalten:

1. was du erreichen willst
2. was du genau dazu tust
3. was du statt dessen erhältst

Punkt 3. ist nach wie vor leer

von Marcel D. (diablokiller999)


Angehängte Dateien:

Lesenswert?

Axel S. schrieb:
> Aha. Und warum machst du das? Warum verwendest du nicht die
> Sinus-Tabelle aus dem ROM? Oder hast du einen Sinus ins RAM geladen?

Weil ich mit einer angepassten LUT und Takt einen genaueren Takt/Teiler 
hinbekomme und so einen saubereren Sinus erhalte.


> Eine sinnvolle Problembeschreibung würde enthalten:
>
> 1. was du erreichen willst
Einen Sinus mit unterschiedlichen Frequenzen zwischen 10 und 30MHz so 
sauber wie möglich erzeugen. Das mitgelieferte LabView Programm liefert 
mit der internen ROM-DDS ein unsauberes Signal.
> 2. was du genau dazu tust
Einen Sinus mit 6 Werten in den RAM laden, die DDS über diese 6 Werte 
mit 180MHz iterieren lassen um einen 1/6 Teiler zu erhalten der mir 
einen 30MHz Sinus erzeugen sollte.
> 3. was du statt dessen erhältst
Siehe Bild

: Bearbeitet durch User
von Achim S. (Gast)


Lesenswert?

Ich habe das Datenblatt nicht in Gänze durchgelesen, aber als möglicher 
Hinweis: die Daten aus dem RAM scheinen laut Schemazeichnung ja mit 
einer anderen Waveform multipliziert zu werden (mit einer Konstanten 
oder mit Sägezahn oder mit DDS-Sinus). Mit deinem Wave Config Wert von 
0x30 multiplizierst du den Sinus aus dem RAM mit dem Sinus aus der DDS.

Um deinen selbst erzeugten SRAM-Sinus am Ausgang zu sehen würde ich 
erwarten, dass du ihn eher mit einem Constant Value multiplizieren 
musst. Also Wave Config auf 00 und DAC CST auf einen sinnvollen 
konstanten Wert.

Bei diesem recht komplexen Chip gibt es natürlich noch jede Menge 
weiterer Fallstricke, über die man stolpern kann.

Marcel D. schrieb:
> Einen Sinus mit unterschiedlichen Frequenzen zwischen 10 und 30MHz so
> sauber wie möglich erzeugen. Das mitgelieferte LabView Programm liefert
> mit der internen ROM-DDS ein unsauberes Signal.

Eine Sinuskurve mit nur 6 Stützstellen sieht nie besonders sauber aus. 
Wie gut sie ist, hängt primär von den Analogen Filtern hinter dem DAC 
ab. Ich glaube ehrlich gesagt nicht, dass du mit einer "angepassten LUT" 
bessere Kurvenformen hinkriegen kannst als mit der Sinus-LUT, die fest 
im DDS-ROM gespeichert ist.

von Egon D. (Gast)


Lesenswert?

Achim S. schrieb:

> Eine Sinuskurve mit nur 6 Stützstellen sieht nie
> besonders sauber aus. Wie gut sie ist, hängt primär
> von den Analogen Filtern hinter dem DAC ab.

"Welches Filter???"

SCNR

von Achim S. (Gast)


Lesenswert?

Ach ja, noch ein Punkt, der mir grade im Supermarkt an der Kasse noch 
einfiel: deine Zahlenwerte fürs RAM passen auch nicht zu einem Sinus. 
Der DAC und das SRAM haben 14 Bit. Der Wert 16384 (= 0x4000) ist 15 Bit 
lang und wird als 0 gespeichert.

8192 passt zwar ins SRAM, ich habe aber beim Drüberschauen nicht 
rausgekriegt, ob es als unipolare Binärzahl interpretiert wird (dann 
wäre es gerade der Mittelpunkt des DAC-Bereichs, wie von dir eigentlich 
gewünscht) oder als Zweierkomplementzahl (dann entspräche 8192=0x2000 
der größten negativen Zahl, also nicht das, was du eigentlich willst)

von Achim S. (Gast)


Lesenswert?

und gleich noch eins hinterher: du behandelst das Frequency Tuning Word 
als wäre es 32 Bit breit. Tatsächlich hat es nur 24 Bit. Dein Wert 
0x40000000 wird also ebenfalls als 0 interpretiert.

von Marcel D. (diablokiller999)


Angehängte Dateien:

Lesenswert?

Achim S. schrieb:
> Ich habe das Datenblatt nicht in Gänze durchgelesen, aber als möglicher
> Hinweis: die Daten aus dem RAM scheinen laut Schemazeichnung ja mit
> einer anderen Waveform multipliziert zu werden (mit einer Konstanten
> oder mit Sägezahn oder mit DDS-Sinus). Mit deinem Wave Config Wert von
> 0x30 multiplizierst du den Sinus aus dem RAM mit dem Sinus aus der DDS.
>
> Um deinen selbst erzeugten SRAM-Sinus am Ausgang zu sehen würde ich
> erwarten, dass du ihn eher mit einem Constant Value multiplizieren
> musst. Also Wave Config auf 00 und DAC CST auf einen sinnvollen
> konstanten Wert.
Habe die WaveConfig auf 0x0000 gestellt und DAC_CST auf 0x2000, geändert 
hat sich eigentlich nichts soweit ich sehen kann. (Bild)

> Eine Sinuskurve mit nur 6 Stützstellen sieht nie besonders sauber aus.
> Wie gut sie ist, hängt primär von den Analogen Filtern hinter dem DAC
> ab. Ich glaube ehrlich gesagt nicht, dass du mit einer "angepassten LUT"
> bessere Kurvenformen hinkriegen kannst als mit der Sinus-LUT, die fest
> im DDS-ROM gespeichert ist.
Das habe ich soweit nicht getestet, unser Layouter hat den Chip vorher 
auf dem EvalBoard mit einem LabView Programm betrieben, das von Analog 
Devices bereitgestellt wurde und den Output vermessen. Seine Aussage 
war, dass die 6 Punkt LUT im RAM ihm bessere Werte gab als das über die 
interne Routine laufen zu lassen.

Achim S. schrieb:
> Ach ja, noch ein Punkt, der mir grade im Supermarkt an der Kasse noch
> einfiel: deine Zahlenwerte fürs RAM passen auch nicht zu einem Sinus.
> Der DAC und das SRAM haben 14 Bit. Der Wert 16384 (= 0x4000) ist 15 Bit
> lang und wird als 0 gespeichert.
Anders als im Datenblatt, sind die Werte für den RAM 15 - 2 und nicht 13 
- 0. Es werden also nur die unteren Bits abgeschnitten. Das scheint aber 
nicht der einzige Fehler in dem Datenblatt zu sein, den ich schon 
ausfindig gemacht habe.
> 8192 passt zwar ins SRAM, ich habe aber beim Drüberschauen nicht
> rausgekriegt, ob es als unipolare Binärzahl interpretiert wird (dann
> wäre es gerade der Mittelpunkt des DAC-Bereichs, wie von dir eigentlich
> gewünscht) oder als Zweierkomplementzahl (dann entspräche 8192=0x2000
> der größten negativen Zahl, also nicht das, was du eigentlich willst)
Ob die signed oder unsigned sind, ist auch für mich noch immer ein 
großes Mysterium. Habe jetzt mal die LUT wie folgt ersetzt
1
uint16_t ramvalues[LUTSIZE] = { 0,0x7FFC,0x7FFC,0,0x8000,0x8000};
und bekomme eine ähnliche Form (siehe Bild).

> du behandelst das Frequency Tuning Word
> als wäre es 32 Bit breit. Tatsächlich hat es nur 24 Bit. Dein Wert
> 0x40000000 wird also ebenfalls als 0 interpretiert.
Das Tuningword besteht aus High und Low, das High ist 16Bit und das Low 
8 Bit breit (15 - 8), die 0x40000000 werden also zu 0x400000. Aber das 
Tuning Word hat gar keinen Einfluss auf die Kurve im Bild, ich kann das 
High Word auch auf 0 setzen und bekomme die selbe Frequenz angezeigt.

von Achim S. (Gast)


Lesenswert?

Marcel D. schrieb:
> Seine Aussage
> war, dass die 6 Punkt LUT im RAM ihm bessere Werte gab als das über die
> interne Routine laufen zu lassen.

Demnach muss er ja schon eine funktionierende Konfiguration gehabt haben 
- dann würde ich mich an der orientieren.

Marcel D. schrieb:
> geändert
> hat sich eigentlich nichts soweit ich sehen kann. (Bild)

Na ja, ein Faktor 2 in der Amplitude hat sich ergeben. Aber tastsächlich 
weiter kein schöner Sinus.

Marcel D. schrieb:
> und bekomme die selbe Frequenz angezeigt.

Für mich ist da keine ernstzunehmende Frequenz sondern ein einzelner 
negativer Puls (Für einen Einschalt-Glitch des DACs ist es wohl zu 
kräftig). Das Nachschwingen mit ~50MHz ist für mich eher ein 
Messartefakt (oder durch die evtl. vorhandene Filterschaltung bedingt) 
als ein Ergebnis der Signalerzeugung.

von Marcel D. (diablokiller999)


Lesenswert?

Achim S. schrieb:
> Demnach muss er ja schon eine funktionierende Konfiguration gehabt haben
> - dann würde ich mich an der orientieren.
Habe nun einen Sinus mit der internen DDS generieren können:
1
#include <SPI.h>
2
3
//#########################     REGISTERS     #########################
4
#define SPICONFIG_REG               0x0000
5
#define POWERCONFIG_REG             0x0001
6
#define CLKCONFIG_REG               0x0002
7
#define REFRESISTOR_REG             0x0003
8
#define DAC_AGAIN_REG               0x0007
9
#define DAC_RANGE_REG               0x0008
10
#define DAC_RESET_REG               0x000C
11
#define CAL_CONFIG_REG              0x000D
12
#define COMP_OFFSET_REG             0x000E
13
#define RAM_UPDATE_REG              0x001D
14
#define PAT_STATUS_REG              0x001E
15
#define PAT_TYPE_REG                0x001F
16
#define PAT_DELAY_REG               0x0020
17
#define DAC_DOFFSET_REG             0x0025
18
#define WAV_CONFIG_REG              0x0027
19
#define PAT_TIMEBASE_REG            0x0028
20
#define PAT_PERIOD_REG              0x0029
21
#define DAC_PAT_REG                 0x002B
22
#define DOUT_START_REG              0x002C
23
#define DOUT_CONFIG_REG             0x002D
24
#define DAC_CST_REG                 0x0031
25
#define DAC_DGAIN_REG               0x0035
26
#define SAW_CONFIG_REG              0x0037
27
#define DDS_TW32_REG                0x003E
28
#define DDS_TW1_REG                 0x003F
29
#define DDS_PHASE_OFFSET_REG        0x0043
30
#define TRIG_TW_SEL_REG             0x0044
31
#define DDS_CONFIG_REG              0x0045
32
#define TW_RAM_CONFIG_REG           0x0047
33
#define START_DLY_REG               0x005C
34
#define START_ADDR_REG              0x005D
35
#define STOP_ADDR_REG               0x005E
36
#define DDS_CYC_REG                 0x005F
37
#define CFG_ERROR_REG               0x0060
38
#define SRAM_LOW_ADDR               0x6000
39
#define SRAM_HIGH_ADDR              0x6FFF
40
//#########################     COMMANDS     #########################
41
#define PAT_WRITE                   0x0004
42
#define PAT_RUN                     0x0001
43
44
void write_lookup();
45
uint16_t write_register(uint16_t reg, uint16_t value);
46
47
const int SPICS = 10;
48
const int SPIMISO = 12;
49
const int SPIMOSI = 11;
50
const int SPISCK = 13;
51
52
uint16_t adreg = 0; 
53
uint16_t adval = 0;
54
uint16_t answer = 0;
55
56
57
void setup() {
58
  Serial.begin(115200);
59
  digitalWrite(SPICS, HIGH);
60
  // start the SPI library:
61
  SPI.begin();
62
  SPI.setClockDivider(SPI_CLOCK_DIV8); //Geschwindigkeit verlangsamen
63
  // initalize the  data ready and chip select pins:
64
  pinMode(SPIMISO, INPUT);
65
  pinMode(SPICS, OUTPUT);
66
  pinMode(SPIMOSI, OUTPUT);
67
  pinMode(SPISCK, OUTPUT);
68
69
  delay(100);
70
  write_lookup();
71
  // give the sensor time to set up:
72
  
73
}
74
void loop() {
75
  while(Serial.available() < 4);
76
  
77
  adreg = Serial.read() <<8;
78
  adreg += Serial.read();
79
  adval = Serial.read() << 8;
80
  adval += Serial.read();
81
  answer = write_register(adreg,adval);
82
  Serial.write(answer>>8);
83
  Serial.write(answer);
84
85
}
86
87
void write_lookup()
88
{
89
    write_register(PAT_STATUS_REG          ,PAT_WRITE);
90
    write_register(SPICONFIG_REG           ,0x0000);
91
    write_register(POWERCONFIG_REG         ,0x0e00);
92
    write_register(CLKCONFIG_REG           ,0x0000);
93
    write_register(REFRESISTOR_REG         ,0x0000);
94
    write_register(DAC_AGAIN_REG           ,0x4000);
95
    write_register(DAC_RANGE_REG           ,0x0000);
96
    write_register(DAC_RESET_REG           ,0x1f00);
97
    write_register(CAL_CONFIG_REG          ,0x0000);
98
    write_register(COMP_OFFSET_REG         ,0x0000);
99
    write_register(PAT_STATUS_REG          ,0x0000);
100
    write_register(PAT_TYPE_REG            ,0x0000);
101
    write_register(PAT_DELAY_REG           ,0x000E);
102
    write_register(DAC_DOFFSET_REG         ,0x0000);
103
    write_register(WAV_CONFIG_REG          ,0x1232);
104
    write_register(PAT_TIMEBASE_REG        ,0x0111);
105
    write_register(PAT_PERIOD_REG          ,0xffff);
106
    write_register(DAC_PAT_REG             ,0x0101);
107
    write_register(DOUT_START_REG          ,0x0003);
108
    write_register(DOUT_CONFIG_REG         ,0x0000);
109
    write_register(DAC_CST_REG             ,0x0000);
110
    write_register(DAC_DGAIN_REG           ,0x4000);
111
    write_register(SAW_CONFIG_REG          ,0x7E00);
112
    write_register(DDS_TW32_REG            ,0x0750);
113
    write_register(DDS_TW1_REG             ,0x7500);
114
    write_register(DDS_PHASE_OFFSET_REG    ,0x0000);
115
    write_register(TRIG_TW_SEL_REG         ,0x0002);
116
    write_register(DDS_CONFIG_REG          ,0x0000);
117
    write_register(TW_RAM_CONFIG_REG       ,0x0000);
118
    write_register(START_DLY_REG           ,0x0fa0);
119
    write_register(START_ADDR_REG          ,0x0000);
120
    write_register(STOP_ADDR_REG           ,0x0000);
121
    write_register(DDS_CYC_REG             ,0x7FFF);
122
    // Update all registers
123
    write_register(RAM_UPDATE_REG          ,0x0001);
124
    // Set RUN bit to start wave generation
125
    write_register(PAT_STATUS_REG          ,PAT_RUN);
126
}
127
128
129
uint16_t write_register(uint16_t reg, uint16_t value)
130
{   uint16_t answer;
131
    digitalWrite(SPICS,LOW);
132
    SPI.transfer16(reg);
133
    answer = SPI.transfer16(value);
134
    digitalWrite(SPICS,HIGH);
135
    return answer;
136
}

Trotzdem bin ich grad noch am grübeln, was ich in meiner Implementierung 
bis jetzt übersehen habe.

von Wolfgang (Gast)


Lesenswert?

Marcel D. schrieb:
> Weil ich mit einer angepassten LUT und Takt einen genaueren Takt/Teiler
> hinbekomme und so einen saubereren Sinus erhalte.

Bei 180MSPS wird ein sauberer 30MHz Sinus ein Wunschtraum bleiben. Da 
bleiben dir gerade mal 6 Punkte, um eine Sinusperiode zu approximieren.

von Egon D. (Gast)


Lesenswert?

Wolfgang schrieb:

> Marcel D. schrieb:
>> Weil ich mit einer angepassten LUT und Takt einen
>> genaueren Takt/Teiler hinbekomme und so einen
>> saubereren Sinus erhalte.
>
> Bei 180MSPS wird ein sauberer 30MHz Sinus ein
> Wunschtraum bleiben.

Klar... seit die EU flächendeckend den Einsatz von
Tiefpässen verboten hat und Ingenieure, die etwas
von Filterentwurf verstehen, überall vom Tode bedroht
sind, kann man praktisch keine sauberen Sinussignale
mehr erzeugen...

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.