Search code examples
assemblyarduinoarduino-uno

Light Sensor Assembler in Arduino


I am doing a home automation project with an Ardoino UNO. I need to use a light sensor, however since in assembly there is no analogRead function (), I have to program it. I already read the documentation of AVRand Atmel about the ADC, the MUX and its selection but it is not clear to me how it is programmed or how it works.

I could use an explanation of the following code that was passed to me by a colleague who tells me, that the values ​​of a certain PIN are stored in ADCL and ADCH. My question is if someone would be so kind as to explain me the code and what is happening in the ADC MUX. It's a 328p

.equ F_CPU = 16000000;
.DEF rmp = R16 
.DEF tmp = R17
.DEF tmp2 = R18
.DEF reg1 = r20 
.DEF reg2 = r21
.EQU ALFA=250
.DSEG
.ORG  0X0100
.CSEG

.ORG $0000

jmp Main 

Main:

ldi rmp, HIGH(RAMEND) ; Init MSB stack
out SPH,rmp
ldi rmp, LOW(RAMEND) ; Init LSB stack
out SPL,rmp
; Init Port B
ldi rmp,0xff ; DIRECCION DEL Port B
out DDRB,rmp
rcall ad ; Call ADC Initialization
; [Add all other init routines here]
ldi rmp,1<<SE ; 
out MCUCR,rmp
sei 

Loop:

rcall adcRead
out PORTB,r18
rjmp loop ; go back to loop

ad: 

ldi rmp, (1<<REFS0) 
sts ADMUX, rmp 
ldi rmp, (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
sts ADCSRA, rmp 
ret 

adcRead: 

lds rmp, ADCSRA    
ori rmp, (1<<ADSC) 
sts ADCSRA, rmp    

loopx:

lds  rmp, ADCSRA
sbrc rmp,ADSC
rjmp loopx     
lds r17,ADCL 
lds r18,ADCH 
ret

Solution

  • The analog-to-digital converter is started by setting the ADSC (ADc Start Conversion) bit in the control register ADCSRA to 1. This is done by the lines

        lds rmp, ADCSRA
        ori rmp, (1<<ADSC)
        sts ADCSRA, rmp
    

    The bit stays at 1 as long as the conversion is in progress. Once the conversion is done, the ADC sets the bit to 0 to signal this fact.

    The program then tests that bit in order to know when the conversion is finished. This is the purpose of the loopx loop:

    loopx:                ; start of loop
        lds  rmp, ADCSRA  ; load ADCSRA into register rmp
        sbrc rmp, ADSC    ; conditionally skip next instruction
        rjmp loopx        ; jump back to the start of the loop
    

    The sbrc instruction (Skip if Bit in Register is Clear) test the bit 6 (that's the value of ADSC) of register rmp. If that bit is zero, then it skips the following rjmp instruction, thus getting out of the loop. This way the loop stops when the ADC conversion is completed.

    There can be a couple of reasons this doesn't work in your setup. If you are using an emulator, it could be that it doesn't properly emulate the operation of the ADC. Then, once you set the ADSC bit to 1 it stays at 1 forever, which makes the loop infinite.

    Another possibility is that you didn't wait long enough. The first conversion after the ADC is switched on takes 3200 CPU cycles with the settings you have. That's 640 iterations of the loop. If you are doing single steps through the debugger, that may be too long for your patience.