Help with ADC on Tiny45V

For Adafruit customers who seek help with microcontrollers

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
The_Don125
 
Posts: 373
Joined: Tue Mar 06, 2007 11:51 pm

Help with ADC on Tiny45V

Post by The_Don125 »

Hey everyone, I'm having some issues getting the ADC on my tiny45V to work. The first part of the code that handles LED1 functions as it should, but the part that handles LED2 seems to be malfunctioning.

What I've been doing to test is hooking up PB4 directly to either Vcc or GND, and by my understanding of how the ADC works, that should fill the ADC registers with 1023 and 0, respectively. After dropping the two least significant bits, that should represent the LED being fully lit, and fully off. Unfortunately, what is happening is LED2 is remaining at a constant brightness roughly equivalent to a PWM value of 1 or 2, no matter what PB4 is connected to.

I've tried this on two different chips, one brand-new, so I'm pretty sure its not a hardware issue. Clock is running at 8MHz, fuses are E4 for lfuse and DF for hfuse.

Code: Select all

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <avr/sleep.h>
#include <util/delay.h>
#include <avr/power.h>

int main(void)
{
  uint8_t bright1 = 0;	// PWM value for LED1
  uint8_t bright2 = 0;	// PWM value for LED2
  int analog = 0;	// Storage for 10-bit ADC

  wdt_enable(WDTO_8S);	// Activate watchdog for 8s

  ADCSRA = 0x85;	// ADC Status Register A
			// ADEN = 1
			//   Activate ADC
			// ADSC = 0
			//   ADC idle
			// ADATE = 0
			//   Auto-Trigger off
			// ADIF = 0;
			// ADIE = 0;
			//   ADC Interrupt Disabled
			// ADPS[2:0] = 0b101
			//   Divide clock by 32 to get 250kHz ADC clock

  ADCSRB = 0x00;	// ADC Status Register B
			// BIN = 0
			//   Bipolar input disabled
			// IPR = 0
			//   Input Polarity normal
			//  ADT[2:0]
			//   Not used

  ADMUX = 0x02;		// ADC Multiplexer Register
			// REFS[2:0] = 0b000
			//   Voltage Reference = Vcc
			// ADLAR = 0
			//   Right Adjust Result
			// MUX[3:0] = 0b0010
			//   Read from ADC2 (PB4)

  DDRB = 0x0F;		// Set PB[0:3] to output, others input
  TCCR0A = 0xF3;	// Activate OC0A and OC0B in fast PWM mode
  TCCR0B = 0x01;	// Timer 0 clock = system clock with no prescaling
  while(1)
  {
    do			// Increase brightness of LED1
    {
      OCR0A = bright1++;
      _delay_ms(40);
    } while (bright1 != 14);
    do			// Decrease brightness of LED1
    {
      OCR0A = bright1--;
      _delay_ms(40);
    } while (bright1 != 0);

    ADCSRA = (1<<ADSC);	// Begin ADC conversion
    while(ADCSRA && 0x40);	// Wait until conversion completes
    analog = ADCL | (ADCH << 8);// Put ADC value into variable
    bright2 = (analog >> 2);	// Drop two LSB of ADC value
    OCR0B = bright2;	// Set LED2 Brightness
    wdt_reset();	// Reset watchdog
  }
}

The_Don125
 
Posts: 373
Joined: Tue Mar 06, 2007 11:51 pm

Re: Help with ADC on Tiny45V

Post by The_Don125 »

Nevermind, a couple hours of tinkering later, and its working. The changes I made were enabling left result alignment, and actually remembering to disable the pull-up resistors on the analog inputs (as well as some code-cleaning at the bottom to make it more readable).

The repaired code:

Code: Select all

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <avr/sleep.h>
#include <util/delay.h>
#include <avr/power.h>

int main(void)
{
  uint8_t bright1 = 0;	// PWM value for LED1
  uint8_t bright2 = 0;	// PWM value for LED2

  wdt_enable(WDTO_8S);	// Activate watchdog for 8s

  ADCSRA = 0x85;	// ADC Status Register A
			// ADEN = 1
			//   Activate ADC
			// ADSC = 0
			//   ADC idle
			// ADATE = 0
			//   Auto-Trigger off
			// ADIF = 0;
			// ADIE = 0;
			//   ADC Interrupt Disabled
			// ADPS[2:0] = 0b101
			//   Divide clock by 32 to get 250kHz ADC clock

  ADCSRB = 0x00;	// ADC Status Register B
			// BIN = 0
			//   Bipolar input disabled
			// IPR = 0
			//   Input Polarity normal
			//  ADT[2:0]
			//   Not used

  ADMUX = 0x21;		// ADC Multiplexer Register
			// REFS[2:0] = 0b000
			//   Voltage Reference = Vcc
			// ADLAR = 1
			//   Left Adjust Result
			// MUX[3:0] = 0b0001
			//   Read from ADC1 (PB2)

  DDRB = 0x03;		// Set PB[1:0] to output, others input
  PORTB = 0x00;		// Disable pull-up resistor for inputs
  TCCR0A = 0xF3;	// Activate OC0A and OC0B in fast PWM mode
  TCCR0B = 0x01;	// Timer 0 clock = system clock with no prescaling
  OCR0A = 0;		// Initialize Output Compare 0A to zero
  OCR0B = 0;		// Initialize Output Compare 0B to zero

  while(1)
  {
    do			// Increase brightness of LED1
    {
      OCR0A = bright1++;
      _delay_ms(40);
    } while (bright1 != 14);
    do			// Decrease brightness of LED1
    {
      OCR0A = bright1--;
      _delay_ms(40);
    } while (bright1 != 0);

    ADCSRA |= (1<<ADSC);	// Begin ADC conversion
    while((ADCSRA | 0x40) == 0x40); // Wait until conversion is complete
    bright2 = ADCH;		// Put ADC[9:2] into PWM variable
    OCR0B = bright2;		// Set LED2 Brightness
    wdt_reset();		// Reset watchdog
  }
}

mtbf0
 
Posts: 1645
Joined: Sat Nov 10, 2007 12:59 am

Re: Help with ADC on Tiny45V

Post by mtbf0 »

The_Don125 wrote:

Code: Select all

    while((ADCSRA | 0x40) == 0x40); // Wait until conversion is complete
this ain't right. first of all, "|" is not a bitwise operator. if you had used a bitwise or note that ADSCRA is always going to have ADEN and whatever MUX bits you set set, so your compare will always evaluate false, resulting in no delay.

Code: Select all

    while (ADCSRA && (1 <<ADCS));
will do what you want, waiting 'til ADCS is cleared at the end of the conversion.

The_Don125
 
Posts: 373
Joined: Tue Mar 06, 2007 11:51 pm

Re: Help with ADC on Tiny45V

Post by The_Don125 »

Hm, the tiny45 didn't like that line of code much, now it just hangs when it gets to that line.

I think maybe I'll do without the delay and just start the conversion at the start of my loop, and read at the end. I'm fairly certain there are more than the minimum 25 cycles in between the top and bottom.

edit:
Actually, that might not work either, since its 25 ADC clock cycles, which is 25 * 32 regular clock cycles with my prescaler, though the _delay_ms(40) functions should total up to the 800 clock cycles that I'd need.

Locked
Please be positive and constructive with your questions and comments.

Return to “Microcontrollers”