Communication I2C basée sur FPGA (trois) finale

table des matières

Quatre, conception du système de lecture et d'écriture EEPROM

1. Aperçu général du système

2. fifo_ctrl (module de commande de fifo de données d'écriture)

3. I2C_wr_ctrl (module de contrôle d'écriture I2C)

4. I2C_rd_ctrl (module de contrôle de lecture I2C)

5. fifo_ctrl2 (lire les données du module de contrôle fifo)

6. Module de niveau supérieur I2C_eeprom_top

5. Débogage de la simulation et vérification au niveau de la carte

1. Débogage de la simulation

2. Vérification au niveau du Conseil


  Le lien de téléchargement du projet pour la réalisation de la fonction globale du système de lecture et d'écriture EEPROM (expliqué dans le prochain blog, y compris le contenu de cet article) de cette rubrique est le suivant:
 https://download.csdn.net/download/qq_33231534/12503289


Quatre, conception du système de lecture et d'écriture EEPROM

1. Aperçu général du système

Figure: Schéma fonctionnel général du système de lecture et d'écriture EEPROM
Figure: Vue RTL du système de lecture et d'écriture EEPROM

Les exigences de l'ensemble du système sont les suivantes: envoyer d'abord quatre octets de données via l'assistant de port série, stocker les quatre octets de données dans fifo, puis lire et stocker les données dans fifo dans EEPROM. Les deux EEPROM en lecture et en écriture utilisent la lecture et l'écriture continues 4 Une fois les données écrites, les 4 octets de données qui viennent d'être écrites dans l'EEPROM sont lus séquentiellement et stockés dans fifo, puis les données sont lues à partir de fifo et envoyées à l'exportation, et affichées sur l'assistant de port série.

Parmi eux, le module I2C_ctrl a été expliqué dans le blog précédent. Uart_rx et uart_tx sont également décrits en détail dans les chapitres sur le port série et le système d'acquisition de données. Ce sont des modules à usage général, et sync_fifo1 et sync_fifo2 sont des modules FIFO synchrones, qui peuvent être réalisés en utilisant des cœurs IP, ou Écrivez le code par vous-même. Le module synchrone fifo est aussi un module commun. Il y a des introductions et des codes dans le système d'acquisition de données. J'écrirai un blog sur FIFO dans le futur, donc je ne le répéterai pas ici.

fifo_ctrl (module de contrôle d'écriture de données fifo): Les données envoyées par le port série sont sauvegardées par sync_fifo1, et le module fifo_ctrl est le pont qui contrôle le module de réception du port série et le module fifo pour s'assurer que les données reçues par le port série peuvent être sauvegardées dans fifo.

I2C_wr_ctrl (module de contrôle d'écriture I2C): retirez les données une par une de la FIFO et écrivez-les dans l'EEPROM.

I2C_rd_ctrl (module de contrôle de lecture I2C): Lire les données de l'EEPROM une par une et les stocker dans la FIFO.

fifo_ctrl2 (lire les données du module de contrôle fifo): lire les données dans fifo une par une et les envoyer au module d'envoi du port série.

2. fifo_ctrl (module de commande de fifo de données d'écriture)

Tableau: description du signal du module fifo_ctrl
Nom du signal E / S Chiffres Description de la fonction
clk je 1 Horloge système 50 MHz
rst_n je 1 Réinitialisation du système
data_byte je 8 Recevoir les données du port série
rx_done je 1 Port série recevant l'indicateur de données complètes
Wrreq LES 1 demande d'écriture fifo
data_to_fifo LES 8 Données de sortie enregistrées dans fifo

code montrer comme ci-dessous:

//-------------------------------------------------------------------
//https://blog.csdn.net/qq_33231534 PHF的CSDN   
//File name:           fifo_ctrl.v
//Last modified Date:  2020/6.6
//Last Version:        
//Descriptions:        串口发送的数据由sync_fifo1来保存,而fifo_ctrl模块就
//                     是控制串口接收模块和fifo模块的桥梁,保证串口接受的数据能保存在fifo中。
//-------------------------------------------------------------------
module fifo_ctrl(
    input                   clk         ,//系统时钟50MHz
    input                   rst_n       ,//系统复位
    input   [ 7: 0]         data_byte   ,//接收到串口发的数据
    input                   rx_done     ,//串口接收数据完成标志
    output  reg             wrreq       ,//fifo写请求
    output  reg [ 7: 0]     data_to_fifo //输出保存到fifo的数据
);

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        wrreq <= 0;
    end
    else if(rx_done) begin
        wrreq <= 1;
    end
    else begin
        wrreq <= 0;
    end
end

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        data_to_fifo <= 0;
    end
    else if(rx_done) begin
        data_to_fifo <= data_byte;
    end
    else begin
        data_to_fifo <= data_to_fifo;
    end
end

endmodule

3. I2C_wr_ctrl (module de contrôle d'écriture I2C)

Tableau: Description du signal du module I2C_wr_ctrl
Nom du signal E / S Chiffres Description de la fonction
clk je 1 Horloge système 50 MHz
rst_n je 1 Réinitialisation du système
utiliséw je 3 Nombre de données dans fifo
q je 8 données lues par fifo
wr_data_vaild je 1 Ecrire un signal valide de données dans le module I2C_ctrl
rdreq LES 1 demande de lecture fifo
roitelet LES 1 Activation d'écriture du module I2C_ctrl
wr_data LES 8 Données écrites sur eeprom

code montrer comme ci-dessous:

//-------------------------------------------------------------------
//https://blog.csdn.net/qq_33231534 PHF的CSDN   
//File name:           I2C_wr_ctrl
//Last modified Date:  2020/6/6
//Last Version:        
//Descriptions:        从FIFO中逐个数据取出写入EEPROM
//-------------------------------------------------------------------
module I2C_wr_ctrl(
    input                   clk             ,//系统时钟50MHz
    input                   rst_n           ,//系统复位
    input   [2:0]           usedw           ,//fifo中数据个数
    input   [7:0]           q               ,//fifo读出的数据
    input                   wr_data_vaild   ,//I2C_ctrl模块中写数据有效信号

    output  reg             rdreq           ,//fifo读请求
    output  reg             wr_en           ,//I2C_ctrl模块写使能
    output  reg [7:0]       wr_data          //写入eeprom的数据
);

reg                    rdreq_r;

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        rdreq_r <= 0;
    end
    else begin
        rdreq_r <= rdreq;
    end
end
always  @(*)begin
    if(usedw>=4 || wr_data_vaild) begin
        rdreq = 1;
    end
    else begin
        rdreq = 0;
    end
end

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        wr_en <= 0;
    end
    else if(usedw>=4) begin
        wr_en <= 1;
    end
    else begin
        wr_en <= 0;
    end
end

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        wr_data <= 0;
    end
    else if(rdreq_r) begin
        wr_data <= q;
    end
    else begin
        wr_data <= wr_data;
    end
end

endmodule

4. I2C_rd_ctrl (module de contrôle de lecture I2C)

Tableau: Description du signal du module I2C_rd_ctrl
Nom du signal E / S Chiffres Description de la fonction
clk je 1 Horloge système 50 MHz
rst_n je 1 Réinitialisation du système
terminé je 1 Indicateur de fin de lecture et d'écriture du module I2C_ctrl
rd_data je 8 Données lues à partir de l'EEPROM
rd_data_vaild je 1 Lire le bit valide de données de l'EEPROM
Wrreq LES 1 demande d'écriture fifo
I2C_rd_data LES 8 Données à écrire sur fifo
rd_en LES 1 Activation de lecture du module I2C_ctrl 

code montrer comme ci-dessous:

//-------------------------------------------------------------------
//https://blog.csdn.net/qq_33231534 PHF的CSDN   
//File name:           I2C_rd_ctrl
//Last modified Date:  2020/6/6
//Last Version:        
//Descriptions:        从EEPROM逐个读出数据存入FIFO中
//-------------------------------------------------------------------
module I2C_rd_ctrl(
    input                   clk             ,//系统时钟50MHz
    input                   rst_n           ,//系统复位
    input                   done            ,//I2C_ctrl模块读写数据结束标志
    input   [7:0]           rd_data         ,//从EEPROM读出的数据
    input                   rd_data_vaild   ,//从EEPROM读出数据有效位

    output  reg             wrreq           ,//fifo写请求
    output  reg [7:0]       I2C_rd_data     ,//要写入fifo的数据
    output  reg             rd_en            //I2C_ctrl模块读使能   
);
reg                    flag_rd;

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        flag_rd <= 0;
    end
    else if(done)begin
        flag_rd <= ~flag_rd;
    end
	 else begin
		  flag_rd <= flag_rd;
	 end
	 
end    


always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        rd_en <= 0;
    end
    else if(done && flag_rd==0) begin
        rd_en <= 1;
    end
    else begin
        rd_en <= 0;
    end
end

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        I2C_rd_data <= 0;
    end
    else if(rd_data_vaild) begin
        I2C_rd_data <= rd_data;
    end
    else begin
        I2C_rd_data <= I2C_rd_data;
    end
end

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        wrreq <= 0;
    end
    else if(rd_data_vaild) begin
        wrreq <= 1;
    end
    else begin
        wrreq <= 0;
    end
end

endmodule

5. fifo_ctrl2 (lire les données du module de contrôle fifo)

Nom du signal E / S Chiffres Description de la fonction
clk je 1 Horloge système 50 MHz
rst_n je 1 Réinitialisation du système
utiliséw je 3 Nombre de données dans fifo
q je 8 Données lues dans fifo
tx_done je 1 Le port série envoie un indicateur d'achèvement des données
rdreq LES 1 demande de lecture fifo
send_en LES 1 Signal d'activation d'envoi du port série
data_send LES 8 Données envoyées par port série

code montrer comme ci-dessous:

//-------------------------------------------------------------------
//https://blog.csdn.net/qq_33231534 PHF的CSDN   
//File name:           fifo_ctrl2.v
//Last modified Date:  2020/6/6
//Last Version:        
//Descriptions:        将fifo中的数据逐个读出发送到串口发送模块
//-------------------------------------------------------------------
module fifo_ctrl2(
    input                   clk         ,//系统时钟50MHz
    input                   rst_n       ,//系统复位
    input   [2:0]           usedw       ,//fifo中数据个数
    input   [7:0]           q           ,//fifo中读出的数据
    input                   tx_done     ,//串口发送一次数据完成标志

    output  reg             rdreq       ,//fifo读请求
    output  reg             send_en     ,//串口发送使能信号
    output  reg [7:0]       data_send    //串口发送的数据
);

reg                    rdreq_r;

always  @(*)begin
    if(rst_n==1'b0)begin
        rdreq = 0;
    end
    else if(usedw>=4 || usedw>0&&tx_done) begin
        rdreq = 1;
    end
    else begin
        rdreq = 0;
    end
end

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        rdreq_r <= 0;
    end
    else begin
        rdreq_r <= rdreq;
    end
end

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        data_send <= 0;
    end
    else if(rdreq_r) begin
        data_send <= q;
    end
end

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        send_en <= 0;
    end
    else if(rdreq_r) begin
        send_en <= 1;
    end
    else begin
        send_en <= 0;
    end
end

endmodule

6. Module de niveau supérieur I2C_eeprom_top

Instanciez les modules ci-dessus et connectez les signaux, le code est le suivant:

//-------------------------------------------------------------------
//https://blog.csdn.net/qq_33231534 PHF的CSDN   
//File name:           I2C_eeprom_top.v
//Last modified Date:  2020/6/6
//Last Version:        
//Descriptions:        EEPROM读写系统顶层模块
//-------------------------------------------------------------------

module I2C_eeprom_top(
    input                   clk         ,
    input                   rst_n       ,
    input                   rs232_rx    ,

    output                  rs232_tx    ,
    output                  scl         ,
    inout                   sda             
);

wire  [  7: 0]         data_byte        ;
wire                   rx_done          ;
wire                   wrreq1           ;
wire                   rdreq1           ;
wire  [  7: 0]         data_to_fifo     ;
wire  [  2: 0]         usedw1           ;
wire  [  7: 0]         data_to_eeprom   ;
wire                   wr_en            ;
wire                   wr_data_vaild    ;
wire  [  7: 0]         wr_data          ;

wire                   done             ;
wire  [  7: 0]         rd_data          ;
wire                   rd_data_vaild    ;
wire                   wrreq2           ;
wire  [  7: 0]         I2C_rd_data      ;
wire                   rd_en            ;
wire  [  2: 0]         usedw2           ;
wire  [  7: 0]         data_to_uart     ;
wire                   rdreq2           ;
wire                   tx_done          ;
wire                   send_en          ;
wire  [  7: 0]         data_send        ;


UART_Byte_Rx u_UART_Byte_Rx(
    .clk       (clk) ,//系统时钟50MHz
    .rst_n     (rst_n) ,//系统复位
    .rs232_rx  (rs232_rx) ,//串口串行数据发送数据口
    .baud_set  (3'd1) ,//波特率选择信号

    .data_byte (data_byte) ,//并行数据输出
    .rx_done   (rx_done)  //接收1字节数据完成标志,rx_done可以作为输出有效信号使用
);

fifo_ctrl u_fifo_ctrl(
    .clk           (clk),
    .rst_n         (rst_n),
    .data_byte     (data_byte),
    .rx_done       (rx_done),
    .wrreq         (wrreq1),
    .data_to_fifo  (data_to_fifo)  
);

sync_fifo
#(.WIDTH         (8),          //缓存的数据宽度
  .DEPTH         (5),         //缓存的数据深度
  .MAX_DEPTH_BIT (3))           //可设置的最大深度位数7,即最大深度为2^7-1
u_sync_fifo1(
    .clk       (clk), //系统时钟50MHz
    .rst_n     (rst_n), //系统复位
    .wrreq     (wrreq1), //写使能
    .data      (data_to_fifo), //写数据
    .rdreq     (rdreq1), //读使能

    .q         (data_to_eeprom), //读数据
    .empty     (), //空信号
    .full      (), //满信号
    .half      (), //半满信号
    .usedw     (usedw1)  //fifo中剩余数据个数    
);

I2C_wr_ctrl u_I2C_wr_ctrl(
    .clk           (clk) ,
    .rst_n         (rst_n) ,
    .usedw         (usedw1) ,
    .q             (data_to_eeprom) ,
    .wr_data_vaild (wr_data_vaild) ,

    .rdreq         (rdreq1) ,
    .wr_en         (wr_en) ,
    .wr_data       (wr_data)          
);

I2C_ctrl u_I2C_ctrl(
    .clk           (clk) ,
    .rst_n         (rst_n) ,
    .rd_data_num   (6'd4) ,//最大32
    .wr_data_num   (6'd4) ,//最大32
    .word_addr     (16'h05) ,
    .device_addr   (3'b001) ,
    .wr_en         (wr_en) ,
    .wr_data       (wr_data) ,
    .rd_en         (rd_en) ,

    .wr_data_vaild (wr_data_vaild) ,
    .rd_data       (rd_data) ,
    .rd_data_vaild (rd_data_vaild) ,
    .done          (done) ,
    .scl           (scl) ,

    .sda           (sda)   
);

I2C_rd_ctrl u_I2C_rd_ctrl(
    .clk           (clk) ,
    .rst_n         (rst_n) ,
    .done          (done) ,
    .rd_data       (rd_data) ,
    .rd_data_vaild (rd_data_vaild) ,

    .wrreq         (wrreq2) ,
    .I2C_rd_data   (I2C_rd_data) , 
    .rd_en         (rd_en)     
);

sync_fifo
#(.WIDTH         (8),          //缓存的数据宽度
  .DEPTH         (5),         //缓存的数据深度
  .MAX_DEPTH_BIT (3))           //可设置的最大深度位数7,即最大深度为2^7-1
u_sync_fifo2(
    .clk       (clk), //系统时钟50MHz
    .rst_n     (rst_n), //系统复位
    .wrreq     (wrreq2), //写使能
    .data      (I2C_rd_data), //写数据
    .rdreq     (rdreq2), //读使能

    .q         (data_to_uart), //读数据
    .empty     (), //空信号
    .full      (), //满信号
    .half      (), //半满信号
    .usedw     (usedw2)  //fifo中剩余数据个数    
);

fifo_ctrl2 u_fifo_ctrl2(
    .clk       (clk) ,
    .rst_n     (rst_n) ,
    .usedw     (usedw2) ,
    .q         (data_to_uart) ,
    .tx_done   (tx_done) ,

    .rdreq     (rdreq2) ,
    .send_en   (send_en) ,
    .data_send (data_send)     
);

Uart_Byte_Tx u_Uart_Byte_Tx(
    .clk        (clk), //系统时钟
    .rst_n      (rst_n), //系统复位
    .send_en    (send_en), //发送使能
    .data_byte  (data_send), //发送的数据
    .baud_set   (3'd1), //波特率设置
    .rs232_tx   (rs232_tx), //FPGA将数据转换成串行数据发出
    .tx_done    (tx_done), //发送数据完毕标志
    .uart_state ()  //串口发送状态,1为忙,0为空闲
);

endmodule

5. Débogage de la simulation et vérification au niveau de la carte

1. Débogage de la simulation

Envoyez 4 octets de données depuis le port série pendant le test, 8'b0000_1010, 8'b1000_0101, 8'b0000_1010, 8'b1000_0101, puis recommencez.

Le code de test est le suivant:

`timescale 1 ns/ 1 ns
module I2C_eeprom_top_tb();
// constants                                           
// test vector input registers
reg clk;
reg rs232_rx;
reg rst_n;
//reg treg_sda;
// wires                                               
wire rs232_tx;
wire scl;
wire sda;

parameter clk_period = 20;

// assign statements (if any)                          
//assign sda = treg_sda;
I2C_eeprom_top i1 (
// port map - connection between master ports and signals/registers   
	.clk(clk),
	.rs232_rx(rs232_rx),
	.rs232_tx(rs232_tx),
	.rst_n(rst_n),
	.scl(scl),
	.sda(sda)
);
M24LC64 u_M24LC64(
    .A0(1'b1), 
    .A1(1'b0), 
    .A2(1'b0), 
    .WP(1'b0), 
    .SDA(sda), 
    .SCL(scl), 
    .RESET(!rst_n)
);

pullup(sda);

initial clk = 0;
always #(clk_period/2) clk = ~clk;

initial begin
	#1;
	rst_n = 0;
	rs232_rx = 1;
	#(clk_period*5);
	rst_n = 1;
	#(clk_period*3);
	
	repeat(2)begin
	//0000_1010
	rs232_rx = 0;
	#(clk_period*5208);
	rs232_rx = 0;
	#(clk_period*5208*4);
	rs232_rx = 1;
	#(clk_period*5208);
	rs232_rx = 0;
	#(clk_period*5208);
	rs232_rx = 1;
	#(clk_period*5208);
	rs232_rx = 0;
	#(clk_period*5208);
	rs232_rx = 1;
	#(clk_period*5208);
	
	//1000_0101
	rs232_rx = 0;
	#(clk_period*5208);
	rs232_rx = 1;
	#(clk_period*5208);
	rs232_rx = 0;
	#(clk_period*5208*4);
	rs232_rx = 1;
	#(clk_period*5208);
	rs232_rx = 0;
	#(clk_period*5208);
	rs232_rx = 1;
	#(clk_period*5208);
	rs232_rx = 1;
	#(clk_period*5208);	
	end
	#(clk_period*500000);

    repeat(2)begin
	//0000_1010
	rs232_rx = 0;
	#(clk_period*5208);
	rs232_rx = 0;
	#(clk_period*5208*4);
	rs232_rx = 1;
	#(clk_period*5208);
	rs232_rx = 0;
	#(clk_period*5208);
	rs232_rx = 1;
	#(clk_period*5208);
	rs232_rx = 0;
	#(clk_period*5208);
	rs232_rx = 1;
	#(clk_period*5208);
	
	//1000_0101
	rs232_rx = 0;
	#(clk_period*5208);
	rs232_rx = 1;
	#(clk_period*5208);
	rs232_rx = 0;
	#(clk_period*5208*4);
	rs232_rx = 1;
	#(clk_period*5208);
	rs232_rx = 0;
	#(clk_period*5208);
	rs232_rx = 1;
	#(clk_period*5208);
	rs232_rx = 1;
	#(clk_period*5208);
	end
	#(clk_period*500000);

	$stop;
end
endmodule

Les résultats de la simulation sont les suivants:

Vous pouvez voir que les données envoyées par le port série sont cohérentes avec les données reçues par le dernier port série.

2. 板级验证

管脚分配:

由于硬件上没有对 scl 和 sda 信号进行上拉,而I2C协议要求 scl 和 sda 上拉,因此在对管脚分配时可对这两个信号进行弱上拉,具体设置如下:

在分配管脚右侧空白处右击,选择Customize Columns,将 Weak Pull-Up Resistor移到右边,点击OK,再到引脚分配后边将其弱上拉打开,见下图所示:

 将jic文件下载到FPGA板子后,打开串口助手,发送四个数据 12 13 12 11,可以看到串口接收到发送的四个数据,验证成功。

测试发现,输入有些数据,接收到会有异常,如下:

在这里,我将别人写好的比较权威的I2C代码移植到我的系统里,测试发现还是这个样子,这里分析了一下,可能是这个测试系统逻辑方面存在不足,但已经通过仿真和部分测试验证了I2C控制器模块的正确性。

 

Je suppose que tu aimes

Origine blog.csdn.net/qq_33231534/article/details/106594428
conseillé
Classement