Jesús Torres
/
ADNS9500
Interface to access to Avago ADNS-9500 laser mouse sensors.
Revision 1:fa3052be61b5, committed 2012-02-13
- Comitter:
- aplatanado
- Date:
- Mon Feb 13 11:39:24 2012 +0000
- Parent:
- 0:782f2061a8f5
- Child:
- 2:ee0c13ef1320
- Commit message:
- rename files to remove uppercase letters.
Changed in this revision
--- a/ADNS9500.cpp Fri Feb 10 21:35:22 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,485 +0,0 @@ -/* - * ADNS9500.hpp - Interface to access to Avago ADNS-9500 laser mouse sensors - * - * Copyright 2012 Jesus Torres <jmtorres@ull.es> - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <fstream> -#include <math.h> -#include <mbed.h> -#include <string> - -#include <ADNS9500.hpp> - -#define WAIT_TSRAD() wait_us(100) -#define WAIT_TSRWR() wait_us(20) -#define WAIT_TBEXIT() wait_us(1) // 500ns -#define WAIT_TNCSSCLK() wait_us(1) // 120ns -#define WAIT_TSCLKNCS() wait_us(20) -#define WAIT_TLOAD() wait_us(15) - -#define DEFAULT_MAX_FPS 1958 -#define DEFAULT_MAX_FRAME_PERIOD ceil(1.0e6 / DEFAULT_MAX_FPS) // in us -#define DEFAULT_X_CPI 1620 -#define DEFAULT_Y_CPI 1620 -#define CPI_CHANGE_UNIT 90 - -#define SET_BIT(word, mask) (word | mask) -#define CLEAR_BIT(word, mask) (word & (~mask)) - -namespace adns9500 { - - ADNS9500::ADNS9500(PinName mosi, PinName miso, PinName sclk, PinName ncs, - int spi_frequency, PinName motion) - : spi_(mosi, miso, sclk), - motion_(motion), - ncs_(ncs), - enabled_(false), - dx_(0), dy_(0), - xCpi_(DEFAULT_X_CPI), yCpi_(DEFAULT_Y_CPI) - { - motion_.fall(this, &ADNS9500::motionTrigger); - } - - ADNS9500::~ADNS9500() - { - shutdown(); - } - - void ADNS9500::reset(const char* firmware) - { - // SPI port reset - ncs_.write(1); - WAIT_TNCSSCLK(); - ncs_.write(0); - WAIT_TNCSSCLK(); - - // send 0x3a to POWER_UP_RESET and wait for at least 50ms - spi_.write(POWER_UP_RESET); - WAIT_TSRAD(); - spi_.write(0x5a); - wait_ms(50); - - // clear observation register. Only required to deassert shutdown mode. - spi_.write(OBSERVATION); - WAIT_TSRAD(); - spi_.write(0x00); - wait_us(DEFAULT_MAX_FRAME_PERIOD); - - // check observation register bits [5:0] - spi_.write(OBSERVATION); - WAIT_TSRAD(); - int observation = spi_.write(0x00); - WAIT_TSRAD(); - - if (! ADNS9500_IF_OBSERVATION_TEST(observation)) - error("ADNS9500::reset : observation register test failed: 0x%x\n", observation); - - // read motion data - spi_.write(MOTION); - WAIT_TSRAD(); - spi_.write(DELTA_X_L); - WAIT_TSRAD(); - spi_.write(DELTA_X_H); - WAIT_TSRAD(); - spi_.write(DELTA_Y_L); - WAIT_TSRAD(); - spi_.write(DELTA_Y_H); - WAIT_TSRAD(); - - // read product and revision id to test the connection - spi_.write(PRODUCT_ID); - WAIT_TSRAD(); - int product_id = spi_.write(REVISION_ID); - WAIT_TSRAD(); - int revision_id = spi_.write(0x00); - WAIT_TSCLKNCS(); - - ncs_.write(1); - - if (product_id != 0x33) { - error("ADNS9500::reset : bad product ID: 0x%x\n", product_id); - } - - if (revision_id != 0x03) { - error("ADNS9500::reset : bad revision ID: 0x%x\n", revision_id); - } - - enabled_ = true; - - if (firmware) { - sromDownload(firmware); - enableLaser(); - } - } - - void ADNS9500::shutdown() - { - if (! enabled_) - error("ADNS9500::shutdown : the sensor is not enabled\n"); - - ncs_.write(0); - WAIT_TNCSSCLK(); - - // send 0x3a to POWER_UP_RESET - spi_.write(POWER_UP_RESET); - WAIT_TSRAD(); - spi_.write(0x5a); - WAIT_TSCLKNCS(); - - ncs_.write(1); - - enabled_ = false; - } - - int ADNS9500::read(Register lregister) - { - if (! enabled_) - error("ADNS9500::read : the sensor is not enabled\n"); - - ncs_.write(0); - WAIT_TNCSSCLK(); - - // send the command to read the register - spi_.write(lregister); - WAIT_TSRAD(); - int value = spi_.write(0x00); - WAIT_TSCLKNCS(); - - ncs_.write(1); - return value; - } - - int ADNS9500::read(Register uregister, Register lregister) - { - if (! enabled_) - error("ADNS9500::read : the sensor is not enabled\n"); - - ncs_.write(0); - WAIT_TNCSSCLK(); - - // send the command to read the registers - spi_.write(lregister); - WAIT_TSRAD(); - int lvalue = spi_.write(uregister); - WAIT_TSRAD(); - int uvalue = spi_.write(0x00); - WAIT_TSCLKNCS(); - - ncs_.write(1); - - return ADNS9500_UINT16(uvalue, lvalue); - } - - int ADNS9500::sromDownload(const char* filename) - { - if (! enabled_) - error("ADNS9500::sromDownload : the sensor is not enabled\n"); - - ncs_.write(0); - WAIT_TNCSSCLK(); - - // SROM download - spi_.write(CONFIGURATION_IV); - WAIT_TSRAD(); - spi_.write(ADNS9500_CONFIGURATION_IV_SROM_SIZE); - WAIT_TSRAD(); - spi_.write(SROM_ENABLE); - WAIT_TSRAD(); - spi_.write(0x1d); - wait_us(DEFAULT_MAX_FRAME_PERIOD); - spi_.write(SROM_ENABLE); - WAIT_TSRAD(); - spi_.write(0x18); - WAIT_TSRAD(); - spi_.write(SROM_LOAD_BURST); - - // TODO: Comprobar que pasa si el archivo no existe - ifstream ifs(filename, ifstream::in); - while(ifs.good()) { - WAIT_TLOAD(); - spi_.write(ifs.get()); - } - WAIT_TSCLKNCS(); - ncs_.write(1); - WAIT_TBEXIT(); - - if (! ifs.eof()) - error("ADNS9500::sromDownload : error reading from file: %s\n", filename); - - // test if SROM was downloaded successfully - wait_us(160); - ncs_.write(0); - WAIT_TNCSSCLK(); - spi_.write(SROM_ID); - WAIT_TSRAD(); - int srom_id = spi_.write(0x00); - WAIT_TSCLKNCS(); - ncs_.write(1); - - if (! srom_id) - error("ADNS9500::sromDownload : the firmware was not successful downloaded\n"); - - // test laser fault condition - ncs_.write(0); - WAIT_TNCSSCLK(); - spi_.write(MOTION); - WAIT_TSRAD(); - int motion = spi_.write(0x00); - WAIT_TSCLKNCS(); - ncs_.write(1); - - if (ADNS9500_IF_LASER_FAULT(motion)) - error("ADNS9500::sromDownload : laser fault condition detected\n"); - - // return the SROM CRC value - ncs_.write(0); - WAIT_TNCSSCLK(); - - spi_.write(SROM_ENABLE); - WAIT_TSRAD(); - spi_.write(0x15); - wait_us(10); - spi_.write(DATA_OUT_LOWER); - WAIT_TSRAD(); - int lcrc = spi_.write(DATA_OUT_UPPER); - WAIT_TSRAD(); - int ucrc = spi_.write(0x00); - - WAIT_TSCLKNCS(); - ncs_.write(1); - - return ADNS9500_UINT16(ucrc, lcrc); - } - - void ADNS9500::enableLaser(bool enable) - { - if (! enabled_) - error("ADNS9500::enableLaser : the sensor is not enabled\n"); - - ncs_.write(0); - WAIT_TNCSSCLK(); - - spi_.write(LASER_CTRL0); - WAIT_TSRAD(); - if (enable) { - int laser_ctrl0 = CLEAR_BIT(0x00, ADNS9500_LASER_CTRL0_FORCE_DISABLED); - spi_.write(laser_ctrl0); - } - else { - int laser_ctrl0 = SET_BIT(0x00, ADNS9500_LASER_CTRL0_FORCE_DISABLED); - spi_.write(laser_ctrl0); - } - - WAIT_TSCLKNCS(); - ncs_.write(1); - } - - bool ADNS9500::getMotionDelta(int& dx, int& dy) - { - if (! enabled_) - error("ADNS9500::getMotionDelta : the sensor is not enabled\n"); - - ncs_.write(0); - WAIT_TNCSSCLK(); - - spi_.write(MOTION); - WAIT_TSRAD(); - int motion = spi_.write(DELTA_X_L); - WAIT_TSRAD(); - - if (ADNS9500_IF_MOTION(motion)) { - int tmp = spi_.write(DELTA_X_L); - WAIT_TSRAD(); - dx = ADNS9500_UINT16(spi_.write(DELTA_X_H), tmp); - WAIT_TSRAD(); - tmp = spi_.write(DELTA_Y_L); - WAIT_TSRAD(); - dy = ADNS9500_UINT16(spi_.write(DELTA_Y_H), tmp); - - dx_ = dx; - dy_ = dy; - } - else { - spi_.write(0x00); - - dx = dx_; - dy = dy_; - } - - WAIT_TSCLKNCS(); - ncs_.write(1); - - return ADNS9500_IF_MOTION(motion); - } - - bool ADNS9500::getMotionDeltaMM(float& dx, float& dy) - { - int rawDx, rawDy; - - bool motion = getMotionDelta(rawDx, rawDy); - dx = (float)rawDx / xCpi_ * 25.4; - dy = (float)rawDy / yCpi_ * 25.4; - - return motion; - } - - bool ADNS9500::getMotionData(MotionData& data) - { - if (! enabled_) - error("ADNS9500::getMotionData : the sensor is not enabled\n"); - - ncs_.write(0); - WAIT_TNCSSCLK(); - - // activate motion burst mode - spi_.write(MOTION_BURST); - WAIT_TSRAD(); - spi_.write(0x50); - - // if in run mode, wait for 1 frame - wait_us(DEFAULT_MAX_FRAME_PERIOD); - - // read motion burst data - data.motion = spi_.write(0x00); - data.observation = spi_.write(0x00); - - int ldx = spi_.write(0x00); - data.dx = ADNS9500_UINT16(spi_.write(0x00), ldx); - - int ldy = spi_.write(0x00); - data.dy = ADNS9500_UINT16(spi_.write(0x00), ldy); - - data.squal = spi_.write(0x00); - data.pixelSum = spi_.write(0x00); - data.maximumPixel = spi_.write(0x00); - data.minimumPixel = spi_.write(0x00); - - int ushutter = spi_.write(0x00); - data.shutter = ADNS9500_UINT16(ushutter, spi_.write(0x00)); - - int uframe_period = spi_.write(0x00); - data.framePeriod = ADNS9500_UINT16(uframe_period, spi_.write(0x00)); - - WAIT_TSCLKNCS(); - ncs_.write(1); - WAIT_TBEXIT(); - - return ADNS9500_IF_MOTION(data.motion); - } - - void ADNS9500::setResolution(Resolution xy_resolution) - { - if (! enabled_) - error("ADNS9500::setResolution : the sensor is not enabled\n"); - - ncs_.write(0); - WAIT_TNCSSCLK(); - - // enable XY axes CPI in sync mode - spi_.write(CONFIGURATION_II); - WAIT_TSRAD(); - int rpt_mod = spi_.write(0x00); - WAIT_TSRAD(); - spi_.write(CLEAR_BIT(rpt_mod, ADNS9500_CONFIGURATION_II_RPT_MOD)); - WAIT_TSRAD(); - - // set resolution for X-axis and Y-axis - spi_.write(CONFIGURATION_I); - WAIT_TSRAD(); - spi_.write(xy_resolution); - - WAIT_TSCLKNCS(); - ncs_.write(1); - - xCpi_ = xy_resolution * CPI_CHANGE_UNIT; - yCpi_ = xy_resolution * CPI_CHANGE_UNIT; - } - - void ADNS9500::setResolution(Resolution x_resolution, Resolution y_resolution) - { - if (! enabled_) - error("ADNS9500::setResolution : the sensor is not enabled\n"); - - ncs_.write(0); - WAIT_TNCSSCLK(); - - // disable XY axes CPI in sync mode - spi_.write(CONFIGURATION_II); - WAIT_TSRAD(); - int rpt_mod = spi_.write(0x00); - WAIT_TSRAD(); - spi_.write(SET_BIT(rpt_mod, ADNS9500_CONFIGURATION_II_RPT_MOD)); - WAIT_TSRAD(); - - // set resolution for X-axis - spi_.write(CONFIGURATION_I); - WAIT_TSRAD(); - spi_.write(x_resolution); - WAIT_TSRAD(); - - // set resolution for Y-axis - spi_.write(CONFIGURATION_V); - WAIT_TSRAD(); - spi_.write(y_resolution); - - WAIT_TSCLKNCS(); - ncs_.write(1); - - xCpi_ = x_resolution * CPI_CHANGE_UNIT; - yCpi_ = y_resolution * CPI_CHANGE_UNIT; - } - - void ADNS9500::captureFrame(uint8_t pixels[NUMBER_OF_PIXELS_PER_FRAME]) - { - if (! enabled_) - error("ADNS9500::captureFrame : the sensor is not enabled\n"); - - ncs_.write(0); - WAIT_TNCSSCLK(); - - spi_.write(FRAME_CAPTURE); - WAIT_TSRAD(); - spi_.write(0x93); - WAIT_TSRAD(); - spi_.write(FRAME_CAPTURE); - WAIT_TSRAD(); - spi_.write(0xc5); - wait_us(DEFAULT_MAX_FRAME_PERIOD); - wait_us(DEFAULT_MAX_FRAME_PERIOD); - - // check for first pixel reading motion bit - spi_.write(MOTION); - while(true) { - WAIT_TSRAD(); - int motion = spi_.write(MOTION); - if (ADNS9500_IF_MOTION(motion)) - break; - } - - // read pixel values - spi_.write(PIXEL_BURST); - WAIT_TSRAD(); - for (uint8_t* p = pixels; p != pixels + sizeof(pixels); ++p) { - WAIT_TLOAD(); - *p = spi_.write(PIXEL_BURST); - } - - // burst exit - WAIT_TSCLKNCS(); - ncs_.write(1); - WAIT_TBEXIT(); - } -} \ No newline at end of file
--- a/ADNS9500.hpp Fri Feb 10 21:35:22 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,293 +0,0 @@ -/* - * ADNS9500.hpp - Interface to access to Avago ADNS-9500 laser mouse sensors - * - * Copyright 2012 Jesus Torres <jmtorres@ull.es> - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ADNS9500_HPP_ -#define ADNS9500_HPP_ - -#include <mbed.h> -#include <stdint.h> -#include <string> - -#define ADNS9500_CONFIGURATION_II_RPT_MOD (1 << 2) -#define ADNS9500_CONFIGURATION_IV_SROM_SIZE (1 << 1) -#define ADNS9500_LASER_CTRL0_FORCE_DISABLED (1 << 0) -#define ADNS9500_OBSERVATION_CHECK_BITS 0x3f - -#define ADNS9500_IF_MOTION(x) (bool)(x & 0x80) -#define ADNS9500_IF_LASER_FAULT(x) (bool)(x & 0x40) -#define ADNS9500_IF_RUNNING_SROM_CODE(x) (bool)(x & 0x80) -#define ADNS9500_IF_OBSERVATION_TEST(x) (bool)(x & ADNS9500_OBSERVATION_CHECK_BITS) -#define ADNS9500_UINT16(ub, lb) ((uint16_t)ub << 8 | (uint16_t)lb) - -namespace adns9500 -{ - // Maximum SPI clock frequency supported by the sensor - const int MAX_SPI_FREQUENCY = 2000000; - - // Internal oscillator norminal frequency - const int INTERNAL_OSCILLATOR_FREQUENCY = 47000000; - - // Number of pixels per frame - const int NUMBER_OF_PIXELS_PER_FRAME = 900; - - // - // Sensor registers - // - - enum Register - { - PRODUCT_ID = 0x00, - REVISION_ID = 0x01, - MOTION = 0x02, - DELTA_X_L = 0x03, - DELTA_X_H = 0x04, - DELTA_Y_L = 0x05, - DELTA_Y_H = 0x06, - SQUAL = 0x07, - PIXEL_SUM = 0x08, - MAXIMUM_PIXEL = 0x09, - MINIMUM_PIXEL = 0x0a, - SHUTTER_LOWER = 0x0b, - SHUTTER_UPPER = 0x0c, - FRAME_PERIOD_LOWER = 0x0d, - FRAME_PERIOD_UPPER = 0x0e, - CONFIGURATION_I = 0x0f, - CONFIGURATION_II = 0x10, - FRAME_CAPTURE = 0x12, - SROM_ENABLE = 0x13, - LASER_CTRL0 = 0x20, - DATA_OUT_LOWER = 0x25, - DATA_OUT_UPPER = 0x26, - SROM_ID = 0x2a, - OBSERVATION = 0x24, - CONFIGURATION_V = 0x2f, - CONFIGURATION_IV = 0x39, - POWER_UP_RESET = 0x3a, - MOTION_BURST = 0x50, - SROM_LOAD_BURST = 0x62, - PIXEL_BURST = 0x62 - }; - - // - // Supported resolutions - // - - enum Resolution - { - CPI_90 = 0x01, - CPI_1630 = 0x12, - CPI_3240 = 0x24, - CPI_5040 = 0x38 - }; - - // - // Motion burst data - // - - struct MotionData - { - int motion; - int observation; - int dx; - int dy; - int squal; - int pixelSum; - int maximumPixel; - int minimumPixel; - int shutter; - int framePeriod; - }; - - // - // Interface to access to ADNS-9500 mouse sensor - // - - class ADNS9500 - { - public: - - // - // Create the sensor interface - // - // @param mosi MOSI pin for the SPI interface - // @param miso MISO pin for the SPI interface - // @param sclk SCLK pin for the SPI interface - // @param spi_frequency SPI clock frequency in Hz up to MAX_SPI_PORT_FREQUENCY - // @param ncs A digital active-low output pin for sensor chip select - // @param motion A digital active-low input pin activated by the sensor when motion - // is detected - // - ADNS9500(PinName mosi, PinName miso, PinName sclk, PinName ncs, - int spi_frequency = MAX_SPI_FREQUENCY, PinName motion = NC); - - // - // Destroy de sensor interface - // - ~ADNS9500(); - - // - // Power up/reset the sensor - // Terminate with error if the connection can not be established - // - // @param firmware If the firmware has to be downloaded, C-string containing the name - // of the file where it is stored, or NULL in other case - // - void reset(const char* firmware = NULL); - - // - // Shutdown the sensor - // - void shutdown(); - - // - // Read the value of a sensor register - // - // @param lregister The register which to read its value - // @return The value of the register - // - int read(Register lregister); - - // - // Read the values of sensor registers - // - // @param uregister The register which to read the upper byte - // @param lregister The register which to read the lower byte - // @return The values of registers as a 16-bit integer, putting the value - // of uregister in the upper byte and the value of lregister in the - // lower byte - // - int read(Register uregister, Register lregister); - - // - // Get information about sensor status - // - // @return The value of MOTION register. It tells if motion or laser fault - // conditions have ocurred, laser power setting status and operating - // mode in current frame - // - int status() - { return read(MOTION); } - - // - // Download the firmware to the sensor SROM - // - // @param filename The name of the file which contains the sensor firmware - // @return The SROM CRC value - // - int sromDownload(const char* filename); - - // - // Enable the laser - // - // @param enable True if laser must be enabled, or false if laser must be disabled - // - void enableLaser(bool enable=true); - - // - // Get motion deltas from sensor - // - // @param dx The component X of displacement - // @param dy The component Y of displacement - // @return True if motion was occurred since the last time the function was called, - // or false in other case - // - bool getMotionDelta(int& dx, int& dy); - - // - // Get motion deltas in mm. from sensor - // - // @param dx The component X of displacement in mm. - // @param dy The component Y of displacement in mm. - // @return True if motion was occurred since the last time the function was called, - // or false in other case - // - bool getMotionDeltaMM(float& dx, float& dy); - - // - // Get all information about motion - // - // @param data The struct where sensor data will be stored - // @return True if motion was occurred since the last time the function was called, - // or false in other case - // - bool getMotionData(MotionData& data); - - // - // Set the resolution on XY axes together - // - // @param xy_resolution The resolution for X-axis and Y-axis - // - void setResolution(Resolution xy_resolution); - - // - // Set the resolutions on X-axis and Y-axis - // - // @param x_resolution The resolution for X-axis - // @param y_resolution The resolution for Y-axis - // - void setResolution(Resolution x_resolution, Resolution y_resolution); - - // - // Get a full array of pixel values from a single frame. - // This disables navigation and overwrites any donwloaded firmware, - // so call to reset() is needed to restore them - // - // @param pixels The array where pixel values will be stored - // - void captureFrame(uint8_t pixels[NUMBER_OF_PIXELS_PER_FRAME]); - - // - // Member function invoked when motion has ocurred and if a motion pin - // was specified when the object constructor was called. - // By default it invokes the function specified by a previous call to attach() - // - virtual void motionTrigger() - { motionTrigger_.call(); } - - // - // Attach a function to call when a falling edge occurs on motion pin - // - // @param function A pointer to a function or 0 to set the attached function as none - // - void attach(void (*function)(void)) - { motionTrigger_.attach(function); } - - // - // Attach a member function to call when a falling edge occurs on motion pin - // - // @param object A reference to the object to call the member function on - // @param function A pointer to the member function to be called - // - template<typename T> - void attach(T& object, void (T::*member)(void)) - { motionTrigger_.attach(object, member); } - - private: - SPI spi_; - InterruptIn motion_; - DigitalOut ncs_; - - bool enabled_; - int dx_, dy_; - int xCpi_, yCpi_; - - FunctionPointer motionTrigger_; - }; -} - -#endif /* ADNS9500_HPP_ */ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/adns9500.cpp Mon Feb 13 11:39:24 2012 +0000 @@ -0,0 +1,485 @@ +/* + * adns9500.hpp - Interface to access to Avago ADNS-9500 laser mouse sensors + * + * Copyright 2012 Jesus Torres <jmtorres@ull.es> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <fstream> +#include <math.h> +#include <mbed.h> +#include <string> + +#include <adns9500.hpp> + +#define WAIT_TSRAD() wait_us(100) +#define WAIT_TSRWR() wait_us(20) +#define WAIT_TBEXIT() wait_us(1) // 500ns +#define WAIT_TNCSSCLK() wait_us(1) // 120ns +#define WAIT_TSCLKNCS() wait_us(20) +#define WAIT_TLOAD() wait_us(15) + +#define DEFAULT_MAX_FPS 1958 +#define DEFAULT_MAX_FRAME_PERIOD ceil(1.0e6 / DEFAULT_MAX_FPS) // in us +#define DEFAULT_X_CPI 1620 +#define DEFAULT_Y_CPI 1620 +#define CPI_CHANGE_UNIT 90 + +#define SET_BIT(word, mask) (word | mask) +#define CLEAR_BIT(word, mask) (word & (~mask)) + +namespace adns9500 { + + ADNS9500::ADNS9500(PinName mosi, PinName miso, PinName sclk, PinName ncs, + int spi_frequency, PinName motion) + : spi_(mosi, miso, sclk), + motion_(motion), + ncs_(ncs), + enabled_(false), + dx_(0), dy_(0), + xCpi_(DEFAULT_X_CPI), yCpi_(DEFAULT_Y_CPI) + { + motion_.fall(this, &ADNS9500::motionTrigger); + } + + ADNS9500::~ADNS9500() + { + shutdown(); + } + + void ADNS9500::reset(const char* firmware) + { + // SPI port reset + ncs_.write(1); + WAIT_TNCSSCLK(); + ncs_.write(0); + WAIT_TNCSSCLK(); + + // send 0x3a to POWER_UP_RESET and wait for at least 50ms + spi_.write(POWER_UP_RESET); + WAIT_TSRAD(); + spi_.write(0x5a); + wait_ms(50); + + // clear observation register. Only required to deassert shutdown mode. + spi_.write(OBSERVATION); + WAIT_TSRAD(); + spi_.write(0x00); + wait_us(DEFAULT_MAX_FRAME_PERIOD); + + // check observation register bits [5:0] + spi_.write(OBSERVATION); + WAIT_TSRAD(); + int observation = spi_.write(0x00); + WAIT_TSRAD(); + + if (! ADNS9500_IF_OBSERVATION_TEST(observation)) + error("ADNS9500::reset : observation register test failed: 0x%x\n", observation); + + // read motion data + spi_.write(MOTION); + WAIT_TSRAD(); + spi_.write(DELTA_X_L); + WAIT_TSRAD(); + spi_.write(DELTA_X_H); + WAIT_TSRAD(); + spi_.write(DELTA_Y_L); + WAIT_TSRAD(); + spi_.write(DELTA_Y_H); + WAIT_TSRAD(); + + // read product and revision id to test the connection + spi_.write(PRODUCT_ID); + WAIT_TSRAD(); + int product_id = spi_.write(REVISION_ID); + WAIT_TSRAD(); + int revision_id = spi_.write(0x00); + WAIT_TSCLKNCS(); + + ncs_.write(1); + + if (product_id != 0x33) { + error("ADNS9500::reset : bad product ID: 0x%x\n", product_id); + } + + if (revision_id != 0x03) { + error("ADNS9500::reset : bad revision ID: 0x%x\n", revision_id); + } + + enabled_ = true; + + if (firmware) { + sromDownload(firmware); + enableLaser(); + } + } + + void ADNS9500::shutdown() + { + if (! enabled_) + error("ADNS9500::shutdown : the sensor is not enabled\n"); + + ncs_.write(0); + WAIT_TNCSSCLK(); + + // send 0x3a to POWER_UP_RESET + spi_.write(POWER_UP_RESET); + WAIT_TSRAD(); + spi_.write(0x5a); + WAIT_TSCLKNCS(); + + ncs_.write(1); + + enabled_ = false; + } + + int ADNS9500::read(Register lregister) + { + if (! enabled_) + error("ADNS9500::read : the sensor is not enabled\n"); + + ncs_.write(0); + WAIT_TNCSSCLK(); + + // send the command to read the register + spi_.write(lregister); + WAIT_TSRAD(); + int value = spi_.write(0x00); + WAIT_TSCLKNCS(); + + ncs_.write(1); + return value; + } + + int ADNS9500::read(Register uregister, Register lregister) + { + if (! enabled_) + error("ADNS9500::read : the sensor is not enabled\n"); + + ncs_.write(0); + WAIT_TNCSSCLK(); + + // send the command to read the registers + spi_.write(lregister); + WAIT_TSRAD(); + int lvalue = spi_.write(uregister); + WAIT_TSRAD(); + int uvalue = spi_.write(0x00); + WAIT_TSCLKNCS(); + + ncs_.write(1); + + return ADNS9500_UINT16(uvalue, lvalue); + } + + int ADNS9500::sromDownload(const char* filename) + { + if (! enabled_) + error("ADNS9500::sromDownload : the sensor is not enabled\n"); + + ncs_.write(0); + WAIT_TNCSSCLK(); + + // SROM download + spi_.write(CONFIGURATION_IV); + WAIT_TSRAD(); + spi_.write(ADNS9500_CONFIGURATION_IV_SROM_SIZE); + WAIT_TSRAD(); + spi_.write(SROM_ENABLE); + WAIT_TSRAD(); + spi_.write(0x1d); + wait_us(DEFAULT_MAX_FRAME_PERIOD); + spi_.write(SROM_ENABLE); + WAIT_TSRAD(); + spi_.write(0x18); + WAIT_TSRAD(); + spi_.write(SROM_LOAD_BURST); + + // TODO: Comprobar que pasa si el archivo no existe + ifstream ifs(filename, ifstream::in); + while(ifs.good()) { + WAIT_TLOAD(); + spi_.write(ifs.get()); + } + WAIT_TSCLKNCS(); + ncs_.write(1); + WAIT_TBEXIT(); + + if (! ifs.eof()) + error("ADNS9500::sromDownload : error reading from file: %s\n", filename); + + // test if SROM was downloaded successfully + wait_us(160); + ncs_.write(0); + WAIT_TNCSSCLK(); + spi_.write(SROM_ID); + WAIT_TSRAD(); + int srom_id = spi_.write(0x00); + WAIT_TSCLKNCS(); + ncs_.write(1); + + if (! srom_id) + error("ADNS9500::sromDownload : the firmware was not successful downloaded\n"); + + // test laser fault condition + ncs_.write(0); + WAIT_TNCSSCLK(); + spi_.write(MOTION); + WAIT_TSRAD(); + int motion = spi_.write(0x00); + WAIT_TSCLKNCS(); + ncs_.write(1); + + if (ADNS9500_IF_LASER_FAULT(motion)) + error("ADNS9500::sromDownload : laser fault condition detected\n"); + + // return the SROM CRC value + ncs_.write(0); + WAIT_TNCSSCLK(); + + spi_.write(SROM_ENABLE); + WAIT_TSRAD(); + spi_.write(0x15); + wait_us(10); + spi_.write(DATA_OUT_LOWER); + WAIT_TSRAD(); + int lcrc = spi_.write(DATA_OUT_UPPER); + WAIT_TSRAD(); + int ucrc = spi_.write(0x00); + + WAIT_TSCLKNCS(); + ncs_.write(1); + + return ADNS9500_UINT16(ucrc, lcrc); + } + + void ADNS9500::enableLaser(bool enable) + { + if (! enabled_) + error("ADNS9500::enableLaser : the sensor is not enabled\n"); + + ncs_.write(0); + WAIT_TNCSSCLK(); + + spi_.write(LASER_CTRL0); + WAIT_TSRAD(); + if (enable) { + int laser_ctrl0 = CLEAR_BIT(0x00, ADNS9500_LASER_CTRL0_FORCE_DISABLED); + spi_.write(laser_ctrl0); + } + else { + int laser_ctrl0 = SET_BIT(0x00, ADNS9500_LASER_CTRL0_FORCE_DISABLED); + spi_.write(laser_ctrl0); + } + + WAIT_TSCLKNCS(); + ncs_.write(1); + } + + bool ADNS9500::getMotionDelta(int& dx, int& dy) + { + if (! enabled_) + error("ADNS9500::getMotionDelta : the sensor is not enabled\n"); + + ncs_.write(0); + WAIT_TNCSSCLK(); + + spi_.write(MOTION); + WAIT_TSRAD(); + int motion = spi_.write(DELTA_X_L); + WAIT_TSRAD(); + + if (ADNS9500_IF_MOTION(motion)) { + int tmp = spi_.write(DELTA_X_L); + WAIT_TSRAD(); + dx = ADNS9500_UINT16(spi_.write(DELTA_X_H), tmp); + WAIT_TSRAD(); + tmp = spi_.write(DELTA_Y_L); + WAIT_TSRAD(); + dy = ADNS9500_UINT16(spi_.write(DELTA_Y_H), tmp); + + dx_ = dx; + dy_ = dy; + } + else { + spi_.write(0x00); + + dx = dx_; + dy = dy_; + } + + WAIT_TSCLKNCS(); + ncs_.write(1); + + return ADNS9500_IF_MOTION(motion); + } + + bool ADNS9500::getMotionDeltaMM(float& dx, float& dy) + { + int rawDx, rawDy; + + bool motion = getMotionDelta(rawDx, rawDy); + dx = (float)rawDx / xCpi_ * 25.4; + dy = (float)rawDy / yCpi_ * 25.4; + + return motion; + } + + bool ADNS9500::getMotionData(MotionData& data) + { + if (! enabled_) + error("ADNS9500::getMotionData : the sensor is not enabled\n"); + + ncs_.write(0); + WAIT_TNCSSCLK(); + + // activate motion burst mode + spi_.write(MOTION_BURST); + WAIT_TSRAD(); + spi_.write(0x50); + + // if in run mode, wait for 1 frame + wait_us(DEFAULT_MAX_FRAME_PERIOD); + + // read motion burst data + data.motion = spi_.write(0x00); + data.observation = spi_.write(0x00); + + int ldx = spi_.write(0x00); + data.dx = ADNS9500_UINT16(spi_.write(0x00), ldx); + + int ldy = spi_.write(0x00); + data.dy = ADNS9500_UINT16(spi_.write(0x00), ldy); + + data.squal = spi_.write(0x00); + data.pixelSum = spi_.write(0x00); + data.maximumPixel = spi_.write(0x00); + data.minimumPixel = spi_.write(0x00); + + int ushutter = spi_.write(0x00); + data.shutter = ADNS9500_UINT16(ushutter, spi_.write(0x00)); + + int uframe_period = spi_.write(0x00); + data.framePeriod = ADNS9500_UINT16(uframe_period, spi_.write(0x00)); + + WAIT_TSCLKNCS(); + ncs_.write(1); + WAIT_TBEXIT(); + + return ADNS9500_IF_MOTION(data.motion); + } + + void ADNS9500::setResolution(Resolution xy_resolution) + { + if (! enabled_) + error("ADNS9500::setResolution : the sensor is not enabled\n"); + + ncs_.write(0); + WAIT_TNCSSCLK(); + + // enable XY axes CPI in sync mode + spi_.write(CONFIGURATION_II); + WAIT_TSRAD(); + int rpt_mod = spi_.write(0x00); + WAIT_TSRAD(); + spi_.write(CLEAR_BIT(rpt_mod, ADNS9500_CONFIGURATION_II_RPT_MOD)); + WAIT_TSRAD(); + + // set resolution for X-axis and Y-axis + spi_.write(CONFIGURATION_I); + WAIT_TSRAD(); + spi_.write(xy_resolution); + + WAIT_TSCLKNCS(); + ncs_.write(1); + + xCpi_ = xy_resolution * CPI_CHANGE_UNIT; + yCpi_ = xy_resolution * CPI_CHANGE_UNIT; + } + + void ADNS9500::setResolution(Resolution x_resolution, Resolution y_resolution) + { + if (! enabled_) + error("ADNS9500::setResolution : the sensor is not enabled\n"); + + ncs_.write(0); + WAIT_TNCSSCLK(); + + // disable XY axes CPI in sync mode + spi_.write(CONFIGURATION_II); + WAIT_TSRAD(); + int rpt_mod = spi_.write(0x00); + WAIT_TSRAD(); + spi_.write(SET_BIT(rpt_mod, ADNS9500_CONFIGURATION_II_RPT_MOD)); + WAIT_TSRAD(); + + // set resolution for X-axis + spi_.write(CONFIGURATION_I); + WAIT_TSRAD(); + spi_.write(x_resolution); + WAIT_TSRAD(); + + // set resolution for Y-axis + spi_.write(CONFIGURATION_V); + WAIT_TSRAD(); + spi_.write(y_resolution); + + WAIT_TSCLKNCS(); + ncs_.write(1); + + xCpi_ = x_resolution * CPI_CHANGE_UNIT; + yCpi_ = y_resolution * CPI_CHANGE_UNIT; + } + + void ADNS9500::captureFrame(uint8_t pixels[NUMBER_OF_PIXELS_PER_FRAME]) + { + if (! enabled_) + error("ADNS9500::captureFrame : the sensor is not enabled\n"); + + ncs_.write(0); + WAIT_TNCSSCLK(); + + spi_.write(FRAME_CAPTURE); + WAIT_TSRAD(); + spi_.write(0x93); + WAIT_TSRAD(); + spi_.write(FRAME_CAPTURE); + WAIT_TSRAD(); + spi_.write(0xc5); + wait_us(DEFAULT_MAX_FRAME_PERIOD); + wait_us(DEFAULT_MAX_FRAME_PERIOD); + + // check for first pixel reading motion bit + spi_.write(MOTION); + while(true) { + WAIT_TSRAD(); + int motion = spi_.write(MOTION); + if (ADNS9500_IF_MOTION(motion)) + break; + } + + // read pixel values + spi_.write(PIXEL_BURST); + WAIT_TSRAD(); + for (uint8_t* p = pixels; p != pixels + sizeof(pixels); ++p) { + WAIT_TLOAD(); + *p = spi_.write(PIXEL_BURST); + } + + // burst exit + WAIT_TSCLKNCS(); + ncs_.write(1); + WAIT_TBEXIT(); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/adns9500.hpp Mon Feb 13 11:39:24 2012 +0000 @@ -0,0 +1,293 @@ +/* + * adns9500.hpp - Interface to access to Avago ADNS-9500 laser mouse sensors + * + * Copyright 2012 Jesus Torres <jmtorres@ull.es> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ADNS9500_HPP_ +#define ADNS9500_HPP_ + +#include <mbed.h> +#include <stdint.h> +#include <string> + +#define ADNS9500_CONFIGURATION_II_RPT_MOD (1 << 2) +#define ADNS9500_CONFIGURATION_IV_SROM_SIZE (1 << 1) +#define ADNS9500_LASER_CTRL0_FORCE_DISABLED (1 << 0) +#define ADNS9500_OBSERVATION_CHECK_BITS 0x3f + +#define ADNS9500_IF_MOTION(x) (bool)(x & 0x80) +#define ADNS9500_IF_LASER_FAULT(x) (bool)(x & 0x40) +#define ADNS9500_IF_RUNNING_SROM_CODE(x) (bool)(x & 0x80) +#define ADNS9500_IF_OBSERVATION_TEST(x) (bool)(x & ADNS9500_OBSERVATION_CHECK_BITS) +#define ADNS9500_UINT16(ub, lb) ((uint16_t)ub << 8 | (uint16_t)lb) + +namespace adns9500 +{ + // Maximum SPI clock frequency supported by the sensor + const int MAX_SPI_FREQUENCY = 2000000; + + // Internal oscillator norminal frequency + const int INTERNAL_OSCILLATOR_FREQUENCY = 47000000; + + // Number of pixels per frame + const int NUMBER_OF_PIXELS_PER_FRAME = 900; + + // + // Sensor registers + // + + enum Register + { + PRODUCT_ID = 0x00, + REVISION_ID = 0x01, + MOTION = 0x02, + DELTA_X_L = 0x03, + DELTA_X_H = 0x04, + DELTA_Y_L = 0x05, + DELTA_Y_H = 0x06, + SQUAL = 0x07, + PIXEL_SUM = 0x08, + MAXIMUM_PIXEL = 0x09, + MINIMUM_PIXEL = 0x0a, + SHUTTER_LOWER = 0x0b, + SHUTTER_UPPER = 0x0c, + FRAME_PERIOD_LOWER = 0x0d, + FRAME_PERIOD_UPPER = 0x0e, + CONFIGURATION_I = 0x0f, + CONFIGURATION_II = 0x10, + FRAME_CAPTURE = 0x12, + SROM_ENABLE = 0x13, + LASER_CTRL0 = 0x20, + DATA_OUT_LOWER = 0x25, + DATA_OUT_UPPER = 0x26, + SROM_ID = 0x2a, + OBSERVATION = 0x24, + CONFIGURATION_V = 0x2f, + CONFIGURATION_IV = 0x39, + POWER_UP_RESET = 0x3a, + MOTION_BURST = 0x50, + SROM_LOAD_BURST = 0x62, + PIXEL_BURST = 0x62 + }; + + // + // Supported resolutions + // + + enum Resolution + { + CPI_90 = 0x01, + CPI_1630 = 0x12, + CPI_3240 = 0x24, + CPI_5040 = 0x38 + }; + + // + // Motion burst data + // + + struct MotionData + { + int motion; + int observation; + int dx; + int dy; + int squal; + int pixelSum; + int maximumPixel; + int minimumPixel; + int shutter; + int framePeriod; + }; + + // + // Interface to access to ADNS-9500 mouse sensor + // + + class ADNS9500 + { + public: + + // + // Create the sensor interface + // + // @param mosi MOSI pin for the SPI interface + // @param miso MISO pin for the SPI interface + // @param sclk SCLK pin for the SPI interface + // @param spi_frequency SPI clock frequency in Hz up to MAX_SPI_PORT_FREQUENCY + // @param ncs A digital active-low output pin for sensor chip select + // @param motion A digital active-low input pin activated by the sensor when motion + // is detected + // + ADNS9500(PinName mosi, PinName miso, PinName sclk, PinName ncs, + int spi_frequency = MAX_SPI_FREQUENCY, PinName motion = NC); + + // + // Destroy de sensor interface + // + ~ADNS9500(); + + // + // Power up/reset the sensor + // Terminate with error if the connection can not be established + // + // @param firmware If the firmware has to be downloaded, C-string containing the name + // of the file where it is stored, or NULL in other case + // + void reset(const char* firmware = NULL); + + // + // Shutdown the sensor + // + void shutdown(); + + // + // Read the value of a sensor register + // + // @param lregister The register which to read its value + // @return The value of the register + // + int read(Register lregister); + + // + // Read the values of sensor registers + // + // @param uregister The register which to read the upper byte + // @param lregister The register which to read the lower byte + // @return The values of registers as a 16-bit integer, putting the value + // of uregister in the upper byte and the value of lregister in the + // lower byte + // + int read(Register uregister, Register lregister); + + // + // Get information about sensor status + // + // @return The value of MOTION register. It tells if motion or laser fault + // conditions have ocurred, laser power setting status and operating + // mode in current frame + // + int status() + { return read(MOTION); } + + // + // Download the firmware to the sensor SROM + // + // @param filename The name of the file which contains the sensor firmware + // @return The SROM CRC value + // + int sromDownload(const char* filename); + + // + // Enable the laser + // + // @param enable True if laser must be enabled, or false if laser must be disabled + // + void enableLaser(bool enable=true); + + // + // Get motion deltas from sensor + // + // @param dx The component X of displacement + // @param dy The component Y of displacement + // @return True if motion was occurred since the last time the function was called, + // or false in other case + // + bool getMotionDelta(int& dx, int& dy); + + // + // Get motion deltas in mm. from sensor + // + // @param dx The component X of displacement in mm. + // @param dy The component Y of displacement in mm. + // @return True if motion was occurred since the last time the function was called, + // or false in other case + // + bool getMotionDeltaMM(float& dx, float& dy); + + // + // Get all information about motion + // + // @param data The struct where sensor data will be stored + // @return True if motion was occurred since the last time the function was called, + // or false in other case + // + bool getMotionData(MotionData& data); + + // + // Set the resolution on XY axes together + // + // @param xy_resolution The resolution for X-axis and Y-axis + // + void setResolution(Resolution xy_resolution); + + // + // Set the resolutions on X-axis and Y-axis + // + // @param x_resolution The resolution for X-axis + // @param y_resolution The resolution for Y-axis + // + void setResolution(Resolution x_resolution, Resolution y_resolution); + + // + // Get a full array of pixel values from a single frame. + // This disables navigation and overwrites any donwloaded firmware, + // so call to reset() is needed to restore them + // + // @param pixels The array where pixel values will be stored + // + void captureFrame(uint8_t pixels[NUMBER_OF_PIXELS_PER_FRAME]); + + // + // Member function invoked when motion has ocurred and if a motion pin + // was specified when the object constructor was called. + // By default it invokes the function specified by a previous call to attach() + // + virtual void motionTrigger() + { motionTrigger_.call(); } + + // + // Attach a function to call when a falling edge occurs on motion pin + // + // @param function A pointer to a function or 0 to set the attached function as none + // + void attach(void (*function)(void)) + { motionTrigger_.attach(function); } + + // + // Attach a member function to call when a falling edge occurs on motion pin + // + // @param object A reference to the object to call the member function on + // @param function A pointer to the member function to be called + // + template<typename T> + void attach(T& object, void (T::*member)(void)) + { motionTrigger_.attach(object, member); } + + private: + SPI spi_; + InterruptIn motion_; + DigitalOut ncs_; + + bool enabled_; + int dx_, dy_; + int xCpi_, yCpi_; + + FunctionPointer motionTrigger_; + }; +} + +#endif /* ADNS9500_HPP_ */ \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/main.cpp Mon Feb 13 11:39:24 2012 +0000 @@ -0,0 +1,8 @@ +#include "adns9500.hpp" +#include "mbed.h" + +adns9500::ADNS9500 sensor(); + +int main() +{ +}