ft6x06/
lib.rs

1//!
2//! A platform agnostic driver for FT6X06 touchscreen . Built using 'embedded-hal' traits.
3//!
4//! The Touchscreen driver for FT6X06 series touch panel controller
5//!
6//!
7//!  ### Example
8//!
9//! ##### Initializing the Ft6x06 driver struct
10//! 		let mut touch = ft6x06::Ft6X06::new(i2c, addr, ts_int).unwrap();
11
12#![no_std]
13#![no_main]
14
15pub mod constant;
16
17#[cfg(feature = "gesture")]
18use heapless::Vec;
19
20use crate::constant::*;
21use core::marker::PhantomData;
22use embedded_hal as hal;
23use hal::blocking::{
24    delay::{DelayMs, DelayUs},
25    i2c,
26};
27use hal::digital::v2::OutputPin;
28
29#[derive(Copy, Clone, Debug)]
30pub struct Ft6x06Capabilities {
31    #[allow(dead_code)]
32    multi_touch: bool,
33    #[allow(dead_code)]
34    gesture: bool,
35    #[allow(dead_code)]
36    max_touch: u8,
37    #[allow(dead_code)]
38    max_x_length: u16,
39    #[allow(dead_code)]
40    may_y_length: u16,
41}
42
43const TRUE: bool = true;
44const FALSE: bool = false;
45
46const FT6X06_CAPABILITIES: Ft6x06Capabilities = Ft6x06Capabilities {
47    multi_touch: TRUE,
48    //  Gesture is not set as per given in ft6x06 crate by STMicroelectronics
49    gesture: FALSE,
50    max_touch: FT6X06_MAX_NB_TOUCH as u8,
51    max_x_length: FT6X06_MAX_X_LENGTH,
52    may_y_length: FT6X06_MAX_Y_LENGTH,
53};
54
55/// Touch structure - derived from the available I2C registers.
56// #define FT6X06_P1_XH_REG            0x03U
57// #define FT6X06_P1_XL_REG            0x04U
58// #define FT6X06_P1_YH_REG            0x05U
59// #define FT6X06_P1_YL_REG            0X06U
60// #define FT6X06_P1_WEIGHT_REG        0x07U
61// #define FT6X06_P1_MISC_REG          0x08U
62//   followed by:
63// #define FT6X06_P2_XH_REG            0x09U
64// etc
65#[derive(Copy, Clone, Debug, PartialOrd, Ord, Eq, PartialEq)]
66pub struct TouchState {
67    /// Was a touch detected:
68    pub detected: bool,
69    /// X postion
70    pub x: u16,
71    /// Y position
72    pub y: u16,
73    /// Weight of touch
74    pub weight: u8,
75    /// Misc (contents not known)
76    pub misc: u8,
77}
78
79/// For storing multi-touch data
80pub struct MultiTouch {
81    pub detected: bool,
82    /// X postion
83    pub touch_x: [u16; 2],
84    /// Y position
85    pub touch_y: [u16; 2],
86    /// Weight of touch
87    pub touch_weight: [u16; 2],
88    /// Misc (contents not known)
89    pub touch_area: [u16; 2],
90}
91
92#[derive(Debug)]
93/// Possible choices of gesture
94pub enum GestureKind {
95    /// No gesture detected
96    None,
97    /// Up gesture
98    Up,
99    /// Right gesture
100    Right,
101    /// Down gesture
102    Down,
103    /// Left gesture
104    Left,
105    /// ZoomIn gesture
106    ZoomIn,
107    /// ZoomOut gesture
108    ZoomOut,
109    /// Fault gesture
110    Fault,
111}
112
113// Gestures don't seem to work using values of control registers and reading radian_value_reg.
114// I tried working with the GestureInit struct and i2c bus to read gestures but failed.
115// I removed its impl but kept the struct to give idea of how it is implementated in C.
116// This code for gestures is taken from ft5336 repo by bobgates,
117// but it seems control register values are not read in buffer of i2c bus.
118// So I created a algoritmic to detect gesture. The following struct just shows how
119// the gestures registers are to set.
120
121// Structure that holds the values for a gesture
122// The name is what's in the c code.
123// The register definitions are:
124// pub const FT6X06_RADIAN_VALUE_REG: u8 = 0x91;
125// pub const FT6X06_OFFSET_LR_REG: u8 = 0x92;
126// pub const FT6X06_OFFSET_UD_REG: u8 = 0x93;
127// pub const FT6X06_DISTANCE_LR_REG: u8 = 0x94;
128// pub const FT6X06_DISTANCE_UD_REG: u8 = 0x95;
129// pub const FT6X06_DISTANCE_ZOOM_REG: u8 = 0x96;
130
131#[allow(dead_code)]
132pub struct GestureInit<I2C> {
133    addr: u8,
134    i2c: PhantomData<I2C>,
135
136    /// radians required to sense a circle (probably not used)
137    pub radian: u8,
138    /// Offset distance left right
139    pub offset_left_right: u8,
140    /// Offset distance up down
141    pub offset_up_down: u8,
142    /// Distance for swipes left right
143    pub distance_left_right: u8,
144    /// Distance for swipes up down
145    pub distance_up_down: u8,
146    /// Distance for zoom
147    pub distance_zoom: u8,
148}
149
150/// FT6x06 driver object.
151/// I2C bus type and its address are set.
152pub struct Ft6X06<I2C, TouchInterruptPin> {
153    i2c: PhantomData<I2C>,
154    addr: u8,
155    interrupt: TouchInterruptPin,
156}
157
158/// Perform a long hard reset, the FT66206 needs at least 5mS ...
159//
160// - On the STM32F413 the touchscreen shares the reset GPIO pin w/ the LCD.
161// - The ST7789 driver uses a fast (10uS) reset.
162// - The touchscreen controller needs 5mS:
163//   https://www.displayfuture.com/Display/datasheet/controller/FT6206.pdf
164pub fn long_hard_reset<'a, RST, DELAY>(
165    rst: &'a mut RST,
166    delay: &'a mut DELAY,
167) -> Result<(), &'a str>
168where
169    RST: OutputPin,
170    DELAY: DelayUs<u32>,
171{
172    rst.set_low().map_err(|_| "rst.set_low failed")?;
173    delay.delay_us(10_000);
174    rst.set_high().map_err(|_| "rst.set_high failed")?;
175
176    Ok(())
177}
178
179impl<I2C, TouchInterruptPin: hal::digital::v2::InputPin, E> Ft6X06<I2C, TouchInterruptPin>
180where
181    I2C: i2c::WriteRead<Error = E> + i2c::Write<Error = E>,
182    E: core::fmt::Debug,
183{
184    /// Creates a new sensor associated with an I2C peripheral.
185    ///
186    /// Phantom I2C ensures that whatever I2C bus the device was created on is the one that is used for all future interations.
187    pub fn new(_i2c: &I2C, addr: u8, interrupt: TouchInterruptPin) -> Result<Self, E> {
188        let ft6x06 = Ft6X06 {
189            i2c: PhantomData,
190            addr: addr,
191            interrupt,
192        };
193        Ok(ft6x06)
194    }
195
196    /// Initialise device and disable interupt mode.
197    /// FT6X06 should be calibrated once after each power up.
198    pub fn init(&mut self, i2c: &mut I2C, delay_source: &mut impl DelayMs<u32>) {
199        // -> Result<Self, E> {
200        if FT6X06_AUTO_CALIBRATION_ENABLED {
201            self.ts_calibration(i2c, delay_source).unwrap();
202        }
203        // FT6X06_DisableIT(i2c)?;
204        // Ok(*self)
205    }
206
207    ///As the ft6X06 library owns the delay, the simplest way to
208    /// deliver it to the callign code seems to be to return a function call.
209    pub fn delay_ms(&mut self, delay_source: &mut impl DelayMs<u32>, delay: u32) {
210        delay_source.delay_ms(delay);
211    }
212
213    /// Returns the structure that contains all the preset capabilities
214    /// of the FT6X06
215    pub fn get_capabilities(&self) -> Ft6x06Capabilities {
216        FT6X06_CAPABILITIES
217    }
218
219    /// Read whether the FT5663 is in dev mode or not
220    pub fn dev_mode_r(&self, i2c: &mut I2C) -> Result<u8, E> {
221        let mut buf: [u8; 1] = [0];
222
223        i2c.write_read(self.addr, &[FT6X06_DEV_MODE_REG], &mut buf)?;
224
225        let mut value = buf[0];
226        value &= FT6X06_DEV_MODE_BIT_MASK;
227        value &= FT6X06_DEV_MODE_BIT_POSITION;
228
229        Ok(value)
230    }
231
232    /// Put the FT5663 into dev mode
233    pub fn dev_mode_w(&self, i2c: &mut I2C, value: u8) -> Result<bool, E> {
234        let mut buf: [u8; 1] = [0];
235
236        i2c.write_read(self.addr, &[FT6X06_DEV_MODE_REG], &mut buf)?;
237
238        let mut tmp = buf[0];
239
240        tmp &= !FT6X06_DEV_MODE_BIT_MASK;
241        tmp |= value << FT6X06_DEV_MODE_BIT_POSITION;
242
243        i2c.write(self.addr, &[tmp])?;
244
245        Ok(value == 0)
246    }
247
248    /// Get the value of an 8 bit register
249    pub fn get_u8_reg(&self, i2c: &mut I2C, reg: u8) -> Result<u8, E> {
250        let mut ibuf: [u8; 1] = [0];
251        i2c.write_read(self.addr, &[reg], &mut ibuf)?;
252        Ok(ibuf[0])
253    }
254
255    /// Set the value of an 8 bit register
256    pub fn set_u8_reg(&self, i2c: &mut I2C, reg: u8, val: u8) -> Result<(), E> {
257        let obuf: [u8; 2] = [reg, val];
258        i2c.write(self.addr, &obuf)?;
259        Ok(())
260    }
261
262    /// Wait for the touchscreen interrupt to indicate touches
263    pub fn wait_touch_interrupt(&self) {
264        while self
265            .interrupt
266            .is_high()
267            .unwrap_or_else(|_| panic!("trouble checking interrupt"))
268        {}
269    }
270
271    /// Run an internal calibration on the FT6X06
272    pub fn ts_calibration(
273        &mut self,
274        i2c: &mut I2C,
275        delay_source: &mut impl DelayMs<u32>,
276    ) -> Result<bool, &str> {
277        //} -> Result<Self, E> {
278        let mut _ret = FT6X06_OK;
279        let mut _nbr_attempt: u32;
280        let mut _read_data: u8;
281        let mut _end_calibration: u8;
282
283        let _result = self.dev_mode_w(i2c, FT6X06_DEV_MODE_FACTORY);
284
285        delay_source.delay_ms(300);
286
287        for _attempt in 0..100 {
288            match self.dev_mode_r(i2c) {
289                Err(_e) => return Err("Bad comms in ts_calibration"),
290                Ok(n) => {
291                    if n == FT6X06_DEV_MODE_WORKING {
292                        return Ok(true);
293                    }
294                }
295            }
296            delay_source.delay_ms(200);
297        }
298        Err("Calibration does not return")
299    }
300
301    /// Read the touch device status
302    pub fn td_status(&self, i2c: &mut I2C) -> Result<u8, E> {
303        let mut buf: [u8; 1] = [0];
304        i2c.write_read(self.addr, &[FT6X06_TD_STAT_REG], &mut buf)?;
305        Ok(buf[0])
306    }
307
308    /// Read the touch device chip ID. It should be 0x51 if it is the FT6X06 on the
309    /// stm32f746 Discovery board
310    pub fn chip_id(&self, i2c: &mut I2C) -> Result<u8, &str> {
311        let mut buf: [u8; 1] = [0];
312        match i2c.write_read(self.addr, &[FT6X06_CHIP_ID_REG], &mut buf) {
313            Err(_e) => Err("Chip ID call failed"),
314            Ok(_a) => {
315                if buf[0] != FT6X06_ID {
316                    Err("error in chip ID")
317                } else {
318                    Ok(buf[0])
319                }
320            }
321        }
322    }
323
324    /// Is the device being touched? If so, how many fingers?
325    pub fn detect_touch(&mut self, i2c: &mut I2C) -> Result<u8, E> {
326        let ntouch = loop {
327            let n = self.td_status(i2c)?;
328            if n > 0 {
329                break n;
330            }
331        };
332        assert!(ntouch <= FT6X06_MAX_NB_TOUCH as u8);
333        Ok(ntouch)
334    }
335
336    /// Retrieve the FT6X06 firmware id
337    pub fn firmware_id(&mut self, i2c: &mut I2C) -> Result<u8, &str> {
338        let mut buf: [u8; 1] = [0];
339        match i2c.write_read(self.addr, &[FT6X06_FIRMID_REG], &mut buf) {
340            Err(_e) => Err("Error getting firmware ID"),
341            Ok(_d) => Ok(buf[0]),
342        }
343    }
344
345    /// Retrieve the Gesture Init variable
346    pub fn gesture_radian_read(&mut self, i2c: &mut I2C) -> Result<u8, &str> {
347        let mut buf: [u8; 1] = [0];
348        match i2c.write_read(self.addr, &[FT6X06_RADIAN_VALUE_REG], &mut buf) {
349            Err(_e) => Err("Error getting Gesture Init: RADIAN VALUE REG"),
350            Ok(_d) => Ok(buf[0]),
351        }
352    }
353
354    /// Write the Gesture Init variable
355    pub fn gesture_radian_write(&self, i2c: &mut I2C, value: u8) -> Result<bool, E> {
356        let mut buf: [u8; 1] = [value];
357
358        i2c.write_read(self.addr, &[FT6X06_RADIAN_VALUE_REG], &mut buf)?;
359
360        Ok(value == 0)
361    }
362
363    /// Fetch the touch data specified by touch_i
364    /// touch_i should go from 1 to FT6X06_MAX_NB_TOUCH
365    pub fn get_touch(&mut self, i2c: &mut I2C, touch_i: u8) -> Result<TouchState, E> {
366        let mut buf: [u8; 6] = [0; 6];
367        i2c.write_read(self.addr, &[FT6X06_P1_XH_REG + 6 * (touch_i - 1)], &mut buf)?;
368
369        // Tried copying the c code literally here. It makes no difference though
370        let x: u16 = (FT6X06_P1_XH_TP_BIT_MASK & buf[0]) as u16 * 256 + buf[1] as u16;
371        let y: u16 = (FT6X06_P1_YH_TP_BIT_MASK & buf[2]) as u16 * 256 + buf[3] as u16;
372
373        Ok(TouchState {
374            detected: true,
375            x,
376            y,
377            weight: buf[4],
378            misc: buf[5],
379        })
380    }
381
382    /// Fetch the touch data specified by touch_i
383    /// touch_i should go from 1 to FT6X06_MAX_NB_TOUCH
384    pub fn get_multi_touch(&mut self, i2c: &mut I2C, touch_i: u8) -> Result<MultiTouch, E> {
385        let mut buf: [u8; 12] = [0; 12];
386        i2c.write_read(self.addr, &[FT6X06_P1_XH_REG + 6 * (touch_i - 1)], &mut buf)?;
387
388        let mut x: [u16; FT6X06_MAX_NB_TOUCH] = [0; FT6X06_MAX_NB_TOUCH];
389        let mut y: [u16; FT6X06_MAX_NB_TOUCH] = [0; FT6X06_MAX_NB_TOUCH];
390        let mut weight: [u16; FT6X06_MAX_NB_TOUCH] = [0; FT6X06_MAX_NB_TOUCH];
391        let mut misc: [u16; FT6X06_MAX_NB_TOUCH] = [0; FT6X06_MAX_NB_TOUCH];
392
393        let mut it: usize = 0;
394        for i in 0..FT6X06_MAX_NB_TOUCH {
395            x[i] = (FT6X06_P1_XH_TP_BIT_MASK & buf[0 + it]) as u16 * 256 + buf[1 + it] as u16;
396            y[i] = (FT6X06_P1_YH_TP_BIT_MASK & buf[2 + it]) as u16 * 256 + buf[3 + it] as u16;
397            weight[i] = buf[4 + it] as u16;
398            misc[i] = buf[5 + it] as u16;
399            it = it + 6;
400        }
401
402        Ok(MultiTouch {
403            detected: true,
404            touch_x: x,
405            touch_y: y,
406            touch_weight: weight,
407            touch_area: misc,
408        })
409    }
410
411    /// Get gestures interpreted by touchscreen
412    pub fn get_gesture(&mut self, i2c: &mut I2C) -> Result<GestureKind, E> {
413        let mut buf: [u8; 1] = [0];
414        i2c.write_read(self.addr, &[FT6X06_GEST_ID_REG], &mut buf)?;
415
416        let g: GestureKind = match buf[0] {
417            FT6X06_GEST_ID_NO_GESTURE => GestureKind::None,
418            FT6X06_GEST_ID_MOVE_UP => GestureKind::Up,
419            FT6X06_GEST_ID_MOVE_RIGHT => GestureKind::Right,
420            FT6X06_GEST_ID_MOVE_DOWN => GestureKind::Down,
421            FT6X06_GEST_ID_MOVE_LEFT => GestureKind::Left,
422            FT6X06_GEST_ID_ZOOM_IN => GestureKind::ZoomIn,
423            FT6X06_GEST_ID_ZOOM_OUT => GestureKind::ZoomOut,
424            _ => GestureKind::Fault,
425        };
426        Ok(g)
427    }
428
429    pub fn get_coordinates(&mut self, i2c: &mut I2C) -> Result<(u16, u16), E> {
430        self.wait_touch_interrupt();
431        let _ntouch = self.detect_touch(i2c)?;
432        let pt = self.get_touch(i2c, 1)?;
433        Ok((pt.x, pt.y))
434    }
435
436    //    /// Logic for getting the gesture.
437    //    #[cfg(feature = "gesture")]
438    //    pub fn gest_logic(&mut self, i2c: &mut I2C) -> Result<GestureKind, &str> {
439    //        let mut vec1: Vec<u16, 100> = Vec::new();
440    //        let mut vec2: Vec<u16, 100> = Vec::new();
441    //
442    //        for _i in 1..20 {
443    //            let a = self.get_coordinates(i2c);
444    //
445    //           match a {
446    //                Err(_e) => {
447    //                    rprintln!("err");
448    //                    continue;
449    //                }
450    //                Ok((x, y)) => {
451    //                    vec1.push(x).expect("err");
452    //                    vec2.push(y).expect("err");
453    //                }
454    //            };
455    //        }
456    //        let itr1 = vec1.iter();
457    //        let itr2 = vec2.iter();
458    //
459    //        let max_x: u16 = *itr1.max().expect("err");
460    //        let max_y: u16 = *itr2.max().expect("err");
461    //
462    //        let start_x: u16 = vec1[0];
463    //        let start_y: u16 = vec2[0];
464    //
465    //        let end_x: u16 = vec1[19];
466    //        let end_y: u16 = vec2[19];
467    //
468    //        let diff_x = end_x - start_x;
469    //        let diff_y = end_y - start_y;
470    //
471    //        if diff_x > 100 || diff_y > 100 {
472    //            return Err("wrong gestures.");
473    //        } else if diff_x > diff_y {
474    //            if diff_x > 0 {
475    //                return Ok(GestureKind::Right);
476    //            } else {
477    //                return Ok(GestureKind::Left);
478    //            }
479    //        } else if diff_x < diff_y {
480    //            if diff_y > 0 {
481    //                return Ok(GestureKind::Up);
482    //            } else {
483    //                return Ok(GestureKind::Left);
484    //            }
485    //        } else {
486    //            return Err("error gesture");
487    //        }
488    //    }
489}