I guess this is a bit of a general question, rather than a specific HCS12 question, but I want to implement it on an HCS12..... I want to be able to 'filter' the value returned from an on-board ADC. When I say 'filter' I think that perhaps 'average' would be a better description. I want to be able to set an amount of filtering (say 20%) and none of the descriptions of digital filters I've found seem to allow this. They all seem to be designed to filter a specific frequency range that must be stated in the calculations used to design the filter. I thought about having a ring buffer to which you place the ADC results at a constant time interval, then summing the buffer, and dividing by the number of samples taken. But then how do you allow for the amount of filtering you want to apply ?? I'm going round in circles with this !! Any thoughts or ideas would be very much appreciated. I also need the routine to be fairly quick and not take up too much RAM as I'm getting a bit short on it. Andy |
|
Software Filtering of an ADC input
Started by ●March 7, 2004
Reply by ●March 7, 20042004-03-07
This works ok, but if you change NUMSAMP much bigger, you need to make tot
a long. This keeps the rolling average of NUMSAMP samples but subtracting off the old sample from the total, adding in the new sample in the total, and recomputing the avg with one of those clever divide by shifting tricks. By keeping the total, you dont need to add em all up every time. //---used in rolling avg-------- #define NUMSAMP 8 typedef struct{ unsigned char ndx; int tot; int avg; int dat[NUMSAMP]; //avg over NUMSAMP readings }Trollavg; //----global vars in bss at 0x0100------- Trollavg rollavg[NUMICHAN]; //-------------------- void initrollavg(Trollavg *p){ //init rollavg struct char i; p->ndx=0; p->tot=0; p->avg=0; for(i=0; i < NUMSAMP; i++){ p->dat[i]=0; } } //-------------------- int calcrollavg(Trollavg *p, int n){ //return rollavg of last NUMSAMP readings p->tot-=p->dat[p->ndx]; //subtract old value from tot p->tot+=n; //add in new value p->dat[p->ndx++]=n; //remember new value, bump ndx if(p->ndx==NUMSAMP) p->ndx=0; //rewind p->avg=p->tot >> 3; //2^3=8=NUMSAMP; avg is tot/NUMSAMP return p->avg; } |
|
Reply by ●March 7, 20042004-03-07
I thought about doing it like this, but it would need to have a large array to average the samples, and I can't afford the RAM space that would use. I'm sure there must be some clever way of doing this without using an array - just can't see it yet. PS : I'm writing it in Assemebler. Andy --- In , BobGardner@a... wrote: > This works ok, but if you change NUMSAMP much bigger, you need to make tot > a long. > This keeps the rolling average of NUMSAMP samples but subtracting off the > old sample from the total, adding in the new sample in the total, and > recomputing the avg with one of those clever divide by shifting tricks. By keeping the > total, you dont need to add em all up every time. > //---used in rolling avg-------- > #define NUMSAMP 8 > typedef struct{ > unsigned char ndx; > int tot; > int avg; > int dat[NUMSAMP]; //avg over NUMSAMP readings > }Trollavg; > //----global vars in bss at 0x0100------- > Trollavg rollavg[NUMICHAN]; > //-------------------- > void initrollavg(Trollavg *p){ > //init rollavg struct > char i; > p->ndx=0; > p->tot=0; > p->avg=0; > for(i=0; i < NUMSAMP; i++){ > p->dat[i]=0; > } > } > //-------------------- > int calcrollavg(Trollavg *p, int n){ > //return rollavg of last NUMSAMP readings > p->tot-=p->dat[p->ndx]; //subtract old value from tot > p->tot+=n; //add in new value > p->dat[p->ndx++]=n; //remember new value, bump ndx > if(p->ndx==NUMSAMP) p->ndx=0; //rewind > p->avg=p->tot >> 3; //2^3=8=NUMSAMP; avg is tot/NUMSAMP > return p->avg; > } > |
|
Reply by ●March 7, 20042004-03-07
I suppose a sort of "sledgehammer" method would be to put an appropriately chosen capacitor across the analog input (and a series resistance towards the source of the signal) and let the hardware do the filtering ... On Mar 7, 2004, at 3:42 PM, Andrew Leech wrote: > > I thought about doing it like this, but it would need to have a large > array to average the samples, and I can't afford the RAM space that > would use. > > I'm sure there must be some clever way of doing this without using an > array - just can't see it yet. > > PS : I'm writing it in Assemebler. > > Andy > > --- In , BobGardner@a... wrote: >> This works ok, but if you change NUMSAMP much bigger, you need to > make tot >> a long. >> This keeps the rolling average of NUMSAMP samples but subtracting > off the >> old sample from the total, adding in the new sample in the total, and >> recomputing the avg with one of those clever divide by shifting > tricks. By keeping the >> total, you dont need to add em all up every time. >> //---used in rolling avg-------- >> #define NUMSAMP 8 >> typedef struct{ >> unsigned char ndx; >> int tot; >> int avg; >> int dat[NUMSAMP]; //avg over NUMSAMP readings >> }Trollavg; >> //----global vars in bss at 0x0100------- >> Trollavg rollavg[NUMICHAN]; >> //-------------------- >> void initrollavg(Trollavg *p){ >> //init rollavg struct >> char i; >> p->ndx=0; >> p->tot=0; >> p->avg=0; >> for(i=0; i < NUMSAMP; i++){ >> p->dat[i]=0; >> } >> } >> //-------------------- >> int calcrollavg(Trollavg *p, int n){ >> //return rollavg of last NUMSAMP readings >> p->tot-=p->dat[p->ndx]; //subtract old value from tot >> p->tot+=n; //add in new value >> p->dat[p->ndx++]=n; //remember new value, bump ndx >> if(p->ndx==NUMSAMP) p->ndx=0; //rewind >> p->avg=p->tot >> 3; //2^3=8=NUMSAMP; avg is tot/NUMSAMP >> return p->avg; >> } |
Reply by ●March 7, 20042004-03-07
In a message dated 3/7/04 9:42:57 AM Eastern Standard Time, writes: I thought about doing it like this, but it would need to have a large array to average the samples, and I can't afford the RAM space that would use. I'm sure there must be some clever way of doing this without using an array - just can't see it yet. ============================================ OK, there's another trick. When I use floating point, I get a good averaging when I 'add 1/64th of the difference' between the raw value and the smoothed value... but this doesnt work for integers and real long averages because you compute the difference then try and take 1/64th or 1/128th of it or 1/256th, it right shifts down to nothing. So the trick, which the folks in the comp.dsp newsgroup told me, it to keep the avgeraged value scaled up by 256 sitting around, then when you compute the difference between the raw and avg values, you dont need to take 1/256th of it, you just add it into the avg*256 total.thats already there. |
Reply by ●March 7, 20042004-03-07
In a message dated 3/7/04 10:04:57 AM Eastern Standard Time,
swar@compuserve. com writes: I suppose a sort of "sledgehammer" method would be to put an appropriately chosen capacitor across the analog input (and a series resistance towards the source of the signal) and let the hardware do the filtering ... ================================ I think the big picture is that there is a hw and a sw solution to just about every problem, and tayloring the mix in the solution to fit the context of the problem is part of the engineering process. Also I've noticed that the hw guys tend to push the hard stuff they dont know how to do over to the sw guys, and they sw guys sort of do the same thing. Every once in a while you'll see some rare bird that knows both hw and sw, but they are few and far between..... |
|
Reply by ●March 7, 20042004-03-07
An RC filter approach would work, but it would make it fairly tricky to adjust from software. I could use a digital pot to alter the R component, but the whole job becomes rather messy. I'll just keep working on it. I'll figure out a good solution to it i'm sure. I just thought that someone else might have had a better idea than me ! --- In , BobGardner@a... wrote: > In a message dated 3/7/04 10:04:57 AM Eastern Standard Time, swar@compuserve. > com writes: > > I suppose a sort of "sledgehammer" method would be to put an > appropriately chosen capacitor across the analog input (and a series > resistance towards the source of the signal) and let the hardware do > the filtering ... > ================================ > I think the big picture is that there is a hw and a sw solution to just > about every problem, and tayloring the mix in the solution to fit the context of > the problem is part of the engineering process. > Also I've noticed that the hw guys tend to push the hard stuff they dont > know how to do over to the sw guys, and they sw guys sort of do the same thing. > Every once in a while you'll see some rare bird that knows both hw and sw, but > they are few and far between..... > |
|
Reply by ●March 7, 20042004-03-07
This small function , void filter( uint a, uint *b, int nf ) { *b += a - *b/nf ; } filters ATD result sample in a, into a corresponding filtered variable b. This is equivalent to an RC time constant filtering with a time constant of nf samples. The output in b, averages to nf * a. I typically use a value of 8 for nf, so an ADT input ranging from 0 to 1023 results in an output of 0 to 8184. Obviously, if you try too large a value for nf, you will exceed the 16 bit range. This is easy to implement in asm, especially if you restrict to powers of 2 for nf so you can use a shift instead of a divide. Jim At 07:04 AM 3/7/2004, you wrote: >I guess this is a bit of a general question, rather than a specific >HCS12 question, but I want to implement it on an HCS12..... > >I want to be able to 'filter' the value returned from an on-board ADC. >When I say 'filter' I think that perhaps 'average' would be a better >description. > >I want to be able to set an amount of filtering (say 20%) and none of >the descriptions of digital filters I've found seem to allow this. >They all seem to be designed to filter a specific frequency range that >must be stated in the calculations used to design the filter. > >I thought about having a ring buffer to which you place the ADC >results at a constant time interval, then summing the buffer, and >dividing by the number of samples taken. But then how do you allow for >the amount of filtering you want to apply ?? > >I'm going round in circles with this !! > >Any thoughts or ideas would be very much appreciated. > >I also need the routine to be fairly quick and not take up too much >RAM as I'm getting a bit short on it. > >Andy > >--------------------To learn more >about Motorola Microcontrollers, please visit >http://www.motorola.com/mcu >o learn more about Motorola Microcontrollers, please visit >http://www.motorola.com/mcu > >Yahoo! Groups Links > > |
Reply by ●March 7, 20042004-03-07
You could use a sort of PID or PI method: take the average of, say, the last 16 samples as the "i" value, the current value as "p", evtl. the difference between the last and the present sample as the "D" amount and combine the three with adjustable weighting to obtain your filtered value. On Mar 7, 2004, at 5:12 PM, Andrew Leech wrote: > > An RC filter approach would work, but it would make it fairly tricky > to adjust from software. I could use a digital pot to alter the R > component, but the whole job becomes rather messy. > > I'll just keep working on it. I'll figure out a good solution to it > i'm sure. I just thought that someone else might have had a better > idea than me ! > |
Reply by ●March 7, 20042004-03-07
Andrew -- I have used the following method with good results on several projects: 1. Allocate an accumulator in RAM to hold the running average. 2. For every A/D sample update the accumulator as follows: Accum = (Prior Accum * 7/8) + 1/8 * New Sample. Working in assembler this is almost trival to implement. Use shifts and accumulates. Shift 'Prior Accum' left 3 to obtain 1/8 of 'Prior Accum' Subtract 1/8 Prior Accum from 'Prior Accum' to obtain 7/8 'Prior Accum' Now compute 1/8 of 'New Sample' by shifting right 3 and add this to '7/8 Prior Accum' to obtain the Updated accumulator and smoothed value. The smoothing factor of 1/8 was arbitrairly chosen but is representative of factors that have worked well in the past. You can adjust the "degree of filtering" by using different smoothing factors such as 1/4 or 1/16. This only requires that you adjust the shift counts. Note: If you do this all in fixed point, you can expect to see binary truncation effects in the result. In effect the output will develop a dead zone such that new samples very near the running average will not affect the filter output. The output will jump from one smoothed value to another as the new samples change by a few counts. This creates a sort of dead zone effect. This is an inevitble consequence of binary truncation and will show up in any filtering technique that you devise. It is much better to eliminate the noise at the source by careful attention to hardware design and to use little or no software filtering. Warning: Be very careful to consider the dynamic range of the intermediate results of this calculation. Binary overfow at any step of this process will produce absolute rubbish for output!!! You can examine the results of this filtering process by clever use of Excel. Good Luck, Bob Smith --- Avoid computer viruses, Practice safe hex --- -- Specializing in small, cost effective embedded control systems -- http://www.smithmachineworks.com/embedprod.html Robert L. (Bob) Smith Smith Machine Works, Inc. 9900 Lumlay Road Richmond, VA 23236 804/745-1065 ----- Original Message ----- From: "Andrew Leech" <> To: <> Sent: Sunday, March 07, 2004 7:04 AM Subject: [68HC12] Software Filtering of an ADC input > I guess this is a bit of a general question, rather than a specific > HCS12 question, but I want to implement it on an HCS12..... > > I want to be able to 'filter' the value returned from an on-board ADC. > When I say 'filter' I think that perhaps 'average' would be a better > description. > > I want to be able to set an amount of filtering (say 20%) and none of > the descriptions of digital filters I've found seem to allow this. > They all seem to be designed to filter a specific frequency range that > must be stated in the calculations used to design the filter. > > I thought about having a ring buffer to which you place the ADC > results at a constant time interval, then summing the buffer, and > dividing by the number of samples taken. But then how do you allow for > the amount of filtering you want to apply ?? > > I'm going round in circles with this !! > > Any thoughts or ideas would be very much appreciated. > > I also need the routine to be fairly quick and not take up too much > RAM as I'm getting a bit short on it. > > Andy > > --------------------To learn more about Motorola Microcontrollers, please visit > http://www.motorola.com/mcu > o learn more about Motorola Microcontrollers, please visit > http://www.motorola.com/mcu > > Yahoo! Groups Links > |
|