Quadrature Encoder on StartKit

All technical discussions and projects around startKIT
briansaccount
Junior Member
Posts: 6
Joined: Thu Apr 23, 2015 5:26 am

Quadrature Encoder on StartKit

Postby briansaccount » Fri Sep 04, 2015 9:30 pm

Hey XCore Community,

I'm looking for some input on my code and some helpful hints. I've been attempting to incorporate a quadrature encoder with my StartKit and ran into some problems.

After sorting out my hardware issues (needs some pull-up resistors) I found out that it looks like my code is missing transitions. I should be getting ~700 counts / revolution but I end up with ~20-30 (it varies) and more if I rotate slowly. This is leading me to believe my code is inefficient or my approach is bad.
Code is below, but to summarize:

1. Use a 4 bit port (with inputs A and B on bits 0 and 1 respectively)
2. Dwell on this port and wait for changes (and change to A or B should trigger a recalculation)
3. Process the new data and share it to a process on another core to give it to the user (my attempt at speeding up the collection)

Any thoughts on this approach?

Thanks!

Code: Select all

#include <xs1.h>
#include <timer.h>
#include <stdio.h>


/*
 * This code will measure the counts of a quadrature encoder.
 */

// Define the bits that correspond to the signals. Since we are combining them on the 4 bit port this will make it easier to address them
#define quad1_a_mask 1
#define quad1_b_mask 2

// Quadrature Encoder Matrix; props to http://www.robotshop.com/media/files/PDF/tutorial-how-to-use-a-quadrature-encoder-rs011a.pdf for the idea
int quadEncoderMatrix [16] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};

/* Inter task-communication layer */
interface my_interface {
    void encoderCounts ( int x);
};

// Monitor Encoder Task
void monitorEncoder (client interface my_interface i);

//Print Encoder Task
void printEncoder ( server interface my_interface i);

/* This the port where the leds reside */
port quad = XS1_PORT_4E;





int main(void) {

    interface my_interface i1 ;
    par {
        monitorEncoder(i1);
        printEncoder(i1);
    }
    return 0;
}


void monitorEncoder (client interface my_interface i) {
    int quadTicks = 0;
    int oldQuadPulse, newQuadPulse;
    oldQuadPulse = 0;
    newQuadPulse = 0;


    while(1) {

        quad :> newQuadPulse;
        quadTicks += quadEncoderMatrix[oldQuadPulse * 4 + newQuadPulse];
        i.encoderCounts (quadTicks);
        oldQuadPulse = newQuadPulse;

    }
}

void printEncoder ( server interface my_interface i) {
    while (1) {
        // wait for either encoderCounts over connection 'i '.
        select {
            case i.encoderCounts ( int x ):
                printf("Encoder: %i \n", x);
            break ;
        }
    }
}

User avatar
Bianco
XCore Expert
Posts: 754
Joined: Thu Dec 10, 2009 6:56 pm

Postby Bianco » Fri Sep 04, 2015 11:51 pm

You don't want to print over JTAG in timing sensitive code because it halts the core.
Either buffer your print statements to print them at a more suitable time or try xscope printing.
briansaccount
Junior Member
Posts: 6
Joined: Thu Apr 23, 2015 5:26 am

Postby briansaccount » Tue Sep 08, 2015 5:11 pm

Thanks for the reply, Bianco! I'll try out your suggestions.

I'm curious if this would matter since the print statement is in a task separate from the task that is reading the encoder?
User avatar
Bianco
XCore Expert
Posts: 754
Joined: Thu Dec 10, 2009 6:56 pm

Postby Bianco » Tue Sep 08, 2015 8:39 pm

With core I meant tile in xmos-speak, so yes it would matter
briansaccount
Junior Member
Posts: 6
Joined: Thu Apr 23, 2015 5:26 am

Postby briansaccount » Tue Sep 08, 2015 9:55 pm

Roger that - thanks for the head's up! I tried to avoid this by dropping that task on another core, but apparently that's not good enough. I'll swap the code around and report my findings back - thank you!
briansaccount
Junior Member
Posts: 6
Joined: Thu Apr 23, 2015 5:26 am

Postby briansaccount » Tue Sep 08, 2015 10:51 pm

I changed the monitorEncoder loop to print every 1e6 loops (as a debug) and it looks like it's collecting the information I'd expect - thanks!

Is there a way to do non-blocking communication between two tasks on separate cores? It seems like channels and interfaces both were blocking.
User avatar
infiniteimprobability
XCore Legend
Posts: 1124
Joined: Thu May 27, 2010 10:08 am

Postby infiniteimprobability » Wed Sep 09, 2015 12:25 pm

Is there a way to do non-blocking communication between two tasks on separate cores?
Communication is intentionally blocking so that everything is nicely synchronised. However, there are cases where buffering/non blocking is very helpful, so that the client is not blocked on a busy server.

A couple of ways spring to mind:

1) Share memory. You can do whatever you want here but it does require a poll at the other end. SOme info on sharing memory here https://www.xcore.com/forum/viewtopic.php?f=26&t=3061

2) Use a streaming channel. These contain at least one word of buffering (it will be more across tiles) and do not force synchronisation (they boil down to simple in/outs and rely on the circuit always being open).

So:

Code: Select all

void send(streaming chanend c){
  c <: 1234;
  c <: 5678;
}

void receive(streaming chanend c){
  int rx;
  delay_ms(1);
  c :> rx;
  delay_ms(1);
  c :> rx;
}

int main(void){
  streaming chan c;
  par{
    send(c);
    receive(c);
  
  }
  return 0;
}


..1234 will be sent immediately and send() will block on sending 5678 until receive consumes 1234.

3) Use a combination of the two. Write to a buffer and then use the channel to flag a buffer that's ready to read.

Who is online

Users browsing this forum: No registered users and 3 guests