Split mouse sensor implementation
This commit is contained in:
parent
f9f324475b
commit
a4a14fc1de
4 changed files with 143 additions and 93 deletions
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
@ -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
4
src/mouse_sensor/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
mod spi_interface;
|
||||||
|
mod wrapper;
|
||||||
|
|
||||||
|
pub use wrapper::MouseSensor;
|
|
@ -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
109
src/mouse_sensor/wrapper.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue