文章目录

1、概述

2、串口发送驱动

3、任意字节发送的实现方法

4、仿真

4.1、单字节仿真

4.2、双字节仿真

4.3、5字节仿真

5、实测

5.1、单字节实测

5.2、双字节实测

5.3、5字节实测


1、概述

在这篇文章中串口(UART)的FPGA实现(含源码工程),实现了基于FPGA的串口发送驱动。利用发送驱动可以实现 起始位1bit+数据位8bit+停止位1bit 共10bit的单字节传输。

但是在实际应用过程中又经常需要一次性发送多个字节的数据。比如,一次发送一个位宽为【39:0】的数据。诚然,可以直接更改此文中的串口发送驱动,使其变成 起始位1bit+数据位40bit+停止位1bit 共42bit的多字节传输。这种方法理论上是可行的,因为UART协议并没有规定你一次要发送、接收多个少bit的数据,既然能发送8个bit,那同样能发送40个bit。

但是很不幸,实际上基本行不通,因为通用的绝大部分上位机都不支持一次解析40bit的数据位(如果你自己写上位机就当我没说)。

所以只能想点其他办法,比如:写个逻辑,多次调用8bit即单字节的串口发送驱动,40bit的数据就调用5次,也就是切割成5个 起始位1bit+数据位8bit+停止位1bit共10bit的单字节 分别发送过去就可以了。


2、串口发送驱动

请参考:串口(UART)的FPGA实现(含源码工程),在此文详细介绍了串口的发送驱动。

以下代码可以实现 1bit+数据位8bit+停止位1bit 共10bit的单字节传输,无奇偶校验。


