Fabrication industrielle
Internet des objets industriel | Matériaux industriels | Entretien et réparation d'équipement | Programmation industrielle |
home  MfgRobots >> Fabrication industrielle >  >> Industrial programming >> VHDL

Concevoir un contrôleur de servo RC basé sur FPGA avec PWM précis

Les servos radiocommandés (RC) sont au cœur des avions, voitures et bateaux de modélisme. Ils traduisent une commande radio distante en une position angulaire précise. Comme les systèmes RC existent depuis des décennies, l'interface standard de l'industrie est la modulation de largeur d'impulsion (PWM) plutôt qu'un protocole purement numérique. Grâce au timing déterministe qu'offre un FPGA (field-programmable gate array), l'implémentation d'un PWM de haute précision est simple. Cet article présente un contrôleur de servo générique en VHDL qui fonctionne avec tout servo RC utilisant le PWM, incluant un banc de test de simulation et une implémentation réelle sur une carte FPGA Lattice iCEstick.

Comment fonctionne le contrôle PWM pour un servo RC

Bien que les bases du PWM soient traitées ailleurs, les servos RC nécessitent une approche légèrement différente. Le servo ignore le cycle de travail complet ; il n'échantillonne que la durée de la période haute de chaque impulsion. L'intervalle standard entre les impulsions est de 20 ms, ce qui correspond à une fréquence de 50 Hz. Dans cette fenêtre de 20 ms, le servo lit une impulsion dont la largeur varie généralement de 1 ms (position minimale) à 2 ms (position maximale), bien que les valeurs exactes varient selon le fabricant.

Concevoir un contrôleur de servo RC basé sur FPGA avec PWM précis

La figure illustre la relation temporelle : une période de 20 ms se répète, et le temps haut de chaque impulsion correspond directement à la position angulaire du servo.

Le contrôleur de servo VHDL

Nous allons construire un module VHDL générique qui accepte des paramètres génériques pour la fréquence d'horloge, la fréquence d'impulsion PWM, les largeurs d'impulsion minimales et maximales (en microsecondes), et le nombre de positions discrètes entre les extrêmes. Comme l'horloge FPGA tourne dans la gamme des mégahertz, l'arithmétique de compteurs entiers nous donne une résolution suffisante. Pour éviter les ambiguïtés d'arrondi, nous utiliserons la fonction round du paquet ieee.math_real, qui arrondit toujours loin de zéro.

library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use ieee.math_real.round;

Voici la déclaration d'entité pour le module servo :

