Catégories
Commande des moteurs Projets Arduino Projets électroniques Projets FPGA

Projet électronique FPGA #10 : Commande factorielle d’un moteur à CC avec Arduino et FPGA

Commande factorielle d’un moteur à CC avec Arduino et FPGA - Schéma de principe

Objectifs du projet

Répondes aux interrogations suivantes:

  • C’est quoi l’accélération matérielle ?
  • Pourquoi l’accélération matérielle ?
  • Comment câbler l’Arduino avec FPGA ?
  • Comment mettre en pratique l’accélération matérielle ?
  • Comment s’initier aux projets multi-contraintes (rapidité, technologie, …) ?
  • Comment utiliser les ports analogiques comme ports numériques d’Arduino?
  • Comment faire son adaptateur de niveau du 5V au 3.3V pour la liaison Arduino-FPGA?
  • Et autres astuces pratiques

Rappels des cours

  1. Accélération algorithmique et matérielle des méthodes d’estimation de cartes d’abondances en imagerie hyper-spectrale
  2. Accélération matérielle pour le rendu de scènes multimédia vidéo et 3D
  3. Accélération algorithmique et matérielle des méthodes d’estimation de cartes d’abondances en imagerie hyperspectrale
  4. Accélération matérielle pour l’imagerie sismique : modélisation, migration et interprétation
  5. Accélération par l’utilisation du matériel graphique pour la représentation et le rendu volumique
  6. Autres cours

Projets connexes

Analyse de fonctionnement

Dans ce mini-projet on va aborder la notion d’accélération matérielle, en particulier l’accélération d’une fonction mathématique sur FPGA dans un exemple pratique et ludique. Il consiste à Contrôler la vitesse d’un moteur à courant continu(CC) avec un signal PWM du type factoriel. L’objectif principal du projet est la mise en pratique de l’accélération matérielle, il consiste à replacer la fonction mathématique qui calcule la factorielle d’un signal par un composant matériel à base du FPGA afin d’augmenter la rapidité de calcul en exploitant la puissance des FPGA. Dans ce dernier, on va se focaliser sur la partie fonctionnelle et la mise en oeuvre du processus. On analysera en détail les performances de l’accélération dans un projet futur.

C’est quoi l’accélération matérielle ?

L’accélération matérielle consiste l’utilisation d’un circuit matériel pour effectuer une tâche plus efficacement ou déléguer une fonction réalisée par un processeur (CPU) d’une façon séquentielle. Le nom de l’accélération matérielle issu du câblage matériel d’un algorithme ou calcul complexe, et non de manière logicielle comme dans le cas courant par les processeurs (microcontrôleur, microprocesseur).

C’est quoi les types de l’accélération matérielle ?

  1. Accélération 3D : Très utilisé dans les opérations du traitement d’image (conversion des images (2D-D), morphologie mathématique, détection de contour,…). Exemple : Accélération du traitement graphique par les GPU (jeux vidéo, algorithme de traitement d’image en temps réel,…)
  2. Accélération physique : Utilisation des processeurs physiques PPU (Physics Processus Unit) pour la simulation des phénomènes physiques PPU lourdement réalisables par les processeurs basiques (Exemple : Matlab sur CPU de son ordinateur)
    Accélération mathématique : Accélération d’une fonction mathématique (factorielle, opérations en virgule flottante, fonctions trigonométriques, opérations sur les matrices, identification des systèmes, domaine d’apprentissage profond par les réseaux de neurones,…)
  3. Accélération audio : Accélération des opérations des traitements audio (filtrage, compression, reconnaissance audio temps réel, …). On utilise souvent un processeur dédiée au traitement du signal DSP (Digital Signal Processing) ou FPGA.
  4. Plus de détails

Pourquoi l’accélération matérielle ?

  • Réduire le temps d’exécution d’une tâche
  • Parallélisation des calculs
  • Réduire la charge du processeur principal
  • Assurer des performances constantes (Temps d’exécution constant quelle que soit la charge du processeur, cette particularité n’est pas toujours assurée par les CPU)
  • Résolution du problème temps réel pour les systèmes rapides (Acquisition des données hautes débit (quelques dizaines Gb/s voire centaines Gb/s))
  • Etc.

Rôle du potentiomètre

Il sera alimenté par une tension 5V et branché avec la carte Arduino via l’entrée A0. La lecture du convertisseur analogique numérique sur 10 bits donne une valeur variante entre 0(pour 0V) et 1023 (pour la valeur maximale de la tension = 5V). Si on décide d’alimenter le convertisseur avec 3.3V, on obtiendra des valeurs allant de 0 à (3.3/5)*1023=675 !

