Verilog编写的Uart程序

来源:互联网 发布:淘宝女包店铺名字 编辑:程序博客网 时间:2024/05/29 11:04

原文地址:http://blog.sina.com.cn/s/blog_6202cb4101010cc3.html

串口通信是目前比较重要的一种通信方式,主要是用于计算机和外部的通信。首先简单的介绍一下串口通信的原理:

串口用于ASCII码字符的传输。通信使用3根线完成:(1)地线(GND),(2)发送(TXD),(3)接收(RXD)。由于串口通信是异步的,端口能够在一根线上发送数据同时在另一根线上接收数据。其它线用于握手,但是不是必须的。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验位。对于两个进行通信的端口,这些参数必须匹配: a,波特率:这是一个衡量通信速度的参数。它表示每秒钟传送的bit的个数。例如300波特表示每秒钟发送300个bit。当我们提到时钟周期时,我们就是指波特率例如如果协议需要4800波特率,那么时钟是4800Hz。这意味着串口通信在数据线上的采样率为4800Hz。通常电话线的波特率为14400,28800和36600。波特率可以远远大于这些值,但是波特率和距离成反比。高波特率常常用于放置的很近的仪器间的通信,典型的例子就是GPIB设备的通信。 b,数据位:这是衡量通信中实际数据位的参数。当计算机发送一个信息包,实际的数据不会是8位的,标准的值是5、7和8位。如何设置取决于你想传送的信息。比如,标准的ASCII码是0~127(7位)。扩展的ASCII码是0~255(8位)。如果数据使用简单的文本(标准 ASCII码),那么每个数据包使用7位数据。每个包是指一个字节,包括开始/停止位,数据位和奇偶校验位。由于实际数据位取决于通信协议的选取,术语“包”指任何通信的情况。 c,停止位:用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。 d,奇偶校验位:在串口通信中一种简单的检错方式。有四种检错方式:偶、奇、高和低。当然没有校验位也是可以的。对于偶和奇校验的情况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。例如,如果数据是011,那么对于偶校验,校验位为0,保证逻辑高的位数是偶数个。如果是奇校验,校验位位1,这样就有3个逻辑高位。高位和低位不真正的检查数据,简单置位逻辑高或者逻辑低校验。这样使得接收设备能够知道一个位的状态,有机会判断是否有噪声干扰了通信或者是否传输和接收数据是否不同步

串口通信的数据传输时序如图:

基于Verilog下的串口通信实验

下面简单的介绍一下本实验所实现的功能以及相应的程序思想:

      本实验实现的功能有上位机向开发板发送数据,开发板接收以后通过数码管显示,并且将接收到的数据传回给上位机,通过串口调试助手将返回的数据显示出来。

其模块图如下:

基于Verilog下的串口通信实验

     程序思想:(本程序主要是采用特权同学视频教程的程序,本人只是做一个简单的思路总结)

           1. 通过ISE开发工具,新建4个模块,分别为串口接收波特率产生模块,串口接收模块,串口发送波特率产生模块,串口接收模块,另外再加一个顶层模块。

          2. 顶层模块中只是做模块的声明和端口声明,不做任何的逻辑处理。 

          3. 波特率产生模块(两个模块都一样):主要是通过计数来实现波特率产生,并对数据进行采样。此处采用9600bps,由于1s中有104166个us,系统时钟为50M,即20us,即需要计数为104166/20=5208,因此循环计数0~5207,并且在计数到2603时对数据进行采样。

         4. 串口接收模块:当接收到接收信号置位时,对数据进行采集。

         5. 串口发送模块:当接收到发送信号置位时,对数据进行发送。

    具体程序如下:

/=============================顶层模块=====================================

module uart_top(clk,,rst_n,rs232_rx,rs232_tx,led);

 input clk;    //时钟信号50M
 input rst_n;   //复位信号,低有效
 input rs232_rx;  //数据输入信号
 output rs232_tx;  //数据输出信号
 output [7:0] 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==5207)|| !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== 2603)//当波特率计数到一半时,进行采样存储
   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 [7:0] led;
 reg [7:0] 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;
    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

0 0