/// Support for the Cirque Pinnacle 1CA027. use core::cmp::{max, min}; use embedded_hal::{digital::v2::OutputPin, spi}; use nb; /// Default I²C address. #[allow(unused)] const I2C_ADDR: u8 = 0x2a; // the maximum reportable units const MAX_X: u16 = 2048; const MAX_Y: u16 = 1536; // the lowest reachable units const CLAMP_X_MIN: u16 = 127; const CLAMP_Y_MIN: u16 = 63; // the highest reachable units const CLAMP_X_MAX: u16 = 1919; const CLAMP_Y_MAX: u16 = 1471; // masks for register access protocol const WRITE_MASK: u8 = 0x80; const READ_MASK: u8 = 0xa0; fn clamp(val: T, low: T, high: T) -> T { min(max(low, val), high) } /// Register Access Protocol addresses. #[allow(unused)] #[repr(u8)] enum RAPAddress { FirmwareID = 0x00, FirmwareVersion = 0x01, Status1 = 0x02, SysConfig1 = 0x03, FeedConfig1 = 0x04, FeedConfig2 = 0x05, FeedConfig3 = 0x06, CalConfig1 = 0x07, PS2AuxControl = 0x08, SampleRate = 0x09, ZIdle = 0x0a, ZScaler = 0x0b, SleepInterval = 0x0c, SleepTimer = 0x0d, DynamicEMIAdjustThreshold = 0x0e, Reserved1 = 0x0f, Reserved2 = 0x10, Reserved3 = 0x11, PacketByte0 = 0x12, PacketByte1 = 0x13, PacketByte2 = 0x14, PacketByte3 = 0x15, PacketByte4 = 0x16, PacketByte5 = 0x17, PortAGPIOControl = 0x18, PortAGPIOData = 0x19, PortBGPIOControlAndData = 0x1a, ExtendedRegisterAccessValue = 0x1b, ExtendedRegisterAccessHigh = 0x1c, ExtendedRegisterAccessLow = 0x1d, ExtendedRegisterAccessControl = 0x1e, ProductID = 0x1f, } // TODO: figure out how to use the // embedded_hal::blocking::spi::{Transfer, Write} traits instead of // doing it ourselves. trait SPIWriter: spi::FullDuplex { fn write(&mut self, buf: &[Word]) -> nb::Result<(), Self::Error>; fn transfer(&mut self, buf: &mut [Word]) -> nb::Result<(), Self::Error>; } impl SPIWriter for T where T: spi::FullDuplex, Word: Copy, { fn write(&mut self, buf: &[Word]) -> nb::Result<(), Self::Error> { for i in buf { nb::block!(self.send(*i))?; _ = nb::block!(self.read())?; } Ok(()) } fn transfer(&mut self, buf: &mut [Word]) -> nb::Result<(), Self::Error> { for i in buf.iter_mut() { nb::block!(self.send(*i))?; *i = nb::block!(self.read())?; } Ok(()) } } #[allow(unused)] #[derive(Debug)] pub struct TouchData { x: u16, y: u16, z: u8, buttons: u8, is_pressed: bool, } impl TouchData { pub fn clamp(&mut self) { self.x = clamp(self.x, CLAMP_X_MIN, CLAMP_X_MAX); self.y = clamp(self.y, CLAMP_Y_MIN, CLAMP_Y_MAX); } pub fn scale_to(&mut self, width: u16, height: u16) { let width_factor: f32 = f32::from(width) / f32::from(CLAMP_X_MAX - CLAMP_X_MIN); let height_factor: f32 = f32::from(height) / f32::from(CLAMP_Y_MAX - CLAMP_Y_MIN); self.clamp(); self.x -= CLAMP_X_MIN; self.y -= CLAMP_Y_MIN; assert!(!width_factor.is_nan() && width_factor.is_finite()); assert!(!height_factor.is_nan() && height_factor.is_finite()); unsafe { self.x = (f32::from(self.x) * width_factor).to_int_unchecked(); self.y = (f32::from(self.y) * height_factor).to_int_unchecked(); } } } pub struct Cirque where C: OutputPin, { cs_pin: C, sysclk_speed: u32, } impl Cirque where C: OutputPin, { pub fn new(cs_pin: C, spi: &mut S, sysclk_speed: u32) -> nb::Result where S: spi::FullDuplex, { let mut res = Self { cs_pin, sysclk_speed, }; res.init(spi)?; Ok(res) } fn init(&mut self, spi: &mut S) -> nb::Result<(), S::Error> where S: spi::FullDuplex, { self.cs_pin.set_high().ok(); // spin until power on reset is flagged. // while self.read_flags(spi)? & 0x8 != 0x8 {} self.clear_flags(spi)?; self.wr(spi, RAPAddress::SysConfig1, 0x00)?; self.wr(spi, RAPAddress::FeedConfig2, 0x1e)?; self.wr(spi, RAPAddress::FeedConfig1, 0x03)?; self.wr(spi, RAPAddress::ZIdle, 0x05)?; Ok(()) } // clears the hardware data ready flag fn clear_flags(&mut self, spi: &mut S) -> nb::Result<(), S::Error> where S: spi::FullDuplex, { self.wr(spi, RAPAddress::Status1, 0x00)?; cortex_m::asm::delay(50 * self.sysclk_speed / 1_000_000); Ok(()) } fn read_flags(&mut self, spi: &mut S) -> nb::Result where S: spi::FullDuplex, { let mut flags: [u8; 1] = [0xfb; 1]; self.rd(spi, RAPAddress::Status1, &mut flags)?; Ok(flags[0]) } pub fn poll(&mut self, spi: &mut S) -> nb::Result where S: spi::FullDuplex, { if self.read_flags(spi)? & 0x4 == 0x4 { Ok(self.read_coords(spi)?) } else { Err(nb::Error::WouldBlock) } } fn read_coords(&mut self, spi: &mut S) -> nb::Result where S: spi::FullDuplex, { let mut buf: [u8; 6] = [0xfc; 6]; self.rd(spi, RAPAddress::PacketByte0, &mut buf)?; self.clear_flags(spi)?; let x = buf[2] as u16 | ((buf[4] as u16 & 0x0f) << 8); let y = buf[3] as u16 | ((buf[4] as u16 & 0xf0) << 4); let z = buf[5] & 0x3f; let buttons = 0; let is_pressed = x != 0; assert!(x < MAX_X); assert!(y < MAX_Y); Ok(TouchData { x, y, z, buttons, is_pressed, }) } fn rd(&mut self, spi: &mut S, addr: RAPAddress, buf: &mut [u8]) -> nb::Result<(), S::Error> where S: spi::FullDuplex, { let addr = addr as u8 | READ_MASK; self.cs_pin.set_low().ok(); let res = spi .write(&[addr, 0xfc, 0xfc]) .and_then(|_| spi.transfer(buf)); self.cs_pin.set_high().ok(); res } fn wr(&mut self, spi: &mut S, addr: RAPAddress, data: u8) -> nb::Result<(), S::Error> where S: spi::FullDuplex, { let addr = addr as u8 | WRITE_MASK; self.cs_pin.set_low().ok(); let res = spi.write(&[addr, data]); self.cs_pin.set_high().ok(); res } }