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

calculating logarithms PIC18F452 [SOLVED]

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
artohautala



Joined: 17 Nov 2011
Posts: 187

View user's profile Send private message Send e-mail

calculating logarithms PIC18F452 [SOLVED]
PostPosted: Wed Nov 04, 2015 6:49 am     Reply with quote

Hello,

I know maybe my question is beyond of this discussion forum...

but I'll be very happy if someone will answer my question and friendly advice me how to do ...

So I have ambi light sensor from modern device

https://moderndevice.com/product/ambi-light-sensor/

and it's output current depends logarithmic way when ambient light is changing ...

My output load is 100 k resistor

I made today some measurements and I think these results are OK:

light result from A/D converter PIC18F452 10 bits 0... 2013

100 lx 200
300 lx 254
1000 lx 303
3000 lx 345
9500 lx 394
30000 lx 442

... light sensor internal resistance is about 94 k and my load resistor is 100 k ...

.... I use 5 V power supply so max. voltage from light sensor is 2.58 V to 100 k load...

... so max. ad-result is (2.58/5) * 1024 = 528 (10 bits ad-conversion, ref. voltage = 5 V)

I made excel curve about those values and it seems to me that's linear OK...

So I want to display those light values in luxes maybe using log10() function ...

but I don't know how to make it work ....

Sorry I'm very poor in mathematics javascript:emoticon('Embarassed')

friendly asking and best regards

-arto hautala-


Last edited by artohautala on Wed Nov 11, 2015 11:24 am; edited 1 time in total
alan



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

View user's profile Send private message

PostPosted: Wed Nov 04, 2015 7:09 am     Reply with quote

Depends on how sensitive your application is to timing.
If not just use the log10() function provided by CCS in the math.h file.

Regards
Ttelmah



Joined: 11 Mar 2010
Posts: 19537

View user's profile Send private message

PostPosted: Wed Nov 04, 2015 9:01 am     Reply with quote

You don't want logarithm.
You want exponential.

Thing is the sensor gives a logarithmic output, from Lux. For you to generate 'Lux', from it's output, you need the inverse equation.

Lux=0.7648*e^(0.0239*adc)

The comment already made about speed, obviously applies.

Also, you may well find the results are significantly better if you buffer the voltage. The currents involved (especially at low values), are well below the values recommended to feed the PIC ADC...

Code:

#include <math.h>

   float lux;
   int16 adc_val;

   //then read the adc
   //suggest probably average for a more stable result....

   adc_val=read_adc();
   Lux=0.7648*exp(adc_val*0.0239);

   //Then print your lux value


So for a reading of 200, you get
200*0.0239 = 4.78
e^4.78 = 119
119*0.7648 = 91Lux

While for 442, you get 29603

Which is as close as you are going to get for a simple exponent, with the values given.
artohautala



Joined: 17 Nov 2011
Posts: 187

View user's profile Send private message Send e-mail

PostPosted: Wed Nov 04, 2015 10:40 am     Reply with quote

Ttelmah wrote:
You don't want logarithm.
You want exponential.

Thing is the sensor gives a logarithmic output, from Lux. For you to generate 'Lux', from it's output, you need the inverse equation.

Lux=0.7648*e^(0.0239*adc)

The comment already made about speed, obviously applies.

Also, you may well find the results are significantly better if you buffer the voltage. The currents involved (especially at low values), are well below the values recommended to feed the PIC ADC...

Code:

#include <math.h>

   float lux;
   int16 adc_val;

   //then read the adc
   //suggest probably average for a more stable result....

   adc_val=read_adc();
   Lux=0.7648*exp(adc_val*0.0239);

   //Then print your lux value


So for a reading of 200, you get
200*0.0239 = 4.78
e^4.78 = 119
119*0.7648 = 91Lux

While for 442, you get 29603

Which is as close as you are going to get for a simple exponent, with the values given.


Ok,

Thank you very much for answering me

I don't understand those numbers:

Lux=0.7648*exp(adc_val*0.0239);

Maybe because of natural logaritm base number 0.7648 ?

Where comes this number 0.0239 ?

I tried it but it goes over 65535 ...

I know max. is about 30000 for 442 adc result...

Here's my code following your good advice :


Code:


           
    loop:   
         
         //get_light() returns unsigned int16 adc value 0...1023, here max.  442 for 30000 lux       
         light = get_light();
         Lux = exp(light*0.0239);               //Lux is float
         Lux = Lux * 0.7648;   
         show_calibration((unsigned int16)Lux);
         delay_ms(1500);
        clear_lcd();
         delay_ms(100);
    goto loop;     


