forked from qmk/qmk_firmware
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
355 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
/* Copyright 2021 Colin Lam (Ploopy Corporation) | ||
* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <[email protected]> | ||
* Copyright 2019 Sunjun Kim | ||
* Copyright 2019 Hiroyuki Okada | ||
* | ||
* 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 | ||
* the Free Software Foundation, either version 2 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
#include "pmw3320.h" | ||
#include "wait.h" | ||
#include "debug.h" | ||
#include "gpio.h" | ||
|
||
void pmw3320_init(void) { | ||
// Initialize sensor serial pins. | ||
setPinOutput(PMW3320_SCLK_PIN); | ||
setPinOutput(PMW3320_SDIO_PIN); | ||
setPinOutput(PMW3320_CS_PIN); | ||
|
||
// reboot the sensor. | ||
pmw3320_write_reg(REG_Power_Up_Reset, 0x5a); | ||
|
||
// wait maximum time before sensor is ready. | ||
// this ensures that the sensor is actually ready after reset. | ||
wait_ms(55); | ||
|
||
// read a burst from the sensor and then discard it. | ||
// gets the sensor ready for write commands | ||
// (for example, setting the dpi). | ||
pmw3320_read_burst(); | ||
|
||
// Pretty sure that this shouldn't be in the driver. | ||
// Probably device specific? | ||
// Set rest mode to default | ||
pmw3320_write_reg(REG_Rest_Mode_Status, 0x00); | ||
// Set LED to be always on | ||
pmw3320_write_reg(REG_Led_Control, 0x4); | ||
// Disable rest mode | ||
pmw3320_write_reg(REG_Performance, 0x80); | ||
} | ||
|
||
// Perform a synchronization with sensor. | ||
// Just as with the serial protocol, this is used by the slave to send a | ||
// synchronization signal to the master. | ||
void pmw3320_sync(void) { | ||
writePinLow(PMW3320_CS_PIN); | ||
wait_us(1); | ||
writePinHigh(PMW3320_CS_PIN); | ||
} | ||
|
||
void pmw3320_cs_select(void) { | ||
writePinLow(PMW3320_CS_PIN); | ||
} | ||
|
||
void pmw3320_cs_deselect(void) { | ||
writePinHigh(PMW3320_CS_PIN); | ||
} | ||
|
||
uint8_t pmw3320_serial_read(void) { | ||
setPinInput(PMW3320_SDIO_PIN); | ||
uint8_t byte = 0; | ||
|
||
for (uint8_t i = 0; i < 8; ++i) { | ||
writePinLow(PMW3320_SCLK_PIN); | ||
wait_us(1); | ||
|
||
byte = (byte << 1) | readPin(PMW3320_SDIO_PIN); | ||
|
||
writePinHigh(PMW3320_SCLK_PIN); | ||
wait_us(1); | ||
} | ||
|
||
return byte; | ||
} | ||
|
||
void pmw3320_serial_write(uint8_t data) { | ||
setPinOutput(PMW3320_SDIO_PIN); | ||
|
||
for (int8_t b = 7; b >= 0; b--) { | ||
writePinLow(PMW3320_SCLK_PIN); | ||
|
||
if (data & (1 << b)) | ||
writePinHigh(PMW3320_SDIO_PIN); | ||
else | ||
writePinLow(PMW3320_SDIO_PIN); | ||
|
||
wait_us(2); | ||
|
||
writePinHigh(PMW3320_SCLK_PIN); | ||
} | ||
|
||
// This was taken from ADNS5050 driver. | ||
// There's no any info in PMW3320 datasheet about this... | ||
// tSWR. See page 15 of the ADNS5050 spec sheet. | ||
// Technically, this is only necessary if the next operation is an SDIO | ||
// read. This is not guaranteed to be the case, but we're being lazy. | ||
wait_us(4); | ||
|
||
// Note that tSWW is never necessary. All write operations require at | ||
// least 32us, which exceeds tSWW, so there's never a need to wait for it. | ||
} | ||
|
||
// Read a byte of data from a register on the sensor. | ||
uint8_t pmw3320_read_reg(uint8_t reg_addr) { | ||
pmw3320_cs_select(); | ||
|
||
pmw3320_serial_write(reg_addr); | ||
|
||
uint8_t byte = pmw3320_serial_read(); | ||
|
||
// This was taken directly from ADNS5050 driver... | ||
// tSRW & tSRR. See page 15 of the ADNS5050 spec sheet. | ||
// Technically, this is only necessary if the next operation is an SDIO | ||
// read or write. This is not guaranteed to be the case. | ||
// Honestly, this wait could probably be removed. | ||
wait_us(1); | ||
|
||
pmw3320_cs_deselect(); | ||
|
||
return byte; | ||
} | ||
|
||
void pmw3320_write_reg(uint8_t reg_addr, uint8_t data) { | ||
pmw3320_cs_select(); | ||
pmw3320_serial_write(0b10000000 | reg_addr); | ||
pmw3320_serial_write(data); | ||
pmw3320_cs_deselect(); | ||
} | ||
|
||
report_pmw3320_t pmw3320_read_burst(void) { | ||
pmw3320_cs_select(); | ||
|
||
report_pmw3320_t data; | ||
data.dx = 0; | ||
data.dy = 0; | ||
|
||
pmw3320_serial_write(REG_Motion_Burst); | ||
|
||
uint8_t x = pmw3320_serial_read(); | ||
uint8_t y = pmw3320_serial_read(); | ||
|
||
// Probably burst mode may include contents of delta_xy register, | ||
// which contain HI parts of x/y deltas, but I had no luck finding it. | ||
// Probably it's required to activate 12-bit mode to access this data. | ||
// So we end burst mode early to not read unneeded information. | ||
pmw3320_cs_deselect(); | ||
|
||
data.dx = convert_twoscomp(x); | ||
data.dy = convert_twoscomp(y); | ||
|
||
return data; | ||
} | ||
|
||
// Convert a two's complement byte from an unsigned data type into a signed | ||
// data type. | ||
int8_t convert_twoscomp(uint8_t data) { | ||
if ((data & 0x80) == 0x80) | ||
return -128 + (data & 0x7F); | ||
else | ||
return data; | ||
} | ||
|
||
uint16_t pmw3320_get_cpi(void) { | ||
uint8_t cpival = pmw3320_read_reg(REG_Resolution); | ||
// 0x1F is an inversion of 0x20 which is 0b100000 | ||
return (uint16_t)((cpival & 0x1F) * PMW3320_CPI_STEP); | ||
} | ||
|
||
void pmw3320_set_cpi(uint16_t cpi) { | ||
uint8_t cpival = constrain((cpi / PMW3320_CPI_STEP) - 1U, 0, (PMW3320_CPI_MAX / PMW3320_CPI_STEP) - 1U); | ||
// Fifth bit is probably a control bit. | ||
// PMW3320 datasheet don't have any info on this, so this is a pure guess. | ||
pmw3320_write_reg(REG_Resolution, 0x20 | cpival); | ||
} | ||
|
||
bool pmw3320_check_signature(void) { | ||
uint8_t pid = pmw3320_read_reg(REG_Product_ID); | ||
uint8_t pid2 = pmw3320_read_reg(REG_Inverse_Product_ID); | ||
|
||
return (pid == 0x3b && pid2 == 0xc4); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
/* Copyright 2021 Colin Lam (Ploopy Corporation) | ||
* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <[email protected]> | ||
* Copyright 2019 Sunjun Kim | ||
* Copyright 2019 Hiroyuki Okada | ||
* | ||
* 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 | ||
* the Free Software Foundation, either version 2 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <stdint.h> | ||
#include <stdbool.h> | ||
|
||
#define constrain(amt, low, high) ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt))) | ||
|
||
// Definitions for the PMW3320 serial line. | ||
#ifndef PMW3320_SCLK_PIN | ||
# ifdef POINTING_DEVICE_SCLK_PIN | ||
# define PMW3320_SCLK_PIN POINTING_DEVICE_SCLK_PIN | ||
# else | ||
# error "No clock pin defined -- missing POINTING_DEVICE_SCLK_PIN or PMW3320_SCLK_PIN" | ||
# endif | ||
#endif | ||
|
||
#ifndef PMW3320_SDIO_PIN | ||
# ifdef POINTING_DEVICE_SDIO_PIN | ||
# define PMW3320_SDIO_PIN POINTING_DEVICE_SDIO_PIN | ||
# else | ||
# error "No data pin defined -- missing POINTING_DEVICE_SDIO_PIN or PMW3320_SDIO_PIN" | ||
# endif | ||
#endif | ||
|
||
#ifndef PMW3320_CS_PIN | ||
# ifdef POINTING_DEVICE_CS_PIN | ||
# define PMW3320_CS_PIN POINTING_DEVICE_CS_PIN | ||
# else | ||
# error "No chip select pin defined -- missing POINTING_DEVICE_CS_PIN or PMW3320_CS_PIN define" | ||
# endif | ||
#endif | ||
|
||
typedef struct { | ||
int8_t dx; | ||
int8_t dy; | ||
} report_pmw3320_t; | ||
|
||
// A bunch of functions to implement the PMW3320-specific serial protocol. | ||
// Mostly taken from ADNS5050 driver. | ||
// Note that the "serial.h" driver is insufficient, because it does not | ||
// manually manipulate a serial clock signal. | ||
void pmw3320_init(void); | ||
void pmw3320_sync(void); | ||
uint8_t pmw3320_serial_read(void); | ||
void pmw3320_serial_write(uint8_t data); | ||
uint8_t pmw3320_read_reg(uint8_t reg_addr); | ||
void pmw3320_write_reg(uint8_t reg_addr, uint8_t data); | ||
report_pmw3320_t pmw3320_read_burst(void); | ||
void pmw3320_set_cpi(uint16_t cpi); | ||
uint16_t pmw3320_get_cpi(void); | ||
int8_t convert_twoscomp(uint8_t data); | ||
bool pmw3320_check_signature(void); | ||
|
||
#if !defined(PMW3320_CPI) | ||
# define PMW3320_CPI 1000 | ||
#endif | ||
|
||
#define PMW3320_CPI_STEP 250 | ||
#define PMW3320_CPI_MIN 250 | ||
#define PMW3320_CPI_MAX 3500 | ||
|
||
// PMW3320 register addresses | ||
// clang-format off | ||
#define REG_Product_ID 0x00 | ||
#define REG_Revision_ID 0x01 | ||
#define REG_Motion 0x02 | ||
#define REG_Delta_X 0x03 | ||
#define REG_Delta_Y 0x04 | ||
#define REG_SQUAL 0x05 | ||
#define REG_Shutter_Upper 0x06 | ||
#define REG_Shutter_Lower 0x07 | ||
#define REG_Maximum_Pixel 0x08 | ||
#define REG_Pixel_Accum 0x09 | ||
#define REG_Minimum_Pixel 0x0a | ||
#define REG_Pixel_Grab 0x0b | ||
#define REG_Delta_XY 0x0c | ||
#define REG_Resolution 0x0d | ||
#define REG_Run_Downshift 0x0e | ||
#define REG_Rest1_Period 0x0f | ||
#define REG_Rest1_Downshift 0x10 | ||
#define REG_Rest2_Preiod 0x11 | ||
#define REG_Rest2_Downshift 0x12 | ||
#define REG_Rest3_Period 0x13 | ||
#define REG_Min_SQ_Run 0x17 | ||
#define REG_Axis_Control 0x1a | ||
#define REG_Performance 0x22 | ||
#define REG_Low_Motion_Jitter 0x23 | ||
#define REG_Shutter_Max_HI 0x36 | ||
#define REG_Shutter_Max_LO 0x37 | ||
#define REG_Frame_Rate 0x39 | ||
#define REG_Power_Up_Reset 0x3a | ||
#define REG_Shutdown 0x3b | ||
#define REG_Inverse_Revision_ID 0x3f | ||
#define REG_Led_Control 0x40 | ||
#define REG_Motion_Control 0x41 | ||
#define REG_Burst_Read_First 0x42 | ||
#define REG_Rest_Mode_Status 0x45 | ||
#define REG_Inverse_Product_ID 0x4f | ||
#define REG_Motion_Burst 0x63 | ||
// clang-format on |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters