串口通信:是一种通用串行数据传输总线,可以实现全双工传输。是一种很常见的通信协议,此次写的程序在基于rs-232通信协议,以前在单片机里也用C51语言写过它的驱动程序,不过现在是用Verilog语言来写它的驱动程序,一种全新的思维模式来驱动它,和单片机大不一样。

   

//程序功能:在上位机(串口调试助手)上发送数据给下位机(PFGA),
//下位机接收到数据后,控制LED灯亮灭,并且以原数据返回给上位机;
module  uart_top
        (
         clk,
         rst_n,
         rs232_rx,
         rs232_tx,
         led      
        );

input   clk;               //系统时钟,50MZH;
input   rst_n;             //复位信号,低电平有效;
input   rs232_rx;          //RS232数据接收信号;
output  rs232_tx;          //RS232数据发送信号;
output [3:0] led;          //LED 灯;



wire      bps_start1, bps_start2;    //接收到数据后,波特率时钟信号启动位;  1:启动;0:停止;

wire      clk_bps1,clk_bps2;         //波特率时钟;
 
wire      rx_done ;                  //数据接收完毕信号,接收到数据期间始终为高电平;
   
wire[7:0] rx_data;                   //接收数据寄存器; 

speed_select   speed_rx              //波特率选择接收模块;
                 (
                   .clk(clk),
                   .rst_n(rst_n),
                   .bps_start(bps_start1),
                   .clk_bps(clk_bps1)
                 
                 ); 


                   
uart_rx     uart_rx                      //串口接收模块;
               (
                 .clk(clk),
                 .rst_n(rst_n),
                 .clk_bps(clk_bps1),
                 .rs232_rx(rs232_rx),
                 .bps_start(bps_start1),
                 .rx_done(rx_done),
                 .rx_data(rx_data),
                 .led(led)
               
               );  
               
speed_select   speed_tx              // 波特率选择发送模块;
                 (
                   .clk(clk),
                   .rst_n(rst_n),
                   .bps_start(bps_start2),
                   .clk_bps(clk_bps2)
                 
                 ); 
                 
uart_tx      uart_tx
               (
                .clk(clk),
                .rst_n(rst_n),
                .clk_bps(clk_bps2),
                .rx_done(rx_done),
                .rx_data(rx_data),
                .bps_start(bps_start2),
                .rs232_tx(rs232_tx)
                              
               ); 
endmodule 




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

input   clk;                   //系统时钟,50MZH;
  
input   rst_n;                 //复位信号,低电平有效;

input   bps_start;             // 接收到数据后,波特率时钟信号启动位;  1:启动;0:停止;

output  clk_bps;               //波特率时钟;

wire   clk;
wire   rst_n;
wire   bps_start;
wire   clk_bps;

parameter  BPS  =5207;         //波特率为9600bps的分频计数值;  (10_0000_0000/9600)ns/20ns=5208

parameter  BPS_2=2603;         //波特率为9600bps的一半,用于采样;

reg [15:0] cnt;
always @ (posedge clk or negedge rst_n)
  begin
        if(!rst_n)
           cnt<=16'd0;
        else if((!bps_start)||(cnt==BPS))
           cnt<=16'd0;
        else
          cnt<=cnt+1'b1;    
            
  end 
                  
reg clk_bps_r;
always @ (posedge clk or negedge rst_n)
  begin
        if(!rst_n)
          clk_bps_r<=1'b0;
        else if(cnt==BPS_2)            //clk_bps_r 高电平时为接收数据位的中间采样点,同时也作为发送数据的数据改变点;
          clk_bps_r<=1'b1;
        else
          clk_bps_r<=1'b0;
           
  end  
assign clk_bps=clk_bps_r;  
                                
endmodule   



module uart_rx            //接收模块,注:接收和发送是相对于下位机而言的;
        (
         clk,
         rst_n,
         clk_bps,
         rs232_rx,
         bps_start,
         rx_done,
         rx_data,
         led 
               
        );
        
input      clk;             //系统时钟:50MHZ;

input      rst_n;           //复位信号,低电平有效;

input      clk_bps;         //波特率时钟;

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

output      bps_start;      //接收到数据后,波特率时钟信号启动位;  1:启动;0:停止;

output      rx_done;        //数据接收完毕信号,接收到数据期间始终为高电平;
   
output[7:0]  rx_data;        //接收数据寄存器;
output[3:0]  led;

reg rs232_rx0;
always @ (posedge clk or negedge rst_n)
  begin
       if(!rst_n)
         rs232_rx0<=1'b0;
       else
         rs232_rx0<=rs232_rx;  
  end
  
reg rs232_rx1;
always @ (posedge clk or negedge rst_n)
  begin
       if(!rst_n)
         rs232_rx1<=1'b0;
       else
         rs232_rx1<=rs232_rx0;  
  end
  

  
wire neg_rs232_rx;
assign  neg_rs232_rx=rs232_rx1&(~rs232_rx0);  //当rs232_rx有1变到0时,说明起始位有效,开始启动串口接收;

reg       bps_start_r;
reg       rx_done;
reg [3:0] num;
always @ (posedge clk or negedge rst_n)
  begin
        if(!rst_n)
          begin
            bps_start_r<=1'bz;
            rx_done<=1'b0;
          end  
        else if(neg_rs232_rx)
          begin
              bps_start_r<=1'b1;   //启动串口准备接收数据;
              rx_done<=1'b1;       //接收数据中断信号时能
          end  
        else if(num==4'd12)        //接收完一帧数据; 
            begin
                bps_start_r<=1'b0; //数据接收完毕,释放波特率启动信号;
                rx_done<=1'b0;     //数据接收完毕,接收数据中断信号关闭;
            end  
            
  end
  
assign  bps_start=bps_start_r;

reg[7:0] rx_data_r;
reg[7:0] rx_temp_data;
always @ (posedge clk or negedge rst_n)
 begin
       if(!rst_n)
         begin
           rx_data_r<=8'b0;
           num<=4'd0;
           rx_temp_data<=8'b0;
         end  
       else if(rx_done)        //接收数据处理;
          begin
               if(clk_bps)     //读取并保存数据,接收数据为:一个起始位,8bit数据,1或2个结束位; 
                 begin
                     num<=num+1'b1;
                     case(num)
                       4'h1   : rx_temp_data[0]<=rs232_rx;
                       4'h2   : rx_temp_data[1]<=rs232_rx;
                       4'h3   : rx_temp_data[2]<=rs232_rx;
                       4'h4   : rx_temp_data[3]<=rs232_rx;
                       4'h5   : rx_temp_data[4]<=rs232_rx;
                       4'h6   : rx_temp_data[5]<=rs232_rx;
                       4'h7   : rx_temp_data[6]<=rs232_rx;
                       4'h8   : rx_temp_data[7]<=rs232_rx;
                       default: ;
                     endcase  
                 end  
                else if(num==4'd12)
                  begin
                        num<=4'd0;                //接收到stop位后结束,num清0;
                        rx_data_r<=rx_temp_data;  //把数据锁存到数据寄存器rx_data_r中
                  end 
          end
    
 end 

assign    rx_data=rx_data_r;

assign    led=rx_data_r[3:0];    


endmodule  
  


module uart_tx          //发送模块,注:接收和发送是相对于下位机而言的
      (
       clk,
       rst_n,
       clk_bps,
       rx_done,
       rx_data,
       bps_start,
       rs232_tx  
      
      );

input       clk;
input       rst_n;
input       clk_bps;
input       rx_done;
input[7:0]  rx_data;

output bps_start;
output rs232_tx;


reg rx_done0;

always @ (posedge clk or negedge rst_n)
  begin
        if(!rst_n)
            rx_done0<=1'h0;
        else
           rx_done0<=rx_done;    
  end

reg rx_done1;

always @ (posedge clk or negedge rst_n)
  begin
        if(!rst_n)
            rx_done1<=1'h0;
        else
           rx_done1<=rx_done0;    
  end

wire  neg_rx_done;

assign neg_rx_done=rx_done1&(~rx_done0);

reg      bps_start_r;
reg[7:0] tx_data;       //发送数据寄存器;
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_done)
          begin
               bps_start_r<=1'b1;     //当rx_dnoe有1变为0时,表示接收数据已完毕,开始发送数据;
               tx_data<=rx_data;
               tx_en<=1'b1;
          end
        else if(num==4'd11)
           begin
               bps_start_r<=1'b0;
               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
               rs232_tx_r<=1'b1;
               num<=4'd0;
          end
        else if(tx_en)
          begin
              if(clk_bps)
                 begin
                     num<=num+1'b1;
                     case(num)
                       4'h0   :   rs232_tx_r<=1'h0;       //发送起始位;
                       4'h1   :   rs232_tx_r<=tx_data[0];
                       4'h2   :   rs232_tx_r<=tx_data[1];
                       4'h3   :   rs232_tx_r<=tx_data[2];
                       4'h4   :   rs232_tx_r<=tx_data[3];
                       4'h5   :   rs232_tx_r<=tx_data[4];
                       4'h6   :   rs232_tx_r<=tx_data[5];
                       4'h7   :   rs232_tx_r<=tx_data[6];
                       4'h8   :   rs232_tx_r<=tx_data[7];
                       4'h9   :   rs232_tx_r<=1'b1;       //发送结束位;
                      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