Compare commits

...

6 commits

3 changed files with 167 additions and 0 deletions

9
Cargo.lock generated
View file

@ -2,6 +2,15 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "embedded-hal"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
[[package]]
name = "zel-tca8418"
version = "0.1.0"
dependencies = [
"embedded-hal",
]

View file

@ -4,3 +4,4 @@ version = "0.1.0"
edition = "2024"
[dependencies]
embedded-hal = "1.0.0"

View file

@ -1,2 +1,159 @@
#![no_std]
use embedded_hal::i2c::SevenBitAddress;
pub const I2C_ADDR: SevenBitAddress = 0b0_0110100;
pub struct TCA8418<I2C> {
i2c: I2C,
}
#[derive(Clone, Copy)]
pub struct MatrixConfig {
rows: u8,
columns: u16,
}
impl MatrixConfig {
/// A new config where the matrix has no rows or columns and all of the pins are
/// allocated for GPIO.
pub const fn new_empty() -> Self {
MatrixConfig {
rows: 0,
columns: 0,
}
}
/// Add a row to the keyboard matrix, meaning its pin will not be available for GPIO.
///
/// `row_number` must be in the range `0..=7`
pub const fn with_row(self, row_number: u8) -> Self {
if row_number >= 8 {
panic!("row_number out of bounds");
}
MatrixConfig {
rows: self.rows | (1 << row_number),
columns: self.columns,
}
}
/// Add a column to the keyboard matrix, meaning its pin will not be available for GPIO.
///
/// `column_number` must be in the range `0..=9`
pub const fn with_column(self, column_number: u8) -> Self {
if column_number >= 10 {
panic!("column_number out of bounds");
}
MatrixConfig {
rows: self.rows,
columns: self.columns | (1 << column_number),
}
}
}
#[derive(Clone, Copy)]
pub struct InterruptConfig(u8);
impl InterruptConfig {
/// A new interrupt with the following configuration:
/// - GPI events are tracked when keypad is locked
/// - Overflow data is lost
/// - Processor interrupt remains asserted (or low) if host tries to clear
/// interrupt while there is still a pending key press, key release or
/// GPI interrupt
/// - ~INT is not asserted if the FIFO overflows
/// - ~INT is not asserted after a correct unlock key sequence
/// - ~INT is not asserted for a change on a GPI
/// - ~INT is not asserted when a key event occurs
pub const fn new() -> Self {
Self(0)
}
pub const fn without_gpi_event_tracking_when_locked(self) -> Self {
InterruptConfig(self.0 | 0b0100_0000)
}
pub const fn with_overflow_data_shifting(self) -> Self {
InterruptConfig(self.0 | 0b0010_0000)
}
pub const fn with_temporary_interrupt_deassertion(self) -> Self {
InterruptConfig(self.0 | 0b0001_0000)
}
}
#[derive(Clone, Copy)]
pub struct KeySet(u128);
impl KeySet {
fn contains(&self, key_number: u8) -> bool {
(self.0 & (1 << key_number)) != 0
}
}
impl core::ops::Not for KeySet {
type Output = KeySet;
fn not(self) -> Self::Output {
KeySet(!self.0)
}
}
pub struct KeySetIter {
key_set: KeySet,
shift: u8,
}
impl core::iter::Iterator for KeySetIter {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
let next_shift = self.key_set.0.trailing_zeros() as u8 + 1;
if next_shift >= 128 {
return None;
}
self.key_set.0 >>= next_shift;
self.shift += next_shift;
Some(self.shift - 1)
}
}
impl core::iter::IntoIterator for KeySet {
type Item = u8;
type IntoIter = KeySetIter;
fn into_iter(self) -> Self::IntoIter {
KeySetIter {
key_set: self,
shift: 0
}
}
}
impl<I2C: embedded_hal::i2c::I2c> TCA8418<I2C> {
pub fn new(
i2c: I2C,
matrix_config: MatrixConfig,
interrupt_config: InterruptConfig,
) -> Self {
let mut self_ = Self { i2c };
self_.init(matrix_config, interrupt_config);
self_
}
fn init(&mut self, matrix_config: MatrixConfig, interrupt_config: InterruptConfig) {
self.i2c.write(I2C_ADDR, &[0x1D, matrix_config.rows]);
self.i2c
.write(I2C_ADDR, &[0x1E, matrix_config.columns as u8]);
self.i2c
.write(I2C_ADDR, &[0x1F, (matrix_config.columns >> 8) as u8]);
self.i2c.write(I2C_ADDR, &[0x01, interrupt_config.0]);
self.i2c.write(I2C_ADDR, &[0x0F, 0]);
self.i2c.write(I2C_ADDR, &[0x10, 0]);
self.i2c.write(I2C_ADDR, &[0x0E, 0]);
}
}