Tiny13 and ADC?

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

I am a total novice and am a bit stumped working out ADC on a Tiny13. I am trying to use a light sensitive resistor to know if it is dark or light. I have an STK500 with the Tiny13 on it. I have made a voltage divider on a bread board with a 10K resistor and a light sensitive resistor. I have them connected to VTG and GND on the PORTB connector on the STK500. I have the center of the voltage divider connected to PB2 on the STK500.

My poor coding is here:-

int main(void)
{
	// set PORTB 0 1 for output, test LED's
	DDRB = (1<<DDB1) | (1<<DDB0);

	PORTB = (1<<1) | (1<<0); // turn led's off
	
	// set PB2 as ADC
	ADMUX = (1<<MUX0);

	// set ADC prescaler to 8
	ADCSRA = (1<<ADEN) | (1<<ADPS1) | (1<<ADPS0);

	while(1) {
		Get_Light();		
	}
}

void Get_Light(void)
{
	char i;
	int ADCr = 0;

	sbi(ADCSRA, ADEN); // enable the ADC

	// do a dummy reading first
	ADCSRA |= (1<<ADSC);

	// wait for conversion done flag
	while(!(ADCSRA & 0x10));

	// do conversion 8 times for accuracy
	for(i=0;i<8;i++)
	{
		ADCSRA |= (1<<ADSC);	

		// wait for conversion done flag
		while(!(ADCSRA & 0x10));

		light = ADCL;
		light += (ADCH << 8);

		// accumulate results for averaging later
		ADCr += light;
	}
	
	ADCr = ADCr >> 3; // average 8 samples

	light = ADCr;
 
	cbi(ADCSRA, ADEN); // disable the ADC

                // just for testing
		if (light < 10) 
		{
			cbi (PORTB, 0);
		} else {
			sbi (PORTB, 0); 
		}

}

Now when I run this code I do get an LED light up when I shade the light resistor and turning off when I don't shade it, but it only lights up with a certain amount of shade, if I shade it more it goes out again.

I would also like to move

               // just for testing
		if (light < 10) 
		{
			cbi (PORTB, 0);
		} else {
			sbi (PORTB, 0); 
		}

into main after Get_Light(); but the variable "light" doesn't work globally. I have no idea about declaring a global variable.

I would be greatful if anyone can point out the mistakes in my code and help me get it working right. Thanks.

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

Depending on optimization level, this (on the ATtiny13 and a few other AVRs)
may not do what you intend:

   while(!(ADCSRA & 0x10)); 

Try instead:

   while(ADCSRA & (1 << ADSC));
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As far as the light variable, if you want to use it in the ISR and in main (or anywhere else for that matter), you need to declare it as 'volatile'.

As to why the code works oddly, I don't know. Have you tried a differnt test value? Also, you don't say what cpu frequency you are using, so I don't know if the pre-scaler is enough or not.

But I do have a couple of recommendations. For determining when the conversion finishes (when not using an interrupt) I find it better to check the ADSC bit rather than the ADIF since the ADIF has to be manually cleared after the conversion. ADSC will go low when the conversion is complete. Also, to get the full value of the result you can just do:

light = ADC;

or:

light = ADCW;

which you use depends on what version of WinAvr you are using (the later ones use ADCW).

Regards,
Steve A.

The Board helps those that help themselves.

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

Koshchi wrote:
Also, you don't say what cpu frequency you are using, so I don't know if the pre-scaler is enough or not.

At the moment I am using the default settings Int. RC Osc 9.6Mhz with Divide clock by 8 internally ticked.

I have made those suggested changes to the code but it is still doing the same thing. It lights the LED at a certain light level, but more or less light stops the LED. I have tried lots of different test values but I can't seem to find a pattern. I have measured the voltage at the voltage divider, it is around 4.9volts in the dark and goes down to around 4.2volts in the light. Maybe I should use a different resistor than 10K?

With regards to the global variable, I have declared it at the beginning of the code like this:-

volatile int light = 0;

but if I move the code to light the LED into main it still doesn't work, only in Get_Light.

As you can see I don't know what I am doing, I am using trial and error for a lot of this. Just for the hell of it I was trying to light 2 led's like this:-

		
                if (light < 10) 
		{
			cbi (PORTB, 0);
			sbi (PORTB, 1);
		} else {
			sbi (PORTB, 0); 
			cbi (PORTB, 1); 
		}

What happens with this is that when I shade the resistor LED0 lights up but LED1 dims but does not go out. Does this give any clues to what is happening?

Appreciate the help.

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

The clock frequency of the of the ADC must be between 50KHz and 200KHz to get full resolution. So if you are using the 9.6MHz cpu frequency, you need a divider of at least 64 (9.6MHz / 64 = 150KHz). If you don't need full 10 bit resolution, you may be able to get away with a divider of 32, but certainly not less than that. This sounds like it might be your problem.

Regards,
Steve A.

The Board helps those that help themselves.

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

Thanks Koshchi, that got it working OK, sort of. One thing puzzles me, if I do this

		if (light < 50) 
		{
			// it is light, led on
			cbi (PORTB, 0);
		} else {
			// it is dark, led off
			sbi (PORTB, 0); 
		}

The led comes on when light and off when dark. But if I do this

		if (light > 50) 
		{
			// it should be dark, led on
			cbi (PORTB, 0);
		} else {
			// it should be light, led off
			sbi (PORTB, 0); 
		}

I thought it would just reverse, Led off when light and on when dark but the led comes on when light and goes brighter when dark. This is really confusing me.

Regardless of this, all I need to know is if it is light or dark in the while(1) loop in main. Nothing I try seems to work. The light variable is not the same when I try this code in main. I have declared light volatile.

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

What I would do is replace the light sensor voltage divider with a variable resistor and see if you can get the code working. Also with this, you could use both pins that you have free and set them to the upper two bits of the result (this would be bits 8 and 9 since the ADC has 10 bits of accuracy).

Regards,
Steve A.

The Board helps those that help themselves.

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

Thanks again Koshchi, you have been a great help. Using a variable resistor was a good idea. I was getting some odd results, the led was on/off about 4 times when I varied the resistor from one end ot the other. So I was looking at what you said next:-

Quote:
you could use both pins that you have free and set them to the upper two bits of the result (this would be bits 8 and 9 since the ADC has 10 bits of accuracy).
To be honest, you lost me here so I went and read the ADC section in the document again and read this
Quote:
The ADC generates a 10-bit result which is presented in the ADC Data Registers,
ADCH and ADCL. By default, the result is presented right adjusted, but can optionally
be presented left adjusted by setting the ADLAR bit in ADMUX.
If the result is left adjusted and no more than 8-bit precision is required, it is sufficient to
read ADCH.

so I set the ADLAR bit in ADMUX, and only read ADCH. Now I have a more expected result with led off from 0v to some point and then on from there to 5v. That gives me something solid to play with. I don't know what you meant about setting the 2 free pins to the upper 2 bits of the result. Do you think what I did was a reasonable alternative?