Using Photocell with AVR Atmel86

Go To Last Post
14 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've been trying different things with my AVR chip, trying things starting with timed LED lines to produce words, and trying 7 segment display counters.

All of those just require the AVR chip to output such and such.

I want to try something different and was told I could use a photocell. I want to use a photocell to measure Photoquantigraphic Quantity, q, aka measure of light. I understand the property of the photocell in which the resistance decreases when more light is shined on it. I want to do a project to display the q value on my 7 segment display taking inputs from the photocell.

So I start with my question:
1. How do I use inputs (specific pins) on my AVR to set the outputs as I have been just programming the MCU to just display what I want to display.
2. Can the AVR detect the amount of voltage being input into the MCU?

I understand I can code a voltage division characteristics onto the MCU based on the resistors and have a voltage go to the MCU input and based on that input display the q value on the 7 segment.

I'm kind of blabbering about my logic and help/direction is appreciated.

THANKS :) FUN TO COME

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just use the ADC, if your AVR has one. Details will be in the data sheet.

Leon

Leon Heller G1HSM

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

do you mean to use PORTC as input?

Attached Pin layout for my ATMEGA chip.

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Make a voltage divider with a resistor and your photo cell. Choose a value of resistor about equal to the resistance of the photo cell in room light. Connect the divider between AVcc and ground. Connect the point where the resistor and the photo cell joint to ADC0. Set up the ADC to use AVcc as reference.

With all that, you might be 10% of the way there.

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the help Jim.

I've purchases a CdS Photoresistor that is >20M in the Dark and <200 ohms in light.

I have this flashlight with 5 bulbs, so I can produce a test of q0, 2xq0, 3xq0, 4xq0, 5xq0. (What is the best way to test for their values, if no wattage value is given on the device).

A mini lab of hooking up the CdS to the AVR and have it detect the 5 different ranges of bulbs. Using a lookup table, I can then have it show on an AVR how many bulbs I have lit then.

http://img413.imageshack.us/img4...

I've included a rough paint schematic. Is the Resistor and CdS correct, or should they be switched. For this current setup, using a resistor with the same value of the CdS at room light. As light is decreased more voltage would be input, up to a max of 5 V.

I have a programming question for the AVR part. How can you use the AVR to detect different voltage inputs. I've only set a program to run on the AVR, but never using given inputs.

ie
DDRC = 0x00; set all PORTC to input

How do I check the input?

Thanks for the help :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

To detect an analog voltage between 0..5V you must use ADC. Digital inputs are used to detect 0 & 5V only. You can use several input from PC0 to PC5.. Please refer datasheet at ADC part.

edit:
Oops don't forget to connect AVCC pin. On most application even you don't use any analog part inside AVR you still must connect AVCC pin to 5V power supply. Usually i bond it directly with VCC. But some design considerations must be taken if you're running at noisy environment because the noise could mess up your ADC reading.

KISS - Keep It Simple Stupid!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Also connect the Agnd pin!

The ADC will be needed!

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So I updated my layout. Connecting AVcc directly to Vcc and GND to AGND.

When you both say ADC will be needed. Is this referring to PIN 23 PC0(ADC0).

http://img517.imageshack.us/img5...

Thanks again for help.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, Pin23 is a multi-function pin. How it operates depends on the settings of internal registers. You need to read the section about Analog/Digital Converter.

It may be a challenge, but it is what you need!

Jim

 

Until Black Lives Matter, we do not have "All Lives Matter"!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've completed the schematic on my breadboard.

Here is my program

// ECE 516
// spinme.c
// AVR and LEDs to spell something while being spun
// Feb 29 2008
// Students: Mike, Rio, Kevin

/* HEX TABLE for reference
	0000	0
	0001	1
	0010	2
	0011	3
	0100	4
	0101	5
	0110	6
	0111	7
	1000	8
	1001	9
	1010	A
	1011	B
	1100	C
	1101	D
	1110	E
	1111	F
*/

// AVR IO memory space
#include 
#include 

// Make sure power reduction is off to enable ADC.
/*	char sample[2];
	short int optical;
    PRR &= ~(1<<PRADC);
	
	currentADC = 0;
	ADMUX = 0x40 + currentADC; // read ADC0
	ADCSRA |= 0xC7; //tells you when conversion is finished page 257
    while((ADCSRA&0xC7)==0xC7); 
    sample[0] = ADCL; xxxxxxxx 1111111 = 255
    sample[1] = ADCH; 000000xx00000000 & 1111111100000000
	optical = (sample[1]<<8)& 0xFF00 | sample[0] 0-1023
*/

void display(int number) {
	int first;
	int second;
	if (number >=99)
		first = 9;
		second = 9;
	else
		first = int(number / 10);
		second = int (number % 10); 
	if  (first >= 9)
		PORTD = 0x6F;
	else
		PORTD = firstnumber (first);
		
	if (second >=9)
		PORTB = 0x6F;
	else
		PORTB = secondnumber (second);
}

int firstnumber (int number){
	switch (number)
		case 0:
			return 0x31;
			break;
		case 1:
			return 0x06;
			break;
		case 2:
			return 0x5B;
			break;
		case 3:
			return 0x4F;
			break;
		case 4:
			return 0x66;
			break;
		case 5:
			return 0x6D;
			break;			
		case 6:
			return 0x7D;
			break;
		case 7:
			return 0x07;
			break;
		case 8:
			return 0x7F;
			break;
}


int secondnumber (int number){
	switch (number)
		case 0:
			return 0x31;
			break;
		case 1:
			return 0x06;
			break;
		case 2:
			return 0x5B;
			break;
		case 3:
			return 0x4F;
			break;
		case 4:
			return 0x66;
			break;
		case 5:
			return 0x6D;
			break;			
		case 6:
			return 0x7D;
			break;
		case 7:
			return 0x07;
			break;
		case 8:
			return 0x7F;
			break;
}


int main(){

	char sample[2];
	short int optical;
    PRR &= ~(1<<PRADC);
	
	currentADC = 0;
	ADMUX = 0x40 + currentADC; // read ADC0
	ADCSRA |= 0xC7; //tells you when conversion is finished page 257
    while((ADCSRA&0xC7)==0xC7); 
    sample[0] = ADCL; 
    sample[1] = ADCH; 
	optical = (sample[1]<<8)& 0xFF00 | sample[0];

  // Set up regular i/o ports:
  DDRB	= 0xFF;   // Port B: input
  PORTB	= 0x00;   //              with all pull-up resistors activated

  DDRC	= 0x00;   // Port C: input
  PORTC	= 0xFF;   //              with all pull-up resistors activated

  DDRD	= 0xFF;   // Port D: output
  PORTD	= 0x00;

  // Loop to display "SPIN ME"
  while(1) {
    display ((int)(optical/10));


/*
	delay(5);

	// S
	PORTD = 0x00;
	PORTD = 0x8F;
	PORTD = 0x89;
	PORTD = 0x91;
	PORTD = 0x91;
	PORTD = 0xF1;
	PORTD = 0x00;

	// P
	PORTD = 0x00;
	PORTD = 0xFF;
	PORTD = 0x11;
	PORTD = 0x11;
	PORTD = 0x11;
	PORTD = 0x1F;
	PORTD = 0x00;
	
	// I
	PORTD = 0x00;
	PORTD = 0x00;
	PORTD = 0xFB;
	PORTD = 0xFB;
	PORTD = 0xFB;
	PORTD = 0x00;
	PORTD = 0x00;

	// N
	PORTD = 0x00;
	PORTD = 0xFF;
	PORTD = 0x0C;
	PORTD = 0x10;
	PORTD = 0x30;
	PORTD = 0xFF;
	PORTD = 0x00;

	// space
	delay(2);

	// M
	PORTD = 0x00;
	PORTD = 0xFF;
	PORTD = 0x0C;
	PORTD = 0x10;
	PORTD = 0x0C;
	PORTD = 0xFF;
	PORTD = 0x00;
	
	// E
	PORTD = 0x00;
	PORTD = 0xFF;
	PORTD = 0x91;
	PORTD = 0x91;
	PORTD = 0x91;
	PORTD = 0x91;
	PORTD = 0x00;
*/
	// No overflow detection needed; it just wraps around to 0.
  }

  return 0;
}

What happens is it stays in a single state once powered. Either displaying 3, 5, 6, or a backwards 7?

It doesn't seem to be checking properly. I think my LED could also be drawing too much power from the battery, thus its not powering the MCU Properly?

Any advice.

THANKS

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
int firstnumber (int number){
   switch (number)
      case 0:
         return 0x31;
         break;
      case 1:
         return 0x06;
         break;
      case 2:
         return 0x5B;
         break;
      case 3:
         return 0x4F;
         break;
      case 4:
         return 0x66;
         break;
      case 5:
         return 0x6D;
         break;         
      case 6:
         return 0x7D;
         break;
      case 7:
         return 0x07;
         break;
      case 8:
         return 0x7F;
         break;
}


int secondnumber (int number){
   switch (number)
      case 0:
         return 0x31;
         break;
      case 1:
         return 0x06;
         break;
      case 2:
         return 0x5B;
         break;
      case 3:
         return 0x4F;
         break;
      case 4:
         return 0x66;
         break;
      case 5:
         return 0x6D;
         break;         
      case 6:
         return 0x7D;
         break;
      case 7:
         return 0x07;
         break;
      case 8:
         return 0x7F;
         break;
} 

Any reason why you don't use array? Like this looks like better:

#include 

const int displaypattern[9] PROGMEM= {0x31,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F};

PORTD = pgm_read_byte(&displaypattern[first]);

Do you just want to read the ADC for once only? it seems on global loop while(1) there was no any command to read ADC. It's just calling display((int)(optical/10)).

And what's the point doing this:

optical = (sample[1]<<8)& 0xFF00 | sample[0]; 

Instead this:

optical = ADC;

Your code seems like just translation from Assembly to C, right?

KISS - Keep It Simple Stupid!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the reply.
I think I would know more in elec. So fairly new in programming. Just trying things out and seeing if they work.

I made some adjustment. Noticed I set the 7seg for 0 wrong. I added a delay function (not interupt, sorry) to give it 1/2 a second to display before going into search mode again.

// AVR IO memory space
#include 
#include 
#include  

void ourDelay(int count) {
  int i;
  for(;count>0;count--) {
    for(i=8000000; i>0;i--) {
      i--;
      //PORTB = 0xFF;  // fighting against optimization. Otherwise turn opt off.
    }	
  }
}

int main(){

	char sample[2];
	short int optical;
	int currentADC = 0;
    PRR &= ~(1<<PRADC);

	// Set up regular i/o ports:
	DDRB = 0xFF;   // Port B: output
	PORTB = 0x00;   //              

	DDRC = 0x00;   // Port C: input
	PORTC = 0xFF;   //              with all pull-up resistors activated

	DDRD = 0xFF;   // Port D: output
	PORTD = 0x00;
	
	ADMUX = 0x40 + currentADC; // read ADC0
	ADCSRA |= 0xC7; //tells you when conversion is finished page 257

	while(1) {
  	while((ADCSRA&0xC7)==0xC7); 
    	sample[0] = ADCL; 
    	sample[1] = ADCH; 
		//optical = (sample[1]<<8)& 0xFF00 | sample[0];
		optical = ADC
		
		number = (int) (optical/10);
		int first;
		int second;
		if (number >=99) {
			first = 9;
			second = 9;
		}
		else {
			first = (int)(number / 10);
			second = (int) (number % 10); 
		}	

		const int displaypattern[10] PROGMEM= {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F, 0x6F}; 
		PORTD = pgm_read_byte(&displaypattern[first]); 
		PORTB = pgm_read_byte(&displaypattern[second]);
		
		ourDelay (30);
		//display ((int)(optical/10));
	}

	return 0;
}

Thoughts?

I'll try it now and hopefully no flashing lights. *CROSS FINGERS*

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well it didn't work :( I have 3 different outputs, but not changing unless when switched on or off.

Another thing I thought of, if I put a resistor with room light resistance as a voltage divider. Then 2.5 Volts input would be my max and 0 would be my min (assuming i'm just dealing with room light).

So I changed it to just divide the 10 bit input by 5 (could give a slightly higher number like 7 to give it a little more range above).
So if its likely 500 input, it would be its max unless I shined light onto it.

----------------------------
I've tested it by not using PORTB, but rather just outputting the larger digit to PORTD and try to read it. But what pops up is not even remotely close to a hard coded number

I'm 100% sure my code is correct number wise, because it's using the same values as a 10 second counter I made prior.
7 Seg layout: http://img205.imageshack.us/img2...

I've also seeing some feedback on the system, but putting my finger over the photoresistor, I see the output of the 7 seg change. But alas, the output is not readable, or even look like a number. I also tried throwing in a 0.5-1 second delay every time it is to display a number value. Help is much appreciated.

Thanks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Figured out the problem. Now I can get it to display 1-9 (Uncalculated).

For the code
ADCSRA |= 0xC7; //tells you when conversion is finished page 257

while(1) {
while((ADCSRA&0xC7)==0xC7);
sample[0] = ADCL;
sample[1] = ADCH;
//optical = (sample[1]<<8)& 0xFF00 | sample[0];
optical = ADC

should be
while(1) {
ADCSRA |= 0xC3; //tells you when conversion is finished page 257
while((ADCSRA&0xC3)==0xC3);
sample[0] = ADCL;
sample[1] = ADCH;
//optical = (sample[1]<<8)& 0xFF00 | sample[0];
optical = ADC

Reason being I need to set the bitfactor to 8 and increase the sampling frequency from 64 kHz to 128 kHz.