当前位置:   article > 正文

FPGA实现串口通信(接收+发送)_fpga并行24个串口收发数据

fpga并行24个串口收发数据

 程序中使用到串口通信,mcu串口不够用了,使用fpga扩展出一路串口供使用,特此记录。

功能要求:

        1、串口波特率自定义

        2、串口同时支持发送及接收

        3、串口接收到的数据传输至mcu解析,mcu将要发送的数据送至fpga发出

        4、未使用到校验位,仅预留(未作)

实现思路:

        串口通信过程中状态明确,选择三段式状态机进行实现

        状态包含:IDLE(空闲)START(起始)DATA(数据移位)CHECK(校验)STOP(停止)

        模块分为3部分,接收部分,发送部分,顶层模块

实现代码:

        接收模块

  1. /*
  2. 将rx引脚上的串口数据送至data_o[7:0]寄存器,data_en高有效
  3. */
  4. module uart_rx
  5. #(parameter BPS_WID = 9,
  6. parameter BPS_CNT = 434) //115200
  7. (
  8. input sys_clk,
  9. input rst_n,
  10. input rx,
  11. output reg[7:0]data_o,
  12. output data_en
  13. );
  14. localparam IDLE = 5'b00000,
  15. START= 5'b00001,
  16. DATA = 5'b00010,
  17. CHECK= 5'b00100,
  18. STOP = 5'b01000,
  19. WAIT = 5'b10000; //校验错误时进入
  20. /*******************************************************
  21. RX_module
  22. *******************************************************/
  23. wire rx_str_n,rx_bit;
  24. reg [2:0]rx_r;
  25. reg [BPS_WID-1:0] baud_cnt;
  26. assign rx_bit = rx_r[1];
  27. // assign clk_bps = baud_cnt < BPS_CNT/2;
  28. //消除亚稳态,产生起始下降沿
  29. assign rx_str_n = (!rx_r[1])&(rx_r[2]);
  30. always @(posedge sys_clk) begin
  31. rx_r <= {rx_r[1:0],rx};
  32. end
  33. //--1
  34. (*noprune*)reg [4:0]cur_st,nxt_st;
  35. reg [7:0]rx_data;
  36. always @(posedge sys_clk or negedge rst_n) begin
  37. if(!rst_n) cur_st <= IDLE;
  38. else cur_st <= nxt_st;
  39. end
  40. //--2
  41. reg [3:0]bit_cnt;
  42. always @(*) begin
  43. case(cur_st)
  44. IDLE :begin
  45. if(rx_str_n) nxt_st = START;
  46. else nxt_st = IDLE;
  47. end
  48. START :begin
  49. if(baud_cnt == BPS_CNT/2) nxt_st = DATA;
  50. else nxt_st = START;
  51. end
  52. DATA :begin
  53. if(bit_cnt < 'd9) nxt_st = DATA;
  54. else nxt_st = CHECK;
  55. end
  56. CHECK :begin
  57. if(baud_cnt == BPS_CNT/2) nxt_st = STOP;
  58. else nxt_st = CHECK;
  59. end
  60. STOP :
  61. if(baud_cnt == BPS_CNT/2) nxt_st = IDLE;
  62. else nxt_st = STOP;
  63. WAIT : nxt_st <= IDLE;
  64. default : nxt_st <= IDLE;
  65. endcase
  66. end
  67. //--3
  68. always @(posedge sys_clk) begin
  69. if((nxt_st == START)||(nxt_st == DATA)||
  70. (nxt_st == CHECK)||(nxt_st == STOP))begin
  71. if(baud_cnt < BPS_CNT - 'd1)
  72. baud_cnt <= baud_cnt + 'd1;
  73. else
  74. baud_cnt <= 'd0;
  75. end
  76. end
  77. always @(posedge sys_clk or negedge rst_n) begin
  78. if(!rst_n)begin
  79. rx_data <= 'd0;
  80. bit_cnt <= 'd1;
  81. end
  82. else if((baud_cnt == BPS_CNT/2)&&(nxt_st == DATA)&&(bit_cnt < 'd9))begin
  83. rx_data[bit_cnt - 'd1] <= rx_bit;
  84. bit_cnt <= bit_cnt + 'd1;
  85. end
  86. else if(nxt_st == STOP)begin
  87. data_o <= rx_data;
  88. end
  89. else if(nxt_st == START)begin
  90. rx_data <= 'd0;
  91. bit_cnt <= 'd1;
  92. end
  93. end
  94. //--数据有效信号
  95. wire back_end;
  96. reg [1:0]en_dely;
  97. assign back_end = (cur_st==STOP);
  98. assign data_en = en_dely[1]&(!en_dely[0]);
  99. always @(posedge sys_clk) begin
  100. en_dely <= {en_dely[0],back_end};
  101. end
  102. endmodule

        发送模块

  1. /*
  2. data_i[7:0]寄存器的数据以串口格式发送到tx引脚上,
  3. data_en高时,data_i[7:0]数据有效
  4. busy: 1:模块忙 0:无操作
  5. */
  6. module uart_tx
  7. #(parameter BPS_WID = 9,
  8. parameter BPS_CNT = 434) //115200
  9. (
  10. input sys_clk ,
  11. input rst_n ,
  12. input [7:0]data_i ,
  13. input data_en ,
  14. output reg tx ,
  15. output reg busy //1:busy 0:no busy
  16. );
  17. localparam IDLE = 5'b00000,
  18. START= 5'b00001,
  19. DATA = 5'b00010,
  20. CHECK= 5'b00100,
  21. STOP = 5'b01000;
  22. /*******************************************************
  23. TX_module
  24. *******************************************************/
  25. reg [BPS_WID-1:0] baud_cnt;
  26. reg [7:0]tx_data;
  27. reg str_p,par_data = 1'b1;
  28. //预存数据
  29. always @(posedge sys_clk) begin
  30. if(data_en &(!busy))begin
  31. tx_data <= data_i;
  32. str_p <= 'd1;
  33. end
  34. else
  35. str_p <= 'd0;
  36. end
  37. //--1
  38. (*noprune*)reg [4:0]cur_st,nxt_st;
  39. always @(posedge sys_clk or negedge rst_n) begin
  40. if(!rst_n) cur_st <= IDLE;
  41. else cur_st <= nxt_st;
  42. end
  43. //--2
  44. reg [3:0]bit_cnt;
  45. always @(*) begin
  46. case(cur_st)
  47. IDLE :begin
  48. if(str_p) nxt_st = START;
  49. else nxt_st = IDLE;
  50. end
  51. START :begin
  52. if(baud_cnt < BPS_CNT - 'd1) nxt_st = START;
  53. else nxt_st = DATA;
  54. end
  55. DATA :begin
  56. if(bit_cnt < 'd9) nxt_st = DATA;
  57. else nxt_st = CHECK;
  58. end
  59. CHECK :begin
  60. if(baud_cnt < BPS_CNT - 'd1) nxt_st = CHECK;
  61. else nxt_st = STOP;
  62. end
  63. STOP :begin
  64. if(baud_cnt < BPS_CNT - 'd1) nxt_st = STOP;
  65. else nxt_st = IDLE;
  66. end
  67. default : nxt_st <= IDLE;
  68. endcase
  69. end
  70. //--3
  71. always @(posedge sys_clk) begin
  72. if((nxt_st == START)||(nxt_st == DATA)||
  73. (nxt_st == CHECK)||(nxt_st == STOP))begin
  74. if(baud_cnt < BPS_CNT - 'd1)
  75. baud_cnt <= baud_cnt + 'd1;
  76. else
  77. baud_cnt <= 'd0;
  78. end
  79. else
  80. baud_cnt <= 'd0;
  81. end
  82. always @(posedge sys_clk) begin
  83. case(nxt_st)
  84. IDLE : tx <= 1'b1;
  85. START : tx <= 1'b0;
  86. DATA : if(bit_cnt > 'd0) tx <= tx_data[bit_cnt - 'd1];
  87. CHECK : tx <= par_data;
  88. STOP : tx <= 1'b1;
  89. default : tx <= 1'b1;
  90. endcase
  91. end
  92. always @(posedge sys_clk) begin
  93. if(cur_st == IDLE) busy <= 'd0;
  94. else busy <= 'd1;
  95. end
  96. always @(posedge sys_clk or negedge rst_n) begin
  97. if(!rst_n)begin
  98. bit_cnt <= 'd0;
  99. end
  100. else if((baud_cnt == BPS_CNT-'d1)&&(nxt_st == DATA)&&(bit_cnt < 'd9))begin
  101. bit_cnt <= bit_cnt + 'd1;
  102. end
  103. else if(nxt_st == START)begin
  104. bit_cnt <= 'd0;
  105. end
  106. end
  107. endmodule

        顶层测试模块

功能描述:fpga将串口接收模块收到的数据“+1”后,送至串口发送模块,发送至电脑串口助手

例如:串口助手发送0x11,接收到0x12        (0x11+0x01)

  1. module uart_mt(
  2. input sys_clk,
  3. input rst_n,
  4. // input [7:0]tx_data,
  5. // output [7:0]rx_data,
  6. input rx,
  7. output tx,
  8. output tx_busy
  9. );
  10. localparam BAUD_WID = 11;
  11. localparam BAUD_CNT = 1302; //38400
  12. //----------------
  13. wire [7:0]rx_d;
  14. wire rx_e;
  15. //----------------
  16. wire tx_e;
  17. reg [3:0]dely_cnt;
  18. reg [7:0]rx_data,tx_data;
  19. reg tx_str;
  20. always @(posedge sys_clk) begin
  21. if(rx_e) begin
  22. rx_data <= rx_d;
  23. tx_str <= 1'd1;
  24. dely_cnt <= 'd0;
  25. end
  26. else if((tx_str)&&(dely_cnt < 'd5))
  27. dely_cnt <= dely_cnt + 'd1;
  28. else if(dely_cnt == 'd5)
  29. tx_str <= 'd0;
  30. end
  31. always @(posedge sys_clk) begin
  32. tx_data <= rx_data + 'd1;
  33. end
  34. assign tx_e = (dely_cnt == 'd4);
  35. uart_tx #(
  36. .BPS_WID (BAUD_WID ),
  37. .BPS_CNT (BAUD_CNT )
  38. )u1_uart_tx(
  39. .sys_clk (sys_clk ),
  40. .rst_n (rst_n ),
  41. .data_i (tx_data ),
  42. .data_en (tx_e ),
  43. .tx (tx ),
  44. .busy () //1:busy 0:no busy
  45. );
  46. uart_rx #(
  47. .BPS_WID (BAUD_WID ),
  48. .BPS_CNT (BAUD_CNT )
  49. )u1_uart_rx(
  50. .sys_clk (sys_clk ),
  51. .rst_n (rst_n ),
  52. .rx (rx ),
  53. .data_o (rx_d ),
  54. .data_en (rx_e )
  55. );
  56. endmodule

功能验证:

数据收发验证

signaltap II实物调试波形

串口助手界面

波特率更改验证

波特率:38400

波特率:115200

总结:

如若需要实现连续接收,则可搭配ram或fifo,对数据进行保存。

如需实现连续发送,则将数据写入ram中,持续监测busy信号,再no busy时将数据读出,送入发送模块即可。

至此,串口收发均验证完成,数据收发正常,此次功能简单,未进行仿真验证,但有signtap波形图可以看出功能符合预期,后续有时间再增加modelsim仿真波形图

欢迎大家互相交流

补充:modelsim仿真图

接收数据

测试数据为:0x69

符合预期

发送数据

测试数据为:0xdb

波形数据为0b 1101 1011,即(0xdb),符合预期

注:之前的代码发送模块发送数据时第一位数据发送前2个clk对TX数据线操作有误,实物调试无法观测到,已修正。

#生活很苦 自己加糖# 习惯不贪心,太好的东西总是留不住

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号