FPGA学习之串口发送程序设计(来自小梅哥的教程学习者)

1. UART说明

可参考维基百科的文档:https://zh.wikipedia.org/wiki/UART

2. 实现逻辑图示

image

3. 程序实现

module UART_TX(rst_n,clk,send_en,baud_set,data_in,data_tx,flag,state);
     input rst_n;
     input clk;
     input send_en;
     input [1:0] baud_set;
     input [7:0] data_in;
    
     output reg data_tx;
     output reg flag;    //发送完成标志
     output reg state;    //当前状态
    
     localparam     START = 1'b0;
     localparam    STOP  = 1'b1;
    
    
     wire neg,pos;
     TRI_edge U1(
      .Clk(clk),
      .Rst_n(rst_n),
      .data_in(send_en),
      .neg_out(neg),
      .pos_out(pos)
     );
    
     //查表选择波特率
     //波特率    计数值
     //9600        50000000/9600  ≈ 5208
     //19200        50000000/19200 ≈ 2604
     //38400     50000000/38400 ≈ 1302
     //115200        50000000/115200≈ 434
     reg [15:0] bps_cnt;
     always @(posedge clk or negedge rst_n)
     if(!rst_n)
         bps_cnt <= 16'd433;
     else
         case (baud_set)
             0 : bps_cnt <= 16'd5207;
             1 : bps_cnt <= 16'd2603;
             2 : bps_cnt <= 16'd1301;
             3 : bps_cnt <= 16'd433;
         endcase
    
    
     reg [15:0] cnt;
     always @(posedge clk or negedge rst_n)
     if(!rst_n)
         cnt <= 16'd0;
     else if(state) begin
         if(cnt == bps_cnt)
             cnt <= 16'd0;
         else
             cnt <= cnt + 1'b1;
     end
     else
         cnt <= 16'd0;
    
     //得到波特率相应时钟
     reg baud_clk;
     always @(posedge clk or negedge rst_n)
     if(!rst_n)
         baud_clk <= 1'b0;
     else if(cnt == 16'd1)
         baud_clk <= 1'b1;
     else
         baud_clk <= 1'b0;
    
     //数波特率时钟上升沿,每加一次,传输一个数据位
     reg [3:0] data_sel;
     always @(posedge clk or negedge rst_n)
     if(!rst_n)
         data_sel <= 4'd0;
     else if(data_sel == 4'd11)
         data_sel <= 4'd0;
     else if(baud_clk)
         data_sel <= data_sel + 1'b1;
     else
         data_sel <= data_sel;
        
     always @(posedge clk or negedge rst_n)
     if(!rst_n)
         flag <= 1'b0;
     else if(data_sel == 4'd11)
         flag <= 1'b1;
     else
         flag <= 1'b0;
    
     reg [7:0] r_data_in;
     always @(posedge clk or negedge rst_n)
     if(!rst_n)
         r_data_in <= 8'd0;
     else if(neg)
         r_data_in <= data_in;
     else
         r_data_in <= r_data_in;
    
     //reg r_data_tx;
     always @(posedge clk or negedge rst_n)
     if(!rst_n)
         data_tx <= 1'b1;
     else
         case (data_sel)
             0 : data_tx <= 1'b1;
             1 : data_tx <= START;
             2 : data_tx <= r_data_in[0];
             3 : data_tx <= r_data_in[1];
             4 : data_tx <= r_data_in[2];
             5 : data_tx <= r_data_in[3];
             6 : data_tx <= r_data_in[4];
             7 : data_tx <= r_data_in[5];
             8 : data_tx <= r_data_in[6];
             9 : data_tx <= r_data_in[7];
             10 : data_tx <= STOP;
             default:data_tx <= 1'b1;
         endcase
    
     /*
     always @(*)
         case (data_sel)
             0 : r_data_tx = 1'b1;
             1 : r_data_tx = START;
             2 : r_data_tx = r_data_in[0];
             3 : r_data_tx = r_data_in[1];
             4 : r_data_tx = r_data_in[2];
             5 : r_data_tx = r_data_in[3];
             6 : r_data_tx = r_data_in[4];
             7 : r_data_tx = r_data_in[5];
             8 : r_data_tx = r_data_in[6];
             9 : r_data_tx = r_data_in[7];
             10 : r_data_tx = STOP;
         endcase
     */
    
     always @(posedge clk or negedge rst_n)
     if(!rst_n)
         state <= 1'b0;
     else if(neg)
         state <= 1'b1;
     else if(data_sel == 4'd11)
         state <= 1'b0;
     else
         state <= state;
    
endmodule


4. 程序优化说明

在小梅哥实现发送使能检测,为高电平将会把数据发送出去,这样将导致数据会无数次发送,只要使能信号为高电平。这里优化为使用边沿检测模块:

wire neg,pos;
    TRI_edge U1(
      .Clk(clk),
      .Rst_n(rst_n),
      .data_in(send_en),
      .neg_out(neg),
      .pos_out(pos)
     );

将使能检检测改为上边沿触发或者下边沿触发,问题解决。

  reg [7:0] r_data_in;
     always @(posedge clk or negedge rst_n)
     if(!rst_n)
         r_data_in <= 8'd0;
     else if(neg)
         r_data_in <= data_in;
     else
         r_data_in <= r_data_in;

posted @ 2021-04-20 17:26  二·月半  阅读(430)  评论(0)    收藏  举报