@ -1,6 +1,7 @@
/* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
* Copyright 2019 Sunjun Kim
* Copyright 2020 Ploopy Corporation
* Copyright 2022 Ulrich Spörlein
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
@ -83,7 +84,11 @@
# define MAX_CPI 0x77
# endif
bool _inBurst = false ;
static const pin_t pins [ ] = PMW3360_CS_PINS ;
# define NUMBER_OF_SENSORS (sizeof(pins) / sizeof(pin_t))
// per-sensor driver state
static bool _inBurst [ NUMBER_OF_SENSORS ] = { 0 } ;
# ifdef CONSOLE_ENABLE
void print_byte ( uint8_t byte ) {
@ -92,18 +97,18 @@ void print_byte(uint8_t byte) {
# endif
# define constrain(amt, low, high) ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt)))
bool pmw3360_spi_start ( void ) {
bool status = spi_start ( PMW3360_CS_PIN , PMW3360_SPI_LSBFIRST , PMW3360_SPI_MODE , PMW3360_SPI_DIVISOR ) ;
bool pmw3360_spi_start ( int8_t index ) {
bool status = spi_start ( pins [ index ] , PMW3360_SPI_LSBFIRST , PMW3360_SPI_MODE , PMW3360_SPI_DIVISOR ) ;
// tNCS-SCLK, 120ns
wait_us ( 1 ) ;
return status ;
}
spi_status_t pmw3360_write ( uint8_t reg_addr , uint8_t data ) {
pmw3360_spi_start ( ) ;
spi_status_t pmw3360_write ( int8_t index , uint8_t reg_addr , uint8_t data ) {
pmw3360_spi_start ( index ) ;
if ( reg_addr ! = REG_Motion_Burst ) {
_inBurst = false ;
_inBurst [ index ] = false ;
}
// send address of the register, with MSBit = 1 to indicate it's a write
@ -114,13 +119,13 @@ spi_status_t pmw3360_write(uint8_t reg_addr, uint8_t data) {
wait_us ( 35 ) ;
spi_stop ( ) ;
// tSWW/tSWR (=180us) minus tSCLK-NCS. Could be shortened, but is looks like a safe lower bound
// tSWW/tSWR (=180us) minus tSCLK-NCS. Could be shortened, but it looks like a safe lower bound
wait_us ( 145 ) ;
return status ;
}
uint8_t pmw3360_read ( uint8_t reg_addr ) {
pmw3360_spi_start ( ) ;
uint8_t pmw3360_read ( int8_t index , uint8_t reg_addr ) {
pmw3360_spi_start ( index ) ;
// send adress of the register, with MSBit = 0 to indicate it's a read
spi_write ( reg_addr & 0x7f ) ;
// tSRAD (=160us)
@ -136,36 +141,62 @@ uint8_t pmw3360_read(uint8_t reg_addr) {
return data ;
}
bool pmw3360_init ( void ) {
setPinOutput ( PMW3360_CS_PIN ) ;
bool pmw3360_check_signature ( int8_t index ) {
uint8_t pid = pmw3360_read ( index , REG_Product_ID ) ;
uint8_t iv_pid = pmw3360_read ( index , REG_Inverse_Product_ID ) ;
uint8_t SROM_ver = pmw3360_read ( index , REG_SROM_ID ) ;
return ( pid = = firmware_signature [ 0 ] & & iv_pid = = firmware_signature [ 1 ] & & SROM_ver = = firmware_signature [ 2 ] ) ; // signature for SROM 0x04
}
spi_init ( ) ;
_inBurst = false ;
void pmw3360_upload_firmware ( int8_t index ) {
// Datasheet claims we need to disable REST mode first, but during startup
// it's already disabled and we're not turning it on ...
// pmw3360_write(index, REG_Config2, 0x00); // disable REST mode
pmw3360_write ( index , REG_SROM_Enable , 0x1d ) ;
spi_stop ( ) ;
pmw3360_spi_start ( ) ;
spi_stop ( ) ;
wait_ms ( 10 ) ;
pmw3360_write ( index , REG_SROM_Enable , 0x18 ) ;
pmw3360_spi_start ( index ) ;
spi_write ( REG_SROM_Load_Burst | 0x80 ) ;
wait_us ( 15 ) ;
pmw3360_write ( REG_Shutdown , 0xb6 ) ; // Shutdown first
wait_ms ( 300 ) ;
for ( uint16_t i = 0 ; i < FIRMWARE_LENGTH ; i + + ) {
spi_write ( pgm_read_byte ( firmware_data + i ) ) ;
# ifndef PMW3360_FIRMWARE_UPLOAD_FAST
wait_us ( 15 ) ;
# endif
}
wait_us ( 200 ) ;
pmw3360_spi_start ( ) ;
pmw3360_read ( index , REG_SROM_ID ) ;
pmw3360_write ( index , REG_Config2 , 0x00 ) ;
}
bool pmw3360_init ( int8_t index ) {
if ( index > = NUMBER_OF_SENSORS ) {
return false ;
}
spi_init ( ) ;
// power up, need to first drive NCS high then low.
// the datasheet does not say for how long, 40us works well in practice.
pmw3360_spi_start ( index ) ;
wait_us ( 40 ) ;
spi_stop ( ) ;
wait_us ( 40 ) ;
// power up, need to first drive NCS high then low, see above.
pmw3360_write ( REG_Power_Up_Reset , 0x5a ) ;
pmw3360_write ( index , REG_Power_Up_Reset , 0x5a ) ;
wait_ms ( 50 ) ;
// read registers and discard
pmw3360_read ( REG_Motion ) ;
pmw3360_read ( REG_Delta_X_L ) ;
pmw3360_read ( REG_Delta_X_H ) ;
pmw3360_read ( REG_Delta_Y_L ) ;
pmw3360_read ( REG_Delta_Y_H ) ;
pmw3360_read ( index , REG_Motion ) ;
pmw3360_read ( index , REG_Delta_X_L ) ;
pmw3360_read ( index , REG_Delta_X_H ) ;
pmw3360_read ( index , REG_Delta_Y_L ) ;
pmw3360_read ( index , REG_Delta_Y_H ) ;
pmw3360_upload_firmware ( ) ;
pmw3360_upload_firmware ( index ) ;
spi_stop ( ) ;
@ -174,13 +205,13 @@ bool pmw3360_init(void) {
wait_ms ( 1 ) ;
pmw3360_write ( REG_Config2 , 0x00 ) ;
pmw3360_write ( index , REG_Config2 , 0x00 ) ;
pmw3360_write ( REG_Angle_Tune , constrain ( ROTATIONAL_TRANSFORM_ANGLE , - 127 , 127 ) ) ;
pmw3360_write ( index , REG_Angle_Tune , constrain ( ROTATIONAL_TRANSFORM_ANGLE , - 127 , 127 ) ) ;
pmw3360_write ( REG_Lift_Config , PMW3360_LIFTOFF_DISTANCE ) ;
pmw3360_write ( index , REG_Lift_Config , PMW3360_LIFTOFF_DISTANCE ) ;
bool init_success = pmw3360_check_signature ( ) ;
bool init_success = pmw3360_check_signature ( index ) ;
# ifdef CONSOLE_ENABLE
if ( init_success ) {
dprintf ( " pmw3360 signature verified " ) ;
@ -189,66 +220,38 @@ bool pmw3360_init(void) {
}
# endif
writePinLow ( PMW3360_CS_PIN ) ;
return init_success ;
}
void pmw3360_upload_firmware ( void ) {
// Datasheet claims we need to disable REST mode first, but during startup
// it's already disabled and we're not turning it on ...
// pmw3360_write(REG_Config2, 0x00); // disable REST mode
pmw3360_write ( REG_SROM_Enable , 0x1d ) ;
wait_ms ( 10 ) ;
pmw3360_write ( REG_SROM_Enable , 0x18 ) ;
pmw3360_spi_start ( ) ;
spi_write ( REG_SROM_Load_Burst | 0x80 ) ;
wait_us ( 15 ) ;
for ( uint16_t i = 0 ; i < FIRMWARE_LENGTH ; i + + ) {
spi_write ( pgm_read_byte ( firmware_data + i ) ) ;
# ifndef PMW3360_FIRMWARE_UPLOAD_FAST
wait_us ( 15 ) ;
# endif
}
wait_us ( 200 ) ;
pmw3360_read ( REG_SROM_ID ) ;
pmw3360_write ( REG_Config2 , 0x00 ) ;
}
bool pmw3360_check_signature ( void ) {
uint8_t pid = pmw3360_read ( REG_Product_ID ) ;
uint8_t iv_pid = pmw3360_read ( REG_Inverse_Product_ID ) ;
uint8_t SROM_ver = pmw3360_read ( REG_SROM_ID ) ;
return ( pid = = firmware_signature [ 0 ] & & iv_pid = = firmware_signature [ 1 ] & & SROM_ver = = firmware_signature [ 2 ] ) ; // signature for SROM 0x04
}
// Only support reading the value from sensor #0, no one is using this anyway.
uint16_t pmw3360_get_cpi ( void ) {
uint8_t cpival = pmw3360_read ( REG_Config1 ) ;
uint8_t cpival = pmw3360_read ( 0 , REG_Config1 ) ;
return ( uint16_t ) ( ( cpival + 1 ) & 0xFF ) * CPI_STEP ;
}
// Write same CPI to all sensors.
void pmw3360_set_cpi ( uint16_t cpi ) {
uint8_t cpival = constrain ( ( cpi / CPI_STEP ) - 1 , 0 , MAX_CPI ) ;
pmw3360_write ( REG_Config1 , cpival ) ;
for ( size_t i = 0 ; i < NUMBER_OF_SENSORS ; i + + ) {
pmw3360_write ( i , REG_Config1 , cpival ) ;
}
}
report_pmw3360_t pmw3360_read_burst ( void ) {
report_pmw3360_t pmw3360_read_burst ( int8_t index ) {
report_pmw3360_t report = { 0 } ;
if ( index > = NUMBER_OF_SENSORS ) {
return report ;
}
if ( ! _inBurst ) {
if ( ! _inBurst [ index ] ) {
# ifdef CONSOLE_ENABLE
dprintf ( " burst on " ) ;
dprintf ( " burst on for index %d " , index ) ;
# endif
pmw3360_write ( REG_Motion_Burst , 0x00 ) ;
_inBurst = true ;
pmw3360_write ( index , REG_Motion_Burst , 0x00 ) ;
_inBurst [ index ] = true ;
}
pmw3360_spi_start ( ) ;
pmw3360_spi_start ( index ) ;
spi_write ( REG_Motion_Burst ) ;
wait_us ( 35 ) ; // waits for tSRAD_MOTBR
@ -261,7 +264,7 @@ report_pmw3360_t pmw3360_read_burst(void) {
report . mdy = spi_read ( ) ;
if ( report . motion & 0 b111 ) { // panic recovery, sometimes burst mode works weird.
_inBurst = false ;
_inBurst [ index ] = false ;
}
spi_stop ( ) ;