Powering R + LDR voltage divider from µC's output pin?

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

I conected the output of voltage divider formed from 10k resistor and LDR:

 

               ADC
                o
                |
                |
+5V o----10k----o----LDR----o GND

 

to ATmega328's ADC input and got measures of about 850. Then I tried to power the voltage divider from an output pin of a microcontroller and under the same light conditions the ADC readings dropped to about 650.

 

When I am using an output pin to power the voltage divider, the voltage at the pin supplying power to voltage divider measured with digital multimeter is only 2.8 V instead of Vcc.

 

The outcome is - I have to use one calibration constant when I am powering the voltage divider from a pin and another calibration constant when the voltage divider is powered straight from the +5V.

 

At first I thought I started ADC measuring too fast upon turning the supply pin HIGH but I am getting the same results even if the supply pin is HIGH all the time. I am assuming the voltage drop has something to do with the relatively high resistance of resistor (10k) + LDR (about 47k) but would still like to be sure if that is the cause.

 

I'd like to power the voltage divider from the pin because that way the device wouldn't drain a battery when not measuring light. If the cause of the voltage/measure drop is really high load impedance I might use a voltage follower or a transistor or simply edit the code to use different calibration constant. In that case (using the circuit without an op amp or a transistor) I'd like to know if there is some reason not to use output pin in such conditions.

This topic has a solution.

Chupo_cro

Last Edited: Wed. Oct 4, 2017 - 09:14 PM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Chupo_cro wrote:
When I am using an output pin to power the voltage divider, the voltage at the pin supplying power to voltage divider measured with digital multimeter is only 2.8 V instead of Vcc.

And what is your Vcc level?

 

My guess is that you did not make the pin an output.

 

Tell AVR model.  Show code; complete test program is best.

 

Chupo_cro wrote:
I am assuming the voltage drop has something to do with the relatively high resistance of resistor (10k) + LDR (about 47k) but would still like to be sure if that is the cause.

If it were >>low<< resistance, then you would see voltage drop.  As it is, with 5V Vcc and 50000 ohms load, that is ~100uA draw. There should be no [easily measurable?] voltage drop.  For an arbitrary AVR model datasheet:

 

Looks linear; ~0.15V drop for 5mA so 1/50 of that for 100uA so maybe 3mV?

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

A 10K R and a 47K LDR, at VCC=3.3V would result in 3.3/57000 or about 58 uA.  Close enough to zero that VOH should still be very nearly VCC.  Unless you've programmed RSTDISBL and are using /RESET as an output (you're not doing that, are you?), then the driver strength is much lower.  It's not spec'd in the datasheet for the 328P, but the t25 shows a very poor strength.  At VCC=3V, VOH is only 2.4V even at IOH=0.

 

More likely is that you forgot to make the pin an output, and only the pull-up is working for you.  Not enough to fight off the voltage divider.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Wed. Oct 4, 2017 - 08:11 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

And what is your Vcc level?

 

My guess is that you did not make the pin an output.

 

Tell AVR model.  Show code; complete test program is best.

Yes, you are right - I typed:

LDR_POWER_DDR |= LDR_POWER_PIN_POS;

instead of:

LDR_POWER_DDR |= (1<<LDR_POWER_PIN_POS);

Thank you!

 

I saw that now that I already have stripped the code to send it here so I will send it anyway, maybe someone will use it for something - if nothing else someone might check how to use FDEV_SETUP_STREAM and PSTR and how to use #define guards :-))

 

Vcc is 5V and the AVR is ATmega328 (I already said the AVR model in the initial post).

 

Here is the code:

 

main.c

#include <avr/io.h>
#include <stdio.h>
#include <avr/pgmspace.h>
#include <util/delay.h>

#include "uart.h"
#include "ADC.h"

FILE uart_io = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);

int main(void) {

	init_UART();
	stdin = stdout = &uart_io;		// set the streams
	init_ADC();

	while(1) {
		printf_P(PSTR("ADC reading: %u\n"), ADC_read(0));
		_delay_ms(250);
	}

	return(0);
} // main

uart.h

#ifndef UART_H
#define UART_H

void init_UART(void);
void tx(uint8_t data);
uint8_t rx(void);

int uart_putchar(char c, FILE *stream);
int uart_getchar(FILE *stream);

#endif

uart.c

#include <avr/io.h>
#include <stdio.h>

#include "uart.h"

//int uart_putchar(char c, FILE *stream);
//int uart_getchar(FILE *stream);

// ATmega128: RXD0 --> PE0
//            TXD0 --> PE1 <-------
// kabel:     TX   --> zelena      |
//            RX   --> bijela <----
void init_UART(void) {

	UBRR0H = 0x00;
	UBRR0L = 51;						// 51 = 19200 baud @ 16 MHz
	UCSR0A &= ~(1<<U2X0);				// disable double speed (if using optiboot)
	UCSR0B = (1<<TXEN0)|(1<<RXEN0);		// enable TX & RX
	UCSR0C = (3<<UCSZ00);				// 1 stop bit, 8 data bits, no parity, async
}

void tx(uint8_t data) {

	while(!(UCSR0A & (1<<UDRE0)))
		;				// wait
	UDR0 = data;		// send
}

uint8_t rx(void) {

	while(!(UCSR0A & (1<<RXC0)))
		;				// wait
	return UDR0;
}

// for FDEV_SETUP_STREAM
int uart_putchar(char c, FILE *stream) {
	// new line
	if(c=='\n')
		uart_putchar('\r', stream);
	// TX
	while(!(UCSR0A & (1<<UDRE0)))
		;				// wait
	UDR0 = c;			// send

	return 0;
}

// for FDEV_SETUP_STREAM
int uart_getchar(FILE *stream) {

	while(!(UCSR0A & (1<<RXC0)))
		;				// wait
	return UDR0;
}

ADC.h

#ifndef ADC_H
#define ADC_H

// PORT & DDR
#define LDR_POWER_PORT			PORTB	// voltage divider power supply
#define LDR_POWER_DDR			DDRB
// bit pos
#define LDR_POWER_PIN_POS		PB1

void init_ADC(void);
uint16_t ADC_read(uint8_t ch);

#endif

ADC.c

#include <avr/io.h>
#include <stdio.h>
#include <avr/pgmspace.h>
#include <util/delay.h>

#include "ADC.h"

void init_ADC(void) {

	printf_P(PSTR("ADC innit...\n"));

	ADCSRA |= ((1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0));	// prescaler = 128 (125 kHz @ 16 MHz)
	ADMUX  |= (1<<REFS0);								// ref = AVcc
	ADCSRA |= (1<<ADEN);								// ADC enable
	// here an initial ADC conversion could be done

	LDR_POWER_DDR |= (1<<LDR_POWER_PIN_POS);	// <-- **** THIS WAS WRONG ****
	LDR_POWER_PORT &= ~(1<<LDR_POWER_PIN_POS);	// LDR power supply = OFF

} // init_ADC

uint16_t ADC_read(uint8_t ch) {

	if(ch == 0) {
		LDR_POWER_PORT |= (1<<LDR_POWER_PIN_POS);	// LDR power supply = ON
		//_delay_ms(1000);
	}

	ADMUX &= 0xf0;
	ADMUX |= ch;			// set mux
	ADCSRA |= (1<<ADSC);	// Start Conversion
	while (ADCSRA & (1<<ADSC))
		;	// wait

	LDR_POWER_PORT &= ~(1<<LDR_POWER_PIN_POS);	// LDR power supply = OFF

	return ADCW;			// compiler will generate the code to first read ADCL and then ADCH
} // ADC_read

 

Chupo_cro

Last Edited: Wed. Oct 4, 2017 - 09:24 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joeymorin wrote:

More likely is that you forgot to make the pin an output, and only the pull-up is working for you.  Not enough to fight off the voltage divider.

Yes, that was the problem. Although I didn't forget to assign a value to DDR but rather used pin number insted of mask :-[

 

Thank you for the reply!

Chupo_cro

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

Yes, that was the problem. Although I didn't forget to assign a value to DDR but rather used pin number insted of mask :-[

There's been a rash of these lately ;-) :

http://www.avrfreaks.net/comment/2287146#comment-2287146

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

Maybe a consequence of wide use of Arduino?

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

ka7ehk wrote:

Maybe a consequence of wide use of Arduino?

I had to laugh when I read that :-) The truth is exactly the opposite. In fact it couldn't be more opposite than that. Although I've been using µCs since 16F84 times (and for the last 8-9 years I've been using AVRs only) I have never ever until a few days ago had any Arduino board nor I have ever written one single line of Arduino code targeted for AVR based boards :-) I do have Arduino IDE installed but solely for the purpose of programming ESP8266. I am writing AVR code every day and my estimation is I wrote about 50000+ lines of code in AVR Studio - one of my recent projects having about 2500 lines of code consisting of 29 .c and .h files.

 

Recently I ordered a dozen of Arduino Nano and Arduino Pro mini clones because of their small form factor. The first I did was to try blink example from Arduino IDE just to try the bootloader. Then I tried to use a bootloder from command line to upload .hex from AVR Studio and saw it might be a fast way for rapidly uploading the code during development. When the code will be finished I will remove the bootloader, edit the code to work with internal oscillator and burn it 'normally' (through ISP). The way I was working before was to during development use ATmega128 board with JTAG programmer and to finally port the code to AVR that would be used in the device.

 

BTW, when I saw the original 1KB bootloader that came with the boards can not handle WDT reset I found 256 bytes optiboot which solves that problem. I did occasionally see forum threads mentioning 'optiboot' but have never paid attention because I wasn't using bootloaders. Now I finally know why so many threads about different bootloaders.

Chupo_cro

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

joeymorin wrote:

There's been a rash of these lately ;-) :

http://www.avrfreaks.net/comment/2287146#comment-2287146

If I remember correctly, I already started a thread a few years ago here on the forum - when the problem was the very same mistake as it was now :-))

Chupo_cro