L’étape suivante consiste à convertir le niveau 10 bits en 3 bits de la valeur acquise qu’elle sera ensuite envoyée à la carte FPGA afin de calculer son factorielle.

Ordonnancement d’exécution des tâches du programme

  1. Lecture de l’entrée analogique A0 sur 10 bits (Val_10)
  2. Convertir la valeur du 10 bits au 3 bits (Val_10=>Val_3)
  3. Envoyé la donnée à la carte FPGA sur 3 bits (Val_3)
  4. Calcule de la factorielle de la valeur acquise par FPGA (Fac3=Val_3 !) sur 8 bits
  5. Envoyé le résultat du calcul aux LEDs sur 8 bits au même temps envoyé les 7 bits du poids faible (D0 à D6)à la carte Arduino (Fac3_7bits)
  6. Acquisition de la donnée Fac3_7bits par la carte Arduino
  7. Multiplier la donnée par 2 : Fac3_end= Fac3_7bits*2
  8. Générer un signal PWM de rapport cyclique égal à Fac3_end
  9. Transférer le signal à la sortie numérique D9 pour commander le moteur à CC

Comment réduire la résolution binaire du convertisseur A/N avec la carte Arduino ?

Par défaut la résolution du convertisseur A/N est égale à 10 bits (1024 valeurs possibles ou de 0 à 1023). Dans notre application on aura besoin en réalité que du nombre réduit des valeurs : 0, 1, 2, 3 et 4 pour le calcul de la factorielle (voir les projets 7 et 8 pour plus des détails), donc la résolution 3 bits (8 valeurs, de 0 à 7) est suffisante pour dans notre cas. Alors comment passer du 10 bits au 3 bits concrètement ? On va faire appel à « la règle d’Or » qui marche à tous les coups pour répondre à cette question, c’est la règle de trois illustrée dans l’équation ci-dessous :

Valeur_3=Valeurs_10*7/1023

Exemple 1:

Si Valeurs_10=1023 (tension =5V, 10 bits) alors Valeur_3=1023*7/1023=7 (correspond à 5V sur 3 bits)

Exemple 2 :

clear all; close all; clc;

% Paramètres convertisseur A/N
n_10=10;
n_3=3;
max_10=2^n_10-1;    % 1023
max_3=2^n_3-1;      % 7
V_max=5;            % Amplitude maxe du signal

% Exemple du signal à l'entree (sin) analogique
N=1e3;
t=linspace(0,2*pi,N);
sin_t=V_max*(sin(t)+1)*0.5;

% Signal acqui par le convertisseur A/N d'Arduino
Val_10=sin_t*max_10/V_max;
Val_10=round(Val_10(:));

% Conversion en 10 bits en 3 bits
Val_3=Val_10*max_3/max_10;
Val_3=round(Val_3(:));

% Affichage
subplot(311); plot(t,sin_t,'r'); xlim([t(1) t(end)]); ylim([0 max(sin_t)]);
xlabel('Temps(s)'); ylabel('sin(t)');grid on;

subplot(312); plot(t,Val_10,'r'); xlim([t(1) t(end)]); ylim([0 max(Val_10)]);
xlabel('Temps(s)'); ylabel('Val_{10}(t)');grid on;

subplot(313); plot(t,Val_3,'r'); xlim([t(1) t(end)]); ylim([0 max(Val_3)]);
xlabel('Temps(s)'); ylabel('Val_{3}(t)');grid on;

Comment réduire la résolution binaire du convertisseur A-N avec la carte Arduino 1

Comment utiliser les ports analogiques comme ports numériques avec Arduino ?

Dès fois le nombre des Entrées/Sorties (ES) numériques ne suffit pas pour notre projet. Dans notre cas on utilise la carte Arduino mini, le nombre des ES numériques est limités à 12 (D2 à D13). En effet, le nombre des entrées analogiques est égal à 8 (A0-A7), comme vous l’avez constaté, on dispose uniquement d’une seule entrée de tension (A0) les autres entrées ne sont pas utilisées, c’est pour cette raison on va occuper les 7 entrées restantes comme étant des entrées numériques. La technique consiste à implémenter un comparateur numérique avec un seuil constant est égal à 2.5V (ou 512) : On effectué la lecture de l’entrée analogiques puis on positionne une variable à 1 si la valeur acquise est supérieure à 512 sinon à 0 ! On fera la même chose pour toutes les entrées analogiques. Ci-dessous une aperçue du code Arduino:

// Bit 1
val_1 = analogRead(From_FPGA_pin_2);
if (val_1 > 512)
{
        val_1 =1;
}
else
{
        val_1=0;
}

Comment faire son adaptateur de niveau du 5V au 3.3V pour la liaison Arduino-FPGA ?

On alimente souvent la carte Arduino avec le câble USB de son ordinateur, par défaut le niveau de tension d’alimentation de la liaison USB est égale à 5V. En pratique on n’est pas loin de cette derrière. Par conséquent, lorsqu’on positionne une sortie numérique à 1, on envoie un niveau de tension 5V et 0V pour le niveau 0. Dans notre projet on utilise aussi une carte FPGA (Xilinx, Spartran 3A), le niveau de tension utilisé par ce dernier est égal à 3.3. Les ES ne peuvent pas supporter une tension au delà de 4V (maxi). Donc si on câble directement une sortie Arduino avec une entrée FPGA on risque de griller le port FPGA ! C’est pour cette raison on doit faire quelques modifications avant de brancher la carte FPGA avec l’Arduino.

Note : La liaison FPGA vers Arduino (lier une sortie FPGA avec une entrée Arduino) est sécurisée et compatible (la tension 3.3V <5V, la logique 3.3V est compatible avec la logique 5V).

Notre astuce pour réduire les niveaux des tensions des sorties numériques consiste à alimenter la carte Arduino avec une source de tension externe de 5V via l’entrée Vin (pas par la liaison USB). Attention : il ne faut pas brancher le câble USB avec son ordinateur lorsque la carte FPGA est branchée avec l’Arduino, assurez-vous bien que la liaison FPGA n’est pas effectuée au moment de la programmation du kit Arduino. En effet, lorsque l’Arduino est alimenté par 5V (via Vin), on constate que le niveau des tensions des sorties numériques est de l’ordre de 3.6V ! Donc on est au dessous du 4V ! Pour plus de sécurité, vous pouvez utiliser une tension Vin égale à 4V, dans ce projet on va utiliser une alimentation d’environ 5V. Ci-dessous les images illustratives des divers niveaux des tensions :

  • Sortie numérique de la carte Arduino alimentée par le câble USB: 

  • Valeur de la tension d’alimentation externe: 

Adaptateur de niveau du 5V au 3V3 avec Arduino - Alimentation PC

  • Sortie numérique de la carte Arduino alimentée par la tension externe via Vin:

Adaptateur de niveau du 5V au 3V3 avec Arduino - Alimentation 3v3

Montage électronique

Câblage Entrées/Sorties FPGA

  • Entrées 3 bits : Connecteur P2 de la carte FPGA
  • Sorties 7 bits vers Arduino : Connecteur P1 de la carte FPGA
  • Sorties 8 bits: Copie des sorties 7 bits vers Arduino mais envoyées aux LED afin de visualiser en parallèle l’état du résultat de la factorielle de la valeur d’entrée (3 bits)

Câblage Entrées/Sorties Arduino

  • Entrées 7 bits issues du FPGA: Branchées aux ports analogiques A1-A7
  • Entrée analogique du potentiomètre : A0
  • Sorties 3 bits : Connectées aux ports numériques D2-D4
  • Sorties PWM : Connectée à la sortie numérique D9. Le rapport cyclique du signal PWM (sur 8 bits par défaut, valeurs variées entre 0 et 255) est obtenu grâce aux entrées 7 bits issues de la carte FPGA (0 à 127) et on effectuer une multiplication par deux pour objectif de remettre en plein échelle le rapport cyclique

Montage électronique Arduino FPGA

Programme VHDL coté FPGA (FactorielMem.vhd)

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;



entity FactorielMem is
        Generic ( N : positive := 108;
                          M : positive := 5;
                          P : positive := 8
                        );
    Port ( Rst                         : in  STD_LOGIC;
           Clk                         : in  STD_LOGIC;
           D_in                 : in  STD_LOGIC_VECTOR (M-1 downto 0);
                   D_out_Ready         : out  STD_LOGIC;
           D_out                 : out  STD_LOGIC_VECTOR (P-1 downto 0);
                   D_out_Arduino: out  STD_LOGIC_VECTOR (P-1 downto 0));
end FactorielMem;

architecture Behavioral of FactorielMem is

signal D_outReady_tmp         :   STD_LOGIC :='0';
signal D_out_tmp                 :   STD_LOGIC_VECTOR (N+M-1 downto 0):=x"000000000000000000000000000" & b"00001";
signal D_in_tmp                 :   STD_LOGIC_VECTOR (M-1 downto 0):=b"00001";

COMPONENT FactorielN
PORT(
        Rst : IN std_logic;
        Clk : IN std_logic;
        D_in : IN std_logic_vector(M-1 downto 0);
        D_out_Ready : OUT std_logic;
        D_out : OUT std_logic_vector(N+M-1 downto 0)
        );
END COMPONENT;

begin

        FAC5: FactorielN PORT MAP(
                Rst => Rst,
                Clk => Clk,
                D_in => D_in_tmp,
                D_out_Ready => D_outReady_tmp,
                D_out => D_out_tmp
        );

        -- Memorisation de la sortie + Gestion de 0!
        P_data_out : process(Rst, Clk, D_outReady_tmp)
        begin
                if Rst ='1' then
                        D_out(P-1 downto 1) <=(others =>'0');
                        D_out(0) <= '1';
                elsif Clk = '1' and Clk'event then
                        -- 0!=1
                        if D_in=b"00000" then
                                D_in_tmp <= b"00001";
                        else
                                D_in_tmp<= D_in;
                        end if;
                        -- Saturation de la sortie
                        if D_outReady_tmp='1' then
                                if D_out_tmp > x"000000000000000000000000008" & b"00000" then  -- 256
                                        D_out <= x"FF";
                                        D_out_Arduino <= x"FF";
                                else
                                        D_out <= D_out_tmp(P-1 downto 0);
                                        D_out_Arduino <= D_out_tmp(P-1 downto 0);
                                end if;
                        end if;
                end if;
        end process;
        D_out_Ready <=D_outReady_tmp;
end Behavioral;

Pinout FPGA (pinout_FactorielMem.ucf)

# Alimentation
CONFIG VCCAUX = "3.3" ;

# Horloge 12 MHz
NET "Clk"                        LOC = P129  | IOSTANDARD = LVCMOS33 | PERIOD = 12MHz;

# Entees: Rst + D_in - Interrupteur
NET "Rst"                        LOC = P70   | PULLUP  | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;

# Entree D_in (From Arduino) - Connecteur P2
NET "D_in[0]"           LOC = P10   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
NET "D_in[1]"           LOC = P11   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
NET "D_in[2]"           LOC = P7    | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;


# Sortie D_out_Ready
NET "D_out_Ready"        LOC = P19   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;

# Sortie D_out (LED)
NET "D_out[0]"                LOC = P46   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
NET "D_out[1]"                LOC = P47   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
NET "D_out[2]"                LOC = P48   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
NET "D_out[3]"                LOC = P49   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
NET "D_out[4]"                LOC = P50   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
NET "D_out[5]"                LOC = P51   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
NET "D_out[6]"                LOC = P54   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
NET "D_out[7]"                LOC = P55   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;


# Sortie D_out_Arduino (To Arduino) - Connecteur P1
NET "D_out_Arduino[0]"                 LOC = P31   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
NET "D_out_Arduino[1]"                 LOC = P32   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
NET "D_out_Arduino[2]"                 LOC = P28   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
NET "D_out_Arduino[3]"                 LOC = P30   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
NET "D_out_Arduino[4]"                 LOC = P27   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
NET "D_out_Arduino[5]"                 LOC = P29   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
NET "D_out_Arduino[6]"                 LOC = P24   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;
#NET "IO_P1[7]"                        LOC = P25   | IOSTANDARD = LVCMOS33 | SLEW = SLOW | DRIVE = 12;

Programme Arduino

// SOrtie PWM
int To_Moteur_PWM = 9;

// Entree potentiometre
int Potent_pin = A0;
int val_Pot = 0;
float val_reel = 0.0;

// Sortie To FPGA
int To_FPGA_pin_1 = 2;
int To_FPGA_pin_2 = 3;
int To_FPGA_pin_3 = 4;

// Donnees from FPGA
int From_FPGA_pin_1 = A1;
int From_FPGA_pin_2 = A2;
int From_FPGA_pin_3 = A3;
int From_FPGA_pin_4 = A4;
int From_FPGA_pin_5 = A5;
int From_FPGA_pin_6 = A6;
int From_FPGA_pin_7 = A7;

int val = 0;

int val_0 = 0;
int val_1 = 0;
int val_2 = 0;
int val_3 = 0;
int val_4 = 0;
int val_5 = 0;
int val_6 = 0;

void setup()
{
        // Sorties
        pinMode(To_FPGA_pin_1, OUTPUT);
        pinMode(To_FPGA_pin_2, OUTPUT);
        pinMode(To_FPGA_pin_3, OUTPUT);
        pinMode(To_Moteur_PWM, OUTPUT);

        // Seria port
        Serial.begin(9600);
}

