2012-03-09 22:51:28 -04:00
/*
AP_OpticalFlow_ADNS3080 . cpp - ADNS3080 OpticalFlow Library for Ardupilot Mega
Code by Randy Mackay . DIYDrones . com
This library is free software ; you can redistribute it and / or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation ; either
version 2.1 of the License , or ( at your option ) any later version .
External ADNS3080 OpticalFlow is connected via Serial port 2 ( in SPI mode )
TXD2 = MOSI = pin PH1
RXD2 = MISO = pin PH0
XCK2 = SCK = pin PH2
Chip Select pin is PC4 ( 33 ) [ PH6 ( 9 ) ]
We are using the 16 clocks per conversion timming to increase efficiency ( fast )
The sampling frequency is 400 Hz ( Timer2 overflow interrupt )
So if our loop is at 50 Hz , our needed sampling freq should be 100 Hz , so
we have an 4 x oversampling and averaging .
Methods :
Init ( ) : Initialization of interrupts an Timers ( Timer2 overflow interrupt )
Read ( ) : Read latest values from OpticalFlow and store to x , y , surface_quality parameters
*/
# include "AP_OpticalFlow_ADNS3080.h"
# include "SPI.h"
# if defined(ARDUINO) && ARDUINO >= 100
# include "Arduino.h"
# else
# include "WProgram.h"
# endif
# define AP_SPI_TIMEOUT 1000
union NumericIntType
{
int intValue ;
unsigned int uintValue ;
byte byteValue [ 2 ] ;
} ;
// Constructors ////////////////////////////////////////////////////////////////
AP_OpticalFlow_ADNS3080 : : AP_OpticalFlow_ADNS3080 ( int cs_pin , int reset_pin ) : _cs_pin ( cs_pin ) , _reset_pin ( reset_pin )
{
num_pixels = ADNS3080_PIXELS_X ;
field_of_view = AP_OPTICALFLOW_ADNS3080_08_FOV ;
scaler = AP_OPTICALFLOW_ADNS3080_SCALER ;
}
// Public Methods //////////////////////////////////////////////////////////////
// init - initialise sensor
// initCommAPI parameter controls whether SPI interface is initialised (set to false if other devices are on the SPI bus and have already initialised the interface)
bool
AP_OpticalFlow_ADNS3080 : : init ( bool initCommAPI )
{
int retry = 0 ;
pinMode ( AP_SPI_DATAOUT , OUTPUT ) ;
pinMode ( AP_SPI_DATAIN , INPUT ) ;
pinMode ( AP_SPI_CLOCK , OUTPUT ) ;
pinMode ( _cs_pin , OUTPUT ) ;
if ( _reset_pin ! = 0 )
pinMode ( ADNS3080_RESET , OUTPUT ) ;
digitalWrite ( _cs_pin , HIGH ) ; // disable device (Chip select is active low)
// reset the device
reset ( ) ;
// start the SPI library:
if ( initCommAPI ) {
SPI . begin ( ) ;
}
// check the sensor is functioning
if ( retry < 3 ) {
if ( read_register ( ADNS3080_PRODUCT_ID ) = = 0x17 )
return true ;
2011-09-17 00:38:18 -03:00
retry + + ;
}
return false ;
2012-03-09 22:51:28 -04:00
}
//
// backup_spi_settings - checks current SPI settings (clock speed, etc), sets values to what we need
//
byte
AP_OpticalFlow_ADNS3080 : : backup_spi_settings ( )
{
// store current spi values
orig_spi_settings_spcr = SPCR & ( DORD | CPOL | CPHA ) ;
orig_spi_settings_spsr = SPSR & SPI2X ;
// set the values that we need
SPI . setBitOrder ( MSBFIRST ) ;
SPI . setDataMode ( SPI_MODE3 ) ;
SPI . setClockDivider ( SPI_CLOCK_DIV8 ) ; // sensor running at 2Mhz. this is it's maximum speed
return orig_spi_settings_spcr ;
}
// restore_spi_settings - restores SPI settings (clock speed, etc) to what their values were before the sensor used the bus
byte
AP_OpticalFlow_ADNS3080 : : restore_spi_settings ( )
{
byte temp ;
// restore SPSR
temp = SPSR ;
temp & = ~ SPI2X ;
temp | = orig_spi_settings_spsr ;
SPSR = temp ;
// restore SPCR
temp = SPCR ;
temp & = ~ ( DORD | CPOL | CPHA ) ; // zero out the important bits
temp | = orig_spi_settings_spcr ; // restore important bits
SPCR = temp ;
return temp ;
}
// Read a register from the sensor
byte
AP_OpticalFlow_ADNS3080 : : read_register ( byte address )
{
byte result = 0 , junk = 0 ;
backup_spi_settings ( ) ;
// take the chip select low to select the device
digitalWrite ( _cs_pin , LOW ) ;
// send the device the register you want to read:
junk = SPI . transfer ( address ) ;
// small delay
delayMicroseconds ( 50 ) ;
// send a value of 0 to read the first byte returned:
result = SPI . transfer ( 0x00 ) ;
// take the chip select high to de-select:
digitalWrite ( _cs_pin , HIGH ) ;
restore_spi_settings ( ) ;
return result ;
}
// write a value to one of the sensor's registers
void
AP_OpticalFlow_ADNS3080 : : write_register ( byte address , byte value )
{
byte junk = 0 ;
backup_spi_settings ( ) ;
// take the chip select low to select the device
digitalWrite ( _cs_pin , LOW ) ;
// send register address
junk = SPI . transfer ( address | 0x80 ) ;
// small delay
delayMicroseconds ( 50 ) ;
// send data
junk = SPI . transfer ( value ) ;
// take the chip select high to de-select:
digitalWrite ( _cs_pin , HIGH ) ;
restore_spi_settings ( ) ;
}
// reset sensor by holding a pin high (or is it low?) for 10us.
void
AP_OpticalFlow_ADNS3080 : : reset ( )
{
// return immediately if the reset pin is not defined
if ( _reset_pin = = 0 )
return ;
digitalWrite ( _reset_pin , HIGH ) ; // reset sensor
delayMicroseconds ( 10 ) ;
digitalWrite ( _reset_pin , LOW ) ; // return sensor to normal
}
// read latest values from sensor and fill in x,y and totals
bool
AP_OpticalFlow_ADNS3080 : : update ( )
{
byte motion_reg ;
surface_quality = ( unsigned int ) read_register ( ADNS3080_SQUAL ) ;
delayMicroseconds ( 50 ) ; // small delay
// check for movement, update x,y values
motion_reg = read_register ( ADNS3080_MOTION ) ;
_overflow = ( ( motion_reg & 0x10 ) ! = 0 ) ; // check if we've had an overflow
if ( ( motion_reg & 0x80 ) ! = 0 ) {
raw_dx = ( ( char ) read_register ( ADNS3080_DELTA_X ) ) ;
delayMicroseconds ( 50 ) ; // small delay
raw_dy = ( ( char ) read_register ( ADNS3080_DELTA_Y ) ) ;
_motion = true ;
} else {
raw_dx = 0 ;
raw_dy = 0 ;
}
last_update = millis ( ) ;
apply_orientation_matrix ( ) ;
return true ;
}
void
AP_OpticalFlow_ADNS3080 : : disable_serial_pullup ( )
{
byte regVal = read_register ( ADNS3080_EXTENDED_CONFIG ) ;
regVal = ( regVal | ADNS3080_SERIALNPU_OFF ) ;
delayMicroseconds ( 50 ) ; // small delay
write_register ( ADNS3080_EXTENDED_CONFIG , regVal ) ;
}
// get_led_always_on - returns true if LED is always on, false if only on when required
bool
AP_OpticalFlow_ADNS3080 : : get_led_always_on ( )
{
return ( ( read_register ( ADNS3080_CONFIGURATION_BITS ) & 0x40 ) > 0 ) ;
}
// set_led_always_on - set parameter to true if you want LED always on, otherwise false for only when required
void
AP_OpticalFlow_ADNS3080 : : set_led_always_on ( bool alwaysOn )
{
byte regVal = read_register ( ADNS3080_CONFIGURATION_BITS ) ;
2011-09-17 00:38:18 -03:00
regVal = ( regVal & 0xbf ) | ( alwaysOn < < 6 ) ;
2012-03-09 22:51:28 -04:00
delayMicroseconds ( 50 ) ; // small delay
write_register ( ADNS3080_CONFIGURATION_BITS , regVal ) ;
}
// returns resolution (either 400 or 1600 counts per inch)
int
AP_OpticalFlow_ADNS3080 : : get_resolution ( )
{
if ( ( read_register ( ADNS3080_CONFIGURATION_BITS ) & 0x10 ) = = 0 )
return 400 ;
else
return 1600 ;
}
// set parameter to 400 or 1600 counts per inch
void
AP_OpticalFlow_ADNS3080 : : set_resolution ( int resolution )
{
byte regVal = read_register ( ADNS3080_CONFIGURATION_BITS ) ;
if ( resolution = = ADNS3080_RESOLUTION_400 ) {
regVal & = ~ 0x10 ;
scaler = AP_OPTICALFLOW_ADNS3080_SCALER ;
} else if ( resolution = = ADNS3080_RESOLUTION_1600 ) {
regVal | = 0x10 ;
scaler = AP_OPTICALFLOW_ADNS3080_SCALER * 4 ;
}
delayMicroseconds ( 50 ) ; // small delay
write_register ( ADNS3080_CONFIGURATION_BITS , regVal ) ;
// this will affect conversion factors so update them
update_conversion_factors ( ) ;
}
// get_frame_rate_auto - return whether frame rate is set to "auto" or manual
bool
AP_OpticalFlow_ADNS3080 : : get_frame_rate_auto ( )
{
byte regVal = read_register ( ADNS3080_EXTENDED_CONFIG ) ;
2011-09-17 00:38:18 -03:00
if ( ( regVal & 0x01 ) ! = 0 ) {
2012-03-09 22:51:28 -04:00
return false ;
} else {
return true ;
}
}
// set_frame_rate_auto - set frame rate to auto (true) or manual (false)
void
AP_OpticalFlow_ADNS3080 : : set_frame_rate_auto ( bool auto_frame_rate )
{
byte regVal = read_register ( ADNS3080_EXTENDED_CONFIG ) ;
delayMicroseconds ( 50 ) ; // small delay
if ( auto_frame_rate = = true ) {
// set specific frame period
write_register ( ADNS3080_FRAME_PERIOD_MAX_BOUND_LOWER , 0xE0 ) ;
delayMicroseconds ( 50 ) ; // small delay
write_register ( ADNS3080_FRAME_PERIOD_MAX_BOUND_UPPER , 0x1A ) ;
delayMicroseconds ( 50 ) ; // small delay
// decide what value to update in extended config
regVal = ( regVal & ~ 0x01 ) ;
} else {
// decide what value to update in extended config
regVal = ( regVal & ~ 0x01 ) | 0x01 ;
}
write_register ( ADNS3080_EXTENDED_CONFIG , regVal ) ;
}
// get frame period
unsigned int
AP_OpticalFlow_ADNS3080 : : get_frame_period ( )
{
NumericIntType aNum ;
aNum . byteValue [ 1 ] = read_register ( ADNS3080_FRAME_PERIOD_UPPER ) ;
delayMicroseconds ( 50 ) ; // small delay
aNum . byteValue [ 0 ] = read_register ( ADNS3080_FRAME_PERIOD_LOWER ) ;
return aNum . uintValue ;
}
// set frame period
void
AP_OpticalFlow_ADNS3080 : : set_frame_period ( unsigned int period )
{
NumericIntType aNum ;
aNum . uintValue = period ;
// set frame rate to manual
set_frame_rate_auto ( false ) ;
delayMicroseconds ( 50 ) ; // small delay
// set specific frame period
write_register ( ADNS3080_FRAME_PERIOD_MAX_BOUND_LOWER , aNum . byteValue [ 0 ] ) ;
delayMicroseconds ( 50 ) ; // small delay
write_register ( ADNS3080_FRAME_PERIOD_MAX_BOUND_UPPER , aNum . byteValue [ 1 ] ) ;
}
unsigned int
AP_OpticalFlow_ADNS3080 : : get_frame_rate ( )
{
unsigned long clockSpeed = ADNS3080_CLOCK_SPEED ;
unsigned int rate = clockSpeed / get_frame_period ( ) ;
return rate ;
}
void
AP_OpticalFlow_ADNS3080 : : set_frame_rate ( unsigned int rate )
{
unsigned long clockSpeed = ADNS3080_CLOCK_SPEED ;
unsigned int period = ( unsigned int ) ( clockSpeed / ( unsigned long ) rate ) ;
set_frame_period ( period ) ;
}
// get_shutter_speed_auto - returns true if shutter speed is adjusted automatically, false if manual
bool
AP_OpticalFlow_ADNS3080 : : get_shutter_speed_auto ( )
{
byte regVal = read_register ( ADNS3080_EXTENDED_CONFIG ) ;
if ( ( regVal & 0x02 ) > 0 ) {
return false ;
} else {
return true ;
}
}
// set_shutter_speed_auto - set shutter speed to auto (true), or manual (false)
void
AP_OpticalFlow_ADNS3080 : : set_shutter_speed_auto ( bool auto_shutter_speed )
{
byte regVal = read_register ( ADNS3080_EXTENDED_CONFIG ) ;
delayMicroseconds ( 50 ) ; // small delay
if ( auto_shutter_speed ) {
// return shutter speed max to default
write_register ( ADNS3080_SHUTTER_MAX_BOUND_LOWER , 0x8c ) ;
delayMicroseconds ( 50 ) ; // small delay
write_register ( ADNS3080_SHUTTER_MAX_BOUND_UPPER , 0x20 ) ;
delayMicroseconds ( 50 ) ; // small delay
// determine value to put into extended config
2011-09-17 00:38:18 -03:00
regVal & = ~ 0x02 ;
2012-03-09 22:51:28 -04:00
} else {
// determine value to put into extended config
2011-09-17 00:38:18 -03:00
regVal | = 0x02 ;
2012-03-09 22:51:28 -04:00
}
write_register ( ADNS3080_EXTENDED_CONFIG , regVal ) ;
delayMicroseconds ( 50 ) ; // small delay
}
// get_shutter_speed_auto - returns true if shutter speed is adjusted automatically, false if manual
unsigned int
AP_OpticalFlow_ADNS3080 : : get_shutter_speed ( )
{
NumericIntType aNum ;
aNum . byteValue [ 1 ] = read_register ( ADNS3080_SHUTTER_UPPER ) ;
delayMicroseconds ( 50 ) ; // small delay
aNum . byteValue [ 0 ] = read_register ( ADNS3080_SHUTTER_LOWER ) ;
return aNum . uintValue ;
}
// set_shutter_speed_auto - set shutter speed to auto (true), or manual (false)
2011-09-17 00:38:18 -03:00
void
2012-03-09 22:51:28 -04:00
AP_OpticalFlow_ADNS3080 : : set_shutter_speed ( unsigned int shutter_speed )
{
NumericIntType aNum ;
aNum . uintValue = shutter_speed ;
// set shutter speed to manual
set_shutter_speed_auto ( false ) ;
delayMicroseconds ( 50 ) ; // small delay
// set specific shutter speed
write_register ( ADNS3080_SHUTTER_MAX_BOUND_LOWER , aNum . byteValue [ 0 ] ) ;
delayMicroseconds ( 50 ) ; // small delay
write_register ( ADNS3080_SHUTTER_MAX_BOUND_UPPER , aNum . byteValue [ 1 ] ) ;
delayMicroseconds ( 50 ) ; // small delay
// larger delay
delay ( 50 ) ;
// need to update frame period to cause shutter value to take effect
aNum . byteValue [ 1 ] = read_register ( ADNS3080_FRAME_PERIOD_UPPER ) ;
delayMicroseconds ( 50 ) ; // small delay
aNum . byteValue [ 0 ] = read_register ( ADNS3080_FRAME_PERIOD_LOWER ) ;
delayMicroseconds ( 50 ) ; // small delay
write_register ( ADNS3080_FRAME_PERIOD_MAX_BOUND_LOWER , aNum . byteValue [ 0 ] ) ;
delayMicroseconds ( 50 ) ; // small delay
write_register ( ADNS3080_FRAME_PERIOD_MAX_BOUND_UPPER , aNum . byteValue [ 1 ] ) ;
delayMicroseconds ( 50 ) ; // small delay
}
// clear_motion - will cause the Delta_X, Delta_Y, and internal motion registers to be cleared
void
AP_OpticalFlow_ADNS3080 : : clear_motion ( )
{
write_register ( ADNS3080_MOTION_CLEAR , 0xFF ) ; // writing anything to this register will clear the sensor's motion registers
x = 0 ;
y = 0 ;
dx = 0 ;
dy = 0 ;
_motion = false ;
}
// get_pixel_data - captures an image from the sensor and stores it to the pixe_data array
2011-09-17 00:38:18 -03:00
void
2012-03-09 22:51:28 -04:00
AP_OpticalFlow_ADNS3080 : : print_pixel_data ( Stream * serPort )
{
int i , j ;
bool isFirstPixel = true ;
byte regValue ;
byte pixelValue ;
// write to frame capture register to force capture of frame
write_register ( ADNS3080_FRAME_CAPTURE , 0x83 ) ;
// wait 3 frame periods + 10 nanoseconds for frame to be captured
delayMicroseconds ( 1510 ) ; // min frame speed is 2000 frames/second so 1 frame = 500 nano seconds. so 500 x 3 + 10 = 1510
// display the pixel data
for ( i = 0 ; i < ADNS3080_PIXELS_Y ; i + + ) {
for ( j = 0 ; j < ADNS3080_PIXELS_X ; j + + ) {
regValue = read_register ( ADNS3080_FRAME_CAPTURE ) ;
2011-09-17 00:38:18 -03:00
if ( isFirstPixel & & ( regValue & 0x40 ) = = 0 ) {
2012-03-09 22:51:28 -04:00
serPort - > println ( " failed to find first pixel " ) ;
}
isFirstPixel = false ;
pixelValue = ( regValue < < 2 ) ;
serPort - > print ( pixelValue , DEC ) ;
if ( j ! = ADNS3080_PIXELS_X - 1 )
serPort - > print ( " , " ) ;
delayMicroseconds ( 50 ) ;
}
serPort - > println ( ) ;
}
// hardware reset to restore sensor to normal operation
reset ( ) ;
}