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) ?

asked Apr 29, 2024 at 6:07
\$\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\$ Commented Apr 29, 2024 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, 2024 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\$ Commented Apr 29, 2024 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.)

answered Apr 29, 2024 at 9:48
\$\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.

answered Apr 29, 2024 at 6:11
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.