Thank you very much helping me !
best regards
-arto hautala_
Ttelmah



Joined: 11 Mar 2010
Posts: 19537

View user's profile Send private message

PostPosted: Thu Nov 05, 2015 2:39 am     Reply with quote

The numbers are simply values I calculated to make the exp curve work, from the ADC values you quoted.

If you are getting over 65535, then you are getting a momentary ADC reading over 475. As I said, I would say you need to buffer the ADC, and average the results. At the high impedance this circuit is using, you _will_ be getting inaccurate/noisy results.
artohautala



Joined: 17 Nov 2011
Posts: 187

View user's profile Send private message Send e-mail

PostPosted: Fri Nov 06, 2015 8:59 am     Reply with quote

Ttelmah wrote:
The numbers are simply values I calculated to make the exp curve work, from the ADC values you quoted.

If you are getting over 65535, then you are getting a momentary ADC reading over 475. As I said, I would say you need to buffer the ADC, and average the results. At the high impedance this circuit is using, you _will_ be getting inaccurate/noisy results.


Thank you for your good advice ... Smile

I changed load resistor to 68 k (67.7 k accurate)

because I know there's maximum light is below 65535 lux ...


I made some better measurements today and internal resistance is 92 k

here'my results:

current to gnd uA Lux Uout in sensor Uout to A/D A/D result
5 3 0,46 0,19 40
10 10 0,92 0,39 80
15 32 1,38 0,58 120
20 100 1,84 0,78 160
25 316 2,30 0,97 199
30 1000 2,76 1,17 239
35 3162 3,22 1,36 279
40 10000 3,68 1,56 319
45 31623 4,14 1,75 359
50 100000 4,60 1,95 399

so with 35 current should be 3126 in theory now it is 930 ....

//Lux = exp(light*0.0239); //Lux is float
//Lux = Lux * 0.7648;

so how I have to change those numbers 0.0239 and 0.7648 ??

Sorry I'm really bad in mathematics and I think I'll never learn it !

all the best and nice and happy weekend for you !

-arto-
Ttelmah



Joined: 11 Mar 2010
Posts: 19537

View user's profile Send private message

PostPosted: Fri Nov 06, 2015 2:59 pm     Reply with quote

You are still omitting the part about buffering the ADC. The ADC draw _current_. Typically quite a few uA, and requires a reasonably low impedance to recharge the internal capacitor when it connects to the voltage. This is why the data sheet has the line:
"The maximum recommended impedance for analog
sources is 2.5 kΩ."

You are not going to get anything approaching accuracy till you re-design your circuit. I'm not going to waste my time re-calculating the curve, till you produce real values actually read by the ADC.
artohautala



Joined: 17 Nov 2011
Posts: 187

View user's profile Send private message Send e-mail

PostPosted: Sun Nov 08, 2015 6:34 am     Reply with quote

Ttelmah wrote:
You are still omitting the part about buffering the ADC. The ADC draw _current_. Typically quite a few uA, and requires a reasonably low impedance to recharge the internal capacitor when it connects to the voltage. This is why the data sheet has the line:
"The maximum recommended impedance for analog
sources is 2.5 kΩ."

You are not going to get anything approaching accuracy till you re-design your circuit. I'm not going to waste my time re-calculating the curve, till you produce real values actually read by the ADC.


HI,
thank's a lot for your patience advicing me ...

today I'm spending rainy sunday afternoon and I add buffer amplifier to buffer PIC A/D pin A0...
my buffer is microchip MCP6272 rail to rail opamp (only one opamp is in use because I have no one opamp version MCP6273)...
and I average A/D results measuring 10 times and then I devide 10
Here's my average code:

Code:

//*********************************************************************
unsigned int16 get_light(void){
       
        unsigned int8   counter;
        unsigned int16  an_value;
        unsigned int32  sum_an_value;
       
        set_tris_a(0xFF);
        setup_adc_ports(RA0_ANALOG);       // pin A0 measures voltage level of light sensor
        setup_adc( ADC_CLOCK_INTERNAL );
       
        //measure analog value 10 times and then average it:
       
        sum_an_value = 0;
       
        for(counter = 0; counter < 10; ++ counter){
           set_adc_channel(0);
           delay_ms(20);
           an_value = read_adc();           //A/D conversion
           sum_an_value = sum_an_value + an_value;
        }
       
        sum_an_value =  sum_an_value / 10 ;         
        an_value = sum_an_value;         
     
       return an_value;
}

//*********************************************************************


I made measurements and I found it don't matter if it is buffered or not ...

same thing about averacing results ...

now whit very bright led bulp I get number 988 A/D result ,,, I think it's reasonable result... so ambi light sensor send 4.8 V ...

I measured ambilight sensor output impedance by halving output voltage and I get value
92 kohms ... (I did'nt find any value from datasheet ??)

so in theory there must be like that: (+5 V reference for A/D)

2 lux = 5 uA to ground = 0,46 V = 94 (A/D result)
10 lux = 10 uA to ground = 0,92 V = 188
100 lux = 20 uA to ground = 1,84 V = 376
1000 lux = 30 uA to ground = 2,76 V = 565
10000 lux = 40 uA to ground = 3,68 V = 753
30000 lux = 45 uA to ground = 4,14 V = 847
50000 lux = 48 uA to ground = 4,42 V = 904

I think 50000 lux is good maximum value in practice ...

But my problem is how to change those A/D results to lux values because I'm really bad in math and maybe I
never learn mathematics because I am so dummy... (sorry I don't know right word)

I'll be very gratefull if you help me to solve this problem ...

all the best for you and have fun !

-arto-
temtronic



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

View user's profile Send private message

PostPosted: Sun Nov 08, 2015 8:05 am     Reply with quote

don't know about the math but...

1) I'd change the averaging function from 10 counts to 16. You may not notice the result being different but the code is smaller ! Compiler will simply shift to divide by 16, very fast operation.

2) you(or a friend) should be able to put your results into eXcel or Matlab and 'reverse engineer' to create the math required for the numbers you supply.


Jay
Ttelmah



Joined: 11 Mar 2010
Posts: 19537

View user's profile Send private message

PostPosted: Sun Nov 08, 2015 8:14 am     Reply with quote

OK.

Notice how much larger the actual swing is on the ADC. It's now using 80% of the ADC input range. A lot more hopeful.

However a 'caveat'. Plotting the curve, as an exponential, the top value is off the curve (high), and the bottom one is also a little off. I think you are running into a range limit somewhere.

Try 1.0243*e^0.0122*adc

This does not fit terribly well at either end, but gives the best possible fit through all the other readings.

So adc=753, gives 9999. adc=376, gives 100, but your '904' reading actually converts as 60000, while the bottom reading comes in at 3.2.

If you look at the actual numbers, if it was genuinely logarithmic, given that 10Lux is 10uA, 2 Lux should be just over 3uA, and 50000 Lux should be 46.9uA. the 10, 100, 1000, 10000 & 30000 values all fit perfectly, it is just the end two that go a little wrong....
artohautala



Joined: 17 Nov 2011
Posts: 187

View user's profile Send private message Send e-mail

PostPosted: Sun Nov 08, 2015 8:21 am     Reply with quote

temtronic wrote:
don't know about the math but...

1) I'd change the averaging function from 10 counts to 16. You may not notice the result being different but the code is smaller ! Compiler will simply shift to divide by 16, very fast operation.

2) you(or a friend) should be able to put your results into eXcel or Matlab and 'reverse engineer' to create the math required for the numbers you supply.


Jay


HI Jay,

thank's for your good advice Smile

<1) I'd change the averaging function from 10 counts to 16. You may not notice the result being different but the code is smaller ! Compiler will simply shift to divide by 16, very fast operation.>


In my application speed is not so important ...

<2) you(or a friend) should be able to put your results into eXcel or Matlab and 'reverse engineer' to create the math required for the numbers you supply.>

Yes sure I have no matlab in my computer but excel sure I have ...

it's very fun to try all kind of everything using excel and that's what I'm doing right now ...

I am happy to be your friend I hope you are also my friend !

all the best in the world for you .... and thank's answering me !

(sorry my bad english)

best regards

-arto-
artohautala



Joined: 17 Nov 2011
Posts: 187

View user's profile Send private message Send e-mail

PostPosted: Sun Nov 08, 2015 8:47 am     Reply with quote

Ttelmah wrote:
OK.

Notice how much larger the actual swing is on the ADC. It's now using 80% of the ADC input range. A lot more hopeful.

However a 'caveat'. Plotting the curve, as an exponential, the top value is off the curve (high), and the bottom one is also a little off. I think you are running into a range limit somewhere.

Try 1.0243*e^0.0122*adc

This does not fit terribly well at either end, but gives the best possible fit through all the other readings.

So adc=753, gives 9999. adc=376, gives 100, but your '904' reading actually converts as 60000, while the bottom reading comes in at 3.2.

If you look at the actual numbers, if it was genuinely logarithmic, given that 10Lux is 10uA, 2 Lux should be just over 3uA, and 50000 Lux should be 46.9uA. the 10, 100, 1000, 10000 & 30000 values all fit perfectly, it is just the end two that go a little wrong....


Sorry Thelmah,

there was typing error ,,, 5 uA current to ground should be 3 lux not 2 lux...

I understand that it must not be so accurate ... so I will next follow your good advïce using

1.0243*e^0.0122*adc

so my ambient device behaves log10 but compiler do not understand antilog 10 ??

but it use exp

so I have to change log10 to ln (base number 2.718)

relationship between ln / log10 = 2.303

I'll try this math in excel...

thank's for your patience and all the best

-arto-
Ttelmah



Joined: 11 Mar 2010
Posts: 19537

View user's profile Send private message

PostPosted: Sun Nov 08, 2015 9:21 am     Reply with quote

Most mathematicians will only ever use e^x, and ln.
Base ten is only a matter of applying a scaling.

It's like sin, where mathematicians tend always to use radians rather than degrees. Very few things (in fact I can't think of any), involve base 10. Base ten is a human creation. Very Happy

Because your source numbers are 'adc counts', not 'volts', there is going to have to be a scaling applied, so it makes no basic difference if you use e^x, or 10^x.

The compiler has log10, and log functions. Anti log, is just 10^x, which the compiler can do with pow. However key is that pow, is done internally using exp, and log10, is done by using log. Since you are going to have to scale anyway, it is more efficient to use the 'root' functions, rather than their 'descendants'.

Just to take one as an example:
Code:

float32 log10(float32 x)
{
   float32 r;

   r = log(x);
   r = r*LN10_INV;
   return(r);
}

So 'log10', is just solved by taking log (base e), and multiplying the result by LN10_INV (0.43429228....).

The compiler actually has a define for the log10 conversion (LN10), and it's reciprocal (used above).

If there was a typing error on the 3Lux value, then the curve fits beautifully except for the very top reading, Smile
artohautala



Joined: 17 Nov 2011
Posts: 187

View user's profile Send private message Send e-mail

PostPosted: Sun Nov 08, 2015 10:15 am     Reply with quote

Ttelmah wrote:
Most mathematicians will only ever use e^x, and ln.
Base ten is only a matter of applying a scaling.

It's like sin, where mathematicians tend always to use radians rather than degrees. Very few things (in fact I can't think of any), involve base 10. Base ten is a human creation. Very Happy

Because your source numbers are 'adc counts', not 'volts', there is going to have to be a scaling applied, so it makes no basic difference if you use e^x, or 10^x.

The compiler has log10, and log functions. Anti log, is just 10^x, which the compiler can do with pow. However key is that pow, is done internally using exp, and log10, is done by using log. Since you are going to have to scale anyway, it is more efficient to use the 'root' functions, rather than their 'descendants'.

Just to take one as an example:
Code:

float32 log10(float32 x)
{
   float32 r;

   r = log(x);
   r = r*LN10_INV;
   return(r);
}

So 'log10', is just solved by taking log (base e), and multiplying the result by LN10_INV (0.43429228....).

The compiler actually has a define for the log10 conversion (LN10), and it's reciprocal (used above).

If there was a typing error on the 3Lux value, then the curve fits beautifully except for the very top reading, Smile


So I think this is enough accurate for me ,,, thank's for your help again ... maybe I'll try
to study more math...

now it works fine in practice ,,, thank's for your good advices...


Code:


    loop:   
         
         F_light = (unsigned int16)get_light();
         
         //1.0243*e^0.0122*adc
         
         F_light = 1.0243 *exp(0.0122* F_light);
         
         if(F_light > 65535)
         F_light = 65535;
         
         show_calibration((unsigned int16)F_light);
         
         delay_ms(1500);
         clear_lcd();
         delay_ms(100);
   
    goto loop;




sure I know it is bad to use loop: goto loop but this is only test purpose ....

maybe I close this discussion but I'm very happy you helped me Smile

all the best

-arto-
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
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