|
|
View previous topic :: View next topic |
Author |
Message |
darryl_co
Joined: 11 Nov 2015 Posts: 21 Location: India
|
Easy method of percentage calculation |
Posted: Thu Nov 12, 2015 2:57 am |
|
|
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: 19549
|
|
Posted: Thu Nov 12, 2015 4:31 am |
|
|
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.
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
|
|
Posted: Thu Nov 12, 2015 7:09 am |
|
|
Also depends on actual chip as some of them have a hardware multiplier.
Regards |
|
|
darryl_co
Joined: 11 Nov 2015 Posts: 21 Location: India
|
better and faster way to multiply and divide |
Posted: Thu Nov 12, 2015 2:54 pm |
|
|
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: 19549
|
|
Posted: Thu Nov 12, 2015 3:42 pm |
|
|
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
|
shifting and spliting |
Posted: Mon Nov 23, 2015 3:15 pm |
|
|
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: 19549
|
|
Posted: Mon Nov 23, 2015 3:46 pm |
|
|
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
|
splitting the value |
Posted: Mon Nov 23, 2015 11:51 pm |
|
|
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
|
|
Posted: Fri Nov 27, 2015 2:34 pm |
|
|
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: 9245 Location: Greensville,Ontario
|
|
Posted: Sat Nov 28, 2015 6:07 am |
|
|
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
|
|
Posted: Sat Nov 28, 2015 4:38 pm |
|
|
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: 9245 Location: Greensville,Ontario
|
|
Posted: Sun Nov 29, 2015 6:17 am |
|
|
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: 19549
|
|
Posted: Sun Nov 29, 2015 9:40 am |
|
|
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
|
|
Posted: Sun Nov 29, 2015 1:35 pm |
|
|
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
|
|
Posted: Mon Nov 30, 2015 5:26 am |
|
|
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. |
|
|
|
|
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
|