Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

PIC RMS calculation C-code

Status
Not open for further replies.

Prince Vegeta

Member level 5
Joined
May 29, 2012
Messages
84
Helped
0
Reputation
0
Reaction score
0
Trophy points
1,286
Activity points
2,011
Hi there, I found this code in an old post... It's about calculating RMS value of a sine wave:

Code:
Consider your line frequency = 50.00 Hz
 
50.00 Hz = 20ms = 20000 us
 
for example if you grab sinewave sample @ 100us interval time you get 200 samples .
 
store in some buffer, your ADC is 8 bit so you need buffer of size
signed char adcbuf[200]
 
ok...
 
now do following
 
signed int rawrms ;
int rms ;
 
for( count = 0 ; count < 200 ; count++ )
{
    rawrms += adcbuf[count] * adcbuf[count] ;
}
 
rms = rawrms / 200 ;
rms = sqrt(rms) ;

Now, there are stuff I don't really understand... if I reduced the signal to 5v peak:

1- What about Negative Cycle?

2- Should I start sampling from the 0 point?

3- I made a circuit that makes -Vp=ov and 0v=2.5v and +Vp=5v... will the previous code work here??? here there's no Negative cycle but it's presented by low ADC values... will this work with previous code?

4- about sampling, how can I pick sampling frequency and use it properly in mikroC?

Finally, is there a better way of measuring RMS value? I posted several threads about it but I didn't get any answer so I though about posting this code.

thank you.
 

1-3.
RMS calculation only works if the zero point of the original signal is preserved. An AC voltage scaled to 0..5V respectively only positive numbers must be converted to signed numbers with correct zero before performing RMS calculations.

4. There's is no need for an exact sampling frequency. But the RMS measurement must be either performed on full periods, or use low-pass filtering sufficient to average the fluctuation caused by a different measurement window.

You did neither tell a motivation for RMS measurement, nor accuracy requirements and application enviroment.

As long as the mains voltage is an undistorted sine, a simple rectified average value or even peak measurement can be converted to a RMS value. That's what most cheap voltmeters do. True RMS becomes meaningful for mains voltage with harmonic distortions. The maximum order of expectable harmonics and accuracy requirements determine the measurement bandwidth respectively sampling frequency in this case.
 

So how can I perform this Zero point detection and how can I calculate -ve cycle values?

my project is an Automatic Transfer Switch (ATS) that takes 2 AC sources as inputs... if one have 230v RMS (or more) it will be on... if not, the other one will be on. (it's more complex actually).

I assumed it won't be pure sine wave because one of the sources should be connected to solar energy power source (or any other source) while the second one is at grid.

I think you understand me now.

so can you give me some code example or circuit one?
 

It sounds like you do not need the RMS values if all you do is compare them. The simplest method would be to rectify the voltage from both sources, filter them to give a reasonably ripple free DC level then scale them to the ADC range. That method will produce two ADC readings which are proportional to the RMS sources with only a tiny time lag (because of the filtering) and you only need one ADC reading per source. Comparing them is just a case of seeing which ADC reading is the bigger.

If you really do have to sample within each cycle, full-wave rectify the AC voltages so all the cycles are the same polarity, scale them to the ADC range then do the sampling as in your code to find the average. The primary reason for squaring the voltage in an RMS measurement is ensure the polarity is constant (plus * plus = plus, minus * minus = plus) which isn't too far removed from the effect of using a full wave rectifier to do the same thing.

Brian.
 
It sounds like you do not need the RMS values if all you do is compare them. The simplest method would be to rectify the voltage from both sources, filter them to give a reasonably ripple free DC level then scale them to the ADC range. That method will produce two ADC readings which are proportional to the RMS sources with only a tiny time lag (because of the filtering) and you only need one ADC reading per source. Comparing them is just a case of seeing which ADC reading is the bigger.

If you really do have to sample within each cycle, full-wave rectify the AC voltages so all the cycles are the same polarity, scale them to the ADC range then do the sampling as in your code to find the average. The primary reason for squaring the voltage in an RMS measurement is ensure the polarity is constant (plus * plus = plus, minus * minus = plus) which isn't too far removed from the effect of using a full wave rectifier to do the same thing.

Brian.

So you say that sampling is ONLY done by making all cycles positive? that's a good hint because I was wondering about it for a week or so.

and by rectifying it it still have some ripple and you didn't mention filtering it (in the second paragraph), is that because you want it to be like the AC source but in +ve polarity? which is different than rectifying then filtering it which makes it nearly pure DC signal.

I don't need to compare them to each other but like the following:

1- run the primary source.
2- check if it's RMS voltage is in required range.
3- if it's RMS voltage drops below the range, cut it out and wait for 5ms delay (ATS standard), then run the secondary source (the grid one).
4- keep checking both sources, if the 1st one returned to good range, disable the secondary one and activate the primary.
5- if both sources below range, cut them both and inform the user by lighting a red led.

^
I guess now you fully understand what I want to do.
 

I agree with Brian. The decision of an ATS would be made on presence /absence of mains voltage, may be with specific voltage threshold. No nned for true RMS measurement.
 

I agree with Brian. The decision of an ATS would be made on presence /absence of mains voltage, may be with specific voltage threshold. No nned for true RMS measurement.

you are right but that depends on what application should ATS be attached to.

In my project, the ATS will have a primary AC source of Solar power, that means that this source may actually get lower than what's needed to run normal loads. That depends on RMS value of it (it doesn't matter what exactly this value is, people say it's about 180v RMS... this can be easily adjusted in software).

That's why I need to measure the RMS value. the secondary source is the grid and here a simple voltage detector will do the job since the grid voltage never get down a lot... so no need of measuring RMS... unlike the 1st source.

about full-wave rectification, I tried one in proteus isis and then put voltage division with filter and zener... but when I adjust the source voltage (alternator), it doesn't change the output of the whole circuit the right way... i.e, it should be -350-to-+350v peaks AC source, and output is 0-5v right? so when I make it 200v peak for example, it should get lower say 3v on output..... but it doesn't do it!!!! it only outputs 0,2.5,5v for what ever source voltage I apply...

So, what do you think I need to do?
 

Also, I tried programming using an array like ADCbuffer [200], and PIC16F877A had less RAM than required to store this array (as MikroC told).

so can I execute that program or something like it without using array?

like:

Code:
unsigned short i;
unsigned short v;
unsigned short RMS_readings;
unsigned short RMS;
unsigned short RMS_buffer;
unsigned char l_byte, h_byte;
unsigned int ADR;
unsigned int channel;


unsigned int ADCRead(unsigned int channel){

        ADCON0 = 0x81; //| (channel << 3); //Change channel
        delay_us(40); //Acquisition Delay
        GO_DONE_bit = 1; //Set GO_DONE bit to start conversion
        while (GO_DONE_bit == 1); //Wait for bit to be cleared
        //If bit is cleared, this means conversion is over
        l_byte = ADRESL;
        h_byte = ADRESH;
        ADR = (h_byte<<8)|l_byte;
        return ADR;
}

unsigned int RMS_calculator (unsigned int source) {

                channel = source;
                for ( i = 0 ; i < 20000 ; i++ ) {
                RMS_buffer = ADCRead(channel); //Gets reading from channel 0
                RMS_readings += RMS_buffer*RMS_buffer;

                }
               RMS = RMS_readings/50;
               RMS = sqrt(RMS);
               v = (RMS*350)/1023;

               return v;

}

^ it's not tried yet, I posted it for the sake of an example.

ADCRead function reads ADC value and outputs it as 0-1023 int number...

now, RMS_calculator function calculates RMS of the 0-1023 numbers and THEN convert it to actual voltage level (I converted 350v peak to 5v peak).

So, what do u think?
 

I still can't understand why you need the RMS value, if you rectify it, and filter it you get a DC voltage which is directly proportional to it. It then only needs one ADC measurement to find the original voltage, arrays are not needed at all.

Think of the circuit as being like a low voltage UNregulated 'wall wart' power supply, it's output voltage 'tracks' the AC you supply it from. Less in means less out, more in means more out. Your ADC will need very little current though so you can replace the big value reservoir capacitors you normally find in a PSU with much smaller values which make it more responsive to changes.

For example: you connect the 230V to a rectifier and then add a reservoir capacitor of say 0.1uF, it will charge to the peak of the waveform which is (Sqrt(2) * RMS) = 325.26V. Then using a two-resistor potential divider yo drop that to say 4.5V and measure the result with the ADC. I suggest keeping it a little below 5V to give a safety margin in case the voltage becomes higher but not going too low because that loses you accuracy. The entire code then becomes:
"v = ADCRead(channel);" although if you actually need to know the absolute voltage you will have to scale the result to take into account the potential divider.

Brian.
 

so you say that I should rectify the signal AND filter it to have a nearly pure DC that equals the peak AC voltage?

then:

v_adc = adcread(0); // adc value 0-1023
v_volts = (v_adc*5)/1023; // actual voltage at adc pin 0-5v
v_rms_small = v_volts/sqrt(2); // rms of that 0-5v signal
v_rms_true = (v_rms_small*350)/5 // true rms assuming 350v peak


Is that right?
 

Sort of. Why do you need 'v_rms_small' and 'v_rms_true', they are all proportional to each other and the equations you show are essentially multiplication or division by constants.

For purposes of deciding which supply to use, all you need to know is a relative value from each source that you can compare, there is no need to consider RMS at all. If you need an actual voltage measurement, you must also 'reverse' the action of the potential divider to scale the voltage back up it it's real amount. For example if you used a potential divider of 100K to the filtered DC and 1.562K to ground (325V down to 5V) you would multiply the ADC measurement by 65 to get the real voltage being measured.


Brian.
 

I still can't understand why you need the RMS value, if you rectify it, and filter it you get a DC voltage which is directly proportional to it...

Maybe he´s asking for a true-rms routine calculator.
An assumed sinewave waveform could be deformed.


+++
 

True, but the application is described as being an ATS controller which really only needs to know which is the most suitable of two voltages to select. For that, all it needs to know is the voltage of each supply, the wave shape isn't really relevant and even if it was, the two signals would be of predictable shape so the scaling could be adjusted to compensate for it.

Brian.
 

There's also a conceptual problem involved with deciding ATS control based on voltage comparison. The fact that the solar inverter is achieving a certain output voltage doesn't mean that it's able to drive the load you intend to switch to it.
 

There's also a conceptual problem involved with deciding ATS control based on voltage comparison. The fact that the solar inverter is achieving a certain output voltage doesn't mean that it's able to drive the load you intend to switch to it.

well, that's beyond my consideration... All I need is to make a suitable ATS to output the suitable power source... If the source1 (solar inverter) isn't good enough then it's gonna be it's problem.

Sort of. Why do you need 'v_rms_small' and 'v_rms_true', they are all proportional to each other and the equations you show are essentially multiplication or division by constants.

For purposes of deciding which supply to use, all you need to know is a relative value from each source that you can compare, there is no need to consider RMS at all. If you need an actual voltage measurement, you must also 'reverse' the action of the potential divider to scale the voltage back up it it's real amount. For example if you used a potential divider of 100K to the filtered DC and 1.562K to ground (325V down to 5V) you would multiply the ADC measurement by 65 to get the real voltage being measured.


Brian.

Hmm... you say that v_rms_small isn't necessary? something like: r_rms_true = (volts*350/5)/sqrt(2) ? << this shortens the calculations as you mentioned. Is it good now? ^_^


I assumed that the highest peak voltage will be 350v (it's normally 320vp = 230v rms) but picking resistors in the divider was tough because I needed non-nominal values like 1.47k-1.5k.... and you say 1.562k, how can I achieve this resistance?

________

Now the previous method is to rectify AND filter the signal to achieve nearly pure DC then divide it by sqrt(2).

But if I want to go with sampling, what do I need? (the code suggested sampling).

is it really required to take samples in full 20ms period or what exactly? in 20ms I can use a timer to do it.

and should I begin taking samples from zero-crossing point?
 

is it really required to take samples in full 20ms period or what exactly? in 20ms I can use a timer to do it.
and should I begin taking samples from zero-crossing point?

In order to avoid measurement timming be blind to some feasible spike due to comutation, is desirable to perform A/D acquisition in a period shifted and asynchronous to the sinewave frequency.

In some specific national electric standards, are sugested to use prime number to ensure that ( as 19ms instead 20ms ).
The price to that choice is the fact that is required a lot of wave cycles to close the calculation cycle ( 380ms for a sigle period measurement ).






+++
 
Last edited:

In order to avoid measurement timming be blind to some feasible spike due to comutation, is desirable to perform A/D acquisition in a period shifted and asynchronous to the sinewave frequency.

In some specific national electric standards, are sugested to use prime number to ensure that ( as 19ms instead 20ms ).
The price to that choice is the fact that is required a lot of wave cycles to close the calculation cycle ( 380ms for a sigle period measurement ).






+++

380ms to 1 period? how is that?

plus, 19ms instead of 20ms? I don't understand why this is needed. And how to make it async.

OK, will it be enough to just rectify and filter the signal to make it nearly pure DC (using 100uF cap) and then calc the value and divide it by sqrt(2)??
 

Ignoring the problem FvM highlighted in post #14 which is very valid, I have to ask again - why do you need RMS values at all?

Basically all you need is an analog comparator to see which voltage is highest, you could dispense with the PIC completely if it doesn't have other things to do as well.

The concept of sampling over particular periods (if necessary) is that you should encompass the whole cycle of the waveform so it doesn't really matter where in the cycle you start sampling as long as you take enough to get a an accurate reading and they are taken over at least one complete cycle. If you sample from zero to second zero, or from peak to peak it shouldn't make any difference. Sampling over several cycles lessens the risk of a rogue measurement due to spikes or other interference causing a significant error in the calculations.

If you use the rectifier and capacitor method, the value I mentioned was 100nF not 100uF. In a power supply a high value is needed to maintain the voltage between voltage peaks as it is being drained away by the load but in this application the load is very tiny, only the curent through the potential divider so the value should be much lower. It will work with bigger values but be less responsive when the voltage changes.

Brian.
 

I need RMS to show each source's status on an LCD. And, when source1 is active and source2 is bigger RMS value than it, it doesn't mean that I should deactivate 1 and activate 2.

a source is deactivated only if it's RMS gets below a certain level and that's why I need to measure RMS.

OK, now I will stick with rectifier+capacitor method as it seems easier. 100nF? Hmmm in Proteus I tried this value but it didn't make a pure DC signal (nearly) so I tried 100uF.

There will be loads connected to the source... Or you say that I should care about voltage divider in the design as the load?

anyway, do you find my suggested calculation (vp/sqrt(2)) a good one?
 

Recently, I saw that (vp/sqrt(2)) will not work as there will be non-sine-wave signals like square waves. So, I must do sampling now.

About sampling, should I use a timer to count 20ms and then generate an interrupt that calculates the values?

If yes, please suggest something ^_^
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top