Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 9 of 9

Thread: Setting PORTF on Teensy 2.0 is asynchronous?

  1. #1
    Senior Member
    Join Date
    Jul 2013
    Posts
    123

    Question Setting PORTF on Teensy 2.0 is asynchronous?

    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
    Click image for larger version. 

Name:	port_manipulation.JPG 
Views:	146 
Size:	347.2 KB 
ID:	1224

    Thank you.
    Last edited by wolfv; 12-16-2013 at 02:59 PM.

  2. #2
    Senior Member
    Join Date
    Jul 2013
    Posts
    123

    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 by wolfv; 12-17-2013 at 02:33 AM. Reason: added Update

  3. #3
    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

  4. #4
    Senior Member
    Join Date
    Jul 2013
    Posts
    123
    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?

  5. #5
    Senior Member+ MichaelMeissner's Avatar
    Join Date
    Nov 2012
    Location
    Ayer Massachussetts
    Posts
    3,250
    Quote Originally Posted by wolfv View Post
    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 by MichaelMeissner; 12-17-2013 at 11:45 AM.

  6. #6
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,552
    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.

  7. #7
    Senior Member
    Join Date
    Jul 2013
    Posts
    123
    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 by wolfv; 12-17-2013 at 08:53 PM.

  8. #8
    Senior Member+ MichaelMeissner's Avatar
    Join Date
    Nov 2012
    Location
    Ayer Massachussetts
    Posts
    3,250
    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.

  9. #9
    Senior Member
    Join Date
    Jul 2013
    Posts
    123
    Quote Originally Posted by MichaelMeissner View Post
    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 by wolfv; 12-19-2013 at 06:08 AM.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •