当前位置:   article > 正文

UART设计

UART设计

一、UART通信简介

通用异步收发器,

特点:串行、异步、全双工通信

优点:通信线路简单,传输距离远

缺点:传输速度慢

数据传输速率:波特率(单位:baud,波特)

常见的波特率为:1200、2400、4800、19200、38400、57600、115200

最常用的:9600、115200                

数据通信格式:1个数据位+n个数据位+1个校验位+1个结束位

其中n个数据位:通常为8位,即1个字节

空闲位:当总线处于空闲状态时信号线的状态为1,表示当前线路没有进行数据传输。

起始位:每开始一次通信时发送方先发出一个逻辑0的信号,表示传输字符的开始。(因为总线空闲时为高电平,所以开始一次通信时先发送一个明显区别于空闲状态的信号,即低电平)

数据位:起始位之后就是我们所要传输的数据,数据位可以是5,6,7,8,9位等,构成一个字符。先发送最低位,最后发送最高位

奇偶校验位

数据位加上这一位后,使得1的位数应为偶数(偶校验)或奇数(奇校验)。

串口校验的几种方式:

  1. 无校验
  2. 奇校验:如果数据位中“1”的数目是偶数,则校验位为1,如果“1”的数目是奇数,则校验位为0
  3. 偶校验:如果数据位中1的数目是偶数,则校验位为0,如果是奇数,校验位为1
  4. Mark parity:校验位始终为1(不常用)
  5. Parity:校验位始终为0(不常用)

停止位:

它是一个字符数据的结束标志。可以是1位、2位的高电平。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备之间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟的机会。停止位个数越多,数据传输越稳定,但是数据传输速度也越慢

传输时间:计数=时钟频率除以波特频率

二、Verilog实现:

1.UART_TX设计框图

信号

位宽

类型

功能描述

clk

1bit

input

工作时钟,频率50MHz

tx_data

8bit

input

发送数据

tx_flag

1bit

input

发送数据的有效标志信号

tx

1bit

output

串口发送信号

Verilog代码:

