当前位置:   article > 正文

小梅哥Xilinx FPGA学习笔记9——串口通信发送逻辑_xilinx 串口

xilinx 串口

目录

1、UART(通用异步收发传输器)

1.1 UART基本介绍

1.2UART关键参数

1.3串口发送任务

1.4 串口发送任务整体框图

2、代码实现步骤分析

2.1 设计文件

2.2 仿真文件

2.3 仿真结果

3.将上述串口发送实现任务变成一个通用的串口发送模板

3.1 设计文件

3.2 顶层文件 


1、UART(通用异步收发传输器)

1.1 UART基本介绍

RS232 通信接口标准,通用异步收发传输器(Universal Asynchronous Receiver/Transmitter,UART)是一种异步收发传输器,其在数据发送时将并行数据转换成串行数据来传输,在数据接收时将接收到的串行数据转换成并行数据,可以实现全双工传输和接收。

UART 是异步串行通信的总称(UART 是一种协议)。而 RS232、RS449、RS423、RS422 和 RS485 等,是对应各种异步串行通信口的接口标准和总线标准,它们规定了通信口的电气特性、传输速率、连接特性和接口的机械特性等内容。若系统存在多个 UART接口,则可分别称为 COM1、COM2 等。

RS-232 是美国电子工业联盟(EIA)制定的串行数据通信的接口标准,原始编号全称是 EIA-RS-232(简称 232,RS232),被广泛用于计算机串行接口外设连接。

DB9 接口的针脚定义如图:

通常使用RXD、TXD、GND这三个信号

1.2UART关键参数

UART 通信在使用前需要做多项设置,最常见的设置包括数据位数、波特率大小、奇偶校验类型和停止位数。

数据位(Data bits)该参数定义单个 UART 数据传输在开始到停止期间发送的数据位数。可选择为:5、6、7 或者 8(默认)。

波特率(Baud):是指从一设备发到另一设备的波特率,即每秒钟可以通信的数据比特个数。典型的波特率有 300, 1200, 2400, 9600, 19200, 115200 等。一般通信两端设备都要设为相同的波特率,但有些设备也可设置为自动检测波特率。通俗的说,就是一个数据的高电平/低电平持续的时间,是一个最基本的时间单位。
       串口全部都是使用的二进制,所以波特率就是比特率。

奇偶校验类型(Parity Type):是用来验证数据的正确性。奇偶校验一般不使用,如果使用,则既可以做奇校验(Odd)也可以做偶校验(Even)。在偶校验中,因为奇偶校验位会被相应的置 1 或 0(一般是最高位或最低位),所以数据会被改变以使得所有传送的数位(含字符的各数位和校验位)中“1”的个数为偶数;在奇校验中,所有传送的数位(含字符的各数位和校验位)中“1”的个数为奇数。奇偶校验可以用于接受方检查传输是否发送生错误,如果某一字节中“1”的个数发生了错误,那么这个字节在传输中一定有错误发生。如果奇偶校验是正确的,那么要么没有发生错误,要么发生了偶数个的错误。如果用户选择数据长度为 8 位,则因为没有多余的比特可被用来作为奇偶校验位,因此就叫做“无奇偶校验(Non)”。
停止位(Stop bits):在每个字节的数据位发送完成之后,发送停止位,来标志着一次数据传输完成,同时用来帮助接受信号方硬件重同步。可选择为:1(默认)、1.5 或者 2 位。

1.3串口发送任务

1.4 串口发送任务整体框图

2、代码实现步骤分析

1.首先应该定义一个分频计数器(波特率的产生)

2.定义一个位计数器(用来控制每一位去做什么)

3.定义一个1S的延时计数器(用来推迟1S发送)

4.实现位实现逻辑(定义每一位具体做什么)

5.实现Led翻转逻辑(每完成一次发送就翻转一次)

2.1 设计文件

(底层通用模块)

  1. module uart_byte_tx(
  2. Clk,
  3. Reset_n,
  4. Data,
  5. Send_Go,
  6. Baud_set,
  7. uart_tx,
  8. Tx_done
  9. );
  10. input Clk;
  11. input Reset_n;
  12. input [7:0]Data;
  13. input Send_Go;
  14. input [2:0]Baud_set;
  15. output reg uart_tx;
  16. output reg Tx_done;
  17. //Baud_set = 0 就让波特率 = 9600
  18. //Baud_set = 1 就让波特率 = 19200
  19. //Baud_set = 2 就让波特率 = 38400
  20. //Baud_set = 3 就让波特率 = 57600
  21. //Baud_set = 4 就让波特率 = 115200
  22. reg [17:0]bps_DR;
  23. always@(*)
  24. case(Baud_set)
  25. 0:bps_DR = 1000000000/9600/20;
  26. 1:bps_DR = 1000000000/19200/20;
  27. 2:bps_DR = 1000000000/38400/20;
  28. 3:bps_DR = 1000000000/57600/20;
  29. 4:bps_DR = 1000000000/115200/20;
  30. default:bps_DR = 1000000000/9600/20;
  31. endcase
  32. reg Send_en;
  33. always@(posedge Clk or negedge Reset_n)
  34. if(!Reset_n)
  35. Send_en <= 0;
  36. else if(Send_Go)
  37. Send_en <= 1;
  38. else if(Tx_done)
  39. Send_en <= 0;
  40. reg [7:0]r_Data;
  41. always@(posedge Clk)
  42. if(Send_Go)
  43. r_Data <= Data;
  44. else
  45. r_Data <= r_Data;
  46. reg [17:0]div_cnt;
  47. wire bps_clk;
  48. assign bps_clk = (div_cnt == 1);//用上升沿的脉冲来计数
  49. always@(posedge Clk or negedge Reset_n)
  50. if(!Reset_n)
  51. div_cnt <= 0;
  52. else if(Send_en)begin
  53. if(div_cnt == bps_DR - 1)
  54. div_cnt <= 0;
  55. else
  56. div_cnt <= div_cnt + 1'b1;
  57. end
  58. else
  59. div_cnt <= 0;
  60. reg [3:0]bps_cnt;
  61. always@(posedge Clk or negedge Reset_n)
  62. if(!Reset_n)
  63. bps_cnt <= 0;
  64. else if(Send_en)begin
  65. if(bps_clk)begin//这是以刚开始计数作为位计数的触发,所以应该是11,而不是9
  66. if(bps_cnt == 11)
  67. bps_cnt <= 0;
  68. else
  69. bps_cnt <= bps_cnt + 1'b1;
  70. end
  71. end
  72. else
  73. bps_cnt <= 0;
  74. always@(posedge Clk or negedge Reset_n)
  75. if(!Reset_n) begin
  76. uart_tx <= 1'b1;
  77. end
  78. else begin
  79. case(bps_cnt)
  80. 1:uart_tx <= 0;//起始位
  81. 2:uart_tx <= r_Data[0];
  82. 3:uart_tx <= r_Data[1];
  83. 4:uart_tx <= r_Data[2];
  84. 5:uart_tx <= r_Data[3];
  85. 6:uart_tx <= r_Data[4];
  86. 7:uart_tx <= r_Data[5];
  87. 8:uart_tx <= r_Data[6];
  88. 9:uart_tx <= r_Data[7];
  89. 10:uart_tx <= 1;//停止位
  90. 11:begin uart_tx <= 1;end
  91. default:uart_tx <= 1;
  92. endcase
  93. end
  94. always@(posedge Clk or negedge Reset_n)
  95. if(!Reset_n)
  96. Tx_done <= 0;
  97. else if((bps_clk == 1) && (bps_cnt == 10))
  98. Tx_done <= 1;
  99. else
  100. Tx_done <= 0;
  101. endmodule

