当前位置:   article > 正文

【FPGA】八、UART串口通信_fpga uart串口通信

fpga uart串口通信

文章目录

前言

一、UART简介

1、基本概念

2、UART协议

3、波特率简介

二、UART串口回环实验

1、设计思路

2、程序代码

        ① 串口接收模块

        ② 串口发送模块

        ③ 串口顶层模块

        ④ 串口仿真模块

3、仿真验证

 总结


前言

        在我们进行FPGA设计时,常常会用到一些数据通信接口,这些通信接口都是有着特定的功能以及协议的,其中最常见的莫过于串口uart了,它对于每一个做硬件和嵌入式软件的人来说,几乎就是一个必备的工具,用来调试一个带MCU或者CPU的系统。

        串口uart是一种非常通用的设备接口,可以实现不同硬件间的通信,对于FPGA开发来说,串口也同样可以实现FPGA开发板与电脑PC端的通信,下面我们就来简单介绍一下串口uart的基本协议及功能。


一、UART简介

1、基本概念

        通用异步收发传输器,英文全称为Universal Asynchronous Receiver/Transmitter,简称UART,是一种异步收发传输器,在发送数据通过将并行数据转换成串行数据进行传输,在接收数据时将串行数据转换成并行数据。

        串行通信分为同步串行通信和异步串行通信。同步串行通信即需要时钟的参与,通信双方需要在同一时钟的控制下,同步传输数据;异步串行通信则不需要时钟的干预,通信双方使用各种的时钟来控制数据的发送和接收。uart属于异步串行通信,即没有时钟信号来同步或验证从发送器发送并由接收器接收的数据,这就要求发送器和接收器必须事先就时序参数达成一致。

     UART是通用异步收发器的简称,它包括了RS232、RS422、RS423、RS449以及RS485等接口标准和总线规范标准,UART是异步串行通信接口的总称。而RS323、RS422、RS423、RS449和RS485等是对应各种异步串行通信的接口标准和总线标准,它规定了通信接口的电器特性、传输速率以及接口的机械特性等内容。

2、UART协议

        UART串口通信需要两根信号线来实现,一根用于串口发送数据,一根用于串口接收数据。UART串口传输的数据被组织成数据包,每个数据包包含了一个起始位,5至9个数据位,可选的奇偶校验位和1或者2个停止位,如下图所示。

        UART串口协议规定,当总线处于空闲状态时信号线的状态为高电平,表示当前线路上没有数据传输。

        起始位:开始进行数据传输时发送方要先发送一个低电平来表示传输字符的开始。

        数据位:起始位之后就需要传输数据,数据位可以是5~9位,构成一个字符,一般是8位,先发送最低位后发送最高位。

        奇偶校验位:奇偶校验位是用来检验数据在传输过程中是否出错。在奇校验时,发送方应使数据位中1的个数与校验位中1的个数为奇数,接收方在接收数据时,对1的个数进行检测,若1的个数不为奇数个,则说明数据在传输过程中存在差错。偶校验则相反。

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

3、波特率简介

        在电子通信领域,波特(Baud)即调制速率,指的是有效数据讯号调制载波的速率,即单位时间内载波调制状态变化的次数。

        波特率表示每秒钟传送码元符号的个数,它是对符号传输速率的一种度量,用单位时间内载波调制状态改变的次数来表示,1波特指每秒传输1个字符。

        数据传输速率使用波特率来表示,单位bps(bits per second),常见的波特率有9600、19200、38400、57600、115200等。例如将串口波特率设置位115200bps,那么传输一个bit需要的时间是1/115200 ≈ 8.68us。


二、UART串口回环实验

1、设计思路

        实验任务:通过电脑端的串口调试助手向FPGA发送数据,FPGA通过串口接收数据并将接受到的数据发送给上位机,实现串口回环功能。

        接收模块(RX):通过检测起始位来表示数据传输的开始,在波特率中间时刻去采样总线上的数据,最后将数据进行串并转换。

        发送模块(TX):将并行数据转换成串行数据,然后在串行数据帧头加上起始位,帧尾加上停止位,发送给上位机。

2、程序代码

        本次程序设计中没有用到奇偶校验位,一帧数据为8bit,停止位为1位,波特率可供选择(代码为115200),FPGA的系统晶振时钟为50MHZ。

        ① 串口接收模块

  1. /*===============================*
  2. filename : uart_rx.v
  3. description : 串口接收模块
  4. time : 2022-12-22
  5. author : 卡夫卡与海
  6. *================================*/
  7. module uart_rx(
  8. input clk ,//时钟 50MHZ
  9. input rst_n ,//复位
  10. input uart_rx ,//rx数据线
  11. input [2:0] baud_sel ,//波特率选择
  12. output reg [7:0] po_data ,//接收的数据
  13. output reg po_flag //数据使能
  14. );
  15. //参数定义
  16. parameter SCLK = 50_000_000;//系统时钟 50MHZ
  17. //波特率选择
  18. parameter BAUD_9600 = SCLK/9600 ,
  19. BAUD_19200 = SCLK/19200 ,
  20. BAUD_38400 = SCLK/38400 ,
  21. BAUD_57600 = SCLK/57600 ,
  22. BAUD_115200 = SCLK/115200;
  23. //信号定义
  24. reg uart_rx_1 ;//同步、打拍
  25. reg uart_rx_2 ;
  26. wire rx_nedge ;//下降沿检测
  27. reg start_flag ;//起始标志
  28. reg work_en ;//工作使能
  29. reg [15:0] cnt_baud ;//波特率计数器
  30. reg [15:0] BAUD_NUM ;
  31. reg bit_flag ;//接收数据使能
  32. reg [3:0] cnt_bit ;//bit计数器
  33. reg [7:0] rx_data ;//数据
  34. reg rx_flag ;//数据标志
  35. //同步、打拍 检测下降沿
  36. always @(posedge clk or negedge rst_n)begin
  37. if(!rst_n)begin
  38. uart_rx_1 <= 1'b1;
  39. uart_rx_2 <= 1'b1;
  40. end
  41. else begin
  42. uart_rx_1 <= uart_rx;
  43. uart_rx_2 <= uart_rx_1;
  44. end
  45. end
  46. assign rx_nedge = uart_rx_2 && ~uart_rx_1;
  47. //start_flag
  48. always @(posedge clk or negedge rst_n)begin
  49. if(!rst_n)begin
  50. start_flag <= 1'b0;
  51. end
  52. else if(rx_nedge && work_en == 1'b0)begin
  53. start_flag <= 1'b1;
  54. end
  55. else begin
  56. start_flag <= 1'b0;
  57. end
  58. end
  59. //work_en
  60. always @(posedge clk or negedge rst_n)begin
  61. if(!rst_n)begin
  62. work_en <= 1'b0;
  63. end
  64. else if(start_flag == 1'b1)begin
  65. work_en <= 1'b1;
  66. end
  67. else if(cnt_bit == 4'd8 && bit_flag == 1'b1)begin
  68. work_en <= 1'b0;
  69. end
  70. else begin
  71. work_en <= work_en;
  72. end
  73. end
  74. //cnt_baud
  75. always @(posedge clk or negedge rst_n)begin
  76. if(!rst_n)begin
  77. cnt_baud <= 16'd0;
  78. end
  79. else if((cnt_baud == BAUD_NUM - 1) || (work_en == 1'b0))begin
  80. cnt_baud <= 16'd0;
  81. end
  82. else if(work_en == 1'b1)begin
  83. cnt_baud <= cnt_baud + 1'b1;
  84. end
  85. end
  86. //BAUD_NUM
  87. always @(*)begin
  88. case(baud_sel)
  89. 3'd0 : BAUD_NUM = BAUD_9600 ;
  90. 3'd1 : BAUD_NUM = BAUD_19200 ;
  91. 3'd2 : BAUD_NUM = BAUD_38400 ;
  92. 3'd3 : BAUD_NUM = BAUD_57600 ;
  93. 3'd4 : BAUD_NUM = BAUD_115200;
  94. default : BAUD_NUM = BAUD_115200;
  95. endcase
  96. end
  97. //bit_flag
  98. always @(posedge clk or negedge rst_n)begin
  99. if(!rst_n)begin
  100. bit_flag <= 1'b0;
  101. end
  102. else if(cnt_baud == (BAUD_NUM >> 1) - 1)begin
  103. bit_flag <= 1'b1;
  104. end
  105. else begin
  106. bit_flag <= 1'b0;
  107. end
  108. end
  109. //cnt_bit
  110. always @(posedge clk or negedge rst_n)begin
  111. if(!rst_n)begin
  112. cnt_bit <= 4'd0;
  113. end
  114. else if((cnt_bit == 4'd8) && (bit_flag == 1'b1))begin
  115. cnt_bit <= 4'd0;
  116. end
  117. else if(bit_flag == 1'b1)begin
  118. cnt_bit <= cnt_bit + 1'b1;
  119. end
  120. end
  121. //rx_data
  122. always @(posedge clk or negedge rst_n)begin
  123. if(!rst_n)begin
  124. rx_data <= 8'b0;
  125. end
  126. else if((cnt_bit>=4'd1)&&(cnt_bit<=4'd8)&&(bit_flag==1'b1))begin
  127. rx_data <= {uart_rx_2,rx_data[7:1]};
  128. end
  129. end
  130. //rx_flag
  131. always @(posedge clk or negedge rst_n)begin
  132. if(!rst_n)begin
  133. rx_flag <= 1'b0;
  134. end
  135. else if((cnt_bit == 4'd8) && (bit_flag == 1'b1))begin
  136. rx_flag <= 1'b1;
  137. end
  138. else begin
  139. rx_flag <= 1'b0;
  140. end
  141. end
  142. //输出
  143. //po_data
  144. always @(posedge clk or negedge rst_n)begin
  145. if(!rst_n)begin
  146. po_data <= 8'b0;
  147. end
  148. else if(rx_flag == 1'b1)begin
  149. po_data <= rx_data;
  150. end
  151. end
  152. //po_flag
  153. always @(posedge clk or negedge rst_n)begin
  154. if(!rst_n)begin
  155. po_flag <= 1'b0;
  156. end
  157. else begin
  158. po_flag <= rx_flag;
  159. end
  160. end
  161. endmodule

        ② 串口发送模块

  1. /*===============================*
  2. filename : uart_tx.v
  3. description : 串口发送模块
  4. time : 2022-12-22
  5. author : 卡夫卡与海
  6. *================================*/
  7. module uart_tx(
  8. input clk ,//时钟 50MHZ
  9. input rst_n ,//复位
  10. input [2:0] baud_sel ,//波特率选择
  11. input [7:0] pi_data ,//数据
  12. input pi_flag ,//数据使能
  13. output reg uart_tx //tx数据线
  14. );
  15. //参数定义
  16. parameter SCLK = 50_000_000;//系统时钟 50MHZ
  17. //波特率选择
  18. parameter BAUD_9600 = SCLK/9600 ,
  19. BAUD_19200 = SCLK/19200 ,
  20. BAUD_38400 = SCLK/38400 ,
  21. BAUD_57600 = SCLK/57600 ,
  22. BAUD_115200 = SCLK/115200;
  23. //信号定义
  24. reg work_en ;//工作使能
  25. reg [15:0] cnt_baud ;//波特率计数器
  26. reg [15:0] BAUD_NUM ;
  27. reg bit_flag ;//bit标志信号
  28. reg [3:0] cnt_bit ;//bit计数器
  29. //work_en
  30. always @(posedge clk or negedge rst_n)begin
  31. if(!rst_n)begin
  32. work_en <= 1'b0;
  33. end
  34. else if(pi_flag == 1'b1)begin
  35. work_en <= 1'b1;
  36. end
  37. else if((cnt_bit == 4'd9) && (bit_flag == 1'b1))begin
  38. work_en <= 1'b0;
  39. end
  40. end
  41. //cnt_baud
  42. always @(posedge clk or negedge rst_n)begin
  43. if(!rst_n)begin
  44. cnt_baud <= 16'd0;
  45. end
  46. else if((work_en == 1'b0) || (cnt_baud == BAUD_NUM - 1))begin
  47. cnt_baud <= 16'd0;
  48. end
  49. else if(work_en == 1'b1)begin
  50. cnt_baud <= cnt_baud + 1'b1;
  51. end
  52. end
  53. //BAUD_NUM
  54. always @(*)begin
  55. case(baud_sel)
  56. 3'd0 : BAUD_NUM = BAUD_9600 ;
  57. 3'd1 : BAUD_NUM = BAUD_19200 ;
  58. 3'd2 : BAUD_NUM = BAUD_38400 ;
  59. 3'd3 : BAUD_NUM = BAUD_57600 ;
  60. 3'd4 : BAUD_NUM = BAUD_115200;
  61. default : BAUD_NUM = BAUD_115200;
  62. endcase
  63. end
  64. //bit_flag
  65. always @(posedge clk or negedge rst_n)begin
  66. if(!rst_n)begin
  67. bit_flag <= 1'b0;
  68. end
  69. else if(cnt_baud == 16'd1)begin
  70. bit_flag <= 1'b1;
  71. end
  72. else begin
  73. bit_flag <= 1'b0;
  74. end
  75. end
  76. //cnt_bit
  77. always @(posedge clk or negedge rst_n) begin
  78. if(!rst_n)begin
  79. cnt_bit <= 4'd0;
  80. end
  81. else if((cnt_bit == 4'd9)&&(bit_flag == 1'b1))begin
  82. cnt_bit <= 4'd0;
  83. end
  84. else if((work_en == 1'b1)&&(bit_flag == 1'b1))begin
  85. cnt_bit <= cnt_bit + 1'b1;
  86. end
  87. end
  88. //输出 uart_tx
  89. always @(posedge clk or negedge rst_n)begin
  90. if(!rst_n)begin
  91. uart_tx <= 1'b1;
  92. end
  93. else if(bit_flag == 1'b1)begin
  94. case(cnt_bit)
  95. 0 : uart_tx <= 1'b0;//起始位
  96. 1 : uart_tx <= pi_data[0];
  97. 2 : uart_tx <= pi_data[1];
  98. 3 : uart_tx <= pi_data[2];
  99. 4 : uart_tx <= pi_data[3];
  100. 5 : uart_tx <= pi_data[4];
  101. 6 : uart_tx <= pi_data[5];
  102. 7 : uart_tx <= pi_data[6];
  103. 8 : uart_tx <= pi_data[7];
  104. 9 : uart_tx <= 1'b1;//停止位
  105. default : uart_tx <= 1'b1;
  106. endcase
  107. end
  108. end
  109. endmodule

        ③ 串口顶层模块

  1. /*==============================*
  2. filename : uart_top.v
  3. description : 串口顶层模块
  4. time : 2022-12-22
  5. author : 卡夫卡与海
  6. *================================*/
  7. module uart_top(
  8. input clk ,
  9. input rst_n ,
  10. input uart_rx ,
  11. output uart_tx
  12. );
  13. //波特率选择
  14. /*常用波特率选择:
  15. baud_sel == 3'd0 :波特率为:9600
  16. baud_sel == 3'd1 :波特率为:19200
  17. baud_sel == 3'd2 :波特率为:38400
  18. baud_sel == 3'd3 :波特率为:57600
  19. baud_sel == 3'd4 :波特率为:115200 */
  20. wire [2:0] baud_sel ;//波特率选择
  21. assign baud_sel = 3'd4;//波特率 = 115200
  22. //信号定义
  23. wire [7:0] data ;
  24. wire flag ;
  25. //模块例化
  26. //串口接收模块
  27. uart_rx u_uart_rx(
  28. /*input */.clk (clk ),
  29. /*input */.rst_n (rst_n ),
  30. /*input */.uart_rx (uart_rx ),
  31. /*input [2:0] */.baud_sel (baud_sel),
  32. /*output reg [7:0] */.po_data (data ),
  33. /*output reg */.po_flag (flag )
  34. );
  35. //串口发送模块
  36. uart_tx u_uart_tx(
  37. /*input */.clk (clk ),
  38. /*input */.rst_n (rst_n ),
  39. /*input [2:0] */.baud_sel (baud_sel),
  40. /*input [7:0] */.pi_data (data ),
  41. /*input */.pi_flag (flag ),
  42. /*output reg */.uart_tx (uart_tx )
  43. );
  44. endmodule

        ④ 串口仿真模块

  1. /*========================================*
  2. filename : uart_top_tb.v
  3. description : 串口顶层模块仿真文件
  4. time : 2022-12-22
  5. author : 卡夫卡与海
  6. *========================================*/
  7. `timescale 1ns/1ns
  8. module uart_top_tb();
  9. reg clk ;
  10. reg rst_n ;
  11. reg rx ;
  12. wire tx ;
  13. //产生时钟
  14. initial begin
  15. clk = 1'b1;
  16. rx = 1'b1;
  17. forever
  18. #10
  19. clk = ~clk;
  20. end
  21. //产生复位
  22. initial begin
  23. rst_n = 1'b0;
  24. #20;
  25. rst_n = 1'b1;
  26. end
  27. //产生激励
  28. task rx_bit(
  29. input [7:0] data
  30. );
  31. integer i;
  32. for(i = 0 ; i < 10 ; i = i + 1)begin
  33. case(i)
  34. 0:rx <= 1'b0;//起始位
  35. 1:rx <= data[0];
  36. 2:rx <= data[1];
  37. 3:rx <= data[2];
  38. 4:rx <= data[3];
  39. 5:rx <= data[4];
  40. 6:rx <= data[5];
  41. 7:rx <= data[6];
  42. 8:rx <= data[7];
  43. 9:rx <= 1'b1;//停止位
  44. endcase
  45. #(434*20);//根据不同的波特率,延时的时间不同
  46. end
  47. endtask
  48. task rx_byte();
  49. integer j;
  50. for(j = 0 ; j < 8 ; j = j + 1)
  51. rx_bit(j);
  52. endtask
  53. initial begin
  54. #200
  55. rx_byte();
  56. end
  57. //模块例化
  58. uart_top u_uart_top(
  59. /*input */.clk (clk ),
  60. /*input */.rst_n (rst_n),
  61. /*input */.uart_rx (rx ),
  62. /*output */.uart_tx (tx )
  63. );
  64. endmodule

3、仿真验证

        通过对我们项目工程的仿真来看,显示接收到的数据为0~7共八个字节的数据,发送给上位机的数据也为0~7,说明我们的功能能够正确的接收并发送数据。

        通过放大波形图可以看到,波特率计数器总共计数434次,波特率为115200,系统时钟为50MHZ,则50_000_000/115200≈434,波特率计数器正确。我们的采样时刻是在波特率计数器的中间时刻去采样,这样能够保证采样到的数据的稳定性,这里的bit标志信号在波特率计数器计数到217时拉高,表示此刻进行数据的采样操作。

        最后就是进行串并转换了,接收模块将串行数据转换成并行数据传给发送模块,而发送模块将接收模块传入的并行数据转换成串行数据通过uart_tx信号线发送给上位机,而上位机通过串口调试助手将接收到的数据串并转换并打印出来显示在屏幕上。


 总结

        UART串口通信协议还是比较简单的,在编写代码时最重要的是思路的完整性,要有一个全局的概念,不能写了一部分就不知道接下来要干嘛了。实现UART串口通信的思路还有很大,也可以通过前面讲过的状态机的方法来实现,还可以在中间调用FIFO IP核,我这里这个算是一个简单的串口回环实现方式了,在后续的项目中还得加以改进和优化。

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

闽ICP备14008679号