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

Easy method of percentage calculation
Goto page 1, 2, 3  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
darryl_co



Joined: 11 Nov 2015
Posts: 21
Location: India

View user's profile Send private message

Easy method of percentage calculation
PostPosted: Thu Nov 12, 2015 2:57 am     Reply with quote

Hi,
Is there any better way to calculate than this code
a= 100;
x=((a*1023)/100);
// x would be the percentage of ADC input, to be used to calculate the time on and off of output pin.Is there a code through which just the starting two digits of a can be used.I tried
rotate_left( &a, 1);
but the result is 200,need the result to be 10.I want to try to avoid division function as the calculation takes longer time and adding long delay in the code and output waveform
Ttelmah



Joined: 11 Mar 2010
Posts: 19552

View user's profile Send private message

PostPosted: Thu Nov 12, 2015 4:31 am     Reply with quote

Several comments:
First, 1023 is wrong.

The PIC ADC's are scaled (except for a very few of the very earliest chips), so that they behave as if they counted from 0 to 1024, with the top count missing. They read 1023 counts 1.5 steps _below_ the reference voltage. This is done deliberately, so that code never has to divide by 1023, but can use 1024 instead.
The earliest chips were the exceptions to this (for accuracy on these /1023 is needed). This change was done as the result of some work that TI did at this time, and they suggested that this would be a better way to scale micro-processor ADC's, and rather nicely, did not attempt to patent the idea, and so over the next few years, just about every chip manufacturer started doing this.

I don't see that your maths would even remotely give percentage....

The percentage of range would be given by (adc*100)/1024. So at a value of 512, you would get 512*100 = 51200. 51200/1024 = 50. 50 percent.

Now straight away this is easier, since /1024, the compiler will perform using rotations, not having to get involved in divisions at all....

Even better, you can cheat the multiplication as well:
Code:

    unsigned int16 adc;
    unsigned int32 temp;
    adc=read_adc(); //obviously whatever suits you to get the ADC value
    temp=adc*4;
    temp=temp*8+temp*16+temp+50;
    //Now all of this is done by the compiler using rotations. Much quicker.
    temp/=1024;
    printf("%2d",(int8)temp);
    //this way the print only uses int8


This give 0 to 99 output, and is quite efficient (takes just 84 instruction cycles).
The compiler 'knows' it can perform *2, *4 etc., using rotations, and the same is true for /2, 4, 8 etc..

Try it. Smile


Last edited by Ttelmah on Thu Nov 12, 2015 9:05 am; edited 1 time in total
alan



Joined: 12 Nov 2012
Posts: 357
Location: South Africa

View user's profile Send private message

PostPosted: Thu Nov 12, 2015 7:09 am     Reply with quote

Also depends on actual chip as some of them have a hardware multiplier.

Regards
darryl_co



Joined: 11 Nov 2015
Posts: 21
Location: India

View user's profile Send private message

better and faster way to multiply and divide
PostPosted: Thu Nov 12, 2015 2:54 pm     Reply with quote

I am posting the complete code and need help in calculation the value of timeH in a better and faster way. Using multiply and divide is adding unnecessary delay. The chip is PIC12F675.
Code:
#include <12F675.h>

#device ADC=10
#fuses INTRC_IO,NOWDT,NOPUT,NOPROTECT,NOCPD,MCLR,
#use delay(clock=4000000)
#define GP0 PIN_A0      // ADC input
#define GP1 PIN_A1      // output 1
#define GP2 PIN_A2      // output 2
#define GP3 PIN_A3       // MCLR = Reset Pin
#define GP4 PIN_A4
#define GP5 PIN_A5
#byte OSCCAL = 0x80

void init()
{
OSCCAL = 0x80;                   // set internal oscillator to mid frequency
set_tris_a( 0b11111101 );          // set GP1 output, all other inputs
setup_comparator( NC_NC_NC_NC );    // disable comparators
setup_adc_ports( AN0_ANALOG );       // AN0 Analog,rest Digital enable analog inputs
setup_adc( ADC_CLOCK_INTERNAL );    // enable A2D
set_adc_channel(0);
delay_us(10);
}

void main()
{
int16 step;         // Output frequency step in uSec
int16 ADCval;      // ADC value at GP0 PIN_A0
int16 ADCref=50;   // Start at 5% ADC reference
int ADCpct;          // ADC percentage   
int16 timeH;         // Time output is HIGH
int16 timeL;      // Time output is LOW

init();

   while ( 1 )
      {
      for(step=33;step<500;step++)    // Loop control
         {
         //step=500; // Manual setting for testing/debugging
         ADCval = read_adc();      // Read the value of ADC input
          // Compare  ADC with reference(ADCref) value and calculate the
          //the percentage if changed and add 1 so percentage is never ZERO
         // MAXIMUM INTERESTED IN 50% DUTY CYCLE
         // ADCval = 1023; // Manual setting for testing/debugging
         if (ADCval!=ADCref) (ADCpct=(((ADCval*100)/1023)+1)) & (ADCref=ADCval);
         ADCpct = ADCpct+ADCpct; // use only 50% to give full value
         //ADCpct = 100; // Manual setting for testing/debugging
         ///////////////////////////////////////////////////////
         timeH = ((ADCpct*step))/100);   // Calculate ON  Time
         //////////////////////////////////////////////////////
         timeL = (step - timeH);          // Calculate OFF Time
         output_high( GP1 );          // turn GP1 on
         delay_us(timeH);            // Time output GP1 is ON
         output_low( GP1 );             // turn GP1 off
         delay_us(timeL);            // Time output GP1 is OFF
         delay_cycles(1);            // NOP
         output_high( GP2 );          // turn GP2 on
         delay_us(timeH);            // Time output GP2 is ON
         output_low( GP2 );             // turn GP2 off
         delay_us(timeL);            // Time output GP2 is OFF
         }
      }
}

Ttelmah



Joined: 11 Mar 2010
Posts: 19552

View user's profile Send private message

PostPosted: Thu Nov 12, 2015 3:42 pm     Reply with quote

As I've explained, 1023 is wrong. Also, /1023, uses hundreds of cycles, where /1024, uses a handful.
Then there is a huge problem. ADCval can be up to 1023. What is 1023*100?. Will this fit in an int16?.....
This is why I use int32.

As a further comment though, since you are only using 100 levels, why not just turn the ADC down to 8bit?. ADC then reads quicker, and uses less code, and the maths can be done in an int16. Also you are degrading the accuracy of the ADC. _Read the data sheet_. Is ADC_CLOCK_INTERNAL recommended for 4MHz?.

Generally though, using 'percent' is silly here. Use 'per64', or 'per128' perhaps. A binary value. Then all the multiplications and divisions become _binary_ operations.
darryl_co



Joined: 11 Nov 2015
Posts: 21
Location: India

View user's profile Send private message

shifting and spliting
PostPosted: Mon Nov 23, 2015 3:15 pm     Reply with quote

If I change the value
#device ADC=10; to #device ADC=8;
the ADC value would read 8 bit 0 to 255.If I further use
ADCval=(ADCval>>1); Range would be 0 to 127.As I want max duty cycle 50 percent, I am interested in the range 0 to 64.Is it possible to get in formula (without multiplication or division) two values 6 and 4 as two variables.Split the value of ADCval into two.Example if ADCval=57 than 5 and 7, if 32 than 3 and 2 and so on

Also I am unable to find the formula for finding percentage as mentioned "per64" or "per128".
Ttelmah



Joined: 11 Mar 2010
Posts: 19552

View user's profile Send private message

PostPosted: Mon Nov 23, 2015 3:46 pm     Reply with quote

Percent, just means in hundredths.
Per64, just means in 64ths. Per128, just means in 128ths.
No magic, just as there is no formula for 'percent', except using * or / 100, the same applies to per64 or per128.

For an integer, the compiler 'knows' that it can divide by binary values by simple rotation, so adc_val/4, for the 8bit adc gives you per64 sections for the output. Work out what time you want for each 64th, and "Robert's your Father's brother".....
darryl_co



Joined: 11 Nov 2015
Posts: 21
Location: India

View user's profile Send private message

splitting the value
PostPosted: Mon Nov 23, 2015 11:51 pm     Reply with quote

Ttelmah wrote:
Percent, just means in hundredths.
Per64, just means in 64ths. Per128, just means in 128ths.
No magic, just as there is no formula for 'percent', except using * or / 100, the same applies to per64 or per128.

For an integer, the compiler 'knows' that it can divide by binary values by simple rotation, so adc_val/4, for the 8bit adc gives you per64 sections for the output. Work out what time you want for each 64th, and "Robert's your Father's brother".....

Thanks for the explanation. Can you please help me with the value splitting. That way I can use shift left or right to get multiplication and division done faster
guy



Joined: 21 Oct 2005
Posts: 297

View user's profile Send private message Visit poster's website

PostPosted: Fri Nov 27, 2015 2:34 pm     Reply with quote

If what you want is to optimize runtime, use a 64-entry constant lookup table. Then for each of the 64 adc values you will immediately get the percentage.
I suspect it will be quicker and also take up less code space than division/multiplication.
temtronic



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

View user's profile Send private message

PostPosted: Sat Nov 28, 2015 6:07 am     Reply with quote

Guy is right about the lookup table, even a 128 and maybe a 256 (full 8 bit ADC) table will be faster, especially if you have one division in the calculation.
You can also 'tune' or configure the table to reflect different requirements. IE needing a servo to be semilog, or an LED to be linear.

Jay
asmboy



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

View user's profile Send private message AIM Address

PostPosted: Sat Nov 28, 2015 4:38 pm     Reply with quote

The OP obsession with percent does not make any sense to me.

if it is INFORMATIONAL - for a display or transmission to a host - i could see that . But if it is for process related decisions - it is utterly foolish.

AS is the case to at least 4 sigmas - the OP should carefully consider what
MR. T advised:
Quote:

using 'percent' is silly here. Use 'per64', or 'per128' perhaps.


it is simplicity itself to use 'per1024' for that matter ;-))
Anybody who seriously works with PICs for a living knows this already.........
temtronic



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

View user's profile Send private message

PostPosted: Sun Nov 29, 2015 6:17 am     Reply with quote

been mulling this one after having the furnace blow up in my face (just another day...), so forgive the rough math but...

If you use the ADC in 10 bit mode, you've got 1024 bits. 1% is say 10 bits. Now a 'regular' wiring of the ADC input pin _might_ be good for say +-3or 4 bits, add a 'bit' of noise and you're only good to +-5 bits. Use a filter like Olympic averaging to get a more stable result. If you 'dum down' that to 8 bits it'd be real close to being a percentage.

Someone else can 'do the math'.....seeing how we don't know the 'specs' for the project. 'percent' ? +-1 or +-.1 ??

Jay
Ttelmah



Joined: 11 Mar 2010
Posts: 19552

View user's profile Send private message

PostPosted: Sun Nov 29, 2015 9:40 am     Reply with quote

If you look at what he is currently doing, he is just modulating the time of the pulse train based upon the ADC reading. This can be done just as easily using per256, by just changing the range of values used for 'step', to 13 to 192. No need for anything but a single multiplication by step.....
darryl_co



Joined: 11 Nov 2015
Posts: 21
Location: India

View user's profile Send private message

PostPosted: Sun Nov 29, 2015 1:35 pm     Reply with quote

Thanks everyone for your help. I am a noob just trying my first pic project with your help.After the much discussion I feel that the best way to multiply is shift_left and shift_right for division which I found to be faster than actual multiplication or division by 4 or 8 o16,etc.
I feel lookup table may not be practical in my case as I have 255-30=225 steps and finding the high time for each step would be 225*64(only fifty percent duty cycle).
To explain the percentage part, I am using the ADC readout as percentage and calculating the time the output is high.If a full 10 bit readout is
128=100%, (1024 bit shifted and reduced 127 or instead of 10 bit set ADC=8 bit an than shift_right 1 bit)
than 32=25%
step=100
timeH=25% of 100us= 25us
timeL=step-timeH=100us-25us=75us // Duty cycle of output is 25 percent
RF_Developer



Joined: 07 Feb 2011
Posts: 839

View user's profile Send private message

PostPosted: Mon Nov 30, 2015 5:26 am     Reply with quote

Darryl, what are you trying to do here? What is the real problem? For a first PIC project, you are concentrating a lot on something most of us would never bother about - absolute code speed.

I have an application where I need speed like this. What I needed was as many ADC conversions and calculations per second as was possible. I could have done what you are trying to do: make the calculations as fast as possible as the loop time was the time taken by the ADC conversion plus the time for the calculations and output of the result. But that's naive and far from optimal. Instead I paralleled processing with conversion, so while the ADC is converting the next sample, my code is calcuating the result of the last. Now the loop time is the time needed for by the ADC alone. All processing time determines is the latency between the ADC finishing and the result being output.

When I was young I was obsessed with code speed and optimisation: how to make it go as fast as possible. Pretty soon I learned that that's not the way to get fast code. The way is to make the algorithm, the way the calculations are done, as simpe and elegant as possible. In a way that's what you are doing by asking how to calculate percentages in a faster way.

Ttelmah has shown you alternative ways of doing thing to avoid the slower calculations. I have just suggested a way of using processor time more efficiently. All these are better ways of going faster than trying to do basic calulations faster, and yes, percentages are basic. Peronally I'd try simplfying it to 8 bit integers, which are native to the PIC and on which operations are as fast as possible - an 8x8 mulitply is faster than any bit shift and add equivalent - as they are probably close enough. A look-up table is probably the fastest, but don't try to make it const as that means it may well be in program memory and looking up/reading may well take longer than simple calculations. Though that depends on how the compiler decides to store and access tha table.

So, I am suggesting you look again at what you are really trying to do. What is the actual requirement, and what really needs to be fast and why. Then look again at how that can be done.
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, 3  Next
Page 1 of 3

 
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