FPGA与PC串口自收发通信

转载 2012年03月26日 15:05:03

FPGA与PC串口自收发通信


 


    串口通信其实简单实用,这里我就不多说,只把自己动手写的verilog代码共享下。实现的功能如题,就是FPGA里实现从PC接收数据,然后把接收到的数据发回去。使用的是串口UART协议进行收发数据。上位机用的是老得掉牙的串口调试助手,如下:



点击看大图   


发送数据的波特率可选9600bps,19200bps,38400bps,57600bps,115200bps等,是可调的。发送格式为:1bit起始位,8bit数据,1bit停止位,无校验位。以下的代码有比较详细的注释,经过下载验证,存在误码率(<5%),仅供学习!


代码如下:


(顶层模块):


module my_uart_top(clk,rst_n,rs232_rx,rs232_tx);


 


input clk; // 50MHz主时钟


input rst_n;  //低电平复位信号


input rs232_rx;   // RS232接收数据信号


output rs232_tx;  //  RS232发送数据信号


 


wire bps_start;   //接收到数据后,波特率时钟启动信号置位


wire clk_bps;     // clk_bps的高电平为接收或者发送数据位的中间采样点


wire[7:0] rx_data;   //接收数据寄存器,保存直至下一个数据来到


wire rx_int;  //接收数据中断信号,接收到数据期间始终为高电平


//----------------------------------------------------


speed_select      speed_select( .clk(clk), //波特率选择模块,接收和发送模块复用,不支持全双工通信


                                       .rst_n(rst_n),


                                       .bps_start(bps_start),


                                       .clk_bps(clk_bps)


                                       );


 


my_uart_rx        my_uart_rx(       .clk(clk), //接收数据模块


                                       .rst_n(rst_n),


                                       .rs232_rx(rs232_rx),


                                       .clk_bps(clk_bps),


                                       .bps_start(bps_start),


                                       .rx_data(rx_data),


                                       .rx_int(rx_int)


                                       );


 


my_uart_tx        my_uart_tx(       .clk(clk), //发送数据模块


                                       .rst_n(rst_n),


                                       .clk_bps(clk_bps),


                                       .rx_data(rx_data),


                                       .rx_int(rx_int),


                                       .rs232_tx(rs232_tx),


                                       .bps_start(bps_start)


                                       );


 


endmodule


 


 


module speed_select(clk,rst_n,bps_start,clk_bps);


 


input clk; // 50MHz主时钟


input rst_n;  //低电平复位信号


input bps_start;  //接收到数据后,波特率时钟启动信号置位


output clk_bps;   // clk_bps的高电平为接收或者发送数据位的中间采样点


 


parameter bps9600       = 5207,    //波特率为9600bps


               bps19200   = 2603,    //波特率为19200bps


              bps38400   = 1301,    //波特率为38400bps


              bps57600   = 867, //波特率为57600bps


              bps115200  = 433; //波特率为115200bps


 


parameter bps9600_2 = 2603,


              bps19200_2 = 1301,


              bps38400_2 = 650,


              bps57600_2 = 433,


              bps115200_2 = 216; 


 


reg[12:0] bps_para;  //分频计数最大值


reg[12:0] bps_para_2;    //分频计数的一半


reg[12:0] cnt;           //分频计数


reg clk_bps_r;           //波特率时钟寄存器


 


//----------------------------------------------------------


reg[2:0] uart_ctrl;  // uart波特率选择寄存器


//----------------------------------------------------------


 


always @ (posedge clk or negedge rst_n) begin


    if(!rst_n) begin


           uart_ctrl <= 3'd0;   //默认波特率为9600bps


       end


    else begin


           case (uart_ctrl)  //波特率设置


              3'd0:  begin


                     bps_para <= bps9600;


                     bps_para_2 <= bps9600_2;


                     end


              3'd1:  begin


                     bps_para <= bps19200;


                     bps_para_2 <= bps19200_2;


                     end


              3'd2:  begin


                     bps_para <= bps38400;


                     bps_para_2 <= bps38400_2;


                     end


              3'd3:  begin


                     bps_para <= bps57600;


                     bps_para_2 <= bps57600_2;


                     end


              3'd4:  begin


                     bps_para <= bps115200;


                     bps_para_2 <= bps115200_2;


                     end


              default: ;


              endcase


       end


