CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

RMS Value Calculation Using 12 bit ADC
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
mictel



Joined: 24 May 2014
Posts: 14

View user's profile Send private message

RMS Value Calculation Using 12 bit ADC
PostPosted: Sat Aug 23, 2014 4:45 pm     Reply with quote

I changed code example ex_rmsdb.c from 8bit to 12bit ADC using the 16F1789. The algorithm squares the samples then sums them up so the sum gets very large very quick but thought I was OK since the limit on floating point values is 3.4E38 and I am no where near that. but now I am getting screwy results.

So I loaded ex_float.c to learn more about floating point numbers. I noticed that you get bogus results for larger numbers:

Enter first number: 42949650.0
Enter second number: 2.0

The following are formatted with %E

A= 4.294964E+07
B= 2.000000E+00

a + b = 4.294965E+07
a - b = 4.294964E+07
a * b = 8.589929E+07
a / b = 2.147482E+07

The following are formatted with %f

A= -20.48
B= 2.00

a + b = -15.36
a - b = -20.48
a * b = -40.95
a / b = 21474826.24

Note the values are bogus for %f formatted output except for a/b. And a/b is bogus if the numerator is any larger than 42949650.0 when divided by 2.

The CCS help file says float variables can be as large as 3.4e38 obviously not; what am I missing. Searched but could not find the answer. How are large numbers handled, especially when summing 12 bit ADC values?

Thanks,
temtronic



Joined: 01 Jul 2010
Posts: 9255
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Sat Aug 23, 2014 5:56 pm     Reply with quote

hmmm.. do you get the same 'screwy' numbers if you print using the 'e' format(exponential) instead of 'f' floating ?

also might be a compiler bug, so you should say which version you're using.

hth
jay
mictel



Joined: 24 May 2014
Posts: 14

View user's profile Send private message

PostPosted: Sat Aug 23, 2014 8:02 pm     Reply with quote

The complier version is PCM 5.026

Both versions of the format are shown. The %E format is rounded but not totally bogus. The problem is when I try to use the %e format in my main program, the complier says I am out of memory! Don't know what is up with that, put it back to %f and all is fine with memory but the numbers are bogus.

Thanks for taking a look.
Ttelmah



Joined: 11 Mar 2010
Posts: 19569

View user's profile Send private message

PostPosted: Sun Aug 24, 2014 12:50 am     Reply with quote

The first thing you've discovered, is that FP maths is large....

There were problems with quite a number of compiler versions if you use %f, without specifying a _width_.

Try %8.0f

I thought these had been fixed in the newer V5 compilers, but it may be that there are still problems when values go over a particular size.

Remember that with FP, you only have just over 6 digits of actual 'accuracy' (actually 21bits), so it will not take much for smaller values to be completely lost when summed to sets that have a large value in them. FP, is a 'shortcut' to allow a wide range of values, but comes at a cost of massively reduced actual accuracy. Since your values fit into a fixed range (as read from the ADC 0 to 4095), you will be much better in terms of space, _and_ accuracy, to do the summing in integer.

Square, is just a value multiplied by itself.

So read the ADC, multiply the reading by itself, and sum this in an int32.
Since the largest value you can have is 4095, you can sum up to 256 values in an int32, and have proper accuracy on this, with less maths space.

Then perform your division (so now the largest value is just 16769025 - which can accurately be represented by the 21 bits available in a float), and a FP square root of this (just cast the integer in the conversion).

The square root code will still be very bulky. You could consider using an integer square root as well (do a search for 'fast integer square root'). This will be much smaller and faster.
Then use fp to actually display the result, or (better...), generate a scaled integer (for instance mV in integer), which can then be output without involving float at all.
mictel



Joined: 24 May 2014
Posts: 14

View user's profile Send private message

PostPosted: Mon Aug 25, 2014 4:46 am     Reply with quote

