4

Like I said in the subject, I would like to know the equivalent for the instruction:

PORTB |= 0x01;
PORTB &= ~0x01;

More precisely I was using it to trigger a fast pulse conversion on my shield-ADC on an Arduino Uno. Now I am using an Arduino Mega 2560.

Can you write the exact same instruction for ATMega2560?

dda
1,5951 gold badge12 silver badges17 bronze badges
asked Jan 5, 2017 at 15:35

2 Answers 2

4

This code sends a pulse on pin PB0, which on the Uno is equivalent to digital 8. On the Mega, digital 8 is PH5, thus you would write

PORTH |= _BV(PH5);
PORTH &= ~_BV(PH5);

The equivalence between the Arduino pins and the AVR pins is provided in the official documentation, but there are nicer versions at pighixxx.com:

answered Jan 5, 2017 at 16:22
4

The other answer has provided correct information and useful documentation on the question as asked. This answer uses a slightly more portable method of producing a brief pulse on a given digital pin.

This more-portable method uses some standard (although not well-documented at arduino.cc) functions to get port numbers, port pointers, and bit masks from a pin number. These functions – digitalPinToPort(), portInputRegister(), and digitalPinToBitMask()– can be used at run time to compute physical ports and pins from Arduino pin numbers. In some cases the computation could occur at compile time if compiler is smart enough or is allowed to optimize. But avr-gcc as configured for the Arduino IDE is not so allowed, which is a reasonable default for embedded programming.

This method also uses the fact that an AVR pin – as on most Atmel-based Arduinos – will toggle when you write a 1 to its bit in its input register. For example, see §14.1 in Atmel doc8271, the ATmega48A/PA/88A/PA/168A/PA/328/P datasheet, which states:

Three I/O memory address locations are allocated for each port, one each for the Data Register – PORTx, Data Direction Register – DDRx, and the Port Input Pins – PINx. The Port Input Pins I/O location is read only, while the Data Register and the Data Direction Register are read/write. However, writing a logic one to a bit in the PINx Register, will result in a toggle in the corresponding bit in the Data Register. ...


Example program

/* jiw - Jan 2017 - toggle Arduino digital pin 8 several ways
Re: http://arduino.stackexchange.com/questions/33033/equivalent-for-portb-in-arduino-mega-2560 */
volatile uint8_t *portofpin;
uint8_t bitofpin;
void setup() {
 pinMode(8, OUTPUT); // Set pin as output
 portofpin = portInputRegister(digitalPinToPort(8));
 bitofpin = digitalPinToBitMask(8);
}
void udelay() {
 delayMicroseconds(5);
}
void loop() {
 PORTB |= 0x01; // Set Uno pin 8
 PORTB &= ~0x01; // Clear Uno pin 8
 udelay();
 PINB = 1; // Toggle Uno pin 8
 PINB = 1; // Toggle Uno pin 8
 udelay();
 PINB = bitofpin; // Toggle Uno pin 8
 PINB = bitofpin; // Toggle Uno pin 8
 udelay();
 *portofpin = bitofpin; // Toggle Arduino pin 8
 *portofpin = bitofpin; // Toggle Arduino pin 8
 udelay();
}

Compilation on Uno

loop(), from compilation of the example program for an Uno board:

