FPGA与PC串口自收发通信

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

 

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值