Thank you for the explanation and suggestions. Interestingly, it all seems to be working fine even with the analog input voltage up to maximum, 5V.

The large value of the sum of the squares displays wrong but when divided by the ADC resolution (5/2^12) the value is fairly accurate. When divided by the number of samples and the square root is done, the value is also fairly accurate (within 3%) for the test cases I tried with sine, triangle and square waveforms.

Still verifying performance. Good to know alternatives if problems found. Still puzzled why the code does not compile when changing the format from %f to %E saying I am out of memory.

Out of ROM. A segment or the program is too large decode_command

Change back to %f and all is well with plenty of ROM and RAM remaining.
temtronic



Joined: 01 Jul 2010
Posts: 9255
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Mon Aug 25, 2014 5:33 am     Reply with quote

re: ....
Out of ROM. A segment or the program is too large decode_command

'decode_command' might be 'spanning' two segments of memory.'functions' usually must be within one contiguous 'segment' of memory. At least that's the way it was 'in the beginning'. Perhaps the newer compiler version can handle it ?

It's be interesting to know how LONG it takes to do the FP math vs. scaled integers. If your application is 'time sensitive', FP is NOT the way to go !!


hth
jay
mictel



Joined: 24 May 2014
Posts: 14

View user's profile Send private message

PostPosted: Mon Aug 25, 2014 5:58 am     Reply with quote

Not sure why changing the format in a printf statement from %f to %E would change memory requirements to be out of ROM.

I did not measure how long it takes for the FP RMS calculation but can say the result is displayed immediately after sending the command without any notable delay. The only thing going on when measuring the RMS voltage is measuring the RMS voltage so speed is not an issue for my application provided the RMS voltage is displayed quickly WRT the user. The processor is running at 32MHz internal clock which of course helps. The frequency of the signal is between 10Hz and 500Hz so that too helps.

I am impressed with the results so far. Input signal; Vs = 100Hz Sine

Vpk=1.0V, Vrms=0.697 to 0.707
Vpk=1.5V, Vrms=1.057 to 1.067
Vpk=2.0V, Vrms=1.407 to 1.417
Vpk=2.5V, Vrms=1.757 to 1.767
Vpk=5.0V, Vrms=3.497 to 3.507 (~1% error)
Ttelmah



Joined: 11 Mar 2010
Posts: 19569

View user's profile Send private message

PostPosted: Mon Aug 25, 2014 7:00 am     Reply with quote

Remember that you can still have plenty of memory, but run 'out of ROM'.

As Temtronic says, I'd suspect that you will find 'decode_command, is just a few bytes short of a segment size. Then it only has to grow a tiny amount, to make the problem appear. %E, will be a fraction bulkier in several areas, and the change is just enough to trigger the problem....

Just try doing the sum and division in integer. Dead easy to code, and 'pointless' not to.
temtronic



Joined: 01 Jul 2010
Posts: 9255
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Mon Aug 25, 2014 7:13 am     Reply with quote

re: code...

changing the 'format' of the displayed results, means the compiler uses different code,which can either be bigger or smaller. Think of it as writing on a cereal box. A phrase in English might be 10 words, in French could be 15 and longer words!
Now, if the code is longer...oops...it exceeds the 'segment' length and the 'error' message appear. .From what I understand, individual functions cannot exceed segment size. The entire 'subroutine' must fit into a segment

I like the <1% error ! well with 'spec' of an old guy like me !!

You should try what Mr. t suggest cause sooner or later, you'll really run out of memory or speed....nature of the beast !!

jay
newguy



Joined: 24 Jun 2004
Posts: 1911

View user's profile Send private message

PostPosted: Mon Aug 25, 2014 9:29 am     Reply with quote

As others have said, abandon floating point entirely. Why? Try creating a blank project that does nothing - use any PIC you wish. Make note of the ROM/RAM used. Then simply declare a float variable and recompile. What happens to the ROM/RAM used?

