Setting PORTF on Teensy 2.0 is asynchronous?

Status
Not open for further replies.

wolfv

Well-known member
In the example code, PORTF is set inside a for loop.
The output makes me think that setting PORTF is asynchronous.
Adding a "delay(1)" after PORTF resolves the asynchronous behavior.
I am using Teensy 2.0. Example code, output, and picture of the setup are below.

What is the best way to guarantee that PORTF is set before using PORTF?
Where can I read more about this?

Code:
#include <inttypes.h>
#include <Keyboard.h>

void setup()
{
	// port B has 8 pins
	PORTB = B11111111;	// set all port B pins to pullup

	// port F has 6 pins
	DDRF = B11110011;	// set all port F pins to outputs
}

void loop()
{
	uint16_t keyCode[4] = { KEY_0, KEY_1, KEY_2, KEY_3 };
	const uint8_t FPins[4] = {B00000001, B00000010, B00010000, B00100000};
	uint8_t sample;

	delay(20);

	for (uint8_t pin = 0; pin < 4; pin++)
	{
		PORTF = ~FPins[pin];	// set active pin to low
		//delay(1);		// delay resolves asynchronous behavior
		sample = ~PINB;		// read all pins on port B

		if (sample != 0)	// if key is pressed
		{
			Keyboard.press(keyCode[pin]); // print pin number
			Keyboard.release(keyCode[pin]);			
		}
	}
}
Code:
Pin number button pressed.	Output with delay(1).	Output w/o delay(1).
	0				0			1
	1				1			2
	2				2			3
	3				3			0 // wrap
port_manipulation.JPG

Thank you.
 
Last edited:
more explaining and minor changes for clarity

This post is basically the same as the above post but with more explaining and minor changes for clarity.

The buttons are connected to pins F0, F1, F4, F5 on one side and pin B0 on the other (see picture above)

This is how the code works inside the for loop:
PINB reads from active low pin F0, F1, F4, or F5
if pin B0 is low (from the button conducting pin B0 to the low F pin),
then print the corresponding F pin number 0, 1, 4, or 5​

The code works in an unexpected way; PINB is reading the F pin of the previous "for" loop.
The result is that the printed pin numbers are rotated by one:
F pins 0, 1, 4, 5 print 1, 4, 5, 0 respectively.​

Insert "delay(1)" after "PORTF" and run the program again.
This time PORTF has enough time to update the active low pin, PINB reads the F pin of the current "for" loop, and the pin numbers print as intended:
F pins 0, 1, 4, 5 print 0, 1, 4, 5 respectively.​

Code:
#include <inttypes.h>
#include <Keyboard.h>

void setup()
{
	Serial.println("setup");

	// port B has 8 pins
	PORTB = B11111111;	// set all port B pins to pullup

	// port F has 6 pins
	DDRF = B11110011;	// set all port F pins to outputs
}

uint8_t sample, fPin;

void loop()
{
	const uint16_t keyCode[4] = { KEY_0, KEY_1, KEY_4, KEY_5 };
	const uint8_t FPins[4] = {B00000001, B00000010, B00010000, B00100000};

	delay(20);

	for (fPin = 0; fPin < 4; fPin++)
	{
		PORTF = ~FPins[fPin];	// set active pin to low (F0, F1, F4, or F5)
		//delay(1);		// delay resolves asynchronous behavior
		sample = ~PINB;		// read all pins on port B (only B0 is wired to switches)

		if (sample & B00000001 != 0)	// if a button is pressed
		{
			Keyboard.press(keyCode[fPin]); // print F pin number 0, 1, 4, or 5
			Keyboard.release(keyCode[fPin]);			
		}
	}
}
This is the output when I press buttons 0-1-4-5 (notice how the numbers are rotated):
11111114444445555500000

I am using Teensy 2.0 and the Arduino IDE.

The Atmega32U4 data sheet http://www.atmel.com/Images/doc7766.pdf says it uses Asynchronous and Synchronous Clocking Modes.
Chapter 10 "I/O ports", section 10.2.5 "Digital Input Enable and Sleep Modes" mentions "asynchronous". But this stuff is over my head.

What is the best way to guarantee that PORTF is set before using PORTF?

I am stuck and I really appreciate you looking into this.
Thank you.

Update:
For now I am using "delayMicroseconds(1)" and it seems to be working, although I do not know how reliable it is.
 
Last edited:
The read has up to a 1.5 clock cycle delay to synchronize the inputs. Since there can be one instruction per clock, it could get in ahead.

Also, this could be a compiler 'feature'. Without the delay the compiler has no way of knowing there is any relation between PORTF and PORTB. So if it can optimize something, switching the order of PortF and PortB access is fair game.

