Is 1.01 pinMode an improvement?

In version 1.0 pinMode(pin, mode) only set/cleared the Port Data Direction Register (DDR) bit. 1.0 does not change the Port Data Register (PORT).

In 1.01 pinMode(pin, mode) has three options for mode:

INPUT - clears DDR bit and clears PORT bit. (pullup disabled)

INPUT_PULLUP - clears DDR bit and sets PORT bit. (pullup enabled)

Else - sets DDR bit but does not change PORT bit.

Is this a better option than 1.0? It does impact some programs like software I2C where the SDA pin is used for both input and output.

I am about to release a new version of my fast digital I/O library and need to decide whether to follow 1.01.

1.0 is more flexible since you can put a port in input mode without changing the PORT bit or enable/disable the pullup with digitalWrite().

Why not do something similar with output mode by having modes of OUTPUT_LOW and OUTPUT_HIGH? There are six options - input or output with PORT unchanged, PORT set, or PORT clear.

Simplicity makes me want pinMode() to work like 1.0 but compatibility implies 1.01.

fat16lib: Is this a better option than 1.0?

I like it.

It does impact some programs like software I2C where the SDA pin is used for both input and output.

Don't know. I haven't work through the state machine.

Coding Badly,

After your vote and some thought I decided to implement 1.01 INPUT and INPUT_PULLUP modes.

Now OUTPUT mode bothers me slightly. Too bad the Arduino group didn't set the pin level for OUTPUT mode with something like this:

OUTPUT - put the pin in output mode and set its level low.

OUTPUT_HIGH - put the pin in output mode and set its level high.

Then pinMode() would put the pin in a definite state for both input and output mode.

fat16lib: Is this a better option than 1.0?

I don't like it. Until I saw this post I thought INPUT_PULLUP was the only one that wrote to the port. I see I am mistaken. Plus, it changes the way existing code behaves. I had thought that using INPUT_PULLUP was purely an optional extra. But combining digitalWrite and pinMode into one function with only a subtle warning about it, and changing existing behaviour is not so nice.

It should be called pinConfiguration or something if it does more than change the mode. Plus it isn't symmetrical. Why not do what fat16lib suggested and have two output modes as well?

it changes the way existing code behaves.

Another gem from the house of Arduino. New options should have been added and old ones left alone.


This one is more or less my fault, since I contributed this code. It was indeed discussed at length MANY times on the developer mail list, about basically these same trade-offs, and especially whether to change "INPUT" or remain perfectly compatible with the old behavior.

First of all, before talking about theory, I'd like to mention practice. This behavior has been in Teensyduino for 3+ years. While INPUT was technically changed, in practice the compatibility with virtually all existing sketches and libraries is excellent. There could be some cases were it matters, but in practice they're extremely rare. Virtually all published libraries and sketches use pinMode() first, then digitalWrite(). If this had a significant practical impact on real users, my opinion about changing an existing feature would be different.

The trouble with the old INPUT was its ambiguity. Yes, OUTPUT also suffers the same trouble, but even getting just INPUT and INPUT_PULLUP fixed required years of discussion. If it were my decision (obviously it's not), I'd add OUTPUT_LOW and OUTPUT_HIGH, and I'd probably make OUTPUT default to OUTPUT_LOW. But so far, only INPUT has been addressed, as it was much more urgent.

The huge problem with the old INPUT and the lack of INPUT_PULLUP is it drives all Arduino users one of 3 solutions for the pullup resistors:

1: using digitalWrite() in combination with pinMode() 2: direct register access 3: building their own hardware abstraction APIs.

The trouble with these, except perhaps #3, is they are tightly tied to 8 bit AVR's hardware. Not even 8 bit XMEGA AVR maintains this hardware behavior. It's hopelessly non-portable. If you believe you'll forever use only 8 bit AVR (but never XMEGA AVR), that's not an issue. But if you imagine someday using ARM or other chips, it's a huge problem. Late as it may be, Due is coming...

From a perspective of serving novice users, which is Arduino's main goal, APIs with quirks like depending on prior state are not very good design.

Regarding alternate pin manipulation APIs, many I have seen envision only non-xmega AVR hardware and are barely more portable to any other chips than directly hitting the registers. Some are much better. Designing cross-platform APIs that actually perform well, even on only 1 platform, is quite challenging.

I really appreciate all of your helpful comments.

I am trying to finish a digital I/O library for advanced users. Maybe I should go back to my original thought.

void mode(bool) - writes the DDR bit for the Arduino pin.

bool read() - reads the PIN bit for the Arduino pin.

void write(bool) - writes the PORT bit for the Arduino pin.

This would be easy to explain to an advanced user. I won’t worry about beginners.

Here is a test program that makes 125 ns wide pulses for scope tests:

// scope test for write timing
#include <DigitalPin.h>

// class with compile time pin number
DigitalPin<13> pin13;

void setup() {
  // set mode to OUTPUT
void loop() {

high() and low() are the same as write(1) and write(0). If the argument of write is not a constant, write takes longer than two cycles. A high address port on a Mega takes much longer for atomic writes.

I also provide a class with run-time pin numbers which takes 13 cycles for atomic writes. This is still about four times faster than digitalWrite(). 13 cycles is for a constant write argument.

This library also has a software SPI class for all SPI modes that runs at about 2 MHz. Currently I only provide MSB first but it would be easy to support LSB first also.

I currently use SoftSPI in SdFat for software SPI access to SD cards. This allows 328 SD shields to be used on Mega and Leonardo without jumpers.

I can’t imagine making a reasonable Arduino port for Cortex ARM processors. I use STM32, LPC, and SAM3. I would hate being locked into one Atmel processor that the Arduino team picked.

I can't imagine making a reasonable Arduino port for Cortex ARM processors.

That's what I'm attempting at present, but I've decided not to be too worried about 100% compatibility because it's probably not possible. (Especially as I'm stuck with C and no C++)

I think it's fair not to worry about novice users, they already have enough to work with and won't appreciate many features anyway. I'm providing wrappers for the common Arduino functions but am going my own way after that.

As to how fast it is, no idea yet and I admit I've not been too careful with optimising such things, preferring to have a neat interface but I'll put a LA on some pins soon to find out.


By this:

I can't imagine making a reasonable Arduino port for Cortex ARM processors.

I meant a port that would work on the various families of ARM Cortex processors with access to most of the features. Other than the Cortex core, these processors are very different.

RTOSs have a HAL layer so you can access the features of a given processor but programs are not easily portable between processors.

The single threaded Arduino system is OK for AVR. I don't think I could be satisfied with a system like that on ARM Cortex.

For the last 40 years I have used RTOSs in embedded systems that I developed professionally. For hobby projects I use ChibiOS/RT on ARM Cortex and often on AVR.

I suspect you will do well with your port, likely better than the Arduino group.

Other than the Cortex core, these processors are very different.

Right, at the level we're talking about it's 99% dealing with peripherals with hardly any need to work with the core. That makes the fact that processor X, Y and Z are Cortex-M? based pretty much irrelevant.

If you keep the HAL at a simple level and don't get to specific it should port across easily, but that means a lowest common denominator approach that possibly ignores some good features of various chips.

I started doing this port as a way to learn the LPCs before I tackle another project I have in mind, but it's become a project in it's own right :)

Oh well, it keeps me off the streets.