NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » Photoresistor to ADC

May 04, 2011
by Twarter369
Twarter369's Avatar

Hey everyone. I am working on a small project and I am stuck on some of the MCU code. Basically, what I am trying to do is read from a Photoresistor (exact type is unknown)into the ADC (PC5). If the voltage drops past my predetermined Threshold for x milliseconds I want it to toggle an LED (pin PB1). I am stuck on the code to read the ADC and check it against my threshold. Once that is done I can check the state of the pin and act accordingly.

May 04, 2011
by mongo
mongo's Avatar

You can set it up as one side of a voltage divider and set the threshold on the low input that is usually AGND. It doesn't necessarily have to be power supply ground.

May 05, 2011
by Ralphxyz
Ralphxyz's Avatar

Hi Twarter369, are you using the Nerdkit tempsensor ADC code?

If so you can just monitor adc_read(). Of course the jumpy ADC might bother you.

Post your code lets see how you are doing it.

Ralph

May 08, 2011
by Twarter369
Twarter369's Avatar

@ Mongo, I believe I am using it as 1 half of a Voltage Divider. It goes 5v->PR->ADC1 and R1. I was told that this would drop the voltage in relation to the amount of shadow on the PR. Is there a way for me to post the schematics to this thread?

@ Ralph I started with the Temp sensor project. I modified the code so that if a certain temp was reached it would flip a pin. However that was the entire program. I also need to morph this into an interrupt so that I can do other things while waiting for this pin to change.

Can someone show an example of

IF(PC1 goes lower than 3v){
   //do my awesome thing then clear the ADC for the next time
}

I think I am going to hook up my lcd and print some readings to the screen. I would LIKE to be able to convert adc_read to voltage...but I don't even know where to start on that :)

P.S. sorry for the wait on my reply I got side tracked with another project!

May 08, 2011
by Ralphxyz
Ralphxyz's Avatar

Hi Twarter369, essentially adc_read() is representing 0 to 5 volts within 1024 steps.

Well the 0 to 5volts is a generalization it is dependent on your actual power supply value.

You should be able to figure out the voltage, or a real close approximation.

Ralph

May 08, 2011
by Twarter369
Twarter369's Avatar

OHHHHH that is so helpful. I can use that to test the rest of the system on the LCD.

May 14, 2011
by Twarter369
Twarter369's Avatar

Okay, so I believe I have hit another wall in this subject. I am working with this code

#define F_CPU 14745600
#include <stdio.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "libnerdkits/delay.h"
#include "libnerdkits/lcd.h"
#include "libnerdkits/uart.h"

// PIN DEFINITIONS:
//All pins are set to output right now in the future the ADC pins may be used to take in analog
//readings from a temp sensor and a photo resistor
//
//PB0 = 
//PB1 = LED 10
//PB2 = LED 09
//PB3 = Buzzer +
//PB4 = Relay switch
//PB5 = Motor Foward
//
//PC0 = Motor Backwards
//PC1 = Motor Stall
//PC2 = Future home of the Temp Sensor
//PC3 =
//PC4 = Photo-Resistor 
//PC5 =

void adc_init() {
  // set analog to digital converter
  // for external reference (5v), single ended input ADC0
  ADMUX = 0;

  // set analog to digital converter
  // to be enabled, with a clock prescale of 1/128 (max prescale)
  // so that the ADC clock runs at 115.2kHz.
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);

  // fire a conversion just to get the ADC warmed up
  ADCSRA |= (1<<ADSC);

  //make PC4 input pin
  //DDRC &= ~(1<<PC4);
  //turn on pullup resistor
  PORTC |= (1<<PC4);

  //Enable PIN Change Interrupt 1 - This enables interrupts on pins
  //PCINT14...8 see p70 of datasheet
  PCICR |= (1<<PCIE1);

  //Set the mask on Pin change interrupt 1 so that only PCINT12 (PC4) triggers
  //the interrupt. see p71 of datasheet
  PCMSK1 |= (1<<PCINT12);

  // set PB1, PB2,PB3, PB4 and PB5 as outputs
  DDRB |= (1<<PB1) | (1<<PB2) | (1<<PB3) | (1<<PB4) | (1<<PB5);

}

uint16_t adc_read() {
  // read from ADC, waiting for conversion to finish
  // (assumes someone else asked for a conversion.)
  // wait for it to be cleared
  while(ADCSRA & (1<<ADSC)) {
    // do nothing... just hold your breath.
  }
  // bit is cleared, so we have a result.

  // read from the ADCL/ADCH registers, and combine the result
  // Note: ADCL must be read first (datasheet pp. 259)
  uint16_t result = ADCL;
  uint16_t temp = ADCH;
  result = result + (temp<<8);

  // set ADSC bit to get the *next* conversion started
  ADCSRA |= (1<<ADSC);

  return result;
}

double sampleToVoltage(uint16_t sample) {
  // conversion ratio in mVolts/STEP:
  // (5000 mV / 1024 steps) 
  return sample * (5000.0 / 1024.0);  
}

void lightChanged(){

  uint16_t last_sample = 0;
  double this_samp;
  double samp_avg;
  double first_samp;
  uint8_t i;

    // take 100 samples and average them. This might 
    //be used to determine the ambient light for this operation.
    samp_avg = 0.0;
    for(i=0; i<100; i++) {
        last_sample = adc_read();
        this_samp = sampleToVoltage(last_sample);
        // add this contribution to the average
        samp_avg = samp_avg + this_samp/100.0;
    }
}

//Set up the interrupt handler
ISR(PCINT1_vect) {
    PORTB |= (1<<PB1);
    delay_ms(100);
    PORTB &= ~ (1<<PB1);
    PORTB |= (1<<PB1);
    delay_ms(100);
    PORTB &= ~ (1<<PB1);
    PORTB |= (1<<PB1);
    delay_ms(100);
    PORTB &= ~ (1<<PB1);
}

void connected(){   
    // toggle pins for test.
    PORTB |= (1<<PB1);
    delay_ms(100);
    PORTB |= (1<<PB2);
    delay_ms(500);
    PORTB |= (1<<PB3);
    delay_ms(500);
    PORTB &= ~ (1<<PB3);
    //Print the test char to the uart.
    printf_P(PSTR("Running\r\n"));
    delay_ms(500);
    // toggle pins to complete test.
    PORTB &= ~ (1<<PB1);
    PORTB &= ~ (1<<PB2);
}

int main() {

  // init serial port
  uart_init();
  FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
  stdin = stdout = &uart_stream;
  printf_P(PSTR("Main is operational\r\n"));

  // start up the Analog to Digital Converter
  adc_init();

  while(1){

  uint16_t last_sample = 0;
  double this_samp;
  double samp_avg;
  double first_samp;
  uint8_t i;

  char tc;
    tc = uart_read();

    //This is the list of possible characters and there operations

    if(tc==']') connected(); 
    if(tc=='[') lightChanged(); 
    if(tc=='0') PORTB |= (1<<PB1);
    if(tc=='1') PORTB &= ~(1<<PB2);
    if(tc=='2') PORTB |= (1<<PB2);
    if(tc=='3') PORTB &= ~(1<<PB3);
    if(tc=='3') PORTB |= (1<<PB3);
    if(tc=='4') PORTB &= ~(1<<PB4);
    }
  return 0;
}

As you can see, I have set up the ADC. I included the proper ISR(PCInt1_vector) I believe. Now I cant figure out how to tell it to look for PC4 to fire. I should be able to place my finger over the PR and that should activate the ISR() block, right? It obviously isn't or I wouldn't be at a wall! Any help or nudges in the right direction would be greatly appreciated! P.S. some of the commented lines may be a little out of date for the current code.

May 14, 2011
by hevans
(NerdKits Staff)

hevans's Avatar

Hi Twarter,

I think you are mixing up two different concepts here, and I'm not really sure what you are trying to do with your code. You do appear to have set up an interrupt hander for your code, but you I don't see where you enabled the interrupt. You need to toggle a bit on the PCICR register, and then set an appropriate mask a PSMSK register. Check out the interrupts pages of the datasheet pages 70 and 71. This will enable the interrupt, and the interrupt will fire when the pin goes from a logic high to a logic low, or vice versa. This has nothing to do with the ADC, and wont necessarily allow you to measure a precise trigger voltage.

Humberto

May 15, 2011
by Twarter369
Twarter369's Avatar

Humberto, I thought that is what I was doing on lines 44-55...am I mistaken?

May 15, 2011
by Twarter369
Twarter369's Avatar

I should add that I don't strictly NEED the ADC. The idea is to get a PR to work like a toggle switch using the interrupt handler and a status variable.

May 15, 2011
by Ralphxyz
Ralphxyz's Avatar

Twarter369. you should look at huzbum's RPM code.

You can connect your PR to PB0 (Pin14). I had problems using Pin14 but you could change his code to use most any pin using a different interrupt.

I used huzbum's code with a photo transistor in a simulated RPM situation but essentially you just want to capture the pulse of the PR.

Ralph

May 16, 2011
by Twarter369
Twarter369's Avatar

Thank you, I have read the code on that page, it looks likethere are two ISR functions

ISR(TIMER1_CAPT_vect)  // PULSE DETECTED!  (interrupt automatically triggered, not called by main program)
{
   revTick = ICR1;      // save duration of last revolution
   TCNT1 = 0;       // restart timer for next revolution
   revCtr++;                // add to revolution count
}

ISR(TIMER1_OVF_vect)    // counter overflow/timeout
   { revTick = 0; }     // RPM = 0

That specifically deal with the Timer. What _vect would I be using? I haven't found a comprehensive list of the valid _vect choices and their uses. Also, I am trying to avoid using PB0. I don't know enough about the PR and how it functions to know whether or not it would pull that Pin low during startup (thus freezing my chip in Programming mode). Are you suggesting I use PB0 for double duty? Have it connect to the PR and to the switch to ground? I am no EE, but wouldn't that create a short between the PR and ground when you want to be in programming mode?

May 16, 2011
by Ralphxyz
Ralphxyz's Avatar

Twarter369, look up "pin change interrupt" here in the forums and the Nerdkit's Tutorials and of course the datasheet as Humberto suggested.

I would not use PB0 if not needed.

Ralph

May 16, 2011
by hevans
(NerdKits Staff)

hevans's Avatar

Hi Twarter369,

I see it, I didn't check the ADC init function for a pin change interrupt setup. As far as the pin change interrupt goes that looks ok. My suggestion is to undo the connection to your photoresistor and try using a wire to touch the pin to gnd then +5, this should trigger your pin change interrupt and you can verify that works as you expected. If it does, then the photoresistor isn't working the way you expect.

Humberto

May 16, 2011
by Twarter369
Twarter369's Avatar

lol, yeah the code was a little disheveled at that moment. I have since added an interrupt_init()method and cleaned up some other minor issues. I will definitely take your advice on testing the code!

Another question in the same vein, but different application, is "Can I use my 5.5 v (red) wire from the USB to detect when it is plugged in to/unplugged from a port?"

Post a Reply

Please log in to post a reply.

Did you know that an analog comparator can tell when one voltage input crosses another? Learn more...