pmw3901_ehal/
lib.rs

1/*
2Copyright (c) 2020 Todd Stellanova
3LICENSE: BSD3 (see LICENSE file)
4*/
5
6#![no_std]
7
8//!
9//! An embedded-hal no-std driver for the PMW3901 optical flow sensor.
10//!
11
12use embedded_hal as hal;
13use hal::digital::v2::OutputPin;
14use embedded_hal::blocking::delay::DelayMs;
15
16#[cfg(feature = "rttdebug")]
17use panic_rtt_core::rprintln;
18
19/// Errors in this crate
20#[derive(Debug)]
21pub enum Error<CommE, PinE> {
22    /// Sensor communication error
23    Comm(CommE),
24    /// Pin setting error
25    Pin(PinE),
26
27    /// Poor signal quality / insufficient light or texture
28    NoSignal,
29    /// Unrecognized chip ID
30    UnknownChipId,
31    /// Sensor not responding
32    Unresponsive,
33}
34
35pub struct PMW3901<SPI, CSN> {
36    /// the SPI port to use when communicating
37    spi: SPI,
38    /// the Chip Select pin (GPIO output) to use when communicating
39    csn: CSN,
40}
41
42impl<SPI, CSN, CommE, PinE> PMW3901<SPI, CSN>
43    where
44        SPI: hal::blocking::spi::Write<u8, Error = CommE>
45        + hal::blocking::spi::Transfer<u8, Error = CommE>,
46        CSN: OutputPin<Error = PinE>,
47{
48
49
50    pub fn new(spi: SPI, csn: CSN) -> Self {
51        let mut inst = Self { spi: spi, csn: csn };
52        //ensure that the device is initially deselected
53        let _ = inst.csn.set_high();
54        inst
55    }
56
57    /// Initialize this device
58    pub fn init(&mut self, delay_source: &mut impl DelayMs<u32>) -> Result<(), Error<CommE, PinE>> {
59        //perform a soft reset
60        self.register_write(Register::PowerUpReset as u8, 0x5A)?;
61        delay_source.delay_ms(3000);
62
63        // verify chip IDs
64        let cid = self.register_read(Register::ProductId)?;
65        let inv_cid = self.register_read(Register::InverseProductId)?;
66
67        if !(Self::PRODUCT_ID == cid && Self::INV_PRODUCT_ID == inv_cid) {
68            #[cfg(feature = "rttdebug")]
69            rprintln!("unknown cid 0x{:x} inv_cid 0x{:x} ", cid, inv_cid);
70            return Err(Error::UnknownChipId);
71        }
72
73        self.write_config_optimizations()?;
74        // send an initial to the sensor: this is allowed to fail (squal == 0)
75        let _ = self.get_motion();
76
77        Ok(())
78    }
79
80    /// Below this threshold we assume the quality of flow measurement is poor
81    const SQUAL_THRESHOLD: u8 = 5;
82    const DIR_READ: u8 = 0x7f;
83    const MOTION_READ_BLOCK: [u8; 12] = [
84        Self::DIR_READ & (Register::Motion as u8), 0,
85        Self::DIR_READ & (Register::DeltaXL as u8), 0,
86        Self::DIR_READ & (Register::DeltaXH as u8), 0,
87        Self::DIR_READ & (Register::DeltaYL as u8), 0,
88        Self::DIR_READ & (Register::DeltaYH as u8), 0,
89        Self::DIR_READ & (Register::Squal as u8), 0
90    ];
91
92    /// Main method for obtaining the (dx, dy) flow
93    pub fn get_motion(&mut self) -> Result< (i16, i16), Error<CommE, PinE>> {
94        //read the motion block all at once
95        let mut block: [u8; 12] = Self::MOTION_READ_BLOCK;
96        self.transfer_sequence(&mut block)?;
97        #[cfg(feature = "rttdebug")]
98        rprintln!("block: {:?}", block);
99
100        let squal = block[11];
101        if squal < Self::SQUAL_THRESHOLD {
102            return Err(Error::NoSignal);
103        }
104        let dx = ((block[5] as i16) << 8) | (block[3] as i16);
105        let dy = ((block[9] as i16) << 8) | (block[7] as i16);
106
107        Ok((dx, dy))
108    }
109
110    /// Read a single register's value
111    pub fn register_read(&mut self, reg: Register) -> Result<u8, Error<CommE, PinE>> {
112        let mut block: [u8; 2] = [reg as u8, 0];
113        self.transfer_sequence(&mut block)?;
114        // self.read_block(reg, &mut block)?;
115        Ok(block[1])
116    }
117
118    /// Write a value to a single register
119    pub fn register_write(&mut self, reg: u8, val: u8) -> Result<(), Error<CommE, PinE>> {
120        let block: [u8; 2] = [reg, val];
121        self.write_block(&block)?;
122        Ok(())
123    }
124
125    /// transfer a series of bytes to the sensor
126    fn transfer_sequence(&mut self,  buffer: &mut [u8]) -> Result<(), Error<CommE, PinE>> {
127        self.csn.set_low().map_err(Error::Pin)?;
128        let rc = self.spi.transfer(buffer);
129        self.csn.set_high().map_err(Error::Pin)?;
130        rc.map_err(Error::Comm)?;
131
132        Ok(())
133    }
134
135    /// Write a block to the device
136    fn write_block(&mut self, block: &[u8]) -> Result<(), Error<CommE, PinE>> {
137        #[cfg(feature = "rttdebug")]
138        rprintln!("write {:x?} ", block);
139
140        self.csn.set_low().map_err(Error::Pin)?;
141        let rc = self.spi.write(block);
142        self.csn.set_high().map_err(Error::Pin)?;
143        rc.map_err(Error::Comm)?;
144
145        Ok(())
146    }
147
148    /// Write a sequence to undocumented registers in order to optimize performance,
149    /// as advised by datasheet section 8.2.
150    fn write_config_optimizations(&mut self) -> Result<(), Error<CommE, PinE>> {
151        self.register_write(0x7F, 0x00)?;
152        self.register_write(0x61, 0xAD)?;
153        self.register_write(0x7F, 0x03)?;
154        self.register_write(0x40, 0x00)?;
155        self.register_write(0x7F, 0x05)?;
156        self.register_write(0x41, 0xB3)?;
157        self.register_write(0x43, 0xF1)?;
158        self.register_write(0x45, 0x14)?;
159        self.register_write(0x5B, 0x32)?;
160        self.register_write(0x5F, 0x34)?;
161        self.register_write(0x7B, 0x08)?;
162        self.register_write(0x7F, 0x06)?;
163        self.register_write(0x44, 0x1B)?;
164        self.register_write(0x40, 0xBF)?;
165        self.register_write(0x4E, 0x3F)?;
166
167        Ok(())
168    }
169
170    /// supported product IDs
171    const PRODUCT_ID: u8 = 0x49;
172    const INV_PRODUCT_ID: u8  = 0xB6;
173}
174
175
176
177#[repr(u8)]
178#[derive(Copy, Clone, Debug)]
179pub enum Register {
180    ProductId = 0x00,
181    RevisionId = 0x01,
182    Motion = 0x02,
183    DeltaXL = 0x03,
184    DeltaXH = 0x04,
185    DeltaYL = 0x05,
186    DeltaYH = 0x06,
187    Squal = 0x07,
188    RawDataSum = 0x08,
189    MaximumRawData = 0x09,
190    MinimumRawData = 0x0A,
191    ShutterLower = 0x0B,
192    ShutterUpper = 0x0C,
193    Observation = 0x15,
194    MotionBurst = 0x16,
195
196    PowerUpReset = 0x3A,
197    Shutdown = 0x3B,
198
199    RawDataGrab = 0x58,
200    RawDataGrabStatus = 0x59,
201
202    InverseProductId = 0x5F,
203}
204
205