diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cirque.rs | 255 | ||||
-rwxr-xr-x | src/main.rs | 11 |
2 files changed, 265 insertions, 1 deletions
diff --git a/src/cirque.rs b/src/cirque.rs new file mode 100644 index 0000000..b1f5fae --- /dev/null +++ b/src/cirque.rs @@ -0,0 +1,255 @@ +/// Support for the Cirque Pinnacle 1CA027. +use core::cmp::{max, min}; +use embedded_hal::{ + digital::v2::{InputPin, OutputPin}, + spi, +}; +use nb; +use stm32f1xx_hal::rcc::Clocks; + +/// Default I²C address. +const I2C_ADDR: u8 = 0x2a; + +// the maximum reportable units +const MAX_X: u16 = 2047; +const MAX_Y: u16 = 1535; + +// 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<T: Ord>(val: T, low: T, high: T) -> T { + min(max(low, val), high) +} + +/// Register Access Protocol addresses. +#[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, + SleepDelay = 0x0d, + Reserved0 = 0x0e, + Reserved1 = 0x0f, + Reserved2 = 0x10, + Reserved3 = 0x11, + PacketByte0 = 0x12, + PacketByte1 = 0x13, + PacketByte2 = 0x14, + PacketByte3 = 0x15, + PacketByte4 = 0x16, + PacketByte5 = 0x17, + Reserved4 = 0x18, + Reserved5 = 0x19, + Reserved6 = 0x1a, + ERAValue = 0x1b, + ERAHighByte = 0x1c, + ERALowByte = 0x1d, + ERAControl = 0x1e, + ProductID = 0x1f, +} + +trait SPIWriter<Word>: spi::FullDuplex<Word> { + fn write(&mut self, buf: &[Word]) -> Result<(), Self::Error>; + fn transfer(&mut self, buf: &mut [Word]) -> Result<(), Self::Error>; +} + +impl<T, Word> SPIWriter<Word> for T +where + T: spi::FullDuplex<Word>, + Word: Copy, +{ + fn write(&mut self, buf: &[Word]) -> Result<(), Self::Error> { + for i in buf { + // TODO: figure out why the ? operator doesn't work here. + self.send(*i); + } + Ok(()) + } + + fn transfer(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { + for i in buf.iter_mut() { + // TODO: figure out why the ? operator doesn't work here. + self.send(*i); + if let Ok(v) = self.read() { + *i = v; + } + } + 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 = width / (CLAMP_X_MAX - CLAMP_X_MIN); + let height_factor = height / (CLAMP_Y_MAX - CLAMP_Y_MIN); + + self.clamp(); + self.x -= CLAMP_X_MIN; + self.y -= CLAMP_Y_MIN; + + self.x *= width_factor; + self.y *= height_factor; + } +} + +pub struct Cirque<C, D> +where + C: OutputPin, + D: InputPin, +{ + cs_pin: C, + dr_pin: D, + sysclk_speed: u32, +} + +impl<C, D> Cirque<C, D> +where + C: OutputPin, + D: InputPin, +{ + pub fn new<S>( + cs_pin: C, + dr_pin: D, + spi: &mut S, + clocks: Clocks, + ) -> nb::Result<Self, S::Error> + where + S: embedded_hal::spi::FullDuplex<u8>, + { + let sysclk_speed = clocks.sysclk().raw(); + let mut res = Self { + cs_pin, + dr_pin, + sysclk_speed, + }; + res.init(spi)?; + Ok(res) + } + + fn init<S>(&mut self, spi: &mut S) -> nb::Result<(), S::Error> + where + S: embedded_hal::spi::FullDuplex<u8>, + { + self.cs_pin.set_high().ok(); + self.clear_flags(spi)?; + + // SysConfig + self.wr(spi, 0x03, 0x00)?; + // FeedConfig2 + self.wr(spi, 0x05, 0x1f)?; + // FeedConfig1 + self.wr(spi, 0x04, 0x03)?; + // ZIdleCount + self.wr(spi, 0x0a, 0x05)?; + + Ok(()) + } + + fn clear_flags<S>(&mut self, spi: &mut S) -> nb::Result<(), S::Error> + where + S: embedded_hal::spi::FullDuplex<u8>, + { + self.wr(spi, 0x02, 0x00)?; + + cortex_m::asm::delay(50 * self.sysclk_speed / 1_000_000); + + Ok(()) + } + + pub fn poll<S>(&mut self, spi: &mut S) -> nb::Result<TouchData, S::Error> + where + S: embedded_hal::spi::FullDuplex<u8>, + { + if self.dr_pin.is_high().unwrap_or(false) { + self.read_coords(spi) + } else { + Err(nb::Error::WouldBlock) + } + } + + fn read_coords<S>(&mut self, spi: &mut S) -> nb::Result<TouchData, S::Error> + where + S: embedded_hal::spi::FullDuplex<u8>, + { + let mut buf: [u8; 6] = [0xfc; 6]; + self.rd(spi, 0x12, &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 = buf[0] & 0x3f; + let is_pressed = x != 0; + + assert!(x < MAX_X); + assert!(y < MAX_Y); + + Ok(TouchData { + x, + y, + z, + buttons, + is_pressed, + }) + } + + fn rd<S>(&mut self, spi: &mut S, cmd: u8, buf: &mut [u8]) -> Result<(), S::Error> + where + S: embedded_hal::spi::FullDuplex<u8>, + { + let cmd = cmd | READ_MASK; + self.cs_pin.set_low().ok(); + let res = spi + .write(&[cmd, 0xfc, 0xfc]) + .and_then(|_| spi.transfer(buf)); + self.cs_pin.set_high().ok(); + + Ok(()) + } + + fn wr<S>(&mut self, spi: &mut S, addr: u8, data: u8) -> Result<(), S::Error> + where + S: embedded_hal::spi::FullDuplex<u8>, + { + let cmd = addr | WRITE_MASK; + self.cs_pin.set_low().ok(); + let res = spi.write(&[cmd, data]); + self.cs_pin.set_high().ok(); + + res + } +} diff --git a/src/main.rs b/src/main.rs index d0dfe1a..ce5f346 100755 --- a/src/main.rs +++ b/src/main.rs @@ -3,9 +3,11 @@ //extern crate panic_semihosting; +mod cirque; mod led; mod log; +use cirque::Cirque; use led::LED; use cortex_m::{ @@ -82,7 +84,7 @@ fn main() -> ! { phase: Phase::CaptureOnSecondTransition, polarity: Polarity::IdleHigh, }; - let spi = Spi::spi1( + let mut spi = Spi::spi1( dp.SPI1, (sck_pin, miso_pin, mosi_pin), &mut afio.mapr, @@ -90,6 +92,7 @@ fn main() -> ! { 1.MHz(), clocks, ); + let mut cirque = Cirque::new(cs_pin, dr_pin, &mut spi, clocks).expect("couldn't init touch pad"); // BluePill board has a pull-up resistor on the D+ line. // Pull the D+ pin down to send a RESET condition to the USB bus. @@ -119,6 +122,12 @@ fn main() -> ! { loop { logln!("."); + + if let Ok(mut td) = cirque.poll(&mut spi) { + td.scale_to(1920, 1080); + logln!("td: {:?}", td); + } + if !usb_dev.poll(&mut [&mut serial]) { continue; } |