verilog-UART驱动流程

目录

1.理论介绍        

2.verilog代码实现 


1.理论介绍        

        UART(Universal asynchronous receivers-transmitter,通用异步收发器)数据帧结构如下图,zynq7020-PL侧的时钟频率fclk一般设置为50MHz,假设串口波特率为115200bps,则一个1bit传输需要1/115200s,即需要50M/115200个时钟周期,在verilog设计中实现串口功能时,通过计数时钟周期的个数便可得知当前传输的是第几位串口数据,uart接收/uart发送单字节及多字节设计流程可如下:

 

 

 

2.verilog代码实现 

//uart发送verilog实现
module uart_send(
    input       sys_clk,
    input       sys_rst_n,
    input  wire  uart_send_flag,//要给传感器开始发送数据了
    input wire [255:0] uart_send_data,//暂定30个字节的buff
    output   reg uart_txd,  //串口发送端口
    output reg uart_start_send,//串口开始发送
    output reg uart_stop_send,//串口结束发送
    output reg tx_endFlag    //txd发送完成了,下一刻开始在接收模块中等待回复
    );
parameter CLK_FREQ = 100_000_000;
parameter UART_BPS = 460800;
parameter BPS_CNT = CLK_FREQ/UART_BPS;
parameter CMD_PERIOD = 200;//200hz

reg uart_en_d0;
reg uart_en_d1;
reg [15:0] clk_cnt;
reg [7:0] tx_cnt;
reg tx_flag;
//reg tx_endFlag;//收到回车符后开始停止发送
reg en_flag;
reg [25:0] cmd_period_cnt;
reg uart_send_flag0;
reg uart_send_flag1;
wire uart_send_flag_rising;
reg [7:0] uart_send_array[31:0];
assign uart_send_flag_rising = uart_send_flag0 & (~uart_send_flag1);
always @(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)begin
        uart_send_flag0 <= 1'b0;
        uart_send_flag1 <= 1'b0;
    end
    else begin
        uart_send_flag0 <= uart_send_flag;
        uart_send_flag1 <= uart_send_flag0;
    end