void loop()
{
        // Lecture entrees FPGA
        val=ReadFromFPGA();

        //Generation PWM/
        analogWrite(To_Moteur_PWM, (val+1)*2-1);

        //Lecture Pot /
        val_Pot = analogRead(Potent_pin);
        val_reel=floor(double(val_Pot)*7.0/1023.0);

        // Envoie 3 bits au FPGA /
        WriteToFPGA(val_reel);

        // Display data Serial port
        /*
        Serial.print("\n Data FPGA = " );
        Serial.print(val);
        Serial.print("\n Data Poto = " );
        Serial.print(val_reel);
        delay(1000);
        */
}

int ReadFromFPGA()
{
        int val_tmp=0;

        // Bit 0
        val_0 = analogRead(From_FPGA_pin_1);
        if (val_0 > 512)
        {
                val_0 =1;
        }
        else
        {
                val_0=0;
        }

        // Bit 1
        val_1 = analogRead(From_FPGA_pin_2);
        if (val_1 > 512)
        {
                val_1 =2;
        }
        else
        {
                val_1=0;
        }

        // Bit 2
        val_2 = analogRead(From_FPGA_pin_3);
        if (val_2 > 512)
        {
                val_2 =4;
        }
        else
        {
                val_2=0;
        }

        // Bit 3
        val_3 = analogRead(From_FPGA_pin_3);
        if (val_3 > 512)
        {
                val_3 =8;
        }
        else
        {
                val_2=0;
        }

        // Bit 4
        val_4 = analogRead(From_FPGA_pin_5);
        if (val_4 > 512)
        {
                val_4 =16;
        }
        else
        {
                val_4=0;
        }

        // Bit 5
        val_5 = analogRead(From_FPGA_pin_6);
        if (val_5 > 512)
        {
                val_5 =32;
        }
        else
        {
                val_5=0;
        }

        // Bit 6
        val_6 = analogRead(From_FPGA_pin_7);
        if (val_6 > 512)
        {
                val_6 =64;
        }
        else
        {
                val_6=0;
        }

        // Resultat
        val_tmp = val_0+val_1+val_2+val_3+val_4+val_5+val_6;

        // Return
        return val_tmp;
}

void WriteToFPGA(int val_reel_tmp)
{
        if (val_reel_tmp==0){
                digitalWrite(To_FPGA_pin_1, HIGH);        // 000
                digitalWrite(To_FPGA_pin_2, LOW);
                digitalWrite(To_FPGA_pin_3, LOW);}
        else if (val_reel_tmp==1){
                digitalWrite(To_FPGA_pin_1, HIGH);        // 001
                digitalWrite(To_FPGA_pin_2, LOW);
                digitalWrite(To_FPGA_pin_3, LOW);}
        else if (val_reel_tmp==2){
                digitalWrite(To_FPGA_pin_1, LOW);        // 010
                digitalWrite(To_FPGA_pin_2, HIGH);
                digitalWrite(To_FPGA_pin_3, LOW);}
        else if (val_reel_tmp==3){
                digitalWrite(To_FPGA_pin_1, HIGH);        // 011
                digitalWrite(To_FPGA_pin_2, HIGH);
                digitalWrite(To_FPGA_pin_3, LOW);}
        else if (val_reel_tmp==4){
                digitalWrite(To_FPGA_pin_1, LOW);        // 100
                digitalWrite(To_FPGA_pin_2, LOW);
                digitalWrite(To_FPGA_pin_3, HIGH);}
        else if (val_reel_tmp==5){
                digitalWrite(To_FPGA_pin_1, HIGH);        // 101
                digitalWrite(To_FPGA_pin_2, LOW);
                digitalWrite(To_FPGA_pin_3, HIGH);}
        else if (val_reel_tmp==6){
                digitalWrite(To_FPGA_pin_1, LOW);        // 110
                digitalWrite(To_FPGA_pin_2, HIGH);
                digitalWrite(To_FPGA_pin_3, HIGH);}
        else if (val_reel_tmp==7){
                digitalWrite(To_FPGA_pin_1, HIGH);        // 110
                digitalWrite(To_FPGA_pin_2, HIGH);
                digitalWrite(To_FPGA_pin_3, HIGH);}
        else{
                digitalWrite(To_FPGA_pin_1, HIGH);        // Autres
                digitalWrite(To_FPGA_pin_2, LOW);
                digitalWrite(To_FPGA_pin_3, LOW);
        }
}

Photos du projet

[masterslider id= »7″]

Vidéo illustrative du projet

************

Un petit commentaire de vous, un Grand encouragement pour nous ?

************

Téléchargement du projet

************

2 réponses sur « Projet électronique FPGA #10 : Commande factorielle d’un moteur à CC avec Arduino et FPGA »

Laisser un commentaire