Putting the delay in tells the compiler there is a relationship and it has to do one access before the other. And given there are 16 instructions/Microsecond you are also safe with the first issue.


TLB
 
Thanks for your help TLB. That makes me feel better. Just one more question.

Is there a way to tell the compiler of the PORTF-PORTB relationship in a way that does not consume resources?
 
Thanks for your help TLB. That makes me feel better. Just one more question.

Is there a way to tell the compiler of the PORTF-PORTB relationship in a way that does not consume resources?
You can use an asm statement like:

Code:
		PORTF = ~FPins[fPin];	// set active pin to low (F0, F1, F4, or F5)
		__asm__ volatile ("@ make sure PORTF is set before using PINB" : "+g" (PINB) : "g" (PORTF));
		sample = ~PINB;		// read all pins on port B (only B0 is wired to switches)

This is perhaps overkill. Here is the asm broken down:

__asm__ declare a special GCC asm statement. You could use asm instead, but I tend to use __asm__ since enabling standards only compliance turns off the asm without the two leading and trailing underscores.

volatile tells GCC that this asm cannot be moved, and that you can't move anything modified after the asm to before it. This tends to be a big hammer, and should be used sparingly.

"@ make sure PORTF is set before using PINB" is the string that will be fed to the assembler with modifications. Normally you would use % followed by some deep magic to indicate how to transform a particular argument into assembler (i.e. print it as a number, print a register name, etc.) What follows the % is dependent on the particular target machine. Note the @ is a comment character for the avr and arm assemblers, and the assembler skips everything to the end of the line or ;. This string must not be empty, or the compiler may delete the whole asm.

: "+g" (PINB) The first colon says what follows describes the output arguments. The "+" in the string says that the output is both read and written to (if it is just written to it would be "="). The "g" in the string is called a constraint and it says the argument is a general input or ouptut. Most of the constraints are dependent on the particular back end, but "g" is a machine independent one. The parenthesis around PINB is part of the required syntax.

: "g" (PORTF) The second colon says what follows describes the input arguments. The "g" is a general constraint as before.

Now, a simpler way is to use the following asm, which uses a third colon to indicate registers/memory that are clobbered without being a formal input or output. This uses the special memory clobber that says all stores before the asm must be done before the asm, and no loads can be moved before it, and it assumes PORTF and PINB are both implemented as memory operations:

Code:
		__asm__ volatile ("@ memory barrier" : : : "memory");
 
Last edited:
When happens if you replace the delay(1) with asm("nop"); ?

I suspect this may be one of the quirks of AVR, where outputs change at the end of an instruction but inputs are sampled at the very beginning, so you might need a minimum of 1 instruction between the write to the port and a read that samples it?

Of course, if you're depending on pullup resistors to change the voltage, it can take time to go from low to high.
 
Hi Paul,
Thank you for your suggestion.
I replaced delay(1); with asm("nop"); and the example code worked fine (no asynchronous behavior).

So I did the same replacement on the keyboard software I am developing, which has the same asynchronous PORTF.
The output was surprising: for each key stroke, it prints the correct key followed by the asynchronous key.
Apparently asm("nop") is unreliable in this case; first PINB reads the F pin of the current "for" loop and then PINB reads the F pin of the previous "for" loop.

Hi Michael,
Thank you for your suggestion.
I tried __asm__ but it didn't compile on Arduino IDE:
Code:
__asm__ volatile ("@ memory barrier" : : : "memory");
C:\Users\wolf\AppData\Local\Temp/ccpAG7N1.s: Assembler messages:
C:\Users\wolf\AppData\Local\Temp/ccpAG7N1.s:47: Error: junk at end of line, first unrecognized character is `@'
 
Last edited:
Use " " (i.e. quote, space, quote) instead of the "@ memory barrier" string. That way it is a non-empty string. Or use the "nop" as the string, since it sounds like you may need a nop anyway.
 
Use " " (i.e. quote, space, quote) instead of the "@ memory barrier" string. That way it is a non-empty string. Or use the "nop" as the string, since it sounds like you may need a nop anyway.

Thanks Michael, those compiled but didn't fix the asynchronous behavior.

Without "nop"
Code:
__asm__ volatile (" " : : : "memory");
Asynchronous keys were printed:
Pressing F pin buttons 0, 1, 4, 5 printed 1, 4, 5, 0 respectively.


With "nop"
Code:
__asm__ volatile ("nop" : : : "memory");
The result was the same as asm("nop");
Each key stroke printed the intended key followed by the asynchronous key:
Pressing F pin buttons 0, 1, 4, 5 printed 01, 14, 45, 50 respectively.
 
Last edited:
Status
Not open for further replies.
Back
Top