initializing unused GPIO pins

paynterf

Well-known member
In a recent thread regarding ESD damage to a Teensy 3.5, it was suggested that all unused GPIO pins be initialized to some known state (like OUTPUT_LOW). I have now added a 'InitAllPins' function to do just that to my Teensy projects, but I'm curious as to how others manage this.

Currently my T3.5 'InitAllPins()' function looks like this:

Code:
void InitAllPins()
{
  pinMode(0, OUTPUT); digitalWrite(0, HIGH);
  pinMode(1, OUTPUT); digitalWrite(1, HIGH);
  pinMode(2, OUTPUT); digitalWrite(2, HIGH);
  pinMode(3, OUTPUT); digitalWrite(3, HIGH);

  pinMode(4, OUTPUT); digitalWrite(4, HIGH);
  pinMode(5, OUTPUT); digitalWrite(5, HIGH);
  pinMode(6, OUTPUT); digitalWrite(6, HIGH);
  pinMode(7, OUTPUT); digitalWrite(7, HIGH);

  pinMode(8, OUTPUT); digitalWrite(8, HIGH);
  pinMode(9, OUTPUT); digitalWrite(9, HIGH);
  pinMode(10, OUTPUT); digitalWrite(10, HIGH);
  pinMode(11, OUTPUT); digitalWrite(11, HIGH);

  pinMode(12, OUTPUT); digitalWrite(12, HIGH);
  pinMode(13, OUTPUT); digitalWrite(13, HIGH);
  pinMode(14, OUTPUT); digitalWrite(14, HIGH);
  pinMode(15, OUTPUT); digitalWrite(15, HIGH);

  pinMode(16, OUTPUT); digitalWrite(16, HIGH);
  pinMode(17, OUTPUT); digitalWrite(17, HIGH);
  pinMode(18, OUTPUT); digitalWrite(18, HIGH);
  pinMode(19, OUTPUT); digitalWrite(19, HIGH);

  pinMode(20, OUTPUT); digitalWrite(20, HIGH);
  pinMode(21, OUTPUT); digitalWrite(21, HIGH);
  pinMode(22, OUTPUT); digitalWrite(22, HIGH);
  pinMode(23, OUTPUT); digitalWrite(23, HIGH);

  pinMode(24, OUTPUT); digitalWrite(24, HIGH);
  pinMode(25, OUTPUT); digitalWrite(25, HIGH);
  pinMode(26, OUTPUT); digitalWrite(26, HIGH);
  pinMode(27, OUTPUT); digitalWrite(27, HIGH);

  pinMode(28, OUTPUT); digitalWrite(28, HIGH);
  pinMode(29, OUTPUT); digitalWrite(29, HIGH);
  pinMode(30, OUTPUT); digitalWrite(30, HIGH);
  pinMode(31, OUTPUT); digitalWrite(31, HIGH);

  pinMode(32, OUTPUT); digitalWrite(32, HIGH);
  pinMode(33, OUTPUT); digitalWrite(33, HIGH);
  pinMode(34, OUTPUT); digitalWrite(34, HIGH);
  pinMode(35, OUTPUT); digitalWrite(35, HIGH);

  pinMode(36, OUTPUT); digitalWrite(36, HIGH);
  pinMode(37, OUTPUT); digitalWrite(37, HIGH);
  pinMode(38, OUTPUT); digitalWrite(38, HIGH);
  pinMode(39, OUTPUT); digitalWrite(39, HIGH);
}

But later it occurred to me that I had not initialized ALL the pins - the T3.5 GPIO pin numbering actually goes up to 63 if you count the pins on the back and the SD card pins. Moreover, it 'would be nice' (I used to hate hearing those words when I was working!) if the same function could be used for both T3.2 and T3.5 (and others, but those are the only two I use currently).

Something like

Code:
#define MAX_PIN_NUM 63 //T3.5. For T3.2 it is 33

void InitAllPins
{
  for (size_t i = 0; i < MAX_PIN_NUM; i++)
    {
      pinMode(I, OUTPUT); 
      digitalWrite(I, HIGH);
    }
}

Moreover, I suspect there is a way to automagically change the MAX_PIN_NUM value from 63 (for T3.5) to 33 (for T3.2) at compile time so the '#define MAX_PIN_NUM' wouldn't have to be edited manually.

The above loop-based 'InitAllPins()' function compiles (and apparently runs fine) on my T3.5 project, but I have no idea if I'm causing more problems than I'm fixing.

