【verilog】UART 串口接收(FPGA)

module ser_rx(clk,rst,rx,rx_done,rx_state,outdata);
        input clk;
        input rst;
        input rx;
        output reg[7:0]outdata;
        output reg rx_done;
        output reg rx_state;

    reg[15:0] baud = 16'd324;
    //9600=324, 19200=162, 38400=80, 57600=53, 115200=26.
    //设置波特率:
    //对每一bit进行16次采样,分为【5+6+5】,将中间六次作为稳定期
    //并将其累加,所得结果可能为:000,001,010,011,100,101,110
    //我们认定在采样期间出现3次以上为1,那么就认定此bit为1.
    //即:中间六次采样累加的结果的最高位为1,就认定此bit为1.
    //9600bit/s -》 1bit=104167ns -》104167/20ns(50Mhz)=5208个时钟 -》5208/16(次采样)=325个时钟
    //对系统时钟计数从0~324 正好为325次

 
   //同步
   reg sync1,sync2;  
   always@(posedge clk or negedge rst)
        if(!rst)
        begin
        sync1 <= 1'b0;
        sync2 <= 1'b0;
        end
        
        else
        begin
        sync1 <= rx;
        sync2 <= sync1;
        end
     
     
    //产生符合时间标准的采样flag
    reg[15:0] cnt;
    reg flag;
    always@(posedge clk or negedge rst)
        if(!rst)
        cnt <= 16'b0;
        else if(rx_state)begin
            if(cnt == baud)
                cnt <= 16'd0;
            else  
                cnt <= cnt + 16'd1;
            end
        else
            cnt <= 16'd0;
            
    always@(posedge clk or negedge rst)
        if(!rst)
            flag <= 1'b0;
        else if(cnt == 16'd1)
            flag <= 1'b1;
        else
            flag <= 1'b0;
            
                
    //串口数据为10bit 10*16=160,所以一组数据需要160个采样flag
    reg [8:0] cnt_for_flag;  
    always@(posedge clk or negedge rst)
        if(!rst)
            cnt_for_flag <= 8'd0;
        else if(cnt_for_flag == 8'd159 || (cnt_for_flag == 8'd12 && accum_START_BIT > 2'd2 ))    
            //计满 或 出现起始位出错[当计数到12时(这时对起始位的采样累加已经完成)但是采到1的次数大于2(but起始位为低电平)]将计数器清零!
            cnt_for_flag <= 8'd0;
        else if(flag)
            cnt_for_flag <= cnt_for_flag + 1'd1;
        else 
            cnt_for_flag <= cnt_for_flag;
          
          
    //多路器,数据端为下面这三个寄存器,选择端为cnt_for_flag的特定计数值,当处于特定计数值的时候下面这三个某个寄存器会与sync2进行累加
    reg [2:0]accum_START_BIT;
    reg [2:0]accum_STOP_BIT; 
    reg [2:0]accum_data[7:0];     //累加寄存器(深度为8bit,宽度为3bit)
    always@(posedge clk or negedge rst)
        if(!rst) begin
            accum_START_BIT <= 3'd0;
            accum_data[0] <= 3'd0;
            accum_data[1] <= 3'd0;
            accum_data[2] <= 3'd0;
            accum_data[3] <= 3'd0;
            accum_data[4] <= 3'd0;
            accum_data[5] <= 3'd0;
            accum_data[6] <= 3'd0;
            accum_data[7] <= 3'd0;
            accum_STOP_BIT <= 3'd0;
            end
        else if(flag)
            case(cnt_for_flag)
                0:begin
                    accum_START_BIT <= 3'd0;
                    accum_data[0] <= 3'd0;
                    accum_data[1] <= 3'd0;
                    accum_data[2] <= 3'd0;
                    accum_data[3] <= 3'd0;
                    accum_data[4] <= 3'd0;
                    accum_data[5] <= 3'd0;
                    accum_data[6] <= 3'd0;
                    accum_data[7] <= 3'd0;
                    accum_STOP_BIT <= 3'd0;
                  end
           6,7,8,9,10,11:accum_START_BIT <= accum_START_BIT + sync2;
           22,23,24,25,26,27:accum_data[0] <= accum_data[0] + sync2;
           38,39,40,41,42,43:accum_data[1] <= accum_data[1] + sync2;
           54,55,56,57,58,59:accum_data[2] <= accum_data[2] + sync2;
           70,71,72,73,74,75:accum_data[3] <= accum_data[3] + sync2;
           86,87,88,89,90,91:accum_data[4] <= accum_data[4] + sync2;
           102,103,104,105,106,107:accum_data[5] <= accum_data[5] + sync2;
           118,119,120,121,122,123:accum_data[6] <= accum_data[6] + sync2;
           134,135,136,137,138,139:accum_data[7] <= accum_data[7] + sync2;
           150,151,152,153,154,155:accum_STOP_BIT <= accum_STOP_BIT + sync2;
           default:
              begin
                    accum_START_BIT <= accum_START_BIT;
                    accum_data[0] <= accum_data[0];
                    accum_data[1] <= accum_data[1];
                    accum_data[2] <= accum_data[2];
                    accum_data[3] <= accum_data[3];
                    accum_data[4] <= accum_data[4];
                    accum_data[5] <= accum_data[5];
                    accum_data[6] <= accum_data[6];
                    accum_data[7] <= accum_data[7];
                    accum_STOP_BIT <= accum_STOP_BIT;
              end
              endcase
     
     
     //取累加结果的最高位,作为最终结果
     always@(posedge clk or negedge rst)
            if(!rst)
                outdata <= 8'd0;
            else if(cnt_for_flag == 8'd159)
                begin 
                outdata[0] <= accum_data[0][2]; 
                outdata[1] <= accum_data[1][2]; 
                outdata[2] <= accum_data[2][2];
                outdata[3] <= accum_data[3][2];
                outdata[4] <= accum_data[4][2];
                outdata[5] <= accum_data[5][2];
                outdata[6] <= accum_data[6][2];
                outdata[7] <= accum_data[7][2];
            end    
     
     //一次接收完成标志“rx_done”
     always@(posedge clk or negedge rst)
        if(!rst)
            rx_done <= 1'd0;
        else if(cnt_for_flag == 8'd159)
            rx_done <= 1'd1;
        else 
            rx_done <= 1'd0;
            

   //下降沿检测:用于检测从空闲到检测起始位那个下降沿
   wire f_edge;     
   reg data1,data2;       
   always@(posedge clk or negedge rst)
        if(!rst)
        begin
        data1 <= 1'b0;
        data2 <= 1'b0;
        end
        
        else
        begin
        data1 <= sync2;
        data2 <= data1;
        end
        
    assign f_edge = !data1 && data2;
    
    //输出工作状态:rx_state,从空闲到起始位的下降沿开始 至 一次数据接收完成
    always@(posedge clk or negedge rst)
    if(!rst)
        rx_state <= 1'b0;
    else if(f_edge)
        rx_state <= 1'b1;
    else if(rx_done || (cnt_for_flag == 8'd12 && (accum_START_BIT > 2)))
        rx_state <= 1'b0;
    else
        rx_state <= rx_state;
        
endmodule
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搞IC的那些年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值