GPIO read issue...

Status
Not open for further replies.

Frank B

Senior Member
Hi, i have a question... i can't find the reason for the following behavior.

The pins are too slow when used as input (?)


Please try the sketch below (connect pin 2 to pin 5 first!)
It toggles pin 2, then reads the port.
The first reading is zero. (remarkable: at any F_CPU)

Is that correct ? It looks like a filter somewhere...
For 120MHz, it works, when i insert the two NOPs.

Reason for my question: I'm accessing a flash chip in parallel mode and need a lot of nops between setting clk and reading the port to get valid results. The chip is fast enough, it is the Winbond W25Q128FV, so this should'nt be the problem....

But: For the W25Q128FV, two NOPs are not enough. i need 4... Why ?
It does respond to every sck without nops in between, so think that is a sign that the chip is fast enough :) i have to inbsert the NOPs for reading the port only...

The first time i struggled about this was a year ago, when in tried the same with a SD-Card. Same issue.

Code:
void setup() {
 pinMode(13, OUTPUT); //LED
 pinMode(2, OUTPUT);
 pinMode(5, INPUT);
 CORE_PIN2_CONFIG = 0x140; // DSE enable, Fast Slew Rate
 CORE_PIN5_CONFIG = 0x100; // passive filter off
}

void loop() {
  // put your main code here, to run repeatedly:
 uint8_t b[8];
 
 GPIOD_PCOR = 1; // pin 2 = low
 memset(b, 0, sizeof(b)) ;
 delay(1);
 GPIOD_PTOR = 1; //toggle pin 2
 /*
 asm volatile("nop");
 asm volatile("nop");
 */
 b[0] = GPIOD_PDIR; // this reads what ?
 b[1] = GPIOD_PDIR;
 b[2] = GPIOD_PDIR;
 b[3] = GPIOD_PDIR;
 b[4] = GPIOD_PDIR;
 b[5] = GPIOD_PDIR; 
 b[6] = GPIOD_PDIR;
 b[7] = GPIOD_PDIR;
 
 for (int i = 0; i < sizeof(b); i++) 
  Serial.printf("%d ", b[i]);
 Serial.println(); 
 
 GPIOC_PTOR = (1<<5); ///blink...
 delay(200);
 
}

Output:
edit: see below.

Edit:
Even more interesting:
Connect the wire to GND instead.

sry, the output above is from an old version with gnd connection :)
 
Last edited:
That same '0' reproduces on a T_3.2 here. And when I do this - slowing the PIN2 change I get a ZERO in both first entries.

<EDIT>: Your code as written works properly on a TEENSY_LC!

Code:
 //CORE_PIN2_CONFIG = 0x140; // DSE enable, Fast Slew Rate

If I put in three NOP's [ yield() or delayMicroseconds(1) ] then it works right.

It also works right inserting this before the first read' b[0] = b[1]; ' - it just takes time?

I put this line " if ( !(millis()%10)) Serial.print("_"); " before your println so I can see it is scrolling.
 
Last edited:
UPDATE: Actually I was seeing lines more like this: "0 129 129 129 129 129 129 129"

I pulled PIN2>5 wire and the results were similar? On both LC and T_3.2?

after pulling the wire: Actually I was seeing lines more like this: "128 129 129 129 129 129 129 129 "

Gotta run now - but something isn't what it might seem?
 
Last edited:
according to the manual, GPIO is clocked by the system clock...is this F_CPU?
There must be some delay or digital filter... ??
 
Last edited:
Updated the Sketch - looping test - code to follow - same compiled for T_LC and T_3.2:

I may be confused on what GPIOD_PDIR is?

The T_LC does it with what I expected based on your post - except with PIN5 wire to GND the result is still alternate 1's - somehow the 'input' of pin5 is not being read?:
1 0 1 0 1 0 1 0
As Stored /\ :: Read&Print \/
1 0 1 0 1 0 1 0

