Author Topic: RMS Voltage calculation usin PIC16F877A with C-code  (Read 26753 times)

0 Members and 1 Guest are viewing this topic.

Offline VEGETATopic starter

  • Super Contributor
  • ***
  • Posts: 1905
  • Country: jo
  • I am the cult of personality
    • Thundertronics
RMS Voltage calculation usin PIC16F877A with C-code
« on: February 23, 2013, 09:58:26 pm »
Hi Dave, everyone...

I work in a project (ATS) which needs calculating RMS value of two 230v RMS AC source (one is the mains).

I made a lot of research and came to rectify the signal then use a voltage divider to make it 0-5v one... after that add it to PIC ADC pin.

^ rectification without filtering, because I think it's better to have the sine-wave shape but -ve cycle becomes +ve as I will show the calculation needed later.

now, it's like this:

- get ADC value of 0-1023.
- then RMS_reading += adc*adc for some period of time.... say 20ms (50Hz signal).
- after squaring it then divide it by the number of samples say 200 samples (depends on the period of time 20ms.. this I need help with).... like: RMS_values = RMS_reading/200.
-after division, take the sqrt of it... like: RMS = sqrt(RMS_values)

^

now we should get the RMS value of the ADC readings right??? so what ever the value is if we used the equation I'm about to post, it will give us the RMS value of the voltage, right????

I assumed 350v peak signal so that 350v = 5v after rectification and scaling so the equation will be:

V_rms = (RMS*350)/1023

^ Is all that right or I'm missing something?

Now this is the code (mikroC) I used:

Code: [Select]
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 the full code and it's still buggy as it didn't work... I used Proteus ISIS for simulation and I think I did something wrong somewhere...

Please help me finding it ^_^

1- Does the code apply the theory calculations or what?

2- Is there any other good way of measuring RMS value of the signal? with example or so plz.

3- Any recommendations?

Thanks All.






Offline amspire

  • Super Contributor
  • ***
  • Posts: 3802
  • Country: au
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #1 on: February 23, 2013, 10:59:57 pm »
Without looking too closely, I can see one huge problem. It looks like you are taking 8 bit A/D readings. Then you square them - that needs 16 bits storage. Then you accumulate 20000 of these 16 bit products. You now need 31 bits - lets call it 32 bits or a 4 byte unsigned int. You are storing this in an "Unsigned short" register that is only 8 bits long.

You need to declare RMS_readings and RMS as a unsigned long.

You have to be careful about variable "v' as well - if it can possibly be over 256, make it an unsigned int or unsigned long.
 

Offline VEGETATopic starter

  • Super Contributor
  • ***
  • Posts: 1905
  • Country: jo
  • I am the cult of personality
    • Thundertronics
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #2 on: February 23, 2013, 11:06:38 pm »
so if I make them long, will the code work?

Offline mariush

  • Super Contributor
  • ***
  • Posts: 4979
  • Country: ro
  • .
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #3 on: February 23, 2013, 11:17:24 pm »
How about you correct the code and let us know?
 

Offline amspire

  • Super Contributor
  • ***
  • Posts: 3802
  • Country: au
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #4 on: February 24, 2013, 01:06:42 am »
so if I make them long, will the code work?
It won't work as you have posted it as there is no main loop calling the RMS_calculator routine. About the code to control the A/D. I wouldn't have a clue. I would have to look at the data sheets for that. We do not have a clue about the circuit, what you are measuring and what you are going to do with "v" so we cannot judge if the scaling factors are correct.

Let us know how it goes.

Richard
 

Offline VEGETATopic starter

  • Super Contributor
  • ***
  • Posts: 1905
  • Country: jo
  • I am the cult of personality
    • Thundertronics
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #5 on: February 24, 2013, 03:33:36 pm »
Quote
It won't work as you have posted it as there is no main loop calling the RMS_calculator routine. About the code to control the A/D. I wouldn't have a clue. I would have to look at the data sheets for that. We do not have a clue about the circuit, what you are measuring and what you are going to do with "v" so we cannot judge if the scaling factors are correct.

Let us know how it goes.

Richard

I will adjust variables' length. And ADC is 10bit.

I have a main loop but it does nothing really till now, all it does is it loads the RMS_calculator function as a trial.

I use Proteus ISIS for simulation, do you suggest anything else?

Do you have any way that I can use to check the readings are correct or not?

