• 基于FPGA的任意字节数(单字节、多字节)的串口(UART)发送(含源码工程)


    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的单字节传输,无奇偶校验。

    1. // *******************************************************************************************************
    2. // ** 作者 : 孤独的单刀
    3. // ** 邮箱 : zachary_wu93@163.com
    4. // ** 博客 : https://blog.csdn.net/wuzhikaidetb
    5. // ** 日期 : 2022/07/31
    6. // ** 功能 : 1、基于FPGA的串口发送驱动模块;
    7. // 2、可设置波特率BPS、主时钟CLK_FRE;
    8. // 3、起始位1bit,数据位8bit,停止位1bit,无奇偶校验;
    9. // 4、每发送1个字节后拉高uart_tx_done一个周期,可用于后续发送多字节模块。
    10. // *******************************************************************************************************
    11. module uart_tx
    12. #(
    13. parameter integer BPS = 9_600 , //发送波特率
    14. parameter integer CLK_FRE = 50_000_000 //主时钟频率
    15. )
    16. (
    17. //系统接口
    18. input sys_clk , //系统时钟
    19. input sys_rst_n , //系统复位,低电平有效
    20. //用户接口
    21. input [7:0] uart_tx_data , //需要通过UART发送的数据,在uart_tx_en为高电平时有效
    22. input uart_tx_en , //发送有效,当其为高电平时,代表此时需要发送的数据有效
    23. //UART发送
    24. output reg uart_tx_done , //成功发送1BYTE数据后拉高一个周期
    25. output reg uart_txd //UART发送数据线tx
    26. );
    27. //param define
    28. localparam integer BPS_CNT = CLK_FRE / BPS; //根据波特率计算传输每个bit需要计数多个系统时钟
    29. localparam integer BITS_NUM = 10 ; //发送格式确定需要发送的bit数,10bit = 1起始位 + 8数据位 + 1停止位
    30. //reg define
    31. reg tx_state ; //发送标志信号,拉高代表发送过程正在进行
    32. reg [7:0] uart_tx_data_reg ; //寄存要发送的数据
    33. reg [31:0] clk_cnt ; //计数器,用于计数发送一个bit数据所需要的时钟数
    34. reg [3:0] bit_cnt ; //bit计数器,标志当前发送了多少个bit
    35. //当发送使能信号到达时,寄存待发送的数据以免后续变化、丢失
    36. always @(posedge sys_clk or negedge sys_rst_n)begin
    37. if(!sys_rst_n)
    38. uart_tx_data_reg <=8'd0;
    39. else if(uart_tx_en) //要发送有效的数据
    40. uart_tx_data_reg <= uart_tx_data; //寄存需要发送的数据
    41. else
    42. uart_tx_data_reg <= uart_tx_data_reg;
    43. end
    44. //当发送使能信号到达时,进入发送过程
    45. always @(posedge sys_clk or negedge sys_rst_n)begin
    46. if(!sys_rst_n)
    47. tx_state <=1'b0;
    48. else if(uart_tx_en)
    49. tx_state <= 1'b1; //发送信号有效则进入发送过程
    50. //发送完了最后一个数据则退出发送过程
    51. else if((bit_cnt == BITS_NUM - 1'b1) && (clk_cnt == BPS_CNT - 1'b1))
    52. tx_state <= 1'b0;
    53. else
    54. tx_state <= tx_state;
    55. end
    56. //发送数据完毕后拉高发送完毕信号一个周期,指示一个字节发送完毕
    57. always @(posedge sys_clk or negedge sys_rst_n)begin
    58. if(!sys_rst_n)
    59. uart_tx_done <=1'b0;
    60. //发送数据完毕后拉高发送完毕信号一个周期
    61. else if((bit_cnt == BITS_NUM - 1'b1) && (clk_cnt == BPS_CNT - 1'b1))
    62. uart_tx_done <=1'b1;
    63. else
    64. uart_tx_done <=1'b0;
    65. end
    66. //进入发送过程后,启动时钟计数器与发送个数bit计数器
    67. always @(posedge sys_clk or negedge sys_rst_n)begin
    68. if(!sys_rst_n)begin
    69. clk_cnt <= 32'd0;
    70. bit_cnt <= 4'd0;
    71. end
    72. else if(tx_state) begin //在发送状态
    73. if(clk_cnt < BPS_CNT - 1'd1)begin //一个bit数据没有发送完
    74. clk_cnt <= clk_cnt + 1'b1; //时钟计数器+1
    75. bit_cnt <= bit_cnt; //bit计数器不变
    76. end
    77. else begin //一个bit数据发送完了
    78. clk_cnt <= 32'd0; //清空时钟计数器,重新开始计时
    79. bit_cnt <= bit_cnt+1'b1; //bit计数器+1,表示发送完了一个bit的数据
    80. end
    81. end
    82. else begin //不在发送状态
    83. clk_cnt <= 32'd0; //清零
    84. bit_cnt <= 4'd0; //清零
    85. end
    86. end
    87. //根据发送数据计数器来给uart发送端口赋值
    88. always @(posedge sys_clk or negedge sys_rst_n)begin
    89. if(!sys_rst_n)
    90. uart_txd <= 1'b1; //默认为高状态,空闲状态
    91. else if(tx_state) //处于发送状态
    92. case(bit_cnt) //数据发送从低位到高位
    93. 4'd0: uart_txd <= 1'b0; //起始位,拉低
    94. 4'd1: uart_txd <= uart_tx_data_reg[0]; //发送最低位数据
    95. 4'd2: uart_txd <= uart_tx_data_reg[1]; //
    96. 4'd3: uart_txd <= uart_tx_data_reg[2]; //
    97. 4'd4: uart_txd <= uart_tx_data_reg[3]; //
    98. 4'd5: uart_txd <= uart_tx_data_reg[4]; //
    99. 4'd6: uart_txd <= uart_tx_data_reg[5]; //
    100. 4'd7: uart_txd <= uart_tx_data_reg[6]; //
    101. 4'd8: uart_txd <= uart_tx_data_reg[7]; //发送最高位数据
    102. 4'd9: uart_txd <= 1'b1; //终止位,拉高
    103. default:uart_txd <= 1'b1;
    104. endcase
    105. else //不处于发送状态
    106. uart_txd <= 1'b1; //默认为高状态,空闲状态
    107. end
    108. endmodule

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

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

           

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

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

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

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

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

            

            完整代码如下:

    1. // *******************************************************************************************************************
    2. // ** 作者 : 孤独的单刀
    3. // ** 邮箱 : zachary_wu93@163.com
    4. // ** 博客 : https://blog.csdn.net/wuzhikaidetb
    5. // ** 日期 : 2022/07/31
    6. // ** 功能 : 1、基于FPGA的串口多字节发送模块;
    7. // 2、可设置一次发送的字节数、波特率BPS、主时钟CLK_FRE;
    8. // 3、UART协议设置为起始位1bit,数据位8bit,停止位1bit,无奇偶校验(不可在端口更改,只能更改发送驱动源码);
    9. // 4、每发送1次多字节后拉高指示信号一个周期,指示一次多字节发送结束;
    10. // 5、数据发送顺序,先发送低字节、再发送高字节。如:发送16’h12_34,先发送单字节8'h34,再发送单字节8'h12。
    11. // *******************************************************************************************************************
    12. module uart_bytes_tx
    13. #(
    14. parameter integer BYTES = 4 , //发送字节数,单字节8bit
    15. parameter integer BPS = 9_600 , //发送波特率
    16. parameter integer CLK_FRE = 50_000_000 //输入时钟频率
    17. )
    18. (
    19. //系统接口
    20. input sys_clk , //系统时钟
    21. input sys_rst_n , //系统复位,低电平有效
    22. //用户接口
    23. input [(BYTES * 8 - 1):0] uart_bytes_data , //需要通过UART发送的多字节数据,在uart_bytes_en为高电平时有效
    24. input uart_bytes_en , //发送有效,当其为高电平时,代表此时需要发送的数据有效
    25. //UART发送
    26. output uart_bytes_done , //成功发送完所有字节数据后拉高1个时钟周期
    27. output uart_txd //UART发送数据线tx
    28. );
    29. //reg define
    30. reg [(BYTES*8-1):0] uart_bytes_data_reg; //寄存接收到的多字节数据
    31. reg work_en; //高电平表示处于发送状态,低电平表示空闲状态
    32. reg [9:0] byte_cnt; //发送的字节个数计数(因为懒直接用10bit计数,最大可以表示1024BYTE,大概率不会溢出)
    33. reg [7:0] uart_sing_data; //拆解的要发送的单个字节数据
    34. reg uart_sing_en; //要发送的单个字节数据发送使能
    35. reg uart_bytes_done_reg; //所有字节发送完毕打拍
    36. reg uart_sing_done_reg; //单个字节数据发送完毕打拍
    37. //wire define
    38. wire uart_sing_done; //单个字节发送完成标志信号
    39. //对端口赋值
    40. assign uart_bytes_done = uart_bytes_done_reg;
    41. //当发送使能信号到达时,寄存待发送的多字节数据以免后续变化、丢失
    42. always @(posedge sys_clk or negedge sys_rst_n)begin
    43. if(!sys_rst_n)
    44. uart_bytes_data_reg <= 0;
    45. else if(uart_bytes_en && ~work_en) //要发送有效的数据,且并未处于发送状态
    46. uart_bytes_data_reg <= uart_bytes_data; //寄存需要发送的数据
    47. else if(uart_sing_done)
    48. uart_bytes_data_reg <= uart_bytes_data_reg >> 8; //发送完一个数据后,把多字节数据右移8bit
    49. else
    50. uart_bytes_data_reg <= uart_bytes_data_reg;
    51. end
    52. //当发送使能信号到达时,进入工作状态
    53. always @(posedge sys_clk or negedge sys_rst_n)begin
    54. if(!sys_rst_n)
    55. work_en <= 1'b0;
    56. else if(uart_bytes_en && ~work_en) //要发送有效的数据且未处于工作状态
    57. work_en <= 1'b1; //进入发送状态
    58. else if(uart_sing_done && byte_cnt == BYTES - 1) //发送完了最后一个字节的数据
    59. work_en <= 1'b0; //发送完毕,退出工作状态
    60. else
    61. work_en <= work_en;
    62. end
    63. //在工作状态对发送的数据个数进行计数
    64. always @(posedge sys_clk or negedge sys_rst_n)begin
    65. if(!sys_rst_n)
    66. byte_cnt <= 0;
    67. else if(work_en)begin //处于发送状态则需要对发送的字节个数计数
    68. if(uart_sing_done && byte_cnt == BYTES - 1) //计数到了最大值则清零
    69. byte_cnt <= 0;
    70. else if(uart_sing_done) //发送完一个单字节则计数器+1
    71. byte_cnt <= byte_cnt + 1'b1;
    72. else
    73. byte_cnt <= byte_cnt;
    74. end
    75. else //不处于发送状态则清零
    76. byte_cnt <= 0;
    77. end
    78. //打拍凑时序·~·
    79. always @(posedge sys_clk or negedge sys_rst_n)begin
    80. if(!sys_rst_n)
    81. uart_sing_done_reg <= 0;
    82. else
    83. uart_sing_done_reg <= uart_sing_done;
    84. end
    85. //发送单个字节的数据
    86. always @(posedge sys_clk or negedge sys_rst_n)begin
    87. if(!sys_rst_n)
    88. uart_sing_data <= 8'd0;
    89. else if(uart_bytes_en && ~work_en) //进入工作状态后马上发送第一个数据
    90. uart_sing_data <= uart_bytes_data[7:0]; //发送最低字节
    91. else if(uart_sing_done_reg) //发送完一个字节则发送另一个字节
    92. uart_sing_data <= uart_bytes_data_reg[7:0]; //先右移8bit,然后取低8bit
    93. else
    94. uart_sing_data <= uart_sing_data; //保持稳定
    95. end
    96. //发送单个字节的数据使能
    97. always @(posedge sys_clk or negedge sys_rst_n)begin
    98. if(!sys_rst_n)
    99. uart_sing_en <= 1'b0;
    100. else if(uart_bytes_en && ~work_en) //进入工作状态后马上发送第一个数据
    101. uart_sing_en <= 1'b1;
    102. else if(uart_sing_done_reg && work_en) //发送完一个字节则发送另一个字节
    103. uart_sing_en <= 1'b1;
    104. else
    105. uart_sing_en <= 1'b0; //其他时候则为0
    106. end
    107. //所有数据发送完毕
    108. always @(posedge sys_clk or negedge sys_rst_n)begin
    109. if(!sys_rst_n)
    110. uart_bytes_done_reg <= 1'b0;
    111. else if(uart_sing_done && byte_cnt == BYTES - 1)
    112. uart_bytes_done_reg <= 1'b1;
    113. else
    114. uart_bytes_done_reg <= 1'b0;
    115. end
    116. //例化发送驱动模块
    117. uart_tx #(
    118. .BPS (BPS ),
    119. .CLK_FRE (CLK_FRE )
    120. )
    121. uart_tx_inst(
    122. .sys_clk (sys_clk ),
    123. .sys_rst_n (sys_rst_n ),
    124. .uart_tx_data (uart_sing_data ),
    125. .uart_tx_en (uart_sing_en ),
    126. .uart_tx_done (uart_sing_done ),
    127. .uart_txd (uart_txd )
    128. );
    129. endmodule

    4、仿真

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


    4.1、单字节仿真

            TB如下:

    1. `timescale 1ns/1ns //定义时间刻度
    2. module tb_uart_bytes_tx();
    3. localparam integer BYTES = 1 ; //一次发送的字节个数
    4. localparam integer BPS = 230400 ; //波特率
    5. localparam integer CLK_FRE = 50_000_000 ; //系统频率50M
    6. reg sys_clk ; //系统时钟
    7. reg sys_rst_n ; //系统复位,低电平有效
    8. reg [(BYTES * 8 - 1):0] uart_bytes_data ; //需要通过UART发送的多字节数据,在uart_bytes_en为高电平时有效
    9. reg uart_bytes_en ; //发送有效,当其为高电平时,代表此时需要发送的数据有效
    10. wire uart_bytes_done ; //成功发送完所有BYTE数据后拉高1个时钟周期
    11. wire uart_txd ; //UART发送数据线
    12. initial begin
    13. sys_clk <=1'b0;
    14. sys_rst_n <=1'b0;
    15. uart_bytes_en <=1'b0;
    16. uart_bytes_data <= 0;
    17. #80 //系统开始工作
    18. sys_rst_n <=1'b1;
    19. //*******************************************************************************
    20. //第1次发送随机数据
    21. #90 //发送1次随机的多字节数据
    22. uart_bytes_en <=1'b1;
    23. uart_bytes_data <= {$random}; //生成随机数据
    24. #20
    25. uart_bytes_en <=1'b0;
    26. wait(uart_bytes_done); //等待其发送完
    27. //*******************************************************************************
    28. //*******************************************************************************
    29. //第2次发送随机数据
    30. #20
    31. uart_bytes_en <=1'b1;
    32. uart_bytes_data <= {$random}; //发送1次随机的多字节数据
    33. #20
    34. uart_bytes_en <=1'b0;
    35. wait(uart_bytes_done); //等待其发送完
    36. //*******************************************************************************
    37. //第3次发送随机数据
    38. #20
    39. uart_bytes_en <=1'b1;
    40. uart_bytes_data <= {$random}; //发送1次随机的多字节数据
    41. #20
    42. uart_bytes_en <=1'b0;
    43. wait(uart_bytes_done); //等待其发送完
    44. //*******************************************************************************
    45. #1000 $finish(); //结束仿真
    46. end
    47. always #10 sys_clk=~sys_clk; //设置主时钟,20ns,50M
    48. //例化多字节发送模块
    49. uart_bytes_tx #(
    50. .BYTES (BYTES ),
    51. .BPS (BPS ),
    52. .CLK_FRE (CLK_FRE )
    53. )
    54. uart_bytes_tx_inst(
    55. .sys_clk (sys_clk ),
    56. .sys_rst_n (sys_rst_n ),
    57. .uart_bytes_data (uart_bytes_data ),
    58. .uart_bytes_en (uart_bytes_en ),
    59. .uart_bytes_done (uart_bytes_done ),
    60. .uart_txd (uart_txd )
    61. );
    62. 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'h358'h81,8'h5e8'h09,8'hd6
    • 根据先发低字节后发高字节的原则,将3个双字节拆成了6个单字节分别发送

    4.3、5字节仿真

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

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


    5、实测

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


    5.1、单字节实测

            顶层模块如下:

    1. // *******************************************************************************************************************
    2. // ** 作者 : 孤独的单刀
    3. // ** 邮箱 : zachary_wu93@163.com
    4. // ** 博客 : https://blog.csdn.net/wuzhikaidetb
    5. // ** 日期 : 2022/07/31
    6. // ** 功能 : 1、对基于FPGA的串口多字节发送模块进行测试的模块;
    7. // 2、设置好一次发送的字节数、波特率、主时钟频率;
    8. // 3、UART协议设置为起始位1bit,数据位8bit,停止位1bit,无奇偶校验(不可在端口更改,只能更改发送驱动源码);
    9. // 4、数据发送顺序,先发送低字节、再发送高字节。如:发送16’h12_34,先发送单字节8'h34,再发送单字节8'h12。
    10. // *******************************************************************************************************************
    11. module uart_bytes_tx_test(
    12. //系统接口
    13. input sys_clk , //主时钟
    14. input sys_rst_n , //低电平有效的复位信号
    15. //UART发送线
    16. output uart_txd //UART发送线
    17. );
    18. localparam integer BYTES = 1 ; //发送的字节数,单字节8bit
    19. localparam integer BPS = 115200 ; //发送波特率
    20. localparam integer CLK_FRE = 50_000_000 ; //输入时钟频率
    21. localparam integer CNT_MAX = 50_000_000 ; //发送时间间隔,1秒
    22. reg [31:0] cnt_time;
    23. reg uart_bytes_en; //发送使能,当其为高电平时,代表此时需要发送数据
    24. reg [BYTES*8-1 :0] uart_bytes_data; //需要通过UART发送的数据,在uart_bytes_en为高电平时有效
    25. //1s计数模块,每隔1s发送一个数据和拉高发送使能信号一次,数据从初始值开始递增1
    26. always @(posedge sys_clk or negedge sys_rst_n)begin
    27. if(!sys_rst_n)begin
    28. cnt_time <= 'd0;
    29. uart_bytes_en <= 1'd0;
    30. uart_bytes_data <= 'h12; //初始数据
    31. end
    32. else if(cnt_time == (CNT_MAX - 1'b1))begin
    33. cnt_time <= 'd0;
    34. uart_bytes_en <= 1'd1; //拉高发送使能
    35. uart_bytes_data <= uart_bytes_data + 1'd1; //发送数据累加1
    36. end
    37. else begin
    38. cnt_time <= cnt_time + 1'd1;
    39. uart_bytes_en <= 1'd0;
    40. uart_bytes_data <= uart_bytes_data;
    41. end
    42. end
    43. //例化串口多字节发送模块
    44. uart_bytes_tx
    45. #(
    46. .BYTES (BYTES ),
    47. .BPS (BPS ),
    48. .CLK_FRE (CLK_FRE )
    49. )
    50. uart_bytes_tx_inst
    51. (
    52. .sys_clk (sys_clk ),
    53. .sys_rst_n (sys_rst_n ),
    54. .uart_bytes_data (uart_bytes_data ),
    55. .uart_bytes_en (uart_bytes_en ),
    56. .uart_bytes_done ( ),
    57. .uart_txd (uart_txd )
    58. );
    59. endmodule

            

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

     

            测试结果与预期一致。


    5.2、双字节实测

            顶层模块如下:

    1. // *******************************************************************************************************************
    2. // ** 作者 : 孤独的单刀
    3. // ** 邮箱 : zachary_wu93@163.com
    4. // ** 博客 : https://blog.csdn.net/wuzhikaidetb
    5. // ** 日期 : 2022/07/31
    6. // ** 功能 : 1、对基于FPGA的串口多字节发送模块进行测试的模块;
    7. // 2、设置好一次发送的字节数、波特率、主时钟频率;
    8. // 3、UART协议设置为起始位1bit,数据位8bit,停止位1bit,无奇偶校验(不可在端口更改,只能更改发送驱动源码);
    9. // 4、数据发送顺序,先发送低字节、再发送高字节。如:发送16’h12_34,先发送单字节8'h34,再发送单字节8'h12。
    10. // *******************************************************************************************************************
    11. module uart_bytes_tx_test(
    12. //系统接口
    13. input sys_clk , //主时钟
    14. input sys_rst_n , //低电平有效的复位信号
    15. //UART发送线
    16. output uart_txd //UART发送线
    17. );
    18. localparam integer BYTES = 2 ; //发送的字节数,单字节8bit
    19. localparam integer BPS = 115200 ; //发送波特率
    20. localparam integer CLK_FRE = 50_000_000 ; //输入时钟频率
    21. localparam integer CNT_MAX = 50_000_000 ; //发送时间间隔,1秒
    22. reg [31:0] cnt_time;
    23. reg uart_bytes_en; //发送使能,当其为高电平时,代表此时需要发送数据
    24. reg [BYTES*8-1 :0] uart_bytes_data; //需要通过UART发送的数据,在uart_bytes_en为高电平时有效
    25. //1s计数模块,每隔1s发送一个数据和拉高发送使能信号一次,数据从初始值开始递增1
    26. always @(posedge sys_clk or negedge sys_rst_n)begin
    27. if(!sys_rst_n)begin
    28. cnt_time <= 'd0;
    29. uart_bytes_en <= 1'd0;
    30. uart_bytes_data <= 'h3412; //初始数据
    31. end
    32. else if(cnt_time == (CNT_MAX - 1'b1))begin
    33. cnt_time <= 'd0;
    34. uart_bytes_en <= 1'd1; //拉高发送使能
    35. uart_bytes_data <= uart_bytes_data + 1'd1; //发送数据累加1
    36. end
    37. else begin
    38. cnt_time <= cnt_time + 1'd1;
    39. uart_bytes_en <= 1'd0;
    40. uart_bytes_data <= uart_bytes_data;
    41. end
    42. end
    43. //例化串口多字节发送模块
    44. uart_bytes_tx
    45. #(
    46. .BYTES (BYTES ),
    47. .BPS (BPS ),
    48. .CLK_FRE (CLK_FRE )
    49. )
    50. uart_bytes_tx_inst
    51. (
    52. .sys_clk (sys_clk ),
    53. .sys_rst_n (sys_rst_n ),
    54. .uart_bytes_data (uart_bytes_data ),
    55. .uart_bytes_en (uart_bytes_en ),
    56. .uart_bytes_done ( ),
    57. .uart_txd (uart_txd )
    58. );
    59. endmodule

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

            

     

            测试结果与预期一致。


    5.3、5字节实测

            顶层模块如下:

    1. // *******************************************************************************************************************
    2. // ** 作者 : 孤独的单刀
    3. // ** 邮箱 : zachary_wu93@163.com
    4. // ** 博客 : https://blog.csdn.net/wuzhikaidetb
    5. // ** 日期 : 2022/07/31
    6. // ** 功能 : 1、对基于FPGA的串口多字节发送模块进行测试的模块;
    7. // 2、设置好一次发送的字节数、波特率、主时钟频率;
    8. // 3、UART协议设置为起始位1bit,数据位8bit,停止位1bit,无奇偶校验(不可在端口更改,只能更改发送驱动源码);
    9. // 4、数据发送顺序,先发送低字节、再发送高字节。如:发送16’h12_34,先发送单字节8'h34,再发送单字节8'h12。
    10. // *******************************************************************************************************************
    11. module uart_bytes_tx_test(
    12. //系统接口
    13. input sys_clk , //主时钟
    14. input sys_rst_n , //低电平有效的复位信号
    15. //UART发送线
    16. output uart_txd //UART发送线
    17. );
    18. localparam integer BYTES = 5 ; //发送的字节数,单字节8bit
    19. localparam integer BPS = 115200 ; //发送波特率
    20. localparam integer CLK_FRE = 50_000_000 ; //输入时钟频率
    21. localparam integer CNT_MAX = 50_000_000 ; //发送时间间隔,1秒
    22. reg [31:0] cnt_time;
    23. reg uart_bytes_en; //发送使能,当其为高电平时,代表此时需要发送数据
    24. reg [BYTES*8-1 :0] uart_bytes_data; //需要通过UART发送的数据,在uart_bytes_en为高电平时有效
    25. //1s计数模块,每隔1s发送一个数据和拉高发送使能信号一次,数据从初始值开始递增1
    26. always @(posedge sys_clk or negedge sys_rst_n)begin
    27. if(!sys_rst_n)begin
    28. cnt_time <= 'd0;
    29. uart_bytes_en <= 1'd0;
    30. uart_bytes_data <= 40'h9a_78_56_34_12; //初始数据
    31. end
    32. else if(cnt_time == (CNT_MAX - 1'b1))begin
    33. cnt_time <= 'd0;
    34. uart_bytes_en <= 1'd1; //拉高发送使能
    35. uart_bytes_data <= uart_bytes_data + 1'd1; //发送数据累加1
    36. end
    37. else begin
    38. cnt_time <= cnt_time + 1'd1;
    39. uart_bytes_en <= 1'd0;
    40. uart_bytes_data <= uart_bytes_data;
    41. end
    42. end
    43. //例化串口多字节发送模块
    44. uart_bytes_tx
    45. #(
    46. .BYTES (BYTES ),
    47. .BPS (BPS ),
    48. .CLK_FRE (CLK_FRE )
    49. )
    50. uart_bytes_tx_inst
    51. (
    52. .sys_clk (sys_clk ),
    53. .sys_rst_n (sys_rst_n ),
    54. .uart_bytes_data (uart_bytes_data ),
    55. .uart_bytes_en (uart_bytes_en ),
    56. .uart_bytes_done ( ),
    57. .uart_txd (uart_txd )
    58. );
    59. 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):点击这里下载
    • 📣博客主页:wuzhikai.blog.csdn.net
    • 📣本文由 孤独的单刀 原创,首发于CSDN平台🐵
    • 📣您有任何问题,都可以在评论区和我交流📞!
    • 📣创作不易,您的支持是我持续更新的最大动力!如果本文对您有帮助,还请多多点赞👍、评论💬和收藏⭐!

  • 相关阅读:
    猿创征文|瑞吉外卖——管理端_后台登录与退出
    2007-2021年31省市财政环保支出占比数据(含原始数据+计算过程+测算结果)
    阿里二面:接口流量突增,如何做好性能调优?
    前端高频vue面试题合集
    scala/java redis的cluster模式 删除固定前缀的key
    SQL server中字段自增:IDENTITY、序列Sequence
    HTML入门篇---01常用标签
    Mysql索引优化实战
    Jlink_V9固件修复教程
    数据结构——动态顺序表
  • 原文地址:https://blog.csdn.net/wuzhikaidetb/article/details/126093301