On the T_3.2 I get this with the wire ( again removing the wire results alternate 1's also when to GND):
1 0 1 1 1 1 1 1
As Stored /\ :: Read&Print \/
1 0 1 0 1 0 1 0

When pin5 tied to 3V3 then it shows all ones.

Code:
void setup() {
  pinMode(13, OUTPUT); //LED
  pinMode(2, OUTPUT);
  pinMode(5, INPUT);
  CORE_PIN2_CONFIG = 0x140; // DSE enable, Fast Slew Rate
  CORE_PIN5_CONFIG = 0x100; // passive filter off
}

void loop() {
  // put your main code here, to run repeatedly:
  uint8_t b[8];

  GPIOD_PCOR = 1; // pin 2 = low
  memset(b, 0, sizeof(b)) ;
  delay(1);
  GPIOD_PTOR = 1; //toggle pin 2
  for ( int ii = 0; ii < sizeof(b); ii++ ) {
    b[ii] = GPIOD_PDIR; // this reads what ?
    GPIOD_PTOR = 1; //toggle pin 2
  }
  for (int i = 0; i < sizeof(b); i++)
    if ( b[i])   Serial.print(" 1 "); else   Serial.print("0 "); // this reads what ?
  Serial.print("\n As Stored /\\ :: Read&Print \\/");
  if ( !(millis() % 10))  Serial.print("_");
  Serial.println();
  for ( int ii = 0; ii < sizeof(b); ii++ ) {
    if ( GPIOD_PDIR)   Serial.print(" 1 "); else   Serial.print("0 "); // this reads what ?
    GPIOD_PTOR = 1; //toggle pin 2
  }
  Serial.println("\n");

  GPIOC_PTOR = (1 << 5); ///blink...
  delay(200);
}
 
PDIR means "Port Data Input Register" - it returns all bits of Port D in this case.
I don't know where Pin2/Pin5 is connected on the LC, i did'nt look at the schematic

Hm, if the delay of a few cycles is fixed, there may be a workaround with interleaving setting clk and reading.. or with spending the dead-time with something useful (but would mean using assembler)
 
Interleaving works... this code prints "0" when the pin is 1 and vice versa..

funny :) the teensy has a built-in time-machine...

Code:
void setup() {
 pinMode(13, OUTPUT); //LED
 pinMode(2, OUTPUT);
 CORE_PIN2_CONFIG = 0x140; // DSE enable, Fast Slew Rate

}

void loop() {
  // put your main code here, to run repeatedly:
 uint8_t b[8];
 
 GPIOD_PCOR = 1; // pin 2 = low
 memset(b, 0, sizeof(b)) ;
 delay(1);
 GPIOD_PSOR = 1; // pin 2 = high
 b[0] = GPIOD_PDIR; // this reads what ?
 GPIOD_PCOR = 1; // pin 2 = low
 b[1] = GPIOD_PDIR;
  GPIOD_PSOR = 1; // pin 2 = high
 b[2] = GPIOD_PDIR;
 GPIOD_PCOR = 1; // pin 2 = low
 b[3] = GPIOD_PDIR;
  GPIOD_PSOR = 1; // pin 2 = high
 b[4] = GPIOD_PDIR;
 GPIOD_PCOR = 1; // pin 2 = low
 b[5] = GPIOD_PDIR; 
  GPIOD_PSOR = 1; // pin 2 = high
 b[6] = GPIOD_PDIR;
 GPIOD_PCOR = 1; // pin 2 = low
 b[7] = GPIOD_PDIR;
 
 for (int i = 0; i < sizeof(b); i++) 
  Serial.printf("%d ", b[i] & 1);
  
 Serial.println(); 
 
 GPIOC_PTOR = (1<<5); ///blink...
 delay(200);
 
}
 
As written - the T_3.2 still shows the bad behavior you indicated? Adding two NOP's is enough.

T_LC port may be assigned similar enough as it works to show the connection - except it works to alternate properly, unlike the T_3.1

Frank - try it with the wire to GND and then 3.3V - oddly alternate 1's never go away?
 
Hi Tim, yes, that last code displays the reading of pin 2 only :) .... but it is the state from some cycles earlier. A time-machine...

Good night :)
 
