问答
直播中

何立立

9年用户 86经验值
擅长:可编程逻辑 嵌入式技术
私信 关注

【Z-turn Board试用体验】FPGA模拟串口接收模块

本帖最后由 何立立 于 2015-8-11 16:42 编辑

   关于到串口接收,基本上就是“采样”的操作。当你明白“bps”的意义后,对于理解串口的接收,非常的简单。
rx.png

Rx_module.v 是一个组合模块,主要是包含 detect_module.v , bps_module.v 和 rx_control_module.v,3个功能模块。
detect_module.v 的输入是连结物理引脚rx,它主要检测一帧数据的第0位,也就是起始位,然后产生一个高脉冲经 H2L_Sig 给 rx_control_module.v ,以表示一帧数据接收工作已经开始。
rx_bps_module.v 是产生波特率定时的功能模块。换一句话说,它是配置波特率的模块。
当rx_control_module.v拉高Count_Sig, bps_module.v经BPS_CLK对 rx_control_module.v 产生定时。

rx_control_module.v 是核心控制模块。针对串口的配置主要是1帧11位的数据,重视八位数据位,无视起始位,校验位和结束位。当RX_En_Sig 拉高,这个模块就开始工作,它将采集来自 RX_Pin_In 的数据,当完成一帧数据接收的时候,就会产生一个高脉冲给 RX_Done_Sig。
  它到底是如何实现采集的呢?
caiji.png

如上图所示,数据采集都是在“每位数据的中间”进行着。在上图中 RX_Pin_In 输入一帧数据,当 detect_module.v 检测到低电平(起始位),rx_control_module.v 和 rx_bps_module.v 就产生定时(与RX_Pin_In的波特率是一致)。然而rx_bps_module.v 产生的定时是在每个位时间的中间。
在第0位数据,采取忽略的态度,然后接下来的8位数据位都被采集,最后校验位和停止位,却是采取了忽略的操作。有一点你必须好好注意,串口传输数据“从最低位开始,到最高位结束”。
  1. module detect_module
  2. (
  3.     CLK, RSTn,
  4.          RX_Pin_In,
  5.          H2L_Sig
  6. );
  7.     input CLK;
  8.          input RSTn;
  9.          input RX_Pin_In;
  10.          output H2L_Sig;
  11.          /******************************/
  12.          reg H2L_F1;
  13.          reg H2L_F2;
  14.          always @ ( posedge CLK or negedge RSTn )
  15.              if( !RSTn )
  16.                       begin
  17.                                     H2L_F1 <= 1'b1;
  18.                                          H2L_F2 <= 1'b1;
  19.                                 end
  20.                   else
  21.                       begin
  22.                                     H2L_F1 <= RX_Pin_In;
  23.                                          H2L_F2 <= H2L_F1;
  24.                                 end
  25.         /***************************************/
  26.         assign H2L_Sig = H2L_F2 & !H2L_F1;
  27.         /***************************************/
  28. endmodule
detect_module.v 这个功能模块,读者也非常眼熟了吧,而该功能模块是为了检查电平由高变低。当检测到电平又高变低,在第31行就会输出高脉冲。

  1. module rx_bps_module
  2. (
  3.     CLK, RSTn,
  4.          Count_Sig,
  5.          BPS_CLK
  6. );
  7.     input CLK;
  8.          input RSTn;
  9.          input Count_Sig;
  10.          output BPS_CLK;
  11.          /***************************/
  12.          reg [12:0]Count_BPS;
  13.          always @ ( posedge CLK or negedge RSTn )
  14.             if( !RSTn )
  15.                      Count_BPS <= 13'd0;
  16.                  else if( Count_BPS == 13'd1250 )
  17.                      Count_BPS <= 13'd0;
  18.                  else if( Count_Sig )      //Count_Sig为1时候启动计数
  19.                      Count_BPS <= Count_BPS + 1'b1;
  20.                  else
  21.                      Count_BPS <= 13'd0;
  22.          /********************************/
  23.     assign BPS_CLK = ( Count_BPS == 13'd625) ? 1'b1 : 1'b0;
  24.     /*********************************/
  25. endmodule
9600 bps 传输速度使一位数据的周期是 0.000104166666666667s 。以12Mhz时钟频率要得到上述的定时需要:N = 0.000104166666666667 / ( 1 / 12Mhz ) = 1250

  1. module rx_control_module
  2. (
  3.     CLK, RSTn,
  4.          H2L_Sig, RX_Pin_In, BPS_CLK, RX_En_Sig,
  5.     Count_Sig, RX_Data, RX_Done_Sig
  6. );
  7.     input CLK;
  8.          input RSTn;
  9.          input H2L_Sig;
  10.          input RX_En_Sig;
  11.          input RX_Pin_In;
  12.          input BPS_CLK;
  13.          output Count_Sig;
  14.          output [7:0]RX_Data;
  15.          output RX_Done_Sig;
  16.          /********************************************************/
  17.          reg [3:0]i;
  18.          reg [7:0]rData;
  19.          reg isCount;
  20.          reg isDone;
  21.          always @ ( posedge CLK or negedge RSTn )
  22.              if( !RSTn )
  23.                       begin
  24.                           i <= 4'd0;
  25.                                          rData <= 8'd0;
  26.                                          isCount <= 1'b0;
  27.                                          isDone <= 1'b0;         
  28.                                 end
  29.                   else if( RX_En_Sig )
  30.                       case ( i )
  31.                                4'd0 :
  32.                                          if( H2L_Sig ) begin i <= i + 1'b1; isCount <= 1'b1; end//启动波特率定时模块
  33.                                          4'd1 :
  34.                                          if( BPS_CLK ) begin i <= i + 1'b1; end //起始位可以忽略
  35.                                          4'd2, 4'd3, 4'd4, 4'd5, 4'd6, 4'd7, 4'd8, 4'd9 :
  36.                                          if( BPS_CLK ) begin i <= i + 1'b1; rData[i - 2] <= RX_Pin_In; end//采集8位数据
  37.                                          4'd10 :
  38.                                          if( BPS_CLK ) begin i <= i + 1'b1; end//校验位
  39.                                          4'd11 :
  40.                                          if( BPS_CLK ) begin i <= i + 1'b1; end//停止位
  41.                                          4'd12 :
  42.                                          begin i <= i + 1'b1; isDone <= 1'b1; isCount <= 1'b0; end
  43.                                          4'd13 :
  44.                                          begin i <= 4'd0; isDone <= 1'b0; end
  45.                                 endcase
  46.     /********************************************************/
  47.          assign Count_Sig = isCount;
  48.          assign RX_Data = rData;
  49.          assign RX_Done_Sig = isDone;
  50.          /*********************************************************/
  51. endmodule
RX_En_Sig 如果没有拉高,这个模块是不会工作,isCount 标志寄存器,为了使能 rx_bps_module.v 输出采集定时信号;当 rx_control_module.v 模块被使能,该模块就会处于就绪状态,一旦 detect_module.v 检查到由高变低的电平变化,会使步骤i进入第0位采集,然而 isCount 标志寄存器同时也会被设置为逻辑1, rx_bps_module.v 便会开始产生波特率的定时。
  rx_bps_module.v 产生的定时是在“每位数据的中间,第一次的定时采集时第0位数据(起始位),保持忽略态度,定时采集的是八位数据位,每一位数据位会依低位到最高位储存入 rData 寄存器;

最后两位的定时采集(校验位,停留位),同时采取忽略的态度。当进入
  1. 4'd12 :
  2. begin i <= i + 1'b1; isDone <= 1'b1; isCount <= 1'b0; end
  3. 4'd13 :
  4. begin i <= 4'd0; isDone <= 1'b0; end


[size=14.3999996185303px]endcase,这表示一帧数据的采集工作已经结束。最后会产生一个完成信号的高脉冲,同时间 isCount会被设置为逻辑0,亦即停止rx_bps_module.v 的操作。

顶层模块:
  1. module rx_module_demo
  2. (
  3.     CLK, RSTn,
  4.          RX_Pin_In,
  5.          Number_Data
  6. );
  7.     input CLK;
  8.          input RSTn;
  9.          input RX_Pin_In;
  10.     output [3:0]Number_Data;         
  11.          /**********************************/
  12.          wire RX_Done_Sig;
  13.          wire [7:0]RX_Data;
  14.          wire RX_En_Sig;
  15.          rx_module U4
  16.          (
  17.              .CLK( CLK ),
  18.                   .RSTn( RSTn ),
  19.                   .RX_Pin_In( RX_Pin_In ),   
  20.                   .RX_En_Sig( RX_En_Sig ),   
  21.                   .RX_Done_Sig( RX_Done_Sig ),  
  22.                   .RX_Data( RX_Data )           
  23.          );
  24.          /**********************************/
  25.          
  26.          wire [7:0]Output_Data;
  27.          control_module U5
  28.          (
  29.              .CLK( CLK ),
  30.              .RSTn( RSTn ),
  31.                   .RX_Done_Sig( RX_Done_Sig ),  
  32.                   .RX_Data( RX_Data ),         
  33.                   .RX_En_Sig( RX_En_Sig ),      
  34.                   .Number_Data( Output_Data )  
  35.          );
  36.          /***********************************/
  37.          assign Number_Data = Output_Data[3:0];
  38. endmodule
RTL级原理图:
rx_demo.png

自此就完成了串口接收模块了!

回帖(1)

中科院

2015-8-22 21:33:31
非常不错的文章
举报

更多回帖

发帖
×
20
完善资料,
赚取积分