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

  • 2
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
UART串口通信_FPGA上位机通信实验FPGA设计Verilog逻辑源码Quartus11.0工程文件. 功能描述:串口通信__FPGA上位机通信(波特率:9600bps,10个bit是1位起始位,8个数据位,1个结束) ** 操作过程:按动key2,FPGA向PC发送“da xi gua"一次,KEY1是复位按键。 ** 字符串(串口调试工具设成字符格式接受发送方式),FPGA接受(0到9)后显示在7段数码管上。 module uart(clk,rst,rxd,txd,en,seg_data,key_input); input clk,rst; input rxd; //串行数据接收端 input key_input; //按键输入 output[7:0] en; output[7:0] seg_data; reg[7:0] seg_data; output txd; //串行数据发送端 ////////////////////inner reg//////////////////// reg[15:0] div_reg; //分频计数器,分频值由波特率决定。分频后得到频率8倍波特率的时钟 reg[2:0] div8_tras_reg; //该寄存器的计数值对应发送时当前位于的时隙数 reg[2:0] div8_rec_reg; //该寄存器的计数值对应接收时当前位于的时隙数 reg[3:0] state_tras; //发送状态寄存器 reg[3:0] state_rec; //接受状态寄存器 reg clkbaud_tras; //以波特率为频率的发送使能信号 reg clkbaud_rec; //以波特率为频率的接受使能信号 reg clkbaud8x; //以8倍波特率为频率的时钟,它的作用是将发送接受一个bit的时钟周期分为8个时隙 reg recstart; //开始发送标志 reg recstart_tmp; reg trasstart; //开始接受标志 reg rxd_reg1; //接收寄存器1 reg rxd_reg2; //接收寄存器2,因为接收数据为异步信号,故用两级缓存 reg txd_reg; //发送寄存器 reg[7:0] rxd_buf; //接受数据缓存 reg[7:0] txd_buf; //发送数据缓存 reg[2:0] send_state; //这是发送状态寄存器 reg[19:0] cnt_delay; //延时去抖计数器 reg start_delaycnt; //开始延时计数标志 reg key_entry1,key_entry2; //确定有键按下标志 //////////////////////////////////////////////// parameter div_par=16'h145; //分频参数,其值由对应的波特率计算而得,按此参数分频的时钟频率是波倍特率的8 //倍,此处值对应9600的波特率,即分频出的时钟频率是9600*8 (CLK 50M) //////////////////////////////////////////////// assign txd=txd_reg; //assign lowbit=0; assign en=0; //7段数码管使能信号赋值 always@(posedge clk ) begin if(!rst) begin cnt_delay<=0; start_delaycnt<=0; end else if(start_delaycnt) begin if(cnt_delay!=20'd800000) begin cnt_delay<=cnt_delay+1'b1; end else begin cnt_delay<=0;

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值