Add first mouse sensor implementation
This commit is contained in:
parent
dc62866577
commit
73f246e7d3
8 changed files with 369 additions and 4 deletions
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
@ -8,4 +8,5 @@
|
|||
"rust-analyzer.linkedProjects": [
|
||||
"./Cargo.toml"
|
||||
],
|
||||
"rust-analyzer.showUnlinkedFileNotification": false,
|
||||
}
|
2
build.rs
2
build.rs
|
@ -119,7 +119,7 @@ fn create_config_struct(content: &str, path: &str, struct_name: &str) {
|
|||
fn main() {
|
||||
create_config_struct(
|
||||
include_str!("mouse.conf"),
|
||||
"src/mouse_config.rs",
|
||||
"src/mouse_hid/mouse_config.rs",
|
||||
"MouseConfig",
|
||||
);
|
||||
}
|
||||
|
|
15
src/main.rs
15
src/main.rs
|
@ -2,9 +2,10 @@
|
|||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![feature(never_type)]
|
||||
#![feature(async_closure)]
|
||||
|
||||
mod mouse_config;
|
||||
mod mouse_hid;
|
||||
mod mouse_sensor;
|
||||
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_futures::join::join;
|
||||
|
@ -12,7 +13,11 @@ use embassy_time::{Duration, Timer};
|
|||
|
||||
use usbd_hid::descriptor::MouseReport;
|
||||
|
||||
use crate::{mouse_config::MouseConfig, mouse_hid::MouseHID};
|
||||
use crate::{
|
||||
mouse_hid::mouse_config::MouseConfig,
|
||||
mouse_hid::MouseHID,
|
||||
mouse_sensor::{MouseSensor, MouseSensorPins},
|
||||
};
|
||||
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
|
@ -21,6 +26,12 @@ async fn main(_spawner: Spawner) {
|
|||
let p = embassy_rp::init(Default::default());
|
||||
|
||||
let mut mouse_hid = MouseHID::new(p.USB, MouseConfig::new());
|
||||
let mut mouse_sensor = MouseSensor::new(
|
||||
p.PIN_25,
|
||||
MouseSensorPins::new(p.SPI0, p.PIN_2, p.PIN_3, p.PIN_4, p.PIN_5),
|
||||
|_| (),
|
||||
)
|
||||
.await;
|
||||
|
||||
let usb_fut = MouseHID::run_usb().await;
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
pub mod mouse_config;
|
||||
|
||||
use core::future::Future;
|
||||
|
||||
use embassy_rp::usb::Driver;
|
||||
|
@ -9,7 +11,7 @@ use embassy_usb::{Builder, Config, UsbDevice};
|
|||
|
||||
use usbd_hid::descriptor::{MouseReport, SerializedDescriptor};
|
||||
|
||||
use crate::mouse_config::MouseConfig;
|
||||
use mouse_config::MouseConfig;
|
||||
|
||||
static mut DEVICE_DESCRIPTOR_BUFFER: [u8; 256] = [0; 256];
|
||||
static mut CONFIG_DESCRIPTOR_BUFFER: [u8; 256] = [0; 256];
|
19
src/mouse_hid/mouse_config.rs
Normal file
19
src/mouse_hid/mouse_config.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
pub struct MouseConfig<'a> {
|
||||
pub vendor_id: i32,
|
||||
pub product_id: i32,
|
||||
pub manufacturer: &'a str,
|
||||
pub product: &'a str,
|
||||
pub serial_number: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> MouseConfig<'a> {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
vendor_id: 0x046d,
|
||||
product_id: 0x101b,
|
||||
manufacturer: "Logitech",
|
||||
product: "Performance Plus M705",
|
||||
serial_number: "B14D65DA",
|
||||
}
|
||||
}
|
||||
}
|
4
src/mouse_sensor/mod.rs
Normal file
4
src/mouse_sensor/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
mod paw3212db_tjdt;
|
||||
mod wrapper;
|
||||
|
||||
pub use wrapper::{MouseSensor, MouseSensorPins};
|
159
src/mouse_sensor/paw3212db_tjdt.rs
Normal file
159
src/mouse_sensor/paw3212db_tjdt.rs
Normal file
|
@ -0,0 +1,159 @@
|
|||
use core::mem;
|
||||
|
||||
use embassy_rp::{
|
||||
gpio::{Level, Output},
|
||||
spi::{
|
||||
Blocking, ClkPin, Config, CsPin, Instance as SpiInstance, MisoPin, MosiPin, Polarity, Spi,
|
||||
},
|
||||
Peripheral,
|
||||
};
|
||||
use embassy_time::{Duration, Timer};
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct PAW3212DB_TJDT<'a, L, CS, T>
|
||||
where
|
||||
L: FnMut(&str),
|
||||
CS: CsPin<T> + 'a,
|
||||
T: SpiInstance,
|
||||
{
|
||||
cs: Output<'a, CS>,
|
||||
spi: Spi<'a, T, Blocking>,
|
||||
|
||||
pub log: L,
|
||||
}
|
||||
|
||||
impl<'a, L, CS, T> PAW3212DB_TJDT<'a, L, CS, T>
|
||||
where
|
||||
L: FnMut(&str),
|
||||
CS: CsPin<T> + 'a,
|
||||
T: SpiInstance,
|
||||
{
|
||||
const READ: u8 = 0b0111_1111;
|
||||
const WRITE: u8 = 0b1000_0000;
|
||||
|
||||
const REG_PRODUCT_ID1: u8 = 0x00;
|
||||
const REG_PRODUCT_ID2: u8 = 0x01;
|
||||
const REG_MOTION_STATUS: u8 = 0x02;
|
||||
const REG_DELTA_X: u8 = 0x03;
|
||||
const REG_DELTA_Y: u8 = 0x04;
|
||||
const REG_OPERATION_MODE: u8 = 0x05;
|
||||
const REG_CONFIGURATION: u8 = 0x06;
|
||||
const REG_WRITE_PROTECT: u8 = 0x09;
|
||||
const REG_SLEEP1: u8 = 0x0A;
|
||||
const REG_SLEEP2: u8 = 0x0B;
|
||||
const REG_SLEEP3: u8 = 0x0C;
|
||||
const REG_CPI_X: u8 = 0x0D;
|
||||
const REG_CPI_Y: u8 = 0x0E;
|
||||
const REG_DELTA_XY_HI: u8 = 0x12;
|
||||
const REG_IQC: u8 = 0x13;
|
||||
const REG_SHUTTER: u8 = 0x14;
|
||||
const REG_FRAME_AVG: u8 = 0x17;
|
||||
const REG_MOUSE_OPTION: u8 = 0x19;
|
||||
const REG_SPI_MODE: u8 = 0x26;
|
||||
|
||||
pub async fn new(
|
||||
spi: impl Peripheral<P = T> + 'a,
|
||||
clk: impl Peripheral<P = impl ClkPin<T> + 'a> + 'a,
|
||||
mosi: impl Peripheral<P = impl MosiPin<T> + 'a> + 'a,
|
||||
miso: impl Peripheral<P = impl MisoPin<T> + 'a> + 'a,
|
||||
cs: impl Peripheral<P = CS> + 'a,
|
||||
log: L,
|
||||
) -> PAW3212DB_TJDT<'a, L, CS, T> {
|
||||
let mut spi_config = Config::default();
|
||||
spi_config.frequency = 2_000_000;
|
||||
spi_config.polarity = Polarity::IdleHigh;
|
||||
|
||||
let spi = Spi::new_blocking(spi, clk, mosi, miso, spi_config);
|
||||
|
||||
let mut cs = Output::new(cs, Level::Low);
|
||||
|
||||
cs.set_low();
|
||||
|
||||
Timer::after(Duration::from_millis(2)).await;
|
||||
|
||||
cs.set_high();
|
||||
|
||||
Self { cs, spi, log }
|
||||
}
|
||||
|
||||
fn access<F, R>(&mut self, f: F) -> R
|
||||
where
|
||||
F: Fn(&mut Self) -> R,
|
||||
{
|
||||
self.cs.set_low();
|
||||
Timer::after(Duration::from_micros(1));
|
||||
|
||||
let res = f(self);
|
||||
|
||||
self.cs.set_high();
|
||||
Timer::after(Duration::from_micros(2));
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
fn read(&mut self, address: u8) -> u8 {
|
||||
loop {
|
||||
let res = self.access(move |me| {
|
||||
if me.spi.blocking_write(&[address & Self::READ, 0]).is_err() {
|
||||
(me.log)("send address (read) failed");
|
||||
}
|
||||
|
||||
let mut buf = [0];
|
||||
|
||||
if me.spi.blocking_read(&mut buf).is_err() {
|
||||
(me.log)("read failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
buf[0] << 1
|
||||
});
|
||||
|
||||
if res != address {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, address: u8, value: u8) {
|
||||
self.access(move |me| {
|
||||
if me
|
||||
.spi
|
||||
.blocking_write(&[address | Self::WRITE, value])
|
||||
.is_err()
|
||||
{
|
||||
(me.log)("send address (write) failed");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, L, CS, T> PAW3212DB_TJDT<'a, L, CS, T>
|
||||
where
|
||||
L: FnMut(&str),
|
||||
CS: CsPin<T> + 'a,
|
||||
T: SpiInstance,
|
||||
{
|
||||
pub fn delta_x(&mut self) -> i8 {
|
||||
unsafe { mem::transmute(self.read(Self::REG_DELTA_X)) }
|
||||
}
|
||||
|
||||
pub fn delta_y(&mut self) -> i8 {
|
||||
unsafe { mem::transmute(self.read(Self::REG_DELTA_Y)) }
|
||||
}
|
||||
|
||||
pub fn product_id_1(&mut self) -> u8 {
|
||||
self.read(Self::REG_PRODUCT_ID1)
|
||||
}
|
||||
|
||||
pub fn product_id_2(&mut self) -> u8 {
|
||||
self.read(Self::REG_PRODUCT_ID2)
|
||||
}
|
||||
|
||||
pub fn operation_mode(&mut self) -> u8 {
|
||||
self.read(Self::REG_OPERATION_MODE)
|
||||
}
|
||||
|
||||
pub fn motion_status(&mut self) -> u8 {
|
||||
self.read(Self::REG_MOTION_STATUS)
|
||||
}
|
||||
}
|
169
src/mouse_sensor/wrapper.rs
Normal file
169
src/mouse_sensor/wrapper.rs
Normal file
|
@ -0,0 +1,169 @@
|
|||
use super::paw3212db_tjdt::PAW3212DB_TJDT;
|
||||
|
||||
use embassy_rp::{
|
||||
gpio::{Level, Output},
|
||||
peripherals::PIN_25,
|
||||
spi::{ClkPin, CsPin, Instance, MisoPin, MosiPin},
|
||||
Peripheral,
|
||||
};
|
||||
|
||||
use core::marker::PhantomData;
|
||||
|
||||
pub struct MouseSensorPins<'a, T, Clk, Mosi, Miso, CSP, P, C, MI, MO, CS>
|
||||
where
|
||||
T: Instance,
|
||||
Clk: ClkPin<T> + 'a,
|
||||
Mosi: MosiPin<T> + 'a,
|
||||
Miso: MisoPin<T> + 'a,
|
||||
CSP: CsPin<T> + 'a,
|
||||
|
||||
P: Peripheral<P = T> + 'a,
|
||||
C: Peripheral<P = Clk> + 'a,
|
||||
MI: Peripheral<P = Mosi> + 'a,
|
||||
MO: Peripheral<P = Miso> + 'a,
|
||||
CS: Peripheral<P = CSP> + 'a,
|
||||
{
|
||||
spi: P,
|
||||
clk: C,
|
||||
mosi: MI,
|
||||
miso: MO,
|
||||
cs: CS,
|
||||
|
||||
d: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl<'a, T, Clk, Mosi, Miso, CSP, P, C, MI, MO, CS>
|
||||
MouseSensorPins<'a, T, Clk, Mosi, Miso, CSP, P, C, MI, MO, CS>
|
||||
where
|
||||
T: Instance,
|
||||
Clk: ClkPin<T> + 'a,
|
||||
Mosi: MosiPin<T> + 'a,
|
||||
Miso: MisoPin<T> + 'a,
|
||||
CSP: CsPin<T> + 'a,
|
||||
|
||||
P: Peripheral<P = T> + 'a,
|
||||
C: Peripheral<P = Clk> + 'a,
|
||||
MI: Peripheral<P = Mosi> + 'a,
|
||||
MO: Peripheral<P = Miso> + 'a,
|
||||
CS: Peripheral<P = CSP> + 'a,
|
||||
{
|
||||
pub fn new(spi: P, clk: C, mosi: MI, miso: MO, cs: CS) -> Self {
|
||||
Self {
|
||||
spi,
|
||||
clk,
|
||||
mosi,
|
||||
miso,
|
||||
cs,
|
||||
|
||||
d: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MouseSensor<'d, L, T, CSP>
|
||||
where
|
||||
L: FnMut(&str),
|
||||
T: Instance,
|
||||
CSP: CsPin<T> + 'd,
|
||||
{
|
||||
spi_device: PAW3212DB_TJDT<'d, L, CSP, T>,
|
||||
|
||||
led: Output<'d, PIN_25>,
|
||||
}
|
||||
|
||||
impl<'d, L, T, CSP> MouseSensor<'d, L, T, CSP>
|
||||
where
|
||||
L: FnMut(&str) + 'd,
|
||||
T: Instance,
|
||||
CSP: CsPin<T> + 'd,
|
||||
{
|
||||
pub async fn new<'a, Clk, Mosi, Miso, P, C, MI, MO, CS>(
|
||||
led: PIN_25,
|
||||
sensor_pins: MouseSensorPins<'d, T, Clk, Mosi, Miso, CSP, P, C, MI, MO, CS>,
|
||||
log: L,
|
||||
) -> MouseSensor<'d, L, T, CSP>
|
||||
where
|
||||
Clk: ClkPin<T> + 'a,
|
||||
Mosi: MosiPin<T> + 'a,
|
||||
Miso: MisoPin<T> + 'a,
|
||||
|
||||
P: Peripheral<P = T> + 'a,
|
||||
C: Peripheral<P = Clk> + 'a,
|
||||
MI: Peripheral<P = Mosi> + 'a,
|
||||
MO: Peripheral<P = Miso> + 'a,
|
||||
CS: Peripheral<P = CSP> + 'a,
|
||||
{
|
||||
// These are implicitly used by the spi driver if they are in the correct mode
|
||||
|
||||
let led = Output::new(led, Level::Low);
|
||||
let spi_device = PAW3212DB_TJDT::new(
|
||||
sensor_pins.spi,
|
||||
sensor_pins.clk,
|
||||
sensor_pins.mosi,
|
||||
sensor_pins.miso,
|
||||
sensor_pins.cs,
|
||||
log,
|
||||
)
|
||||
.await;
|
||||
|
||||
let mut me = Self { spi_device, led };
|
||||
|
||||
me.led.set_high();
|
||||
|
||||
// verify initialization
|
||||
loop {
|
||||
if me.verify_product_id_1() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
if me.verify_product_id_2() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
me.led.set_low();
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
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(&'d 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