void loop() {
 PORTB |= 0x01; // Set Uno pin 8
 10e: 28 9a sbi 0x05, 0 ; 5
 PORTB &= ~0x01; // Clear Uno pin 8
 110: 28 98 cbi 0x05, 0 ; 5
 udelay();
 112: 0e 94 83 00 call 0x106 ; 0x106 <_Z6udelayv>
 PINB = 1; // Toggle Uno pin 8
 116: 81 e0 ldi r24, 0x01 ; 1
 118: 83 b9 out 0x03, r24 ; 3
 PINB = 1; // Toggle Uno pin 8
 11a: 83 b9 out 0x03, r24 ; 3
 udelay();
 11c: 0e 94 83 00 call 0x106 ; 0x106 <_Z6udelayv>
 PINB = bitofpin; // Toggle Uno pin 8
 120: 80 91 00 01 lds r24, 0x0100
 124: 83 b9 out 0x03, r24 ; 3
 PINB = bitofpin; // Toggle Uno pin 8
 126: 80 91 00 01 lds r24, 0x0100
 12a: 83 b9 out 0x03, r24 ; 3
 udelay();
 12c: 0e 94 83 00 call 0x106 ; 0x106 <_Z6udelayv>
 *portofpin = bitofpin; // Toggle Arduino pin 8
 130: e0 91 01 01 lds r30, 0x0101
 134: f0 91 02 01 lds r31, 0x0102
 138: 80 91 00 01 lds r24, 0x0100
 13c: 80 83 st Z, r24
 *portofpin = bitofpin; // Toggle Arduino pin 8
 13e: e0 91 01 01 lds r30, 0x0101
 142: f0 91 02 01 lds r31, 0x0102
 146: 80 91 00 01 lds r24, 0x0100
 14a: 80 83 st Z, r24
 udelay();
 14c: 0c 94 83 00 jmp 0x106 ; 0x106 <_Z6udelayv>

As you can see in the above, instead of using read/modify/write sequences to implement PORTB |= 0x01; PORTB &= ~0x01;, the compiler produced sbi 0x05,0; cbi 0x05,0;, to directly set and then clear bit 0 of register 5, PORTB. This uses 4 clock cycles total, producing a pulse that's high for 2 cycles.

avr-gcc implemented PINB = 1; PINB = 1; via ldi r24, 1; out 3,r24; out 3,r24, which takes 3 cycles total and produces a pulse that's high for 1 cycle.

It implemented PINB = bitofpin; PINB = bitofpin; via lds r24, 0x0100; out 3,r24; lds r24, 0x0100; out 3,r24, which takes 6 cycles total and produces a pulse that's high for 3 cycles. (In setup(), the value of bitofpin was stored in RAM location 0x100, and the value of portofpin was stored in 0x101, 0x102.)

Finally, it implemented *portofpin = bitofpin; *portofpin = bitofpin; via a six-instruction sequence that takes 14 cycles and produces a pulse that's high for 7 cycles. Obviously, the compiler could have produced a 4-instruction sequence (by not reloading r30, r31, and r24) that takes 8 cycles and a pulse that's high for 1 cycle, but it didn't.


Compilation on Mega

loop(), from compilation of the example program for a Mega or Mega 2560 board:

void loop() {
 PORTB |= 0x01; // Set Uno pin 8
 218: 28 9a sbi 0x05, 0 ; 5
 PORTB &= ~0x01; // Clear Uno pin 8
 21a: 28 98 cbi 0x05, 0 ; 5
 udelay();
 21c: fa df rcall .-12 ; 0x212 <_Z6udelayv>
 21e: 81 e0 ldi r24, 0x01 ; 1
 PINB = 1; // Toggle Uno pin 8
 220: 83 b9 out 0x03, r24 ; 3
 222: 83 b9 out 0x03, r24 ; 3
 PINB = 1; // Toggle Uno pin 8
 224: f6 df rcall .-20 ; 0x212 <_Z6udelayv>
 udelay();
 226: 80 91 00 02 lds r24, 0x0200
 PINB = bitofpin; // Toggle Uno pin 8
 22a: 83 b9 out 0x03, r24 ; 3
 22c: 80 91 00 02 lds r24, 0x0200
 PINB = bitofpin; // Toggle Uno pin 8
 230: 83 b9 out 0x03, r24 ; 3
 232: ef df rcall .-34 ; 0x212 <_Z6udelayv>
 234: e0 91 01 02 lds r30, 0x0201
 udelay();
 238: f0 91 02 02 lds r31, 0x0202
 *portofpin = bitofpin; // Toggle Arduino pin 8
 23c: 80 91 00 02 lds r24, 0x0200
 240: 80 83 st Z, r24
 242: e0 91 01 02 lds r30, 0x0201
 246: f0 91 02 02 lds r31, 0x0202
 *portofpin = bitofpin; // Toggle Arduino pin 8
 24a: 80 91 00 02 lds r24, 0x0200
 24e: 80 83 st Z, r24
 250: e0 cf rjmp .-64 ; 0x212 <_Z6udelayv>

The assembly code for Mega is much the same as that for the Uno, except that bitofpin and portofpin values were stored in RAM 0x200–0x202 instead of 0x100–0x102, and rcall or rjmp was used to call udelay();, instead of call or jmp.


Making assembly listings

To make assembly listing files for a program, use a command like

avr-objdump -CSI$PWD $TMPPATH/togglepin8.cpp.elf > togglepin8.ino.mega.asm

where $PWD represents the current working directory that contains a sketch like togglepin8.ino, and $TMPPATH represents whatever temporary directory the IDE stuck the source in to compile and link it. The C switch (not used for above listings) tells avr-objdump to demangle symbols. Switch S says to display source code intermixed with disassembly if possible. Switch I tells location of source code.


Conditional compilation

Conditional compilation is another way to make the source code portable to several Arduinos. For example, near the beginning of the sketch one could include the following #ifdef and #define statements to set up BITOFPIN and PORTOFPIN values for later use:

#ifdef ARDUINO_AVR_UNO
#define BITOFPIN 1
#define PORTOFPIN PINB
#endif
#ifdef ARDUINO_AVR_MEGA2560
#define BITOFPIN _BV(PH5)
#define PORTOFPIN PINH
#endif

To find out what board-dependent symbols are defined at compile time, turn on verbose output during compilation and look for flags of the form -Dsym=val.

answered Jan 5, 2017 at 19:46

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.