ADNS2051 Optical Sensor library

ADNS2051.cpp

Committer:
clemente
Date:
2012-05-23
Revision:
0:462017ee2a5b

File content as of revision 0:462017ee2a5b:

/* mbed ADNS2051 Optical Sensor library.

    Copyright (c) 2011 NXP 3787
 
    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:
 
    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.
 
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.
*/

#include "mbed.h"
#include "ADNS2051.h"


#define SCLK_HIGH    _sclk=1;
#define SCLK_LOW    _sclk=0;
//
#define SOUT_HIGH    _sout=1;
#define SOUT_LOW    _sout=0;
//
#define CLKSPEED_100KHZ      10
#define CLKSPEED_200KHZ     5
#define CLKSPEED_500KHZ     2
#define CLKSPEED_1MHZ       1
#define CLKSPEED            CLKSPEED_1MHZ

// unsigned char pixmap[256];

ADNS2051::ADNS2051(  PinName sout, PinName sclk) : _sout( sout), _sclk( sclk) {

    _sout.output();

}

/*
 Init the ADNS 2051 connection. 
 @return
    0 = OK
    1 = BAD connection
*/
unsigned char ADNS2051::init( void)
{
    // sync the communication line to the ADNS chip
    sync();
    // check the connection
    return ( chkconn());
    
    // for now I use the ADNS's default values.
}

/*
 * Sync the communication to the ADNS.
 * Start a sequence, and then wait the watchdog timer expire.
 * After this sequence the sout and sclk are HIGH and ready to star a correct 
 * write to or read from the ADNS chip.
*/
void ADNS2051::sync( void)
{
    // let the data pin as output!
    _sout.output();
    // 
    SOUT_HIGH;
    SCLK_HIGH;
    wait_us(100);
    SCLK_LOW;
    wait_us(100);
    SCLK_HIGH;
    wait_us(100);
    
    /* the program wait until the ADNS's serial watchdog timer expire  */
    wait_ms( 1200);        // Serial Port Transaction Timer [Tsptt] value: 1s (DS pag:10)
}

/* 
 Verify the ADNS 2051 connection. 
 return:
    0 = OK
    1 = BAD connection
*/
unsigned char ADNS2051::chkconn( void)
{
    if ( read( PRODUCTID_REG) == PRODUCTID_VAL)
        return 0;
    else
        return 1;
}

/* */
unsigned char ADNS2051::get_productID( void)
{
    return read( PRODUCTID_REG);
}

/* */
unsigned char ADNS2051::get_revisionID( void)
{
    return read( REVISIONID_REG);
}

/** Change the default frame rate (1500 f/s) to 2300 f/s
*/
void ADNS2051::setframerate( void)
{
    // write to the lower reg first.
    // the values are 2's compl hex for 2300 f/s (DS pag:38)
    write( FRMPRDLW_REG, 0x6E);
    write( FRMPRDUP_REG, 0xE1);
}


/** Return the SQUAL value.
 *
 *  SQUAL is a measure of the number of features visible by the sensor in the
 *  current frame. The maximum value is 255. Since small changes in the current frame
 *  can result in changes in SQUAL, variations in SQUAL when looking at a surface are
 *  expected. SQUAL is nearly equal to zero, if there is no surface below the sensor.
*/
unsigned char ADNS2051::surfacequality( void)
{
    return read( SQUAL_REG);
}

/** Return the Average pixel value
 * 
 *  Average Pixel value in current frame. Minimum value = 0,
 *  maximum = 63. The average pixel value can be adjusted every frame.
*/
unsigned char ADNS2051::averagepixel( void)
{
    return read( AVRPXL_REG);
}

/** Return the Maximum Pixel value
 *
 *  Maximum Pixel value in current frame. Minimum value = 0,
 *  maximum value = 63. The maximum pixel value can be adjusted every frame.
*/
unsigned char ADNS2051::maxpixelval( void)
{
    return read( MAXPXL_REG);
}


