Compare commits
6 commits
9ea123186d
...
95d5a18e99
| Author | SHA1 | Date | |
|---|---|---|---|
| 95d5a18e99 | |||
| 2d11808a91 | |||
| 0e697b48f1 | |||
| 597d2584b1 | |||
| 382a018b22 | |||
| 4967aa362f |
3 changed files with 167 additions and 0 deletions
9
Cargo.lock
generated
9
Cargo.lock
generated
|
|
@ -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",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -4,3 +4,4 @@ version = "0.1.0"
|
|||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
embedded-hal = "1.0.0"
|
||||
|
|
|
|||
157
src/lib.rs
157
src/lib.rs
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue