Split mouse sensor implementation

This commit is contained in:
hodasemi 2023-04-12 13:28:49 +02:00
parent f9f324475b
commit a4a14fc1de
4 changed files with 143 additions and 93 deletions

View file

@ -8,4 +8,5 @@
"rust-analyzer.cargo.features": [ "rust-analyzer.cargo.features": [
"serial" "serial"
], ],
"rust-analyzer.showUnlinkedFileNotification": false,
} }

4
src/mouse_sensor/mod.rs Normal file
View file

@ -0,0 +1,4 @@
mod spi_interface;
mod wrapper;
pub use wrapper::MouseSensor;

View file

@ -6,34 +6,34 @@ use fugit::{HertzU32, MegahertzU32};
use rp_pico::{ use rp_pico::{
hal::{ hal::{
gpio::{ gpio::{
self, bank0::{Gpio2, Gpio3, Gpio4, Gpio5},
bank0::{Gpio2, Gpio25, Gpio3, Gpio4, Gpio5}, Function, Output, Pin, PushPull, Spi,
Function, Output, Pin, PushPull, PushPullOutput, Spi,
}, },
spi::{self, Enabled, Spi as SpiDevice}, spi::{self, Enabled, Spi as SpiDevice},
}, },
pac::{RESETS, SPI0}, pac::{RESETS, SPI0},
Pins,
}; };
pub struct MouseSensor<L> pub struct SpiPins {
pub sclk: Pin<Gpio2, Function<Spi>>,
pub mosi: Pin<Gpio3, Function<Spi>>,
pub miso: Pin<Gpio4, Function<Spi>>,
pub cs: Pin<Gpio5, Output<PushPull>>,
}
pub struct SpiMouseSensor<L>
where where
L: FnMut(&str), L: FnMut(&str),
{ {
_sclk: Pin<Gpio2, Function<Spi>>, pins: SpiPins,
_mosi: Pin<Gpio3, Function<Spi>>,
_miso: Pin<Gpio4, Function<Spi>>,
cs: Pin<Gpio5, Output<PushPull>>,
led: Pin<Gpio25, PushPullOutput>,
spi: SpiDevice<Enabled, SPI0, 8>, spi: SpiDevice<Enabled, SPI0, 8>,
delay: Delay, delay: Delay,
log: L, pub log: L,
} }
impl<L> MouseSensor<L> impl<L> SpiMouseSensor<L>
where where
L: FnMut(&str), L: FnMut(&str),
{ {
@ -61,19 +61,13 @@ where
const REG_SPI_MODE: u8 = 0x26; const REG_SPI_MODE: u8 = 0x26;
pub fn new( pub fn new(
pins: Pins, mut pins: SpiPins,
resets: &mut RESETS, resets: &mut RESETS,
spio: SPI0, spio: SPI0,
peripheral_clock: impl Into<HertzU32>, peripheral_clock: impl Into<HertzU32>,
mut delay: Delay, mut delay: Delay,
mut log: L, mut log: L,
) -> Self { ) -> Self {
// These are implicitly used by the spi driver if they are in the correct mode
let _sclk = pins.gpio2.into_mode::<gpio::FunctionSpi>();
let _mosi = pins.gpio3.into_mode::<gpio::FunctionSpi>();
let _miso = pins.gpio4.into_mode::<gpio::FunctionSpi>();
let mut cs = pins.gpio5.into_push_pull_output();
// Create an SPI driver instance for the SPI0 device // Create an SPI driver instance for the SPI0 device
let spi = spi::Spi::<_, _, 8>::new(spio); let spi = spi::Spi::<_, _, 8>::new(spio);
@ -85,59 +79,31 @@ where
&embedded_hal::spi::MODE_2, &embedded_hal::spi::MODE_2,
); );
if let Err(_err) = cs.set_low() { if let Err(_err) = pins.cs.set_low() {
log("cs high failed"); log("cs high failed");
} }
delay.delay_ms(2); delay.delay_ms(2);
if let Err(_err) = cs.set_high() { if let Err(_err) = pins.cs.set_high() {
log("cs high failed"); log("cs high failed");
} }
let mut me = Self { Self {
_sclk, pins,
_mosi,
_miso,
cs,
led: pins.led.into_push_pull_output(),
spi, spi,
delay, delay,
log, log,
};
if me.led.set_high().is_err() {
(me.log)("led high failed");
} }
// verify initialization
loop {
if me.verify_product_id_1() {
break;
}
}
loop {
if me.verify_product_id_2() {
break;
}
}
if me.led.set_low().is_err() {
(me.log)("led low failed");
}
me
} }
fn access<F, T>(&mut self, f: F) -> T fn access<F, T>(&mut self, f: F) -> T
where where
F: Fn(&mut Self) -> T, F: Fn(&mut Self) -> T,
{ {
if self.cs.set_low().is_err() { if self.pins.cs.set_low().is_err() {
(self.log)("cs low failed"); (self.log)("cs low failed");
} }
@ -145,7 +111,7 @@ where
let res = f(self); let res = f(self);
if self.cs.set_high().is_err() { if self.pins.cs.set_high().is_err() {
(self.log)("cs low failed"); (self.log)("cs low failed");
} }
@ -154,7 +120,7 @@ where
res res
} }
pub fn read(&mut self, address: u8) -> u8 { fn read(&mut self, address: u8) -> u8 {
loop { loop {
let res = self.access(|me| { let res = self.access(|me| {
if me.spi.send(address & Self::READ).is_err() { if me.spi.send(address & Self::READ).is_err() {
@ -180,7 +146,7 @@ where
} }
} }
pub fn write(&mut self, address: u8, value: u8) { fn write(&mut self, address: u8, value: u8) {
self.access(|me| { self.access(|me| {
if me.spi.send(address | Self::WRITE).is_err() { if me.spi.send(address | Self::WRITE).is_err() {
(me.log)("send address (write) failed"); (me.log)("send address (write) failed");
@ -191,24 +157,12 @@ where
} }
}); });
} }
pub fn read_movement(&mut self) -> Option<(i8, i8)> {
let (motion, x_overflow, y_overflow) = self.motion_detected();
if motion {
if x_overflow || y_overflow {
return None;
}
let x = self.delta_x();
let y = self.delta_y();
Some((x, y))
} else {
None
}
} }
impl<L> SpiMouseSensor<L>
where
L: FnMut(&str),
{
pub fn delta_x(&mut self) -> i8 { pub fn delta_x(&mut self) -> i8 {
unsafe { mem::transmute(self.read(Self::REG_DELTA_X)) } unsafe { mem::transmute(self.read(Self::REG_DELTA_X)) }
} }
@ -217,16 +171,6 @@ where
unsafe { mem::transmute(self.read(Self::REG_DELTA_Y)) } unsafe { mem::transmute(self.read(Self::REG_DELTA_Y)) }
} }
pub fn motion_detected(&mut self) -> (bool, bool, bool) {
let reg = self.read(Self::REG_MOTION_STATUS);
let motion = (reg & 0b10000000) != 0;
let x_overflow = (reg & 0b00010000) != 0;
let y_overflow = (reg & 0b00001000) != 0;
(motion, x_overflow, y_overflow)
}
pub fn product_id_1(&mut self) -> u8 { pub fn product_id_1(&mut self) -> u8 {
self.read(Self::REG_PRODUCT_ID1) self.read(Self::REG_PRODUCT_ID1)
} }
@ -239,15 +183,7 @@ where
self.read(Self::REG_OPERATION_MODE) self.read(Self::REG_OPERATION_MODE)
} }
fn verify_product_id_1(&mut self) -> bool { pub fn motion_status(&mut self) -> u8 {
const DEFAULT_VALUE: u8 = 0x30; self.read(Self::REG_MOTION_STATUS)
DEFAULT_VALUE == self.product_id_1()
}
fn verify_product_id_2(&mut self) -> bool {
const DEFAULT_VALUE: u8 = 0x02;
DEFAULT_VALUE == self.product_id_2()
} }
} }

109
src/mouse_sensor/wrapper.rs Normal file
View file

@ -0,0 +1,109 @@
use super::spi_interface::{SpiMouseSensor, SpiPins};
use cortex_m::delay::Delay;
use embedded_hal::digital::v2::OutputPin;
use fugit::HertzU32;
use rp_pico::{
hal::gpio::{self, bank0::Gpio25, Pin, PushPullOutput},
pac::{RESETS, SPI0},
Pins,
};
pub struct MouseSensor<L>
where
L: FnMut(&str),
{
spi_device: SpiMouseSensor<L>,
led: Pin<Gpio25, PushPullOutput>,
}
impl<L> MouseSensor<L>
where
L: FnMut(&str),
{
pub fn new(
pins: Pins,
resets: &mut RESETS,
spio: SPI0,
peripheral_clock: impl Into<HertzU32>,
delay: Delay,
log: L,
) -> Self {
// These are implicitly used by the spi driver if they are in the correct mode
let spi_pins = SpiPins {
sclk: pins.gpio2.into_mode::<gpio::FunctionSpi>(),
mosi: pins.gpio3.into_mode::<gpio::FunctionSpi>(),
miso: pins.gpio4.into_mode::<gpio::FunctionSpi>(),
cs: pins.gpio5.into_push_pull_output(),
};
let led = pins.led.into_push_pull_output();
let spi_device = SpiMouseSensor::new(spi_pins, resets, spio, peripheral_clock, delay, log);
let mut me = Self { spi_device, led };
if me.led.set_high().is_err() {
(me.spi_device.log)("led high failed");
}
// verify initialization
loop {
if me.verify_product_id_1() {
break;
}
}
loop {
if me.verify_product_id_2() {
break;
}
}
if me.led.set_low().is_err() {
(me.spi_device.log)("led low failed");
}
me
}
fn verify_product_id_1(&mut self) -> bool {
const DEFAULT_VALUE: u8 = 0x30;
DEFAULT_VALUE == self.spi_device.product_id_1()
}
fn verify_product_id_2(&mut self) -> bool {
const DEFAULT_VALUE: u8 = 0x02;
DEFAULT_VALUE == self.spi_device.product_id_2()
}
pub fn motion_detected(&mut self) -> (bool, bool, bool) {
let reg = self.spi_device.motion_status();
let motion = (reg & 0b10000000) != 0;
let x_overflow = (reg & 0b00010000) != 0;
let y_overflow = (reg & 0b00001000) != 0;
(motion, x_overflow, y_overflow)
}
pub fn read_movement(&mut self) -> Option<(i8, i8)> {
let (motion, x_overflow, y_overflow) = self.motion_detected();
if motion {
if x_overflow || y_overflow {
return None;
}
let x = self.spi_device.delta_x();
let y = self.spi_device.delta_y();
Some((x, y))
} else {
None
}
}
}