The final project will have an LCD screen to output voltages but until now I haven't deal with one.

THX

Offline amspire

  • Super Contributor
  • ***
  • Posts: 3802
  • Country: au
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #6 on: February 24, 2013, 11:40:42 pm »
If you are using 10 bit ADC, then that will be 20 bits when you square the ADC readings squared. Since a unsigned long is 32 bits, that means you can only total a count of readings that is 32 - 20 = 12 bits long - instead of 20,000 samples, you are limited to 212 = 4096 samples max.

You are probably measuring an AC signal which means before squaring the results, you have to offset the ADC readings so that positive voltages are positive, negative voltages are negative and zero volts is zero volts. If you do not do this, all the rest of the calculations will be nonsense. You would also have to store this ADC reading in a 16 bit signed integer.

The results after squaring can be accumulated in an unsigned long int.

You can use a different method where you do a rolling average. That means you can be sampling continuously and you always have an average of squares available rather then reading 20,000 samples, calculating the result and doing another 20,000 samples. This post may help:

https://www.eevblog.com/forum/projects/power-quality-monitor-work-in-progress/msg89957/#msg89957

Check out the links to earlier posts.

To debug the code, split things up. Test out the calculation routines with numbers you input, not the ADC. Get that working properly from minimum voltage to maximum voltage.

When that is working, get the ADC to work. Add debugging code - for example code to toggle spare pins. That way you can use an oscilloscope to see if a particular piece of code is running and how often it is running.

 

Offline VEGETATopic starter

  • Super Contributor
  • ***
  • Posts: 1905
  • Country: jo
  • I am the cult of personality
    • Thundertronics
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #7 on: February 25, 2013, 12:16:17 pm »
For what you said...

I'm using 10-bit ADC and the 20000 is just assumed by me.

about reading AC signals, I rectified it and then scaled it to 5v-peak which means I don't have to do anything in code to "adjust" voltages because the -ve values are going to be squared and hence a +ve value will result. That's why I rectified it (full-wave) but currently without filters as I want the sine-wave shape (except that -ve cycle is now +ve).

The other things you said about continuous average I didn't understand it.

Can you write a simple code example so I can use as a guide? I'm running out of time in my project so I need quick help... you put a code example and I will understand and modify it. Thanks in advance.

I wanted to sample the signal because there will be square-wave signals which applying (v_rms = vp/sqrt(2)) will never work right?

Other thing you said is having a signed variable... If I rectify the signal and do things as I wrote in this post, this won't be necessary right?

Anyway, ADC of PIC doesn't read AC signals because of it can't read negative values... Or I'm understanding it the wrong way?


As I told you, just post a simple example and I will do my best to work with it... Any example.

Thanks man, you really helped me a lot so far.



Offline amspire

  • Super Contributor
  • ***
  • Posts: 3802
  • Country: au
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #8 on: February 25, 2013, 02:42:46 pm »
Here is some pseudocode for a continuous average. For the details, you will have to follow the links and read the earlier posts:

Code: [Select]

unsigned int Vmains ;
unsigned long Vmains_squared, Paccumulator ;
double  Vaccumulator, Scaling_Factor ;

Measurement Loop:

Vmains =  read_ADC
Vmains_squared = Vmains*Vmains

# multiply Paccumulator by 255/256. To do this, subtract 1/256th of the accumulator value from the accumulator:

Paccumulator = Paccumulator - (Paccumulator>>8)   # ie the Paccumulator value shifted 8 bits to the right to do the divide by 256

Paccumulator = Paccumulator + Vmains_squared
End of Measurement Loop


To Calculate RMS volts:

Code: [Select]
Vaccumulator = square_root( Paccumulator )   

Vresult = Vaccumulator * Scaling_Factor

Notice I used floating point "double" variables after the square root. This will give the right result, but will be slow on a small microcontroller. It may be adequate - something you need to try for yourself.

There are ways to do it faster using integers only, but they are not as simple as using floating point calculations. Methods like using lookup tables to do the square root function.

I am not regularly doing microcontroller programming, so I will not try and right some actual code. You will have to do that.

What is it doing? It is a kind of average where new readings have a strong influence, and old readings fade away with time. So if the new ADC  reading has an influence of 1.0 (an arbitrary number I just picked out of the air) on the result, the previous reading has an influence of 0.998. The one before that an influence of 0.996. The one before that 0.994. The one before that 0.992 and so on.  The 2000th previous reading only has 0.018 influence on the reading. The 5000th previous reading has an effect of 0.000044.

