Skip to content

Commit

Permalink
Improve optical sensor (PMW3360) driver
Browse files Browse the repository at this point in the history
* Improved register access timings
* Stabilized motion burst
* Makde SROM uploadable, to support high CPI setting
* Fix Keyball 61's develop keymap
  • Loading branch information
koron committed Mar 21, 2024
1 parent a09a281 commit 1f2d4ff
Show file tree
Hide file tree
Showing 7 changed files with 648 additions and 44 deletions.
55 changes: 48 additions & 7 deletions qmk_firmware/keyboards/keyball/drivers/pmw3360/pmw3360.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "quantum.h"
#include "pmw3360.h"

// Include SROM definitions.
#include "srom_0x04.c"
#include "srom_0x81.c"

#define PMW3360_SPI_MODE 3
#define PMW3360_SPI_DIVISOR (F_CPU / PMW3360_CLOCKS)
#define PMW3360_CLOCKS 2000000

static bool motion_bursting = false;

bool pmw3360_spi_start(void) {
return spi_start(PMW3360_NCS_PIN, false, PMW3360_SPI_MODE, PMW3360_SPI_DIVISOR);
}
Expand All @@ -31,17 +37,24 @@ uint8_t pmw3360_reg_read(uint8_t addr) {
spi_write(addr & 0x7f);
wait_us(160);
uint8_t data = spi_read();
wait_us(1);
spi_stop();
wait_us(20);
wait_us(19);
// Reset motion_bursting mode if read from a register other than motion
// burst register.
if (addr != pmw3360_Motion_Burst) {
motion_bursting = false;
}
return data;
}

void pmw3360_reg_write(uint8_t addr, uint8_t data) {
pmw3360_spi_start();
spi_write(addr | 0x80);
spi_write(data);
wait_us(35);
spi_stop();
wait_us(180);
wait_us(145);
}

