Tutorial on digital I/O, ATMega PIN/PORT/DDR D/B registers vs. ARM GPIO_PDIR / _PDOR

Arduino 1.6.4 is likely to release very soon too...

The fun never ends - I just downloaded 1.23beta to install.

But after the big jumps through 1.6.2 you made 1.6.3 seem easy . . . though they have had more time to put changes in 1.6.4, are you seeing any preview of their extent?
 
Hello,
I am new to teensy and wondered if also 16 or 32 bit port manipulation was possible with some effort through the GPIO_PDOR registers.

It was mentioned that these are flexible. So is it possible to map 16 or even 32 of the teensy pins to one of these registers and then write all the pins in a single instruction ?

I guess this is an ultra-demanded feature, but I couldnt find a discussion about it.
 
I am pretty novice at all this, but am converting over an mbed LPC1768 project to Teensy 3.1. It is a can/bdm project. I am in the process of wading through the code and changing it over to Teensy and am trying to get some port register stuff working for the BDM portion.

The code I am trying to figure out and port over is:
Code:
#define IN_BDM              (bool)((LPC_GPIO2->FIOPIN) & (1 << 0))      // FREEZE is p26 P2.0

I think this is a macro which is checking if gpio2 is either high or low and setting a state (IN_BDM) based on that. Previously, I would
Code:
PinMode(FREEZE, INPUT);
if (digitalRead(FREEZE) == HIGH){ IN_BDM }

I really need a character by character explanation of what that macro does. I think it is reading the pin GPIO2 and bitwise anding it to (1<<0)? If so, what does that do/mean?

How do I structure this? I have defined all of portD as my BDM interface. (2,14,7,8,6,20,21,5)
Code:
// For Teensy
#define  PIN_PWR 2                  // power supply
#define  PIN_NC 14                 // connection signal
#define  PIN_BERR 7                // double bus fault input - will be an input when it is working properly
#define  PIN_BKPT 8                // breakpoint/serial clock
#define  PIN_RESET 6               // reset signal
#define  PIN_DSI 20                // data input (to ECU) signal
#define  PIN_DSO 21                // data output (from ECU) signal
#define  PIN_FREEZE 5              // freeze signal

EDIT: I think I got it. Now how to do 'bit-banding' writes/reads. This is also done in this project and it quite a bit beyond me. I understand in principle, but c++ practice is a bit beyond me.
 
Last edited:
Can we set pin mode using GPIOx_PDIR as well? I ran across this in my adventure converting this mbed code and it appears that they use something similar. On the Teensy 3.x, do these commands work/make sense?
Code:
GPIOD_PDIR &= ~(1 << 2);   // Set pin D2 as input.
...
GPIOD_PDIR |= (1 << 2);    // Set pin D2 as output.
 
Last edited:
help! thanks!

Hello everyone! I happened to stumble onto this thread and realized this would be perfect for reading the HCTL-2022 quadrature decoding counter, which gives 32 bits in counts, shuffling between 4 x 8 bits. Currently I am using a teensy 3.2, and looking through its schematic found that the PIND is still the same few pins, {2,14,7,8,6,20,21,5}.

The first code did not work, so I added in normal digitalRead's to compare and see what was going on (fastcount vs. count), and everything suddenly started working, both fastcount and count were consistent. So I played around with the bytes and found that all the values were good, however everytime I comment out the digitalRead's or digitalReadFast's, fastcount goes haywire. The theoretical speed was doubled, but I only received bad data.

So, right now I have stuck to normal digitalRead's, which loops at 4.7 milliseconds (I'm trying to hit 1kHz, oh well), digitalReadFast's sometimes give me inconsistent data too. Could it be because I directly connected OE_COUNT, RST_COUNT, SEL1, SEL2 as OUTPUTs (3.3v logic to 5v device)? It works perfectly with normal digitalRead's though. The teensy GPIO's are 5v tolerant so I assume that using PIND is fine without translation.

Or could it be that the register does not "fill" itself up unless there's a digitalRead going on?

Below is a snippet of the code that uses PIND,

Code:
#define RST_COUNT 0
#define OE_COUNT 1
#define SEL1 3
#define SEL2 4

byte pinTable[] = {2,14,7,8,6,20,21,5}; //5 is D7 while 2 is D0
byte firstByte;
byte secondByte;
byte thirdByte;
byte fourthByte;

long fastcount = 0;

void setup() {
  Serial.begin(115200);
  
  for (int i=0; i<8; i++) { pinMode(pinTable[i],INPUT); } //I changed it from INPUT_PULLUP to INPUT

  pinMode(SEL1, OUTPUT);
  pinMode(SEL2, OUTPUT);
  pinMode(RST_COUNT, OUTPUT);
  pinMode(OE_COUNT, OUTPUT); 
  
  digitalWrite(RST_COUNT, HIGH);
  digitalWrite(OE_COUNT, HIGH);
}

void loop() {
  digitalWrite(OE_COUNT,LOW);

  digitalWrite(SEL1,1);
  digitalWrite(SEL2,0);
  firstByte = GPIOD_PDIR & 0xFF;
  fastcount = (long)firstByte;

  digitalWrite(SEL1,0);
  digitalWrite(SEL2,0);
  secondByte = GPIOD_PDIR & 0xFF;
  fastcount |= (long)(secondByte<<8);

  digitalWrite(SEL1,1);
  digitalWrite(SEL2,1);
  thirdByte = GPIOD_PDIR & 0xFF;
  fastcount |= (long)(thirdByte<<16);
  
  digitalWrite(SEL1,0); //SEL1
  digitalWrite(SEL2,1); //SEL2
  fourthByte = GPIOD_PDIR & 0xFF;
  fastcount |= (long)(fourthByte<<24);

  digitalWrite(OE_COUNT,HIGH);
  
  timer = micros();
  Serial.print(timer);
  Serial.print("\t");
  Serial.print(fastcount);
  Serial.println("  ");
}

Then this is the code that I used to compare between fastcount and count,
Code:
#define RST_COUNT 0
#define OE_COUNT 1
#define SEL1 3
#define SEL2 4

byte pinTable[] = {2,14,7,8,6,20,21,5}; //5 is D7 while 2 is D0
byte firstByte;
byte secondByte;
byte thirdByte;
byte fourthByte;

int encoder_array[32];
long count = 0;
long fastcount = 0;

void setup() {
  Serial.begin(115200);
  
  for (int i=0; i<8; i++) { pinMode(pinTable[i],INPUT); } //I changed it from INPUT_PULLUP to INPUT

  pinMode(SEL1, OUTPUT);
  pinMode(SEL2, OUTPUT);
  pinMode(RST_COUNT, OUTPUT);
  pinMode(OE_COUNT, OUTPUT); 
  
  digitalWrite(RST_COUNT, HIGH);
  digitalWrite(OE_COUNT, HIGH);
}

