www.mikrocontroller.net

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


Autor: Hans-Werner (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 ?

Autor: Hans-Werner (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Achso, sollte in VHDl sein.

Autor: Boris (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Hans-Werner (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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).
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.sine_package.all;

entity adc_out is
  port (
      clock     : in std_logic;
      SPI_SCK    : out std_logic;
      SPI_MOSI   : out std_logic;
      DAC_CLR    : out std_logic;
      DAC_CS      : out std_logic;
      led         : out std_logic_vector(7 downto 0);
      BTN_SOUTH   : in std_logic; -- enable
      BTN_EAST    : in std_logic; -- reset
      Rot_Center  : in std_logic
      );
end entity adc_out;

architecture rtl of adc_out is
  
  component sekunden_takt
  port
  (
      clock : in std_logic;
      takt  : out std_logic
  );
  end component sekunden_takt;
  
  component square_wave
  port (
      clock   : in std_logic;    -- Systemclock Pin
      wave_out  : out signed(11 downto 0)
      );
  end component square_wave;
  
  component sine_wave
  port
  (
      clock   : in std_logic;
      reset   : in std_logic;
      enable  : in std_logic;
      wave_out  : out signed(11 downto 0)
  );
  end component sine_wave;
  
  type wave_mod is (sine_wave_mod, square_wave_mod);
  signal modus : wave_mod := square_wave_mod;
  
  type dacStateType is (idle, sendBit, clockHigh, csHigh);
  signal dacState   : dacStateType := idle;
  signal enable     : std_logic := '1';
  signal old_BTN_SOUTH : std_logic;
  signal old_Rot_Center : std_logic;
  signal reset      : std_logic := '0';
  signal sine_wave_out   : signed(11 downto 0);
  signal square_wave_out : signed(11 downto 0);
  signal output     : unsigned(11 downto 0);
  -- 32 Bit Format
  signal dacCounter : integer range 0 to 31;
  signal DAC_OUT    : unsigned(31 downto 0);
   signal takt  : std_logic;
  
begin

   takt_teiler : sekunden_takt
  port map
  (
    clock => clock,
    takt  => takt
  );
  
  square_wave_label : square_wave
  port map
  (
    clock => clock,
    wave_out => square_wave_out
  );
  
  sine_wave_label : sine_wave
  port map
  (
    clock => clock,
    reset => BTN_EAST,
    enable => enable,
    wave_out => sine_wave_out
  );
  
  ----------------------------------------------------------------------------------------
  
  process (clock, BTN_SOUTH)
  begin
    if rising_edge(clock)
    then
       old_BTN_SOUTH <= BTN_SOUTH;
      -- Erkennung der steigenden Flanke
      if old_BTN_SOUTH = '0' and BTN_SOUTH = '1'
      then
        if enable = '0'
        then
          enable <= '1';          
        else
          enable <= '0';            
        end if;            
      end if;
    end if;
  end process;
  
  process (clock, BTN_EAST)
  begin
    if rising_edge(clock)
    then
      if BTN_EAST = '1'
      then
        reset <= not reset;
      end if;
    end if;
  end process;
  
  ----------------------------------------------------------------------------------------
  
  leds : process (clock)
  begin
    if rising_edge(clock)
    then  
      if modus = square_wave_mod
      then
        led(7 downto 6) <= "10";
      elsif modus = sine_wave_mod
      then
        led(7 downto 6) <= "01";
      end if;
      led(5 downto 2) <= "0000";  
      led(1) <= enable;
      led(0) <= reset;    
    end if;
  end process;
  
--  rotary_knob : process (clock)
--  begin
--    if rising_edge(clock)
--    then
--      old_Rot_Center <= Rot_Center;
--      -- Erkennung der Taktflanke
--      if old_Rot_Center = '0' and Rot_Center = '1'
--      then
--        if modus /= wave_mod'high
--        then
--          modus <= wave_mod'succ(modus);
--        else
--          modus <= wave_mod'low;
--        end if;
--      end if;
--    end if;
--  end process;
  
  ----------------------------------------------------------------------------------------
  
  process(clock, enable)
   begin
    if enable = '0' 
    then
            dacState <= idle;
     elsif rising_edge(clock) 
    then
      -- Wellenform berücksichtigen
      if modus = sine_wave_mod
      then
        output <= unsigned(sine_wave_out);
      elsif modus = square_wave_mod
      then
        output <= unsigned(square_wave_out);
      end if;
      
      -- Daten an den ADC senden
      case dacState is
         when idle =>
--            if takt = '1' 
--            then
              DAC_CS <= '0';
              SPI_SCK <= '0';
              dacState <= sendBit;
              dacCounter <= 31;
              -- 32 Bit Format 
              -- 4Bit Dont care & output & Address & Command & 8Bit Dont care
              DAC_OUT <= "0000" & output & "1111" & "0011" & "00000000";
--            end if;
         when sendBit =>
            SPI_SCK <= '0';
            -- SPI_MOSI <= DAC_OUT(23);
            SPI_MOSI <= DAC_OUT(31);
            DAC_OUT <= DAC_OUT(30 downto 0) & "0";
            dacState <= clockHigh;
         when clockHigh =>
            SPI_SCK <= '1';
            if dacCounter = 0 
            then
              dacState <= csHigh;
            else
              dacCounter <= dacCounter - 1;
              dacState <= sendBit;
            end if;
         when csHigh =>
            DAC_CS <= '1';
            dacState <= idle;
      end case;
    end if;
  end process;
    
  DAC_CLR <= '1';
    
end architecture rtl;

Autor: Kurt Georg (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Duke Scarring (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
entity dac_spi is
  port (
      SPI_SCK    : in std_logic;
      SPI_MOSI   : in std_logic;
      DAC_CLR    : in std_logic;
      DAC_CS     : in std_logic
      );
end entity dac_spi;

architecture testbench of dac_spi is
begin
  ...
  process (DAC_CS, DAC_CLR. SPI_SCK, SPI_MOSI)
  variable out_value : integer := 0;
  variable old_value : integer := integer'low;
  begin
    if DAC_CS = '0' then -- low active
       if DAC_CLR = '0' then
         out_value := 0;
       else
         -- catch here new output value
         -- google for spi slave or something
         out_value := ...;
       end if;
    end if;

    if old_value /= out_value then
       report "new DAC output value: " & integer'image(out_value);
       old_value := out_value;
    end if;

  end process;

end architecture testbench;


Autor: Hans-Werner (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 ?

Autor: Hans-Werner (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 ?

Autor: Duke Scarring (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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 ;-)

Autor: Kurt Georg (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: franz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
halo hanz-werner,

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

danke schonmal im voraus

gruss:
Franz

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [vhdl]VHDL-Code[/vhdl]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.