基于FPGA的串口(UART)发送

八.基于FPGA的串口(UART)发送

1.简介

在这里插入图片描述

  1. 串口通信模块设计的目的是用来发送数据的,因此需要一个数据输入端口。
  2. 串口通信,支持不同的波特率,所以需要一个波特兰设置端口。
  3. 串口通信的本质是将8位并行的数据通过一根信号线。在不同的时刻传输并行数据的不同位,通过多个时刻,最终将8位并行数据全部传出。
  4. 串口通信以1位的低电平标志串行传输的开始,待8位数据传输完成之后,再以1位的高电平标志传输结束。

2.代码

   `timescale 1ns / 1ps
module uart_byte_tx(
    sys_clk ,sys_rst , date , date_tx ,baud_set ,tx_done ,send_en
    );
    input sys_clk ;
    input sys_rst ;
    input  [2:0]baud_set  ; 
    input [7:0] date ;
    output reg date_tx; 
    output reg tx_done ;
    input send_en ;
// 1.波特率的设置    
    reg [12:0] bps_DR ;
    always@(*) begin 
    case(baud_set)
        0 :  bps_DR <= 5208; 	// 1_000_000_000 / 9600 /20 ;
        1 :  bps_DR <=2604 ;	// 1_000_000_000 / 19200/20 ;
        2 :  bps_DR <= 1302; 	//1_000_000_000 / 38600/20 ;
        3 :  bps_DR <= 868 ;	//1_000_000_000 / 57600/20 ;
        4 :  bps_DR <= 434 ;	// 1_000_000_000 / 115200/20 ;
      default  : bps_DR = 5208; // 1_000_000_000 / 9600/20  ;
      endcase
      end
      
//2 波特率计数时钟  div_cnt       
reg [17:0]   div_cnt ; //一位数据位发送时间计数器,记录发送数据的时间,用于更新下一段数据
always@(posedge sys_clk or negedge sys_rst )
    if(!sys_rst)
        div_cnt <= 0 ;
    else if (send_en == 1 ) //如果有使能才可以进行自加。
    begin  
        if (div_cnt >= bps_DR - 1 )
            div_cnt <= 0 ;
        else 
            div_cnt <= div_cnt + 1'd1 ; 
        end
     else 
        div_cnt <= 0 ;  //没有使能信号,使计数器归0
        
// 3.对波特率时钟 进行计数
wire bps_clk ;//每一位发送数据前后的标志位
assign bps_clk = (div_cnt == 1) ;  //当每次计数器记到 1 的时候就让数据开始发送

reg [3:0] bps_cnt ;
always@(posedge sys_clk or negedge sys_rst )
    if(!sys_rst)
        bps_cnt <= 0 ;
    else if (send_en) begin
        if(bps_clk == 1 ) //当有发送的标志时,状态才自增一次。
            begin 
            if (bps_cnt == 11) //当状态计数到第11个状态,状态归0。
                bps_cnt <= 0 ;
             else
                bps_cnt<= bps_cnt + 1'd1 ; end
                        end 
      else 
         bps_cnt <= 0 ;
                         
// 5.数据的发送环节
always@(posedge sys_clk or negedge sys_rst )
    if(!sys_rst) 
        date_tx <= 1 ;
    else 
        begin 
        case (bps_cnt) //开始发送数据,以数据状态来决定发送的是哪个数据。
            // 0 :   date_tx <= 0 ; 
            1 :   date_tx <= 0; //发送起始位,为什么不是0状态时刻发送0,因为在复位或者空闲时状态为0,
                                      //而此时需要·uart_tx为高电平,所以在0状态时刻不能为0。
            2 :   date_tx <= date[0];  //发送数据低位,数据从低位发送到高位,此下依次发送数据。 
            3 :   date_tx <= date[1];
            4 :   date_tx <= date[2];
            5 :   date_tx <= date[3];
            6 :   date_tx <= date[4];
            7 :   date_tx <= date[5];
            8 :   date_tx <= date[6];
            9 :   date_tx <= date[7];
            default  date_tx <= 1 ; //数据位发送完成之后,需要发送停止位,我们可以发现,空闲和停止位都是高电平,所以用default来将这些状态综合。
//细心的可以发现,我们状态定义了11个,这儿9个就结束了,为什么需要定义11个呢?因为在最后一个停止位发送为第10个状态,而停止位需要保留一个数据周期,
                // 所以在第10个状态发送停止位后,需要再延后一个状态,来保证停止位的时间,所以是11个状态。
           endcase 
         end 
         
         
 // 6. 专门处理tx_done 
always@(posedge sys_clk or negedge sys_rst )
     if(!sys_rst) 
         tx_done <= 0 ;         
     else if ((bps_clk ) && ( 10 <= bps_cnt)) //当状态为第10或者11个状态时且发送一个数据的标志位置位时才使总发送标志位置1。
        tx_done <= 1 ;
     else 
        tx_done <= 0 ; //其余时刻都将状态标志位置0。

endmodule

2.测试代码

`timescale 1ns / 1ps
module uart_byte_tx_tb( );
    reg     sys_clk ;
    reg     sys_rst ;
    reg     [7:0] date ;
    reg     send_en ;
    reg     [2:0] baud_set  ; 
    wire    tx_done ;
    wire    date_tx;
    
     uart_byte_tx uart_byte_tx(
      .sys_clk    (sys_clk )       ,
      .sys_rst    (sys_rst)        ,
      .date       (date)           ,
      .send_en    (send_en)        ,
      .baud_set   (baud_set)              ,
      .tx_done    (tx_done)        ,
      .date_tx    (date_tx) 
      );


initial sys_clk = 1 ;
always#10 sys_clk = !sys_clk ;



initial 
    begin 
     sys_rst = 0 ;
     date = 0 ;
     send_en = 0 ;
     baud_set = 4; 
     #201
        sys_rst = 1 ;
      #20 
        date =  8'b1001_1000 ;
        send_en = 1 ;
      #20 ;
       @(posedge tx_done)  // 在这里一直等待 Tx_Done =1;
      send_en = 0;
      #6000 
        date =  8'b1001_1111 ;
         send_en = 1 ;
          #20 ;
          @(posedge tx_done)  // 在这里一直等待 Tx_Done =1;
          send_en = 0;
      
        $stop;     
end
endmodule

4.仿真结果

在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值