entity servo is generic ( clk_hz : real; pulse_hz : real; -- Fréquence d'impulsion PWM min_pulse_us : real; -- Microsecondes à la position minimale max_pulse_us : real; -- Microsecondes à la position maximale step_count : positive -- Nombre de positions discrètes ); port ( clk : in std_logic; rst : in std_logic; position : in integer range 0 to step_count - 1; pwm : out std_logic ); end servo;

L'entrée position pilote le servo de son minimum (0) à son maximum (step_count‑1). La sortie pwm se connecte au fil de signal du servo (généralement jaune ou blanc). La plupart des FPGA sortent une logique 3,3 V, donc un décalage de niveau est nécessaire pour correspondre à l'entrée 5 V du servo.

La région déclarative

Nous commençons par définir une fonction auxiliaire qui convertit un temps en microsecondes en le nombre entier le plus proche de cycles d'horloge :

function cycles_per_us (us_count : real) return integer is begin return integer(round(clk_hz / 1.0e6 * us_count)); end function;

En utilisant cette fonction, nous calculons les constantes de timing principales :

constant min_count : integer := cycles_per_us(min_pulse_us); constant max_count : integer := cycles_per_us(max_pulse_us); constant min_max_range_us : real := max_pulse_us - min_pulse_us; constant step_us : real := min_max_range_us / real(step_count - 1); constant cycles_per_step : positive := cycles_per_us(step_us);

Ensuite, nous configurons le compteur PWM qui tourne à la fréquence d'impulsion souhaitée :

constant counter_max : integer := integer(round(clk_hz / pulse_hz)) - 1; signal counter : integer range 0 to counter_max; signal duty_cycle : integer range 0 to max_count;

Comptage des cycles d'horloge

Le processus suivant implémente le compteur libre :

COUNTER_PROC : process(clk) begin if rising_edge(clk) then if rst = '1' then counter <= 0; else if counter < counter_max then counter <= counter + 1; else counter <= 0; end if; end if; end if; end process;

Processus de sortie PWM

La sortie PWM est maintenue haute pendant autant de cycles de compteur que dicte la valeur actuelle de duty_cycle :

PWM_PROC : process(clk) begin if rising_edge(clk) then if rst = '1' then pwm <= '0'; else pwm <= '0'; if counter < duty_cycle then pwm <= '1'; end if; end if; end if; end process;

Calcul du cycle de travail

Au reset, duty_cycle prend par défaut la largeur d'impulsion minimale. Sinon, il est calculé à partir de l'entrée position :

DUTY_CYCLE_PROC : process(clk) begin if rising_edge(clk) then if rst = '1' then duty_cycle <= min_count; else duty_cycle <= position * cycles_per_step + min_count; end if; end if; end process;

Bien que cycles_per_step soit arrondi à l'entier le plus proche, l'erreur résultante est négligeable car l'horloge FPGA est des ordres de grandeur plus rapide que le taux d'impulsion de 50 Hz.

Le banc de test du servo

Pour vérifier la conception, nous avons construit un banc de test simple qui parcourt manuellement toutes les valeurs de position et capture la forme d'onde. Une horloge de test à 1 MHz accélère la simulation tout en reflétant un timing réaliste.

Constantes de simulation

constant clk_hz : real := 1.0e6; constant clk_period : time := 1 sec / clk_hz; constant pulse_hz : real := 50.0; constant pulse_period : time := 1 sec / pulse_hz; constant min_pulse_us : real := 1000.0; constant max_pulse_us : real := 2000.0; constant step_count : positive := 5;

Signaux DUT

signal clk : std_logic := '1'; signal rst : std_logic := '1'; signal position : integer range 0 to step_count - 1; signal pwm : std_logic;

Génération d'horloge :

clk <= not clk after clk_period / 2;

Instanciation du DUT

DUT : entity work.servo(rtl) generic map ( clk_hz => clk_hz, pulse_hz => pulse_hz, min_pulse_us => min_pulse_us, max_pulse_us => max_pulse_us, step_count => step_count ) port map ( clk => clk, rst => rst, position => position, pwm => pwm );

Séquenceur du banc de test

Le séquenceur réinitialise le DUT, puis itère sur toutes les valeurs de position, attendant une période d'impulsion entre chaque changement. Après avoir complété la séquence, la simulation se termine.

SEQUENCER : process begin wait for 10 * clk_period; rst <= '0'; wait for pulse_period; for i in 0 to step_count - 1 loop position <= i; wait for pulse_period; end loop; report "Simulation done. Check waveform."; finish; end process;

Forme d'onde de simulation du servo

L'instantané suivant montre la forme d'onde du banc de test. La sortie PWM est haute seulement pour les premiers cycles de compteur, correspondant à la largeur d'impulsion attendue pour chaque valeur de position.

Concevoir un contrôleur de servo RC basé sur FPGA avec PWM précis

Téléchargez les fichiers de projet complets pour reproduire la simulation.

Exemple d'implémentation FPGA

Ensuite, nous pilotons un vrai servo TowerPro SG90 depuis une carte Lattice iCEstick. Le servo nécessite un niveau logique 5 V, donc nous utilisons un décalage de niveau entre la broche FPGA et le fil de signal du servo.

Entité du module top

entity top is port ( clk : in std_logic; rst_n : in std_logic; -- Pull‑up pwm : out std_logic ); end top;

Signaux et constantes

Nous avons calibré les largeurs d'impulsion du servo expérimentalement : 0,5 ms à 2,5 ms produisent un balayage complet de 180°. Les constantes suivantes correspondent à l'horloge de l'iCEstick et aux paramètres du servo.

constant clk_hz : real := 12.0e6; -- Horloge iCEstick constant pulse_hz : real := 50.0; constant min_pulse_us : real := 500.0; -- 0,5 ms constant max_pulse_us : real := 2500.0; -- 2,5 ms constant step_bits : positive := 8; -- 0 à 255 constant step_count : positive := 2**step_bits;

Le compteur libre est large de 25 bits, donnant un temps de retour d'environ 2,8 secondes sur l'horloge 12 MHz.

constant cnt_bits : integer := 25; signal cnt : unsigned(cnt_bits - 1 downto 0);

Connexion des modules :

signal rst : std_logic; signal position : integer range 0 to step_count - 1; signal rom_addr : unsigned(step_bits - 1 downto 0); signal rom_data : unsigned(step_bits - 1 downto 0);

Instanciation du module servo

SERVO : entity work.servo(rtl) generic map ( clk_hz => clk_hz, pulse_hz => pulse_hz, min_pulse_us => min_pulse_us, max_pulse_us => max_pulse_us, step_count => step_count ) port map ( clk => clk, rst => rst, position => position, pwm => pwm );

Instanciation du compteur auto-emboîtant

COUNTER : entity work.counter(rtl) generic map ( counter_bits => cnt_bits ) port map ( clk => clk, rst => rst, count_enable => '1', counter => cnt );

Instanciation de la ROM sinus

SINE_ROM : entity work.sine_rom(rtl) generic map ( data_bits => step_bits, addr_bits => step_bits ) port map ( clk => clk, addr => rom_addr, data => rom_data );

Les affectations concurrentes câblent le compteur, la ROM et le servo ensemble :

position <= to_integer(rom_data); rom_addr <= cnt(cnt'left downto cnt'left - step_bits + 1);

Test sur la Lattice iCEstick

Le circuit complet est monté sur plaque d'essai : la sortie 3,3 V du FPGA est décalée à 5 V pour le servo. Le signal PWM se connecte à la broche 119 de l'iCEstick. Une fois alimenté, le servo oscille doucement entre 0° et 180°, marquant une brève pause aux extrêmes. Une vidéo de la forme d'onde PWM sur un oscilloscope démontre le timing correct.

Concevoir un contrôleur de servo RC basé sur FPGA avec PWM précis

Réflexions finales

Il existe de nombreuses façons d'implémenter un contrôleur de servo PWM en VHDL, mais l'utilisation de compteurs entiers garde la logique synthétisée simple — seulement des compteurs, des registres et des multiplexeurs. Le piège le plus courant est le débordement silencieux en arithmétique 32 bits, donc vérifiez toujours que les calculs intermédiaires restent dans les limites pour vos paramètres choisis.

Notez que ce style PWM est adapté aux servos RC. Pour les applications où le cycle de travail importe plus que la fréquence (ex: contrôle de puissance analogique), une stratégie PWM différente est préférable.

Lisez sur le contrôle de puissance analogique utilisant le PWM ici :
Comment créer un contrôleur PWM en VHDL

Prêt à essayer vous-même ? Téléchargez l'archive ZIP contenant le code VHDL complet, le banc de test ModelSim, le projet iCEcube2 et la configuration du programmeur en entrant votre adresse e-mail ci-dessous.

Dites-moi ce que vous en pensez dans les commentaires !

VHDL

  1. Contrôleur de puissance PWM : construction d'un driver de lampe à modulation de largeur d'impulsion
  2. Implémenter un contrôleur PWM en VHDL : Conception, simulation et démo FPGA
  3. Initialiser la RAM bloc FPGA depuis des fichiers texte avec la bibliothèque TEXTIO de VHDL
  4. Streaming de données de température en temps réel depuis un ppDAQC Pi‑Plate vers InitialState avec un Raspberry Pi headless
  5. Surveiller la température de votre maison avec un tableau de bord Raspberry Pi
  6. Guide étape par étape : Extraire les données d'automates avec l'IIoT pour des insights en temps réel
  7. Vos contrôleurs servo peuvent-ils être réparés ? Insights d'experts et solutions rentables
  8. Résolution de l'erreur 'C Axis Drive Not OK' sur variateur servo Osai-Prima Electro 97942050N
  9. Contrôle de ventilateur PWM 4 broches 25 kHz avec Arduino Uno – Construction, code et test