/**
 * read the motion register and update the deltaX and deltaY variable with the register value
 * 
 * @return :
 *   value 0 => no motion, no led fault, no overflow
 *   bit 0 == 1 => motion
 *   bit 1 == 1 => led fault
 *   bit 3 == 1 => overflow
*/
unsigned char ADNS2051::readmotion( void)
{
    unsigned char status;
    unsigned char res=0;        // no motion, no LED fault...
    
    // Avago RECOMMENDS that registers 0x02, 0x03 and 0x04 be read sequentially
    status = read( MOTION_REG);
    deltaX = read( DELTAX_REG);
    deltaY = read( DELTAY_REG);
    
    // 
    if ( status & (1<<OVERFY_BIT) || status & (1<<OVERFX_BIT) ) {
        res|=4;
    }
    //
    if ( status & (1<<RESOLUTION_BIT) )
        currentresolution=800;
    else
        currentresolution=400;
    //
    if ( status & (1<<MOTION_BIT) )
        res|=1;
    if ( status & (1<<LEDFAULT_BIT) )
        res|=2;
    
    return( res);
}

/*
 Start the dump of the pixel map. Store the value to the array pixmap
*/
void ADNS2051::pixeldump( unsigned char *buffer)
{

    unsigned char res;
    unsigned char i;
    
    // set no sleep and start pixdump
    write( CONFIGBIT_REG, 0x00 | (1<<SLEEP_BIT) | (1<<PIXDUMP_BIT));
    
    i=0;
    while( 1) {
        // read the pix address...
        res=read( DTOUTUP_REG);
        // read the pix value
        res=read( DTOUTLW_REG);
        // if the MSB is set: no valid data...
        if ( res & (1<<PIXDATAVALID_BIT))
            continue;
        // store the pix value
        buffer[i++]=res;
        // check if the pixel counter overflow...
        if ( i==0)
            break;
    }
    
    // reset PixDump bit.
    write( CONFIGBIT_REG, 0x00);

}


/* 
 * Read a register from the ADNS2051 sensor.
 * Before to return wait 120us as required and set the IO pin as output.
*/
unsigned char ADNS2051::read(unsigned char address)
{
    unsigned char value;
    int bit_pos;
    
    SCLK_HIGH;                 // Set the clock high. 
    address &= 0x7F;         // A '0' as its MSB to indicate data direction as "READ".
 
    /* Send the Address to the ADNS2051 */
    for( bit_pos=7; bit_pos >=0; bit_pos--){
        SCLK_LOW;                  // Set the clock LOW
                                
        // SDIO is changed on falling edges of SCLK
        if(address & (1<<bit_pos)){
            SOUT_HIGH;
        }
        else{
            SOUT_LOW;
        }
        //
        wait_us( CLKSPEED);
        SCLK_HIGH;
        wait_us( CLKSPEED);
    }
    /* 
     SCLK will need to be delayed after the last address data bit to ensure that 
     the ADNS-2051 has at least 100 µs to prepare the requested data (DS pag.19)
    */
    wait_us(120);
    // Change the sout to input...
    _sout.input();
    _sout.mode( PullUp);
    
    /* Read the data byte from the ADNS2051 */
    value=0;
    for( bit_pos=7; bit_pos >= 0; bit_pos--){
        SCLK_LOW;
        wait_us( CLKSPEED);
        SCLK_HIGH;            // data is ready on rising edge of clock signal.
        wait_us( CLKSPEED);
        /* read the input pin */
        if ( _sout)
            value |= (1<<bit_pos);

    }
    //
    _sout.output();
    // Timing between read and either write or subsequent read commands must be >120us
    wait_us( 120);
    
    return value;
}    

void ADNS2051::write(unsigned char address, unsigned char value)
{
    int bit_pos;
    
    SCLK_HIGH;                 // Set the clock high. 
    address |= 0x80;         // A '1' as its MSB to indicate data direction as "WRITE".

    /* Send the Address to the ADNS2051 */
    for( bit_pos=7; bit_pos >=0; bit_pos--){
        SCLK_LOW;                  // Set the clock LOW
                                
        // SDIO is changed on falling edges of SCLK
        if(address & (1<<bit_pos)){
            SOUT_HIGH;
        }
        else{
            SOUT_LOW;
        }
        //
        wait_us( CLKSPEED);
        SCLK_HIGH;
        wait_us( CLKSPEED);
    }

    /* Send the value to the ADNS2051 */
    for( bit_pos=7; bit_pos >=0; bit_pos--){
        SCLK_LOW;                  // Set the clock LOW
                                
        // SDIO is changed on falling edges of SCLK
        if(value & (1<<bit_pos)){
            SOUT_HIGH;
        }
        else{
            SOUT_LOW;
        }
        //
        wait_us( CLKSPEED);
        SCLK_HIGH;
        wait_us( CLKSPEED);
    }
    // Timing between read and either write or subsequent read commands must be >120us
    wait_us( 120);

}