capsules_extra/
ft6x06.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! SyscallDriver for the FT6x06 Touch Panel.
6//!
7//! I2C Interface
8//!
9//! <http://www.tvielectronics.com/ocart/download/controller/FT6206.pdf>
10//!
11//! Usage
12//! -----
13//!
14//! ```rust,ignore
15//! let mux_i2c = components::i2c::I2CMuxComponent::new(&stm32f4xx::i2c::I2C1)
16//!     .finalize(components::i2c_mux_component_helper!());
17//!
18//! let ft6x06 = components::ft6x06::Ft6x06Component::new(
19//!     stm32f412g::gpio::PinId::PG05.get_pin().as_ref().unwrap(),
20//! )
21//! .finalize(components::ft6x06_i2c_component_helper!(mux_i2c));
22//!
23//! Author: Alexandru Radovici <msg4alex@gmail.com>
24
25#![allow(non_camel_case_types)]
26
27use core::cell::Cell;
28use enum_primitive::cast::FromPrimitive;
29use enum_primitive::enum_from_primitive;
30use kernel::hil::gpio;
31use kernel::hil::i2c;
32use kernel::hil::touch::{self, GestureEvent, TouchEvent, TouchStatus};
33use kernel::utilities::cells::{OptionalCell, TakeCell};
34use kernel::ErrorCode;
35
36pub static NO_TOUCH: TouchEvent = TouchEvent {
37    id: 0,
38    x: 0,
39    y: 0,
40    status: TouchStatus::Released,
41    size: None,
42    pressure: None,
43};
44
45enum_from_primitive! {
46    enum Registers {
47        REG_GEST_ID = 0x01,
48        REG_TD_STATUS = 0x02,
49        REG_CHIPID = 0xA3,
50    }
51}
52
53pub struct Ft6x06<'a, I: i2c::I2CDevice> {
54    i2c: &'a I,
55    interrupt_pin: &'a dyn gpio::InterruptPin<'a>,
56    touch_client: OptionalCell<&'a dyn touch::TouchClient>,
57    gesture_client: OptionalCell<&'a dyn touch::GestureClient>,
58    multi_touch_client: OptionalCell<&'a dyn touch::MultiTouchClient>,
59    num_touches: Cell<usize>,
60    buffer: TakeCell<'static, [u8]>,
61    events: TakeCell<'static, [TouchEvent]>,
62}
63
64impl<'a, I: i2c::I2CDevice> Ft6x06<'a, I> {
65    pub fn new(
66        i2c: &'a I,
67        interrupt_pin: &'a dyn gpio::InterruptPin<'a>,
68        buffer: &'static mut [u8],
69        events: &'static mut [TouchEvent],
70    ) -> Ft6x06<'a, I> {
71        // setup and return struct
72        interrupt_pin.enable_interrupts(gpio::InterruptEdge::FallingEdge);
73        Ft6x06 {
74            i2c,
75            interrupt_pin,
76            touch_client: OptionalCell::empty(),
77            gesture_client: OptionalCell::empty(),
78            multi_touch_client: OptionalCell::empty(),
79            num_touches: Cell::new(0),
80            buffer: TakeCell::new(buffer),
81            events: TakeCell::new(events),
82        }
83    }
84}
85
86impl<I: i2c::I2CDevice> i2c::I2CClient for Ft6x06<'_, I> {
87    fn command_complete(&self, buffer: &'static mut [u8], _status: Result<(), i2c::Error>) {
88        self.num_touches.set((buffer[1] & 0x0F) as usize);
89        self.touch_client.map(|client| {
90            if self.num_touches.get() <= 2 {
91                let status = match buffer[2] >> 6 {
92                    0x00 => Some(TouchStatus::Pressed),
93                    0x01 => Some(TouchStatus::Released),
94                    0x02 => Some(TouchStatus::Moved),
95                    _ => None,
96                };
97                if let Some(status) = status {
98                    let x = (((buffer[2] & 0x0F) as u16) << 8) + (buffer[3] as u16);
99                    let y = (((buffer[4] & 0x0F) as u16) << 8) + (buffer[5] as u16);
100                    let pressure = Some(buffer[6] as u16);
101                    let size = Some(buffer[7] as u16);
102                    client.touch_event(TouchEvent {
103                        status,
104                        x,
105                        y,
106                        id: 0,
107                        pressure,
108                        size,
109                    });
110                }
111            }
112        });
113        self.gesture_client.map(|client| {
114            if self.num_touches.get() <= 2 {
115                let gesture_event = match buffer[0] {
116                    0x10 => Some(GestureEvent::SwipeUp),
117                    0x14 => Some(GestureEvent::SwipeRight),
118                    0x18 => Some(GestureEvent::SwipeDown),
119                    0x1C => Some(GestureEvent::SwipeLeft),
120                    0x48 => Some(GestureEvent::ZoomIn),
121                    0x49 => Some(GestureEvent::ZoomOut),
122                    _ => None,
123                };
124                if let Some(gesture) = gesture_event {
125                    client.gesture_event(gesture);
126                }
127            }
128        });
129        self.multi_touch_client.map(|client| {
130            if self.num_touches.get() <= 2 {
131                let mut num_touches = 0;
132                for touch_event in 0..2 {
133                    let status = match buffer[touch_event * 6 + 2] >> 6 {
134                        0x00 => Some(TouchStatus::Pressed),
135                        0x01 => Some(TouchStatus::Released),
136                        0x02 => Some(TouchStatus::Moved),
137                        _ => None,
138                    };
139                    if let Some(status) = status {
140                        let x = (((buffer[touch_event * 6 + 2] & 0x0F) as u16) << 8)
141                            + (buffer[touch_event * 6 + 3] as u16);
142                        let y = (((buffer[touch_event * 6 + 4] & 0x0F) as u16) << 8)
143                            + (buffer[touch_event * 6 + 5] as u16);
144                        let pressure = Some(buffer[touch_event * 6 + 6] as u16);
145                        let size = Some(buffer[touch_event * 6 + 7] as u16);
146                        let id = (buffer[touch_event * 6 + 4] >> 4) as usize;
147                        self.events.map(|buffer| {
148                            buffer[num_touches] = TouchEvent {
149                                status,
150                                x,
151                                y,
152                                id,
153                                size,
154                                pressure,
155                            };
156                        });
157                        num_touches += 1;
158                    }
159                }
160                self.events.map(|buffer| {
161                    client.touch_events(buffer, num_touches);
162                });
163            }
164        });
165        self.buffer.replace(buffer);
166        self.interrupt_pin
167            .enable_interrupts(gpio::InterruptEdge::FallingEdge);
168    }
169}
170
171impl<I: i2c::I2CDevice> gpio::Client for Ft6x06<'_, I> {
172    fn fired(&self) {
173        self.buffer.take().map(|buffer| {
174            self.interrupt_pin.disable_interrupts();
175
176            buffer[0] = Registers::REG_GEST_ID as u8;
177
178            match self.i2c.write_read(buffer, 1, 15) {
179                Ok(()) => {}
180                Err((_err, buffer)) => {
181                    self.buffer.replace(buffer);
182                    self.interrupt_pin
183                        .enable_interrupts(gpio::InterruptEdge::FallingEdge);
184                }
185            }
186        });
187    }
188}
189
190impl<'a, I: i2c::I2CDevice> touch::Touch<'a> for Ft6x06<'a, I> {
191    fn enable(&self) -> Result<(), ErrorCode> {
192        Ok(())
193    }
194
195    fn disable(&self) -> Result<(), ErrorCode> {
196        Ok(())
197    }
198
199    fn set_client(&self, client: &'a dyn touch::TouchClient) {
200        self.touch_client.replace(client);
201    }
202}
203
204impl<'a, I: i2c::I2CDevice> touch::Gesture<'a> for Ft6x06<'a, I> {
205    fn set_client(&self, client: &'a dyn touch::GestureClient) {
206        self.gesture_client.replace(client);
207    }
208}
209
210impl<'a, I: i2c::I2CDevice> touch::MultiTouch<'a> for Ft6x06<'a, I> {
211    fn enable(&self) -> Result<(), ErrorCode> {
212        Ok(())
213    }
214
215    fn disable(&self) -> Result<(), ErrorCode> {
216        Ok(())
217    }
218
219    fn get_num_touches(&self) -> usize {
220        2
221    }
222
223    fn get_touch(&self, index: usize) -> Option<TouchEvent> {
224        self.buffer.map_or(None, |buffer| {
225            if index <= self.num_touches.get() {
226                // a touch has 7 bytes
227                let offset = index * 7;
228                let status = match buffer[offset + 1] >> 6 {
229                    0x00 => TouchStatus::Pressed,
230                    0x01 => TouchStatus::Released,
231                    0x02 => TouchStatus::Moved,
232                    _ => TouchStatus::Released,
233                };
234                let x = (((buffer[offset + 2] & 0x0F) as u16) << 8) + (buffer[offset + 3] as u16);
235                let y = (((buffer[offset + 4] & 0x0F) as u16) << 8) + (buffer[offset + 5] as u16);
236                let pressure = Some(buffer[offset + 6] as u16);
237                let size = Some(buffer[offset + 7] as u16);
238                Some(TouchEvent {
239                    status,
240                    x,
241                    y,
242                    id: 0,
243                    pressure,
244                    size,
245                })
246            } else {
247                None
248            }
249        })
250    }
251
252    fn set_client(&self, client: &'a dyn touch::MultiTouchClient) {
253        self.multi_touch_client.replace(client);
254    }
255}