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 |