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

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

[FPGA] Créer un circuit logique pour piloter un télémètre à ultrasons (SRF05 / HC-SR04)
Un billet blog de f-leb

Le , par f-leb

0PARTAGES

Je vous présente aujourd'hui un contrôleur pour télémètre à ultrasons (de type SRF05 ou HC-SR04). La carte de développement est une carte FPGA Altera DE0-nano :


Le fonctionnement de ce type de capteurs est résumé sur les chronogrammes ci-dessous (Trigger et Echo séparés) :


Une impulsion de 10μs minimum sur la broche Trigger du module va préparer l’envoi d’un train d’ultrasons. Quand le train d’ultrasons part, le signal Echo bascule à l’état haut (environ 750μs après l’impulsion du signal Trigger). Le signal Echo retourne à l’état bas au retour du train d’ultrasons après réflexion sur l’obstacle (ou après un timeout de 30ms si aucun obstacle n’est rencontré). La largeur du signal Echo représente le temps pris par le train d’ultrasons pour faire l’aller-retour entre le capteur et l’obstacle, et donc une image de la distance de l'obstacle (la vitesse du son étant connue, 345m/s environ).

Exemple : si la largeur du signal Echo relevée est de 20ms, alors la distance de l’obstacle est d = 345x0,02/2=3,45m environ (le facteur 1/2 s’explique car le train d’ultrasons parcourt deux fois la distance, aller et retour après réflexion, entre le capteur et l’obstacle).
Un contrôleur pour ce capteur doit donc être capable au minimum de produire un signal Trigger sur commande, et de récupérer finalement une donnée numérique qui sera l’image de la largeur du signal Echo.

Le module ultrasonic.v du contrôleur présente les entrées-sorties suivantes :


