Forum: FPGA, VHDL & Co. Ansteuerung DA-Wandler Spartan 3 Starter Kit


von Hans-Werner (Gast)


Lesenswert?

Wr hat schon einmal den DA und den AD-Wandler auf dem Spartan 3 Starter 
Kit angesteuert ?
Ich habe mit Mühe und Not im Internet ein Beispiel zur DA-Ausgabe 
gefunden, welches jedoch nicht funktioniert. Das von mir erwartete 
Rechtecksignal erscheint leider nicht am Oszi.
Hat jemand ein funktionierendes Beispiel ?

von Hans-Werner (Gast)


Lesenswert?

Achso, sollte in VHDl sein.

von Boris (Gast)


Lesenswert?

Poste hier doch einfach mal deinen Code - ich denke dann bekommst du 
mehr Antworten...

Ich habe schon mal DACs mit einem XC9572 angesteuert - vielleicht kann 
ich ja was zu deinem VHDL-Code sagen...

von Hans-Werner (Gast)


Lesenswert?

Na schön. Hier die Entity für die Ausgabe an den DA-Wandler. Alles 
andere habe ich weggelassen. Es geht um den letzten Prozess. "output" 
enthält den 12bit Wert der ausgegeben werden soll. Bis auf den Ausgang 
des DA-Wandlers kann ich ja alles mittels Simulation überprüfen. Danach 
geht es nur per Hardware (Oszi).
1
library ieee;
2
use ieee.std_logic_1164.all;
3
use ieee.numeric_std.all;
4
use work.sine_package.all;
5
6
entity adc_out is
7
  port (
8
      clock     : in std_logic;
9
      SPI_SCK    : out std_logic;
10
      SPI_MOSI   : out std_logic;
11
      DAC_CLR    : out std_logic;
12
      DAC_CS      : out std_logic;
13
      led         : out std_logic_vector(7 downto 0);
14
      BTN_SOUTH   : in std_logic; -- enable
15
      BTN_EAST    : in std_logic; -- reset
16
      Rot_Center  : in std_logic
17
      );
18
end entity adc_out;
19
20
architecture rtl of adc_out is
21
  
22
  component sekunden_takt
23
  port
24
  (
25
      clock : in std_logic;
26
      takt  : out std_logic
27
  );
28
  end component sekunden_takt;
29
  
30
  component square_wave
31
  port (
32
      clock   : in std_logic;    -- Systemclock Pin
33
      wave_out  : out signed(11 downto 0)
34
      );
35
  end component square_wave;
36
  
37
  component sine_wave
38
  port
39
  (
40
      clock   : in std_logic;
41
      reset   : in std_logic;
42
      enable  : in std_logic;
43
      wave_out  : out signed(11 downto 0)
44
  );
45
  end component sine_wave;
46
  
47
  type wave_mod is (sine_wave_mod, square_wave_mod);
48
  signal modus : wave_mod := square_wave_mod;
49
  
50
  type dacStateType is (idle, sendBit, clockHigh, csHigh);
51
  signal dacState   : dacStateType := idle;
52
  signal enable     : std_logic := '1';
53
  signal old_BTN_SOUTH : std_logic;
54
  signal old_Rot_Center : std_logic;
55
  signal reset      : std_logic := '0';
56
  signal sine_wave_out   : signed(11 downto 0);
57
  signal square_wave_out : signed(11 downto 0);
58
  signal output     : unsigned(11 downto 0);
59
  -- 32 Bit Format
60
  signal dacCounter : integer range 0 to 31;
61
  signal DAC_OUT    : unsigned(31 downto 0);
62
   signal takt  : std_logic;
63
  
64
begin
65
66
   takt_teiler : sekunden_takt
67
  port map
68
  (
69
    clock => clock,
70
    takt  => takt
71
  );
72
  
73
  square_wave_label : square_wave
74
  port map
75
  (
76
    clock => clock,
77
    wave_out => square_wave_out
78
  );
79
  
80
  sine_wave_label : sine_wave
81
  port map
82
  (
83
    clock => clock,
84
    reset => BTN_EAST,
85
    enable => enable,
86
    wave_out => sine_wave_out
87
  );
88
  
89
  ----------------------------------------------------------------------------------------
90
  
91
  process (clock, BTN_SOUTH)
92
  begin
93
    if rising_edge(clock)
94
    then
95
       old_BTN_SOUTH <= BTN_SOUTH;
96
      -- Erkennung der steigenden Flanke
97
      if old_BTN_SOUTH = '0' and BTN_SOUTH = '1'
98
      then
99
        if enable = '0'
100
        then
101
          enable <= '1';          
102
        else
103
          enable <= '0';            
104
        end if;            
105
      end if;
106
    end if;
107
  end process;
108
  
109
  process (clock, BTN_EAST)
110
  begin
111
    if rising_edge(clock)
112
    then
113
      if BTN_EAST = '1'
114
      then
115
        reset <= not reset;
116
      end if;
117
    end if;
118
  end process;
119
  
120
  ----------------------------------------------------------------------------------------
121
  
122
  leds : process (clock)
123
  begin
124
    if rising_edge(clock)
125
    then  
126
      if modus = square_wave_mod
127
      then
128
        led(7 downto 6) <= "10";
129
      elsif modus = sine_wave_mod
130
      then
131
        led(7 downto 6) <= "01";
132
      end if;
133
      led(5 downto 2) <= "0000";  
134
      led(1) <= enable;
135
      led(0) <= reset;    
136
    end if;
137
  end process;
138
  
139
--  rotary_knob : process (clock)
140
--  begin
141
--    if rising_edge(clock)
142
--    then
143
--      old_Rot_Center <= Rot_Center;
144
--      -- Erkennung der Taktflanke
145
--      if old_Rot_Center = '0' and Rot_Center = '1'
146
--      then
147
--        if modus /= wave_mod'high
148
--        then
149
--          modus <= wave_mod'succ(modus);
150
--        else
151
--          modus <= wave_mod'low;
152
--        end if;
153
--      end if;
154
--    end if;
155
--  end process;
156
  
157
  ----------------------------------------------------------------------------------------
158
  
159
  process(clock, enable)
160
   begin
161
    if enable = '0' 
162
    then
163
            dacState <= idle;
164
     elsif rising_edge(clock) 
165
    then
166
      -- Wellenform berücksichtigen
167
      if modus = sine_wave_mod
168
      then
169
        output <= unsigned(sine_wave_out);
170
      elsif modus = square_wave_mod
171
      then
172
        output <= unsigned(square_wave_out);
173
      end if;
174
      
175
      -- Daten an den ADC senden
176
      case dacState is
177
         when idle =>
178
--            if takt = '1' 
179
--            then
180
              DAC_CS <= '0';
181
              SPI_SCK <= '0';
182
              dacState <= sendBit;
183
              dacCounter <= 31;
184
              -- 32 Bit Format 
185
              -- 4Bit Dont care & output & Address & Command & 8Bit Dont care
186
              DAC_OUT <= "0000" & output & "1111" & "0011" & "00000000";
187
--            end if;
188
         when sendBit =>
189
            SPI_SCK <= '0';
190
            -- SPI_MOSI <= DAC_OUT(23);
191
            SPI_MOSI <= DAC_OUT(31);
192
            DAC_OUT <= DAC_OUT(30 downto 0) & "0";
193
            dacState <= clockHigh;
194
         when clockHigh =>
195
            SPI_SCK <= '1';
196
            if dacCounter = 0 
197
            then
198
              dacState <= csHigh;
199
            else
200
              dacCounter <= dacCounter - 1;
201
              dacState <= sendBit;
202
            end if;
203
         when csHigh =>
204
            DAC_CS <= '1';
205
            dacState <= idle;
206
      end case;
207
    end if;
208
  end process;
209
    
210
  DAC_CLR <= '1';
211
    
212
end architecture rtl;

von Kurt Georg (Gast)


Lesenswert?

Hans-Werner, du hast zwei Probleme zu lösen, die du klar voneinander 
trennen musst:

1. Stelle den Ausgabewert bereit, und zwar nicht schneller, als der 
D/A-Wandler kann. Z.B. zum Anfang alle 10 Mikrosekunden einen Neuen.

2. Sobald ein neuer Ausgabewert bereitsteht, triggere einen 
Zustandsautomaten, der den Ausgabewert an den D/A-Wandler raustaktet.

Der Zustandsautomat könnte mit folgenden Zuständen arbeiten (nur 
ungefähr, da ich die genauen Notwendigkeiten für deine  D/A-Wandler 
nicht kenne):
a) idle
b) Zusammenstellung der auszugebenden Bitfolge
c) Ausgabe eines Bits mit Takt L (oder umgekehrt)
d) Takt H (oder umgekehrt)
----(c) und d) werden so oft wiederholt, wie Bits in der Bitfolge, dazu 
brauchst du einen Zähler 0 bis 23 (oder ähnlich)
e) Rückkehr in a)

Der Übergang von a) nach b) erfolgt, wenn ein neuer Ausgabewert 
bereitsteht.

In jedem Zustand sind die Steuersignale an den D/A-Wandler definiert.

Schaffe dir Debug-Möglichkeiten, dass du z.B. siehst, wie oft ein neuer 
Ausgabewert erscheint. Prüfe, ob die Steuersignale an den D/A-WAndler ok 
sind (siehe dessen Datenblatt).

Viel Erfolg!

von Duke Scarring (Gast)


Lesenswert?

Ich muß Kurt Georg noch ergänzen:

Schreibe Dir eine Testbench, die u.a. so tut als wenn sie ein DA-Wandler 
wäre. Die muß auch nicht synthetisierbar sein. Damit kannst Du erstmal 
solange simulieren/rumnspielen, bis dein "DA-Wandler" was erkennt.

Duke
1
entity dac_spi is
2
  port (
3
      SPI_SCK    : in std_logic;
4
      SPI_MOSI   : in std_logic;
5
      DAC_CLR    : in std_logic;
6
      DAC_CS     : in std_logic
7
      );
8
end entity dac_spi;
9
10
architecture testbench of dac_spi is
11
begin
12
  ...
13
  process (DAC_CS, DAC_CLR. SPI_SCK, SPI_MOSI)
14
  variable out_value : integer := 0;
15
  variable old_value : integer := integer'low;
16
  begin
17
    if DAC_CS = '0' then -- low active
18
       if DAC_CLR = '0' then
19
         out_value := 0;
20
       else
21
         -- catch here new output value
22
         -- google for spi slave or something
23
         out_value := ...;
24
       end if;
25
    end if;
26
27
    if old_value /= out_value then
28
       report "new DAC output value: " & integer'image(out_value);
29
       old_value := out_value;
30
    end if;
31
32
  end process;
33
34
end architecture testbench;

von Hans-Werner (Gast)


Lesenswert?

Ja, Testbench ist vorhanden.
Inzwischen habe ich auch ein Signal auf dem Oszi.
Ich hatte die Bits in der verkehrten Reihenfolge an den DAC geschoben.
Ist aber noch nicht der erwartete Sinus, sieht eher wie eine 
Überlagerung mehrerer Sinusschwingungen aus. Könnte natürlich sein das 
ich den DAC zu schnell ansteuere. Momentan gebe ich alle 1/(50MHz/256) 
Sekunden einen 24 Bit Wert aus. Die einzelnen Bits werden mit 50 MHZ 
getaktet.
Wann muss ich das Clear Signal (Aktive Low) zum Rücksetzen der DAC 
Ausgänge auf 0 Volt setzen ?
Ist dieses überhaupt erforderlich ?

von Hans-Werner (Gast)


Lesenswert?

Mir ist mal wieder ein Kronleuchter aufgegangen.
Der Sinus ist in einer Tabelle enthalten.
Jeden 50 MHz Takt wird ein Wert gelesen.
Der DA Wandler benötigt im 24bit Format für die Umsetzung der 12bit 
jedoch 48 Takte. Daher wird aus der Tabelle nur jeder 48 Eintrag 
verwendet.
Bei 128 Einträgen für einen Quadranten werden somit nur 2-3 Werte je 
Quadrant in den entsprechenden Analogwert ungesetzt.
Daher das verstümmelte Ausgangssignal. Das kann nicht funktionieren.
Der Prozess für die DA-Wandlung muss sich nach jeder Wandlung den 
nächsten Wert aus der Tabelle holen.
Noch eine Frage an alle Intelligenzbestien:
Wie kann ich eine Sinusschwingung mit unterschiedlichen Frequenzen 
ausgeben ?
a) Jeweils n Werte in der Tabelle überspringen. Der Sinus wird immer 
ungenauer.
b) Die Tabelle mit einem schnelleren Takt auslesen. Für jede 
Ausgabefrequenz würde dadurch ein eigener Taktgenerator benötigt. Man 
könnte mit einem DCM einen sehr hohen Takt erzeugen und diesen dann 
unterschiedlich teilen.
c) Noch andere Ideen ?

von Duke Scarring (Gast)


Lesenswert?

@Hans-Werner:

1. Wer eine (richtige) Testbench (tm) hat, braucht kein Oszi :-(

2. Für verschiedene Frequenzen such mal nach den Stichworten DDS und 
phase accumulator.

Duke

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

> a) Jeweils n Werte in der Tabelle überspringen.
> b) Die Tabelle mit einem schnelleren Takt auslesen.
Eine Kombination aus a) und b) wird zum Erfolg führen. Die Stichworte 
dazu wurden schon genannt.

> Ich hatte die Bits in der verkehrten Reihenfolge an den DAC geschoben.
Das hätte man schon mit einer einfachen TB erkannt...

> Wer eine (richtige) Testbench (tm) hat, braucht kein Oszi :-(
Naja, dann muß die Simulation aber schon tief ins Layout gehen...
Aber mit einer TB spart man sich oft das Einschalten des Oszis ;-)

von Kurt Georg (Gast)


Lesenswert?

> Der Sinus wird immer ungenauer.

Deswegen brauchst du ja auch ein analoges Tiefpassfilter hinter dem 
D/A-Wandler. Dieses Filter sollte alles oberhalb der halben Samplerate 
wegfiltern.

von franz (Gast)


Lesenswert?

halo hanz-werner,

kannst du auch bitte die codes deiner drei componenten jeweils posten..

danke schonmal im voraus

gruss:
Franz

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.