void loop() {
  digitalWrite(OE_COUNT,LOW);

  digitalWrite(SEL1,1);
  digitalWrite(SEL2,0);

  encoder_array[0] = digitalRead(2); //I was trying to use digitalReadFast with the argument as a constant
  encoder_array[1] = digitalRead(14);
  encoder_array[2] = digitalRead(7);
  encoder_array[3] = digitalRead(8);
  encoder_array[4] = digitalRead(6);
  encoder_array[5] = digitalRead(20);
  encoder_array[6] = digitalRead(21);
  encoder_array[7] = digitalRead(5);

  firstByte = GPIOD_PDIR & 0xFF;
  fastcount = (long)firstByte;

  digitalWrite(SEL1,0);
  digitalWrite(SEL2,0);

  encoder_array[8] = digitalRead(2);
  encoder_array[9] = digitalRead(14);
  encoder_array[10] = digitalRead(7);
  encoder_array[11] = digitalRead(8);
  encoder_array[12] = digitalRead(6);
  encoder_array[13] = digitalRead(20);
  encoder_array[14] = digitalRead(21);
  encoder_array[15] = digitalRead(5);

  secondByte = GPIOD_PDIR & 0xFF;
  fastcount |= (long)(secondByte<<8);

  digitalWrite(SEL1,1);
  digitalWrite(SEL2,1);

  encoder_array[16] = digitalRead(2);
  encoder_array[17] = digitalRead(14);
  encoder_array[18] = digitalRead(7);
  encoder_array[19] = digitalRead(8);
  encoder_array[20] = digitalRead(6);
  encoder_array[21] = digitalRead(20);
  encoder_array[22] = digitalRead(21);
  encoder_array[23] = digitalRead(5);

  thirdByte = GPIOD_PDIR & 0xFF;
  fastcount |= (long)(thirdByte<<16);
  
  digitalWrite(SEL1,0); //SEL1
  digitalWrite(SEL2,1); //SEL2

  encoder_array[24] = digitalRead(2);
  encoder_array[25] = digitalRead(14);
  encoder_array[26] = digitalRead(7);
  encoder_array[27] = digitalRead(8);
  encoder_array[28] = digitalRead(6);
  encoder_array[29] = digitalRead(20);
  encoder_array[30] = digitalRead(21);
  encoder_array[31] = digitalRead(5);

  fourthByte = GPIOD_PDIR & 0xFF;
  fastcount |= (long)(fourthByte<<24);
  
  digitalWrite(OE_COUNT,HIGH);

  long count = 0;
  for (int i=0; i<32; i++)
  {
      count = count + (encoder_array[i] * (int)pow(2,i));
  }

  timer = micros();
  Serial.print(timer);
  Serial.print("\t");
  Serial.print(fastcount);
  Serial.print("\t");
  Serial.print(count);
  Serial.println("  ");
}

Sorry if the code is a mess, I was copying and pasting small pieces and rewriting some of them. So tldr:

Case 1 : PIND only, didn't work
Case 2 : PIND and digitalReadFast/digitalRead, works, but with SOME inconsistencies
Case 3 : digitalReadFast only, works, but with A LOT of inconsistencies
Case 4 : digitalRead only, works perfectly

Any insights would be greatly appreciated! In the meantime, I will use a TXS0108PWR to translate all the outputs (RST_COUNT, OE_COUNT, SEL1, SEL2) to 5V and see if the problem is still there.
 
Last edited:
Does it work with Teensy @ 24 MHZ ? If yes, perhaps the new code is too fast :)

Hmm, it kind of defeats the purpose of running at 24MHz, I was intending to have it as fast as possible.

Update :

I found the issue (or I hope it is the issue). I had the nagging suspicion that the registers are not refreshing properly when I call them, so I removed all the digitalRead's except for one random one, just to see. And hey it works, however the speed is not very much improved from using just all digitalRead's.

PIND + 1 random digitalRead + all the other sensors, I am getting ~4.6 millisecond per loop

all digitalRead's + all the other sensors, I am getting ~4.7 millisecond per loop

is there a way to refresh the 8 bits faster, or in a more optimized way (other than manually reading one pin)?

Below is my code that I have working perfectly but not fast,

Code:
byte pinTable[] = {2,14,7,8,6,20,21,5}; //5 is D7 while 2 is D0
byte firstByte;
byte secondByte;
byte thirdByte;
byte fourthByte;

#define RST_COUNT 0
#define OE_COUNT 1
#define SEL1 3
#define SEL2 4

int encoder_array[32];
long fastcount = 0;

unsigned long timer;

