IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

VHDL Discussion :

Utilisation d'un ADC


Sujet :

VHDL

  1. #1
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut Utilisation d'un ADC
    Hello,

    J'essaie de faire quelque chose de simple : lire les données que me fourni un ADC, et balancer les 8 bits de poids fort sur 8 leds pour voir si ça marche (et par la suite, je trouverai un moyen de tracer le signal pour faire un oscilloscope).

    Mais je suis un peu perdu. Ya quelque chose qui marche pas, et se sais pas vraiment comment debugguer ça.

    l'ADC en question : http://cds.linear.com/docs/en/datasheet/2308fb.pdf (500ksps / 8 canaux / 12 bits).

    Pour la partie intéressante de la doc :





    Donc, une horloge de 40MHz, une mesure toutes les 2µs (80 cycles).
    Pour une mesure :
    convst est à 1 pendant les 2 à 3 premiers cycles (20 à 40 ns d'après la doc, 2/3 cycles donnent 25 / 37.5ns)
    sck fait 12 cycles à partir de 1.6µs (12 cycles à partir du 60ème / 80)
    on envoie les paramètres de la prochaine mesure via sdi durant les 6 premiers cycles de sck
    on récupère la mesure sur sdo pendant les 12 cycles de sck

    Les paramètres pour les mesures sont "100010" ou "100000" : Single-ended / canal 0 / unipolar (faut donner un 1 ou 0 pour une mesure unipolaire ?) / le dernier bit est inutile, il n'est pas pris en compte avec un signal convst court.

    Ce que j'ai fait :


    Une horloge de 50MHz, un PLL pour transformer ça en 40MHz.

    clk_adc compte jusque 80 et génère 5 signaux qui sont à 1 à certains moments précis :
    o_actconvst à 1 de 0 à 3
    o_actsck à 1 de 60 à 72
    o_actsdi à 1 de 60 à 66
    o_actsdo à 1 de 60 à 72
    o_sdocpy à 1 de 72 à 73

    Code VHDL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    library IEEE; 
    use IEEE.std_logic_1164.all;
    use IEEE.numeric_std.all;
     
    entity clk_adc is port(
    	i_clk_40M: in std_logic;
    	o_actconvst: out std_logic;
    	o_actsck: out std_logic;
    	o_actsdi: out std_logic;
    	o_actsdo: out std_logic;
    	o_sdocpy: out std_logic
    );
    end clk_adc;
     
    architecture arch_clk_adc of clk_adc is
     
    signal cnt: unsigned(6 downto 0) := (others => '0');
    signal act_sck: std_logic := '0';
    signal act_sdi: std_logic := '0';
    signal act_convst: std_logic := '1';
    signal sdo_done: std_logic := '0';
     
    begin
    process(i_clk_40M)
    begin
    	if rising_edge(i_clk_40M) then
    		if cnt = 79 then
    			cnt <= (others => '0');
    			act_convst <= '1';
    		else
    			cnt <= cnt + 1;
     
    			if cnt = 2 then
    				act_convst <= '0';
    			elsif cnt = 59 then
    				act_sdi <= '1';
    				act_sck <= '1';
    			elsif cnt = 65 then
    				act_sdi <= '0';
    			elsif cnt = 71 then
    				act_sck <= '0';
    				sdo_done <= '1';
    			elsif cnt = 72 then
    				sdo_done <= '0';
    			end if;
    		end if;
     
    		o_actconvst <= act_convst;
    		o_actsck <= act_sck;
    		o_actsdi <= act_sdi;
    		o_actsdo <= act_sck;
    		o_sdocpy <= sdo_done;
    	end if;	
    end process;
     
    end arch_clk_adc;
    (Il y à un retard d'un cycle, mais vu que le retard est le même partout c'est pas gênant je pense.)

    D'après une simulation ça marche correctement (il se passe rien sur la période non montrée):


    Filter copie simplement l'horloge si "i_filter" est à 1.
    Code VHDL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    library IEEE; 
    use IEEE.std_logic_1164.all;
    use IEEE.numeric_std.all;
     
    entity filter is port(
    	i_clk40M: in std_logic;
    	i_filter: in std_logic;
    	o_clk: out std_logic
    );
    end filter;
     
    architecture arch_filter of filter is
     
    begin
     
    o_clk <= i_clk40M and i_filter;
     
    end arch_filter;
    Le manque de process m'inquiète un peu vu que je rajoute un délai sur le signal d'horloge (?). Est-ce un problème ?

    Niveau simulation ça semble bon aussi :


    Send_data converti des données parallèle -> série pour les envoyer à l'ADC.
    Code VHDL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    library IEEE; 
    use IEEE.std_logic_1164.all;
    use IEEE.numeric_std.all;
     
    entity send_data is port(
    	i_clk40M: in std_logic;
    	i_actsdi: in std_logic;
    	i_actconvst: in std_logic;
    	o_data: out std_logic
    );
    end send_data;
     
    architecture arch_send_data of send_data is
     
    signal data: std_logic_vector(5 downto 0) := "000001"; -- ou "010001"
    signal s: std_logic_vector(5 downto 0);
     
    begin
    process(i_clk40M)
    begin
    	if rising_edge(i_clk40M) then
    		if i_actconvst = '1' then
    			s <= data;
    		elsif i_actsdi = '1' then
    			o_data <= s(0);
    			s(4 downto 0) <= s(5 downto 1);
    		end if;
    	end if;
    end process;
     
    end arch_send_data;

    Galère à simuler car j'arrive pas à donner la forme que je veux aux signaux d'entrées. Du coup j'utilise des horloges avec des fréquences différentes.
    Çà marche une fois sur deux sur la simulation à cause du fait que i_actconvst et i_actsdi puissent être tout deux à 1 sur la simulation, alors qu'ils ne peuvent normalement pas (ils sortent de clk_adc). Quand ils ne sont pas tous les deux à 1, ça marche.



    Et enfin store_data fait l'inverse : lis les données de l'ADC (en série) et les écrit en parallèle pour les leds.
    Code VHDL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    library IEEE; 
    use IEEE.std_logic_1164.all;
    use IEEE.numeric_std.all;
     
    entity store_data is port(
    	i_clk40M: in std_logic;
    	i_actsdo: in std_logic;
    	i_sdocpy: in std_logic;
    	i_data: in std_logic;
    	o_data: out std_logic_vector(11 downto 0)
    );
    end store_data;
     
    architecture arch_store_data of store_data is
     
    signal buff: std_logic_vector(11 downto 0);
     
    begin
    process(i_clk40M)
    begin
    	if rising_edge(i_clk40M) then
    		if i_actsdo = '1' then
    			buff(10 downto 0) <= buff(11 downto 1);
    			buff(11) <= i_data;
    		elsif i_sdocpy = '1' then
    			o_data <= buff;
    		end if;
    	end if;
    end process;
     
    end arch_store_data;

    Même problème pour la simulation, i_actsdo et i_sdocpy ne peuvent normalement pas être à 1 tous les deux en même temps.
    o_data(0) contient le bit de poids fort, o_data(11) le bit de poids faible.



    Au niveau de l'assignation des pins, je pense que c'est bon (grande image) : http://i.imgur.com/rIBVtNc.png

    Dernier truc, concernant l'utilisation, pour le moment j'essaie simplement avec une pile pour voir si je retrouve bien 1.5 V (ou une mesure proche).



    J'ai essayé en mettant le - de la pile sur le pin 10, et le + sur le pin 2. Mais je suis vraiment pas sur de ça.
    Le +5V sur le pin 1 il est fourni ? Ou faut que je le fournisse ?
    La masse commune (pin 10) c'est censée être quoi exactement dans mon cas ? Masse de la carte ? Masse de la pile ?

    Normalement avec tout ça, les 8 leds m'affichent les 8 bits de poids fort de la mesure, avec led0 = MSB et led7 = LSB.
    Avec rien de branché, les leds m'affichent 1111 1111 (1 = allumé, je m'attendais au contraire, que tout soit éteint oO).
    Avec la pile j'ai 0000 1101. (Mesure unipolaire ou dipolaire ne change rien).

    Ce qui me donnerait une mesure entre 208 et 223mV (ou entre 3.873 et 3.888V si une led allumée veut dire 0) pour ma pile.

    Bref ya évidement quelque chose qui déconne, mais je sais pas où ni comment chercher .
    Par où je commence ?

  2. #2
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    Petit up,

    J'ai trouvé comment utiliser SignalTap pour déboguer. Je m'attendais vraiment pas à quelques chose d'aussi poussé. Bonne nouvelle donc. (En plus c'est simple à utiliser <3)



    J'avais un décalage d'un cycle au niveau de sdi / sdo (send_data / store_data qui provoquent ces décalages). C'est corrigé, ainsi que 2/3 autres petites modifications.

    Mais ça marche toujours pas
    Maintenant les leds m'affichent toujours 1110 0001.

    D'après SignalTap, les données sont bien envoyées sur sdi (et au bon moment) : "100010";
    Je récupère un résultat via sdo : 1110 0001 0000; ça correspond à l'état des leds.

    Mais cette valeur n'a pas de sens.

    Une idée du problème ?

  3. #3
    Membre expérimenté

    Homme Profil pro
    Collégien
    Inscrit en
    Juillet 2010
    Messages
    545
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Afghanistan

    Informations professionnelles :
    Activité : Collégien

    Informations forums :
    Inscription : Juillet 2010
    Messages : 545
    Points : 1 429
    Points
    1 429
    Par défaut
    Bonjour,

    Bravo pour ce post bien détaillé!

    Voila comment je ferais.

    Utilise la PLL pour générer un 80 Mhz à partir du 50Mhz.

    Utilise ce 80 Mhz pour faire tourner une machine d'état.
    Pars du principe que l'horloge 40Mhz que tu doit générer n'est plus une horloge mais un signal comme un autre synchrone au 80 Mhz.
    Tu verras que toute ton interface avec cet ADC tiendra dans une seule entité de moins de 300 Lignes.

    Ecrit un model non synthétisable de ton ADC à partir de ce que tu as compris de la DOC. Pour valider ton interface dans un testBench.

    Oublie les outils de design en schématique. Utilise un langage fait pour : VHDL ou Verilog.

    Bon courage.

  4. #4
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    Citation Envoyé par mith06 Voir le message
    Pars du principe que l'horloge 40Mhz que tu doit générer n'est plus une horloge mais un signal comme un autre synchrone au 80 Mhz.
    J'y ai pensé au début, mais je pensais pas qu'il était possible de générer une horloge de fréquence supérieure.

    Citation Envoyé par mith06 Voir le message
    Oublie les outils de design en schématique. Utilise un langage fait pour : VHDL ou Verilog.
    Je trouvais ça pratique justement : les entités définies en VHDL, et reliées entre elles via un schéma. C'est à éviter ? Pour quelle raison ? Ça génère du code dégueulasse ?

    Merci pour ta réponse en tout cas, je teste ça.

  5. #5
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    Ça marche !

    J'en profite pour demander, Pour faire une machine à état, j'ai vu 2 modèles
    Code VHDL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    process (clk, et tous les signaux utilisés)
    begin
       if rising_edge(clk) then
          state <= next_state;
       end if;
     
       case state is
          when state1 =>
             next_state <= state2;
     
          when state2 =>
             next_state <= state3;
     
          when others =>
             next_state <= state1;
       end case;
    end process;

    Et
    Code VHDL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    process (clk)
    begin
       if rising_edge(clk) then
          case state is
             when state1 =>
                state <= state2;
     
             when state2 =>
                state <= state3;
     
             when others =>
                state <= state1;
          end case;
       end if;
    end process;

    L'un est meilleurs que l'autre ? Quand utiliser l'un et l'autre ? (J'étais parti sur le premier, mais j'avais un soucis avec une boucle qui me faisais sortir d'un état au bout d'un cycle au lieu de 6).

    Mon code (assez crade je pense), si vous avez des conseils / commentaires, je prend :
    Code VHDL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    library IEEE; 
    use IEEE.std_logic_1164.all;
    use IEEE.numeric_std.all;
     
    entity adc_state is port(
    	i_clk_80M: in std_logic;
    	i_sdo: in std_logic;
    	o_convst: out std_logic;
    	o_sck: out std_logic;
    	o_sdi: out std_logic;
    	o_data: out std_logic_vector(11 downto 0)
    );
    end adc_state;
     
    architecture arch_adc_state of adc_state is
     
    signal cnt: unsigned(7 downto 0) := (others => '0');
    signal state: std_logic_vector(2 downto 0) := (others => '0');
    signal sdi_data: std_logic_vector(4 downto 0);
    signal sdo_buffer: std_logic_vector(11 downto 0);
     
    constant ST_CONV_START : std_logic_vector(2 downto 0) := "000";
    constant ST_WAIT_CONV : std_logic_vector(2 downto 0) := "001";
    constant ST_FIRST_PART_DATA : std_logic_vector(2 downto 0) := "010";
    constant ST_SECOND_PART_DATA : std_logic_vector(2 downto 0) := "011";
    constant ST_LAST_PART_DATA : std_logic_vector(2 downto 0) := "100";
    constant ST_WAIT_NEXT_CONV : std_logic_vector(2 downto 0) := "101";
     
    begin
     
    process(i_clk_80M)
    begin
    	if rising_edge(i_clk_80M) then
    		if cnt = 159 then
    			cnt <= (others => '0');
    		else
    			cnt <= cnt + 1;			
    		end if;	
     
    		case state is
    		when ST_CONV_START => -- convst
    			if cnt = 6 then
    				state <= ST_WAIT_CONV;
    				o_convst <= '0';
    				sdi_data <= "01000";
    			else
    				state <= ST_CONV_START;
    				o_convst <= '1';
    				o_sdi <= '1';
    			end if;
     
    		when ST_WAIT_CONV => -- wait for conversion
    			if cnt = 119 then
    				state <= ST_FIRST_PART_DATA;
    			else
    				state <= ST_WAIT_CONV;
    			end if;
     
    		when ST_FIRST_PART_DATA => -- sdi, sdo, sck
    			if cnt = 131 then
    				state <= ST_SECOND_PART_DATA;
    				o_sdi <= sdi_data(0);
    				sdi_data(3 downto 0) <= sdi_data(4 downto 1);
    				o_sck <= not cnt(0);
     
    			else
    				state <= ST_FIRST_PART_DATA;
     
    				if cnt(0) = '0' then
    					o_sck <= '1';
    					sdo_buffer <= sdo_buffer(10 downto 0) & i_sdo;
    				else
    					o_sck <= '0';
    					o_sdi <= sdi_data(0);
    					sdi_data(3 downto 0) <= sdi_data(4 downto 1);
    				end if;
    			end if;
     
    		when ST_SECOND_PART_DATA => -- sck / sdo
    			if cnt = 143 then
    				state <= ST_LAST_PART_DATA;		
    				o_sck <= '0';
     
    			else
    				state <= ST_SECOND_PART_DATA;				
    				if cnt(0) = '0' then
    					o_sck <= '1';
    					sdo_buffer <= sdo_buffer(10 downto 0) & i_sdo;
    				else
    					o_sck <= '0';
    				end if;
    			end if;
     
    		when ST_LAST_PART_DATA => -- sdo last bit
    			if cnt = 145 then
    				state <= ST_WAIT_NEXT_CONV;				
    				o_data <= sdo_buffer;
     
    			else
    				state <= ST_LAST_PART_DATA;				
    				if cnt(0) = '0' then
    					sdo_buffer <= sdo_buffer(10 downto 0) & i_sdo;
    				end if;
    			end if;
     
    		when ST_WAIT_NEXT_CONV => -- wait for next conversion
    			if cnt = 0 then
    				state <= ST_CONV_START;				
    				o_convst <= '1';
    			else
    				state <= ST_WAIT_NEXT_CONV;
    			end if;
     
    		when others => state <= ST_CONV_START;
     
    		end case;
    	end if;
    end process;
     
    end arch_adc_state;

  6. #6
    Membre expérimenté

    Homme Profil pro
    Collégien
    Inscrit en
    Juillet 2010
    Messages
    545
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Afghanistan

    Informations professionnelles :
    Activité : Collégien

    Informations forums :
    Inscription : Juillet 2010
    Messages : 545
    Points : 1 429
    Points
    1 429
    Par défaut
    via un schéma. C'est à éviter ? Pour quelle raison ? Ça génère du code dégueulasse ?
    Oui généralement on évite, en tout cas cela aurait tendance à montrer que tu ne maîtrise pas ton sujet.

    L'un est meilleurs que l'autre ?
    Oui le deuxième.

    Quand utiliser l'un et l'autre ?
    Le premier JAMAIS!!!!
    Les FPGA permettent de générer de la logique SYNCHRONE (à une horloge).
    Mettre autre chose dans la liste de sensibilité qu'une horloge, rend le process asynchrone. Donc par définition antagoniste à l'utilisation des FPGA.
    La seule exception à cette règle est le RESET (qui sera donc asynchrone).

    Mon code (assez crade je pense)
    pas vraiment, j'ai vu bien pire!!!!

  7. #7
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    Merci pour ces précisions.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. utiliser les tag [MFC] [Win32] [.NET] [C++/CLI]
    Par hiko-seijuro dans le forum Visual C++
    Réponses: 8
    Dernier message: 08/06/2005, 15h57
  2. Réponses: 4
    Dernier message: 05/06/2002, 14h35
  3. utilisation du meta type ANY
    Par Anonymous dans le forum CORBA
    Réponses: 1
    Dernier message: 15/04/2002, 12h36
  4. [BCB5] Utilisation des Ressources (.res)
    Par Vince78 dans le forum C++Builder
    Réponses: 2
    Dernier message: 04/04/2002, 16h01
  5. Réponses: 2
    Dernier message: 20/03/2002, 23h01

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo