ADC, assembly and ATTiny13

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

I've been trying to figure out how to do ADC in assembly for 3 days and feel like a total idiot. My setup - STK500 with ATTiny13 running at 4MHz. A 10k potentiometer is wired pretty much the same way as in the tutorial and its output is connected to channel ADC2 of the uC. This gives me a signal with a range of ~0.25-4.75V, I checked. First, I took the C code from the ADC tutorial, simplified it and it worked. All it does is toggle b/w two LEDs as you adjust the potentiometer:

#include 

int main (void)
{
   DDRB |= (1 << PINB0); // Set LED1 as output
   DDRB |= (1 << PINB2); // Set LED2 as output

   ADCSRA |= (1 << ADPS2) | (0 << ADPS1) | (1 << ADPS0); // Set ADC prescalar to 128 - 125KHz sample rate @ 16MHz

   ADMUX |= (0 << REFS0); // Set ADC reference to AVCC
   ADMUX |= (1 << MUX1);
   ADMUX |= (0 << MUX0);
   ADMUX |= (1 << ADLAR); // Left adjust ADC result to allow easy 8 bit reading

   // No MUX values need to be changed to use ADC0
   ADCSRA |= (1 << ADATE);  // Set ADC to Free-Running Mode
   ADCSRA |= (1 << ADEN);  // Enable ADC
   ADCSRA |= (1 << ADSC);  // Start A2D Conversions

   for(;;)  // Loop Forever
   {
      if(ADCH < 128)
      {
         PORTB |= (1 << PINB0); // Turn on LED1
         PORTB &= ~(1 << PINB2); // Turn off LED2
      }
      else
      {
         PORTB &= ~(1 << PINB0); // Turn off LED1
         PORTB |= (1 << PINB2); // Turn on LED2
      }
   }
} 

When I try to do the same thing in assembly, one LED comes on and never changes (the LED on PINB0):

.include "tn13def.inc"

; configure the interrupt vectors
; the .org directive sets vector jump locations
.org $0000
	rjmp Reset

; define some variables
.def TEMP=r16
.def TEMP2=r17

Reset:
	; initialize the stack
	ldi TEMP,low(RAMEND)
	out SPL,TEMP

	; configure PINB2 and PINB0 as output to drive an LED
	ldi TEMP,(1<<PINB2)|(1<<PINB0)
	out DDRB,TEMP
	out PORTB,TEMP

	; configure the ADC
  	; select the voltage reference (Vcc or 1.1V) (Vcc in this case)
  	; select input channel by writing to the mux bits in admux (ADC2 or PB4 in this case)
	ldi TEMP,(0<<REFS0)|(1<<MUX1)|(0<<MUX0)|(1<<ADLAR)
	out ADMUX,TEMP
	; enable auto triggering
	ldi TEMP,(1<<ADATE)
  	; enable ADC by setting the ADC enable bit, ADEN, in ADCSRA, set prescaler to 32
	ori TEMP,(1<<ADEN)|(1<<ADPS2)|(1<<ADPS0)
	out ADCSRA,TEMP

	; start conversion by writing 1 to the ADC start conversion bit, ADSC.
	; this bit stays high as long as the conversion is in progress and will
	; be cleared by hw when the conversion is completed.
	ori TEMP,(1<<ADSC)
	out ADCSRA,TEMP

loop:
	; check if the conversion is complete through the ADIF flag in ADCSRA
	in TEMP,ADCSRA
	andi TEMP,(1<<ADIF)
	cpi TEMP,0x00
	breq loop

        ; read the high register (8 MSBs, left-adjusted)
	in TEMP,ADCH
        ; unsigned compare with 128
	cpi TEMP,0x80
	brlo led2b

        ; turn on LED on PINB0
	in TEMP,PINB
	andi TEMP,~(1<<PINB0)
	out PORTB,TEMP
	rjmp loop_ret	

led2b:
        ; turn on LED on PINB2
	in TEMP,PINB
	andi TEMP,~(1<<PINB2)
	out PORTB,TEMP

loop_ret:
	rjmp loop

I'm guessing that ADCH is always at 0xFF, which can happen if the input signal is over the reference voltage, Vcc in this case. But I checked the signal, and that's not the case. Am I doing something else wrong? Any help is greatly appreciated!

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

What happens if you use PORTB instead of PINB in:

in TEMP,PINB ; Use PORTB here
andi TEMP,~(1<<PINB0)
out PORTB,TEMP

You also could use SBI/CBI instructions.

edit: rereading your code... you only clear bits, you never set them.

in   TEMP,PORTB
ori  TEMP,(1<<PINB0)
andi TEMP,~(1<<PINB2)
out  PORTB,TEMP

.
.
.

in   TEMP,PORTB
ori  TEMP,(1<<PINB2)
andi TEMP,~(1<<PINB0)
out  PORTB,TEMP