3

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?

5

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:

3

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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