Add first mouse sensor implementation

This commit is contained in:
hodasemi 2023-04-14 10:13:32 +02:00
parent dc62866577
commit 73f246e7d3
8 changed files with 369 additions and 4 deletions

View file

@ -8,4 +8,5 @@
"rust-analyzer.linkedProjects": [
"./Cargo.toml"
],
"rust-analyzer.showUnlinkedFileNotification": false,
}

View file

@ -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",
);
}

View file

@ -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;

View file

@ -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];

View 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
View file

@ -0,0 +1,4 @@
mod paw3212db_tjdt;
mod wrapper;
pub use wrapper::{MouseSensor, MouseSensorPins};

View 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
View 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
}
}
}