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
.