Hi folks,
I don't know how to make an moving average filter.
What I don't understand is how to store the data coming from the ADC10MEM,
(to fill a vector for example).
Thanks for helping me.
This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
Hi folks,
I don't know how to make an moving average filter.
What I don't understand is how to store the data coming from the ADC10MEM,
(to fill a vector for example).
Thanks for helping me.
abbiatishow said:I don't know how to make an moving average filter.
In its simplest form, a moving average, or rolling average, is nothing more than taking the sum of the N latest samples and dividing by N. This particular implementation allows each sample to have the same weight. There are other modified methods which allow you to weight samples differently, perhaps to place heavier emphasis on the most recent samples over the older ones.
For example, if you wanted to perform a filter on the last 4 samples, the equation would be : SUM = (x[n] + x[n-1] + x[n-2] + x[n-3])/4
abbiatishow said:What I don't understand is how to store the data coming from the ADC10MEM,
(to fill a vector for example).
The ADC10MEM register holds the latest conversion for a particular channel. This will only hold 1 sample. Therefore, your software, perhaps an Interrupt service routine, would copy the value stored in this register and place it in an array. An example is below based on the above example.
Then in your background task, you perform the filter above.
#define NUM_SAMPLES 4
short samples[NUM_SAMPLES] ;
int sample_index = 0 ;
void ADC_interrupt (void)
{
samples[sample_index] = ADC10MEM ;
sample_index = (sample_index + 1) % NUM_SAMPLES ; // modulus 4 (wrap around)
}
Thank you for the quick reply, butI still don't understand how to do it.
I have to adquire 3 analog signals coming from the axis of my acceletometer,
read them and calculate mean , variance etc.
This is part of my code:
void get_accelerations()
{
float battery_voltage;
ADC10AE0 = 0xff;
ADC10CTL0 = 0x00;
ADC10CTL0 = SREF_0 + ADC10SHT_2 + ADC10ON;
ADC10CTL1 = INCH_0; // input 1 of 3 // these lines have to be repeated for every axis
ADC10MEM = 0xffff;
ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start
do{}while(ADC10MEM == 0xffff);
voltage_x = ((ADC10MEM * battery_voltage) / 1023.0);
//
// filtering here... take the last 16 samples of voltage_x and make some calculation
// for example mean or variance or some classification etc
//
// SUM = (x[n] + x[n-1] + x[n-2] + x[n-3])/4 ??? how to do it ??
}
you told me to add this code.. but I didn t understand exactly what to do...
#define NUM_SAMPLES 4
short samples[NUM_SAMPLES] ;
int sample_index = 0 ;
void ADC_interrupt (void)
{
samples[sample_index] = ADC10MEM ;
sample_index = (sample_index + 1) % NUM_SAMPLES ; // modulus 4 (wrap around)
}
Thank you so much for helping me....
The example code I provided assumed you were using interrupts on the ADC. Since you are not, no big deal.
The pseudo-code (ie. SUM = (x[n] + x[n-1] + x[n-2] + x[n-3])/4) was meant to describe what you need to implement. Fundamentally, you need to store the last 4 samples, one of which is the most recent sample acquired. x[n] would represent the latest sample. x[n-3] represents the 3rd oldest sample. Therefore, this is typically implemented as an array of samples. You will need to maintain pointers into this array, or indexes, to know where the most recent sample is in relation to this array. Effectively the suggestion is to implement a circular buffer where the newest sample replaces the oldest sample in the array.
short samples[NUM_SAMPLES] ;
Represents the array of samples.
int sample_index = 0 ;
Represents the circular buffer index which points to the position in the array where the newest sample is to be placed.
A modification of your code would be the following.
short voltage_history[4] ;
int voltage_index = 0 ;
void get_accelerations()
{
float battery_voltage;
int voltage_ave = 0 ;
int i ;
ADC10AE0 = 0xff;
ADC10CTL0 = 0x00;
ADC10CTL0 = SREF_0 + ADC10SHT_2 + ADC10ON;
ADC10CTL1 =
INCH_0; // input 1 of
3 // these lines have to be repeated for every axis
ADC10MEM = 0xffff;
ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start
while(!(ADC10CTL0 & ADC10IFG)); // Wait for conversion to complete
voltage_x = ((ADC10MEM * battery_voltage) / 1023.0);
//
// filtering here... take the last 16 samples of voltage_x and make some calculation
// for example mean or variance or some classification etc
//
// SUM = (x[n] + x[n-1] + x[n-2] + x[n-3])/4
// Place latest voltage into history circular buffer
voltage_history[voltage_index] = voltage_x ;
voltage_index = (voltage_index + 1) % 4 ;
for (i=0; i<4; i++)
{
voltage_ave = voltage_ave + voltage_history[i] ;
}
voltage_ave = voltage_ave >> 2 ; // divide by 4 by right shifting 2
}
So the code has to be like this (or not) ?
void ADC_interrupt (void);
int voltage_ave = 0 ; // int or float ?
int i ;
short voltage_history[4] ;
int voltage_index = 0 ;
void get_accelerations()
{
ADC10CTL0 = 0x00;
ADC10CTL0 = SREF_0 + ADC10SHT_2 + ADC10ON;
ADC10CTL1 = INCH_0; // input A0(?) X
ADC10MEM = 0xffff;
ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start
do{}while(ADC10MEM == 0xffff);
while(!(ADC10CTL0 & ADC10IFG)); // Wait for conversion to complete
voltage_x = ((ADC10MEM * battery_voltage) / 1023.0);
// Place latest voltage into history circular buffer
voltage_history[voltage_index] = voltage_x ;
voltage_index = (voltage_index + 1) % 4 ;
for (i=0; i<4; i++)
{
voltage_ave = voltage_ave + voltage_history[i] ;
}
} // end get_accelerations()
One more question:
in this way, doing the shift, voltage_ave can just be a integer value.
If I want a float value.. what to do?
Is it possible to do: voltage_ave = voltage_ave /4 ?
Thanks again
An example implementation is below. Note, since you are starting the conversion of the ADC and polling for completion within the get_accelerations() routine, you do not need to implement and ADC interrupt service routine.
int voltage_ave ;
short voltage_history[4] ;
int voltage_index = 0 ;
void get_accelerations()
{
int i ;
ADC10CTL0 = 0x00;
ADC10CTL0 = SREF_0 + ADC10SHT_2 + ADC10ON;
ADC10CTL1 = INCH_0; // Input : A0
ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start
while(!(ADC10CTL0 & ADC10IFG)); // Wait for conversion to complete
// Place latest voltage into history circular buffer
voltage_history[voltage_index] = ADC10MEM ;
voltage_index = (voltage_index + 1) % 4 ;
voltage_ave = 0 ;
for (i=0; i<4; i++)
{
voltage_ave = voltage_ave + voltage_history[i] ;
}
voltage_ave = voltage_ave >> 2 ; // divide by 4 by right shifting 2
} // end get_accelerations()
abbiatishow said:One more question:
in this way, doing the shift, voltage_ave can just be a integer value.
The ADC10 samples are natively 10-bit data. The short declaration will implement a 16-bit data. Therefore, you should not have any issue of clipping if you are adding 4 10-bit numbers, as it will be properly contained within a 16-bit value.
Furthermore, after the shift by 2, or effective divide by 4, you will get back to a 10-bit number anyway.
abbiatishow said:
If I want a float value.. what to do?
Firstly, this would add quite a bit to the code base to implement the conversions and secondly, there is a native floating point unit on the MSP430.
abbiatishow said:
Is it possible to do: voltage_ave = voltage_ave /4 ?
Sure, but the would force the implementation of a div() routine from the run-time support math functions and cost more cycles to implement. A shift by 2 would be the minimal cost in terms of program words to implement and cycles to execute.
Thank you so much.. it looks like working.. except for voltage_index, I think it has to start from 1.
abbiatishow said:Thank you so much.. it looks like working..
Great.
abbiatishow said:
except for voltage_index, I think it has to start from 1.
Why do you say that?
Sorry, you are right.
And how to do if I want to vectorize the final result as well?
What I want to say is that I am trying to use a bigger window (short voltage_history[8] or more)
so I can store the final result that changes with the time.
abbiatishow said:What I want to say is that I am trying to use a bigger window (short voltage_history[8] or more)
It sounds like you want to expand the history buffer to 8 and generate an average based on the 8 most recent samples. As you eluded to, this is possible conveniently using powers of 2 as this will allow a shift instead of a divide.
If you want to divide by 2, you would right-shift by 1. A divide by 4, you would right-shift by 2. Etc.
The for() loop would need to expand to go from 0 through 7.
abbiatishow said:And how to do if I want to vectorize the final result as well?
so I can store the final result that changes with the time.
I presume this is a separate subject from creating the averaging effect on the voltage measured. This sounds like you want to then keep track of N number of average values. You would implement a very similar structure to create an array for the averages and implement an index for the average history buffer.
Hi,
I am doing a project with MSP430G2231. I have to measure energy of the waveform and I am planning to do this by applying the signal to ADC of MSP430G2231, then transferring the digital values in an array and then using some mathematical calculations to compute energy. so for the first part I am trying to use the same code as given above,
#include "msp430g2231.h"
void main(void)
{
int voltage_ave ;
short voltage_history[4] ;
int voltage_index = 0 ;
int i ;
ADC10CTL0 = 0x00;
ADC10CTL0 = SREF_0 + ADC10SHT_2 + ADC10ON;
ADC10CTL1 = INCH_0; // Input : A0
ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start
while(!(ADC10CTL0 & ADC10IFG)); // Wait for conversion to complete
// Place latest voltage into history circular buffer
voltage_history[voltage_index] = ADC10MEM ;
voltage_index = (voltage_index + 1) % 4 ;
voltage_ave = 0 ;
for (i=0; i<4; i++)
{
voltage_ave = voltage_ave + voltage_history[i] ;
}
voltage_ave = voltage_ave >> 2 ; // divide by 4 by right shifting 2
but I don't know why the code is not working. Can you please guide me through this? The problems I get are garbage values in voltage_av, voltage_history. Possibly the program halts at while(!(ADC10CTL0 & ADC10IFG)); // Wait for conversion to complete,
Please let me know.
Thank you for your help.
**Attention** This is a public forum