UART的回环实例

举报
李锐博恩 发表于 2021/07/15 04:19:39 2021/07/15
【摘要】 根据代码综合出来的RTL电路图可以当成原理框图来看: 程序模块分为顶层Uart_top、发送模块uart_tx、接收模块uart_rx以及时钟产生模块clk_div。uart_rx将收到的包解析出8位数据,再传送给uart_tx发出,形成回环。参考时钟为100Mhz,波特率为9600bps。例子使用最简单的串口设置,没有校验位。 各模块程序如下: 顶层: `tim...

根据代码综合出来的RTL电路图可以当成原理框图来看:

程序模块分为顶层Uart_top、发送模块uart_tx、接收模块uart_rx以及时钟产生模块clk_div。uart_rx将收到的包解析出8位数据,再传送给uart_tx发出,形成回环。参考时钟为100Mhz,波特率为9600bps。例子使用最简单的串口设置,没有校验位。

各模块程序如下:

顶层:


  
  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 20:44:47 03/04/2019
  7. // Design Name:
  8. // Module Name: Uart
  9. //
  10. //
  11. module Uart_top(
  12. output txd,
  13. input rxd,
  14. input clk
  15. );
  16. wire clk_9600;
  17. wire receive_ack;
  18. wire [7:0] data;
  19. //串口发送模块
  20. uart_tx uart_tx_u1(
  21. .clk(clk_9600),
  22. .txd(txd),
  23. .rst(1),
  24. .data_o(data),
  25. .receive_ack(receive_ack)
  26. );
  27. //串口接收模块
  28. uart_rx uart_rx_u1(
  29. .rxd(rxd),
  30. .clk(clk_9600),
  31. .receive_ack(receive_ack),
  32. .data_i(data)
  33. );
  34. //时钟模块
  35. clk_div clk_div_u1(
  36. .clk(clk),
  37. .clk_out(clk_9600)
  38. );
  39. endmodule

接收模块:


  
  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 20:52:35 03/04/2019
  7. // Design Name:
  8. // Module Name: uart_rx
  9. // Project Name:
  10. //
  11. module uart_rx(
  12. input rxd,
  13. input clk,
  14. output receive_ack,
  15. output reg [7:0] data_i
  16. );
  17. //串口接收状态机分为三个状态:等待,接收,接收完成
  18. localparam IDLE = 0,
  19. RECEIVE = 1,
  20. RECEIVE_END = 2;
  21. reg [3:0] cur_st,nxt_st; //状态机变量
  22. reg [4:0] count;
  23. always@(posedge clk) begin
  24. cur_st <= nxt_st;
  25. end
  26. always@( * ) begin
  27. nxt_st = cur_st;
  28. case(cur_st)
  29. IDLE: if(!rxd) nxt_st = RECEIVE; //接收到开始信号,开始接收数据
  30. RECEIVE: if(count == 7) nxt_st = RECEIVE_END; //八位数据接收计数
  31. RECEIVE_END: nxt_st = IDLE; //接收完成
  32. default: nxt_st = IDLE;
  33. endcase
  34. end
  35. always@(posedge clk) begin //接收数据计数
  36. if(cur_st == RECEIVE) count <= count + 1;
  37. else if(cur_st == IDLE|cur_st == RECEIVE_END)
  38. count <= 0;
  39. end
  40. always@(posedge clk) begin //从高到底发送数据
  41. if(cur_st == RECEIVE) begin
  42. data_i[6:0] <= data_i[7:1];
  43. data_i[7] <= rxd;
  44. end
  45. end
  46. assign receive_ack = (cur_st == RECEIVE_END)?1:0; //接收完成时回复信号 1,否则 0
  47. endmodule

发送模块:


  
  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 20:52:52 03/04/2019
  7. // Design Name:
  8. // Module Name: uart_tx
  9. // Project Name:
  10. //
  11. module uart_tx(
  12. input [7:0] data_o,
  13. output reg txd,
  14. input clk,
  15. input rst,
  16. input receive_ack
  17. );
  18. //发送状态机分为四个状态:等待、发送起始位、发送数据、发送结束
  19. localparam IDLE = 0,
  20. SEND_START = 1,
  21. SEND_DATA = 2,
  22. SEND_END = 3;
  23. reg [3:0] cur_st,nxt_st;
  24. reg [4:0] count;
  25. reg [7:0] data_o_tmp;
  26. always@(posedge clk)
  27. cur_st <= nxt_st;
  28. always@(*) begin
  29. nxt_st = cur_st;
  30. case(cur_st)
  31. IDLE: if(receive_ack == 1) nxt_st = SEND_START; //接收完成时开始发送数据
  32. SEND_START: nxt_st = SEND_DATA; //发送起始位
  33. SEND_DATA: if(count == 7) nxt_st = SEND_END; //发送八位数据
  34. SEND_END: if(receive_ack == 1) nxt_st = SEND_START; //发送结束
  35. default: nxt_st = IDLE;
  36. endcase
  37. end
  38. always@(posedge clk)
  39. if(cur_st == SEND_DATA)
  40. count <= count + 1;
  41. else if(cur_st == IDLE|cur_st == SEND_END)
  42. count <= 0;
  43. always@(posedge clk) //发送低位到高位
  44. if(cur_st == SEND_START)
  45. data_o_tmp <= data_o; //将发送数据导入变量
  46. else if(cur_st == SEND_DATA)
  47. data_o_tmp[6:0] <= data_o_tmp[7:1]; //每发送一位数据后将data_o_tmp右移一位,便于下一个数据的发送
  48. always@(posedge clk)
  49. if(cur_st == SEND_START)
  50. txd <= 0;
  51. else if(cur_st == SEND_DATA)
  52. txd <= data_o_tmp[0]; //由于每次发送后右移,所以每次发送最低位
  53. else if(cur_st == SEND_END)
  54. txd <= 1;
  55. endmodule

时钟分频模块:


  
  1. `timescale 1ns / 1ps
  2. //
  3. // Company:
  4. // Engineer:
  5. //
  6. // Create Date: 20:53:08 03/04/2019
  7. // Design Name:
  8. // Module Name: clk_div
  9. // Project Name:
  10. //
  11. module clk_div(
  12. input clk,
  13. output reg clk_out
  14. );
  15. localparam Baud_Rate = 9600;
  16. localparam Div_Num = 'd100_000_000/Baud_Rate; //分频数为时钟速率除以波特率
  17. reg [15:0] num;
  18. always@(posedge clk)
  19. if(num == Div_Num) begin
  20. num <= 0;
  21. clk_out <= 1;
  22. end
  23. else begin
  24. num <= num + 1;
  25. clk_out <= 0;
  26. end
  27. endmodule

