首页 > 其他分享 >verilog-UART驱动流程

verilog-UART驱动流程

时间:2024-08-26 10:53:54浏览次数:7  
标签:wire UART 流程 send sys uart verilog output reg

目录

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

标签:wire,UART,流程,send,sys,uart,verilog,output,reg
From: https://blog.csdn.net/fly_802/article/details/141531566

相关文章

  • 婚宴座位号查询流程
            在一场精心策划的婚宴中,座位号的快速查询对于宾客而言至关重要。本文将一步步指导您如何利用云分组官网和小程序,高效地完成座位号的查询工作。步骤一:电脑端信息上传1. 官网访问:打开云分组官网。2. 登录操作:使用微信扫码登录系统。3. 查询菜单选择:点击“......
  • Python3.11二进制AI项目程序打包为苹果Mac App(DMG)-应用程序pyinstaller制作流程(App
    众所周知,苹果MacOs系统虽然贵为Unix内核系统,但由于系统不支持N卡,所以如果想在本地跑AI项目,还需要对相关的AI模块进行定制化操作,本次我们演示一下如何将基于Python3.11的AI项目程序打包为MacOS可以直接运行的DMG安装包,可以苹果系统中一键运行AI项目。MacOs本地部署AI项目首先确......
  • Renesa Version Board开发RT-Thread 之UART驱动应用
    目录概述1硬件介绍2软件配置2.1RT-ThreadStudio配置参数 2.2FSP配置MCU3RT-Thread中UART的接口介绍3.1RT-ThreadUART简介3.2  RT-Thread下的UART接口4 UART的应用4.1应用功能实现 4.2源代码文件5测试程序下载地址:RenesaVersionBoard开发RT-Th......
  • adc-ads1281驱动流程
            ADS1281是一款高性能、低功耗的模拟-数字转换器(ADC),关于其数据读写,从数据手册中获取的一些重要信息。1.时序        同步信号:上电SYNC引脚给出一个高低脉冲后挂载多个ADS1281后,同一个DREADY信号控制从机数据同步。2.连续读数据模式     ......
  • Eureka的生命周期管理:服务注册、续约与下线的完整流程解析
    Eureka的生命周期管理:服务注册、续约与下线的完整流程解析引言在分布式系统中,服务发现是微服务架构的核心问题之一。Eureka是Netflix开源的一个服务发现框架,它能够有效地管理微服务的生命周期,包括服务注册、续约和下线。这些功能确保了微服务之间能够进行高效、可靠的通信......
  • Infor CloudSuite软件二次开发:InforCloudSuite业务流程定制
    InforCloudSuite软件二次开发:InforCloudSuite业务流程定制InforCloudSuite简介InforCloudSuite平台概述InforCloudSuite是一个集成的企业资源规划(ERP)解决方案,专为特定行业设计,提供了一系列的云应用,旨在优化业务流程,提升运营效率。该平台融合了先进的技术,如人工智......
  • verilog代码与设计总结
    Verilog编码风格及设计建议相比于case语句,casez语句将z态看做不关心,casex语句将z态和x态看做不关心。并且所有case类型语句均没有优先级。锁存器是组合逻辑产生的,一般没有复位端,所以根据其所存特性,在上电的时候没法确定其初始状态,因此正常情况下要避免使用。组合逻辑环是一种......
  • java+vue计算机毕设山西工程技术学院任务流程管理系统【源码+开题+论文】
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景在当今信息化快速发展的时代,高校及各类组织机构的日常运营与管理日益依赖于高效的任务流程管理系统。山西工程技术学院作为一所培养工程技术人才的高......
  • 精美的Vue3流程插件,欢迎使用。
    dawn-flow插件是博主使用Vue3自主封装的流程插件(持续迭代中)。截图:dawn-flow采用拖拽方式创建流程。dawn-flow,开放了2个属性和2个事件,具体如下:属性NodeBar:节点栏目,默认true,开启显示栏目ToolBar:工具栏目,默认true,开启显示栏目事件  @currentNodeDoubleClick:节......
  • docker 修改容器内容后更新镜像的流程
    在Docker中,如果你修改了一个容器的内容并希望将这些更改保存为一个新的镜像,可以按照以下步骤进行:dockerversion:26.11.确保容器运行首先,确保你正在修改的容器是运行中的。如果容器已经停止,你需要启动它:dockerstart<container_id>2.进入容器并进行修改(如果尚未修改)你......