end


 


always @ (posedge clk or negedge rst_n)


    if(!rst_n) cnt <= 13'd0;


    else if(cnt<bps_para && bps_start) cnt <= cnt+1'b1;  //波特率时钟计数启动


    else cnt <= 13'd0;


 


always @ (posedge clk or negedge rst_n)


    if(!rst_n) clk_bps_r <= 1'b0;


    else if(cnt==bps_para_2 && bps_start) clk_bps_r <= 1'b1;    // clk_bps_r高电平为接收或者发送数据位的中间采样点


    else clk_bps_r <= 1'b0;


 


assign clk_bps = clk_bps_r;


 


endmodule


 


 


module my_uart_rx(clk,rst_n,rs232_rx,clk_bps,bps_start,rx_data,rx_int);


 


input clk; // 50MHz主时钟


input rst_n;  //低电平复位信号


input rs232_rx;   // RS232接收数据信号


input clk_bps;    // clk_bps的高电平为接收或者发送数据位的中间采样点


output bps_start; //接收到数据后,波特率时钟启动信号置位


output[7:0] rx_data; //接收数据寄存器,保存直至下一个数据来到


output rx_int;    //接收数据中断信号,接收到数据期间始终为高电平


 


//----------------------------------------------------------------


reg rs232_rx0,rs232_rx1,rs232_rx2; //接收数据寄存器,滤波用


wire neg_rs232_rx;   //表示数据线接收到下降沿


 


always @ (posedge clk or negedge rst_n) begin


    if(!rst_n) begin


           rs232_rx0 <= 1'b1;


           rs232_rx1 <= 1'b1;


           rs232_rx2 <= 1'b1;


       end


    else begin


           rs232_rx0 <= rs232_rx;


           rs232_rx1 <= rs232_rx0;


           rs232_rx2 <= rs232_rx1;


       end


end


 


assign neg_rs232_rx = rs232_rx2 & ~rs232_rx1; //接收到下降沿后neg_rs232_rx置高一个时钟周期


 


//----------------------------------------------------------------


reg bps_start_r;


reg[3:0]   num;   //移位次数


reg rx_int;   //接收数据中断信号,接收到数据期间始终为高电平


 


always @ (posedge clk or negedge rst_n) begin


    if(!rst_n) begin


           bps_start_r <= 1'bz;


           rx_int <= 1'b0;


       end


    else if(neg_rs232_rx) begin


           bps_start_r <= 1'b1; //启动接收数据


           rx_int <= 1'b1;   //接收数据中断信号使能


           end


    else if(num==4'd12) begin


           bps_start_r <= 1'bz; //数据接收完毕


           rx_int <= 1'b0;      //接收数据中断信号关闭


       end


end


 


assign bps_start = bps_start_r;


 


//----------------------------------------------------------------


reg[7:0] rx_data_r;  //接收数据寄存器,保存直至下一个数据来到


//----------------------------------------------------------------


 


reg[7:0]   rx_temp_data; //但前接收数据寄存器


reg rx_data_shift;   //数据移位标志


 


always @ (posedge clk or negedge rst_n) begin


    if(!rst_n) begin


           rx_data_shift <= 1'b0;


           rx_temp_data <= 8'd0;


           num <= 4'd0;


           rx_data_r <= 8'd0;


       end


    else if(rx_int) begin    //接收数据处理


       if(clk_bps) begin //读取并保存数据,接收数据为一个起始位,8bit数据,一个结束位      


              rx_data_shift <= 1'b1;


              num <= num+1'b1;


              if(num<=4'd8) rx_temp_data[7] <= rs232_rx;    //锁存9bit(1bit起始位,8bit数据)


           end


       else if(rx_data_shift) begin    //数据移位处理   


              rx_data_shift <= 1'b0;


              if(num<=4'd8) rx_temp_data <= rx_temp_data >> 1'b1;  //移位8次,第1bit起始位移除,剩下8bit正好时接收数据


              else if(num==4'd12) begin


                     num <= 4'd0;  //接收到STOP位后结束,num清零


                     rx_data_r <= rx_temp_data;  //把数据锁存到数据寄存器rx_data中


                  end


           end


       end


end


 


assign rx_data = rx_data_r;


 


endmodule


 


 


module my_uart_tx(clk,rst_n,clk_bps,rx_data,rx_int,rs232_tx,bps_start);


 


input clk;    // 50MHz主时钟


input rst_n;  //低电平复位信号


input clk_bps;       // clk_bps的高电平为接收或者发送数据位的中间采样点


input[7:0] rx_data;  //接收数据寄存器


input rx_int;     //接收数据中断信号,接收到数据期间始终为高电平,在次利用它的下降沿来启动发送数据


output rs232_tx;  // RS232发送数据信号


output bps_start; //接收或者要发送数据,波特率时钟启动信号置位


 


//---------------------------------------------------------


reg rx_int0,rx_int1,rx_int2;    //rx_int信号寄存器,捕捉下降沿滤波用


wire neg_rx_int;  // rx_int下降沿标志位


 


always @ (posedge clk or negedge rst_n) begin


    if(!rst_n) begin


           rx_int0 <= 1'b0;


           rx_int1 <= 1'b0;


           rx_int2 <= 1'b0;


       end


    else begin


           rx_int0 <= rx_int;


           rx_int1 <= rx_int0;


           rx_int2 <= rx_int1;


       end


end


 


assign neg_rx_int =  ~rx_int1 & rx_int2;  //捕捉到下降沿后,neg_rx_int拉地保持一个主时钟周期


 


//---------------------------------------------------------


reg[7:0] tx_data; //待发送数据的寄存器


//---------------------------------------------------------


reg bps_start_r;


reg tx_en; //发送数据使能信号,高有效


reg[3:0] num;


 


always @ (posedge clk or negedge rst_n) begin


    if(!rst_n) begin


           bps_start_r <= 1'bz;


           tx_en <= 1'b0;


           tx_data <= 8'd0;


       end


    else if(neg_rx_int) begin   //接收数据完毕,准备把接收到的数据发回去


           bps_start_r <= 1'b1;


           tx_data <= rx_data;  //把接收到的数据存入发送数据寄存器


           tx_en <= 1'b1;       //进入发送数据状态中


       end


    else if(num==4'd11) begin   //数据发送完成,复位


           bps_start_r <= 1'bz;


           tx_en <= 1'b0;


       end


end


 


assign bps_start = bps_start_r;


 


//---------------------------------------------------------


reg rs232_tx_r;


 


always @ (posedge clk or negedge rst_n) begin


    if(!rst_n) begin


           num <= 4'd0;


           rs232_tx_r <= 1'b1;


       end


    else if(tx_en) begin


           if(clk_bps)   begin


                  num <= num+1'b1;


                  case (num)


                     4'd0:  rs232_tx_r <= 1'b0; //发送起始位


                     4'd1:  rs232_tx_r <= tx_data[0];   //发送bit0


                     4'd2:  rs232_tx_r <= tx_data[1];   //发送bit1


                     4'd3: rs232_tx_r <= tx_data[2]; //发送bit2


                     4'd4: rs232_tx_r <= tx_data[3]; //发送bit3


                     4'd5: rs232_tx_r <= tx_data[4]; //发送bit4


                     4'd6: rs232_tx_r <= tx_data[5]; //发送bit5


                     4'd7:  rs232_tx_r <= tx_data[6];   //发送bit6


                     4'd8: rs232_tx_r <= tx_data[7]; //发送bit7


                     4'd9: rs232_tx_r <= 1'b0;   //发送结束位


                      default: rs232_tx_r <= 1'b1;


                     endcase


              end


           else if(num==4'd11) num <= 4'd0;   //复位


       end


end


 


assign rs232_tx = rs232_tx_r;


 


endmodule


转自:http://bbs.ednchina.com/BLOG_ARTICLE_153551.HTM

 

一种串口高效收发思路及方案

摘要:本文在探讨传统数据收发不足之后,介绍如何使用带FIFO的串口来减少接收中断次数,通过一种自定义通讯协议格式,给出帧打包方法;之后介绍一种特殊的串口数据发送方法,可在避免使用串口发送中断的情况下,...
  • zhzht19861011
  • zhzht19861011
  • 2015年09月17日 15:46
  • 7952

PC至FPGA的通信方式选取——Virtual JTAG

近几日都在学习PC和FPGA的通信。选择Virtual JTAG的原因是因为其符合我的硬件资源,而不是因为高效或者低成本。 由于自己之前毫无做过硬件接口的经历,所以这次的通信方式选择完全就是一个无厘头...
  • MineralterMan
  • MineralterMan
  • 2015年07月16日 00:26
  • 2260

FPGA与PC串口自收发通信

FPGA与PC串口自收发通信       串口通信其实简单实用,这里我就不多说,只把自己动手写的verilog代码共享下。实现的功能如题,就是FPGA里实现从PC接收数据,然后把接收到的数...
  • ankwyq
  • ankwyq
  • 2012年03月26日 15:05
  • 1124

使用STM32的串口进行大量数据传输

最近的一个项目需要用单片机和上位机进行大量数据的传输,每次传输的内容为一个1000*7的矩阵,矩阵中的每个元素都是一个4位有效数字,通过字符的形式保存,(如:1.000占5个字节)简单计算一下,100...
  • c12345423
  • c12345423
  • 2016年11月02日 21:51
  • 3446

FPGA实验——串口发送/接收学习笔记

1. 基础知识 1.1 波特率的概念        波特率(Baud rate),指的是信号被调制以后在单位时间内的变化,即单位时间内载波参数变化的次数,如每秒钟传送240个字符,而每个字符格式包含1...
  • CSDN__NET
  • CSDN__NET
  • 2015年07月23日 14:54
  • 1803

FPGA学习笔记之串口收发

仿真测试 timescale 1 ps/ 1 ps define clock_period 20 module uart_ram_vlg_tst();reg clk; reg rst_n; ...
  • qq_40532956
  • qq_40532956
  • 2017年10月26日 11:08
  • 99

串口通信如何传输大数据量

      串口通信应用中,经常会碰到串口速率低不能满足传输需求的情况。如何实现呢? 扩大发送、接收缓冲区? 不行。拆包连续发送? 也不行,也有个极限值。大概不会超过4-6/k, 下面我就写一个我自己...
  • BuilderC
  • BuilderC
  • 2009年10月16日 09:24
  • 2138

基于FPGA的串口通讯设计与实现

繁體 基于FPGA的串口通讯设计与实现 [日期:2012-03-26] 来源: 作者: [字体:大 中 小] ...
  • linuxheik
  • linuxheik
  • 2013年03月14日 12:55
  • 1526

在写一个FPGA的串口通讯问题

最近在弄一个工程用16位串口通讯,结果在2个FPGA板之间测试通讯一切正常,如A板发出100,B板接收后加1 变为101 返回给A A在+1 变为1-2 给B.....模块测试过程一切正常,然后添加到...
  • zmq5411
  • zmq5411
  • 2013年01月19日 20:13
  • 1510

PC机串口通信的工作原理

串口是计算机上一种非常通用设备通信的协议(不要与通用串行总线Universal Serial Bus或者USB混淆)。大多数计算机包含两个基于RS232的串口。串口同时也是仪器仪表设备通用的通信协议;...
  • suxinpingtao51
  • suxinpingtao51
  • 2013年11月25日 12:39
  • 3035
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:FPGA与PC串口自收发通信
举报原因:
原因补充:

(最多只允许输入30个字)