顶层调用文件

  1. module uart_tx_test(
  2. Clk,
  3. Reset_n,
  4. uart_tx,
  5. Led,//Led作为输出
  6. Data
  7. );
  8. input Clk;
  9. input Reset_n;
  10. input [7:0]Data;//对应7位拨码开关的值
  11. output uart_tx;
  12. output reg Led;
  13. reg Send_Go;//产生的脉冲信号
  14. wire Tx_done;
  15. uart_byte_tx uart_byte_tx( //将底层通用模块例化进来
  16. .Clk(Clk),
  17. .Reset_n(Reset_n),
  18. .Data(Data),
  19. .Send_Go(Send_Go),
  20. .Baud_set(3'd4),
  21. .uart_tx(uart_tx),
  22. .Tx_done(Tx_done)
  23. );
  24. //1s一次
  25. parameter Dly_cnt = 50000000-1;
  26. reg [24:0]counter;//定时1S计数器
  27. always@(posedge Clk or negedge Reset_n)
  28. if(!Reset_n)
  29. counter <= 0;
  30. else if(counter == Dly_cnt)
  31. counter <= 0;
  32. else
  33. counter <= counter + 1;
  34. always@(posedge Clk or negedge Reset_n)
  35. if(!Reset_n)
  36. Send_Go <= 0;
  37. else if(counter == 1)
  38. Send_Go <= 1;
  39. else
  40. Send_Go <= 0;//因为是脉冲信号而不是电平信号,所以它的值只关心什么时候产生高脉冲,其他不关心。
  41. always@(posedge Clk or negedge Reset_n)
  42. if(!Reset_n)
  43. Led <= 0;
  44. else if(Tx_done)
  45. Led <= !Led;//每当发完一次就翻转
  46. else
  47. Led <= Led;
  48. endmodule

2.2 仿真文件

1.复制端口声明,例化连接,ctrl可以实现列编辑,在端口名前加点。
2.复制输入端口,直接将input替换成reg。在vivado中,单击鼠标右键,选择replace in files,可以选择设计文件/仿真文件的替换 。

  1. `timescale 1ns / 1ns
  2. module uart_byte_tb();
  3. reg Clk;
  4. reg Reset_n;
  5. reg [7:0]SW;
  6. wire uart_tx;
  7. wire Led;
  8. uart_byte_tx uart_byte_tx (
  9. .Clk(Clk),
  10. .Reset_n(Reset_n),
  11. .SW(SW),
  12. .uart_tx(uart_tx),
  13. .Led(Led)
  14. );
  15. defparam uart_byte_tx.MCNT_DLY = 500000-1;//更改空闲时间
  16. initial Clk = 1;
  17. always #10 Clk =~Clk;
  18. initial begin
  19. Reset_n = 0;
  20. #201;
  21. Reset_n =1;
  22. SW = 8'b0101_0101;
  23. #30000000;
  24. SW = 8'b1010_1010;
  25. #30000000;
  26. $stop;
  27. end
  28. endmodule

2.3 仿真结果

3.将上述串口发送实现任务变成一个通用的串口发送模板

3.1 设计文件

  1. module uart_byte_tx(
  2. Clk,
  3. Reset_n,
  4. Data,
  5. Send_Go,
  6. Baud_set,
  7. uart_tx,
  8. Tx_done
  9. );
  10. input Clk;
  11. input Reset_n;
  12. input [7:0]Data;
  13. input Send_Go;
  14. input [2:0]Baud_set;
  15. output reg uart_tx;
  16. output reg Tx_done;
  17. //Baud_set = 0 就让波特率 = 9600
  18. //Baud_set = 1 就让波特率 = 19200
  19. //Baud_set = 2 就让波特率 = 38400
  20. //Baud_set = 3 就让波特率 = 57600
  21. //Baud_set = 4 就让波特率 = 115200
  22. reg [17:0]bps_DR;
  23. always@(*)
  24. case(Baud_set)
  25. 0:bps_DR = 1000000000/9600/20;
  26. 1:bps_DR = 1000000000/19200/20;
  27. 2:bps_DR = 1000000000/38400/20;
  28. 3:bps_DR = 1000000000/57600/20;
  29. 4:bps_DR = 1000000000/115200/20;
  30. default:bps_DR = 1000000000/9600/20;
  31. endcase
  32. reg Send_en;
  33. always@(posedge Clk or negedge Reset_n)
  34. if(!Reset_n)
  35. Send_en <= 0;
  36. else if(Send_Go)
  37. Send_en <= 1;
  38. else if(Tx_done)
  39. Send_en <= 0;
  40. reg [7:0]r_Data;
  41. always@(posedge Clk)
  42. if(Send_Go)
  43. r_Data <= Data;
  44. else
  45. r_Data <= r_Data;
  46. reg [17:0]div_cnt;
  47. wire bps_clk;
  48. assign bps_clk = (div_cnt == 1);//用上升沿的脉冲来计数
  49. always@(posedge Clk or negedge Reset_n)
  50. if(!Reset_n)
  51. div_cnt <= 0;
  52. else if(Send_en)begin
  53. if(div_cnt == bps_DR - 1)
  54. div_cnt <= 0;
  55. else
  56. div_cnt <= div_cnt + 1'b1;
  57. end
  58. else
  59. div_cnt <= 0;
  60. reg [3:0]bps_cnt;
  61. always@(posedge Clk or negedge Reset_n)
  62. if(!Reset_n)
  63. bps_cnt <= 0;
  64. else if(Send_en)begin
  65. if(bps_clk)begin//这是以刚开始计数作为位计数的触发,所以应该是11,而不是9
  66. if(bps_cnt == 11)
  67. bps_cnt <= 0;
  68. else
  69. bps_cnt <= bps_cnt + 1'b1;
  70. end
  71. end
  72. else
  73. bps_cnt <= 0;
  74. always@(posedge Clk or negedge Reset_n)
  75. if(!Reset_n) begin
  76. uart_tx <= 1'b1;
  77. end
  78. else begin
  79. case(bps_cnt)
  80. 1:uart_tx <= 0;//起始位
  81. 2:uart_tx <= r_Data[0];
  82. 3:uart_tx <= r_Data[1];
  83. 4:uart_tx <= r_Data[2];
  84. 5:uart_tx <= r_Data[3];
  85. 6:uart_tx <= r_Data[4];
  86. 7:uart_tx <= r_Data[5];
  87. 8:uart_tx <= r_Data[6];
  88. 9:uart_tx <= r_Data[7];
  89. 10:uart_tx <= 1;//停止位
  90. 11:begin uart_tx <= 1;end
  91. default:uart_tx <= 1;
  92. endcase
  93. end
  94. always@(posedge Clk or negedge Reset_n)
  95. if(!Reset_n)
  96. Tx_done <= 0;
  97. else if((bps_clk == 1) && (bps_cnt == 10))
  98. Tx_done <= 1;
  99. else
  100. Tx_done <= 0;
  101. endmodule

 3.2 顶层文件   

  1. module uart_tx_test(
  2. Clk,
  3. Reset_n,
  4. uart_tx,
  5. );
  6. input Clk;
  7. input Reset_n;
  8. output uart_tx;
  9. output reg Led;
  10. reg Send_Go;//产生的脉冲信号
  11. wire Tx_done;
  12. reg [7:0]Data;
  13. uart_byte_tx uart_byte_tx( //将底层通用模块例化进来
  14. .Clk(Clk),
  15. .Reset_n(Reset_n),
  16. .Data(Data),
  17. .Send_Go(Send_Go),
  18. .Baud_set(3'd4),
  19. .uart_tx(uart_tx),
  20. .Tx_done(Tx_done)
  21. );
  22. //1s一次
  23. parameter Dly_cnt = 50000000-1;
  24. reg [24:0]counter;//定时1S计数器
  25. always@(posedge Clk or negedge Reset_n)
  26. if(!Reset_n)
  27. counter <= 0;
  28. else if(counter == Dly_cnt)
  29. counter <= 0;
  30. else
  31. counter <= counter + 1;
  32. always@(posedge Clk or negedge Reset_n)
  33. if(!Reset_n)
  34. Send_Go <= 0;
  35. else if(counter == 1)
  36. Send_Go <= 1;
  37. else
  38. Send_Go <= 0;//因为是脉冲信号而不是电平信号,所以它的值只关心什么时候产生高脉冲,其他不关心。
  39. endmodule

         

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/402670
推荐阅读
相关标签
  

闽ICP备14008679号