The F4/F7 have plenty of memory to handle this. You'll need just over 200 bytes (100 samples * 2 bytes each), which is a fraction of even the smallest F4 chips. A moving average is a type of FIR filter and has some advantages to an IIR filter (such as an "infinite average"), such as that it responds more quickly to changes and will settle more quickly on a value if your ADC readings settle on a value. Often, this means it'll be more accurate at any given time than an IIR filter.
There are a few ways to do a moving average:
1) Use the DMA to write the block of 100 samples. Setup the DMA to write to the address of some variable like uint16_t adc_readings[100]
, and set the number of transfers to 100, and the memory increment size the halfword. Also setup the DMA to be in circular mode, so it will automatically wrap around to the beginning of the adc_readings array. Start the ADC and DMA and everything will happen in the background, no CPU needed.
Whenever you need to get the moving average value, you'll need to use the CPU, however. Write a function that takes the sum of all 100 values in the array, and divide by 100.
This method is great if you only need to read the average value every once in a while, since there's no CPU load otherwise.
2) Create an interrupt routine for when the ADC has a value for you. In the interrupt, append the value to your array. Something like this:
uint16_t idx=0;
uint16_t adc_readings[100]={0};
uint16_t moving_average;
void ADC1_IRQHandler(void) {
uint16_t oldest_value = adc_readings[idx]; //grab the value we're about to overwrite
uint16_t new_value = ADC1->DR; //read whatever register you need to get the ADC reading
moving_average = ((100 * moving_average) - oldest_value + new_value) / 100; //this is the shortcut trick!
adc_readings[idx] = new_value;
if (idx++ > 100) idx = 0;
}
The advantage of this method is that the moving average is always kept current and is available any time with no extra CPU load when you need to read it. Also, calculating the average each time requires less operations because of the "trick" where you can just pop off the oldest value and pop on the newest one, and update the average by an amount proportional to their difference. The disadvantage is that the CPU is involved with every ADC reading, each time the ADC IRQ is called. So if you go several hundred readings without needing to know the moving average, you'll have some unnecessary CPU load. But if you need to know the moving average quickly, especially if you need to know it with every new ADC reading, then this method wins.
Tip: If your moving average size is a power of 2, such as 128 or 64, then the moving average will be much faster to compute since the divide can be a simple bit shift.