Displaying AVR ADC value in LUX on the LCD

Status
Not open for further replies.

ArdyNT

Full Member level 2
Joined
Nov 6, 2012
Messages
126
Helped
7
Reputation
14
Reaction score
7
Trophy points
1,298
Visit site
Activity points
2,304
Need Help: Displaying AVR ADC value in LUX on the LCD

The following equation is derived from the datasheet of photo-resistor:

E[lux] = 10000 / (R[kΩ]*10)^(4/3)

It clearly shows the relation between RESISTANCE and LUX, NOT relation between ADC and LUX

My problem is how do I use this equation in my code so I can change the 10bit ADC value of my avr into LUX.
I got confuse since the ADC value is not equal to the resistance, right?

Here is part of my code (codevisionavr):

Code:
    while (1)
      {
        ldr1=read_adc(0);   // [I][COLOR="#00FF00"]I need it to be LUX, help me to modify it[/COLOR][/I]
        lcd_gotoxy(0,0);
        sprintf(buf,"L1:%d%d%d%d",ldr1/1000,(ldr1%1000)/100,(ldr1%1000%100)/10,ldr1%1000%100%10);
        lcd_puts(buf);
        
        ldr2=read_adc(1);
        lcd_gotoxy(9,0);
        sprintf(buf,"L2:%d%d%d%d",ldr2/1000,(ldr2%1000)/100,(ldr2%1000%100)/10,ldr2%1000%100%10);
        lcd_puts(buf);
      }


Thanks..
 
Last edited:

The ADC always measures a voltage, then depending on where this voltage is measured you can calculate different things.

If you want to find the resistance you place the unknown resistor in series with a known resistor and create a voltage divider connected to a known voltage.

Have you used a voltage divider , post a schematic.
 

Yes, I've used voltage divider. Here it is:



and it should be like this right:

Vout = 5 x [ (10 000)/(LDR + 10 000) ]
 

Yes your equation is correct so , LDR= (50000/Vout)-10000

Vout of your ADC is ADC * Vref/1023

- - - Updated - - -

If you combine these two equations you get

LDR= (10230000/ADC) - 10000
 
Reactions: ArdyNT

    ArdyNT

    Points: 2
    Helpful Answer Positive Rating
I'm sorry, I still got confuse.

Right now I have three equations:

1. E[lux] = 10000 / (R[kΩ]*10)^(4/3)

2. Vdvd = 5 x [ (10 000)/(LDR + 10 000) ]

3. ADC = (Vdvd * 1023)/V.ref

The problem is, how can I modify my code to display "E[lux]"
 

I assume your ADC is set to Vref 5v?

If so then the 2 and 3 are combined and Resistance equals LDR= (10230000/ADC) - 10000

If you get an ADC result of 400 then LDR= (10230000/400) - 10000 = 15575 ohm

Then use this Resistance in the LUX equation
 

Yes, I connected the Vref pin to 5 Volt.

Ok, now I understand how it works. Next, I modify the code to be like this:

Code:
while (1)
{
a=read_adc(0);
ldr1=(10230000/a) - 10000;
Lux=10000/((ldr1*10)^(4/3));   [I][COLOR="#FF0000"]  // No sure about this line, is it right ? (also previously ldr1 is in k.Ohm in eq. 1)[/COLOR][/I]
        
lcd_gotoxy(0,0);
sprintf(buf,"L1:%d%d%d%d",Lux/1000,(Lux%1000)/100,(Lux%1000%100)/10,Lux%1000%100%10);
lcd_puts(buf);
}
 
Last edited:

Lux=10000/((ldr1*10)^(4/3));

Note that in an equation like this the calculation will be done using integers, this means that 4/3 will be truncated and will give a result of 1.
For floats you have to use 4.0/3 or typacast (float)4/3.

The symbol ^ will also not work as intended, this is the XOR symbol in C not power, for power check the math.h library https://www.nongnu.org/avr-libc/user-manual/group__avr__math.html
 

I tried this:

Code:
#include <mega8535.h>
#include <stdio.h>
#include <delay.h>
#include <math.h>

unsigned char buf[10];

unsigned int a;
volatile unsigned int ldr1;
float x;
float lux;

// Alphanumeric LCD functions
#include <lcd.h>
#asm
.equ __lcd_port=0x15 ;PORTC
#endasm

#define ADC_VREF_TYPE 0x40


// Read the AD conversion result
unsigned int read_adc(unsigned char adc_input)
{
ADMUX=adc_input | (ADC_VREF_TYPE & 0xff);
// Delay needed for the stabilization of the ADC input voltage
delay_us(10);
// Start the AD conversion
ADCSRA|=0x40;
// Wait for the AD conversion to complete
while ((ADCSRA & 0x10)==0);
ADCSRA|=0x10;
return ADCW;
}


void main(void)
{
PORTA=0xFF;
DDRA=0x00;

// ADC initialization
ADMUX=ADC_VREF_TYPE & 0xff;
ADCSRA=0x84;
SFIOR&=0xEF;

lcd_init(16);


while (1)
      {
        a = read_adc(0);
        ldr1 = (10230000/a) - 10000;
        x = 4.0/3.0;
        lux = 10000/(pow((ldr1*10),x));
        
        lcd_gotoxy(0,0);
        sprintf(buf,"L1:%d%d%d%d",lux/1000,(lux%1000)/100,(lux%1000%100)/10,lux%1000%100%10);
        lcd_puts(buf);
       }
}

But I got error message: "Error: operand types 'float' and 'int' are incompatible with the '%' or '%=' operator"
 

For float modulo there is a function in math.h
https://www.nongnu.org/avr-libc/user-manual/group__avr__math.html#gaefa8edb8c13adf7fb4b6b7dbe7261a24

bu it makes no sense to use it for what you are trying to do.

I would suggest you use sprintf (I mean the float version %f) to convert the number to ASCII string

- - - Updated - - -

The other solution is to multiply the float with a power of 10 (depending on the significant digits you want) and convert it to an integer (truncate the remaining decimal digits using typecasting) which can then be converted using the modulo process you do
 

Ough, I got confuse now.
Seems I like I dont know so many things yet.

Is there any other sources with clear explanation or maybe some simple examples related with this math operations?


Aa, about the second solution, I prefer it. I've read some articles about that.
 

The conversion of a float using sprintf is like
Code:
char my_string[16];
float my_float=123.456789;

// one way to convert the float to string is
sprintf(my_string,"%f",my_float);  // you will get "123.456789"

// the other way is to use a specified number of decimals
sprintf(my_string,"%.2f",my_float);  // you will get "123.45"

The other way you say you prefer is to mupliply 123.456789*1000 , then typecast it (uint32_t)1234567 ad then convert it using midulo

To use sprintf with floats you have to add :
the libraries libprintf_flt.a and libm.a (in that order), and
a custom linker option -Wl,-u,vfprintf

- - - Updated - - -

for float sprintf in AVR refer to
 

This is the new one, using the second suggested solution:
Code:
#include <mega8535.h>
#include <stdio.h>
#include <delay.h>
#include <math.h>
#include <stdlib.h>

char str[100];
unsigned int a1;
float a2;
float x;
float lux;
int m1;
float d1;
int m2;


// Alphanumeric LCD functions
#include <lcd.h>
#asm
.equ __lcd_port=0x15 ;PORTC
#endasm

#define ADC_VREF_TYPE 0x40


// Read the AD conversion result
unsigned int read_adc(unsigned char adc_input)
{
ADMUX=adc_input | (ADC_VREF_TYPE & 0xff);
// Delay needed for the stabilization of the ADC input voltage
delay_us(10);
// Start the AD conversion
ADCSRA|=0x40;
// Wait for the AD conversion to complete
while ((ADCSRA & 0x10)==0);
ADCSRA|=0x10;
return ADCW;
}


void main(void)
{
PORTA=0xFF;
DDRA=0x00;

// ADC initialization
ADMUX=ADC_VREF_TYPE & 0xff;
ADCSRA=0x84;
SFIOR&=0xEF;

lcd_init(16);


while (1)
      {
        a1 = read_adc(0);
        a2 = (10230000/a1) - 10000;    
        x = 4.0/3.0;     
        lux = 10000/(pow((a2*10000),x));  [COLOR="#00FF00"]// Let say the result is xxx,yyyy[/COLOR]
        
        m1 = lux;                        [COLOR="#00FF00"]// Get the integer part (xxx)[/COLOR]
        d1 = lux - m1;                 [COLOR="#00FF00"]// Get the fractional part (0.yyyy)[/COLOR]
        m2 = trunc(d1*10000);     [COLOR="#00FF00"]// Turn into integer (yyyy)[/COLOR]
        
        lcd_gotoxy(0,0);
        sprintf(str,"L1:%d.%04d\n", m1,m2);
        lcd_puts(str);
      }
}

First: Am I doing it right?
Second: I got this problem now=> Error: undefined symbol 'trunc'


I'm sorry, I just get your updated post, so I did something a bit different. (internet conn. problem)
 
Last edited:

trunc is defined in math.h so I'm not sure of the problem but it is not needed .

The decimals will be truncated anyway when you typecast the number and store it in the integer
m2 = (int)(d1*10000); // only the integer part will be stored in m2

I'm not sure why you have used signed types for m1 and m2 , do you expect negative lux?

I would suggest
Code:
unsigned int m1, m2;
 m2 = (unsigned int)(d1*10000);

sprintf(str,"L1:%u.%04u\n", m1,m2);
 

No no, no negative lux, I'll change it. My mistake.

Well, I removed trunc and follow your suggestion. I got no error now but the LCD displays "L1:0.0000" all the time even I changed the resistance.

and next I just want to know what is the difference between:

Code:
sprintf(str,"L1:%u.%04u\n", m1,m2);
with
Code:
sprintf(str,"L1:%d.%04d\n", m1,m2);
 

d is for signed, u is for unsigned.
You can read any sprintf tutorial to see all the parameter explanation.

Put manually a value instead of the ADC result in the equation and see if you get a result.
For example use 511.
 

Ok, I will.

I tested this:

Code:
        a1 = read_adc(0);
        a2 = (10230000/a1) - 10000;    
        x = 4.0/3.0;     
        lux = 123.5656       [COLOR="#FF0000"]// I change this[/COLOR]
        
        m1 = lux;               // Get the integer part (xxx)
        d1 = lux - m1;          // Get the fractional part (0.yyyy)
        m2 = d1*10000;          // Turn into integer (yyyy)
        
        lcd_gotoxy(0,0);
        sprintf(str,"L1:%u.%04u\n", m1,m2);
        lcd_puts(str);

And I got the right display. but when I change it back to
Code:
lux = 10000/(pow((a2*10000),x));

it display 0.0000 again. Is the way I write the exponent right?
 

Status
Not open for further replies.

Similar threads

Cookies are required to use this site. You must accept them to continue using the site. Learn more…