Skip to main content
RKTK API Docs RKTK Home Repo

rktk_drivers_common/mouse/pmw3360/
mod.rs

1//! PMW3360 optical sensor driver.
2
3#![allow(dead_code)]
4
5mod error;
6mod registers;
7mod srom_liftoff;
8mod srom_tracking;
9
10use embassy_time::Timer;
11use embedded_hal_async::spi::Operation;
12
13use error::Pmw3360Error;
14use registers as reg;
15use rktk::drivers::interface::mouse::MouseDriver;
16
17use crate::spi::{ExtendedSpi, IterOperation};
18
19mod timing {
20    /// NCS To SCLK Active
21    pub const NCS_SCLK: u32 = 120;
22}
23
24#[derive(Default, Debug)]
25#[cfg_attr(feature = "defmt", derive(defmt::Format))]
26pub struct BurstData {
27    pub motion: bool,
28    pub on_surface: bool,
29    pub op_mode: u8,
30    pub frame_data_first: bool,
31    pub dx: i16,
32    pub dy: i16,
33    pub surface_quality: u8,
34    pub raw_data_sum: u8,
35    pub max_raw_data: u8,
36    pub min_raw_data: u8,
37    pub shutter: u16,
38}
39
40#[derive(Debug, Default)]
41#[cfg_attr(feature = "defmt", derive(defmt::Format))]
42pub enum Pmw3360Srom {
43    #[default]
44    None,
45    Liftoff,
46    Tracking,
47}
48
49impl Pmw3360Srom {
50    fn id(&self) -> u8 {
51        match self {
52            Pmw3360Srom::None => 0x00,
53            Pmw3360Srom::Liftoff => 0x81,
54            Pmw3360Srom::Tracking => 0x04,
55        }
56    }
57}
58
59#[derive(Debug)]
60#[cfg_attr(feature = "defmt", derive(defmt::Format))]
61pub struct Pmw3360Config {
62    pub srom: Pmw3360Srom,
63    pub cpi: u16,
64    pub auto_reset: bool,
65}
66
67impl Default for Pmw3360Config {
68    fn default() -> Self {
69        Self { srom: Pmw3360Srom::default(), cpi: 1000, auto_reset: true }
70    }
71}
72
73/// PMW3360 optical sensor driver.
74///
75/// # Notice about SPI
76/// This driver requires [`ExtendedSpi`] trait for SPI communication instead of
77/// [`embedded_hal_async::spi::SpiDevice`].
78/// This is because the PMW3360 driver needs to perform streaming transfers with CS held low,
79///
80/// Although, you still can use any SPI device that implements
81/// [`embedded_hal_async::spi::SpiDevice`]. In that case, SROM download will be skipped.
82pub struct Pmw3360<S: ExtendedSpi> {
83    spi_device: S,
84    in_burst_mode: bool,
85    config: Pmw3360Config,
86}
87
88impl<S: ExtendedSpi> Pmw3360<S> {
89    pub fn new(spi_device: S, config: Pmw3360Config) -> Self {
90        Self { spi_device, in_burst_mode: false, config }
91    }
92}
93
94impl<S: ExtendedSpi> MouseDriver for Pmw3360<S> {
95    type Error = Pmw3360Error<S::Error>;
96
97    async fn init(&mut self) -> Result<(), Self::Error> {
98        self.power_up().await
99    }
100
101    async fn read(&mut self) -> Result<(i8, i8), Self::Error> {
102        self.burst_read().await.map(|data| (data.dx as i8, data.dy as i8))
103    }
104
105    async fn set_cpi(&mut self, cpi: u16) -> Result<(), Self::Error> {
106        self.set_cpi(cpi).await?;
107        Ok(())
108    }
109
110    async fn get_cpi(&mut self) -> Result<u16, Self::Error> {
111        Err(Self::Error::NotSupported)
112    }
113}
114
115impl<S: ExtendedSpi> Pmw3360<S> {
116    async fn write(&mut self, address: u8, data: u8) -> Result<(), <Self as MouseDriver>::Error> {
117        self.in_burst_mode = false;
118        self.spi_device
119            .transaction(&mut [
120                Operation::DelayNs(timing::NCS_SCLK),
121                // send adress of the register, with MSBit = 1 to indicate it's a write and send data
122                Operation::TransferInPlace(&mut [address | 0x80, data]),
123                // tSCLK-NCS (write)
124                Operation::DelayNs(35 * 1000),
125            ])
126            .await
127            .map_err(Pmw3360Error::Spi)?;
128
129        // tSWW/tSWR minus tSCLK-NCS (write)
130        Timer::after_micros(145).await;
131
132        Ok(())
133    }
134
135    async fn read(&mut self, address: u8) -> Result<u8, <Self as MouseDriver>::Error> {
136        self.in_burst_mode = false;
137        let mut buf = [0x00];
138        self.spi_device
139            .transaction(&mut [
140                Operation::DelayNs(timing::NCS_SCLK),
141                // send adress of the register, with MSBit = 0 to indicate it's a read
142                Operation::Write(&[address & 0x7f]),
143                // tSRAD
144                Operation::DelayNs(160 * 1000),
145                // read the data
146                Operation::Read(&mut buf),
147                // tSCLK-NCS (read)
148                Operation::DelayNs(120),
149            ])
150            .await
151            .map_err(Pmw3360Error::Spi)?;
152
153        //  tSRW/tSRR
154        Timer::after_micros(20).await;
155
156        Ok(buf[0])
157    }
158
159    pub async fn burst_read(&mut self) -> Result<BurstData, <Self as MouseDriver>::Error> {
160        if !self.in_burst_mode {
161            self.write(reg::MOTION_BURST, 0x00).await?;
162            self.in_burst_mode = true;
163        }
164
165        let mut data = [0u8; 12];
166
167        self.spi_device
168            .transaction(&mut [
169                Operation::DelayNs(timing::NCS_SCLK),
170                Operation::Write(&[reg::MOTION_BURST]),
171                // tSRAD-MOTBR
172                Operation::DelayNs(35 * 1000),
173                Operation::Read(&mut data),
174            ])
175            .await
176            .map_err(Pmw3360Error::Spi)?;
177
178        // tBEXIT
179        Timer::after_micros(1).await;
180
181        //combine the register values
182        let data = BurstData {
183            motion: (data[0] & 0x80) != 0,
184            on_surface: (data[0] & 0x08) == 0,
185            op_mode: (data[0] >> 1) & 0x03,
186            frame_data_first: data[0] & 0x01 != 0,
187            dx: ((data[3] as i16) << 8) | (data[2] as i16),
188            dy: ((data[5] as i16) << 8) | (data[4] as i16),
189            surface_quality: data[6],
190            raw_data_sum: data[7],
191            max_raw_data: data[8],
192            min_raw_data: data[9],
193            shutter: ((data[11] as u16) << 8) | (data[10] as u16),
194        };
195
196        // FIXME: This is a workaround for a phenomenon in which the PMW3360 sensor stacks with
197        // OP_MODE remaining at Rest1 and then stops moving.
198        // It is necessary to investigate whether this is hardware-specific or whether other programs are incorrect.
199        if data.motion
200            && data.op_mode == 0x01
201            && data.on_surface
202            && data.dx == 0
203            && data.dy == 0
204            && self.config.auto_reset
205        {
206            rktk_log::warn!("Invalid motion detected. Performing reset:\n{:?}", data);
207            self.power_up().await?;
208        }
209
210        Ok(data)
211    }
212
213    pub async fn set_cpi(&mut self, cpi: u16) -> Result<(), <Self as MouseDriver>::Error> {
214        self.config.cpi = cpi;
215        let val: u16;
216        if cpi < 100 {
217            val = 0
218        } else if cpi > 12000 {
219            val = 0x77
220        } else {
221            val = (cpi - 100) / 100;
222        }
223        self.write(reg::CONFIG_1, val as u8).await?;
224        Ok(())
225    }
226
227    pub async fn get_cpi(&mut self) -> Result<u16, <Self as MouseDriver>::Error> {
228        let val = self.read(reg::CONFIG_1).await.unwrap_or_default() as u16;
229        Ok((val + 1) * 100)
230    }
231
232    pub async fn check_signature(&mut self) -> Result<bool, <Self as MouseDriver>::Error> {
233        let srom = self.read(reg::SROM_ID).await.unwrap_or(0);
234        let pid = self.read(reg::PRODUCT_ID).await.unwrap_or(0);
235        let ipid = self.read(reg::INVERSE_PRODUCT_ID).await.unwrap_or(0);
236
237        Ok(srom == self.config.srom.id() && pid == 0x42 && ipid == 0xBD)
238    }
239
240    #[allow(dead_code)]
241    pub async fn self_test(&mut self) -> Result<bool, <Self as MouseDriver>::Error> {
242        self.write(reg::SROM_ENABLE, 0x15).await?;
243        Timer::after_micros(10000).await;
244
245        let u = self.read(reg::DATA_OUT_UPPER).await.unwrap_or(0); // should be 0xBE
246        let l = self.read(reg::DATA_OUT_LOWER).await.unwrap_or(0); // should be 0xEF
247
248        Ok(u == 0xBE && l == 0xEF)
249    }
250
251    async fn power_up(&mut self) -> Result<(), <Self as MouseDriver>::Error> {
252        let is_valid_signature = self.power_up_inner().await?;
253        if is_valid_signature {
254            self.set_cpi(self.config.cpi).await?;
255            Ok(())
256        } else {
257            Err(Pmw3360Error::InvalidSignature)
258        }
259    }
260
261    async fn power_up_inner(&mut self) -> Result<bool, <Self as MouseDriver>::Error> {
262        // reset spi port
263        self.spi_device.transaction(&mut []).await.map_err(Pmw3360Error::Spi)?;
264
265        // Write to reset register
266        self.write(reg::POWER_UP_RESET, 0x5A).await?;
267
268        // Wait at least 50ms
269        Timer::after_millis(100).await;
270
271        // read registers 0x02 to 0x06
272        self.read(reg::MOTION).await?;
273        self.read(reg::DELTA_X_L).await?;
274        self.read(reg::DELTA_X_H).await?;
275        self.read(reg::DELTA_Y_L).await?;
276        self.read(reg::DELTA_Y_H).await?;
277
278        // perform SROM download
279        self.srom_download().await?;
280
281        let is_valid_signature = self.check_signature().await.unwrap_or(false);
282
283        // Write 0x00 (rest disable) to Config2 register for wired mouse or 0x20 for
284        // wireless mouse design.
285        self.write(reg::CONFIG_2, 0x00).await?;
286
287        Timer::after_micros(100).await;
288
289        Ok(is_valid_signature)
290    }
291
292    async fn srom_download(&mut self) -> Result<(), <Self as MouseDriver>::Error> {
293        let fw = match self.config.srom {
294            Pmw3360Srom::None => {
295                rktk_log::info!("SROM download skipped: no SROM selected");
296                return Ok(());
297            }
298            Pmw3360Srom::Liftoff => &srom_liftoff::FW,
299            Pmw3360Srom::Tracking => &srom_tracking::FW,
300        };
301        if !self.spi_device.transaction_iter_supported() {
302            rktk_log::warn!("SROM download skipped: transaction_iter not supported");
303            return Ok(());
304        }
305
306        // Write 0 to Rest_En bit of Config2 register to disable Rest mode.
307        self.write(reg::CONFIG_2, 0x00).await?;
308
309        // write 0x1d in SROM_enable reg for initializing
310        self.write(reg::SROM_ENABLE, 0x1d).await?;
311
312        // wait for 10 ms
313        Timer::after_micros(10000).await;
314
315        // Write 0x18 to SROM_Enable register again to start SROM Download
316        self.write(reg::SROM_ENABLE, 0x18).await?;
317
318        let operations = [IterOperation::Write(reg::SROM_LOAD_BURST | 0x80)].into_iter().chain(
319            fw.iter()
320                .flat_map(|byte| [IterOperation::Write(*byte), IterOperation::DelayNs(15 * 1000)]),
321        );
322
323        self.spi_device.transaction_iter(operations).await.map_err(Pmw3360Error::Spi)?;
324
325        Timer::after_micros(185).await;
326
327        let srom_id = self.read(reg::SROM_ID).await?;
328        if srom_id != self.config.srom.id() {
329            rktk_log::error!("SROM Download failed. ID: 0x{:02x}", srom_id);
330            return Err(Pmw3360Error::InvalidSignature);
331        }
332        rktk_log::debug!("SROM Download succeeded. ID: 0x{:02x}", srom_id);
333
334        Ok(())
335    }
336}