Verilog接收PWM信號並計算佔空比

程序描述:

// Description
//---------------------------------------------------------------------------
// This module reads the pulse width of a repetitive variable duty cycle
// digital input.  Pulse Width Modulated (PWM) inputs are produced by many 
// devices, including certain real world sensors.
// In this case, the specific sensor for which this module was created
// is the Analog Devices ADXL202e (a +/- 2g. dual accelerometer.)
//
// Operation of this module is very simple:
//
// An up-counter (T) counts clocks in one period of the incoming PWM signal.
// The T counter is reset with each rising edge of the incoming PWM waveform.
//
// Another (clock-enabled) up-counter (S) counts the clocks during the time
// when the PWM signal input is high.
// The S counter is also reset at the rising edge of the incoming PWM wave.
//
// To derive the duty cycle as a fraction, a serial divider calculates S/T.
//
// Once the fraction is obtained, the output is presented at "dat_o."
// The signal "stb_o" (strobe output) indicates that a valid reading is
// present on the data bus output.  The strobe will remain high until the
// reading is acknowledged by pulsing "ack_i" for one clock cycle.
//
// If "stb_o" is tied directly to "ack_i" then each time a new reading is 
// obtained a pulse will be produced at "stb_o" which is one single clock wide.
//
// It is important to note that in the absence of any ack_i signal, the unit
// will hold the contents of the last reading, and any new readings from the
// PWM device will be missed.
//
//---------------------------------------------------------------------------
/*		     ____Th____		      ____Th____
		    |	       |	     |		|
		    |          |             |          |
		    |          |_____Tl______|          |

*/


外界輸入的PWM信號,如上圖,高電平時間Th和低電平時間Tl,佔空比計算根據週期T= Th+Tl,佔空比D=Th/T.

本程序關鍵在於計算Th和TL。

方法:實例化兩個計數器,在上升沿清零計數值並啓動計數,

計數器1:增量計數器,PWM高電平計數,記錄高電平時長Th。只在高電平計數。

計數器2:增量計數器,記錄PWM的週期 T。

則PWM的佔空比是D=Th/T。


module pwm_reader (
                    clk_i,
                    clk_en_i,
                    rst_i,
                    pwm_signal_i,
                    ack_i,
                    dat_o,
                    stb_o
                    );

parameter COUNTER_WIDTH_PP     = 10; // Size to hold (max_pwm_period)*(Fclk_i)
parameter DAT_WIDTH_PP         = 8;  // Size of fraction from divider
parameter DIV_COUNT_WIDTH_PP   = 3;  // Must be enough bits to hold the number:
                                     // DAT_WIDTH_PP-1

input clk_i;
input clk_en_i;
input rst_i;
input pwm_signal_i;
input ack_i;

output [DAT_WIDTH_PP-1:0] dat_o;
output stb_o;

// Local signals
reg  [COUNTER_WIDTH_PP-1:0] s_cnt;
reg  [COUNTER_WIDTH_PP-1:0] t_cnt;
reg  pwm_ff_1;
reg  pwm_ff_2;
reg  ack_r;

wire rising_edge;
wire divide_done;

//-----------------------------------------------------------------------
// Try to avoid metastability by running the input signal through
// a D flip flop.
always @(posedge clk_i)
begin
  if (rst_i) pwm_ff_1 <= 0;
  else pwm_ff_1 <= pwm_signal_i;
end

// This is the rising edge detection
always @(posedge clk_i)
begin
  if (rst_i) pwm_ff_2 <= 0;
  else pwm_ff_2 <= pwm_ff_1;
end
assign rising_edge = pwm_ff_1 && ~pwm_ff_2;

// This is period counter
always @(posedge clk_i)
begin
  if (rst_i || rising_edge) t_cnt <= 0;
  else if (clk_en_i) t_cnt <= t_cnt + 1;
end

// This is duty cycle counter
always @(posedge clk_i)
begin
  if (rst_i || rising_edge) s_cnt <= 0;
  else if (clk_en_i && pwm_signal_i) s_cnt <= s_cnt + 1;
end
// This unit provides for the division s_cnt/t_cnt.
// The values in the counters are latched into the divider whenever
// the divide_i input is pulsed.
// The divider ignores the inputs whenever "divide_i" is not pulsed, so
// there is no problem with the counters continually advancing until
// the division is started.
//
// Since the result is expected to be purely fractional, the quotient part
// is all zero, and is thrown away on purpose.  (A special divider could be
// build possibly to avoid this?)
serial_divide_uu #(
                   COUNTER_WIDTH_PP,    // M_PP (dividend width)
                   COUNTER_WIDTH_PP,    // N_PP (divisor width)
                   DAT_WIDTH_PP,        // R_PP (remainder width)
                   COUNTER_WIDTH_PP,    // S_PP (skip integer part of quotient)
                   DIV_COUNT_WIDTH_PP,  // counter bits
                   1                    // need output holding buffers?
                   )
  divider_unit
  (
   .clk_i(clk_i),
   .clk_en_i(1'b1),
   .rst_i(rst_i),
   // New divide is not allowed until previous value is read or acknowledged
   .divide_i(rising_edge && ~stb_o),
   .dividend_i(s_cnt),
   .divisor_i(t_cnt),
   .quotient_o(dat_o),
   .done_o(divide_done)
  );

// This latches the ack_i input, so that at least one pulse of stb_o is produced
// before the ack_i takes it down low again, in case the stb_o output is wired
// directly to the ack_i input.
always @(posedge clk_i)
begin
  if (rst_i || (rising_edge && ~stb_o)) ack_r <= 0;
  else if (stb_o) ack_r <= ack_i;
end

assign stb_o = divide_done && ~ack_r;

endmodule


計算佔空比使用了串行除法運算



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章