It is rather like an old-fashion moving needle meter - it continuously is showing the power rather then jumping to a new reading every second. This form of average just keeps on going without stopping and restarting.

Is this better then taking 4000 readings and then calculating a result and then taking another 4000 readings - Maybe. Maybe not. You will only find out by trying it for yourself.

Richard.
 
« Last Edit: February 25, 2013, 11:38:54 pm by amspire »
 

Offline VEGETATopic starter

  • Super Contributor
  • ***
  • Posts: 1905
  • Country: jo
  • I am the cult of personality
    • Thundertronics
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #9 on: February 25, 2013, 04:14:06 pm »
nice example.

but I don't know the exact benefit of this scaling factor and dividing by 256. And this scaling factor, is it 256?

Also, I need to measure at least 2 voltages not just one... will this kinda code work?

I thought of making 2 functions; one that outputs ADC result in 10-bits. the other one calculates RMS using your way or another one.

can I make it somehow that I choose the channel (2 channels at least) AND keeps continuous average?

Offline VEGETATopic starter

  • Super Contributor
  • ***
  • Posts: 1905
  • Country: jo
  • I am the cult of personality
    • Thundertronics
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #10 on: February 25, 2013, 04:29:07 pm »
I forgot to say that I intend to use PIC16F877A with 16MHz crystal... will that be enough to measure 2 signals and use a LCD to output values?


jucole

  • Guest
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #11 on: February 25, 2013, 05:03:52 pm »
I forgot to say that I intend to use PIC16F877A with 16MHz crystal... will that be enough to measure 2 signals and use a LCD to output values?

I've used a demo of Proteus ISIS, and from memory I think it was pretty good!  and I'm sure that this project is easily simulated in such a program?

Just out of interest, what frequency are the 2 lines?
 

Offline mariush

  • Super Contributor
  • ***
  • Posts: 4979
  • Country: ro
  • .
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #12 on: February 25, 2013, 05:16:10 pm »
The datasheet is here : http://ww1.microchip.com/downloads/en/devicedoc/30292c.pdf


At 16 mhz clock will give you 4 million cycles a second, each operation in the microcontroller taking one or more of these cycles... page 136 tells you how many cycles each instruction takes. Most take 1 cycle.
That's 4.000.000 cycles a second , 4000 cycles a ms, 4 cycles each uS.

At page 111, you have the ADC chapter, where it explains how to do conversions and estimate how much it takes. I'd say it takes roughly 25 uS to do one measurement is a safe value, page 114 has a formula you can use to measure aquisition time. 
25 uS is very little, that's 4 measurements in 100uS or 40 measurements in 1 ms ... so 4000 measurements are doable in 100ms

So if you're smart, you can initiate the adc to sample and instead of waiting 25-30uS for the conversion to finish, use those 25-30 uS to do some math with previous values you retrieved, and repeat everything. 25-30uS means about 100-150 cycles, quite a lot of additions, setting variables, shifting bits etc

The LCD is not a problem, once it's initialized at beginning of the program, you only reset the cursor and send a few bytes through a parallel connection. That takes a couple of ms at most.

ps. not sure if it would help you, as assembler is probably too advanced for you if you make mistakes in c code, but posting it anyway... here's some assembly multiplication routines, for example there are 10 bit x 10bit values multiplication in case you'll multiply the values you read:

http://www.piclist.com/techref/microchip/math/mul/index.htm

With a max of 73 cycles, it's easily possible to multiply and do other stuff while waiting for the adc to finish acquisition
« Last Edit: February 25, 2013, 05:24:32 pm by mariush »
 

Offline VEGETATopic starter

  • Super Contributor
  • ***
  • Posts: 1905
  • Country: jo
  • I am the cult of personality
    • Thundertronics
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #13 on: February 25, 2013, 05:25:59 pm »
well, I thought of something else:

Code: [Select]
for (i=0 ; timer_interrupt == 1; i++) {

v_adc = (ADCRead(channel)*5)/1023;     // outputs 0-5 value
v_squared = v_adc*v_adc;               // square the 0-5v signal
               
Pacc = Pacc + v_squared;               // store the result in an accumelator
}