How are others doing this?

TIA,

Frank
 
Last edited:
If you look at cores\teensy3\core_pins.h you will see there are defines already for this:
Code:
#elif defined(__MK64FX512__)
#define CORE_NUM_TOTAL_PINS     64
#define CORE_NUM_DIGITAL        64
#define CORE_NUM_INTERRUPT      64
#define CORE_NUM_ANALOG         27
#define CORE_NUM_PWM            20
#elif defined(__MK66FX1M0__)
I would use: CORE_NUM_DIGITAL

If you look at the sources for pinMode you will see:
Code:
void pinMode(uint8_t pin, uint8_t mode)
{
	volatile uint32_t *config;

	if (pin >= CORE_NUM_DIGITAL) return;
...
 
So, this?

Code:
void InitAllPins()
{
  for (uint8_t i = 0; i < CORE_NUM_DIGITAL; i++)
    {
      pinMode(i, OUTPUT); 
      digitalWrite(i, HIGH);
    }
}

Any issues that would lead me to use 'digitalWrite(i, LOW)' instead of 'digitalWrite(i, HIGH)'?

Frank
 
Sorry I am a software guy not an EE so not an expert in ESD and the like. Hopefully one understands this will answer.

A couple of from the gut comments.

If you are worried at all about running in low power Setting to high, maybe will cause it to use my power than low...

My gut tells me, that in most cases if I were not using pins I would probably disable the pins: INPUT_DISABLE

If I thought that there was some benefit to having the pins high or low, I might be tempted to use INPUT_PULLUP or INPUT_PULLDOWN in case something shorts.

But again this is just from the gut of a software guy.
 
My worry would be what's protecting the pins when the Teensy's powered down? That would imply protection circuitry is needed anyway,
rather than a software solution. Interference strong enough to take out a pin isn't likely to just go away when you turn your microcontroller off...

A commonly used protection scheme for signals going off-board is to reniforce the internal protection diodes with schottky diodes which
conduct before the internal pn-diodes. Adding some series resistance to protect the schottky diodes from large currents, and the schottky's
prevent the internal diodes from seeing anything extreme (*). You'll often see BAT54S or similar dual-schottky diodes used for this purpose.
There's even a package with 8 diodes to protect 4 pins, the QSBT40. This sort of scheme doesn't care if the power is present or not, so
seems a better approach to me - especially with the series resistance present for current limiting.

I'd also worry that pins initialized as outputs are more at risk of damage through accidental short-circuiting, and would favour having them LOW,
since shorting to ground is the most common scenario with ground plane PCBs.

Its commonly done to initialized unused pins for micro-power operation where the forbidden input states can cause significant
current dissipation in the pads (forbidden state is somewhere betweeb LOW and HIGH, where the CMOS inverter is in cross-conduction,
which can be of the order of a milliamp or so). Typical sleep states can get you to 10µA or so consumption, which gives great battery
life unless the chip is also draining mA's of current sporadically due to floating inputs.

However you only need hysteresis mode or an internal pullup or pulldown to cure this issue. Some processors have bus-hold circuitry
on inputs which is like a weak pull-up or pull-down that changes in response to any strong input state - connect the pin to 0V and its
weak pull-down activates, so removing the 0V doesn't allow the pin to float, for instance.

(*) Large currents flowing through internal protection diodes is one way to risk triggering CMOS latch-up (typically a chip-frying experience,
and not in a good way)
 
While both LOW and HIGH output states should be equivalent electrically, I agree that the LOW option would probably be a better choice for most scenarios, where GND occupies more physical space than +V.

However, I don't think OUTPUT_DISABLE is appropriate, as that makes such pins more vulnerable to ESD events, as I recently found out the hard way.

Note that if you use the InitAllPins() idea, it has to be the FIRST INSTRUCTION in Setup() - otherwise you might well overwrite previous pin initialization code needed to make things like Wire.begin() work (ask me how I know).

Frank
 
Last edited:
Whatever you find to work for the pin state setting might be done with this for T_3.x and 4.x:
Code:
void startup_late_hook(){
  // void InitAllPins()
  for (uint8_t i = 0; i < CORE_NUM_DIGITAL; i++)
    {
      pinMode(i, OUTPUT); 
      digitalWrite(i, HIGH);
    }
}

This would get done sooner but just before the call to setup() so it couldn't get misplaced after some other .begin() pin usage within setup().

Not sure if analog pin usage it compromised with these advanced settings?
 
Back
Top