1use embedded_hal_async::i2c::I2c;
22
23pub const DEFAULT_ADDRESS: u8 = 0x34;
25
26pub const FIFO_DEPTH: usize = 10;
28
29mod reg {
30 pub const CFG: u8 = 0x01;
31 pub const INT_STAT: u8 = 0x02;
32 pub const KEY_LCK_EC: u8 = 0x03;
33 pub const KEY_EVENT_A: u8 = 0x04;
34 pub const KP_GPIO1: u8 = 0x1D;
35 pub const KP_GPIO2: u8 = 0x1E;
36 pub const KP_GPIO3: u8 = 0x1F;
37}
38
39mod cfg {
40 pub const KE_IEN: u8 = 0x01;
42 pub const INT_CFG: u8 = 0x10;
45}
46
47const INT_STAT_ALL: u8 = 0x1F;
49
50#[derive(Clone, Copy, Debug, PartialEq, Eq)]
55#[cfg_attr(feature = "defmt", derive(defmt::Format))]
56pub struct Matrix {
57 rows: u8,
58 cols: u8,
59}
60
61impl Matrix {
62 pub const fn new(rows: u8, cols: u8) -> Self {
68 assert!(rows >= 1 && rows <= 8, "TCA8418 has 8 row pins");
69 assert!(cols >= 1 && cols <= 10, "TCA8418 has 10 column pins");
70 Self { rows, cols }
71 }
72
73 const fn kp_gpio(self) -> [u8; 3] {
75 let rows = ((1u16 << self.rows) - 1) as u8;
76 let cols = (1u16 << self.cols) - 1;
77 [rows, cols as u8, (cols >> 8) as u8]
78 }
79}
80
81#[derive(Clone, Copy, Debug, PartialEq, Eq)]
83#[cfg_attr(feature = "defmt", derive(defmt::Format))]
84pub struct KeyEvent {
85 pub row: u8,
87 pub col: u8,
89 pub pressed: bool,
91}
92
93#[derive(Clone, Copy, Debug, PartialEq, Eq)]
95#[cfg_attr(feature = "defmt", derive(defmt::Format))]
96pub enum Event {
97 Key(KeyEvent),
99 Gpi {
101 pin: u8,
103 active: bool,
105 },
106}
107
108impl Event {
109 pub const fn decode(raw: u8) -> Option<Self> {
114 let pressed = raw & 0x80 != 0;
115 match raw & 0x7F {
116 code @ 1..=80 => {
117 let n = code - 1;
118 Some(Event::Key(KeyEvent {
119 row: n / 10,
120 col: n % 10,
121 pressed,
122 }))
123 }
124 code @ 97..=114 => Some(Event::Gpi {
125 pin: code - 97,
126 active: pressed,
127 }),
128 _ => None,
129 }
130 }
131}
132
133pub struct Tca8418<I2C> {
135 i2c: I2C,
136 addr: u8,
137}
138
139impl<I2C: I2c> Tca8418<I2C> {
140 pub const fn new(i2c: I2C, addr: u8) -> Self {
142 Self { i2c, addr }
143 }
144
145 pub async fn configure(&mut self, matrix: Matrix) -> Result<(), I2C::Error> {
148 let [g1, g2, g3] = matrix.kp_gpio();
149 self.write(reg::KP_GPIO1, g1).await?;
150 self.write(reg::KP_GPIO2, g2).await?;
151 self.write(reg::KP_GPIO3, g3).await?;
152 let mut scratch = [0; FIFO_DEPTH];
154 self.drain(&mut scratch).await?;
155 self.clear_interrupts().await?;
156 self.write(reg::CFG, cfg::KE_IEN | cfg::INT_CFG).await
157 }
158
159 pub async fn pending(&mut self) -> Result<u8, I2C::Error> {
161 Ok(self.read(reg::KEY_LCK_EC).await? & 0x0F)
162 }
163
164 pub async fn drain<'b>(
167 &mut self,
168 buf: &'b mut [u8; FIFO_DEPTH],
169 ) -> Result<&'b [u8], I2C::Error> {
170 let count = usize::from(self.pending().await?).min(FIFO_DEPTH);
171 for slot in &mut buf[..count] {
172 *slot = self.read(reg::KEY_EVENT_A).await?;
174 }
175 Ok(&buf[..count])
176 }
177
178 pub async fn clear_interrupts(&mut self) -> Result<(), I2C::Error> {
180 self.write(reg::INT_STAT, INT_STAT_ALL).await
181 }
182
183 pub fn release(self) -> I2C {
185 self.i2c
186 }
187
188 async fn write(&mut self, reg: u8, value: u8) -> Result<(), I2C::Error> {
189 self.i2c.write(self.addr, &[reg, value]).await
190 }
191
192 async fn read(&mut self, reg: u8) -> Result<u8, I2C::Error> {
193 let mut out = [0];
194 self.i2c.write_read(self.addr, &[reg], &mut out).await?;
195 Ok(out[0])
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 extern crate std;
202 use super::*;
203 use crate::test_util::block_on;
204 use embedded_hal_async::i2c::{ErrorType, Operation};
205 use std::collections::VecDeque;
206 use std::vec::Vec;
207
208 #[derive(Default)]
211 struct FakeChip {
212 regs: [u8; 32],
213 fifo: VecDeque<u8>,
214 writes: Vec<(u8, u8)>,
215 }
216
217 impl ErrorType for FakeChip {
218 type Error = core::convert::Infallible;
219 }
220
221 impl I2c for FakeChip {
222 async fn transaction(
223 &mut self,
224 addr: u8,
225 ops: &mut [Operation<'_>],
226 ) -> Result<(), Self::Error> {
227 assert_eq!(addr, DEFAULT_ADDRESS);
228 let mut ptr = 0u8;
229 for op in ops {
230 match op {
231 Operation::Write(bytes) => {
232 ptr = bytes[0];
233 if let [reg, value] = bytes[..] {
234 self.writes.push((reg, value));
235 self.regs[usize::from(reg)] = value;
236 }
237 }
238 Operation::Read(out) => {
239 out[0] = match ptr {
240 reg::KEY_LCK_EC => self.fifo.len() as u8,
241 reg::KEY_EVENT_A => self.fifo.pop_front().unwrap_or(0),
242 r => self.regs[usize::from(r)],
243 };
244 }
245 }
246 }
247 Ok(())
248 }
249 }
250
251 #[test]
252 fn decode_key_codes() {
253 assert_eq!(
255 Event::decode(0x81),
256 Some(Event::Key(KeyEvent {
257 row: 0,
258 col: 0,
259 pressed: true
260 }))
261 );
262 assert_eq!(
263 Event::decode(80),
264 Some(Event::Key(KeyEvent {
265 row: 7,
266 col: 9,
267 pressed: false
268 }))
269 );
270 assert_eq!(
271 Event::decode(0x80 | 23),
272 Some(Event::Key(KeyEvent {
273 row: 2,
274 col: 2,
275 pressed: true
276 }))
277 );
278 assert_eq!(
279 Event::decode(0x80 | 97),
280 Some(Event::Gpi {
281 pin: 0,
282 active: true
283 })
284 );
285 assert_eq!(Event::decode(0), None);
286 assert_eq!(Event::decode(85), None);
287 }
288
289 #[test]
290 fn matrix_pin_masks() {
291 assert_eq!(Matrix::new(7, 8).kp_gpio(), [0x7F, 0xFF, 0x00]);
292 assert_eq!(Matrix::new(8, 10).kp_gpio(), [0xFF, 0xFF, 0x03]);
293 assert_eq!(Matrix::new(1, 9).kp_gpio(), [0x01, 0xFF, 0x01]);
294 }
295
296 #[test]
297 #[should_panic]
298 fn matrix_rejects_oversize() {
299 let _ = Matrix::new(9, 8);
300 }
301
302 #[test]
303 fn configure_flushes_stale_events_then_enables_irq() {
304 let mut chip = FakeChip::default();
305 chip.fifo.extend([0x81, 0x01]);
306 let mut kp = Tca8418::new(chip, DEFAULT_ADDRESS);
307 block_on(kp.configure(Matrix::new(7, 8))).unwrap();
308 let chip = kp.release();
309
310 assert!(chip.fifo.is_empty(), "stale events must be discarded");
311 assert_eq!(
312 chip.writes,
313 [
314 (reg::KP_GPIO1, 0x7F),
315 (reg::KP_GPIO2, 0xFF),
316 (reg::KP_GPIO3, 0x00),
317 (reg::INT_STAT, INT_STAT_ALL),
318 (reg::CFG, cfg::KE_IEN | cfg::INT_CFG),
319 ]
320 );
321 }
322
323 #[test]
324 fn drain_returns_events_in_fifo_order() {
325 let mut chip = FakeChip::default();
326 chip.fifo.extend([0x81, 0x8C, 0x01]);
327 let mut kp = Tca8418::new(chip, DEFAULT_ADDRESS);
328 let mut buf = [0; FIFO_DEPTH];
329 let events = block_on(kp.drain(&mut buf)).unwrap();
330 assert_eq!(events, [0x81, 0x8C, 0x01]);
331 assert_eq!(block_on(kp.pending()).unwrap(), 0);
332 }
333}