Sorry that I am late to the party. But wondering about the init code:
Code:
void setup() {
 pinMode(13, OUTPUT); //LED
 pinMode(2, OUTPUT);
 CORE_PIN2_CONFIG = 0x140; // DSE enable, Fast Slew Rate

}
If I am reading this correct: CORE_PIN2_CONFIG=0x140
Is the same as saying: CORE_PIN2_CONFIG = PORT_PCR_MUX(1) | PORT_PCR_DSE;
Not sure about the Fast Slew rate, my guess is it is PORT_PCR_SRE
Code:
#define PORT_PCR_MUX(n)			((uint32_t)(((n) & 7) << 8))	// Pin Mux Control
#define PORT_PCR_MUX_MASK		((uint32_t)0x00000700)
#define PORT_PCR_DSE			((uint32_t)0x00000040)		// Drive Strength Enable
#define PORT_PCR_ODE			((uint32_t)0x00000020)		// Open Drain Enable
#define PORT_PCR_PFE			((uint32_t)0x00000010)		// Passive Filter Enable
#define PORT_PCR_SRE			((uint32_t)0x00000004)		// Slew Rate Enable
#define PORT_PCR_PE			((uint32_t)0x00000002)		// Pull Enable
#define PORT_PCR_PS			((uint32_t)0x00000001)		// Pull Select
If correct you might need the config to be 0x144...

Again sorry if I am completely off, but thought I would mention it, in case this was your issue...

Good Luck
 
Hi Kurt, thank you for looking !

Fast slew rate is 0, so i think 0x140 is ok..

Manual:
Code:
0 Fast slew rate is configured on the corresponding pin, if the pin is configured as a digital output.
1 Slow slew rate is configured on the corresponding pin, if the pin is configured as a digital output.
 
according to the manual, GPIO is clocked by the system clock...is this F_CPU?
There must be some delay or digital filter... ??

I know this is an older post, but I may be able to help a bit. I have been doing what is basically high-speed bit-banging a parallel flash chip, with many of the same issues -- delayed input response necessitating NOPS that shouldn't theoretically be necessary between toggling RE# and reading valid data. You're right that it does look like a delay or filter, but I verified that the digital filters were not enabled, slew rate was set for high speed, etc. Now, I am not a deeply knowledgeable processor guy (which is one of the reasons I like the Teensy's blend of simplicity and power), but I did finally come across some references on NXPs site that made me realize latency on the bus itself is contributing. While the GPIO might toggle full blast, it still takes a couple F_BUS cycles for that data to be visible on the GPIOx_PDIR register.

Importantly, I was able to reduce the number of NOPs necessary to a bare minimum by overclocking the F_BUS as much as possible. So, while I don't know exactly what happens between "data on pins" and "GPIOx_PDIR shows new data," bringing the F_BUS up to a 2:1 ratio with F_CPU made a big improvement. In this case, that means a 240 MHZ F_CPU and 120 MHZ F_BUS on a T3.6. I was able to eliminate two NOPs in my loop by doing that, compared to the 80 MHz stock F_BUS setting (this is in Kinetis.h).

Just tried another test: With F_CPU at 120 MHz and F_BUS at 60 MHz the following snippet works:

GPIOC_PDOR = counter;
asm volatile ("nop;nop;");
read_data = GPIOD_PDIR;

I can then verify that read_data always matches counter. Keeping the CPU at 120 MHz but bringing the BUS up to 1:1 (120 MHz) I can eliminate the NOPs completely. This is with ~6 inch point-to-point jumpers on the Teensy pins.
 
Last edited:
according to the manual, GPIO is clocked by the system clock...is this F_CPU?
There must be some delay or digital filter... ??

This is an older question - but I've not seen a clear answer? Came up again in this post.

Based on post #13 this thread - it seems that the in memory data register changes at CPU speed - but until the F_BUS clocks through no external change is made. Is this correct?
 
https://forum.pjrc.com/threads/41874-STRANGE-GPIO-speed-perfomance-with-digitalWriteFast()-(ring-oscillator-example)?p=132365&viewfull=1#post132365 the answer - a T_3.6 at 240 MHz can do 120 MHz bit change - when F_BUS is set to 120? And 90 MHz change at 180 MHz.

And this post?:
It is possible to overclock F_BUS (don't tell anybody, but I've run up to 240 MHz on a 3.6 for SPI :cool:), but the GPIO Ports are not tied to F_BUS, they are just a function of F_CPU (2 clocks per read/write except in special cases). So it wouldn't make a difference in this case.
 
Last edited:
Last edited:
Or, perhaps, the signals' rising edge wasn't fast enough... i' not sure about the real reason.

Is someone able to check this ? (for > 40MHz)
 
Status
Not open for further replies.
Back
Top