markkimball
Active member
For various reasons I've been looking at various ways to get reasonably fast parallel I/O for 8 bits on a teensy 4.0. None of the T4.0's GPIOs that are used for digital I/O have 8 contiguous bits, but I did find that GPIO6 has two contiguous groups of 4 bits or more. Getting 8 bits still requires a small amount of bit twiddling and shifts but those operations are pretty fast. I wrote some code to test the idea out and it seems to work OK.
I wrote it so it wouldn't be too difficult to turn the core functions into a library. It is simple enough that I have included it here:
I'm a get'er done kind of c/c++ programmer so any suggestions on how to improve the code are welcome.
I wrote it so it wouldn't be too difficult to turn the core functions into a library. It is simple enough that I have included it here:
Code:
/* 8 bit parallel I/O functions.
* For Teensy4.0
* The read/write routines directly access the GPIO6 data register for fast
* reads and writes.
*
* GPIO6 is the best choice because:
* pin #'s 21, 20, 23, 22, 16 and 17 (in MSB to LSB order, GPIO6 bit#'s 27-22) are contiguous;
* and pin#'s 15, 14, 18 and 19 are contiguous in the same order, bit#'s 19-16.
* So one register read could fetch 2 separate nybbles that can be combined to make an 8-bit word.
* If we choose bit #'s 27-24 for the upper nybble, we need to right-shift them by 20 bits;
* and for the lower 4 bits (#'s 19-16) we need to right-shift them by 16 bits.
* the T4's processor only supports 32-bit Read/Write accesses so we're stuck with both shift operations.
*
* if raw_reg = GPIO6_DR, we can do something like this to read an 8-bit input:
* raw_reg &= 0x0f0f0000;
* value = ((raw_reg >> 20) | (raw_reg >> 16)) & 0xff;
*
* outputting an 8 bit value will require some masking to avoid changing the state of other pins associated with GPIO6.
*
*
* The bit ordering can be found in core_pins.h, in the section that defines CORE_PINX_BIT,
* where X = 0...39.
* If we use an external multiplexer to place either the uppper 8 or lower 8 bits from a 16-bit ADC we can
* then combine THEM to acquire a 16-bit value.
* OK this is not optimal compared to directly fetching 16 bits, but the Teensy4.0 board design doesn't permit that.
*
* NOTE: we still will use pinMode() to configure the input/output pins.
*/
#include <Arduino.h>
#include <core_pins.h>
#define _pickbits 0x0f0f0000
#define ledpin 13
const int EightPins[8] = {21,20,23,22,15,14,18,19};
// I'm using a somewhat-Arduino-like naming scheme for these.
uint8_t digitalRead8Bits(void)
{
uint32_t rawregvalue;
rawregvalue = GPIO6_DR & _pickbits;
return (uint8_t) ((rawregvalue >> 20) + (rawregvalue >> 16)) & 0xff;
}
// For a bit more efficiency I'm using the XOR function to change the output bits.
// This avoids the need to explicitly protect other register bits.
// Since the bootup logic states are all LOWs this should work even if digitalWrite8Bits() is called before pinMode8Bits() is called.
volatile uint32_t _last = 0;
void digitalWrite8Bits(uint8_t value)
{
uint32_t writebits,newbits;
newbits = (( (uint32_t) value & 0xf0) << 20 ) + (((uint32_t) value & 0x0f)<<16); // we want to update _last with this value later
writebits = newbits ^ _last;
GPIO6_DR ^= writebits;
_last = newbits;
}
void pinMode8Bits(int mode)
{
int i;
for(i = 0; i < 8; i++)
pinMode(EightPins[i], mode);
}
void setup() {
pinMode8Bits(OUTPUT);
digitalWrite8Bits(LOW);
} // end setup()
// We test this with a simple "walking one" routine
void loop() {
int i,j = 1;
for(i = 0; i < 8; i++)
{
digitalWrite8Bits(j);
j <<= 1;
delay(500);
}
}
I'm a get'er done kind of c/c++ programmer so any suggestions on how to improve the code are welcome.
Last edited: