peculiar port behaviour

Technical questions regarding the XTC tools and programming with XMOS.
babazaroni
Experienced Member
Posts: 94
Joined: Sun Feb 10, 2013 4:47 am

peculiar port behaviour

Post by babazaroni »

I have two startkits connected together via a single 1 bit port and running identical code. The port is initially set as an input port and is pulled up to Vdd. Pressing the button of a startkit sets the port low and triggers the select case statement in the other startkit, but not it's own case statement.

So why don't both startkit's select statements get triggered?

Here's some sample code:

Code: Select all

port debug_port = XS1_PORT_1G;  

debug_port :> void;

while(1)
 {
    select
    {
         case debug_port when pinseq(0) :> void:
           for(;;);  // only the start kit whose button was not pressed gets here
             break;


          case i_button.changed():
                debug_port <: 0;

            break;
	}
}


User avatar
mon2
XCore Legend
Posts: 1913
Joined: Thu Jun 10, 2010 11:43 am

Post by mon2 »

Hi babazaroni. Have not reviewed your logic flow but an important comment is that you are working with mechanical push button switches which are prone to bouncing switch contacts. For this reason, care must be used to offer software to debounce the switch - often logic chips will perform this task in hardware through timed delays but XMOS is able to handle the same task in software.

Take for example the application note AN00184 for the xCore-200 Explorer kit (working with this example this morning) and has the following code snippet for dealing with the pushbuttons:

/* Check for a change in button 1 - Detects 1->0 transition */
if(is_button_pressed(0)) {
if(button_1_valid) {
button_1_valid = 0;
length = sprintf(tmp_string, "\r\nButton 1 Pressed!\r\n");
cdc.write(tmp_string, length);
}
} else {
button_1_valid = 1;
}


/* Check for a change in button 2 - Detects 1->0 transition */
if(is_button_pressed(1)) {
if(button_2_valid) {
button_2_valid = 0;
length = sprintf(tmp_string, "\r\nButton 2 Pressed!\r\n");
cdc.write(tmp_string, length);
}
} else {
button_2_valid = 1;
}

where is_button_pressed() is defined as:

/* Checks if a button is pressed */
int is_button_pressed(int button_id)
{
if(get_button_state(button_id) == BUTTON_PRESSED) {
/* Wait for debounce and check again */
delay_ticks(DEBOUNCE_TIME);
if(get_button_state(button_id) == BUTTON_PRESSED) {
return 1; /* Yes button is pressed */
}
}
/* No button press */
return 0;
}

and

#define DEBOUNCE_TIME (XS1_TIMER_HZ/50)

Here is a complete example on how to deal with mechanical pushbuttons with debounce code support:

https://www.xmos.com/published/an10006- ... ion=latest

You will need to extend your code to offer similar delays and then sample again to confirm that the pushbutton is indeed in the correct position before making a selection.
babazaroni
Experienced Member
Posts: 94
Joined: Sun Feb 10, 2013 4:47 am

Post by babazaroni »

Thanks for the reply, but I use the standard debouncing available from the startkit button interface.

The example shown is a cut down version of my code to demonstrate the basics of the problem.

So now why isn't a port change detected in the local startkit?
User avatar
myndideal
Active Member
Posts: 59
Joined: Thu May 05, 2011 10:00 pm
Location: Budapest

Post by myndideal »

Hi,
We are looking for the problem of triggering a select case of bidirectional ports, approx. in the same time when the port is driven as output.

Have you seen this?:
https://www.xmos.com/published/xs1-ports-specification
on page 21 (3.5.3 Bidirectional I/O) there is a notice:
"It is illegal to perform a select on the port immediately after an output."

and this:
https://www.xmos.com/download/private/I ... 38B%29.pdf

Beside the point: There is a peek() function, which still able to read the exact pins immediate value of a driven bidirectional port, but select/case operates in different way. (doesn't peeks but configure up the port block to trigger a resource, and threads just wait for the resource to fire the predicate condition) Similar than an interrupt (in other platforms), but in multithreaded implementation. (this is just an explanation)
"A PEEK instruction is available to inspect the value of the port pins bypassing all mechanisms described before. This can be used to, for example, check whether a pull-up port is high or low."


For the case, the pin is not driven by the same port: an example is the i2c bus. (driven by external logic level) The i2c data line is bidirectional. There you can find something like this: debug_port :> void; to switch back to input operational mode.

Btw, I suggest to use a separated function for your specific task, and call this function from the both cases. (In other words: Separate the state machine and the task to be done.)
Last edited by myndideal on Sun Aug 16, 2015 11:06 pm, edited 1 time in total.
User avatar
mon2
XCore Legend
Posts: 1913
Joined: Thu Jun 10, 2010 11:43 am

Post by mon2 »

https://www.xmos.com/published/how-use- ... -resources

Case statements are not permitted to contain output operations.
babazaroni
Experienced Member
Posts: 94
Joined: Sun Feb 10, 2013 4:47 am

Post by babazaroni »

Thanks myndideal. I wonder what the definition of immediatelyin "It is illegal to perform a select on the port immediately after an output." is. In my actual code, there are quite a few instructions after the port is set low before the select statement reenters.

Can you give a little more detail on what you mean by separated function? Is it a function in another core?
User avatar
myndideal
Active Member
Posts: 59
Joined: Thu May 05, 2011 10:00 pm
Location: Budapest

Post by myndideal »

1) I don't know exactly, but... if delta time is not zero and port direction must change back to input, it means for me: It is impossible to trigger for a self-driven bidirectional port. If you have some capacitive cirquit externally connected to the pin, which able to keep driving the same pin on the active level, while it goes back to tri-state mode... :). Eh, I dont know...

2) I mean... separated function, which one will be called from two cases (in one thread):

Code: Select all

#include <xs1.h>
#include <print.h>
port debug_port = XS1_PORT_1G;
void doIt(int from){
    printstr("doit"); //for(;;);  // only the start kit whose button was not pressed gets here??
    printhexln(from);
}
interface myTestButtonIf {
  void changed();
};
void mainLoop(interface myTestButtonIf server i_button){
    debug_port <: 0; //a default
    set_port_pull_down(debug_port); //MY hw not support pull up on 1G port, only pull down!
    //So i guess, an external pull ups may required for default high...
    while(1)
        {
           debug_port :> void; //not to be driven from this point
           select
           {
                case debug_port when pinseq(1) :> void: //in my example, the active state is inverse.
                    doIt(1);
                    break;
                 case i_button.changed():
                   debug_port <: 1; //drive it for a while
                   doIt(2);
                   break;
          }
       }
}
void testStimulator(interface myTestButtonIf client i_button){
    int time;  timer t; t:>time;
#define PERIOD (40000)
    //second Device may have different timings, or can be disabled by comment out
    //the interface fn call...
    while(1){
        t when timerafter(time+PERIOD) :> void;
        i_button.changed();
    }
}
int main(){
    interface myTestButtonIf i_button;
    par{
        mainLoop(i_button);
        testStimulator(i_button);
    }
    return 0;
}