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