pimoroni_trackball_driver/
lib.rs

1#![no_std]
2
3//! I2C interface for pimoroni trackball.
4
5use core::convert::Infallible;
6
7use embedded_hal::blocking::delay::DelayUs;
8use embedded_hal::blocking::i2c::{Write, WriteRead};
9use embedded_hal::digital::v2::InputPin;
10use embedded_time::duration::Microseconds;
11use embedded_time::{Clock, TimeError};
12use private::Sealed;
13
14/// I2C communication interface
15pub struct I2CInterface<I2C> {
16    i2c: I2C,
17    addr: u8,
18}
19
20impl<I> I2CInterface<I>
21where
22    I: Write,
23{
24    /// Create new I2C interface with a default I2C address of 0x0A.
25    pub fn new(i2c: I) -> Self {
26        Self::new_custom_address(i2c, 0x0A)
27    }
28
29    /// Create a new I2C interface with the alternate address 0x0B as specified in the datasheet.
30    pub fn new_alternate_address(i2c: I) -> Self {
31        Self::new_custom_address(i2c, 0x0B)
32    }
33
34    /// Create a new I2C interface with a custom address.
35    pub fn new_custom_address(i2c: I, addr: u8) -> Self {
36        Self { i2c, addr }
37    }
38
39    /// Consume the trackball interface and return
40    /// the underlying peripherial driver
41    pub fn release(self) -> I {
42        self.i2c
43    }
44}
45
46const SWITCH_STATE_MASK: u8 = 0b1000_0000;
47
48impl<I2C> I2CInterface<I2C>
49where
50    I2C: Write<Error = <I2C as WriteRead>::Error> + WriteRead,
51{
52    fn chip_id(&mut self) -> Result<u16, <I2C as Write>::Error> {
53        let mut bytes = [0u8; 2];
54        self.i2c.write_read(self.addr, &[CHIP_ID_L], &mut bytes)?;
55        let chip_id: u16 = u16::from_ne_bytes(bytes);
56        // let mut chip_id = (*unsafe { byte.get_unchecked(0) } as u16) << 8;
57        // self.i2c.write_read(self.addr, &[CHIP_ID_L], &mut byte)?;
58        // chip_id |= (*unsafe { byte.get_unchecked(0) }) as u16;
59        Ok(chip_id)
60    }
61
62    fn set_red(&mut self, val: u8) -> Result<(), <I2C as Write>::Error> {
63        self.i2c.write(self.addr, &[RED_REGISTER, val])
64    }
65
66    fn set_green(&mut self, val: u8) -> Result<(), <I2C as Write>::Error> {
67        self.i2c.write(self.addr, &[GREEN_REGISTER, val])
68    }
69
70    fn set_blue(&mut self, val: u8) -> Result<(), <I2C as Write>::Error> {
71        self.i2c.write(self.addr, &[BLUE_REGISTER, val])
72    }
73
74    fn set_white(&mut self, val: u8) -> Result<(), <I2C as Write>::Error> {
75        self.i2c.write(self.addr, &[WHITE_REGISTER, val])
76    }
77
78    fn get_interrupt(&mut self) -> Result<bool, <I2C as Write>::Error> {
79        let mut val = 0;
80        self.i2c.write_read(
81            self.addr,
82            &[WHITE_REGISTER],
83            core::slice::from_mut(&mut val),
84        )?;
85        Ok((val & INTERRUPT_TRIGGERED_MASK) > 0)
86    }
87}
88
89impl<I2C> I2CInterface<I2C>
90where
91    I2C: WriteRead,
92{
93    fn read(&mut self) -> Result<TrackballData, I2C::Error> {
94        let mut left = 0;
95        let mut right = 0;
96        let mut up = 0;
97        let mut down = 0;
98        let mut switch_state = 0;
99        self.i2c
100            .write_read(self.addr, &[LEFT], core::slice::from_mut(&mut left))?;
101        self.i2c
102            .write_read(self.addr, &[RIGHT], core::slice::from_mut(&mut right))?;
103        self.i2c
104            .write_read(self.addr, &[UP], core::slice::from_mut(&mut up))?;
105        self.i2c
106            .write_read(self.addr, &[DOWN], core::slice::from_mut(&mut down))?;
107        self.i2c.write_read(
108            self.addr,
109            &[SWITCH],
110            core::slice::from_mut(&mut switch_state),
111        )?;
112        let switch_changed = (switch_state & !SWITCH_STATE_MASK) > 0;
113        let switch_pressed = (switch_state & SWITCH_STATE_MASK) > 0;
114
115        Ok(TrackballData {
116            left,
117            right,
118            up,
119            down,
120            switch_changed,
121            switch_pressed,
122        })
123    }
124}
125
126const CHIP_ID: u16 = 0xBA11;
127#[allow(dead_code)]
128const VERSION: u8 = 1;
129const DEFAULT_TIMEOUT_US: u32 = 5;
130
131/// Data read from the trackball.
132#[derive(Copy, Clone, Debug)]
133pub struct TrackballData {
134    /// How many units the trackball was moved to the left.
135    pub left: u8,
136    /// How many units the trackball was moved to the right.
137    pub right: u8,
138    /// How many units the trackball was moved up.
139    pub up: u8,
140    /// How many units the trackball was moved down.
141    pub down: u8,
142    /// Whether or not the switch state has changed from the last update.
143    pub switch_changed: bool,
144    /// The state of the switch being pressed.
145    pub switch_pressed: bool,
146}
147
148const RED_REGISTER: u8 = 0x00;
149const GREEN_REGISTER: u8 = 0x01;
150const BLUE_REGISTER: u8 = 0x02;
151const WHITE_REGISTER: u8 = 0x03;
152
153const LEFT: u8 = 0x04;
154const RIGHT: u8 = 0x05;
155const UP: u8 = 0x06;
156const DOWN: u8 = 0x07;
157const SWITCH: u8 = 0x08;
158
159#[allow(dead_code)]
160const USER_FLASH: u8 = 0xD0;
161#[allow(dead_code)]
162const FLASH_PAGE: u8 = 0xF0;
163const INTERRUPT: u8 = 0xF9;
164
165const CHIP_ID_L: u8 = 0xFA;
166#[allow(dead_code)]
167const CHIP_ID_H: u8 = 0xFB;
168#[allow(dead_code)]
169const VERSION_REG: u8 = 0xFC;
170const I2C_ADDR: u8 = 0xFD;
171#[allow(dead_code)]
172const CTRL: u8 = 0xFE;
173
174const INTERRUPT_OUT_ENABLE_MASK: u8 = 0b0000_0010;
175const INTERRUPT_TRIGGERED_MASK: u8 = 0b0000_0001;
176
177/// Possible errors from trackball functions.
178pub enum TrackballError<I, P> {
179    /// Error in I2C communication.
180    I2C(I),
181    /// Error reading interrupt pin.
182    Pin(P),
183    /// Error running timers.
184    TimeError(TimeError),
185    /// Mismatching chip id.
186    ChipId,
187    /// Timeout occurred after setting new I2C address.
188    Timeout,
189}
190
191impl<I, P> From<TimeError> for TrackballError<I, P> {
192    fn from(t: TimeError) -> Self {
193        Self::TimeError(t)
194    }
195}
196
197// Helper alias for results.
198pub type TrackballResult<T, I, P = NoneT> =
199    Result<T, TrackballError<<I as Write>::Error, <P as OptionalPin>::Error>>;
200
201/// A trait used to represent Option<InputPin> at the type level.
202pub trait OptionalPin: Sealed {
203    type Error;
204}
205
206// Sealed impls to allow InputPin and NoneT to implement OptionalPin.
207impl<T> Sealed for T where T: InputPin {}
208impl Sealed for NoneT {}
209
210// Treat InputPin's as Some(InputPin) at the type level.
211impl<T> OptionalPin for T
212where
213    T: InputPin,
214{
215    type Error = <T as InputPin>::Error;
216}
217
218// Treat NoneT's as None: Option<InputPin> at the type level.
219impl OptionalPin for NoneT {
220    type Error = Infallible;
221}
222
223mod private {
224    /// Sealed trait to prevent clients from implementing super traits on their own types.
225    pub trait Sealed {}
226}
227
228/// Empty type used to satisfy the type system in cases where no data is used. Equivalent to None
229/// in the type system.
230pub struct NoneT;
231
232pub trait Interrupt: Sealed {
233    type I: Write;
234    type P: OptionalPin;
235
236    /// Return whether the interrupt is currently triggering.
237    fn get_interrupt(&mut self) -> TrackballResult<bool, Self::I, Self::P>;
238
239    #[doc(hidden)]
240    fn setup_interrupt_mask(&mut self, value: &mut u8);
241
242    #[doc(hidden)]
243    fn process_enable_func<F>(&mut self, func: F)
244    where
245        F: for<'a> FnOnce(&'a mut Self::P);
246}
247
248/// Builder struct to simplify constructing a [Trackball] instance.
249pub struct TrackballBuilder<I, P = NoneT> {
250    i2c: I2CInterface<I>,
251    interrupt_pin: Option<P>,
252    timeout: u32,
253}
254
255impl<I> TrackballBuilder<I> {
256    /// Construct a new builder with the required [I2CInterface].
257    pub fn new(i2c: I2CInterface<I>) -> Self {
258        Self {
259            i2c,
260            interrupt_pin: Some(NoneT),
261            timeout: DEFAULT_TIMEOUT_US,
262        }
263    }
264}
265
266impl<I, P> TrackballBuilder<I, P>
267where
268    P: InputPin,
269{
270    /// Construct a new builder with the required [I2CInterface].
271    pub fn new(i2c: I2CInterface<I>) -> Self {
272        Self {
273            i2c,
274            interrupt_pin: None,
275            timeout: DEFAULT_TIMEOUT_US,
276        }
277    }
278
279    /// Sets the interrupt pin. When this is called, you must supply an
280    pub fn interrupt_pin(mut self, pin: P) -> Self {
281        self.interrupt_pin = Some(pin);
282        self
283    }
284}
285
286impl<I, P> TrackballBuilder<I, P> {
287    /// Override the default timeout of 5 microseconds which is used when
288    /// changing the address of the trackball.
289    pub fn timeout(mut self, timeout: u32) -> Self {
290        self.timeout = timeout;
291        self
292    }
293
294    /// Construct an instance of [Trackball].
295    pub fn build(self) -> Trackball<I, P> {
296        Trackball {
297            i2c: self.i2c,
298            interrupt_pin: self.interrupt_pin.unwrap(),
299            timeout: self.timeout,
300        }
301    }
302}
303
304/// The `Trackball` struct manages communication with a Pimoroni trackball.
305pub struct Trackball<I, P = NoneT> {
306    i2c: I2CInterface<I>,
307    interrupt_pin: P,
308    timeout: u32,
309}
310
311impl<I, P> Trackball<I, P>
312where
313    P: OptionalPin,
314    I: Write<Error = <I as WriteRead>::Error> + WriteRead,
315    Self: Interrupt<I = I, P = P>,
316{
317    /// Initialize the trackball.
318    ///
319    /// The `interrupt_enable_func` is only called if an interrupt pin
320    /// was configured on the [TrackballBuilder].
321    pub fn init(
322        &mut self,
323        interrupt_enable_func: impl FnOnce(&mut P),
324    ) -> TrackballResult<(), I, P> {
325        let chip_id = self.i2c.chip_id().map_err(TrackballError::I2C)?;
326        if chip_id == CHIP_ID {
327            self.enable_interrupt(interrupt_enable_func)?;
328            Ok(())
329        } else {
330            Err(TrackballError::ChipId)
331        }
332    }
333
334    /// Get mutable access to the underlying I2C interface.
335    pub fn i2c(&mut self) -> &mut I {
336        &mut self.i2c.i2c
337    }
338
339    fn enable_interrupt(
340        &mut self,
341        interrupt_enable_func: impl for<'a> FnOnce(&'a mut P),
342    ) -> TrackballResult<(), I, P> {
343        let mut value = 0u8;
344        self.i2c
345            .i2c
346            .write_read(
347                self.i2c.addr,
348                &[INTERRUPT],
349                core::slice::from_mut(&mut value),
350            )
351            .map_err(TrackballError::I2C)?;
352        self.setup_interrupt_mask(&mut value);
353
354        let res = self
355            .i2c
356            .i2c
357            .write(self.i2c.addr, &[INTERRUPT, value])
358            .map_err(TrackballError::I2C);
359
360        self.process_enable_func(interrupt_enable_func);
361        res
362    }
363
364    /// The the rgbw colors on the trackball's LED.
365    pub fn set_rgbw(&mut self, r: u8, g: u8, b: u8, w: u8) -> Result<(), <I as Write>::Error> {
366        self.i2c.set_red(r)?;
367        self.i2c.set_green(g)?;
368        self.i2c.set_blue(b)?;
369        self.i2c.set_white(w)?;
370        Ok(())
371    }
372
373    /// The the red color on the trackball's LED.
374    #[inline]
375    pub fn set_red(&mut self, val: u8) -> Result<(), <I as Write>::Error> {
376        self.i2c.set_red(val)
377    }
378
379    /// The the green color on the trackball's LED.
380    #[inline]
381    pub fn set_green(&mut self, val: u8) -> Result<(), <I as Write>::Error> {
382        self.i2c.set_green(val)
383    }
384
385    /// The the blue color on the trackball's LED.
386    #[inline]
387    pub fn set_blue(&mut self, val: u8) -> Result<(), <I as Write>::Error> {
388        self.i2c.set_blue(val)
389    }
390
391    /// The the white color on the trackball's LED.
392    #[inline]
393    pub fn set_white(&mut self, val: u8) -> Result<(), <I as Write>::Error> {
394        self.i2c.set_white(val)
395    }
396
397    /// Read the current state of the trackball.
398    #[inline]
399    pub fn read(&mut self) -> Result<TrackballData, <I as Write>::Error> {
400        self.i2c.read()
401    }
402
403    /// Change the I2C address of the trackball.
404    ///
405    /// This will save the new value in the trackball's flash, and so needs time to update the flash
406    /// storage. It will delay by the timeout configured in [TrackballBuilder] or the default of
407    /// 5us.
408    pub fn change_address<C, De>(
409        &mut self,
410        new_address: u8,
411        clock: &mut C,
412        delay: &mut De,
413    ) -> TrackballResult<(), I, P>
414    where
415        C: Clock,
416        De: DelayUs<u8>,
417        <C as Clock>::T: From<u32>,
418    {
419        self.i2c
420            .i2c
421            .write(self.i2c.addr, &[I2C_ADDR, new_address])
422            .map_err(TrackballError::I2C)?;
423        self.wait_for_flash(clock, delay)?;
424        Ok(())
425    }
426
427    fn wait_for_flash<C, De>(&mut self, clock: &mut C, delay: &mut De) -> TrackballResult<(), I, P>
428    where
429        C: Clock,
430        De: DelayUs<u8>,
431        <C as Clock>::T: From<u32>,
432    {
433        let timer = clock.new_timer(Microseconds(self.timeout));
434        let timer = timer.start()?;
435        while self.get_interrupt()? {
436            if timer.is_expired()? {
437                return Err(TrackballError::Timeout);
438            }
439            delay.delay_us(1u8);
440        }
441
442        let timer = clock.new_timer(Microseconds(self.timeout));
443        let timer = timer.start()?;
444        while !self.get_interrupt()? {
445            if timer.is_expired()? {
446                return Err(TrackballError::Timeout);
447            }
448            delay.delay_us(1u8);
449        }
450        Ok(())
451    }
452}
453
454impl<I, P> Sealed for Trackball<I, P> {}
455
456impl<I, P> Interrupt for Trackball<I, P>
457where
458    P: InputPin,
459    I: Write<Error = <I as WriteRead>::Error> + WriteRead,
460{
461    type I = I;
462    type P = P;
463
464    fn get_interrupt(&mut self) -> TrackballResult<bool, I, P> {
465        self.interrupt_pin.is_low().map_err(TrackballError::Pin)
466    }
467
468    #[inline]
469    fn setup_interrupt_mask(&mut self, value: &mut u8) {
470        *value |= INTERRUPT_OUT_ENABLE_MASK;
471    }
472
473    #[inline]
474    fn process_enable_func<F>(&mut self, interrupt_enable_func: F)
475    where
476        F: for<'a> FnOnce(&'a mut Self::P),
477    {
478        (interrupt_enable_func)(&mut self.interrupt_pin)
479    }
480}
481impl<I, P> Trackball<I, P>
482where
483    P: InputPin,
484    I: Write<Error = <I as WriteRead>::Error> + WriteRead,
485{
486    /// Get the interrupt pin used by this instance.
487    pub fn interrupt(&mut self) -> &mut P {
488        &mut self.interrupt_pin
489    }
490}
491
492impl<I> Interrupt for Trackball<I>
493where
494    I: Write<Error = <I as WriteRead>::Error> + WriteRead,
495{
496    type I = I;
497    type P = NoneT;
498
499    /// Return whether the interrupt is currently triggering.
500    fn get_interrupt(&mut self) -> TrackballResult<bool, I> {
501        self.i2c.get_interrupt().map_err(TrackballError::I2C)
502    }
503
504    #[inline]
505    fn setup_interrupt_mask(&mut self, value: &mut u8) {
506        *value &= !INTERRUPT_OUT_ENABLE_MASK;
507    }
508
509    #[inline]
510    fn process_enable_func<F>(&mut self, _: F)
511    where
512        F: for<'a> FnOnce(&'a mut Self::P),
513    {
514        // nop
515    }
516}