uart_tx:

  1. module uart_tx
  2. #(
  3. parameter UART_BPS = 'd9600, //串口波特率
  4. parameter CLK_FREQ = 'd50_000_000 //时钟频率
  5. )
  6. (
  7. input wire clk, //系统时钟
  8. input wire rstn, //全局复位
  9. input wire [7:0] tx_data, //发送8bit数据
  10. input wire tx_flag, //发送数据有效标志信号
  11. output reg tx //串转并后的1bit数据
  12. );
  13. localparam cnt_max = CLK_FREQ / UART_BPS;
  14. reg [12:0] bd_cnt;
  15. reg bit_flag;
  16. reg [3:0] bit_cnt;
  17. reg work_en; //接收数据工作使能信号
  18. //work_en:接收数据工作使能信号
  19. always @(posedge clk or negedge rstn) begin
  20. if (rstn == 1'b0)
  21. work_en <= 1'b0;
  22. else if (tx_flag == 1'b1)
  23. work_en <= 1'b1;
  24. else if((bit_flag == 1'b1)&&(bit_cnt == 4'd9))
  25. work_en <= 1'b0;
  26. end
  27. //bd_cnt:波特率计数器计数,从0计数到5207
  28. always @(posedge clk or negedge rstn) begin
  29. if(rstn == 1'b0)
  30. bd_cnt <= 13'b0;
  31. else if((bd_cnt == cnt_max - 1) ||(work_en == 1'b0))
  32. bd_cnt <= 13'b0;
  33. else if(work_en == 1'b1)
  34. bd_cnt <= bd_cnt + 1'b1;
  35. end
  36. //bit_flag : 当bd_cnt计数器计数到1时让bit_flag拉高一个时钟的高电平
  37. always @(posedge clk or negedge rstn) begin
  38. if(rstn == 1'b0)
  39. bit_flag <= 1'b0;
  40. else if(bd_cnt == 13'd1)
  41. bit_flag <= 1'd1;
  42. else
  43. bit_flag <= 1'b0;
  44. end
  45. //bit_cnt:数据位数个数计数,10个有效数据(含起始位和停止位)到来后计数器清零
  46. always@(posedge clk or negedge rstn) begin
  47. if(rstn == 1'b0)
  48. bit_cnt <= 4'b0;
  49. else if((bit_flag == 1'b1)&&(bit_cnt == 4'd9))
  50. bit_cnt <= 4'b0;
  51. else if((bit_flag == 1'b1)&&(work_en == 1'b1))
  52. bit_cnt <= bit_cnt + 1'b1;
  53. end
  54. //tx:输出数据在满足uart协议(起始位为0,停止位为1)的情况下一位一位输出
  55. always @(posedge clk or negedge rstn) begin
  56. if(rstn == 1'b0)
  57. tx <= 1'b1; //空闲状态为高电平
  58. else if(bit_flag == 1'b1)
  59. case(bit_cnt)
  60. 0: tx <= 1'b0 ;
  61. 1: tx <= tx_data[0];
  62. 2: tx <= tx_data[1];
  63. 3: tx <= tx_data[2];
  64. 4: tx <= tx_data[3];
  65. 5: tx <= tx_data[4];
  66. 6: tx <= tx_data[5];
  67. 7: tx <= tx_data[6];
  68. 8: tx <= tx_data[7];
  69. 9: tx <= 1'b1;
  70. default : tx <= 1'b1;
  71. endcase
  72. end
  73. endmodule

testbench:

  1. module tb_uart_tx();
  2. reg clk;
  3. reg rstn;
  4. reg [7:0] tx_data;
  5. reg tx_flag;
  6. wire tx;
  7. //初始化系统时钟,全局复位
  8. initial begin
  9. clk = 1'b1;
  10. rstn = 1'b0;
  11. #20;
  12. rstn <= 1'b1;
  13. end
  14. //模拟发送8次数据,分别为0~7
  15. initial begin
  16. tx_data <= 8'b0;
  17. tx_flag <= 1'b0;
  18. #200
  19. //发送数据0
  20. tx_data <= 8'd0;
  21. tx_flag <= 1'b1;
  22. #20
  23. tx_flag <= 1'b0;
  24. // 每发送1bit数据需要5208个时钟周期,一帧数据为10bit
  25. //所以需要数据延时(5208*20*10)后再产生下一个数据
  26. #(5208*20*10);
  27. //发送数据1
  28. tx_data <= 8'd1;
  29. tx_flag <= 1'b1;
  30. #20
  31. tx_flag <= 1'b0;
  32. #(5208*20*10);
  33. //发送数据2
  34. tx_data <= 8'd2;
  35. tx_flag <= 1'b1;
  36. #20
  37. tx_flag <= 1'b0;
  38. #(5208*20*10);
  39. //发送数据3
  40. tx_data <= 8'd3;
  41. tx_flag <= 1'b1;
  42. #20
  43. tx_flag <= 1'b0;
  44. #(5208*20*10);
  45. //发送数据4
  46. tx_data <= 8'd4;
  47. tx_flag <= 1'b1;
  48. #20
  49. tx_flag <= 1'b0;
  50. #(5208*20*10);
  51. //发送数据5
  52. tx_data <= 8'd5;
  53. tx_flag <= 1'b1;
  54. #20
  55. tx_flag <= 1'b0;
  56. #(5208*20*10);
  57. //发送数据6
  58. tx_data <= 8'd6;
  59. tx_flag <= 1'b1;
  60. #20
  61. tx_flag <= 1'b0;
  62. #(5208*20*10);
  63. //发送数据7
  64. tx_data <= 8'd7;
  65. tx_flag <= 1'b1;
  66. #20
  67. tx_flag <= 1'b0;
  68. end
  69. //clk:每10ns电平翻转一次,产生一个50MHz的时钟信号
  70. always #10 clk = ~clk;
  71. uart_tx uart_tx_inst(
  72. .clk(clk),
  73. .rstn(rstn),
  74. .tx_data(tx_data),
  75. .tx_flag(tx_flag),
  76. .tx(tx)
  77. );
  78. endmodule

仿真截图:

接收模块

uart_rx的设计框图:

信号

位宽

类型

功能描述

clk

1bit

Input

工作时钟,频率50MHz

rstn

1bit

Input

复位信号,低电平有效

rx

1bit

Input

串口接收信号

rx_data

8bit

output

串口接收后转成的8bit数据

rx_flag

1bit

output

串口接收后转成的8bit数据有效标志

Verilog代码:

  1. module uart_rx
  2. #(
  3. parameter UART_BPS = 'd9600,
  4. parameter CLK_FREQ = 'd50_000_000
  5. )
  6. (
  7. input wire clk,
  8. input wire rstn,
  9. input wire rx, //串口接收数据
  10. output reg[7:0] rx_data, //串转并后的8bit数据
  11. output reg rx_flag //串转并后的数据有效标志信号
  12. );
  13. localparam cnt_max = CLK_FREQ / UART_BPS;
  14. reg rx_reg1;
  15. reg rx_reg2;
  16. reg rx_reg3;
  17. reg start_nedge;
  18. reg work_en;
  19. reg [12:0] bd_cnt;
  20. reg bit_flag;
  21. reg [3:0] bit_cnt;
  22. reg [7:0] data;
  23. reg flag;
  24. //插入两级寄存器进行数据同步,用来消除亚稳态
  25. //rx_reg1:第一级寄存器,寄存器空闲状态复位为1
  26. always @(posedge clk or negedge rstn) begin
  27. if(rstn == 1'b0)
  28. rx_reg1 <= 1'b1;
  29. else
  30. rx_reg1 <= rx;
  31. end
  32. //rx_reg2:第二级寄存器,寄存器空闲状态复位为1
  33. always @(posedge clk or negedge rstn) begin
  34. if(rstn == 1'b0)
  35. rx_reg2 <= 1'b1;
  36. else
  37. rx_reg2 <= rx_reg1;
  38. end
  39. //reg3:第三级寄存器和第二级寄存器共同构成下降沿检测
  40. always @(posedge clk or rstn) begin
  41. if(rstn == 1'b0)
  42. rx_reg3 <= 1'b1;
  43. else
  44. rx_reg3 <= rx_reg2;
  45. end
  46. //start_nedge:检测到下降沿时start_nedge产生一个时钟的高电平
  47. always @(posedge clk or negedge rstn) begin
  48. if(rstn == 1'b0)
  49. start_nedge <= 1'b0;
  50. else if((~rx_reg2) && (rx_reg3))
  51. start_nedge <= 1'b1;
  52. else start_nedge <= 1'b0;
  53. end
  54. //work_en:接收数据工作使能信号
  55. always @(posedge clk or negedge rstn) begin
  56. if(rstn == 1'b0)
  57. work_en <= 1'b0;
  58. else if(start_nedge == 1'b1)
  59. work_en <= 1'b1;
  60. else if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
  61. work_en <= 1'b0;
  62. end
  63. //bd_cnt:波特率计数器计数,从0计数到5207
  64. always @(posedge clk or negedge rstn) begin
  65. if(rstn == 1'b0)
  66. bd_cnt <= 13'b0;
  67. else if((bd_cnt == cnt_max - 1)||(work_en == 1'b0))
  68. bd_cnt <= 13'b0;
  69. else if(work_en == 1'b1)
  70. bd_cnt <= bd_cnt + 1'b1;
  71. end
  72. //bit_flag : bd_cnt计数器计数到中间数时采样的数据最稳定
  73. //此时拉高一个标志信号表示数据可以被取走
  74. always @(posedge clk or negedge rstn) begin
  75. if(rstn == 1'b0)
  76. bit_flag <= 1'b0;
  77. else if(bd_cnt == cnt_max/2 - 1)
  78. bit_flag <= 1'b1;
  79. else
  80. bit_flag <= 1'b0;
  81. end
  82. //bit_cnt:有效数据个数计数器,当8个有效数据(不含起始位和停止位)
  83. //都接收完成后计数器清零
  84. always @(posedge clk or negedge rstn) begin
  85. if(rstn == 1'b0)
  86. bit_cnt <= 4'b0;
  87. else if((bit_cnt == 4'd8) && (bit_flag == 1'b1))
  88. bit_cnt <= 4'b0;
  89. else if(bit_flag == 1'b1)
  90. bit_cnt <= bit_cnt + 1'b1;
  91. end
  92. //data:输入数据进行移位
  93. always @(posedge clk or negedge rstn) begin
  94. if(rstn == 1'b0)
  95. data <= 8'b0;
  96. else if((bit_cnt >= 4'd1)&&(bit_cnt <= 4'd8)&&(bit_flag == 1'b1))
  97. data <= {rx_reg3,data[7:1]};
  98. end
  99. //flag:输入数据移位完成时flag拉高一个时钟的高电平
  100. always @(posedge clk or negedge rstn) begin
  101. if(rstn == 1'b0)
  102. flag <= 1'b0;
  103. else if((bit_cnt == 4'd8)&&(bit_flag == 1'b1))
  104. flag <= 1'b1;
  105. else
  106. flag <= 1'b0;
  107. end
  108. //rx_data:输出完整的8位有效数据
  109. always @(posedge clk or negedge rstn) begin
  110. if(rstn == 1'b0)
  111. rx_data <= 8'b0;
  112. else if(rx_flag == 1'b1)
  113. rx_data <= data;
  114. end
  115. //rx_flag:输出数据有效标志(比flag延后一个时钟周期,为了和rx_data同步)
  116. always @(posedge clk or rstn) begin
  117. if(rstn == 1'b0)
  118. rx_flag <= 1'b0;
  119. else
  120. rx_flag <= flag;
  121. end
  122. endmodule

tb_uart_rx:

  1. module tb_uart_rx();
  2. reg clk;
  3. reg rstn;
  4. reg rx;
  5. wire [7:0] rx_data;
  6. wire rx_flag;
  7. //初始化系统时钟,全局复位和输入信号
  8. initial begin
  9. clk = 1'b1;
  10. rstn <= 1'b0;
  11. rx <= 1'b1;
  12. #20
  13. rstn <= 1'b1;
  14. end
  15. //模拟发送8次数据,分别为0~7
  16. initial begin
  17. #200
  18. rx_bit(8'd0);
  19. rx_bit(8'd1);
  20. rx_bit(8'd2);
  21. rx_bit(8'd3);
  22. rx_bit(8'd4);
  23. rx_bit(8'd5);
  24. rx_bit(8'd6);
  25. rx_bit(8'd7);
  26. end
  27. always #10 clk = ~clk;
  28. //定义一个名为rx_bit的任务,每次发送的数据有10
  29. //data的值分别为0~7由i的值传递进来
  30. //任务以task开头, 后面紧跟任务名,调用时使用
  31. task rx_bit(
  32. input [7:0] data
  33. );
  34. integer i;
  35. //for循环产生一帧数据,for括号中最后执行的内容只能写i=i+1;
  36. for(i=0;i<10;i=i+1) begin
  37. case(i)
  38. 0: rx <= 1'b0;
  39. 1: rx <= data[0];
  40. 2: rx <= data[1];
  41. 3: rx <= data[2];
  42. 4: rx <= data[3];
  43. 5: rx <= data[4];
  44. 6: rx <= data[5];
  45. 7: rx <= data[6];
  46. 8: rx <= data[7];
  47. 9: rx <= 1'b1;
  48. endcase
  49. #(5208*20); // 每发送1位数据延时5208个时钟周期
  50. end
  51. endtask //任务以endtask结束
  52. uart_rx uart_rx_inst(
  53. .clk(clk),
  54. .rstn(rstn),
  55. .rx(rx),
  56. .rx_data(rx_data),
  57. .rx_flag(rx_flag)
  58. );
  59. endmodule

仿真截图

参考资料:

5. 串口rs232 — [野火]FPGA Verilog开发实战指南——基于Altera EP4CE10 征途Pro开发板 文档

FPGA协议篇:最简单且通用verilog实现UART协议 - 知乎

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/381822
推荐阅读
相关标签
  

闽ICP备14008679号