use embassy_time::{Duration, Instant, Timer};
use embedded_hal::digital::{InputPin, OutputPin};
use embedded_hal_async::digital::Wait;
use embedded_hal_async::spi::SpiBus;
pub use crate::driver::bitbang_spi::{BitBangError, BitBangSpiBus};
use crate::input_device::pointing::{InitState, MotionData, PointingDevice, PointingDriver, PointingDriverError};
const PMW3610_PROD_ID: u8 = 0x00;
const PMW3610_MOTION: u8 = 0x02;
const PMW3610_DELTA_XY_H: u8 = 0x05;
const PMW3610_PERFORMANCE: u8 = 0x11;
const PMW3610_BURST_READ: u8 = 0x12;
const PMW3610_RUN_DOWNSHIFT: u8 = 0x1b;
const PMW3610_REST1_RATE: u8 = 0x1c;
const PMW3610_REST1_DOWNSHIFT: u8 = 0x1d;
const PMW3610_OBSERVATION1: u8 = 0x2d;
const PMW3610_SMART_MODE: u8 = 0x32;
const PMW3610_POWER_UP_RESET: u8 = 0x3a;
const PMW3610_SPI_CLK_ON_REQ: u8 = 0x41;
const PMW3610_SPI_PAGE0: u8 = 0x7f;
const PMW3610_RES_STEP: u8 = 0x05;
const PMW3610_SPI_PAGE1: u8 = 0x7f;
const BURST_MOTION: usize = 0;
const BURST_DELTA_X_L: usize = 1;
const BURST_DELTA_Y_L: usize = 2;
const BURST_DELTA_XY_H: usize = 3;
const BURST_DELTA_X_H: usize = 4;
const BURST_SHUTTER_HI: usize = 5;
const BURST_SHUTTER_LO: usize = 6;
const BURST_DATA_LEN_NORMAL: usize = BURST_DELTA_XY_H + 1;
const BURST_DATA_LEN_SMART: usize = BURST_SHUTTER_LO + 1;
const OBSERVATION1_INIT_MASK: u8 = 0x0f;
const PERFORMANCE_INIT: u8 = 0x0d;
const RUN_DOWNSHIFT_INIT: u8 = 0x04;
const REST1_RATE_INIT: u8 = 0x04;
const REST1_DOWNSHIFT_INIT: u8 = 0x0f;
const PRODUCT_ID_PMW3610: u8 = 0x3e;
const SPI_WRITE: u8 = 0x80;
const MOTION_STATUS_MOTION: u8 = 0x80;
const SPI_CLOCK_ON_REQ_ON: u8 = 0xba;
const SPI_CLOCK_ON_REQ_OFF: u8 = 0xb5;
const RES_STEP_SWAP_XY_BIT: u8 = 7;
const RES_STEP_INV_X_BIT: u8 = 6;
const RES_STEP_INV_Y_BIT: u8 = 5;
const RES_STEP_RES_MASK: u8 = 0x1f;
const PERFORMANCE_FMODE_MASK: u8 = 0x0f << 4;
const PERFORMANCE_FMODE_NORMAL: u8 = 0x00 << 4;
const PERFORMANCE_FMODE_FORCE_AWAKE: u8 = 0x0f << 4;
const POWER_UP_RESET_VAL: u8 = 0x5a;
const SPI_PAGE0_1: u8 = 0xff;
const SPI_PAGE1_0: u8 = 0x00;
const SHUTTER_SMART_THRESHOLD: u16 = 45;
const SMART_MODE_ENABLE: u8 = 0x00;
const SMART_MODE_DISABLE: u8 = 0x80;
const PMW3610_DATA_SIZE_BITS: usize = 12;
const RESET_DELAY_MS: u64 = 10;
const INIT_OBSERVATION_DELAY_MS: u64 = 10;
const CLOCK_ON_DELAY_US: u64 = 300;
const T_NCS_SCLK_NS: u64 = 120;
const T_SRAD_US: u64 = 4;
const T_SRX_NS: u64 = 250;
const T_SWX_US: u64 = 30;
const T_SCLK_NCS_W_US: u64 = 10;
const T_SCLK_NCS_R_NS: u64 = 120;
const T_BEXIT_NS: u64 = 250;
const RES_STEP: u16 = 200;
const RES_MIN: u16 = 200;
const RES_MAX: u16 = 3200;
#[derive(Clone)]
pub struct Pmw3610Config {
pub res_cpi: i16,
pub invert_x: bool,
pub invert_y: bool,
pub swap_xy: bool,
pub force_awake: bool,
pub smart_mode: bool,
}
impl Default for Pmw3610Config {
fn default() -> Self {
Self {
res_cpi: -1,
invert_x: false,
invert_y: false,
swap_xy: false,
force_awake: false,
smart_mode: false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Pmw3610Error {
Spi,
InvalidProductId(u8),
InitFailed,
InvalidCpi,
}
impl From<Pmw3610Error> for PointingDriverError {
fn from(err: Pmw3610Error) -> Self {
match err {
Pmw3610Error::Spi => PointingDriverError::Spi,
Pmw3610Error::InvalidProductId(id) => PointingDriverError::InvalidProductId(id),
Pmw3610Error::InitFailed => PointingDriverError::InitFailed,
Pmw3610Error::InvalidCpi => PointingDriverError::InvalidCpi,
}
}
}
pub struct Pmw3610<SPI: SpiBus, CS: OutputPin, MOTION: InputPin + Wait> {
id: u8,
spi: SPI,
cs: CS,
motion_gpio: Option<MOTION>,
config: Pmw3610Config,
smart_flag: bool,
}
impl<SPI: SpiBus, CS: OutputPin, MOTION: InputPin + Wait> Pmw3610<SPI, CS, MOTION> {
pub fn new(id: u8, spi: SPI, cs: CS, motion_gpio: Option<MOTION>, config: Pmw3610Config) -> Self {
Self {
id,
spi,
cs,
motion_gpio,
config,
smart_flag: false,
}
}
async fn set_force_awake(&mut self, enable: bool) -> Result<(), PointingDriverError> {
let mut val = self.read_reg(PMW3610_PERFORMANCE).await?;
val &= !PERFORMANCE_FMODE_MASK;
if enable {
val |= PERFORMANCE_FMODE_FORCE_AWAKE;
} else {
val |= PERFORMANCE_FMODE_NORMAL;
}
self.spi_clk_on().await?;
self.write_reg(PMW3610_PERFORMANCE, val).await?;
self.spi_clk_off().await?;
Ok(())
}
async fn read_reg(&mut self, addr: u8) -> Result<u8, Pmw3610Error> {
let _ = self.cs.set_low();
Timer::after(Duration::from_nanos(T_NCS_SCLK_NS)).await;
self.spi.write(&[addr & 0x7f]).await.map_err(|_| Pmw3610Error::Spi)?;
Timer::after(Duration::from_micros(T_SRAD_US)).await;
let mut value = [0u8];
self.spi.read(&mut value).await.map_err(|_| Pmw3610Error::Spi)?;
Timer::after(Duration::from_nanos(T_SCLK_NCS_R_NS)).await;
let _ = self.cs.set_high();
Timer::after(Duration::from_nanos(T_SRX_NS)).await;
Ok(value[0])
}
async fn read_burst(&mut self, addr: u8, data: &mut [u8]) -> Result<(), Pmw3610Error> {
let _ = self.cs.set_low();
Timer::after(Duration::from_nanos(T_NCS_SCLK_NS)).await;
self.spi.write(&[addr & 0x7f]).await.map_err(|_| Pmw3610Error::Spi)?;
Timer::after(Duration::from_micros(T_SRAD_US)).await;
self.spi.read(data).await.map_err(|_| Pmw3610Error::Spi)?;
Timer::after(Duration::from_nanos(T_SCLK_NCS_R_NS)).await;
let _ = self.cs.set_high();
Timer::after(Duration::from_nanos(T_BEXIT_NS)).await;
Ok(())
}
async fn write_reg(&mut self, addr: u8, value: u8) -> Result<(), Pmw3610Error> {
let _ = self.cs.set_low();
Timer::after(Duration::from_nanos(T_NCS_SCLK_NS)).await;
self.spi
.write(&[addr | SPI_WRITE, value])
.await
.map_err(|_| Pmw3610Error::Spi)?;
Timer::after(Duration::from_micros(T_SCLK_NCS_W_US)).await;
let _ = self.cs.set_high();
Timer::after(Duration::from_micros(T_SWX_US)).await;
Ok(())
}
async fn spi_clk_on(&mut self) -> Result<(), Pmw3610Error> {
self.write_reg(PMW3610_SPI_CLK_ON_REQ, SPI_CLOCK_ON_REQ_ON).await?;
Timer::after(Duration::from_micros(CLOCK_ON_DELAY_US)).await;
Ok(())
}
async fn spi_clk_off(&mut self) -> Result<(), Pmw3610Error> {
self.write_reg(PMW3610_SPI_CLK_ON_REQ, SPI_CLOCK_ON_REQ_OFF).await
}
async fn configure(&mut self) -> Result<(), Pmw3610Error> {
self.write_reg(PMW3610_POWER_UP_RESET, POWER_UP_RESET_VAL).await?;
Timer::after(Duration::from_millis(RESET_DELAY_MS)).await;
let val = self.read_reg(PMW3610_PROD_ID).await?;
if val != PRODUCT_ID_PMW3610 {
error!("Invalid product id: {:#02x}", val);
return Err(Pmw3610Error::InvalidProductId(val));
}
info!("PMW3610 detected, product ID: {:#02x}", val);
self.spi_clk_on().await?;
self.write_reg(PMW3610_OBSERVATION1, 0).await?;
Timer::after(Duration::from_millis(INIT_OBSERVATION_DELAY_MS)).await;
let val = self.read_reg(PMW3610_OBSERVATION1).await?;
if (val & OBSERVATION1_INIT_MASK) != OBSERVATION1_INIT_MASK {
error!("Unexpected OBSERVATION1 value: {:#02x}", val);
return Err(Pmw3610Error::InitFailed);
}
for reg in PMW3610_MOTION..=PMW3610_DELTA_XY_H {
self.read_reg(reg).await?;
}
self.write_reg(PMW3610_PERFORMANCE, PERFORMANCE_INIT).await?;
self.write_reg(PMW3610_RUN_DOWNSHIFT, RUN_DOWNSHIFT_INIT).await?;
self.write_reg(PMW3610_REST1_RATE, REST1_RATE_INIT).await?;
self.write_reg(PMW3610_REST1_DOWNSHIFT, REST1_DOWNSHIFT_INIT).await?;
self.write_reg(PMW3610_SPI_PAGE0, SPI_PAGE0_1).await?;
let mut res_step_val = self.read_reg(PMW3610_RES_STEP).await?;
if self.config.swap_xy {
res_step_val |= 1 << RES_STEP_SWAP_XY_BIT;
} else {
res_step_val &= !(1 << RES_STEP_SWAP_XY_BIT);
}
if self.config.invert_x {
res_step_val |= 1 << RES_STEP_INV_X_BIT;
} else {
res_step_val &= !(1 << RES_STEP_INV_X_BIT);
}
if self.config.invert_y {
res_step_val |= 1 << RES_STEP_INV_Y_BIT;
} else {
res_step_val &= !(1 << RES_STEP_INV_Y_BIT);
}
self.write_reg(PMW3610_RES_STEP, res_step_val).await?;
self.write_reg(PMW3610_SPI_PAGE1, SPI_PAGE1_0).await?;
self.spi_clk_off().await?;
if self.config.res_cpi > 0 {
self.set_resolution(self.config.res_cpi as u16)
.await
.map_err(|_| Pmw3610Error::Spi)?;
}
self.set_force_awake(self.config.force_awake)
.await
.map_err(|_| Pmw3610Error::Spi)?;
info!("PMW3610 initialized successfully");
Ok(())
}
fn sign_extend(value: u16, bits: usize) -> i16 {
let sign_bit = 1 << bits;
if value & sign_bit != 0 {
(value | !((1 << (bits + 1)) - 1)) as i16
} else {
value as i16
}
}
}
impl<SPI, CS, MOTION> PointingDriver for Pmw3610<SPI, CS, MOTION>
where
SPI: SpiBus,
CS: OutputPin,
MOTION: InputPin + Wait,
{
type MOTION = MOTION;
async fn init(&mut self) -> Result<(), PointingDriverError> {
let _ = self.cs.set_high();
Timer::after(Duration::from_millis(1)).await;
self.configure().await?;
Ok(())
}
async fn read_motion(&mut self) -> Result<MotionData, PointingDriverError> {
let burst_data_len = if self.config.smart_mode {
BURST_DATA_LEN_SMART
} else {
BURST_DATA_LEN_NORMAL
};
let mut burst_data = [0u8; BURST_DATA_LEN_SMART];
self.read_burst(PMW3610_BURST_READ, &mut burst_data[..burst_data_len])
.await?;
if (burst_data[BURST_MOTION] & MOTION_STATUS_MOTION) == 0x00 {
return Ok(MotionData::default());
}
let x = ((burst_data[BURST_DELTA_XY_H] as u16) << 4) & 0xf00 | (burst_data[BURST_DELTA_X_L] as u16);
let y = ((burst_data[BURST_DELTA_XY_H] as u16) << 8) & 0xf00 | (burst_data[BURST_DELTA_Y_L] as u16);
let dx = Self::sign_extend(x, PMW3610_DATA_SIZE_BITS - 1);
let dy = Self::sign_extend(y, PMW3610_DATA_SIZE_BITS - 1);
if self.config.smart_mode {
let shutter_val = ((burst_data[BURST_SHUTTER_HI] as u16) << 8) | (burst_data[BURST_SHUTTER_LO] as u16);
if self.smart_flag && shutter_val < SHUTTER_SMART_THRESHOLD {
self.spi_clk_on().await?;
self.write_reg(PMW3610_SMART_MODE, SMART_MODE_ENABLE)
.await
.map_err(|_| PointingDriverError::Spi)?;
self.spi_clk_off().await?;
self.smart_flag = false;
} else if !self.smart_flag && shutter_val > SHUTTER_SMART_THRESHOLD {
self.spi_clk_on().await?;
self.write_reg(PMW3610_SMART_MODE, SMART_MODE_DISABLE)
.await
.map_err(|_| PointingDriverError::Spi)?;
self.spi_clk_off().await?;
self.smart_flag = true;
}
}
Ok(MotionData { dx, dy })
}
fn motion_pending(&mut self) -> bool {
match &mut self.motion_gpio {
Some(gpio) => gpio.is_low().unwrap_or(true),
None => true,
}
}
fn motion_gpio(&mut self) -> Option<&mut MOTION> {
self.motion_gpio.as_mut()
}
async fn set_resolution(&mut self, cpi: u16) -> Result<(), PointingDriverError> {
if !(RES_MIN..=RES_MAX).contains(&cpi) {
return Err(PointingDriverError::InvalidCpi);
}
self.spi_clk_on().await?;
self.write_reg(PMW3610_SPI_PAGE0, SPI_PAGE0_1).await?;
let mut val = self.read_reg(PMW3610_RES_STEP).await?;
val &= !RES_STEP_RES_MASK;
val |= (cpi / RES_STEP) as u8;
self.write_reg(PMW3610_RES_STEP, val).await?;
self.write_reg(PMW3610_SPI_PAGE1, SPI_PAGE1_0).await?;
self.spi_clk_off().await?;
debug!("PMW3610: Resolution set to {} CPI", cpi);
Ok(())
}
}
impl<SPI, CS, MOTION> PointingDevice<Pmw3610<SPI, CS, MOTION>>
where
SPI: SpiBus,
CS: OutputPin,
MOTION: InputPin + Wait,
{
const DEFAULT_POLL_INTERVAL_US: u64 = 500;
const DEFAULT_REPORT_HZ: u16 = 125;
pub fn new(id: u8, spi: SPI, cs: CS, motion_gpio: Option<MOTION>, sensor_config: Pmw3610Config) -> Self {
Self::with_poll_interval_and_report_hz(
id,
spi,
cs,
motion_gpio,
sensor_config,
Self::DEFAULT_POLL_INTERVAL_US,
Self::DEFAULT_REPORT_HZ,
)
}
pub fn with_report_hz(
id: u8,
spi: SPI,
cs: CS,
motion_gpio: Option<MOTION>,
sensor_config: Pmw3610Config,
report_hz: u16,
) -> Self {
Self::with_poll_interval_and_report_hz(
id,
spi,
cs,
motion_gpio,
sensor_config,
Self::DEFAULT_POLL_INTERVAL_US,
report_hz,
)
}
pub fn with_poll_interval(
id: u8,
spi: SPI,
cs: CS,
motion_gpio: Option<MOTION>,
sensor_config: Pmw3610Config,
poll_interval_us: u64,
) -> Self {
Self::with_poll_interval_and_report_hz(
id,
spi,
cs,
motion_gpio,
sensor_config,
poll_interval_us,
Self::DEFAULT_REPORT_HZ,
)
}
pub fn with_poll_interval_and_report_hz(
id: u8,
spi: SPI,
cs: CS,
motion_gpio: Option<MOTION>,
sensor_config: Pmw3610Config,
poll_interval_us: u64,
report_hz: u16,
) -> Self {
let report_interval = Duration::from_hz(report_hz as u64);
let poll_interval = Duration::from_micros(poll_interval_us).min(report_interval);
Self {
id,
sensor: Pmw3610::new(id, spi, cs, motion_gpio, sensor_config),
init_state: InitState::Pending,
poll_interval,
report_interval,
last_poll: Instant::MIN,
last_report: Instant::MIN,
accumulated_x: 0,
accumulated_y: 0,
}
}
}