Parallel I/O - e.g reading or writing a whole byte, not one pin at a time

So far, everything I've seen in the documentation is about reading or writing single bits of I/O, i.e. digitalWrite() and digitalRead(), which only handle one bit at a time.

How can I do parallel I/O, e.g. write a whole byte at once to an output port, without having to loop through each bit doing a digitalWrite()?

Each of the ports have a variable defined in c for such activity; PORTA, PORTB, ...

I was surprised by this too...

In my application, I just wrote a little Output(byte) function, which of course is a simple little loop that writes one bit at a time. There is one advantage that I can define the port on any I/O pins I wish to use, and I can create a port of any width. In fact, I was passing a byte into the function but only using 7 bits and 7 I/O pins.

Each of the ports have a variable defined in c for such activity; PORTA, PORTB, ...

Where is that function, and how is it used? (I didn't see that in the in the Arduino language reference, and I don't remember it from ANSI C.)

wanderson:
Each of the ports have a variable defined in c for such activity; PORTA, PORTB, ...

Excellent. Thanks. I take it I can still configure them with pinMode()?

I don't see anything about that in the language reference either. Is there a list of predefined variables somewhere?

I've just found a Reference page called Port Manipulation, which gives all the details:

  • but only by googling what wanderson said. I can't find any way to get to it from the Language Reference page.

What's the best part of the forum to report mistakes or shortcomings in the documentation in the Reference section? I posted something earlier about the SoftwareSerial constructor documentation being out of date / incomplete, but I couldn't see where to put it. The link to the forum that's given on those pages takes you to Arduino Forum :: Development :: Other Software Development, which just doesn't seem appropriate.

1 Like

The functions are part of AVR-LIBC which is what the Arduino IDE uses at its base. The relevant section of that manual is avr-libc: <avr/sfr_defs.h>: Special function registers

Andy2No:

wanderson:
Each of the ports have a variable defined in c for such activity; PORTA, PORTB, ...

Excellent. Thanks. I take it I can still configure them with pinMode()?

I don't see anything about that in the language reference either. Is there a list of predefined variables somewhere?

You can still configure them with pinMode(); however, there is also a variable for each port that can allow you to set pin modes for the entire port at one time; DDRA, DDRB, etc...

Note that these port variables work MUCH faster than the Arduino functions...

Thanks for the link, wanderson. The hardware abstraction of the Arduino environment is all very well, but it's nice to be able to actually get at the hardware. As a lot of the pins have special functions, just numbering them from 0 to 13 doesn't seem all that useful to me anyway.

I read the bit about DDRA, DDRB etc, and there'll definitely be times when that's more convenient than using pinMode for each bit. The speed consideration won't normally be all that important, but there may still be situations where frequently switching the ports between inputs and outputs is needed. Being able to use PORTA or PINA, instead of looping for each bit, will make things much faster because those will be used over and over.

Bear in mind that of the 20 pins, only 8 are visible on one port (D0 to D7 on PIND/PORTD). Then you have D8 to D13 on PINDB/PORTB and A0 to A5 on PINC/PORTC.

And of the only port that has all 8 bits exposed, two of those are used for hardware serial, and are influenced by pull-up resistors to the USB chip, if you are working on the development board.

You can save a few microseconds by using direct port manipulation, but unless you really need to, I suggest sticking to the higher-level function calls. Think: readability and portability.

And I believe there is a Fast Digital IO library (can't remember its name) which effectively does port manipulation, providing you pass constants to it.

Yes, I realise there are restrictions on using certain pins, and actually an 8 bit output will more than likely be spread over two ports. However, that output will be done over and over again, so saving a few microseconds at that stage will make a huge difference in speed, and leave the CPU more time to do other things - I intend it to be interrupt driven, eventually.

Having set which bits I want as outputs, I could just do this, for example:

// Using bits 0..5 of PORTC for the low 6 bits, and bits 6..7 of PORTB for the high two bits.  All other bits are inputs, or not used.
PORTC= data;
PORTB= data >> 6;

That has to be a lot faster than using a loop with digitalWrite() for each pin... I'm not even sure how to loop through the analog pins as outputs, though it's allowable to use them for that. They seem to have to be referred to by name.

I'm not really concerned about portability, though using a library that does it better than using digitalWrite() for each pin separately, would be acceptable too.

Andy2No:
I'm not even sure how to loop through the analog pins as outputs, though it's allowable to use them for that. They seem to have to be referred to by name.

They are just numbers, effectively. Eg. on the Uno:

const static uint8_t A0 = 14;
const static uint8_t A1 = 15;
const static uint8_t A2 = 16;
const static uint8_t A3 = 17;
const static uint8_t A4 = 18;
const static uint8_t A5 = 19;

So you can loop through pins 0 to 19 if you want to.

Here:

http://code.google.com/p/digitalwritefast/downloads/list

1 Like

Thanks, Nick. I've ordered a couple of cheap ATmega168 boards based on the Arduino Mini, which also have A6 and A7. Would those be addressed as 20 and 21, following on from A5=19? I guess I'll find that out anyway.

The DigitalWriteFast library looks useful, but it still only manipulates one bit at a time, unless I'm missing something. Also, in the PDF that comes with it it says "For Ports above F, the microprocessor must read in all the 8 pins in a Port, modify and rewrite all 8 pins." (to change a single bit) - I guess that applies to the ATmega1280 and 2560. That would appear to involve a huge performance loss if what you wanted to do was change most or all of those bits together. By looping for each bit in a byte, you'd be doing more than eight times the work - reading the whole port eight times over, and ignoring all but one bit each time.

Andy2No:
Thanks, Nick. I've ordered a couple of cheap ATmega168 boards based on the Arduino Mini, which also have A6 and A7. Would those be addressed as 20 and 21, following on from A5=19? I guess I'll find that out anyway.

No, they're analog only. Read the datasheet for more info.

Also, nearly all pin writing is reading the current state, changing the pin you want, and writing it back. that way each write is independent of the other pins. It's 3 instructions instead of 1.

Is this the datasheet you're referring to, Nick, or is there a more detailed one somewhere?

The ReadMe PDF for the DigitalWriteFast library impliies that it uses sbi and cbi to set or clear individual bits on ports below F. Looking at the .h file, it uses bitWrite(). I can see lots of references to that in the forum, but I can't find any official documentation for it.

The Reference section seems to contain more than it admits to though. The index page appears to be incomplete, and I don't see a way to search it - all searches just seem to go to the forum.

My mistake. BitWrite() is mentioned on the main Reference page:

http://arduino.cc/en/Reference/BitWrite

There doesn't seem to be any way to search the Reference section though, and I'm sure there's more stuff in there, having found the Port Manipulation page.

Andy2No:
Thanks, Nick. I've ordered a couple of cheap ATmega168 boards based on the Arduino Mini, which also have A6 and A7. Would those be addressed as 20 and 21, following on from A5=19? I guess I'll find that out anyway.

I left out those two lines so I wouldn't confuse you. :wink:

const static uint8_t A0 = 14;
const static uint8_t A1 = 15;
const static uint8_t A2 = 16;
const static uint8_t A3 = 17;
const static uint8_t A4 = 18;
const static uint8_t A5 = 19;
const static uint8_t A6 = 20;
const static uint8_t A7 = 21;

"Use the Source, Luke!".


Looking at the .h file, it uses bitWrite(). I can see lots of references to that in the forum, but I can't find any official documentation for it.

#ifndef digitalWriteFast
#define digitalWriteFast(P, V) \
do {                       \
if (__builtin_constant_p(P) && __builtin_constant_p(V))   __atomicWrite__((uint8_t*) digitalPinToPortReg(P),P,V) \
else  digitalWrite((P), (V));         \
}while (0)
#endif  //#ifndef digitalWriteFast2

It appears to use a compiler trick, which is to examine the arguments, and if they are constants, use the constant method (the fast one) otherwise use the slow one.

Look, it's all very well wanting to be "fast" but for most applications it just isn't required. Sure, there will be counter examples, but for the most part, no. I use port manipulation as a last resort (eg. when clocking out bits for a VGA monitor, where nanonseconds count).

Is this the datasheet you're referring to, Nick, or is there a more detailed one somewhere?

The official Atmega328 datasheet should be your friend:

http://atmel.com/dyn/resources/prod_documents/8271S.pdf

Huh. I see those in the source, but I was under the impression the extra analog pins were just connected to the MUX and not to any digital pins. port C already has 7 pins in use (6 brought out on arduino + reset) and port B has 8 (6 brought out + 2 crystal), so there's no room for them.

Maybe he is referring to the 32 pin MLF version of the Atmega168. That has ADC6 and ADC7.

ATmega168, yes, but I didn't know the Mutant Liberation Front made microcontrollers...