我是靠谱客的博主 内向荷花,最近开发中收集的这篇文章主要介绍FPGA学习之UART串口通信(当UART控制器从上位机接收到数据后,立即将数据输出,并发送回上位机,完成“回环测试”),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

​FPGA需要控制的仅为两条信号线:RXD和TXD,即数据接收线和数据发送线,因此我们只需要关注数据接收和发送时的时序图。传送的一帧数据里面包括起始位(0),8位数据位,奇偶校验位,停止位(1)。当空闲时,10位数据均为高电平,在检测到起始位低电平时,开始传输。

波特率是衡量数据传输速率的指标,表示每秒传输的二进制位数,在此例中设为9600,所以50M/9600==5207,所以分频时要计数到5207.

1.串口接收数据的速率控制模块:用于控制串口接收的速率

module bps_rx(
    input clk,
    input rst_n,
    input rx_en,       //bps_rx开始计数的使能信号
    
    output reg rx_sel_data,       //控制数据采集的尖峰脉冲信号
    output reg [3:0] rx_num         //有效数据位的计数值
);

parameter bps_div_1 = 13'd5207;        //50M/5207=9600频率(波特率)
parameter bps_div_2 = 13'd2603;         //数据在波特率的中间值时采集

reg flag; //接收标志位,当接收到rx_en时,拉高
reg [12:0] cnt;    //波特率计数


        
always @(posedge clk or negedge rst_n)
    if(!rst_n)
        cnt <= 13'b0;
    else if(flag && cnt < bps_div_1)
        cnt <= cnt + 1'b1;
    else
        cnt <= 13'b0;
        
always@(posedge clk or negedge rst_n)
    if(!rst_n)
        flag <= 0;
    else if(rx_en)
        flag <= 1;
    else if (rx_num == 4'd10)   //当数据接收完毕时,标志位清零
        flag <= 0;

        
always @(posedge clk or negedge rst_n)  //规定接收数据的范围,即一帧数据 两个起始位 8位数据位
        if(!rst_n)
            rx_num = 4'd0;
        else if(rx_sel_data && flag)
            rx_num = rx_num + 1'b1;
        else if (rx_num == 4'd10)
            rx_num = 4'd0;
            
always @(posedge clk or negedge rst_n)        //数据在波特率的中间值时采集

        if(!rst_n)
            rx_sel_data = 1'b0;
        else if(cnt == bps_div_2)  //生成尖峰脉冲,尖峰脉冲为采集数据的使能信号
            rx_sel_data = 1'b1;
        else 
            rx_sel_data = 1'b0;
        
endmodule

2.串口串行数据的接收模块:用于接收串口串行数据

module uart_rx(
    input clk,
    input rst_n,
    input rx_sel_data,       //控制数据采集的尖峰脉冲信号
    input [3:0] rx_num,         //有效数据位的计数值
    input rs232_rx,    //输入串行数据
    
    output rx_en,      //使能信号:启动接收波特率计数
    output reg tx_en,      //使能信号:在接收完数据后,开始发送数据
    output reg [7:0] rx_d  //将采集的8位数据转换为并行数据
);

reg in_1, in_2; 
reg [7:0] rx_d_r;   //数据寄存器
always @(posedge clk or negedge rst_n)
    if (!rst_n)
        begin
            in_1 <= 1'b1; //因为起始位为0,所以初值设为1
            in_2 <= 1'b1;
        end
    else 
        begin
            in_1 <= rs232_rx; 
            in_2 <= in_1;
        end
assign rx_en = in_2 && (~in_1);   //当in_1变为0时,说明已经开始接收数据,就可以拉高接收使能信号

//确保在一帧数据的中间8位进行数据读取
always @(posedge clk or negedge rst_n)
    if (!rst_n)
        begin
            rx_d_r <= 8'b0;
            rx_d <= 8'b0;
        end
    else if (rx_sel_data)    //数据采集的尖峰脉冲信号来的时候开始接收数据
        case (rx_num)
        0:; //起始位不处理
        1: rx_d_r[0] <= rs232_rx;
        2: rx_d_r[1] <= rs232_rx;
        3: rx_d_r[2] <= rs232_rx;
        4: rx_d_r[3] <= rs232_rx;
        5: rx_d_r[4] <= rs232_rx;
        6: rx_d_r[5] <= rs232_rx;
        7: rx_d_r[6] <= rs232_rx;
        8: rx_d_r[7] <= rs232_rx;
        9: rx_d <= rx_d_r;       //将数据传给输出
        default :;
        endcase
        
always @(posedge clk or negedge rst_n)
    if(!rst_n)
        tx_en <= 0;
    else if (rx_num == 9 && rx_sel_data)     //在接收完停止位后 把发送标志位拉高
        tx_en <= 1;
    else
        tx_en <= 0;
endmodule

3.串口发送数据的速率控制模块:用于控制串口发送的速率

module bps_tx(
    input clk,
    input rst_n,
    input tx_en,       //bps_tx开始计数的使能信号
    
    output reg tx_sel_data,       //控制数据采集的尖峰脉冲信号
    output reg [3:0] tx_num         //有效数据位的计数值
);

parameter bps_div_1 = 13'd5207;        //50M/5207=9600频率(波特率)
parameter bps_div_2 = 13'd2603;         //数据在波特率的中间值时采集

reg flag; //接收标志位,当接收到tx_en时,拉高
reg [12:0] cnt;    //波特率计数

always@(posedge clk or negedge rst_n)
    if(!rst_n)
        flag <= 0;
    else if(tx_en)
        flag <= 1;
    else if (tx_num == 4'd10)   //当数据接收完毕时,标志位清零
        flag <= 0;

        
always @(posedge clk or negedge rst_n)
    if(!rst_n)
        cnt <= 13'b0;
    else if(flag && cnt < bps_div_1)
        cnt <= cnt + 1'b1;
    else
        cnt <= 13'b0;
        

        
        
always @(posedge clk or negedge rst_n)  //规定接收数据的范围,即一帧数据 两个起始位 8位数据位
        if(!rst_n)
            tx_num = 4'd0;
        else if(tx_sel_data && flag)
            tx_num = tx_num + 1'b1;
        else if (tx_num == 4'd10)
            tx_num = 4'd0;
            
            
always @(posedge clk or negedge rst_n)        //数据在波特率的中间值时采集

        if(!rst_n)
            tx_sel_data = 1'b0;
        else if(cnt == bps_div_2)  //生成尖峰脉冲,尖峰脉冲为采集数据的使能信号
            tx_sel_data = 1'b1;
        else 
            tx_sel_data = 1'b0;            
endmodule

4.串口串行数据的发送模块:用于发送串口串行数据

module uart_tx(
    input clk,
    input rst_n,
    input tx_sel_data,       //控制数据采集的尖峰脉冲信号
    input [3:0] tx_num,         //有效数据位的计数值
    input [7:0] rx_d,    //输入8wei数据
    
    output reg rs232_tx  //发送数据
);


//确保在一帧数据的中间8位进行数据读取
always @(posedge clk or negedge rst_n)
    if (!rst_n)
        rs232_tx <= 1'b1;
    else if (tx_sel_data)    //数据采集的尖峰脉冲信号来的时候开始接收数据
        case (tx_num)
        0:rs232_tx <= 1'b0; //起始位为0
        1: rs232_tx <= rx_d[0];
        2: rs232_tx <= rx_d[1];
        3: rs232_tx <= rx_d[2];
        4: rs232_tx <= rx_d[3];
        5: rs232_tx <= rx_d[4];
        6: rs232_tx <= rx_d[5];
        7: rs232_tx <= rx_d[6];
        8: rs232_tx <= rx_d[7];
        9: rs232_tx <= 1'b1;     //停止位1
        default :rs232_tx <= 1'b1;
        endcase
       
endmodule

5.顶层模块:

module uart(
    input clk,
    input rst_n,
    input rs232_rx,  
    
    output  rs232_tx
);

wire rx_en;
wire tx_en;
wire [7:0] rx_d;
wire [3:0] rx_num,tx_num;

//模块例化
bps_rx bps_rx(
    .clk(clk),
    .rst_n(rst_n),
    .rx_en(rx_en),
    .rx_num(rx_num),
    .rx_sel_data(rx_sel_data)
);

uart_rx uart_rx(
    .clk(clk),
    .rst_n(rst_n),
    .rx_en(rx_en),
    .rx_num(rx_num),
    .rx_sel_data(rx_sel_data),
    .rx_d(rx_d),
    .rs232_rx(rs232_rx),
    .tx_en(tx_en)
);

bps_tx bps_tx(
    .clk(clk),
    .rst_n(rst_n),
    .tx_en(tx_en),
    .tx_num(tx_num),
    .tx_sel_data(tx_sel_data)
);

uart_tx uart_tx(
    .clk(clk),
    .rst_n(rst_n),
    .rx_d(rx_d),
    .tx_num(tx_num),
    .rs232_tx(rs232_tx),
    .tx_sel_data(tx_sel_data)
);
endmodule

6.测试文件

`timescale 1 ns/ 1 ns
module uart_tb();

parameter T = 20;
reg clk;
reg rst_n;
reg rs232_rx;

                                          
wire rs232_tx;
                     

initial                                                
begin                                                  

	clk = 1'b0;
	rst_n = 1'b0;
    rs232_rx = 1;
	#200 
	rst_n = 1'b1;
    //模拟发送一帧数据
    #200 rs232_rx = 0;   //起始位
    #110000 rs232_rx = 0; 
    #110000 rs232_rx = 1; 
    #110000 rs232_rx = 1; 
    #110000 rs232_rx = 0; 
    #110000 rs232_rx = 0; 
    #110000 rs232_rx = 1; 
    #110000 rs232_rx = 0; 
    #110000 rs232_rx = 1; //停止位
    #1500000  $stop;
	
                   
end                                                    
always #(T/2)  clk = ~clk;                                              
    
uart i1 (

	.rs232_rx(rs232_rx),
    .rs232_tx(rs232_tx),
	.clk(clk),
	.rst_n(rst_n)
);                              
endmodule

7.RTL图:

 仿真图:

工程文件上传至qq群:868412045

最后

以上就是内向荷花为你收集整理的FPGA学习之UART串口通信(当UART控制器从上位机接收到数据后,立即将数据输出,并发送回上位机,完成“回环测试”)的全部内容,希望文章能够帮你解决FPGA学习之UART串口通信(当UART控制器从上位机接收到数据后,立即将数据输出,并发送回上位机,完成“回环测试”)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(50)

评论列表共有 0 条评论

立即
投稿
返回
顶部