Remember, the more ROM used, the more instructions that must be executed by the processor, and the slower it will perform. Do yourself a favour and learn how to do it exclusively with integers and your code will be both faster and more precise.

Here's working code that performs a sqrt on an integer:

Code:
unsigned int32 my_sqrt(unsigned int32 n) {
   unsigned int32 root = 0;
   unsigned int32 bit, trial;
   
   if (n >= 0x10000) {
      bit = 0x40000000;
   }
   else {
      bit = 0x4000;
   }
   
   do {
      trial = root + bit;
      if (n >= trial) {
         n -= trial;
         root = trial + bit;
      }
      root >>= 1;
      bit >>= 2;
   } while (bit != 0);
   
   return (root);
}
mictel



Joined: 24 May 2014
Posts: 14

View user's profile Send private message

PostPosted: Tue Aug 26, 2014 6:31 am     Reply with quote

You folks are great! I really appreciate the suggestions and encouragement to consider switching over to integer math. I presently lack the knowledge on how to do that so took the easy way out; FP. Thanks to your guidance and example code that will soon change.
mictel



Joined: 24 May 2014
Posts: 14

View user's profile Send private message

PostPosted: Fri Sep 05, 2014 4:25 pm     Reply with quote

Ditched the FP math thanks to the knowledge shared here. Now have the 12 bit ADC RMS measurement working with integer math. Did not do a comparison on speed but here is the results for accuracy:

Vs= 100Hz Sine wave

Vpk=1.0V, Vrms = 0.699 to 0.705
Vpk=1.5V, Vrms = 1.0579 to 1.062
Vpk=2.0V, Vrms = 1.4038 to 1.4086
Vpk=2.5V, Vrms = 1.7467 to 1.7626
Vpk=5.0V, Vrms = 3.4948 to 3.5161

WC error was ~1.15% using integer math for a single measurement. Averaging measurements will lower error even further.

The 12 bit ADC takes 52 samples of the 100Hz sine wave operating at 32MHz. Maximum frequency tested was 1kHz; only 6 samples taken and the error goes up to ~7.8%

Thanks again for sharing the knowledge.
temtronic



Joined: 01 Jul 2010
Posts: 9255
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Fri Sep 05, 2014 4:43 pm     Reply with quote

You should try 'binary numbers' of samples like 8,16,32 maybe 48.


The 'math' should be a lot faster to get average and smaller code as well.



Just food for thought..

jay
Ttelmah



Joined: 11 Mar 2010
Posts: 19569

View user's profile Send private message

PostPosted: Sat Sep 06, 2014 12:35 pm     Reply with quote

As a comment consider using a modified binary approach.

As you add up (say) 34 values, also record the largest and the smallest value. Then when you have your 34 value sum, subtract the recorded largest and smallest value from the sum, so you have the sum of the 32 values ignoring the largest and smallest. Then divide this by 32.
This gives the 'Olympic' approach (commonly used when scoring to 'throw away' any unusually high or low scores), combined with a binary average.
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Sat Sep 06, 2014 2:45 pm     Reply with quote

i've watched this thread as you made progress, but realized that it is NOT trivial, and really perhaps not even possible to get a correct RMS value
with your code for anything BUT a near perfect sine wave.
Even then, differing frequencies may throw off the result.

As my hero Richard Feynman once famously said:
"the easiest person to fool is always yourself" ;-))

True RMS for any waveform, both uni or bipolar
evaluates to the integrated area under the curve,
for the absolute value of the waveform under measurement.
Or viewed another way: it's the heat value if it were seen across a pure resistor.

Without seeing your circuitry, You appear to be using only the positive PEAK value and finessing the math on the leading assumption its a pure sine.

were you to attempt this on an audio stream, an asymmetric sine wave with a dc offset or an intermittent ac signal with a high peak to average ratio,in all these cases you will find your method to
be wildly wrong, typically on the HIGH side of correct.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group