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

 

基于FPGA的GoogLeNet加速器-卷积层/汇聚层

CNN叫作卷积神经网络,可见卷积在CNN的地位,写出Conv可以说万里长征迈出了第一步。卷积的操作其实很简单,就是若干个for循环的嵌套。可以想象成可以想象一下卷积的过程。 0、复习一下卷积过程 ...
  • lulugay
  • lulugay
  • 2016年12月06日 16:26
  • 289

用verilog语言设计一个2s的呼吸灯

本人第一次写博客,仅供学习参考使用,如有写的不恰当的地方还望友友们指出来。 本设计的目的是产生一个2s的led呼吸灯(呼吸灯原理我就不讲了,大家自行百度)。 程序粗略解读: 本设计中参数m,n共用的一...

基于FPGA的红外遥控解码与PC串口通信

基于FPGA的红外遥控解码与PC串口通信        这是我的《电子设计EDA》的课程设计作业(呵呵,这个月都拿来做大作业了,各种大作业,能发上来和大家分享的我会发上来,否则博客太冷清了)...

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

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

USB协议的下的PC机与FPGA的数据通信

  • 2012年06月13日 11:30
  • 3.63MB
  • 下载

verilog-1 FPGA串口通信问题解析

FPGA串口通信实验 问题待解决中。。。

FPGA基础设计(三):UART串口通信

概述  串口通信也是一个基础实验,是FPGA与电脑、单片机、DSP通信的一种最简单的方案,对通信速率要求不高时可以选择UART通信。您可能已经知道UART时序的控制、波特率的配置等方面的内容,但在实际...

FPGA与STM32串口通信

FPGA中,添加一个TTL串口;与STM32串口通信引脚相连。波特率一致;   FPGA:   void uart_stm32_isr(void *context,alt_u32 id)//中...
  • kobesdu
  • kobesdu
  • 2015年09月28日 17:28
  • 2973
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:FPGA与PC串口自收发通信
举报原因:
原因补充:

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