La position actuelle:Accueil du site>Conception de l'interface UART basée sur la FPGA

Conception de l'interface UART basée sur la FPGA

2022-07-23 18:59:45QQ 44985628

Un.、Idées de conception de haut niveau:

    UARTC'est - à - dire l'interface universelle de transmission asynchrone(Universal Asynchronous Receiver/Transmitter),Pour faire court au port série,Est une interface de communication commune,Le principe de l'accord n'est pas précisé,Ceux qui ne le savent pas peuvent consulter les documents par eux - mêmes.(Ne pas répéter ne signifie pas que ce n'est pas important,Au contraire.,Pour chaqueFPGAConception,Une bonne compréhension des principes est la base et la prémisse,EtFPGAEtVerilogJuste des outils.)AvecFPGAPour réaliserUART,La clé est deUARTLa séquence de temps utilisée pour envoyer et recevoir les données estVerilogDécrivez - le..

    SelonUARTPrincipe du Protocole,Peut transformer toutUARTDivisé en deux modules:Module récepteur série“UART_RX”Et le module d'envoi de port série“UART_TX”,Le premier recevra1Données de série bits“uart_rxd”Convertir en8Bits données parallèles“data[7:0]”,Ce dernier, à son tour,8Bits données parallèles“data[7:0]”Convertir en1Données de série bits“uart_txd”Produits,Enfin, la réception et la réception des données en série.UARTLe diagramme du bloc de fonctions de haut niveau est illustré à la figure1Comme indiqué:

Insérer la description de l'image ici
2.、Conception du module récepteur série:

Ce module implémente ce qui sera reçu1Les données de série de bits sont converties en8Bits données parallèles,Et seulement si les données sont saisies, Le module ne fonctionne que , Il faut donc un signal d'activation “rxd_en” Pour contrôler le module ,Quand“rxd_en”Quand ça marche, Le module ne fonctionne que .SelonUARTPrincipe du Protocole,Reçu1 Le premier bit des données de série de bits est le BIT de départ (“0”),Et“uart_rxd” Au ralenti pour “1”, Donc, au début de la réception des données en série ,“uart_rxd” Il doit y avoir un bord de descente , Pour pouvoir détecter ce bord de descente , Chaque fois que le bord de descente arrive “rxd_en”Set to valid, Pour que le module fonctionne ; Chaque fois que toutes les données en série sont reçues ,Encore“rxd_en”Set to invalid, Envoyer simultanément le drapeau d'achèvement de la réception “rx_done”, Pour fermer le module .(À propos deVerilog Comment réaliser la détection des bords , Vous pouvez voir ce que j'ai écrit sur DDS Le blog du générateur de signaux .)

Pendant la réception des données , Le taux est basé sur le taux Baud du port série , Les taux de Baud de port série couramment utilisés sont :1200、2400、4800、9600、14400、57600、115200Attendez un peu!. Donc un compteur de débit Baud est nécessaire “baud_cnt” Comptez l'horloge principale à un taux de Baud spécifique , Chaque fois que ce compteur de débit Baud est plein , Recevoir un chiffre de données .Par exemple, L'horloge principale est 50MHz,Quand le taux de Baud est9600Heure, La valeur maximale du compteur de débit Baud doit être :50000000/9600-1=5207,En ce moment, Chaque fois que le compteur de débit Baud compte 5207 Zéro à l'heure , Réception simultanée d'un bit de données en série .

Dans ce design, Les bits de données sont communs à chaque donnée 10Bits(1Bit Start bit、8Bits de données、1Bit stop bit), Donc dans le processus de réception , Un compteur de bits est nécessaire “bit_cnt” Pour compter les bits de données de chaque série ,Les opérations spécifiques sont les suivantes:: Chaque fois que le compteur de débit Baud est plein , Le compteur de bits augmente automatiquement 1, Jusqu'à ce que la valeur du compteur de bits soit 9Zéro..

    Pendant la réception, Pour recevoir des données stables en série , Cette conception permet d'échantillonner et de recevoir des données en série de chaque bit au milieu ,Les opérations spécifiques sont les suivantes:: Chaque fois que le compteur de débit Baud atteint la moitié de la valeur maximale , Pour échantillonner les données de série actuelles , Ensuite, selon la valeur du compteur de bits , Assigner les valeurs échantillonnées aux bits de données parallèles correspondants .En outre, Pour éliminer la métastabilité , Les données en série à recevoir doivent être tamponnées par un registre à deux chiffres avant la détection des bords .

    Sur la base de l'analyse ci - dessus, Le diagramme de séquence du module récepteur de port série est illustré à la figure 2Comme indiqué(“uart_rxd_r”- Oui.“uart_rxd” Signal après élimination de l'état métastable et détection du bord du registre ):

Insérer la description de l'image ici
Selon le diagramme de séquence, Pour que le module de réception de port série RTLDescription,Écrit parVerilogLes codes sont les suivants::

`timescale 1ns / 1ps
 
module UART_RX(
    input clk, //Horloge principale,50MHz
    input rst, //Réinitialiser,Haute efficacité
    input uart_tx_data, // Données série envoyées au port série 
    
    output reg [7:0] uart_rx_data, // Données parallèles reçues par le port série 
    output reg rx_done //Recevoir le drapeau d'achèvement
    );
    
    parameter CLK_F = 50000000; // Fréquence de l'horloge principale 
    parameter UART_B = 9600; //Taux de Baud du port série
    parameter B_CNT = CLK_F / UART_B; // La valeur maximale du compteur de débit Baud 
    
    reg [1:0] uart_tx_data_r1; // Pour éliminer l'état métastable des données en série d'entrée 
    reg [1:0] uart_tx_data_r2; // Saisissez le registre de détection des bords de données série 
    reg rxd_en; // Réception du signal d'activation 
    reg [15:0] baud_cnt = 16'd0; //115200Compteur de débit Baud
    reg [3:0] bit_cnt = 4'd0; //Compteur de bits
    reg [7:0] rx_data; //Registre des données reçues
 
    
/*************** Éliminer la métastabilité des données en série d'entrée *************/
    always @ (posedge clk)
    begin
        uart_tx_data_r1 <= {
    uart_tx_data_r1[0] , uart_tx_data};
    end
 
/*************** Saisissez la détection de bord de données série *************/
    always @ (posedge clk)
    begin
        uart_tx_data_r2 <= {
    uart_tx_data_r2[0] , uart_tx_data_r1[1]};
    end
 
/*************** Commande du signal d'activation de réception *************/
    always @ (posedge clk or negedge rst)
    begin
        if (rst)
            rxd_en <= 1'd0;
        else
        begin 
            if (uart_tx_data_r2 == 2'b10)
                rxd_en <= 1'd1;
            else if ((bit_cnt == 4'd9) && (baud_cnt == B_CNT / 2))
                rxd_en <= 1'd0;
            else 
                rxd_en <= rxd_en;
        end
    end
 
/*************** Contrôle du compteur de débit Baud *************/
    always @ (posedge clk or negedge rst)
    begin
        if (rst)
            baud_cnt <= 16'd0;
        else if (rxd_en)
        begin
            if (baud_cnt == B_CNT - 1)
                baud_cnt <= 16'd0;
            else
                baud_cnt <= baud_cnt + 1'b1;
        end
        else
            baud_cnt <= 16'd0;
    end
 
/*************** Contrôle du compteur de bits *************/
    always @ (posedge clk or negedge rst)
    begin
        if (rst)
            bit_cnt <= 4'd0;
        else if (rxd_en)
        begin
            if (baud_cnt == B_CNT - 1)
                bit_cnt <= bit_cnt + 1'b1;
            else
                bit_cnt <= bit_cnt;
        end
        else
            bit_cnt <= 4'd0;
    end
 
/***************Recevoir le cache*************/
    always @ (posedge clk or negedge rst)
    begin
        if (rst)
            rx_data <= 8'd0;
        else if (rxd_en)
        begin
            if (baud_cnt == B_CNT / 2)
            begin
                case (bit_cnt)
                    4'd1:rx_data[0] <= uart_tx_data_r2[1];
                    4'd2:rx_data[1] <= uart_tx_data_r2[1];
                    4'd3:rx_data[2] <= uart_tx_data_r2[1];
                    4'd4:rx_data[3] <= uart_tx_data_r2[1];
                    4'd5:rx_data[4] <= uart_tx_data_r2[1];
                    4'd6:rx_data[5] <= uart_tx_data_r2[1];
                    4'd7:rx_data[6] <= uart_tx_data_r2[1];
                    4'd8:rx_data[7] <= uart_tx_data_r2[1];
                    default:;
                endcase
            end
            else
                rx_data <= rx_data;
        end
        else
            rx_data <= 8'd0;
    end
 
/***************Réception*************/
    always @ (posedge clk or negedge rst)
    begin
        if (rst)
            uart_rx_data <= 8'd0;
        else if (bit_cnt == 4'd9)
            uart_rx_data <= rx_data;
        else
            uart_rx_data <= 8'd0;
    end
 
/*************** Recevoir le contrôle du drapeau d'achèvement *************/
    always @ (posedge clk or negedge rst)
    begin
        if (rst)
            rx_done <= 1'd0;
        else if (bit_cnt == 4'd9)
            rx_done <= 1'd1;
        else
            rx_done <= 1'd0;
    end
    
endmodule 

Écrit partestbeachComme suit:

`timescale 1ns / 1ps
module tb_uart_rx();
 
    reg clk; //Horloge principale,50MHz
    reg rst; //Réinitialiser,Faible niveau actif
    reg uart_tx_data; // Données série envoyées au port série 
    
    wire [7:0] uart_rx_data; // Données parallèles reçues par le port série 
    wire rx_done; //Recevoir le drapeau d'achèvement
   
/***************Instanciation modulaire*************/ 
    UART_RX tb_uart_rx(
        .clk(clk),      
        .rst(rst),     
        .uart_tx_data(uart_tx_data), 
                             
        .uart_rx_data(uart_rx_data), 
        .rx_done(rx_done)  
    );
 
/*************** Générer l'horloge principale *************/
    always #10 clk = ~clk;
    
/***************Initialisation*************/
    initial 
    begin
        clk = 1'd0;
        rst = 1'd0;
        uart_tx_data = 1'b1;
        #1000000
        uart_tx_data = 1'b0;
        #200000
        uart_tx_data = 1'b1;
        #200000
        uart_tx_data = 1'b0;
        #200000
        uart_tx_data = 1'b1;
    end
   
endmodule

Les résultats de la simulation sont présentés à la figure 3Comme indiqué, Vous pouvez voir que la fonction de réception est implémentée :
Insérer la description de l'image ici
Trois、 Conception du module d'envoi de port série :

     La mise en œuvre du module 8 Bits données parallèles converties en 1 Sortie de données série bit , Comme pour le module récepteur , Le module d'envoi de port série a également besoin d'un signal d'activation “txd_en”Pour contrôler,Quand“txd_en”Quand ça marche, Le module ne fonctionne que .SelonUARTPrincipes, Ce n'est qu'après réception du module récepteur , Envoyer le module pour commencer à fonctionner , Par conséquent, le drapeau d'achèvement de la réception dans le module de réception est le drapeau de début de transmission du module de transmission “txd_start”, Il est donc possible de détecter le bord ascendant du drapeau de départ d'envoi pour permettre “txd_en”, Cela permet d'envoyer des modules .

    Sur la base de l'analyse ci - dessus, Le diagramme de synchronisation du module d'envoi de port série est illustré à la figure 4Comme indiqué:

Insérer la description de l'image ici
Selon le diagramme de séquence,Écrit parVerilogLes codes sont les suivants::

 `timescale 1ns / 1ps
 
module UART_TX(
    input clk, //Horloge principale,50MHz
    input rst, //Réinitialiser,Haute efficacité
    input txd_start, // Envoyer le drapeau de départ 
    input [7:0] uart_rx_data, // Données parallèles reçues par le port série 
    
    output reg uart_tx_data // Données série envoyées par le port série 
    );
    
    parameter CLK_F = 50000000; // Fréquence de l'horloge principale 
    parameter UART_B = 9600; //Taux de Baud du port série
    parameter B_CNT = CLK_F / UART_B; // La valeur maximale du compteur de débit Baud 
    
    reg [1:0] txd_start_r; // Envoyer le registre de détection des bords du drapeau de départ  
    reg txd_en; //Envoyer un signal d'activation
    reg [15:0] baud_cnt = 16'd0; //115200Compteur de débit Baud
    reg [3:0] bit_cnt = 4'd0; //Compteur de bits
    reg [7:0] tx_data; //Envoyer un registre de données
 
/*************** Envoyer la détection du bord du drapeau de départ *************/
    always @ (posedge clk)
    begin
        txd_start_r <= {
    txd_start_r[0] , txd_start};
    end
    
/*************** Commande du signal d'activation de la transmission *************/
    always @ (posedge clk or negedge rst)
    begin
        if (rst)
            txd_en <= 1'd0;
        else
        begin 
            if (txd_start_r == 2'b01)
                txd_en <= 1'd1;
            else if ((bit_cnt == 4'd9) && (baud_cnt == B_CNT / 2))
                txd_en <= 1'd0;
            else 
                txd_en <= txd_en;
        end
    end
    
/***************Envoyer le cache*************/
    always @ (posedge clk or negedge rst)
    begin
        if (rst)
            tx_data <= 8'd0;
        else
        begin 
            if (txd_start_r == 2'b01)
                tx_data <= uart_rx_data;
            else if ((bit_cnt == 4'd9) && (baud_cnt == B_CNT / 2))
                tx_data <= 8'd0;
            else 
                tx_data <= tx_data;
        end
    end
    
/*************** Contrôle du compteur de débit Baud *************/
    always @ (posedge clk or negedge rst)
    begin
        if (rst)
            baud_cnt <= 16'd0;
        else if (txd_en)
        begin
            if (baud_cnt == B_CNT - 1)
                baud_cnt <= 16'd0;
            else
                baud_cnt <= baud_cnt + 1'b1;
        end
        else
            baud_cnt <= 16'd0;
    end
 
/*************** Contrôle du compteur de bits *************/
    always @ (posedge clk or negedge rst)
    begin
        if (rst)
            bit_cnt <= 4'd0;
        else if (txd_en)
        begin
            if (baud_cnt == B_CNT - 1) 
                bit_cnt <= bit_cnt + 1'b1;
            else
                bit_cnt <= bit_cnt;
        end
        else
            bit_cnt <= 4'd0;
    end
 
/***************Envoyer*************/
    always @ (posedge clk or negedge rst)
    begin
        if (rst)
            uart_tx_data <= 1'd1;
        else if (txd_en)
        begin
            case (bit_cnt)
                4'd0:uart_tx_data <= 1'd0;
                4'd1:uart_tx_data <= tx_data[0];
                4'd2:uart_tx_data <= tx_data[1];
                4'd3:uart_tx_data <= tx_data[2];
                4'd4:uart_tx_data <= tx_data[3];
                4'd5:uart_tx_data <= tx_data[4];
                4'd6:uart_tx_data <= tx_data[5];
                4'd7:uart_tx_data <= tx_data[6];
                4'd8:uart_tx_data <= tx_data[7];
                4'd9:uart_tx_data <= 1'd1;
                default:;
            endcase
        end
        else
            uart_tx_data <= 1'd1;
    end
    
endmodule

Écrit partestbeachComme suit:

`timescale 1ns / 1ps
module tb_uart_tx();
 
    reg clk; //Horloge principale,50MHz
    reg rst; //Réinitialiser,Faible niveau actif
    reg txd_start; // Envoyer le drapeau de départ 
    reg [7:0] uart_rx_data; // Données parallèles reçues par le port série 
    
    wire uart_tx_data; // Données série envoyées par le port série 
   
/***************Instanciation modulaire*************/ 
    UART_TX tb_uart_tx(
        .clk(clk),      
        .rst(rst),     
        .txd_start(txd_start),    
        .uart_rx_data(uart_rx_data), 
                             
        .uart_tx_data(uart_tx_data)
    );
 
/*************** Générer l'horloge principale *************/
    always #10 clk = ~clk;
    
/***************Initialisation*************/
    initial 
    begin
        clk = 1'd0;
        rst = 1'd0;
        txd_start = 1'd0;
        uart_rx_data = 8'h5a;
        #20
        txd_start = 1'd1;
        #20
        txd_start = 1'd0;
        #100000
        uart_rx_data = 8'h3f;
        #100000
        uart_rx_data = 8'he6;
    end
   
endmodule

Les résultats de la simulation sont présentés à la figure 5Comme indiqué, Vous pouvez voir que la fonction d'envoi est implémentée :

Insérer la description de l'image ici Fig.5
Quatre、 .Code de haut niveau et mise en service de son tableau supérieur :

Après la conception des deux sous - modules , Selon le diagramme de bloc de fonctions de haut niveau, vous pouvez écrire RTLDescription,Écrit parVerilogLes codes sont les suivants:( Parce que l'horloge sur mon tableau de développement est l'horloge différentielle , Il est donc nécessaire d'appeler un primitif de conception pour convertir un signal différentiel en signal à une seule extrémité “IBUFDS”, L'utilisation de ce primitif est simple , Il n'y a pas d'introduction ici ,Ceux qui ne le savent pas peuvent consulter les documents par eux - mêmes):
`timescale 1ns / 1ps

module UART_TOP(
    input clk_p, // Fin positive de l'horloge principale différentielle ,50MHz
    input clk_n, // Fin négative de l'horloge principale différentielle ,50MHz
    input rst, //Réinitialiser,Haute efficacité
    input uart_rxd, //Récepteur
    
    output uart_txd //L'expéditeur
    );
    
    parameter  CLK_FREQ = 50000000; // Fréquence de l'horloge principale 
    parameter  UART_BPS = 460800; //Taux de Baud du port série
    
    wire clk; //Horloge principale,50MHz
    wire [7:0] uart_rx_data_w; // Câble parallèle dans le port série 
    wire rx_done_w; // Réception de la ligne de signal d'achèvement 
 
/*************** Horloge différentielle à horloge à une extrémité *************/
    IBUFDS #(
          .DIFF_TERM("FALSE"), 
          .IBUF_LOW_PWR("TRUE"), 
          .IOSTANDARD("DEFAULT") 
       ) IBUFDS_inst (
          .O(clk), 
          .I(clk_p), 
          .IB(clk_n)
       );
    
/*************** Appelez le module de réception du port série *************/
    UART_RX #(
    .CLK_F(CLK_FREQ),
    .UART_B(UART_BPS)
    )
    uut_rxd(
        .clk(clk),
        .rst(rst),
        .uart_tx_data(uart_rxd),
        .uart_rx_data(uart_rx_data_w),
        .rx_done(rx_done_w)
    );
    
/*************** Appelez le module d'envoi du port série *************/
    UART_TX #(
    .CLK_F(CLK_FREQ), 
    .UART_B(UART_BPS)
    )
    uut_txd(
        .clk(clk),
        .rst(rst),
        .txd_start(rx_done_w),
        .uart_rx_data(uart_rx_data_w),
        .uart_tx_data(uart_txd) 
    );
     
endmodule

Synthèse、Après la réalisation, Effectuer la mise en service de la plaque supérieure ,Par souci de simplicité, La conception adopte la boucle pour déboguer et vérifier ,C'est - à - dire:PCEnvoyer les données àFPGAAllez.,FPGA Les données sont reçues par le port série avant d'être retournées par le port série PC. Les résultats de la mise en service sont présentés à la figure 6Comme indiqué,Je vois., Le port série conçu fonctionne correctement :

Insérer la description de l'image ici

Mentions de copyright
Auteur de cet article [QQ 44985628],Réimpression s’il vous plaît apporter le lien vers l’original, merci
https://fra.chowdera.com/2022/204/202207231641422375.html

Recommandé au hasard