/*
Written April 2018 by Richard Martin.
Inspired by https://forum.pjrc.com/threads/17532-Tutorial-on-digital-I-O-ATMega-PIN-PORT-DDR-D-B-registers-vs-ARM-GPIO_PDIR-_PDOR?highlight=slew+rate+limiting:
This program was written for the Teensy 3.6 for the purpose of evaluating non-atomic behavior in port-at-a-time operations.
What you will need:
A Teensy 3.6 with both rows of 24 pins soldered in
A handful of 3.3v-tolerant LEDs (and appropriate resistors, as necessary)
A breadboard for all the parts
A logic analyzer (Saleae, etc.)
*/
// Choose a port:
// 1 = A, 2 = B, 3 = C, 4 = D, 5 = E...
#define PORT 5
// Choose a test:
// 1 = every other, 2 = count with mask, 3 = count without mask...
#define TEST 2
// Choose a delay...
#define DELAY_MS 100
// Connect your logic analyzer, upload the code, and test away...
#if PORT==1
#define PORT_PDOR GPIOA_PDOR
#define pinCount 10
// Not all the pins are included here! The first 4 bits of this port (0,1,2,3) are for the bootloader chip.
uint8_t portPins[pinCount] = {25, 3, 4, 26, 27, 28, 39, 42, 40, 41}; // Port A bits 5, 12, 13, 14, 15, 16, 17, 26, 28, 29
uint32_t portMask = 0xFFFFCFDF; // only the first 3 pins, not all 10 (0xCBFC0FDF)!
#elif PORT==2
#define PORT_PDOR GPIOB_PDOR
#define pinCount 16
uint8_t portPins[pinCount] = {16, 17, 19, 18, 49, 50, 31, 32, 0, 1, 29, 30, 43, 46, 44, 45}; // Port B bits 0, 1, 2, 3, 4, 5, 10, 11, 16, 17, 18, 19, 20, 21, 22, 23
uint32_t portMask = 0xFFFFFFF8; // only the first 3 pins, not all 16 (0xFF00F3C0)!
#elif PORT==3
#define PORT_PDOR GPIOC_PDOR
#define pinCount 12
uint8_t portPins[pinCount] = {15, 22, 23, 9, 10, 13, 11, 12, 35, 36, 37, 38}; // Port C bits 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
uint32_t portMask = 0xFFFFFFF8; // only the first 3 pins, not all 12 (0XFFFFF000)!
#elif PORT==4
#define PORT_PDOR GPIOD_PDOR
#define pinCount 15
// Not all the pins are being brought out here! The first 6 bits of this port are for the
uint8_t portPins[pinCount] = {2, 14, 7, 8, 6, 20, 21, 5, 47, 48, 55, 53, 52, 51, 54}; // Port D bits 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15
uint32_t portMask = 0xFFFFFFF8; // only the first 3 pins, not all 15 (0xFFF0400)!
#else
#define PORT_PDOR GPIOE_PDOR
#define pinCount 5
// Not all the pins are included here! The first 6 bits of this port (0,1,2,3,4,5) are for the SD Card Reader.
uint8_t portPins[pinCount] = {56, 57, 33, 34, 24}; // Port E bits 10, 11, 24, 25, 26
uint32_t portMask = 0xF8FFFFFF; // only the last 3 pins, not all 5 (0xF8FFF3FF)!
#endif
uint8_t count = 0;
uint32_t currentState;
void setup() {
Serial.begin(250000);
while(!Serial) {/*wait*/}
Serial.println("Initializing pins...");
// Turn all the brought-out pins in Port X on...
for (int a=0; a<pinCount; a++) {
pinMode(portPins[a], OUTPUT);
digitalWrite(portPins[a], HIGH);
delay(500);
}
// Save and print the current state of the port...
currentState = PORT_PDOR;
Serial.print("Current pin state is: "); Serial.println(currentState, HEX);
}
void loop() {
#if TEST==1
// Set every other pin, then every *other* pin, HIGH...
PORT_PDOR = 0xAAAAAAAA;
delay(DELAY_MS);
PORT_PDOR = ~PORT_PDOR;
delay(DELAY_MS);
// I observed that the shorter the delay, the more synchronously the pins "hit".
// The longer the delay, the less synchronous they hit. At 1mS, all the pins being measured "hit" at the same time.
// With longer delays, some pins "hit" 10-20nS later than others.
// Another observation: all the bits in Port B "hit" consistently synchronously, but Port E was wildly non-atomic, with
// some pin "hits" more than 150nS from others...!
#elif TEST==2
// Count from 0-7 (first 3 pins) preserving the current state of the register, as recorded at
// startup, except for the first 3 pins, which I'm displaying the count on...
#if PORT==1
PORT_PDOR = (PORT_PDOR & portMask) | ((((count & 0x4) >> 2) << 5) | (((count & 0x2) >> 1) << 12) | ((count & 0x1) << 13));
#elif PORT==5
PORT_PDOR = (PORT_PDOR & portMask) | ((((count & 0x4) >> 2) << 24) | (((count & 0x2) >> 1) << 25) | ((count & 0x1) << 26));
#else
PORT_PDOR = (PORT_PDOR & portMask) | count;
#endif
count++;
if (count > 7) count = 0;
delay(DELAY_MS);
// I observed that the pin "hits" were consistently off by 10-20nS. Only when all 3 pins go back to 0
// do they all appear to do this synchronously. In this case, longer delays between changes seem to help;
// at 250mS, only one pin was lagging 20nS behind the other two.
#else
// Count from 0-7 (first 3 pins) but don't preserve the state of the register.
#if PORT==1
PORT_PDOR = (((count & 0x4) >> 2) << 5) | (((count & 0x2) >> 1) << 12) | ((count & 0x1) << 13);
#elif PORT==5
PORT_PDOR = (((count & 0x4) >> 2) << 24) | (((count & 0x2) >> 1) << 25) | ((count & 0x1) << 26);
// PORT_PDOR = ((count & 0x4) << 22) | ((count & 0x2) << 24) | ((count & 0x1) << 26); // It can be written this way too, with the shifts compounded...
#else
PORT_PDOR = count;
#endif
count++;
if (count > 7) count = 0;
delay(DELAY_MS);
// I observed that the pin "hits" were consistently more synchronous, although some of them were still
// "off" by 10-20nS. Still, not using a mask seems to help the bits hit more synchronously.
#endif
}