// *******************************************************************************************************
// ** 作者 : 孤独的单刀
// ** 邮箱 : zachary_wu93@163.com
// ** 博客 : https://blog.csdn.net/wuzhikaidetb
// ** 日期 : 2022/07/31
// ** 功能 : 1、基于FPGA的串口发送驱动模块;
//            2、可设置波特率BPS、主时钟CLK_FRE;
//            3、起始位1bit,数据位8bit,停止位1bit,无奇偶校验;
//            4、每发送1个字节后拉高uart_tx_done一个周期,可用于后续发送多字节模块。
// *******************************************************************************************************  module uart_tx
#(parameter integer BPS     = 9_600        ,   //发送波特率parameter    integer CLK_FRE = 50_000_000   //主时钟频率
)
(
//系统接口input             sys_clk         ,           //系统时钟input             sys_rst_n       ,           //系统复位,低电平有效
//用户接口  input   [7:0]   uart_tx_data    ,           //需要通过UART发送的数据,在uart_tx_en为高电平时有效input          uart_tx_en      ,           //发送有效,当其为高电平时,代表此时需要发送的数据有效
//UART发送    output  reg     uart_tx_done    ,           //成功发送1BYTE数据后拉高一个周期output  reg     uart_txd                    //UART发送数据线tx
);//param define
localparam  integer BPS_CNT  = CLK_FRE / BPS;  //根据波特率计算传输每个bit需要计数多个系统时钟
localparam  integer BITS_NUM = 10          ;   //发送格式确定需要发送的bit数,10bit = 1起始位 + 8数据位 + 1停止位//reg define
reg         tx_state            ;               //发送标志信号,拉高代表发送过程正在进行
reg [7:0]   uart_tx_data_reg    ;               //寄存要发送的数据
reg [31:0]  clk_cnt             ;               //计数器,用于计数发送一个bit数据所需要的时钟数
reg [3:0]   bit_cnt             ;               //bit计数器,标志当前发送了多少个bit//当发送使能信号到达时,寄存待发送的数据以免后续变化、丢失
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)uart_tx_data_reg <=8'd0;else if(uart_tx_en)                          //要发送有效的数据uart_tx_data_reg <= uart_tx_data;     //寄存需要发送的数据         else uart_tx_data_reg <= uart_tx_data_reg;
end     //当发送使能信号到达时,进入发送过程
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)tx_state <=1'b0; else if(uart_tx_en)                                             tx_state <= 1'b1;                      //发送信号有效则进入发送过程//发送完了最后一个数据则退出发送过程      else if((bit_cnt == BITS_NUM - 1'b1) && (clk_cnt == BPS_CNT - 1'b1))      tx_state <= 1'b0;                                                  else tx_state <= tx_state;
end//发送数据完毕后拉高发送完毕信号一个周期,指示一个字节发送完毕
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)uart_tx_done <=1'b0;//发送数据完毕后拉高发送完毕信号一个周期        else if((bit_cnt == BITS_NUM - 1'b1) && (clk_cnt == BPS_CNT - 1'b1))                                              uart_tx_done <=1'b1;                                       else uart_tx_done <=1'b0;
end//进入发送过程后,启动时钟计数器与发送个数bit计数器
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)beginclk_cnt <= 32'd0;bit_cnt <= 4'd0;endelse if(tx_state) begin                                        //在发送状态if(clk_cnt < BPS_CNT - 1'd1)begin                        //一个bit数据没有发送完clk_cnt <= clk_cnt + 1'b1;                          //时钟计数器+1bit_cnt <= bit_cnt;                                   //bit计数器不变end                   else begin                                              //一个bit数据发送完了   clk_cnt <= 32'd0;                                  //清空时钟计数器,重新开始计时bit_cnt <= bit_cnt+1'b1;                           //bit计数器+1,表示发送完了一个bit的数据end                    end                 else begin                                                  //不在发送状态clk_cnt <= 32'd0;                                      //清零bit_cnt <= 4'd0;                                       //清零end
end//根据发送数据计数器来给uart发送端口赋值
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)uart_txd <= 1'b1;                                        //默认为高状态,空闲状态else if(tx_state)                                           //处于发送状态case(bit_cnt)                                           //数据发送从低位到高位4'd0: uart_txd <= 1'b0;                               //起始位,拉低4'd1: uart_txd <= uart_tx_data_reg[0];              //发送最低位数据4'd2: uart_txd <= uart_tx_data_reg[1];                //4'd3: uart_txd <= uart_tx_data_reg[2];               //4'd4: uart_txd <= uart_tx_data_reg[3];               //4'd5: uart_txd <= uart_tx_data_reg[4];               //4'd6: uart_txd <= uart_tx_data_reg[5];               //4'd7: uart_txd <= uart_tx_data_reg[6];               //4'd8: uart_txd <= uart_tx_data_reg[7];               //发送最高位数据4'd9: uart_txd <= 1'b1;                              //终止位,拉高default:uart_txd <= 1'b1;           endcase         else                                                        //不处于发送状态uart_txd <= 1'b1;                                     //默认为高状态,空闲状态
endendmodule 

3、任意字节发送的实现方法

串口发送驱动模块的对外端口如下:

其中uart_tx_done信号是关键。当一个字节的数据被按约定的串口协议发送出去后,该信号就会拉高一个时钟周期,表示一次传输结束。

可以在发送完一个字节后,将该信号作为发送下一个字节的使能信号,直到所有的字节都被发送完毕。

但是有一个需要注意的问题:第一个字节的发送使能是无法参考信号 uart_tx_done,那么第一个信号何时发送?同样的,在任意字节发送模块设计一个对外信号:uart_bytes_en,只有当该信号有效时才发起一次传输,同时该信号还可以作为第一个字节的发送使能信号,如下:

总结一下,多字节的发送逻辑:

  • 1、用户传递多字节发送使能+多字节数据
  • 2、根据多字节发送使能发送最低字节(最低8bit),作为第一次发送的单字节
  • 3、根据单字节发送完毕指示信号,开始发送第二个单字节,即多字节的次低byte
  • 4、所有单字节均被发送完毕后,拉高uart_bytes_done,表示一次多字节发送结束

