[FPGA] Créer un circuit logique pour décoder les signaux en quadrature d’un encodeur rotatif
par
, 15/08/2022 à 08h00 (6039 Affichages)
Les encodeurs rotatifs sont des dispositifs électromécaniques qui convertissent la position angulaire d'un axe en signaux électriques. Les encodeurs optiques qui fonctionnent en quadrature proposent deux sorties A et B en décalage de phase (90°). Le nombre d’impulsions par tour de l’axe étant une caractéristique spécifique de l’encodeur, compter les impulsions donne une image du décalage angulaire (et le nombre d’impulsions par seconde donne une image de la vitesse de rotation). L’avance ou le retard de phase d’une sortie par rapport à l’autre renseigne sur le sens de rotation.
Sens de rotation horaire (ClockWise), signal A en avance de phase sur B.
Sens de rotation antihoraire (CounterClockWise), signal B en avance de phase sur A
On fournit les chronogrammes d’une simulation ci-dessous :
En entrée de notre décodeur, les signaux A et B en quadrature sont délivrés en fonction de la rotation de l’axe à la même vitesse dans un sens sur la première moitié du temps (compteur POSCNT qui croît de 0 à 12), puis dans l’autre sens sur la seconde moitié (compteur POSCNT qui décroît de 12 à 0). Le signal de sortie UPDN renseigne aussi sur le sens de rotation de l’axe (état bas quand le compteur croît, état haut quand il décroît).
Pour une résolution maximale, le compteur doit évoluer sur tous les fronts montants et descendants des signaux A et B (signal interne count_enable, avec une impulsion pour chaque front détecté sur les signaux A et B).
Pour détecter les fronts, on reprend les principes détaillés dans un billet précédent [FPGA] Créer un circuit logique pour détecter les fronts d’un signal avec les signaux A et B qui traversent des bascules D en cascade :
Schéma partiel
Code verilog : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 reg [2:0] chA, chB; // chA et chB, tableaux de 3 registres … always @(posedge CLOCK_50) chA <= {chA[1:0], A}; always @(posedge CLOCK_50) chB <= {chB[1:0], B};
Les bascules en entrée chA[0] et chB[0] synchronisent les signaux de l’encodeur sur l’horloge. Chaque bascule traversée ensuite « retarde » le signal afin de déterminer les transitions :
En prenant en compte tous les fronts :
Soit en Verilog :
Code verilog : Sélectionner tout - Visualiser dans une fenêtre à part assign count_enable = (chA[1] ^ chA[2]) || (chB[1] ^ chB[2]);
La détection du sens de rotation est plus délicate, mais son équation logique est révélée si on pose la table de vérité :
On en déduit :
chA[1] chB[1] chA[2] chB[2] Incrément UPDN 1 1 0 1 +1 0 0 0 1 0 +1 0 0 1 0 0 +1 0 1 0 1 1 +1 0 1 0 0 0 -1 1 0 1 1 1 -1 1 1 1 1 0 -1 1 0 0 0 1 -1 1 x x x x 0 x
Et en Verilog :
Code verilog : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 assign count_direction = (chA[1] ^ chB[2]); … always @(posedge CLOCK_50) begin // sur front montant de l'horloge if (count_enable) begin UPDN <= count_direction; …
Le code complet :
Code verilog : 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 module top(CLOCK_50, A, B, POSCNT, UPDN); localparam N = 8; // compteur 8 bits input CLOCK_50, // horloge principale 50MHz A, B; // signaux de l'encodeur en quadrature output reg [N-1:0] POSCNT, // position, compteur N bits reg UPDN; // sens de rotation reg [2:0] chA, chB; // chA et chB, tableaux de 3 registres assign count_enable = (chA[1] ^ chA[2]) || (chB[1] ^ chB[2]); // ^ : opérateur Xor assign count_direction = (chA[1] ^ chB[2]); always @(posedge CLOCK_50) chA <= {chA[1:0], A}; always @(posedge CLOCK_50) chB <= {chB[1:0], B}; always @(posedge CLOCK_50) begin // sur front montant de l'horloge if (count_enable) begin UPDN <= count_direction; if (count_direction) POSCNT <= POSCNT - 1'b1; else POSCNT <= POSCNT + 1'b1; end end endmodule