0
\$\begingroup\$

I want to program the ADC module of arduino Mega2560 using Assembly language (for simulation and learning), I can initialise the ADC Module using Assembly and my program converts in the first iteration, but since the ATmega2560 has an exteded I/O registers, I did not find a way to reset the ADC Flag bit (ADIF) once the first conversion is done. If I use the SBI instruction, I got a error message that says: "operand out of range: 122"

#define __SFR_OFFSET 0x00
#include <avr/io.h>

.global init_ADC
.global init_PORT
.global read_ADC

;----PORT Initilasation--------

init_PORT:
    LDI R23,0xFF;
    OUT DDRB,R23    ;Configure PORTB as OUTPUT  
    OUT DDRC,R23    ;Configure PORTC as OUTPUT
    RET

;----Initializing ADC Module----------

init_ADC:
    LDI R19,0x00     ;
    OUT DDRF,R19     ;Configure ADC0 as INPUT for analog conversion
    LDI R16,0x40     ;Load R16 with 0x40
    STS ADMUX,R16    ;Configure ADMUX with 5V vref and channel 0 for conversion
    LDI R17,0x00
    STS ADCSRB,R17   ;Set MUX5=0
    LDI R18,0x85
    STS ADCSRA,R18   ;Set Enable bit and prescaler to 1:32
    RET

read_ADC:
    LDI R20,0x40     ;
    LDS R21,ADCSRA;
    ADD R20,R21;
    STS ADCSRA,R20    ;Set ADSC bit to start conversion
    RCALL WAIT       ;   
    LDS R21,ADCH;
    OUT PORTC,R21
    LDS R22,ADCL
    OUT PORTB,R22
 ;==CLEAR FLAG BIT===+
    SBI ADCSRA,ADIF   ;Set ADC flag bit to clear it
    RET

WAIT:
    LDS R16,ADCSRA  ;Load ADCSRA to R16 register
    ANDI R16,0x10;
    BREQ WAIT
   
    RET

How could I clear or set any bit of a register that is out of range of these instructions (In my case is the ADCSRA) ?

\$\endgroup\$
3
  • \$\begingroup\$ lds r24,ADCSRA ori r24, (1 << ADIF) sts ADCSRA,r24 in C ADCSRA |= (1 << ADIF); And why do you want to clear this flag? This flag is cleared by hardware. Also, do not forget that ADIF is not equal to 0x10 but 0x04 instead. This is why I used ori r24,(1 << ADIF) whit is equivalent to ori r24,0x10 \$\endgroup\$
    – G36
    Commented Apr 29 at 16:47
  • \$\begingroup\$ @G36 In ATmega2560, ADIF, is the fifth bit in ADCSRA register, as it is shown in the datasheet, so I expect the ADIF set should be 0X10 in HEX format !!! \$\endgroup\$ Commented Apr 29 at 18:28
  • 1
    \$\begingroup\$ ADFI is a macro and it's equal to 4. To get 0x10 in asm you need to use this (1 << ADIF). And this ldi r16,ADIF will load 4 to r16. \$\endgroup\$
    – G36
    Commented Apr 29 at 18:53

2 Answers 2

1
\$\begingroup\$

IN/OUT/SBI/CBI are just shortcuts for ST/LD family instructions (plus additional operations in the case of the latter), for addresses within the IO range. The IO range is very limited; registers outside that range, must use regular SRAM access. This is a common oversight, so this seems relevant here.

But you're already using LDS and STS to access registers outside that range; it seems you do know this. So I'm not sure precisely what you're asking. Then, you know how to read-modify-write, right?:

For example, avr-gcc 13 compiles

    ADCSRA |= 1 << ADIF;

into

    ldi r30,lo8(122)
    ldi r31,0
    ld r24,Z
    ori r24,lo8(16)
    st Z,r24

Of course if you're already using Z, you can use LDS/STS directly; same code size (and, cycle count I think?).

Finally, as mentioned in the other answer, a read-modify-write is not actually required for this register; the bit is already set and you can simply read then write. Convenient!

Indeed, if you already know the value of the other bits of ADCSRA, you don't need to read it at all (and, generally settings aren't changing, so this can indeed be used in practice).

Or if the values are changing (say, stopping ADC after a series of acquisitions, to save power), I suppose you can write them back at the same time; unless there's errata that says not to.

(Semantics: note the above instructions are not a direct substitute for SBI/CBI, as they are not atomic; a truer emulation would wrap the passage in CLI...SEI (depending on initial I flag state), but this only accounts for software atomicity. Nothing can be done to account for register atomicity -- but AVR peripherals are at least reasonably well behaved, and we're generally fine with this.)

\$\endgroup\$
0
\$\begingroup\$

In general, read a non-bit-accessible IO register into a general purpose register, modify the bits you want, and write it back.

But actually, you don't need to modify any bits.

The ADIF bit will be set when ready, and to clear it, you need to write '1' to it, so just write back what you wrote to clear ADIF.

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.