end
//main code
//对发送信号使能uart延迟两个时钟周期
integer byte_index;
reg [7:0] tail_pre;
always @(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)begin
        en_flag <= 1'b0;      
        cmd_period_cnt <= 26'd0;
        tail_pre <= 8'd0;
    end
    //else if(cmd_period_cnt == (CLK_FREQ/CMD_PERIOD) )begin
    else if(uart_send_flag_rising)begin
        en_flag <= 1'b1;//开始发送数据
        for(byte_index = 0;byte_index < 8'd32; byte_index = byte_index+1)begin
            //sensor参数设置是以回车符结束
            if(tail_pre == 8'h0D && uart_send_data[255-(byte_index*8)-:8] == 8'h0A)begin
                uart_send_array[byte_index] <= uart_send_data[255-(byte_index*8)-:8];
                byte_index <= 8'd32;
                tail_pre <= 8'd0;
            end
            else begin
                uart_send_array[byte_index] <= uart_send_data[255-(byte_index*8)-:8];
                tail_pre <= uart_send_data[255-(byte_index*8)-:8];
            end
        end
        cmd_period_cnt <= 26'd0;
    end
    else begin
        cmd_period_cnt <= cmd_period_cnt + 1'b1;
        en_flag <= 1'b0;
    end    
end

//当脉冲信号en_flag到达时,将待发送的数据存起来并进入发送过程
always @(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)begin
        tx_flag <= 1'd0;
        uart_start_send <= 1'b0;
        uart_stop_send <= 1'b0;
    end
    else if(en_flag)begin
        tx_flag <= 1'b1;//进入发送过程 发送标志位拉高
        uart_start_send <= 1'b1;
    end
    else if(tx_endFlag)begin
        tx_flag <= 1'b0;
        uart_stop_send <= 1'b1;
    end
    else begin
        tx_flag <= tx_flag;
         uart_start_send <= 1'b0;
         uart_stop_send <= 1'b0;
    end
end

//进入发送过程后,启动系统时钟计数器
always @(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)begin
        clk_cnt <= 16'd0;
    end
    else if(tx_flag)begin//处于发送过程
        if(clk_cnt < (BPS_CNT-1))
            clk_cnt <= clk_cnt + 1'b1;
        else
            clk_cnt <= 16'd0;//对系统时钟计数达一个波特率周期后清零
    end
    else 
        clk_cnt <= 16'd0;
end

//进入发送过程后,启动发送数据计数器
always @(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        tx_cnt <= 8'd0;
    else if(tx_flag)begin
        if(clk_cnt == (BPS_CNT -1))
            tx_cnt <= tx_cnt + 1'b1;
        else
            tx_cnt <= tx_cnt;
    end
    else 
        tx_cnt <= 8'd0;
end

//根据发送数据计数器给uart发送端口赋值
always @(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)begin
        uart_txd <= 1'b1;
        tx_endFlag <= 1'b0;
    end
    else if(tx_flag)begin
        case(tx_cnt%10)
            4'd0: uart_txd <= 1'b0;//起始位
            4'd1: uart_txd <= uart_send_array[tx_cnt/10][0];
            4'd2: uart_txd <= uart_send_array[tx_cnt/10][1];
            4'd3: uart_txd <= uart_send_array[tx_cnt/10][2];
            4'd4: uart_txd <= uart_send_array[tx_cnt/10][3];
            4'd5: uart_txd <= uart_send_array[tx_cnt/10][4];
            4'd6: uart_txd <= uart_send_array[tx_cnt/10][5];
            4'd7: uart_txd <= uart_send_array[tx_cnt/10][6];
            4'd8: uart_txd <= uart_send_array[tx_cnt/10][7];
            4'd9: begin
                uart_txd <= 1'b1;//停止位
                if(uart_send_array[tx_cnt/10] == 8'h0A)begin
                    tx_endFlag <= 1'b1;
                end
                /*
                else if(tx_cnt > 8'd250)begin //若没有收到结束符,防止一直发送数据
                    tx_endFlag <= 1'b1;
                end
                */
            end
            default:;           
        endcase
        
    end
    else begin
        uart_txd <= 1'b1;
        tx_endFlag <= 1'b0;
    end
end
endmodule

 

//uart接收

module uart_recv(
    input       sys_clk,
    input       sys_rst_n,
    input       uart_rxd,
    output reg [31:0] uart_buff,
    output reg [31:0] uart_buff_addr,
    output reg uart_data_done,//触发一次数据传输 也作为接收完成标识
    output reg uart_start_recv, //uart检测到帧头后触发开始接收信号标识
    output reg uart_stop_recv,
    input  wire txd_end_flag, //检测到此信号变高电平开始等待回复 
    output reg [7:0] uart_data,
    output reg [15:0]total_cnt,
    output reg uart_recFlag,
    input wire sensor_mode//外部传输两种模式,根据不同的数据模式执行不同的处理动作
    );

parameter CLK_FREQ = 100000000;
parameter UART_BPS =   460800;
//localparam 相比与parameter 参数不可传递
parameter BPS_CNT = CLK_FREQ/UART_BPS;//一个位需要的时钟数

reg        uart_rxd_d0;
reg        uart_rxd_d1;
reg [15:0] clk_cnt;
reg [3:0]  rx_cnt;
reg        rx_flag;
reg [7:0]  rxdata;//接收数据寄存器

reg  uart_done_1Byte;
reg uart_done_1Byte_flag;
wire  uart_done_1Byte_en;


//缓存16字节的数据 
//reg [7:0] uart_data;
wire start_flag;//抓到的下降沿后就是起始位


wire txd_end_rising;
reg txd_end0;
reg txd_end1;



assign start_flag = uart_rxd_d1 & (~uart_rxd_d0);
assign uart_done_1Byte_en = uart_done_1Byte & (!uart_done_1Byte_flag);

//对UART接收端口的数据延迟两个时钟周期
always @(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)begin//复位则将双寄存器赋值0
        uart_rxd_d0 <= 1'b0;
        uart_rxd_d1 <= 1'b0;
    end
    else begin
        uart_rxd_d0 <= uart_rxd;
        uart_rxd_d1 <= uart_rxd_d0;
    end
end

//当脉冲信号start_flag到达时进入接收过程
always @(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)begin
        rx_flag <= 1'b0;
    end
    else begin
        if(start_flag)   //检测到起始位
            rx_flag <= 1'b1;//进入接收过程,标志位rx_flag拉高,计数到停止位中间时,停止接收过程
        else if((rx_cnt == 4'd9)&&(clk_cnt == BPS_CNT/2))
            rx_flag <= 1'b0;//接收过程结束,标志位拉低
        else
            rx_flag <= rx_flag;
    end
end

//进入接收过程后,启动系统时钟计数器
always @(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        clk_cnt <= 16'd0;
    else if(rx_flag)begin//处于接收过程
        if(clk_cnt < BPS_CNT-1)
            clk_cnt <= clk_cnt + 1'b1;
        else
            clk_cnt <= 16'd0;
    end
    else 
        clk_cnt <= 16'd0;
end

//进入接收过程后,启动接收数据计数器
always @(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        rx_cnt <= 4'd0;
    else if(rx_flag)begin
        if(clk_cnt == BPS_CNT -1)
            rx_cnt <= rx_cnt + 1'b1;
        else
            rx_cnt <= rx_cnt;
    end
    else
        rx_cnt <= 4'd0;
   
end

//根据接收数据计数器来寸uart接收端口数据
always @(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)
        rxdata <= 8'd0;
    else if(rx_flag)
        if(clk_cnt == BPS_CNT/2)begin //判断系统时钟计数器计数到数据位中间
            case(rx_cnt)
                4'd1:rxdata[0] <= uart_rxd_d1;//最低位 异步通信防止亚稳态,用已经寄存两拍的值来赋值而不直接用uart_rxd
                4'd2:rxdata[1] <= uart_rxd_d1;
                4'd3:rxdata[2] <= uart_rxd_d1;
                4'd4:rxdata[3] <= uart_rxd_d1;
                4'd5:rxdata[4] <= uart_rxd_d1;
                4'd6:rxdata[5] <= uart_rxd_d1;
                4'd7:rxdata[6] <= uart_rxd_d1;
                4'd8:rxdata[7] <= uart_rxd_d1;
                default:;
            endcase
        end
        else
            rxdata <= rxdata;
    else
        rxdata <= 8'd0;
    
end



//数据接收完后给出标识信号并输出接收到的数据
always @(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)begin
        uart_data <= 8'd0;
        uart_done_1Byte <= 1'b0;
    end
    else if(rx_cnt == 4'd9)begin//接收数据计数器计数到了停止位(第9位)
        uart_data <= rxdata;
        uart_done_1Byte <= 1'b1;
    end
    else begin 
        uart_data <= 8'd0;
        uart_done_1Byte <= 1'b0;
    end
end



always @(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)begin
        uart_done_1Byte_flag <= 1'b0;
    end
    else begin
        uart_done_1Byte_flag <= uart_done_1Byte;
    end
end

//发送设置指令,下一刻开始等待回复指令
assign txd_end_rising = txd_end0 & (~txd_end1);
always @(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)begin
        txd_end0 <= 1'b0;
        txd_end1 <= 1'b0;
    end
    else begin
        txd_end0 <= txd_end_flag;
        txd_end1 <= txd_end0;
    end
end
//开始收到设置指令起5s为回复时间,这里有两种模式
reg recv_timeout;
reg [5:0] cnts_timeout;
reg [1:0] state;
reg [27:0] cnts_1s;
always @(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)begin
        recv_timeout <= 1'b0;
        state <= 2'd0;
        cnts_1s <= 28'd0;
        cnts_timeout <= 6'd0;
    end
    else if(sensor_mode)begin//设置模式    
        case(state)
            2'd0:begin 
                recv_timeout <= 1'b0; 
                cnts_1s <= 28'd0;
                cnts_timeout <= 6'd0;              
                if(txd_end_rising)begin
                    state <= 2'd1;
                end
                else begin
                    state <= state;
                end                           
            end
            2'd1:begin
                if(cnts_timeout == 6'd5)begin
                    cnts_timeout <= 6'd0; 
                    state <= 2'd2; 
                    cnts_1s <= 28'd0;  
                end               
                else if(cnts_1s == 28'd100_000_000)begin
                    cnts_1s <= 28'd0;
                    cnts_timeout <= cnts_timeout + 1'b1;
                end
                else begin
                    cnts_1s <= cnts_1s + 1'b1;
                end                       
            end
            2'd2:begin
                recv_timeout <= 1'b1;
                state <= 2'd0;
            end
            default:state <= 2'd0;        
        endcase   
    end
    else begin//数据模式    
        case(state)
            2'd0:begin 
                recv_timeout <= 1'b0; 
                cnts_1s <= 28'd0;
                cnts_timeout <= 6'd0;              
                if(total_cnt == 16'd100)begin
                    state <= 2'd1;
                end
                else begin
                    state <= state;
                end                           
            end
            2'd1:begin
                state <= 2'd2;                       
            end
            2'd2:begin
                recv_timeout <= 1'b1;
                state <= 2'd0;
            end
            default:state <= 2'd0;        
        endcase   
    end
end


reg [7:0] data_cnt; //发送数据个数计数器
//reg uart_recFlag;//收到uart起始信号后拉高  
always @(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)begin
        uart_recFlag <= 1'b0;
        uart_start_recv <= 1'b0;
        uart_stop_recv <= 1'b0;
    end
    else if(recv_timeout)begin
        uart_recFlag <= 1'b0;
        uart_stop_recv <= 1'b1;
    end
    else if(start_flag)begin
        uart_recFlag <= 1'b1;
        uart_start_recv <= 1'b1;
    end
    else begin
        uart_recFlag <= uart_recFlag;
        uart_start_recv <= 1'b0;
        uart_stop_recv <= 1'b0;
    end
 end  
//reg [15:0]total_cnt;//一共接收的字节数
always @(posedge sys_clk or negedge sys_rst_n)begin
    if(!sys_rst_n)begin
        data_cnt <= 8'd0;
        uart_buff <= 32'd0;
        uart_buff_addr <= 32'h0000_0000;
        uart_data_done <= 1'd0;
        total_cnt <= 16'd0;
    end
    else if(state == 2'd2)begin   //recv_timeout的前一个时钟判断最后一次uart_buff是不是4字节对齐了,没有则补零防止数据丢失
        total_cnt <= total_cnt + 16'd4;
        case(data_cnt)
            8'd1,8'd2,8'd3:begin
                uart_buff <= uart_buff;
                uart_buff_addr <= uart_buff_addr + 32'd4;
                uart_data_done <= 1'b1;
            end
            default:;
        endcase
    end
    else if(recv_timeout)begin//数据和地址清零
        uart_buff <= {total_cnt,16'h3030};
        uart_buff_addr <= 32'h0000_0000;//该地址存数据长度
        total_cnt <= 16'd0;
        data_cnt <= 8'd0;
        uart_data_done <= 1'b1;
    end
    else if(uart_done_1Byte_en && uart_recFlag==1'b1)begin
        uart_data_done <= 1'b0;
        total_cnt <= total_cnt + 1'b1;
        if(data_cnt == 8'd3)begin
            data_cnt <= 8'd0;
            uart_buff <= {uart_buff[31:8],uart_data};
            uart_buff_addr <= uart_buff_addr + 32'd4;
            uart_data_done <= 1'b1;
        end
        else begin
            uart_buff[(31-8*data_cnt)-:8] <= uart_data;//从高位开始缓存
            data_cnt <= data_cnt + 1'b1;
        end            
    end
    else begin
        uart_buff <= uart_buff;
        data_cnt <= data_cnt;
        total_cnt <= total_cnt;
        uart_data_done <= 1'b0;
    end
end



endmodule
//顶层文件
module uart_top(
    input sys_clk,
    input sys_rst_n,
    input uart_rxd,
    output uart_txd,
    output wire  uart_data_done,
    output wire [31:0] uart_buff,
    output wire [31:0] uart_buff_addr,
    output wire uart_start_recv,
    output wire uart_stop_recv,
    input wire   uart_send_flag,
    input wire [255:0] uart_send_data,
    output wire uart_start_send,
    output wire uart_stop_send,
    input wire sensor_mode,
    output wire [7:0] uart_data,
    output wire [15:0]total_cnt,
    output wire uart_recFlag
    );
 
  wire tx_endFlag;

 

uart_recv u_uart_recv(
    .sys_clk          (sys_clk),
    .sys_rst_n        (sys_rst_n),
    .uart_rxd         (uart_rxd),
    .uart_buff (uart_buff),
    .uart_buff_addr (uart_buff_addr),
    .uart_data_done(uart_data_done),
    .uart_start_recv(uart_start_recv),
    .uart_stop_recv(uart_stop_recv),
    .txd_end_flag(tx_endFlag),
    .uart_data(uart_data),
    .total_cnt(total_cnt),
    .uart_recFlag (uart_recFlag),
    .sensor_mode(sensor_mode)
);
uart_send u_uart_send(
    .sys_clk          (sys_clk),
    .sys_rst_n        (sys_rst_n),
    .uart_txd    (uart_txd),
    .uart_send_flag(uart_send_flag),
    .uart_send_data(uart_send_data),
    .uart_start_send(uart_start_send),
    .uart_stop_send(uart_stop_send),
    .tx_endFlag(tx_endFlag)
);
endmodule

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值