有关串口协议的东西还是第一次涉猎,还有些没有弄懂,例如系统时钟和波特率时钟之间的关系?这里记录再此,回头再看吧。

这种忙成狗子的日子。


更新:2019/05/27

系统时钟和波特率之间的关系是一种分频关系,具体的实现方法见博文:

RS232 波特率时钟产生方法?

将我最新产生的波特率时钟的参数化方法记录于此:


  
  1. module BaudGen #(
  2. parameter Clkfrequency = 25000_000,
  3. parameter Baud = 115200,
  4. parameter Oversampling = 8
  5. )
  6. (
  7. input clk,
  8. input enable,
  9. output BaudTick
  10. );
  11. parameter Ratio = Clkfrequency/(Baud*Oversampling);
  12. parameter AddWidth = 16;
  13. parameter Inc = (1<<AddWidth)/Ratio;
  14. reg [AddWidth:0] acc = 0;
  15. always@(posedge clk) begin
  16. if(enable) begin
  17. acc <= acc[AddWidth - 1 : 0] + Inc;
  18. end
  19. else begin
  20. acc <= Inc;
  21. end
  22. end
  23. assign BaudTick = acc[AddWidth];
  24. endmodule

 

 

文章来源: reborn.blog.csdn.net,作者:李锐博恩,版权归原作者所有,如需转载,请联系作者。

原文链接:reborn.blog.csdn.net/article/details/88219653

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。