FPGA 串口发送和接收程序

更新一篇博客吧,写一写关于这周自己对串口通信的理解。也会分享一下自己写的代码,

先简单的介绍一下一些概念:

并行通信是指数据的各个位用多条数据线同时进行传输

串行通信是将数据分成一位一位的形式在一条传输线上逐个传输

同步通信带时钟同步信号的数据传输;发送方和接收方在同一时钟的控制下,同步传输数据。

异步通信不带时钟同步信号的数据传输。发送方与接收方使用各自的时钟控制数据的发送和接收过程。

单工   :数据只能沿一个方向传输

半双工:数据传输可以沿两个方向,但需要分时进行

全双工:数据可以同时进行双向传输

下面是串口通信的过程:

串口通信是全双工,有Tx,Rx两条线。Tx是发送,Rx是接收。

波特率:每秒钟传送数据位的数目来表示,9600,就是1秒,可以传送9600bit个数据(0,1)

首先先介绍一下FPGA接收电脑串口助手发过来的数据过程,

首先,如果电脑还没有发数据之前,Rx这条线上一直是高电平,处于空闲状态,如果一旦电脑发数据过来, 这条线就会拉低,持续Period时间,这个Period时间就是一位数据需要的时间,这个时间计算:

例如:如果系统时钟频率clk=50000000hz,波特率baud=9600 ,Period=(50000000/9600)*20ns;

我们检测Rx如果发生下降沿,我们就开始用一个计数器进行计时,如果记到Period这个时间到了,我们进入下个状态,准备接收第1bit数据,如果电脑发过来的数据是1。1的ASCII码就是:49,49对应的二进制为:0011 0001,电脑会在Rx这条线上,先发低位数据,然后是高位数据,所以就是发过来的数据就是:1000_1100。然后FPGA接收数据时,最好在每位中间时刻去保存数据,这个时候的数据比较稳定。按照这样一共接收八位数据。最后一位就是停止位。电脑会发过来1bit的停止位,为高电平。我们一定要在1bit数据之前结束接收状态。(给一个建议,在一半period那个时刻,就结束)。

 

//串口助手设置:波特率9600 无奇偶校验位,8位数据位,一个停止位
//time:2019.11.13.22.41
module uart_rx(
 input            clk    ,
 input            rst_n  ,
 input            uart_rx,
 output reg       uart_rx_done,
 output reg	[7:0] data
 );

   
parameter  CLK_FREQ = 50000000;                 
parameter  UART_BPS = 9600;      //波特率                
localparam PERIOD   = CLK_FREQ/UART_BPS;        
                                              

//接收的数据
reg [7:0] rx_data; 


reg       rx1,rx2;
wire      start_bit;
reg       start_flag;

 reg   [15:0]   cnt0;
 wire           add_cnt0;
 wire           end_cnt0;

 reg   [3:0]    cnt1;
 wire           add_cnt1;
 wire           end_cnt1;
 
 
 //下降沿检测 
assign  start_bit=(rx2)&(~rx1);
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
         rx1<=1'b0;
         rx2<=1'b0; 
    end
    else begin
         rx1<=uart_rx;
         rx2<=rx1;
    end
end

//开始标志位
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        start_flag<=0;
    end
    else if(start_bit) begin
        start_flag<=1;
    end
    else if(end_cnt1) begin
        start_flag<=0;
    end
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt0 <= 0;
    end
	 else if(end_cnt1) begin
	     cnt0 <= 0;
	 end
	 else if(end_cnt0) begin
	     cnt0 <= 0;
	 end
    else if(add_cnt0)begin
        cnt0 <= cnt0 + 1;
    end
end
assign add_cnt0 = start_flag;       
assign end_cnt0 = add_cnt0 && cnt0==PERIOD-1;


always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt1 <= 0;
    end
	 else if(end_cnt1) begin
	     cnt1 <= 0;
	 end
    else if(add_cnt1)begin
        cnt1 <= cnt1 + 1;
    end
end

assign add_cnt1 = end_cnt0 ;    
assign end_cnt1 = (cnt0==((PERIOD-1)/2))&& (cnt1==10-1) ;  

//数据接收
always  @(posedge clk or negedge rst_n)begin
      if(rst_n==1'b0)begin
         rx_data<=8'd0;
      end
      else if(start_flag) begin
		  if(cnt0== PERIOD/2)begin
           case(cnt1)
            4'd1:rx_data[0]<=rx2;
            4'd2:rx_data[1]<=rx2;
            4'd3:rx_data[2]<=rx2;
            4'd4:rx_data[3]<=rx2;
            4'd5:rx_data[4]<=rx2;
            4'd6:rx_data[5]<=rx2;
            4'd7:rx_data[6]<=rx2;
            4'd8:rx_data[7]<=rx2;
		    default:rx_data<=rx_data;   
          endcase
        end
        else begin
            rx_data<=rx_data;
        end
     end
     else begin
         rx_data<=8'd0;
     end   
  end 

  //数据接收
 always  @(posedge clk or negedge rst_n)begin
      if(rst_n==1'b0)begin
           data<=0;
      end
      else if(end_cnt1)begin
          data<=rx_data;
      end  
  end
  
  //接收完成标志
 always  @(posedge clk or negedge rst_n)begin
     if(rst_n==1'b0)begin
         uart_rx_done<=0;
     end
     else if(end_cnt1)begin
         uart_rx_done<=1;
     end
     else begin
        uart_rx_done<=0;
     end
 end
endmodule

发送代码:

//串口助手设置:波特率9600 无奇偶校验位,8位数据位,一个停止位
//time:2019.11.13.22.41
module uart_tx(
 input            clk    ,
 input            rst_n  ,
 output   reg     uart_tx,
 input    [7:0]   data,
 input            tx_start
 );
 
 
parameter   CLK_FREQ = 50000000;                 
parameter   UART_BPS = 9600;     //波特率                 
localparam  PERIOD   = CLK_FREQ/UART_BPS;  
 
reg [7:0] tx_data;        //发送的数据
reg start_tx_flag;        //发送数据标志位

//记算一位数据需要多长时间PERIOD
reg   [15:0]   cnt0;
wire           add_cnt0;
wire           end_cnt0;

//发送几个数据
reg   [3:0]    cnt1;
wire           add_cnt1;
wire           end_cnt1;
 

 //发送标志位
 always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        start_tx_flag<=0;
		  tx_data<=0;
    end
    else if(tx_start) begin
        start_tx_flag<=1;    
		  tx_data<=data;      //把发送的数据存到这里来
		  
    end
    else if(end_cnt1) begin
        start_tx_flag<=0;
    end
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt0 <= 0;
    end
	 else if(end_cnt1) begin
	     cnt0 <= 0;
	 end
	 else if(end_cnt0) begin
	     cnt0 <= 0;
	 end
    else if(add_cnt0)begin
        cnt0 <= cnt0 + 1;
    end
end
assign add_cnt0 = start_tx_flag;       
assign end_cnt0 = add_cnt0 && cnt0==PERIOD-1;   //一位时间

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt1 <= 0;
    end
	 else if(end_cnt1) begin
	     cnt1 <= 0;
	 end
    else if(add_cnt1)begin
        cnt1 <= cnt1 + 1;
    end
end

assign add_cnt1 = end_cnt0 ;    
assign end_cnt1 = (cnt0==((PERIOD-1)/2))&& (cnt1==10-1);   //发送10位,包括停止位,空闲位

always  @(posedge clk or negedge rst_n)begin
      if(rst_n==1'b0)begin
         uart_tx<=1;               //空闲状态
      end
      else if(start_tx_flag) begin
		  if(cnt0==0)begin
           case(cnt1)
            4'd0:uart_tx<=0;         //起始位      
            4'd1:uart_tx<=tx_data[0]; 
				4'd2:uart_tx<=tx_data[1]; 
            4'd3:uart_tx<=tx_data[2];
            4'd4:uart_tx<=tx_data[3];
            4'd5:uart_tx<=tx_data[4];
            4'd6:uart_tx<=tx_data[5];
            4'd7:uart_tx<=tx_data[6];
            4'd8:uart_tx<=tx_data[7];
            4'd9:uart_tx<=1;       //停止位  
				default:;   
          endcase
        end  
      end 
end
endmodule

 

顶层模块:

//注意:
//串口助手设置:波特率9600 无奇偶校验位,8位数据位,一个停止位
//time:2019.11.13.22.41
module uart(
    input  clk    ,
    input  rst_n  ,
	input  uart_rx  ,
	output  uart_tx  ,
   output wire [7:0] seg_sel,
   output wire [7:0] segment
	
);

wire [7:0] data; 
wire uart_rx_done;
																
//数码管				
seg_num seg_disp_uut(
    .clk       (clk),
    .rst_n     (rst_n),
    .seg_sel   (seg_sel),
    .segment   (segment),
	 .data      (data)
 );


//FPGA接收串口助手发来的数据 
uart_rx uart_rx_uut(
     .clk    (clk),
    .rst_n   (rst_n),
    .uart_rx (uart_rx),
    .uart_rx_done(uart_rx_done),
	 .data    (data)
);
	 
//FPGA把接收的数据发送到串口助手上	 	 
uart_tx uart_tx_uut(
   .clk     (clk),
   .rst_n   (rst_n),
   .uart_tx (uart_tx),
   .data    (data),
   .tx_start(uart_rx_done)
 );
	 
endmodule

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 27
    点赞
  • 151
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值