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

Thread: Pixy Camera with Teensy 3.5 and SPI

  1. #1
    Member randomvibe's Avatar
    Join Date
    Mar 2016
    Location
    CA
    Posts
    88

    Pixy Camera with Teensy 3.5 and SPI

    The Pixy camera is an object tracking device that communicates object information via UART, I2C, SPI, etc. I'm opting for SPI for speed and signal robustness and I have had good success with the ILI9341 display and an IMU. The Pixy SPI library was written for the Arduino, and peeking into it, I don't see how it will work with Teensy. Has anyone successfully linked the Pixy to a 32-bit Teensy with SPI?

    9/17/2017 Update:
    * Went ahead and developed SPI functions for Teensy 3.5 & Teensy 3.6 here: RandomVibe Code

    9/13/2017 Update:
    * Pixy board requires 5V, mainly for camera and peripherals.
    * Pixy SPI pins are 3.3V, so no need for level shifting with Teensy 3.6.
    * Pixy I2C SDA pin is 5V.
    Last edited by randomvibe; 09-14-2017 at 01:45 AM.

  2. #2
    Junior Member
    Join Date
    May 2017
    Posts
    8
    Quote Originally Posted by randomvibe View Post
    The Pixy SPI library was written for the Arduino, and peeking into it, I don't see how it will work with Teensy. Has anyone successfully linked the Pixy to a 32-bit Teensy with SPI?
    More details please. Why?
    Is EVL yours?
    I have Pixy CMUcam5; only have used with Leonardo/Zumo as Pixy Pet by Adafruit.
    I don’t see any reason why not to connect Teensy 3.5/3.6 to SPI all are 3.3 volts.
    Last edited by tarzan; 08-31-2017 at 01:36 PM. Reason: Leonardo not Uno

  3. #3
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    5,562
    Quote Originally Posted by randomvibe View Post
    T...The Pixy SPI library was written for the Arduino, and peeking into it, I don't see how it will work with Teensy. Has anyone successfully linked the Pixy to a 32-bit Teensy with SPI?
    Which library? Do you have a link?

  4. #4
    Member randomvibe's Avatar
    Join Date
    Mar 2016
    Location
    CA
    Posts
    88
    All latest Pixy libraries by Charmed Labs are here: Wiki Releases

    The Windows 10 Pixy library (v2.0.9) I downloaded is here: pixymon_windows-2.0.9.exe

    The relevant header file for SPI with slave select is called "PixySPI_SS.h". Contents as follows:
    Code:
    //
    // begin license header
    //
    // This file is part of Pixy CMUcam5 or "Pixy" for short
    //
    // All Pixy source code is provided under the terms of the
    // GNU General Public License v2 (http://www.gnu.org/licenses/gpl-2.0.html).
    // Those wishing to use Pixy source code, software and/or
    // technologies under different licensing terms should contact us at
    // cmucam@cs.cmu.edu. Such licensing terms are available for
    // all portions of the Pixy codebase presented here.
    //
    // end license header
    //
    // This file is for defining the link class for SPI with Slave Select.  The 
    // default communication for Arduino is through the ICSP connector, which uses
    // SPI without a slave select.  The LinkSPI_SS allows you to use a slave select
    // so you can share the SPI port with other devices, or use multiple Pixys. 
    //
    // Note, the PixySPI_SS class takes an optional argument, which is the pin 
    // number of the slave select signal you wish to use.  The default pin is the 
    // SS pin (used when no argument is used.)  So, for example, if you wished to 
    // use pin 14 for slave select, declare like this:
    //
    // PixySPI_SS pixy(14);
    //
    
    #ifndef PIXYSPI_SS_H
    #define PIXYSPI_SS_H
    
    #include "TPixy.h"
    #include "SPI.h"
    
    
    #define PIXY_SYNC_BYTE              0x5a
    #define PIXY_SYNC_BYTE_DATA         0x5b
    #define PIXY_OUTBUF_SIZE            6
    
    class LinkSPI_SS
    {
      public:
        void init()
        {
          outLen = 0;
          SPI.begin();
    
          #ifdef __SAM3X8E__
          // DUE clock divider //
          SPI.setClockDivider(84);
          #else
          // Default clock divider //
          SPI.setClockDivider(SPI_CLOCK_DIV16);
          #endif
        }
        
        uint16_t getWord()
        {
          // ordering is different because Pixy is sending 16 bits through SPI 
          // instead of 2 bytes in a 16-bit word as with I2C
          uint16_t w;
          uint8_t c, cout = 0;
    	  
    	  // assert slave select
    	  digitalWrite(ssPin, LOW);
    
          if (outLen)
          {
            w = SPI.transfer(PIXY_SYNC_BYTE_DATA);
            cout = outBuf[outIndex++];
            if (outIndex==outLen)
              outLen = 0; 
          }
          else
            w = SPI.transfer(0);
    
          w <<= 8;
          c = SPI.transfer(cout);
          w |= c;
    
    	  // negate slave select
    	  digitalWrite(ssPin, HIGH);
          return w;
        }
    	
        uint8_t getByte() // this shouldn't be called normally
    	// It should only be called if we get out of sync, but with slave select
    	// we should stay in sync 
        {
    	  uint8_t c;
     	  // assert slave select
    	  digitalWrite(ssPin, LOW);
          c = SPI.transfer(0x00);
     	  // negate slave select
    	  digitalWrite(ssPin, HIGH);
    	  
    	  return c;
       }
        
        int8_t send(uint8_t *data, uint8_t len)
        {
          if (len>PIXY_OUTBUF_SIZE || outLen!=0)
            return -1;
          memcpy(outBuf, data, len);
          outLen = len;
          outIndex = 0;
          return len;
        }
    
        void setArg(uint16_t arg)
        {
          if (arg==PIXY_DEFAULT_ARGVAL)
            ssPin = SS; // default slave select pin
    	  else
    	    ssPin = arg;
        }
    
      private:
        uint8_t outBuf[PIXY_OUTBUF_SIZE];
        uint8_t outLen;
        uint8_t outIndex;
    	uint16_t ssPin;
    };
    
    
    typedef TPixy<LinkSPI_SS> PixySPI_SS;
    
    #endif

    I don't see how it can work for Teensy 3.5 or 3.6 without the Teensy SPI initialization commands shown below. There may be other missing components too. Just thought I'd check with the community first on a working SPI library for Pixy. IMHO, I think video object tracking is the next big thing in robotics. Pixy is a good way to learn about it through practice.

    Code:
    #define   MOSIX   11
    #define   SCKX    13
    #define   MISOX   12
    #define   CSX     15   // slave select
    
    pinMode(CSX, OUTPUT);
    digitalWrite(CSX,HIGH);
     
    SPI.setMOSI(MOSIX);
    SPI.setMISO(MISOX);
    SPI.setSCK(SCKX);
    SPI.begin();

  5. #5
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    5,562
    I took a quick look through this and most of this looks like standard SPI stuff.

    You do need to do the pinMode and probably digitalWrite calls, like you mentioned. If I looked correctly you can set the CS pin in the library by doing something like;
    Code:
    pixy.setArg(CSX);
    Assuming pixy is your instance of the class...

    As for setMOSI, setMISO, setSCK - Pins 11, 12, 13 are the default pins so you don't need to make these calls. As for needing a call to SPI.begin(), There is one in the init function.
    so again;
    Code:
    pixy.init();
    Will take care of that.

    If I were using this, I would set it up to use SPI transactions instead of the older SPI.setClockDivider(SPI_CLOCK_DIV16), which I think is then trying to use SPI at 1MHz, I would instead
    use SPI.beginTransaction/endTransaction and put the calls into the getWord call around where the digitalWrite calls are... Probably also in getByte...

  6. #6
    Member randomvibe's Avatar
    Join Date
    Mar 2016
    Location
    CA
    Posts
    88
    I went ahead a developed functions to read Pixy data with an SPI connection for Teensy 3.5 & 3.6. It's simple and robust code designed to use with other SPI devices with a single SPI connection. You select the CS port for the Pixy. The main function "pixy_parse_data" outputs an array of PIXY structures for multiple object detections per frame. It makes use of a checksum output provided by the Pixy to ensure the data stream is correct. I tested the functions and all works properly with Normal objects or Color-Coded objects. Full example program with my functions included below. It's best to set the Pixy object signatures with PixyMON. I hope this helps you.


    Code:
    /*
    PIXY SPI Reader for Teensy 3.5 and 3.6
    Written By: randomvibe
    Date: 9/7/2017
    
    Main Functions:
       pixy_spi_read_16bit  ~ Reads PIXY data via SPI connection using Teensy 3.5/3.6 SPI functions
       pixy_parse_data      ~ Parses SPI data stream and saves to array of PIXY struct
    
    */
    
    #include  <SPI.h> 
    
    
    
    // Teensy 3.5/3.6 SPI Pins
    #define     MOSIX   11
    #define     SCKX    13
    #define     MISOX   12
    SPISettings PIXYSPI(1000000, MSBFIRST, SPI_MODE0); 
    
    // Pixy Parameters
    #define  PIXY_SPI_CS             15        // SPI Chip Select
    #define  PIXY_SPI_TRY            24
    #define  PIXY_SYNC_BYTE_DATA     0x5b      // Sync with Pixy Device
    #define  PIXY_START_WORD         0xaa55    // Start word in Pixy Data Block
    #define  PIXY_START_WORD_CC      0xaa56    // Stard word in Pixy Data Block with Color Codes
    #define  PIXY_MAX_BLOCKS         10        // Max No. of detectable blocks 
    
    
    // PIXY data block struct array
    typedef struct 
    {
        uint16_t  obj = 0;   // Object type: Normal (0xaa55), Color Code (0xaa56)
        uint16_t  sig = 0;   // Signature Number
        uint16_t  x   = 0;   // x center of object
        uint16_t  y   = 0;   // y center of object
        uint16_t  wd  = 0;   // width of object
        uint16_t  ht  = 0;   // height of object
        int16_t   phi = 0;   // angle of object (applies to Color-Code objects only)
    } PIXY;
    
    
    
    
    uint16_t  pixy_spi_read_16bit(uint16_t target, SPISettings SETX, uint8_t CSX)
    {
        uint8_t   a, b;
        uint16_t  c;
    
        // Read First Byte
        SPI.beginTransaction(SETX);      
            digitalWrite(CSX,LOW);    
            a = SPI.transfer(target);    
        SPI.endTransaction();
    
        // Read Second Byte
        SPI.beginTransaction(SETX);
            b = SPI.transfer(0x00);              
            digitalWrite(CSX,HIGH); 
        SPI.endTransaction();
    
        // Build Pixy 16-bit Word 
        c = (uint16_t)(b | (a<<8));
    
        return  c;
    }
    
    
    
    
    uint16_t  pixy_parse_data( uint16_t target, SPISettings SETX, uint8_t CSX, PIXY *pixy)
    {
        int       ii, jj;
        uint16_t  word1, word2, wordx, checksum_spi, checksum_verify;
        uint16_t  nblock, data[8];
        bool      SYNC;
    
    
        // Initialize variables
        for (ii=0; ii<8; ii++)  data[ii]=0;
        
    
        // Sync with Pixy
        SYNC  = 0;
        word1 = 0;
        word2 = pixy_spi_read_16bit( target, SETX, CSX);  
        jj    = 0; 
        while ( SYNC==0 && jj<PIXY_SPI_TRY)
        {
            jj++;
            word1 = word2;
            word2 = pixy_spi_read_16bit( target, SETX, CSX);
    
            if (word1==PIXY_START_WORD && (word2==PIXY_START_WORD || word2==PIXY_START_WORD_CC) )  {
                SYNC  = 1;
                wordx = word2;
            }
        }
    
       
        // Read Pixy Data Blocks 
        nblock=0;
    
        while (SYNC==1)
        {                       
            SYNC=0;
    
            if ( wordx==PIXY_START_WORD || wordx==PIXY_START_WORD_CC )
            {
                data[0] = wordx;                                      // Object type: Normal (0xaa55), Color Code (0xaa56)
                data[1] = pixy_spi_read_16bit( target, SETX, CSX);    // Checksum
                data[2] = pixy_spi_read_16bit( target, SETX, CSX);    // Signature No.
                data[3] = pixy_spi_read_16bit( target, SETX, CSX);    // X Center of object
                data[4] = pixy_spi_read_16bit( target, SETX, CSX);    // Y Center of object
                data[5] = pixy_spi_read_16bit( target, SETX, CSX);    // Width of object
                data[6] = pixy_spi_read_16bit( target, SETX, CSX);    // Height of object
    
                if (wordx==PIXY_START_WORD)     data[7]=0;                                        // Angle not available for Normal Objects
                if (wordx==PIXY_START_WORD_CC)  data[7]=pixy_spi_read_16bit( target, SETX, CSX);  // Angle of object for Color-Coded-Objects
    
                // Verify CHECKSUM
                checksum_spi    = data[1];
                checksum_verify = data[2] + data[3] + data[4] + data[5] + data[6] + data[7];
    
    
                // Capture Block Data
                if (checksum_spi==checksum_verify  && nblock<PIXY_MAX_BLOCKS)
                {
                    SYNC = 1;                
                    nblock++;
                                    
                    pixy->sig = data[2];
                    pixy->x   = data[3];
                    pixy->y   = data[4];
                    pixy->wd  = data[5];
                    pixy->ht  = data[6];
                    pixy->phi = (int16_t)data[7];
        
                    pixy++;  // Increment pointer to PIXY struct array
    
                    wordx = pixy_spi_read_16bit( target, SETX, CSX);    // Advance to next data block
                }             
                
            }  // if ( wordx==PIXY_START_WORD || wordx==PIXY_START_WORD_CC )
            
        }  // while (SYNC==1)
    
        return  nblock;
      
    }
     
    
    
    
    #define MAXLINE 136
    char  nmeac[MAXLINE];
    
    
    void setup() 
    {
        int       ii, jj, kk;
        uint16_t  nblock;
    
        PIXY      pixy[PIXY_MAX_BLOCKS];
    
        
        // INITIALIZE SPI
        pinMode( PIXY_SPI_CS, OUTPUT);
        digitalWrite( PIXY_SPI_CS,HIGH);
     
        SPI.setMOSI(MOSIX);
        SPI.setMISO(MISOX);
        SPI.setSCK(SCKX);
        SPI.begin(); 
    
        Serial.begin(115200); 
        delayMicroseconds(1000000);    
        Serial.println("Initializing...");
        
         
        // Stream Pixy Data Blocks
        for( ii=0; ii<10000; ii++)
        {        
            nblock = pixy_parse_data( PIXY_SYNC_BYTE_DATA, PIXYSPI, PIXY_SPI_CS, pixy);
    
            if (nblock>0)
            {
                for (jj=0; jj<nblock; jj++)
                {
                    sprintf(nmeac, "  %d %d %d %d %d %d %d\r\n", nblock, pixy[jj].sig, pixy[jj].x, pixy[jj].y, pixy[jj].wd, pixy[jj].ht, pixy[jj].phi ); 
                    Serial.print(nmeac);
                }
            }
            else  Serial.println("0");
    
          
            delayMicroseconds(20000);  // Delay=0.02sec (50 Hz sample rate)
        }
    
    }
    
    
    
    
    void loop() 
    {
    }

  7. #7
    Member randomvibe's Avatar
    Join Date
    Mar 2016
    Location
    CA
    Posts
    88
    The aforementioned Pixy functions for SPI communication with the Teensy 3.5 are working well. Now I want to try the setup on my Teensy 3.6 for processing speed, however it's not 5V tolerant. Researching PRJC threads on level shifting say bi-directional level shifters don't work. What's the recommended approach for level shifting SPI signals at 1 Mhz for the Teensy 3.6?

  8. #8
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    4,207

  9. #9
    Junior Member
    Join Date
    May 2017
    Posts
    8
    Quote Originally Posted by randomvibe View Post
    The aforementioned Pixy functions for SPI communication with the Teensy 3.5 are working well. Now I want to try the setup on my Teensy 3.6 for processing speed, however it's not 5V tolerant. Researching PRJC threads on level shifting say bi-directional level shifters don't work. What's the recommended approach for level shifting SPI signals at 1 Mhz for the Teensy 3.6?
    This should help you.
    http://cmucam.org/attachments/915/cmucam5_1_2.pdf
    Teensy 3.6 connect directly to SPI.

  10. #10
    Member randomvibe's Avatar
    Join Date
    Mar 2016
    Location
    CA
    Posts
    88
    Quote Originally Posted by tarzan View Post
    Thanks for that. I've been in communication with Pixy people and they verified that the SPI pins are 3.3V. So need for level shifting with the Teensy 3.6.

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
  •