v_mean = Pacc/i;
v_rms = sqrt(v_mean);                    // takes the sqrt of the accumelated values
v = (v_rms *350)/5;                        // as 350v is the peak of actual voltage... I will rectify it and use a voltage divider... but no filter, so it can be like sinewave shape but +

timer will count 20ms and then generates an interrupt... when it generates an interrupt, for loop is over and all calculated samples are gathered...

then this calculations occurs and you see that I didn't count the number of samples but let the code do it (Am I right here?).

this will update RMS value in each 20ms but will it be enough to measure 2-4 signals?

And, is this done right or should I adjust it?

PIC16F877A has about 368b of RAM...

Offline mariush

  • Super Contributor
  • ***
  • Posts: 4979
  • Country: ro
  • .
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #14 on: February 25, 2013, 05:44:00 pm »


Why use an interrupt in the first place ?

You know how much a a/d conversion takes, so you know how many conversions you can do in about 20-50 ms .. just  use an 8 bit variable, increment it with 1 with each adc reading, when you get to 255, reset it to 0 and do the calculations and show result on screen.

Read the datasheet and the explanations about analog to digital, pay attention to the details about how much time it takes to get a reading, and how much time you need to wait until you do a second reading.  I skimmed through and it says something about waiting 2 times the acquisition time between readings.

I'm not so found of dividing by 1023 there... and depending on compiler you're not saving much in processing but you're losing accuracy everywhere.

If you want to make it use less memory, maybe make it so that every result is 4 bit, a value between 0 and 15, not 0 to 5.  That way,  you'll have v_squared an unsigned byte, with a maximum value of 225, which fits inside the 0-255 possible.
 
 

Offline VEGETATopic starter

  • Super Contributor
  • ***
  • Posts: 1905
  • Country: jo
  • I am the cult of personality
    • Thundertronics
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #15 on: February 25, 2013, 06:27:21 pm »
A timer is hardware which makes it easier for the processor right?

I don't really know how much ADC takes and if I can, there are more and more lines to do not just the ADC.

that 8 bit variable is like unsigned int i that I used in for loop... Is it good?

the ADCread(....) is a function I wrote which has some lines... and I wrote another one called RMS_calculator that uses ADCread and have the for loop and other things.


the 1023 is the 5v ADC result... the previous formula outputs a 0-5 number that is the voltage at the ADC pin.

now, we can calculate the RMS value of these voltage readings and it will be 0-5v right?

after all that, we take that 0-5v RMS and convert it to 0-350v one... using the other formula.

see? these numbers have meanings... maybe I miss understand you anyway ^_^.

this 4 bit stuff I don't know why you choose them.

OK, put an example code from my previous for loop one (or any) that describes your idea please.

thank you

Offline mariush

  • Super Contributor
  • ***
  • Posts: 4979
  • Country: ro
  • .
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #16 on: February 25, 2013, 06:52:26 pm »
You use a voltage divider to downscale that  0-350v  to 0- 5v

So let's take some random voltages, let's say 180v  and 230v ..

What will the ADC see on the pin?

350v  ---- 5v
180v  ---- ?v   

? = 180x5/350 = 2.57v

350v  ---- 5v
230v  ---- ?v   

? = 230x5/350 =3.28v

The ADC gives you a 10 bit value.... that means 1024 values, between 0 and 1023.

1023 ---- 5v
?       ----- 2.57v

ADCRead will give you  2.57 x 1023 / 5 = 525.8=526


1023 ---- 5v
?       ----- 3.28v

ADCRead will give you  3.28 x 1023 / 5 = 671.08=671

So let's see in your code what happens :

v_adc = (ADCRead(channel)*5)/1023;     // outputs 0-5 value

v_adc = 526  * 5 / 1023 = 2.57  but since you save it to a char short  (1 byte or 2 byte variables) and not a floating point number as far as I can see, the value you store in v_adc is 3 (rounded up from 2.57)

v_adc = 671 * 5 / 1023 =  3.279 since you save it to an short or int (1 byte or 2 byte variables)  the value you store in v_adc is 3 (rounded down from 3.28)

So for both 180v and 230v  you come to the conclusion that both are 3 - you lost so much precision in the adc by shrinking down to 0-5 that you calculations are meaningless.


There's no reason why you'd want to divide that measurement you get from adc down to 0-5, you lose everything there. Just define v_adc as unsigned 2 bytes (unsigned short or whatever your choice of compiler likes to use) , define v_squared as an unsigned 4 byte variable (unsigned int or whatever) because it has to hold the multiplication of v_acc and that's 20 bit so it's more than 2 bytes.
Define that Pacc as unsigned 4 byte (unsigned int again)... 4 byte unsigned is 0 to 4,294,967,295  and since v_acc can be a maximum of 1024 x 1024 -1  (1.048.576) ... this pacc variable will be big enough to add up to about 3500 measurements without worrying that it may overflow.
Then divide by the number of measurements and do a square root to get a value between 0 and 1023 , and that's your voltage ... 1023 being 350v.


« Last Edit: February 25, 2013, 06:56:09 pm by mariush »
 

Offline VEGETATopic starter

  • Super Contributor
  • ***
  • Posts: 1905
  • Country: jo
  • I am the cult of personality
    • Thundertronics
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #17 on: February 25, 2013, 07:59:46 pm »
man I forgot to mention variables type, they are as follow:

Code: [Select]
unsigned int i;
float v_adc;
unsigned long v_squared;
double  v, v_rms, v_mean;
unsigned long Pacc;
unsigned short channel;

now, any modifications you suggest? and how can I take samples properly (timer or what...)?

Offline brainwash

  • Frequent Contributor
  • **
  • Posts: 463
  • Country: de
    • Hack Correlation
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #18 on: February 25, 2013, 10:36:39 pm »
A timer is hardware which makes it easier for the processor right?

I don't really know how much ADC takes and if I can, there are more and more lines to do not just the ADC.


Nope, the timer doesn't help the processor a bit, it's just there to keep time. If you use interrupts then you are putting more 'stress' on the CPU/MCU because it has to stop everything, save the state, run the interrupt, restore the state. You could use an interrupt in your case, but for updating the display, not for calculations.
Updating the display takes way more time than doing ADC and calculations so I would go with the option to reset all the calculations after each display. Instead, have a single main loop doing something like this:

start adc conversion
wait until conversion is done
:start loop
i = 0
....
read adc register into x
start adc conversion
do all kinds of averaging with x
i++
if i=1000 (or something)
  compute final rms value
  display value
  reset rms value
  i := 0
:end loop

As far as I can remember it takes a minimum of 13 cycles to do an A/D conversion on most PICs, so by doing your math in between you do not need to worry about that time. I assume your math takes at least 50 cycles to execute.
By the time the display is updated all the values you have collected are meaningless so it's reason enough to start again.
 

Offline VEGETATopic starter

  • Super Contributor
  • ***
  • Posts: 1905
  • Country: jo
  • I am the cult of personality
    • Thundertronics
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #19 on: February 25, 2013, 10:50:37 pm »
doing stuff while AD converts?

I don't know how is that possible... and I didn't take any value from ADC to calculate right?

in my ADCRead routine I wrote like:

while ( GO_DONE_Bit == 1 )

^
this makes the processor wait until ADC converts the sample.

Is that what you meant?

Offline brainwash

  • Frequent Contributor
  • **
  • Posts: 463
  • Country: de
    • Hack Correlation
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #20 on: February 25, 2013, 10:58:34 pm »
No, you do not need to use the while if you do the math in between. Just read the datasheet and check the Tus number that tells you how many cycles you need after starting a conversion.
So in my post "start adc conversion" corresponds to "GO_DONE_Bit = 1" (note, it's assign operation not equals). You can define a function "StartADCRead" do that for you.
Change the ADCRead routine to return the ADREG value without any of the GO_DONE manipulations or loops.

Oh, just in case your calculations take less than 13 cycles (which I HIGHLY doubt) the ADREG value will just have a lower resolution, so it's not breaking up the algorithm. What I'm trying to say is that if you omit the wait loop you probably don't lose anything. Just don't call readADC immediately after starting the conversion and you should be fine.
 

Offline amspire

  • Super Contributor
  • ***
  • Posts: 3802
  • Country: au
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #21 on: February 25, 2013, 11:58:44 pm »
nice example.

but I don't know the exact benefit of this scaling factor and dividing by 256. And this scaling factor, is it 256?
Appologies. I was typing that late at night and I got the comment all wrong. It is meant to say multiply by 255/256 and I instead said divide by 255 - I have fixed it now. The pseudocode was right, the comment was way out.

The effect of this is the running average is always at 256 x Average Vmains_squared.

The reason I used 256 is that the compiler does an 8 bit right shift very fast - it just copies the top three bytes of Paccumulator into the lower three bytes of a temporary unsigned long register, and subtracts it from Paccumulator. So the Paccumulator * 255/256 is done very fast without any multiplication or division.

If you want a slower average, you can increase the right shift. The slowest average would be done by shifting right 12 times. This would make Paccumulator 212 times Vmains_squared ie 4096. That is the limit that Paccumulator can take without overflowing.
Quote

Also, I need to measure at least 2 voltages not just one... will this kinda code work?

I thought of making 2 functions; one that outputs ADC result in 10-bits. the other one calculates RMS using your way or another one.

can I make it somehow that I choose the channel (2 channels at least) AND keeps continuous average?

It could but it always comes down to speed, and a 16F877A is challenged for speed in an application like this. You need to do a test to see how long it takes for the PIC to multiply two unsigned int variables together since it has to do this for every ADC reading. You can start with the inbuilt compiler multiplication, but to improve the performance, you may have to look at custom code to achieve the 10bit x 10bit multiplication in an absolutely minimal number of clock cycles. The more samples you can fit into mains cycle, the more accurate the resulting measurement.

Two ADC channels measuring at 100 samples per 50Hz cycle means one ADC reading has to be squared and added to the accumulator every 100 uS. It would be good to get far more readings per second. You will probably want to run the PIC at the maximum possible clock frequency.
 

Offline mariush

  • Super Contributor
  • ***
  • Posts: 4979
  • Country: ro
  • .
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #22 on: February 25, 2013, 11:59:22 pm »
Sigh... from datasheet .. page 113

These steps should be followed for doing an A/D Conversion:
1. Configure the A/D module:
• Configure analog pins/voltage reference and digital I/O (ADCON1)
• Select A/D input channel (ADCON0)
• Select A/D conversion clock (ADCON0)
• Turn on A/D module (ADCON0)

^ this you do at the start of the program, before reading values, once it's initialized you don't have to turn it off and on again and mess with it

2. Configure A/D interrupt (if desired):
• Clear ADIF bit
• Set ADIE bit
• Set PEIE bit
• Set GIE bit

^ This you don't care about because you won't use interrupts

3. Wait the required acquisition time.

^  The datasheet lists a formula a page or so below this text where it says the acquisition time is about 20 uS.  So after initializing the adc and before first conversion, and then between every conversion, your program needs to wait about 20 us for the controller to be "ready" to do the actual conversion

4. Start conversion:
• Set GO/DONE bit (ADCON0)

^ Self explanatory

5. Wait for A/D conversion to complete, by either:
• Polling for the GO/DONE bit to be cleared (with interrupts enabled); OR
• Waiting for the A/D interrupt

^ Instead of waiting with that while loop, you can do some math here, this is going to take just a few uS. The conversion goes separately and you know it's done when that bit is cleared so even if you do some math the result is going to be in those ADRESH and ADRESL after a few moments when you're done

6. Read A/D result register pair (ADRESH:ADRESL), clear bit ADIF if required.

^ put the value in a variable

7. For the next conversion, go to step 1 or step 2, as required. The A/D conversion time per bit is defined as Tad. A minimum wait of 2Tad is required before the next acquisition starts.

This basically says you can't just do something like this   for ( i =1; i<=100; i++) {  results=read_adc(); }
The adc becomes "unavailable" for a few us after the conversion is done and the bit is cleared and the result is stored and waiting for you. Then you also have the acquisition time of about 20us.
One Tad  for this pic16 controller is at least 1.6us, but it depends on how you set the AD clock - in your case it will actually be more like 2us.


page 114, note 4

4: After a conversion has completed, a 2.0TAD delay must complete before acquisition can begin again.
During this time, the holding capacitor is not connected to the selected A/D input channel.

Further on the next pages it says  (page 115):

11.2 Selecting the A/D Conversion Clock
The A/D conversion time per bit is defined as TAD. The  A/D conversion requires a minimum 12TAD per 10-bit conversion. The source of the A/D conversion clock is software selected. The four possible options for TAD are:
• 2TOSC
• 8TOSC
• 32TOSC
• Internal A/D module RC oscillator (2-6 ?s)
For correct A/D conversions, the A/D conversion clock (TAD) must be selected to ensure a minimum TAD time of 1.6 us.

and I'm going to quote something I saved a while ago:

Quote
Due to the settling times and bandwidths of the analog components involved, each comparison step takes a certain minimum amount of time and the operation is controlled by the A/D clock and it is important that this time per bit (Tad) not be less than a critical minimum otherwise these steps will fail to settle correctly and the A/D resolution will be impaired.

In the case of the 16F877, Tad is a minimum of 1.6us and although it is a 10 bit A/D, it actually takes 12 of these bit times as a couple of additional clocks are used for set up and data transfer.

Theoretically the A/D clock source could come from anywhere but since the PIC already has a master clock oscillator for the processor, it is convenient to use a submultiple of that or an onboard RC clock and the choices provided in the ADCON0 register are 2Tosc, 8Tosc, 32Tosc or RC.

If you are operating with an 8MHz clock, then Tosc = 0.125us so you will need a multiple of at least 12.8 ( the minimum 1.6us / 0.125us). If you wish to use the processor clock, you would have to choose the 32Tosc option as 8Tosc & 2Tosc would be too fast. 32Tosc will result in a Tad of 32x0.125 = 4us and a total A/D conversion time (the time from starting the A/D to getting a result) of 12 x 4 = 48us.

You could also choose the internal RC clock, but at 4uS typical, it would be about the same 48us conversion time.

In addition to the A/D conversion time, there is an analog input settling time (referred to in the data sheet as acquisition time). It is controlled by the source resistance and the A/D input capacitance (see Example 11-1 in the data sheet) and can easily add another 12-20us to the conversion time.

The two times (acquisition + conversion) would then be the total time required after applying a new analog input voltage, waiting for the input to settle, commanding the A/D to start, waiting for it to convert and ending up with a 10 bit result (possibly 60-70us total).

He did the math for a 8 Mhz clock. At 16 Mhz, you're still going to have to set the ADC for 32Tosc but  Tad will actually be half of that, 2us, which is ok because it's higher than 1.6us. And since the conversion needs 12 x Tad to convert, you're looking at 24us instead of the 48us he determined.

So for 16 Mhz, you have: 

Tad = ~ 2 us
Taquisition = ~ 20 us
Tconversion =  12 Tad = ~ 24 us
pause between conversions  = 2 Tad = ~ 4 us


and you have in the loop :
 
 wait  ~ 20 uS  for aquisition or so something in this time
 set the bit to go so that conversion will start ...  from this point, the conversion takes about 12 Tad = 24 us so in the meantime you can do something. Results will go automatically in those registers and bit cleared when done
 wait 2Tad  (about 4 us) between conversions because the adc is not available
 go back to waiting 20us before you can request another conversion, first line in loop

If however you treat the first conversion as a "special case", you can change the loop to something like this:

Code: [Select]
// initialize adc here, set the clock to 32 Tosc, turn it on

value = -1; // this stores the adc result
i =  0;
delay_us(25); // wait at least 25 us, the aquisition time

while (1=1) {
  adc.go = 1;  // start the conversion process, this is gonna run separately for at least 24us or so, then it's gonna need 2xTad to be ready for next conversion so do some stuffstuff
  if (value!==-1) { 
     v_square = value * value; // this is gonna take about 150-200 cycles, at 16 Mhz that's about  4 cycles per us so about 40 us (varies from compiler to compiler how it optimizes it). by the time this multiplication is done the conversion should be ready
     v_sum = v_sum + v_square;  // about half a us
      i++ ; increment the number of readings
  }
  while (adc.go==1) { }; // just in case we finished faster than the conversion or it's the first time in loop so the if never executed, wait until conversion is done
  value =  0 to 1023; //  adresshi (two bits ) + addresslo  (8 bits )   <-  this is not correct but you get the point, put the value from those 2 registers in the variable "value", to be used in next iteration
  // now you're ready to do another reading but you need to give it time, those 2Tad and the aquisition so try to do more stuff here instead of just freezing the controller with a delay_us().
  // between the moment the adc generated result and next measurement, the adc needs that 2 x Tad + Tacquisition, about 20us + 2xTad (2x2us) .. about 24us
  // the multiplication alone above in the if may have taken 40us or more but we can't be sure it was more than this Tacquisition + 2xTad + 12 Tad (actual conversion time) = 20 + 4 + 24 = 68us that's required for the whole process. If you do your own multiplication routing,  you'd no exactly how many cycles would be used, therefore how many us would take to multiply, then use a lower delay value.
  // so wait some more
  delay_us(30);  // 30 us should be a safe value
  if (i>1000) { // or whatever amount of values you think it's suitable
     // divide the v_sum  to i 
     // print to lcd
     i = 0;
  } // this if should take about 100us but it's only done when sending to lcd, not every time, you don't update the lcd 1000 times a second, the lcd is not that fast.. maybe 10 times a second is even too much

}



This will get a reading every 60-80us or so, or about 8-10 readings a ms ... that should be enough.

« Last Edit: February 26, 2013, 12:09:48 am by mariush »
 

Offline VEGETATopic starter

  • Super Contributor
  • ***
  • Posts: 1905
  • Country: jo
  • I am the cult of personality
    • Thundertronics
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #23 on: February 26, 2013, 10:25:56 am »
I saw this IC on internet: http://www.linear.com/product/LTC1966

it says that it needs 1Vpeak! now how can I properly convert 350v peak to 1v peak?

jucole

  • Guest
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #24 on: February 26, 2013, 11:06:37 am »
I saw this IC on internet: http://www.linear.com/product/LTC1966

it says that it needs 1Vpeak! now how can I properly convert 350v peak to 1v peak?

I found an application note which might give you some pointers (page 2)  I think the RMS converter chip is a good solution.

http://www.eetasia.com/ARTICLES/2007JUN/PDF/EEOL_2007JUN19_POW_AN.pdf?SOURCES=DOWNLOAD
 

Offline mariush

  • Super Contributor
  • ***
  • Posts: 4979
  • Country: ro
  • .
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #25 on: February 26, 2013, 01:17:55 pm »
I saw this IC on internet: http://www.linear.com/product/LTC1966

it says that it needs 1Vpeak! now how can I properly convert 350v peak to 1v peak?

It says how right there in the datasheet! There's even an example circuit there.  Why don't you bother to read the datasheets?

There's plenty of ICs that do this trueRMS  stuff but I thought you had a school project or something to do it by yourself, not to use some ready made stuff.

If you can use pre-existing chips, you might as well use something like AD736, which is what a lot of multimeters use :

http://www.analog.com/static/imported-files/data_sheets/AD736.pdf

But if you plan to use it, read the datasheet carefully, it's easier to use than that ltc1966 but you still can't just give it 350v and call it a day
 

Offline BravoV

  • Super Contributor
  • ***
  • Posts: 7547
  • Country: 00
  • +++ ATH1
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #26 on: February 26, 2013, 01:24:42 pm »
Agree with Marius, don't be lazy, just download and "read" the datasheet and especially the "app note", there is an example interfacing with high voltage like mains measuring.

Offline VEGETATopic starter

  • Super Contributor
  • ***
  • Posts: 1905
  • Country: jo
  • I am the cult of personality
    • Thundertronics
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #27 on: February 26, 2013, 07:26:51 pm »
I tried the AD736 IC in Proteus ISIS and made a circuit according to the data sheet... photos in the attachments both data sheet circuit and my circuit.

And it still give me 0v output... I had no luck with LTC1966 one as it still gives me 0v.

What do you think I need to do?

I will use a voltage divider to trim the 350vp to 5vp (or 1 as the IC requires) to feed the IC input. But this DC/AC coupling stuff I have a problem with.

thanks.

Offline JVR

  • Regular Contributor
  • *
  • Posts: 201
  • Country: be
Re: RMS Voltage calculation usin PIC16F877A with C-code
« Reply #28 on: March 11, 2013, 09:06:29 am »
Jeez, you gents like to sukkel.

Set the ADC to convert at 1kSPS, then make two buffers of 32 values at 16 bits.
Now get the ADC interrupt (since you don't have DMA) to push a sample into the buffer at each int.
When the buffer fills, you simply switch to buffer 2, and start processing buffer 1.
Run through and check for zero-crosses, isolate the points in the buffer, and you should have 20 points for a 50Hz signal.
Run RMS algorithm on the buffer.

Tune the code above, and its good for <1% accuracy.  I've built a power monitoring system that does 17 channels at once, 16 RMS currents, and one RMS voltage.  Total accuracy was at 0.3%
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf