特权同学

FPGA与PC串口自收发通信

0
阅读(3526)

串口通信其实简单实 用,这里我就不多说,只把自己动手写的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