Electrical – STM32 ADC and timer(s): Measuring moving average peak voltage of PWM signal with varying amplitude

adcstm32timer

I want to measure a moving average of the amplitude of a fix frequency, fixed dutycyle PWM signal with slowly varying amplitude but in a noisy environment (DC motors).

I want to use as little as possible CPU power to do this and have the ADC do this for me. I have an internal signal coming from a timer that is in sync with the signal to measure. I can use this as a trigger.

Below is what I try to do:
enter image description here

  1. use the internal trigger from the timer on the chip that actually generates the PWM signal

  2. wait 1/4th of the onPeriod before taking any samples to filter out rising time or overshoot

  3. then take N samples and filter out noise by taking the arithmetic average
  4. keep a constant moving average of the M past cycles (3 in the drawing, 100 in reality)

  5. Store this result in an ADC register such that the application can always retrieve the last 100ms average

So far, I figured out 1 (interconnect of timer output channel and ADC external trigger) and 3 (oversampling) by reading the reference manual and think I can do 2 by using a basic timer inbetween first timer output and ADC. But I completely struggle with 4 and 5.

Can this be done? I am using STM32L4 and F4/7.

Best Answer

If it were only a small number of averages then I would create a shift register.

Pseudo code for a small number of averages.

function movingAverage(pulseAverage) {
  static register[2];          // 3 registers
  register[2] = register[1];   // data shift
  register[1] = register[0];   // data shift
  register[0] = pulseAverage;  // move the input into the array
  return (register[2] + register[1] + register[0]) / 3;
}

For a larger number such as the 100 in your question this may use up too much precious RAM in a microprocessor. Instead, I would consider running an "eternal average", averaging all the values since the unit powered up. For example, in your 100 cycle average you would calculate the new average as $$ avg = \frac {avg \times 99 + newRead}{100} $$.

This is very simple and requires only one variable. The downside is that it is a little more sluggish in its response. A quick spreadsheet simulation shows the following with averages of the last three cycles and "eternal average" with a weight of 1/3 for the new reading.

enter image description here

Figure 1. Simulation results.

You can try this out quite quickly in a spreadsheet.