完整代码如下:


// *******************************************************************************************************************
// ** 作者 : 孤独的单刀
// ** 邮箱 : zachary_wu93@163.com
// ** 博客 : https://blog.csdn.net/wuzhikaidetb
// ** 日期 : 2022/07/31
// ** 功能 : 1、基于FPGA的串口多字节发送模块;
//            2、可设置一次发送的字节数、波特率BPS、主时钟CLK_FRE;
//            3、UART协议设置为起始位1bit,数据位8bit,停止位1bit,无奇偶校验(不可在端口更改,只能更改发送驱动源码);
//            4、每发送1次多字节后拉高指示信号一个周期,指示一次多字节发送结束;
//            5、数据发送顺序,先发送低字节、再发送高字节。如:发送16’h12_34,先发送单字节8'h34,再发送单字节8'h12。
// *******************************************************************************************************************  module uart_bytes_tx
#(parameter integer BYTES    = 4           ,               //发送字节数,单字节8bitparameter integer BPS      = 9_600       ,               //发送波特率parameter    integer CLK_FRE  = 50_000_000                  //输入时钟频率
)
(
//系统接口input                             sys_clk         ,           //系统时钟input                             sys_rst_n       ,           //系统复位,低电平有效
//用户接口  input   [(BYTES * 8 - 1):0]     uart_bytes_data ,           //需要通过UART发送的多字节数据,在uart_bytes_en为高电平时有效input                            uart_bytes_en   ,           //发送有效,当其为高电平时,代表此时需要发送的数据有效
//UART发送    output                          uart_bytes_done ,           //成功发送完所有字节数据后拉高1个时钟周期output                            uart_txd                    //UART发送数据线tx
);//reg define
reg [(BYTES*8-1):0]     uart_bytes_data_reg;                    //寄存接收到的多字节数据
reg                     work_en;                                //高电平表示处于发送状态,低电平表示空闲状态
reg [9:0]               byte_cnt;                               //发送的字节个数计数(因为懒直接用10bit计数,最大可以表示1024BYTE,大概率不会溢出)
reg [7:0]               uart_sing_data;                         //拆解的要发送的单个字节数据
reg                     uart_sing_en;                           //要发送的单个字节数据发送使能
reg                     uart_bytes_done_reg;                    //所有字节发送完毕打拍
reg                     uart_sing_done_reg;                     //单个字节数据发送完毕打拍//wire define
wire                    uart_sing_done;                         //单个字节发送完成标志信号//对端口赋值
assign uart_bytes_done = uart_bytes_done_reg;//当发送使能信号到达时,寄存待发送的多字节数据以免后续变化、丢失
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)uart_bytes_data_reg <= 0;else if(uart_bytes_en && ~work_en)                           //要发送有效的数据,且并未处于发送状态uart_bytes_data_reg <= uart_bytes_data;                  //寄存需要发送的数据         else if(uart_sing_done)                                     uart_bytes_data_reg <= uart_bytes_data_reg >> 8;      //发送完一个数据后,把多字节数据右移8bitelse uart_bytes_data_reg <= uart_bytes_data_reg;
end //当发送使能信号到达时,进入工作状态
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)work_en <= 1'b0;else if(uart_bytes_en && ~work_en)                           //要发送有效的数据且未处于工作状态work_en <= 1'b1;                                     //进入发送状态            else if(uart_sing_done && byte_cnt == BYTES - 1)          //发送完了最后一个字节的数据work_en <= 1'b0;                                        //发送完毕,退出工作状态    else        work_en <= work_en;
end         //在工作状态对发送的数据个数进行计数
always @(posedge sys_clk or negedge sys_rst_n)begin        if(!sys_rst_n)      byte_cnt <= 0;      else if(work_en)begin                                       //处于发送状态则需要对发送的字节个数计数if(uart_sing_done && byte_cnt == BYTES - 1)              //计数到了最大值则清零byte_cnt <= 0;                                      else if(uart_sing_done)                                 //发送完一个单字节则计数器+1byte_cnt <= byte_cnt + 1'b1;                     else        byte_cnt <= byte_cnt;           end     else                                                        //不处于发送状态则清零byte_cnt <= 0;
end//打拍凑时序·~·
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)uart_sing_done_reg <= 0;else uart_sing_done_reg <= uart_sing_done;
end//发送单个字节的数据
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)uart_sing_data <= 8'd0;else if(uart_bytes_en && ~work_en)                        //进入工作状态后马上发送第一个数据uart_sing_data <= uart_bytes_data[7:0];               //发送最低字节else if(uart_sing_done_reg)                             //发送完一个字节则发送另一个字节uart_sing_data <= uart_bytes_data_reg[7:0];            //先右移8bit,然后取低8bitelseuart_sing_data <= uart_sing_data;                  //保持稳定
end//发送单个字节的数据使能
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)uart_sing_en <= 1'b0;else if(uart_bytes_en && ~work_en)                      //进入工作状态后马上发送第一个数据uart_sing_en <= 1'b1;        else if(uart_sing_done_reg && work_en)                  //发送完一个字节则发送另一个字节uart_sing_en <= 1'b1;     else        uart_sing_en <= 1'b0;                              //其他时候则为0
end//所有数据发送完毕
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)uart_bytes_done_reg <= 1'b0;else if(uart_sing_done && byte_cnt == BYTES - 1)uart_bytes_done_reg <= 1'b1;else uart_bytes_done_reg <= 1'b0;
end//例化发送驱动模块
uart_tx #(.BPS          (BPS            ),      .CLK_FRE        (CLK_FRE        )
)
uart_tx_inst(   .sys_clk        (sys_clk        ),          .sys_rst_n      (sys_rst_n      ),.uart_tx_data (uart_sing_data ),          .uart_tx_en     (uart_sing_en   ),.uart_tx_done (uart_sing_done ),.uart_txd     (uart_txd       )
);endmodule 

4、仿真

使用该模块发送数据3次,观测发送结果是否符合UART协议要求。分别使用单字节(8位)、双字节(16位)、5字节(40位)进行测试。


4.1、单字节仿真

TB如下:

`timescale 1ns/1ns  //定义时间刻度module tb_uart_bytes_tx();localparam    integer BYTES    = 1           ;       //一次发送的字节个数localparam   integer BPS      = 230400      ;       //波特率
localparam  integer CLK_FRE  = 50_000_000  ;       //系统频率50Mreg                        sys_clk         ;       //系统时钟
reg                         sys_rst_n       ;       //系统复位,低电平有效
reg [(BYTES * 8 - 1):0]     uart_bytes_data ;       //需要通过UART发送的多字节数据,在uart_bytes_en为高电平时有效
reg                         uart_bytes_en   ;       //发送有效,当其为高电平时,代表此时需要发送的数据有效
wire                        uart_bytes_done ;       //成功发送完所有BYTE数据后拉高1个时钟周期
wire                        uart_txd        ;       //UART发送数据线initial begin    sys_clk <=1'b0;    sys_rst_n <=1'b0;uart_bytes_en <=1'b0;uart_bytes_data <= 0;#80                                            //系统开始工作sys_rst_n <=1'b1;
//*******************************************************************************
//第1次发送随机数据#90                                              //发送1次随机的多字节数据uart_bytes_en <=1'b1;    uart_bytes_data <= {$random};                   //生成随机数据#20 uart_bytes_en <=1'b0;  wait(uart_bytes_done);                              //等待其发送完
//*******************************************************************************
//*******************************************************************************
//第2次发送随机数据#20 uart_bytes_en <=1'b1;   uart_bytes_data <= {$random};                   //发送1次随机的多字节数据#20 uart_bytes_en <=1'b0;    wait(uart_bytes_done);                              //等待其发送完
//*******************************************************************************
//第3次发送随机数据 #20 uart_bytes_en <=1'b1;  uart_bytes_data <= {$random};                   //发送1次随机的多字节数据#20 uart_bytes_en <=1'b0;    wait(uart_bytes_done);                              //等待其发送完
//*******************************************************************************       #1000 $finish();            //结束仿真
endalways #10 sys_clk=~sys_clk;    //设置主时钟,20ns,50M//例化多字节发送模块
uart_bytes_tx #(.BYTES              (BYTES              ),.BPS              (BPS                ),      .CLK_FRE            (CLK_FRE            )
)
uart_bytes_tx_inst(         .sys_clk            (sys_clk            ),          .sys_rst_n          (sys_rst_n          ),.uart_bytes_data  (uart_bytes_data    ),          .uart_bytes_en      (uart_bytes_en      ),.uart_bytes_done  (uart_bytes_done    ),.uart_txd         (uart_txd           )
);
endmodule 

仿真结果如下:

  • 该模块调用了3次,分别发送数据8'h24,8'h81,8'h09
  • 串口发送驱动发送了同样的3个数据8'h24,8'h81,8'h09
  • 以第一次发送8'h24为例,此时串口发送线TX上的数据分别为0(起始位) --0--0--1--0--0--1--0--0--1(停止位),根据数据低位在前,高位在后的原则,即8'b00100100,也就是8'h24

4.2、双字节仿真

TB基本不用修改,仅仅把BYTES这个参数改成2就行。仿真结果如下:

  • 该模块调用了3次,分别发送数据16'h3524,16'h5e81,16'hd609
  • 串口发送驱动发送了6个数据8'h24,8'h35,8'h81,8'h5e,8'h09,8'hd6
  • 根据先发低字节后发高字节的原则,将3个双字节拆成了6个单字节分别发送

4.3、5字节仿真

TB基本不用修改,仅仅把BYTES这个参数改成5就行。仿真结果如下:

您照着上面的逻辑看看就行,我就不啰嗦了。


5、实测

下板实测的话,需要写个顶层模块来例化这个任意字节的串口发送模块。在顶层模块,设置成每1s拉高一次发送信号,同时将要发送的数据+1。


5.1、单字节实测

顶层模块如下:


// *******************************************************************************************************************
// ** 作者 : 孤独的单刀
// ** 邮箱 : zachary_wu93@163.com
// ** 博客 : https://blog.csdn.net/wuzhikaidetb
// ** 日期 : 2022/07/31
// ** 功能 : 1、对基于FPGA的串口多字节发送模块进行测试的模块;
//            2、设置好一次发送的字节数、波特率、主时钟频率;
//            3、UART协议设置为起始位1bit,数据位8bit,停止位1bit,无奇偶校验(不可在端口更改,只能更改发送驱动源码);
//            4、数据发送顺序,先发送低字节、再发送高字节。如:发送16’h12_34,先发送单字节8'h34,再发送单字节8'h12。
// *******************************************************************************************************************  module uart_bytes_tx_test(
//系统接口input                 sys_clk         ,                   //主时钟input              sys_rst_n       ,                   //低电平有效的复位信号
//UART发送线   output              uart_txd                            //UART发送线
);localparam    integer     BYTES    = 1           ;           //发送的字节数,单字节8bit
localparam  integer     BPS      = 115200      ;           //发送波特率
localparam  integer     CLK_FRE  = 50_000_000  ;           //输入时钟频率
localparam  integer     CNT_MAX  = 50_000_000      ;           //发送时间间隔,1秒reg       [31:0]          cnt_time;
reg                     uart_bytes_en;                      //发送使能,当其为高电平时,代表此时需要发送数据
reg     [BYTES*8-1 :0]  uart_bytes_data;                    //需要通过UART发送的数据,在uart_bytes_en为高电平时有效//1s计数模块,每隔1s发送一个数据和拉高发送使能信号一次,数据从初始值开始递增1
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)begincnt_time <= 'd0;uart_bytes_en <= 1'd0;uart_bytes_data <= 'h12;                            //初始数据endelse if(cnt_time == (CNT_MAX - 1'b1))begincnt_time <= 'd0;uart_bytes_en <= 1'd1;                              //拉高发送使能uart_bytes_data <= uart_bytes_data + 1'd1;            //发送数据累加1endelse begincnt_time <= cnt_time + 1'd1;uart_bytes_en <= 1'd0;uart_bytes_data <= uart_bytes_data; end
end//例化串口多字节发送模块
uart_bytes_tx
#(.BYTES                (BYTES              ),              .BPS                (BPS                ),              .CLK_FRE            (CLK_FRE            )
)
uart_bytes_tx_inst
(       .sys_clk            (sys_clk            ),          .sys_rst_n          (sys_rst_n          ),                                                  .uart_bytes_data    (uart_bytes_data    ),          .uart_bytes_en      (uart_bytes_en      ),                                             .uart_bytes_done (                   ),          .uart_txd           (uart_txd           )
);endmodule

初始值设定为8'h13,这样上位机每1s就可以接收一个数据,其值分别为8'h13、8'h14、8'h15、8'h16、8'h17·····,上位机接收到的数据如下:

测试结果与预期一致。


5.2、双字节实测

顶层模块如下:


// *******************************************************************************************************************
// ** 作者 : 孤独的单刀
// ** 邮箱 : zachary_wu93@163.com
// ** 博客 : https://blog.csdn.net/wuzhikaidetb
// ** 日期 : 2022/07/31
// ** 功能 : 1、对基于FPGA的串口多字节发送模块进行测试的模块;
//            2、设置好一次发送的字节数、波特率、主时钟频率;
//            3、UART协议设置为起始位1bit,数据位8bit,停止位1bit,无奇偶校验(不可在端口更改,只能更改发送驱动源码);
//            4、数据发送顺序,先发送低字节、再发送高字节。如:发送16’h12_34,先发送单字节8'h34,再发送单字节8'h12。
// *******************************************************************************************************************  module uart_bytes_tx_test(
//系统接口input                 sys_clk         ,                   //主时钟input              sys_rst_n       ,                   //低电平有效的复位信号
//UART发送线   output              uart_txd                            //UART发送线
);localparam    integer     BYTES    = 2           ;           //发送的字节数,单字节8bit
localparam  integer     BPS      = 115200      ;           //发送波特率
localparam  integer     CLK_FRE  = 50_000_000  ;           //输入时钟频率
localparam  integer     CNT_MAX  = 50_000_000      ;           //发送时间间隔,1秒reg       [31:0]          cnt_time;
reg                     uart_bytes_en;                      //发送使能,当其为高电平时,代表此时需要发送数据
reg     [BYTES*8-1 :0]  uart_bytes_data;                    //需要通过UART发送的数据,在uart_bytes_en为高电平时有效//1s计数模块,每隔1s发送一个数据和拉高发送使能信号一次,数据从初始值开始递增1
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)begincnt_time <= 'd0;uart_bytes_en <= 1'd0;uart_bytes_data <= 'h3412;                          //初始数据endelse if(cnt_time == (CNT_MAX - 1'b1))begincnt_time <= 'd0;uart_bytes_en <= 1'd1;                              //拉高发送使能uart_bytes_data <= uart_bytes_data + 1'd1;            //发送数据累加1endelse begincnt_time <= cnt_time + 1'd1;uart_bytes_en <= 1'd0;uart_bytes_data <= uart_bytes_data; end
end//例化串口多字节发送模块
uart_bytes_tx
#(.BYTES                (BYTES              ),              .BPS                (BPS                ),              .CLK_FRE            (CLK_FRE            )
)
uart_bytes_tx_inst
(       .sys_clk            (sys_clk            ),          .sys_rst_n          (sys_rst_n          ),                                                  .uart_bytes_data    (uart_bytes_data    ),          .uart_bytes_en      (uart_bytes_en      ),                                             .uart_bytes_done (                   ),          .uart_txd           (uart_txd           )
);endmodule

初始值设定为16'h3413,这样上位机每1s就可以接收一个数据,其值分别为8'h13、8'h34、8'h14、8'h34、8'h15、8'h34·····,上位机接收到的数据如下:

测试结果与预期一致。


5.3、5字节实测

顶层模块如下:


// *******************************************************************************************************************
// ** 作者 : 孤独的单刀
// ** 邮箱 : zachary_wu93@163.com
// ** 博客 : https://blog.csdn.net/wuzhikaidetb
// ** 日期 : 2022/07/31
// ** 功能 : 1、对基于FPGA的串口多字节发送模块进行测试的模块;
//            2、设置好一次发送的字节数、波特率、主时钟频率;
//            3、UART协议设置为起始位1bit,数据位8bit,停止位1bit,无奇偶校验(不可在端口更改,只能更改发送驱动源码);
//            4、数据发送顺序,先发送低字节、再发送高字节。如:发送16’h12_34,先发送单字节8'h34,再发送单字节8'h12。
// *******************************************************************************************************************  module uart_bytes_tx_test(
//系统接口input                 sys_clk         ,                   //主时钟input              sys_rst_n       ,                   //低电平有效的复位信号
//UART发送线   output              uart_txd                            //UART发送线
);localparam    integer     BYTES    = 5           ;           //发送的字节数,单字节8bit
localparam  integer     BPS      = 115200      ;           //发送波特率
localparam  integer     CLK_FRE  = 50_000_000  ;           //输入时钟频率
localparam  integer     CNT_MAX  = 50_000_000      ;           //发送时间间隔,1秒reg       [31:0]          cnt_time;
reg                     uart_bytes_en;                      //发送使能,当其为高电平时,代表此时需要发送数据
reg     [BYTES*8-1 :0]  uart_bytes_data;                    //需要通过UART发送的数据,在uart_bytes_en为高电平时有效//1s计数模块,每隔1s发送一个数据和拉高发送使能信号一次,数据从初始值开始递增1
always @(posedge sys_clk or negedge sys_rst_n)beginif(!sys_rst_n)begincnt_time <= 'd0;uart_bytes_en <= 1'd0;uart_bytes_data <= 40'h9a_78_56_34_12;              //初始数据endelse if(cnt_time == (CNT_MAX - 1'b1))begincnt_time <= 'd0;uart_bytes_en <= 1'd1;                              //拉高发送使能uart_bytes_data <= uart_bytes_data + 1'd1;            //发送数据累加1endelse begincnt_time <= cnt_time + 1'd1;uart_bytes_en <= 1'd0;uart_bytes_data <= uart_bytes_data; end
end//例化串口多字节发送模块
uart_bytes_tx
#(.BYTES                (BYTES              ),              .BPS                (BPS                ),              .CLK_FRE            (CLK_FRE            )
)
uart_bytes_tx_inst
(       .sys_clk            (sys_clk            ),          .sys_rst_n          (sys_rst_n          ),                                                  .uart_bytes_data    (uart_bytes_data    ),          .uart_bytes_en      (uart_bytes_en      ),                                             .uart_bytes_done (                   ),          .uart_txd           (uart_txd           )
);endmodule

初始值设定为40'h9a78563413,这样上位机每1s就可以接收一个数据,其值分别为8'h13、8'h34、8'h56、8'h78、8'h9a、8'h14、8'h34、8'h56、8'h78、8'h9a、·····,上位机接收到的数据如下:

测试结果与预期一致。

工程源码点这里下载(提取码:nk20)