void setup() {
  Serial.begin(115200);
  for (int i=0; i<8; i++) { pinMode(pinTable[i],INPUT); }
  pinMode(SEL1,OUTPUT); //SEL1
  pinMode(SEL2,OUTPUT); //SEL2
  pinMode(RST_COUNT,OUTPUT); //RST_COUNT
  pinMode(OE_COUNT,OUTPUT); //OE_COUNT
  digitalWrite(0,HIGH); //RST COUNT
  digitalWrite(1,HIGH); //OE_COUNT
}

void loop() {
  long fastcount = 0;
  
  digitalWrite(1,LOW); //OE_COUNT

  digitalWrite(3,1);
  digitalWrite(4,0);

  encoder_array[0] = digitalRead(2); //{2,14,7,8,6,20,21,5}  **leaving this random read to refresh the 8 bits
//  encoder_array[1] = digitalRead(14);
//  encoder_array[2] = digitalRead(7);
//  encoder_array[3] = digitalRead(8);
//  encoder_array[4] = digitalRead(6);
//  encoder_array[5] = digitalRead(20);
//  encoder_array[6] = digitalRead(21);
//  encoder_array[7] = digitalRead(5);

  firstByte = GPIOD_PDIR & 0xFF;
  fastcount = (long)firstByte;
  
  digitalWrite(3,0);
  digitalWrite(4,0);

  encoder_array[8] = digitalRead(2); //**leaving this random read to refresh the 8 bits
//  encoder_array[9] = digitalRead(14);
//  encoder_array[10] = digitalRead(7);
//  encoder_array[11] = digitalRead(8);
//  encoder_array[12] = digitalRead(6);
//  encoder_array[13] = digitalRead(20);
//  encoder_array[14] = digitalRead(21);
//  encoder_array[15] = digitalRead(5);

  secondByte = GPIOD_PDIR & 0xFF;
  fastcount |= (long)(secondByte<<8);

  digitalWrite(3,1);
  digitalWrite(4,1);

  encoder_array[16] = digitalRead(2);  //**leaving this random read to refresh the 8 bits
//  encoder_array[17] = digitalRead(14);
//  encoder_array[18] = digitalRead(7);
//  encoder_array[19] = digitalRead(8);
//  encoder_array[20] = digitalRead(6);
//  encoder_array[21] = digitalRead(20);
//  encoder_array[22] = digitalRead(21);
//  encoder_array[23] = digitalRead(5);

  thirdByte = GPIOD_PDIR & 0xFF;
  fastcount |= (long)(thirdByte<<16);
  
  digitalWrite(3,0); //SEL1
  digitalWrite(4,1); //SEL2

  encoder_array[24] = digitalRead(2);  //**leaving this random read to refresh the 8 bits
//  encoder_array[25] = digitalRead(14);
//  encoder_array[26] = digitalRead(7);
//  encoder_array[27] = digitalRead(8);
//  encoder_array[28] = digitalRead(6);
//  encoder_array[29] = digitalRead(20);
//  encoder_array[30] = digitalRead(21);
//  encoder_array[31] = digitalRead(5);

  fourthByte = GPIOD_PDIR & 0xFF;
  fastcount |= (long)(fourthByte<<24);

  digitalWrite(1,HIGH); //OE_COUNT

  timer = micros();
  Serial.print(timer);
  Serial.print("\t");
  Serial.print(fastcount);
}
 
Hmm, it kind of defeats the purpose of running at 24MHz, I was intending to have it as fast as possible.
I know, but this test would help to know wether it is a speed issue or not. It takes some seconds only....
If it is a speed-issue, a delayMicroseconds(1) would help, too.... then, you could try shorter delays, and insert some asm volatile ("nop").
 
I know, but this test would help to know wether it is a speed issue or not. It takes some seconds only....
If it is a speed-issue, a delayMicroseconds(1) would help, too.... then, you could try shorter delays, and insert some asm volatile ("nop").

Oh my god, it works! Thank you so much, Frank! I see, so what you meant by the code being too fast, would be that the decoder couldn't keep up!

Now it runs at ~2.7 milliseconds :D
 
I made an account just to say thanks.
It would have been so annoying going through the datasheet until that WTF moment.
I discovered that the PINx registers were emulated because you can't assign as you would in AVR likeso DDRA=0x0F|1<<6. It throws an error. I hope the guys on PJRC put together a better pin register access tutorial, because I think that pin register emulation is pointless, and direct access to pins is critical.
 
I made an account just to say thanks.
It would have been so annoying going through the datasheet until that WTF moment.
I discovered that the PINx registers were emulated because you can't assign as you would in AVR likeso DDRA=0x0F|1<<6. It throws an error. I hope the guys on PJRC put together a better pin register access tutorial, because I think that pin register emulation is pointless, and direct access to pins is critical.

Best is, to take a look at the Datasheet.
It can tell more than every tutorial.
 
Joaquin, you most definitely CAN access the PINx registers just like in AVR. Except that they are named "GPIOx_PDIR" instead of "PINx". This is just a symbol error, not a missing piece of hardware! Using them invokes no overhead, they are direct hardware registers just as their AVR namesakes.

Wherever you had "PINA" use "GPIOA_PDIR". PDIR = Port Data Input Register.
Likewise use GPIOA_PDDR instead of DDR. ("Port data direction register)
and use GPIOA_PDOR instead of PORTA. ("Port data output register)

The purpose of my first post, which started this thread, was to show the pin ordering. For example, see this code snippet, adopted from that post:

// byte pinTable[] = {2,14,7,8,6,20,21,5};
for (int i=0; i<=7; i++)
{
GPIOD_PDOR = 1<<i;
delay(1000);
}

This sets each of 8 pins high, sequentially 1 second apart. What is different about GPIOD_PDOR as compared to AVR's PORTD is that on the AVR the 8 pins would probably be found on the board in sequential order, whereas on the teensy, they are pins 2,14,7,8,6 etc in that (funny) order.

Hope that helps clarify.
 
Hope that helps clarify.
Hey! thanks for the answer. Indeed I understand. Now I have two questions
- Why was the decision of scrambling the ports taken? It probably brought a lot of trouble in the making of the libraries
- Why immortalSpirit said that changing data registers is more complex?

*edit*
I just happened to have an older teensy at reach, which happens to have an atmel microcontroller, so the pin mapping probably was to keep the same footprint. Am I right?
 
Last edited:
Yes, all the teensy's have kept similar/same ordering, but then that just defers the question to why the 1st version was scrambled. In the final analysis, you'd have to ask Paul.

If u look at the ATMEGA 832 chip, used by Arduino Uno and others, the pins are much more orderly. The Teensy's are based on the ARM Cortex-M4 and if you google it's pinout you'll see it is itself quite scrambled.

I didn't spend a lot of time looking at it, but the ARM Cortex manual shows what appears to be a fantastic level of programmability on the ports, and it may be that with more research one could do more unscrambling. You are getting pretty low-level and affecting timer ports and internal signals and all kinds of stuff that is sensitive and tricky. I wouldn't mess with it and I'm guessing neither would Paul. There is just not much need to care, except in a few cases which I guess to be rare.

I suspect that the problem isn't had by many people. I suspect that most libraries, for portability, probably use the slower digitalRead() and digitalWrite() functions. By "slower" we are just talking about a few microseconds, which is kinda sorta silly to worry about for *most* cases.

In your own case, Joaquin, it was merely a matter of using a different register than the one you expected. The technical capability was still there, just in a harder-to-find place.

For the rest of us, the "big" problem is not electrical or technical in any way, it is merely "neatness" on a breadboard. If we have say an 8-bit parallel cable, the Arduino Uno will connect them all in a nice "pretty" row of jumpers, whereas the teensy jumpers will end in a tangle of crossed wires. I would say, for most projects, this is a trivial concern. I've attached a pix of one of my breadboards. One end of the jumper cable is "pretty", the other is "messy". That is not a terrible price to pay! :)

Screen Shot 2016-11-09 at 12.49.18 PM.jpg
 
This from Paul in an earlier discussion:
A lot of planning went into maximizing Arduino compatibility. A lesson learned from earlier versions of Teensy was how entrenched the Arduino Uno pinout is, even years after Arduino has released other boards with signals on different pins. For example, a LOT of tutorials on many websites, particularly Adafruit and Sparkfun, are written for the SPI signals to be pins 10, 11, 12 and 13. A tremendous effort went into studying nearly all libraries and examples that existed 2 years ago (when Teensy 3.0 was designed) and crafting a pinout that would maximize compatibility.

Even with a 4 layer PCB, routing all those pins was incredibly difficult. In the end, I believe it was worthwhile. For most advanced users, the pin arrangement doesn't matter much. But for a lot of novices, having the signals on pins that correspond to many tutorials published on other websites really makes things simpler.
https://forum.pjrc.com/threads/17532-Tutorial-on-digital-I-O-ATMega-PIN-PORT-DDR-D-B-registers-vs-ARM-GPIO_PDIR-_PDOR?p=21228&viewfull=1#post21228
--Michael

Edit: DOH! Had bookmarked this and just now realized it's earlier in this same thread...
--M
 
Last edited:
PIN 13 (LED) has a different behaviour due to the LED capacitor. So 8 bits register C is not available. Register A is not completely available (A0 A1 A2 A" already used by memory) and B is not available because PTB6 is don't exist. Would it be possible to design a Teensy with 3 really available 8 bits ports ?
 
LED capacitor? Never heard of that. Is that the capacitance formed by the depletion region between the N and P junctions, or is it a physical capacitor included within the LED package?
 
About pins changing states at the exact same moment, why don't you try using transparent latches like HEF40373 or 74HCT573 (D-type Flip-flops with Output latches)? You input your parallel data, and latch it, and unlatch it, on and on, and you get simultaneous bus bits state changes. I think the downside of this is more on about speed issues.
 
About pins changing states at the exact same moment, why don't you try using transparent latches like HEF40373 or 74HCT573 (D-type Flip-flops with Output latches)? You input your parallel data, and latch it, and unlatch it, on and on, and you get simultaneous bus bits state changes. I think the downside of this is more on about speed issues.

Obviously, time response is different on PIN 13 (LED) (PTC5) than on any other Pin. I'am using this pin (and the others ones PTC0 1 2 3 4 for 6 74HCT138 controls. All are running fine, but the PTC5 is never instantly changed. Fortunately, I can do another tasks before putting PTC5 HIGH. Otherwise, the 138 don't run correctly. (The 5 other HCT138 controlled by PTC0 1 2 3 4 are running fine. So there is obviously a different behaviour on PIN 13. So I would like to have really 3 complete registers (on new Teensy), fast 8 bits registers A B C without LED, which could be controlled on upper pins such as PTX11, 12, 13, and so on ... But not on bits 0 to 7. Thanks a lot.
 
I replaced the LED in my Teensy 3.6 from orange to white - white has higher forward voltage than orange so it loads less to pin 13 compared to orange LED. The voltage may be ramping because of low current capability of Teensy ports. This hasn't been an issue for me though, maybe because of the replacement I did.
 
time response is different on PIN 13 (LED) (PTC5) than on any other Pin.

Can you provide a small but complete program to demonstrate the problem? Will it work if I just run your program on a Teensy 3.2 here and use my oscilloscope to view the waveforms on pin 13 and up to 3 other pins? Or does the problem only occur when Teensy is soldered to your PCB?

I have a 4 channel scope with 200 MHz bandwidth available for viewing the waveforms from whatever code you post. But until you post a complete program I can run here on a Teensy 3.2, there's little I can do other than guess. Even then, from these 2 message, I simply do not have guesses. I need to actually see the problem. Please post a complete program which I can copy and paste into Arduino and run on a Teensy 3.2 here.
 
Back
Top