作为FPGA的初学者,必学入门的串口程序。

网上有很多关于串口的程序,但大部分都是用于仿真,此程序可在板子上实现,可用上位机发送实现接收。(由于板子不同,要对UCF中的信号进行定义需要更改)

 

主模块:

module uart_top(clk,rst_n,rs232_rx,rs232_tx,led);
 input clk;    //时钟信号50M
 input rst_n;   //复位信号,低有效
 input rs232_rx;  //数据输入信号
 output rs232_tx;  //数据输出信号
 output  led;
 
 wire bps_start1,bps_start2;//
 wire clk_bps1,clk_bps2;
 wire [7:0] rx_data;   //接收数据存储器,用来存储接收到的数据,直到下一个数据接收
 wire rx_int;     //接收数据中断信号,接收过程中一直为高,
 
///////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////子模块端口申明///////////////////////////////////
speed_select_rx     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),
         .bps_start(bps_start1),
         .clk_bps(clk_bps1),
         .rs232_rx(rs232_rx),
         .rx_data(rx_data),
         .rx_int(rx_int),
         .led(led)
        );
speed_select_tx  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),
         .bps_start(bps_start2),
         .clk_bps(clk_bps2),
         .rs232_tx(rs232_tx),
         .rx_data(rx_data),
         .rx_int(rx_int)        
        );
endmodule

 

接收端时钟模块:

module speed_select_rx(clk,rst_n,bps_start,clk_bps);//波特率设定
 input clk;   //50M时钟
 input rst_n;  //复位信号
 input bps_start; //接收到信号以后,波特率时钟信号置位,当接收到uart_rx传来的信号以后,模块开始运行
 output clk_bps; //接收数据中间采样点,
 
// `define BPS_PARA 5207;//9600波特率分频计数值
// `define BPS_PARA_2 2603;//计数一半时采样
 
 reg[12:0] cnt;//分频计数器
 reg clk_bps_r;//波特率时钟寄存器
 
 reg[2:0] uart_ctrl;//波特率选择寄存器
 
 always @(posedge clk or negedge rst_n)
  if(!rst_n)
   cnt<=13'd0;
  else if((cnt==512)|| !bps_start)//判断计数是否达到1个脉宽
   cnt<=13'd0;
  else
   cnt<=cnt+1'b1;//波特率时钟启动
   
 always @(posedge clk or negedge rst_n) begin
  if(!rst_n)
   clk_bps_r<=1'b0;
  else if(cnt== 205)//当波特率计数到一半时,进行采样存储
   clk_bps_r<=1'b1;
  else
   clk_bps_r<=1'b0;
 end
 assign clk_bps = clk_bps_r;//将采样数据输出给uart_rx模块
endmodule

发送端时钟模块:

 

module speed_select_tx(clk,rst_n,bps_start,clk_bps);//波特率设定
 input clk;   //50M时钟
 input rst_n;  //复位信号
 input bps_start; //接收到信号以后,波特率时钟信号置位,当接收到uart_rx传来的信号以后,模块开始运行
 output clk_bps; //接收数据中间采样点,
 
// `define BPS_PARA 5207;//9600波特率分频计数值
// `define BPS_PARA_2 2603;//计数一半时采样
 
 reg[12:0] cnt;//分频计数器
 reg clk_bps_r;//波特率时钟寄存器
 
 reg[2:0] uart_ctrl;//波特率选择寄存器
 
 always @(posedge clk or negedge rst_n)
  if(!rst_n)
   cnt<=13'd0;
  else if((cnt==512)|| !bps_start)//判断计数是否达到1个脉宽
   cnt<=13'd0;
  else
   cnt<=cnt+1'b1;//波特率时钟启动
   
 always @(posedge clk or negedge rst_n) begin
  if(!rst_n)
   clk_bps_r<=1'b0;
  else if(cnt== 205)//当波特率计数到一半时,进行采样存储
   clk_bps_r<=1'b1;
  else
   clk_bps_r<=1'b0;
 end
 assign clk_bps = clk_bps_r;//将采样数据输出给uart_rx模块
endmodule

 

接收模块:

 

module uart_rx(
     clk,
     rst_n,
     bps_start,
     clk_bps,
     rs232_rx,
     rx_data,
     rx_int,
     led
     );
 input clk;   //时钟
 input rst_n;  //复位
 input rs232_rx; //接收数据信号
 input clk_bps;  //高电平时为接收信号中间采样点
 output bps_start; //接收信号时,波特率时钟信号置位
 output [7:0] rx_data;//接收数据寄存器
 output rx_int;  //接收数据中断信号,接收过程中为高
 output  led;
 reg  led;
 reg rs232_rx0,rs232_rx1,rs232_rx2,rs232_rx3;//接收数据寄存器
 wire neg_rs232_rx;//表示数据线接收到下沿
 
 always @(posedge clk or negedge rst_n)begin
  if(!rst_n) begin
   rs232_rx0 <= 1'b0;
   rs232_rx1 <= 1'b0;
   rs232_rx2 <= 1'b0;
   rs232_rx3 <= 1'b0;
  end
  
  else begin
   rs232_rx0 <= rs232_rx;
   rs232_rx1 <= rs232_rx0;
   rs232_rx2 <= rs232_rx1;
   rs232_rx3 <= rs232_rx2;
  end
 end 
 assign neg_rs232_rx = rs232_rx3 & rs232_rx2 & ~rs232_rx1 & ~rs232_rx0;//串口传输线的下沿标志
 reg bps_start_r;
 reg [3:0] num;//移位次数
 reg rx_int;  //接收中断信号
 
 always @(posedge clk or negedge rst_n)
  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'b0;  //接收完毕,改变波特率置位,方便下次接收
   rx_int <= 1'b0;   //接收信号关闭
  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)
   if(!rst_n) begin
     rx_temp_data <= 8'd0;
     num <= 4'd0;
     rx_data_r <= 8'd0;
   end
   else if(rx_int) begin //接收数据处理
    if(clk_bps) begin
     num <= num+1'b1;
     case(num)
       4'd1: rx_temp_data[0] <= rs232_rx;
       4'd2: rx_temp_data[1] <= rs232_rx;
       4'd3: rx_temp_data[2] <= rs232_rx;
       4'd4: rx_temp_data[3] <= rs232_rx;
       4'd5: rx_temp_data[4] <= rs232_rx;
       4'd6: rx_temp_data[5] <= rs232_rx;
       4'd7: rx_temp_data[6] <= rs232_rx;
       4'd8: rx_temp_data[7] <= rs232_rx;
       default: ;
     endcase
     led <= rx_temp_data[2];
    end
    else if(num==4'd12) begin
     num <= 4'd0;   //数据接收完毕
     rx_data_r <= rx_temp_data;
    end          
   end
  assign rx_data = rx_data_r;
endmodule

 

发送模块:

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

 input clk;
 input rst_n;
 input clk_bps;//中间采样点
 input [7:0] rx_data;//接收数据寄存器
 input rx_int;//数据接收中断信号
 output rs232_tx;//发送数据信号
 output bps_start;//发送信号置位
 
 reg rx_int0,rx_int1,rx_int2;//信号寄存器,捕捉下降沿
 wire neg_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;//捕捉下沿
  
  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'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
   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];//数据位 开始
      4'd2: rs232_tx_r <= tx_data[1];
      4'd3: rs232_tx_r <= tx_data[2];
      4'd4: rs232_tx_r <= tx_data[3];
      4'd5: rs232_tx_r <= tx_data[4];
      4'd6: rs232_tx_r <= tx_data[5];
      4'd7: rs232_tx_r <= tx_data[6];
      4'd8: rs232_tx_r <= tx_data[7];
      4'd9: rs232_tx_r <= 1'b1;//数据结束位,1位
      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

 

ucf文件:

net "clk"   loc="C14";
net "rs232_rx"  loc="AF6";
net "rs232_tx"  loc="AF5";
net "led"       loc="N7";
net "rst_n"    loc="D2";

猜你喜欢

转载自blog.csdn.net/mshgocn/article/details/80981376