uint8_t pmw3360_cpi_get(void) {
Expand Down Expand Up @@ -95,20 +108,24 @@ bool pmw3360_motion_burst(pmw3360_motion_t *d) {
#ifdef DEBUG_PMW3360_SCAN_RATE
pmw3360_scan_perf_task();
#endif
// Start motion burst if motion burst mode is not started.
if (!motion_bursting) {
pmw3360_reg_write(pmw3360_Motion_Burst, 0);
motion_bursting = true;
}

pmw3360_spi_start();
spi_write(pmw3360_Motion_Burst);
wait_us(35);
uint8_t mot = spi_read();
if ((mot & 0x88) != 0x80) {
spi_stop();
return false;
}
spi_read(); // skip MOT
spi_read(); // skip Observation
d->x = spi_read();
d->x |= spi_read() << 8;
d->y = spi_read();
d->y |= spi_read() << 8;
spi_stop();
// Required NCS in 500ns after motion burst.
wait_us(1);
return true;
}

Expand All @@ -133,3 +150,27 @@ bool pmw3360_init(void) {
spi_stop();
return pid == 0x42 && rev == 0x01;
}

uint8_t pmw3360_srom_id = 0;

void pmw3360_srom_upload(pmw3360_srom_t srom) {
pmw3360_reg_write(pmw3360_Config2, 0x00);
pmw3360_reg_write(pmw3360_SROM_Enable, 0x1d);
wait_us(10);
pmw3360_reg_write(pmw3360_SROM_Enable, 0x18);

// SROM upload (download for PMW3360) with burst mode
pmw3360_spi_start();
spi_write(pmw3360_SROM_Load_Burst | 0x80);
wait_us(15);
for (size_t i = 0; i < srom.len; i++) {
spi_write(pgm_read_byte(srom.data + i));
wait_us(15);
}
spi_stop();
wait_us(200);

pmw3360_srom_id = pmw3360_reg_read(pmw3360_SROM_ID);
pmw3360_reg_write(pmw3360_Config2, 0x00);
wait_ms(10);
}
89 changes: 56 additions & 33 deletions qmk_firmware/keyboards/keyball/drivers/pmw3360/pmw3360.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,46 +34,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
//#define DEBUG_PMW3360_SCAN_RATE

//////////////////////////////////////////////////////////////////////////////
// Top level API
// Types

/// pmw3360_init initializes PMW3360DM-T2QU module.
/// It will return true when succeeded, otherwise false.
bool pmw3360_init(void);
typedef struct {
const uint8_t *data;
size_t len;
} pmw3360_srom_t;

typedef struct {
int16_t x;
int16_t y;
} pmw3360_motion_t;

/// pmw3360_motion_read gets a motion data by Motion register.
/// This requires to write a dummy data to pmw3360_Motion register
/// just before.
bool pmw3360_motion_read(pmw3360_motion_t *d);

/// pmw3360_motion_burst gets a motion data by Motion_Burst command.
/// This requires to write a dummy data to pmw3360_Motion_Burst register
/// just before.
bool pmw3360_motion_burst(pmw3360_motion_t *d);

/// pmw3360_scan_rate_get gets count of scan in a last second.
/// This works only when DEBUG_PMW3360_SCAN_RATE is defined.
uint32_t pmw3360_scan_rate_get(void);

// TODO: document
uint8_t pmw3360_cpi_get(void);

// TODO: document
void pmw3360_cpi_set(uint8_t cpi);

//////////////////////////////////////////////////////////////////////////////
// Register operations

/// pmw3360_reg_write writes a value to a register.
void pmw3360_reg_write(uint8_t addr, uint8_t data);

/// pmw3360_reg_read reads a value from a register.
uint8_t pmw3360_reg_read(uint8_t addr);

typedef enum {
pmw3360_Product_ID = 0x00,
pmw3360_Revision_ID = 0x01,
Expand Down Expand Up @@ -130,6 +102,55 @@ enum {
pmw3360_MAXCPI = 0x77, // = 119: 12000 CPI
};

//////////////////////////////////////////////////////////////////////////////
// Exported values (touch carefully)

/// SROM ID, last uploaded. 0 means not uploaded yet.
extern uint8_t pmw3360_srom_id;

/// SROM 0x04
extern const pmw3360_srom_t pmw3360_srom_0x04;
/// SROM 0x81
extern const pmw3360_srom_t pmw3360_srom_0x81;

//////////////////////////////////////////////////////////////////////////////
// Top level API

/// pmw3360_init initializes PMW3360DM-T2QU module.
/// It will return true when succeeded, otherwise false.
bool pmw3360_init(void);

void pmw3360_srom_upload(pmw3360_srom_t srom);

/// pmw3360_motion_read gets a motion data by Motion register.
/// This requires to write a dummy data to pmw3360_Motion register
/// just before.
bool pmw3360_motion_read(pmw3360_motion_t *d);

/// pmw3360_motion_burst gets a motion data by Motion_Burst command.
/// This requires to write a dummy data to pmw3360_Motion_Burst register
/// just before.
bool pmw3360_motion_burst(pmw3360_motion_t *d);

/// pmw3360_scan_rate_get gets count of scan in a last second.
/// This works only when DEBUG_PMW3360_SCAN_RATE is defined.
uint32_t pmw3360_scan_rate_get(void);

// TODO: document
uint8_t pmw3360_cpi_get(void);

// TODO: document
void pmw3360_cpi_set(uint8_t cpi);

//////////////////////////////////////////////////////////////////////////////
// Register operations

/// pmw3360_reg_write writes a value to a register.
void pmw3360_reg_write(uint8_t addr, uint8_t data);

/// pmw3360_reg_read reads a value from a register.
uint8_t pmw3360_reg_read(uint8_t addr);

//////////////////////////////////////////////////////////////////////////////
// SPI operations

Expand All @@ -139,10 +160,12 @@ void inline pmw3360_spi_stop(void) {
spi_stop();
}

/// \deprecated use pmw3360_reg_write() instead of this.
spi_status_t inline pmw3360_spi_write(uint8_t data) {
return spi_write(data);
}

/// \deprecated use pmw3360_reg_read() instead of this.
spi_status_t inline pmw3360_spi_read(void) {
return spi_read();
}
Loading

0 comments on commit 1f2d4ff

Please sign in to comment.