aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cirque.rs255
-rwxr-xr-xsrc/main.rs11
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;
}