Les entrées :
  • clk : le signal d’horloge (paramètre CLK_MHZ à renseigner, 50MHz par défaut) ;
  • echo : à connecter sur la broche Echo du capteur (Attention, le capteur est alimenté en 5V, le signal Echo du capteur doit être abaissé à 3,3V à l’entrée de la carte FPGA (diviseur de tension) ;
  • start : démarre une mesure avec une impulsion synchronisée sur une période du signal d’horloge.


Les sorties :
  • trigger : à connecter à la broche Trigger du capteur. La paramètre TRIGGER_PULSE_US renseigne la durée de l’impulsion (12μs par défaut);
  • new_measure : signal de contrôle, où une impulsion est générée quand la mesure est complète (signal Echo redescendu ou timeout) ;
  • timeout : signal de contrôle, une impulsion si timeout (paramètre TIMEOUT_MS, 20ms par défaut) ;
  • distance_raw : donnée image de la largeur du signal Echo codée sur 21 bits. Il s’agit en fait d’un compteur 21 bits qui s’incrémente à la fréquence de l’horloge jusqu’au retour des ultrasons (ou timeout). La valeur est garantie si vous lisez la valeur du compteur au moment de l’impulsion du signal new_measure.
    Durée du signal Echo en microsecondes = distance_raw / CLK_MHZ


Le capteur SRF05 a un timeout de 30ms si aucun obstacle n'est rencontré. On peut simuler un timeout inférieur avec le paramètre TIMEOUT_MS. Si le signal Echo n'est pas redescendu après ce timeout, le contrôleur bloque distance_raw, délivre une impulsion sur la sortie new_measure et sur la sortie timeout.

Chronogrammes de simulation fonctionnelle :


Le processus est décrit à la façon d’une machine à états finis (Finite State Machine) :


Voici le code du module ultrasonic.v :

Code verilog : Sélectionner tout
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
121
122
123
124
125
126
/* 
    * Un simple contrôleur pour les capteurs à ultrason 
      du type SRF05 ou HC-SR04 
       
    * Trigger et Echo séparés 
     
   /-----------------------------\ 
   |      ultrasonic sensor      | 
   |         controller          | 
   |                             |   
   |                      trigger --> 
   |                             | 
   |                  new_measure --> 
   |                      timeout --> 
--> echo                         |  
   |           distance_raw[20:0] ==> 
   |                             | 
--> start                        | 
--> clk                          | 
   |                             | 
   \-----------------------------/ 
 
    * clk : horloge (paramètre CLK_MHZ, 50 MHz par défaut) 
    * start : lance une mesure sur impulsion 
    * trigger : à connecter à la broche Trig du capteur 
                    (paramètre TRIGGER_PULSE_US, 12 us par défaut) 
    * echo : à connecter sur la broche Echo du capteur 
    * new_measure : 1 impulsion si la mesure est complète 
    * timeout : 1 impulsion si timeout (paramètre TIMEOUT_MS, 20 ms par défaut 
    * distance_raw: à lire sur impulsion de new_measure 
                         donnée image de la largeur du signal Echo codée sur 21 bits 
                          
      duree du signal echo en microsecondes = distance_raw / CLK_MHZ 
      
      Le capteur SRF05 a un timeout de 30 ms si aucun obtacle n'est rencontré. 
      On peut simuler un timeout inférieur avec le paramètre TIMEOUT_MS 
      Si le signal Echo n'est pas redescendu après ce TIME_OUT, le contrôleur 
      bloque distance_raw, délivre une impulsion sur la sorte new_measure et sur la sortie timeout.      
*/ 
 
 
module ultrasonic(clk, start, trigger, echo, distance_raw, new_measure, timeout); 
 
    input clk, start, echo; 
    output trigger, new_measure, timeout; 
    output reg [20:0] distance_raw; 
 
    parameter   CLK_MHZ = 50,               // fréquence horloge en MHz 
                TRIGGER_PULSE_US = 12,      // durée impulsion trigger en microsecondes 
                TIMEOUT_MS = 25;            // timeout en millisecondes 
     
    localparam  COUNT_TRIGGER_PULSE = CLK_MHZ * TRIGGER_PULSE_US; 
    localparam  COUNT_TIMEOUT = CLK_MHZ * TIMEOUT_MS * 1000; 
 
    reg [20:0] counter; 
     
    reg[2:0]  state, state_next; 
    localparam  IDLE            = 0, 
                TRIG            = 1, 
                WAIT_ECHO_UP    = 2, 
                MEASUREMENT     = 3, 
                MEASURE_OK      = 4; 
     
    always @(posedge clk) state <= state_next; 
     
    wire measurement; 
    assign measurement = (state == MEASUREMENT); 
     
    assign new_measure = (state == MEASURE_OK); 
     
    wire counter_timeout; 
    assign counter_timeout = (counter >= COUNT_TIMEOUT); 
     
    assign timeout = new_measure && counter_timeout; 
    assign trigger = (state == TRIG); 
     
    wire enable_counter; 
    assign enable_counter = trigger || echo;     
     
    always @(posedge clk) begin 
        if (enable_counter) 
            counter <=  counter + 21'b1; 
        else 
            counter <= 21'b0;   
    end  
     
    always @(posedge clk) begin 
        if (enable_counter && measurement) 
            distance_raw <= counter; 
    end 
     
    always @(*) begin 
        state_next <= state; // par défaut, état maintenu 
 
        case (state) 
            IDLE: begin // signal trigger sur impulsion start 
                if (start) state_next <= TRIG; 
            end 
             
            TRIG: begin // durée signal trig > 10us pour SRF05 
                if (counter >= COUNT_TRIGGER_PULSE) state_next <= WAIT_ECHO_UP; 
            end 
             
            WAIT_ECHO_UP: begin 
                // avec le SRF05, il y a un délai de 750us après le trig avant que le 
                // signal echo bascule à état haut. 
                if (echo) state_next <= MEASUREMENT; 
            end 
             
            MEASUREMENT: begin // attente echo qui redescend, ou timeout 
                if (counter_timeout || (~echo)) state_next <= MEASURE_OK; 
            end 
             
            MEASURE_OK: begin 
                state_next <= IDLE;          
            end 
 
            default: begin 
                state_next <= IDLE; 
            end  
        endcase 
         
    end 
                     
     
endmodule

Le code ci-dessous est une petite démonstration qui met en œuvre le télémètre à ultrasons SRF05 (voir la vidéo plus haut). Le bandeau de LED de la carte progresse en fonction de la distance de l'obstacle face au capteur :

Code verilog : Sélectionner tout
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
module top(CLOCK_50, TRIG, ECHO, LED); 
 
    input        CLOCK_50; 
    output       TRIG; 
    input        ECHO;  // /!\  Alim. du capteur 5V, abaisser le signal ECHO à 3v3 (diviseur tension) 
    output [7:0] LED; 
     
    wire start, new_measure, timeout; 
    wire [20:0] distance_raw; 
 
    reg [24:0] counter_ping; 
     
    localparam CLK_MHZ = 50;         // horloge 50MHz 
    localparam PERIOD_PING_MS = 60;  // période des ping en ms 
     
    localparam COUNTER_MAX_PING = CLK_MHZ * PERIOD_PING_MS * 1000; 
 
    //  avec horloge 50MHz et c=345m/s, distance_raw = 2900 * D(cm) 
    localparam D = 2900; 
 
 
    ultrasonic #(   .CLK_MHZ(50),  
                        .TRIGGER_PULSE_US(12),  
                        .TIMEOUT_MS(3) 
                    ) U1 
                        (   .clk(CLOCK_50), 
                            .trigger(TRIG), 
                            .echo(ECHO), 
                            .start(start), 
                            .new_measure(new_measure), 
                            .timeout(timeout), 
                            .distance_raw(distance_raw) 
                        ); 
         
    assign LED[6] = (distance_raw > 40*D); // distance > 40cm                  
    assign LED[5] = (distance_raw > 30*D); 
    assign LED[4] = (distance_raw > 25*D); 
    assign LED[3] = (distance_raw > 20*D); 
    assign LED[2] = (distance_raw > 15*D); 
    assign LED[1] = (distance_raw > 10*D); 
    assign LED[0] = (distance_raw >  5*D); // distance > 5cm 
    assign LED[7] = timeout;    // avec timeout=3ms => distance > 52cm                       
 
    assign start = (counter_ping == COUNTER_MAX_PING - 1); 
 
    always @(posedge CLOCK_50) begin 
        if (counter_ping == COUNTER_MAX_PING - 1) 
            counter_ping <= 25'd0; 
        else begin   
            counter_ping <= counter_ping + 25'd1; 
        end 
    end 
 
 
endmodule

Une erreur dans cette actualité ? Signalez-nous-la !