diff --git a/Cargo.lock b/Cargo.lock index 51d5acb9001c80f18706e90c8eabe911ac557a42..3d13b31af886ac8bcf7362dd91a3483930447c51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,7 +9,6 @@ dependencies = [ "byte-slice-cast 1.2.2", "embedded-hal 0.2.7", "embedded-hal 1.0.0-alpha.10", - "esp-println", "heapless", ] diff --git a/README.md b/README.md index 95f90293e998cb4173f07ebf1e47542acc8d5805..eaddd0f8ba4bd20f6e7c1971d8f2ce67b6fa5c83 100644 --- a/README.md +++ b/README.md @@ -17,11 +17,11 @@ This repo provides a pure-rust board support crate / runtime + drivers for the h - [x] LEDs - [x] Port expanders - [x] Input rockers -- [ ] IMU: possibly working, could not fully test due to broken IMU on my prototype -- [ ] Captouch: driver partially implemented, however currently broken +- [ ] IMU: most probably working, could not fully test due to broken IMU on my prototype -> HELP WANTED FOR TESTING! +- [x] Captouch: driver ~~partially~~ mostly implemented, ~~however currently broken~~, working again, API needs some love though - [ ] BadgeLink/BadgeNet: partially implemented, UART/SLIP driver for embassy-net implemented, interop with C/MicroPython Firmware not tested yet, currently no switching implemented -- [ ] SD-Card: in progress -- [ ] Barometer: not implemented yet +- [ ] SD-Card: not implemented yet +- [ ] Barometer: should come mostly for free with IMU, but could not test that yet - [ ] Audio: not implemented yet If you are interested in implementing or testing one of the missing features, I am always happy for any help. Just open an issue or reach out via matrix (@zdmx:xatellite.space) or e-mail. diff --git a/ad7147/Cargo.lock b/ad7147/Cargo.lock index cabe226a2c62aefcd420598e1d2bc7f968440beb..203259657913be61992fb15738f8046486955c81 100644 --- a/ad7147/Cargo.lock +++ b/ad7147/Cargo.lock @@ -9,7 +9,6 @@ dependencies = [ "byte-slice-cast", "embedded-hal 0.2.7", "embedded-hal 1.0.0-alpha.10", - "esp-println", "heapless", ] @@ -62,15 +61,6 @@ version = "1.0.0-alpha.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f65c4d073f5d91c66e629b216818a4c9747eeda0debedf2deda9a0a947e4e93b" -[[package]] -name = "esp-println" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af6a511d37dba5fb8f01bf5485bc619a1a1959e1aaf666a7597df8fe615a0816" -dependencies = [ - "critical-section", -] - [[package]] name = "hash32" version = "0.2.1" diff --git a/ad7147/Cargo.toml b/ad7147/Cargo.toml index 2c2580aab1b184582cea2eb129bef9b68a907daf..29bbf6e2e5cb676b5ce9dc78d82d990a25e60455 100644 --- a/ad7147/Cargo.toml +++ b/ad7147/Cargo.toml @@ -2,11 +2,12 @@ name = "ad7147" version = "0.1.0" edition = "2021" +description = "embedded-hal driver for AD7147" +license = "MIT OR Apache-2.0" authors = ["zdmx <hi@zdmx.me>"] [dependencies] byte-slice-cast = { version = "1.2.2", default-features = false } embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } embedded-hal = "0.2.7" -esp-println = { version = "0.5.0", features = ["esp32s3"] } heapless = "0.7.16" diff --git a/ad7147/src/device.rs b/ad7147/src/device.rs index e73a612fda5b01e493cf5d613d65cce20e31148f..04a55a32c82d271611a1bb3911356ed5de7a4731 100644 --- a/ad7147/src/device.rs +++ b/ad7147/src/device.rs @@ -241,7 +241,7 @@ impl<STATE, const S: usize> ConfigurationBuilder<STATE, S> { stage_low_int_enable: self.configuration.stage_low_int_enable, stage_high_int_enable: self.configuration.stage_high_int_enable, stage_complete_int_enable: self.configuration.stage_complete_int_enable, - stages: stages, + stages, }, _state: WithStages, }; @@ -338,10 +338,10 @@ pub(crate) struct CalibrationEnable { impl CalibrationEnable { pub(crate) const REGISTER: u16 = 0x001; - pub(crate) fn to_reg_value(&self) -> u16 { + pub(crate) fn to_reg_value(self) -> u16 { let mut res = 0u16; for (i, val) in self.stages_enable.iter().enumerate() { - res |= (val.clone() as u16) << i; + res |= (*val as u16) << i; } res |= (self.avg_fp_skip as u16) << 12; res |= (self.avg_lp_skip as u16) << 14; @@ -385,7 +385,7 @@ pub(crate) struct AmbientCompensationControl { impl AmbientCompensationControl { pub(crate) const REGISTER: u16 = 0x002; - pub(crate) fn to_reg_value(&self) -> [u16; 3] { + pub(crate) fn to_reg_value(self) -> [u16; 3] { let mut res = [0u16; 3]; res[0] |= self.fast_filter_skip as u16; res[0] |= (self.full_power_proximity_disable as u16) << 4; @@ -439,10 +439,10 @@ pub(crate) struct StageLowIntEnable { impl StageLowIntEnable { pub(crate) const _REGISTER: u16 = 0x005; - pub(crate) fn to_reg_value(&self) -> u16 { + pub(crate) fn to_reg_value(self) -> u16 { let mut res = 0u16; for (i, val) in self.stages_enable.iter().enumerate() { - res |= (val.clone() as u16) << i; + res |= (*val as u16) << i; } res |= (self.gpio_setup as u16) << 12; res |= (self.gpio_input_config as u16) << 14; @@ -480,10 +480,10 @@ pub(crate) struct StageHighIntEnable { impl StageHighIntEnable { pub(crate) const _REGISTER: u16 = 0x006; - pub(crate) fn to_reg_value(&self) -> u16 { + pub(crate) fn to_reg_value(self) -> u16 { let mut res = 0u16; for (i, val) in self.stages_enable.iter().enumerate() { - res |= (val.clone() as u16) << i; + res |= (*val as u16) << i; } res } @@ -501,10 +501,10 @@ pub(crate) struct StageCompleteIntEnable { impl StageCompleteIntEnable { pub(crate) const _REGISTER: u16 = 0x007; - pub(crate) fn to_reg_value(&self) -> u16 { + pub(crate) fn to_reg_value(self) -> u16 { let mut res = 0u16; for (i, val) in self.stages_enable.iter().enumerate() { - res |= (val.clone() as u16) << i; + res |= (*val as u16) << i; } res } diff --git a/ad7147/src/driver.rs b/ad7147/src/driver.rs index 164d5adf587b053f329d0703d13e6f05d627ebe4..6e55a5b2cea34adc1d1929b735540ececb9f96fc 100644 --- a/ad7147/src/driver.rs +++ b/ad7147/src/driver.rs @@ -1,14 +1,9 @@ use byte_slice_cast::AsByteSlice; -use embedded_hal_1::delay::DelayUs; use embedded_hal::blocking::i2c::{Write, WriteRead}; -use esp_println::println; - -use crate::{ - device::{ - AmbientCompensationControl, CalibrationEnable, DeviceConfiguration, - InternalDeviceConfiguration, - }, - interrupt::InterruptStatus, +use embedded_hal_1::delay::DelayUs; + +use crate::device::{ + AmbientCompensationControl, CalibrationEnable, DeviceConfiguration, InternalDeviceConfiguration, }; const STAGE_CONVERSION_RESULT_REGISTER: u16 = 0x0B; @@ -54,13 +49,15 @@ where data[data_idx..data_idx + 8].copy_from_slice(&stage.to_reg_value()); } //println!("writing to device: addr: {:016b} stage0: {:016b}, stage0: {:016b}", data[0], data[1], data[2]); - self.i2c.write(self.address, to_be_array(data).as_byte_slice())?; + self.i2c + .write(self.address, to_be_array(data).as_byte_slice())?; let mut data = [0u16; 9]; data[0] = 0x000; data[1..9].copy_from_slice(&configuration.0.to_reg_value_for_init()); //println!("writing to device: addr: {:016b} pwr_cfg: {:016b}, stage_cal: {:016b}, amb_comp_1: {:016b}, amb_comp_2: {:016b}, amb_comp_3: {:016b}, stage_low_int: {:016b}, stage_high_int: {:016b}, stage_complete_int: {:016b}", data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8]); - self.i2c.write(self.address, to_be_array(data).as_byte_slice())?; + self.i2c + .write(self.address, to_be_array(data).as_byte_slice())?; delay.delay_ms(100); @@ -68,7 +65,8 @@ where data[0] = CalibrationEnable::REGISTER; data[1] = configuration.0.calibration_enable.to_reg_value(); //println!("writing to device: addr: {:016b} stage_cal: {:016b}", data[0], data[1]); - self.i2c.write(self.address, to_be_array(data).as_byte_slice())?; + self.i2c + .write(self.address, to_be_array(data).as_byte_slice())?; Ok(Ad7147 { i2c: self.i2c, @@ -84,52 +82,61 @@ where I2C: WriteRead<Error = E> + Write<Error = E>, { pub fn read_all_stages(&mut self) -> Result<[u16; S], E> { - let mut result = [0u8; 12*2]; - self.i2c - .write_read(self.address, to_be_array([STAGE_CONVERSION_RESULT_REGISTER]).as_byte_slice(), &mut result)?; + let mut result = [0u8; 12 * 2]; + self.i2c.write_read( + self.address, + to_be_array([STAGE_CONVERSION_RESULT_REGISTER]).as_byte_slice(), + &mut result, + )?; //println!("read from device {:x?}", result); Ok(to_le_u16_array(&result)) } - pub fn read_interrupt_registers(&mut self) -> Result<InterruptStatus, E> { + pub fn read_interrupt_registers(&mut self) -> Result<[u16; 3], E> { let mut data = [0u8; 6]; - self.i2c - .write_read(self.address, to_be_array([STAGE_COMPLETED_INTERRUPT_RESULT_REGISTER]).as_byte_slice(), &mut data)?; + self.i2c.write_read( + self.address, + to_be_array([STAGE_COMPLETED_INTERRUPT_RESULT_REGISTER]).as_byte_slice(), + &mut data, + )?; //println!("read from device {:x?}", data); - Ok(InterruptStatus::from_registers(to_le_u16_array(&data))) + Ok(to_le_u16_array(&data)) } pub fn read_device_id(&mut self) -> Result<u16, E> { let mut data = [0u8; 2]; - self.i2c - .write_read(self.address, to_be_array([DEVICE_ID_REGISTER]).as_byte_slice(), &mut data)?; + self.i2c.write_read( + self.address, + to_be_array([DEVICE_ID_REGISTER]).as_byte_slice(), + &mut data, + )?; Ok(u16::from_be_bytes(data)) } pub fn reset(&mut self) -> Result<(), E> { - let mut acc_reg = self.config.ambient_comp_control.clone(); + let mut acc_reg = self.config.ambient_comp_control; acc_reg.conversion_reset = true; let mut data = [0u16; 4]; data[0] = AmbientCompensationControl::REGISTER; - data[1..8].copy_from_slice(&acc_reg.to_reg_value()); - self.i2c.write(self.address, to_be_array(data).as_byte_slice()) + data[1..4].copy_from_slice(&acc_reg.to_reg_value()); + self.i2c + .write(self.address, to_be_array(data).as_byte_slice()) } } - -fn to_be_array<const N: usize>(data: [u16;N]) -> [u16;N] { - let mut result = [0u16;N]; +fn to_be_array<const N: usize>(data: [u16; N]) -> [u16; N] { + let mut result = [0u16; N]; for (i, word) in data.into_iter().enumerate() { result[i] = word.to_be(); } result } -fn to_le_u16_array<const N: usize>(data: &[u8]) -> [u16;N] { - let mut result = [0u16;N]; +fn to_le_u16_array<const N: usize>(data: &[u8]) -> [u16; N] { + let mut result = [0u16; N]; for i in 0..N { - let (int_bytes, _) = data[i*2..].split_at(core::mem::size_of::<u16>()); + let (int_bytes, _) = data[i * 2..].split_at(core::mem::size_of::<u16>()); result[i] = u16::from_be_bytes(int_bytes.try_into().unwrap()); } result -} \ No newline at end of file +} diff --git a/ad7147/src/lib.rs b/ad7147/src/lib.rs index 1475c9120e08ccb4cf361b9d55b96d8ee46f6cc9..bd8734f1562bbd32a3bad9f07946640a62058076 100644 --- a/ad7147/src/lib.rs +++ b/ad7147/src/lib.rs @@ -2,7 +2,6 @@ pub mod device; mod driver; -mod interrupt; pub mod stage; pub use device::DeviceConfiguration; diff --git a/ad7147/src/stage.rs b/ad7147/src/stage.rs index e39376037ad0edc332d3f5b50ccaf87a23434204..2dfb74053144f25b7c20c4e739822f00b2906243 100644 --- a/ad7147/src/stage.rs +++ b/ad7147/src/stage.rs @@ -109,7 +109,7 @@ pub(crate) struct InternalStageConfiguration { } impl InternalStageConfiguration { - pub(crate) fn to_reg_value(&self) -> [u16; 8] { + pub(crate) fn to_reg_value(self) -> [u16; 8] { let mut res = [0u16; 8]; res[0..2].copy_from_slice(&self.input_configuration.to_reg_value()); res[1] |= (self.neg_afe_offset_disable as u16) << 14; @@ -141,7 +141,7 @@ enum InputMode { } impl InputMode { - pub(crate) fn to_reg_value(&self) -> [u16; 2] { + pub(crate) fn to_reg_value(self) -> [u16; 2] { let mut res = 0u32; match self { Self::NotConfigured => { @@ -206,7 +206,7 @@ struct AfeOffsetConfiguration { } impl AfeOffsetConfiguration { - pub(crate) fn to_reg_value(&self) -> u16 { + pub(crate) fn to_reg_value(self) -> u16 { let mut res = 0u16; res |= (self.neg_afe_offset & 0b00011111) as u16; res |= (self.neg_afe_offset_swap as u16) << 7; @@ -225,7 +225,7 @@ pub struct StageSensitivity { } impl StageSensitivity { - pub(crate) fn to_reg_value(&self) -> u16 { + pub(crate) fn to_reg_value(self) -> u16 { let mut res = 0u16; res |= self.neg_threshold_sensitivity as u16; res |= (self.neg_peak_detect as u16) << 4; diff --git a/src/demo_tasks.rs b/src/demo_tasks.rs index b0feda9d18230d726644422f6394e408df5749a9..1559a64011bb017c85b0119756e89c67127673bc 100644 --- a/src/demo_tasks.rs +++ b/src/demo_tasks.rs @@ -1,6 +1,6 @@ -use crate::flow3r::{display::Display, imu::ImuHandler}; +use crate::flow3r::{captouch::CaptouchHandler, display::Display, imu::ImuHandler}; use bmi270::AxisData; -use embassy_futures::select::{select, Either, select3, Either3}; +use embassy_futures::select::{select, select3, select_array, Either, Either3}; use embassy_time::{Duration, Timer}; use embedded_graphics::{ image::Image, @@ -65,7 +65,7 @@ pub async fn display_demo(display: &mut crate::flow3r::display::Display) { { Either3::First(_) => move_rectangle(&mut rect, &text, display, &rect_style, -5).await, Either3::Second(_) => move_rectangle(&mut rect, &text, display, &rect_style, 5).await, - Either3::Third(_) => break + Either3::Third(_) => break, } } } @@ -149,3 +149,126 @@ pub async fn imu_demo(display: &mut Display /*imu: &mut ImuHandler*/) { } } } + +pub async fn captouch_demo(display: &mut Display) { + let input = InputHandler; + let mut inputs = input.split(); + + let captouch = CaptouchHandler; + let top = captouch.top_petals(); + let bot = captouch.bottom_petals(); + + let circle_top_style = PrimitiveStyle::with_fill(Rgb565::YELLOW); + let circle_bot_style = PrimitiveStyle::with_fill(Rgb565::GREEN); + + display + .fill_solid(&display.bounding_box(), Rgb565::BLACK) + .unwrap(); + display.flush().await.unwrap(); + + loop { + match select(inputs.sw2_center.wait_for_press(), async { + display + .fill_solid(&display.bounding_box(), Rgb565::BLACK) + .unwrap(); + if top.petal0.pressed().await { + Circle::with_center(Point::new(120, 20), 20) + .draw_styled(&circle_top_style, display) + .unwrap(); + } else { + Circle::with_center(Point::new(120, 20), 10) + .draw_styled(&circle_top_style, display) + .unwrap(); + } + if top.petal2.pressed().await { + Circle::with_center(Point::new(215, 90), 20) + .draw_styled(&circle_top_style, display) + .unwrap(); + } else { + Circle::with_center(Point::new(215, 90), 10) + .draw_styled(&circle_top_style, display) + .unwrap(); + } + if top.petal4.pressed().await { + Circle::with_center(Point::new(178, 200), 20) + .draw_styled(&circle_top_style, display) + .unwrap(); + } else { + Circle::with_center(Point::new(178, 200), 10) + .draw_styled(&circle_top_style, display) + .unwrap(); + } + if top.petal6.pressed().await { + Circle::with_center(Point::new(61, 200), 20) + .draw_styled(&circle_top_style, display) + .unwrap(); + } else { + Circle::with_center(Point::new(61, 200), 10) + .draw_styled(&circle_top_style, display) + .unwrap(); + } + if top.petal8.pressed().await { + Circle::with_center(Point::new(25, 90), 20) + .draw_styled(&circle_top_style, display) + .unwrap(); + } else { + Circle::with_center(Point::new(25, 90), 10) + .draw_styled(&circle_top_style, display) + .unwrap(); + } + + if bot.petal1.pressed().await { + Circle::with_center(Point::new(178, 39), 20) + .draw_styled(&circle_bot_style, display) + .unwrap(); + } else { + Circle::with_center(Point::new(178, 39), 10) + .draw_styled(&circle_bot_style, display) + .unwrap(); + } + if bot.petal3.pressed().await { + Circle::with_center(Point::new(215, 150), 20) + .draw_styled(&circle_bot_style, display) + .unwrap(); + } else { + Circle::with_center(Point::new(215, 150), 10) + .draw_styled(&circle_bot_style, display) + .unwrap(); + } + if bot.petal5.pressed().await { + Circle::with_center(Point::new(120, 220), 20) + .draw_styled(&circle_bot_style, display) + .unwrap(); + } else { + Circle::with_center(Point::new(120, 220), 10) + .draw_styled(&circle_bot_style, display) + .unwrap(); + } + if bot.petal7.pressed().await { + Circle::with_center(Point::new(24, 150), 20) + .draw_styled(&circle_bot_style, display) + .unwrap(); + } else { + Circle::with_center(Point::new(24, 150), 10) + .draw_styled(&circle_bot_style, display) + .unwrap(); + } + if bot.petal9.pressed().await { + Circle::with_center(Point::new(61, 29), 20) + .draw_styled(&circle_bot_style, display) + .unwrap(); + } else { + Circle::with_center(Point::new(61, 29), 10) + .draw_styled(&circle_bot_style, display) + .unwrap(); + } + display.flush().await.unwrap(); + Timer::after(Duration::from_millis(50)).await; + }) + .await + { + Either::First(_) => break, + Either::Second(_) => (), + } + } +} diff --git a/src/flow3r/badgelink/badgelink.rs b/src/flow3r/badgelink/badgelink.rs new file mode 100644 index 0000000000000000000000000000000000000000..ecf93f782ef7f84f8f09e7e948a819b7618dc07e --- /dev/null +++ b/src/flow3r/badgelink/badgelink.rs @@ -0,0 +1,11 @@ + +pub fn init_badgelink_io(i2c: I2cProxy<'static, XtensaMutex<I2C<'static, I2C0>>>) { + let mut pe1 = port_expander::Max7321::new(shared_i2c.acquire_i2c(), true, true, true, false); + + let mut pe1_io = pe1.split(); + pe1_io.p1.set_high().unwrap(); + let _in_r_sw = pe1_io.p3; + let _in_t_sw = pe1_io.p4; + let _out_r_sw = pe1_io.p5; + let _out_t_sw = pe1_io.p6; +} \ No newline at end of file diff --git a/src/flow3r/captouch.rs b/src/flow3r/captouch.rs index 08d97ef238853a466d6c60ca3e2520aed1cd535a..9a7c977cd4e56698bb010f50547925c33fcc5b3c 100644 --- a/src/flow3r/captouch.rs +++ b/src/flow3r/captouch.rs @@ -1,10 +1,15 @@ use ad7147::{ device::DecimationFactor, - stage::{CapInput, CdcInput, InputConnection}, + stage::{CapInput, CdcInput, InputConnection, StageSensitivity, ThresholdSensitivity, PeakDetect}, Ad7147, DeviceConfiguration, Initialized, StageConfiguration, }; -use embassy_futures::select::select; -use embassy_time::{Delay, Duration, Timer}; +use embassy_futures::select::{select, Either}; +use embassy_sync::{ + blocking_mutex::raw::CriticalSectionRawMutex, + mutex::Mutex, + pubsub::{PubSubChannel, Subscriber}, +}; +use embassy_time::Delay; use embedded_hal_async::digital::Wait; use esp_println::println; use hal::{ @@ -14,7 +19,197 @@ use hal::{ }; use shared_bus::{I2cProxy, XtensaMutex}; -pub struct CapTouchHandler; +static CAPTOUCH_CHANNEL: PubSubChannel<CriticalSectionRawMutex, Flow3rCaptouchEvent, 32, 32, 32> = + PubSubChannel::<CriticalSectionRawMutex, Flow3rCaptouchEvent, 32, 32, 32>::new(); + +pub type CaptouchEventListener = + Subscriber<'static, CriticalSectionRawMutex, Flow3rCaptouchEvent, 32, 32, 32>; + +static PETALS: Mutex<CriticalSectionRawMutex, [Flow3rPetal; 10]> = Mutex::new([ + Flow3rPetal::new(0, Flow3rCaptouchController::TOP), + Flow3rPetal::new(1, Flow3rCaptouchController::BOTTOM), + Flow3rPetal::new(2, Flow3rCaptouchController::TOP), + Flow3rPetal::new(3, Flow3rCaptouchController::BOTTOM), + Flow3rPetal::new(4, Flow3rCaptouchController::TOP), + Flow3rPetal::new(5, Flow3rCaptouchController::BOTTOM), + Flow3rPetal::new(6, Flow3rCaptouchController::TOP), + Flow3rPetal::new(7, Flow3rCaptouchController::BOTTOM), + Flow3rPetal::new(8, Flow3rCaptouchController::TOP), + Flow3rPetal::new(9, Flow3rCaptouchController::BOTTOM), +]); + +#[derive(Debug, Clone, Copy)] +struct Flow3rCaptouchEvent { + petals: [Flow3rPetal; 5], +} + +#[derive(Debug, Clone, Copy)] +pub enum Flow3rPetal { + TOP { + number: u8, + ccw: Flow3rPetalPart, + base: Flow3rPetalPart, + cw: Flow3rPetalPart, + }, + BOTTOM { + number: u8, + base: Flow3rPetalPart, + tip: Flow3rPetalPart, + }, +} + +impl Flow3rPetal { + const fn new(num: u8, pos: Flow3rCaptouchController) -> Flow3rPetal { + match pos { + Flow3rCaptouchController::TOP => Self::TOP { + number: num, + ccw: Flow3rPetalPart { + pressed: false, + raw: 0, + }, + base: Flow3rPetalPart { + pressed: false, + raw: 0, + }, + cw: Flow3rPetalPart { + pressed: false, + raw: 0, + }, + }, + Flow3rCaptouchController::BOTTOM => Self::BOTTOM { + number: num, + base: Flow3rPetalPart { + pressed: false, + raw: 0, + }, + tip: Flow3rPetalPart { + pressed: false, + raw: 0, + }, + }, + } + } + + pub fn pressed(&self) -> bool { + match self { + Flow3rPetal::TOP { + number, + ccw, + base, + cw, + } => ccw.pressed | base.pressed | cw.pressed, + Flow3rPetal::BOTTOM { number, base, tip } => base.pressed | tip.pressed, + } + } + + pub fn position(&self) -> i32 { + match self { + Flow3rPetal::TOP { + number, + ccw, + base, + cw, + } => (ccw.raw as i32 + cw.raw as i32) / 2 - base.raw as i32, + Flow3rPetal::BOTTOM { number, base, tip } => base.raw as i32 + tip.raw as i32 / 2, + } + } + + pub fn number(&self) -> u8 { + match self { + Flow3rPetal::TOP { + number, + ccw, + base, + cw, + } => *number, + Flow3rPetal::BOTTOM { number, base, tip } => *number, + } + } + + fn update(&mut self, pressed: &[bool], values: &[u16]) { + match self { + Flow3rPetal::TOP { + number, + ccw, + base, + cw, + } => ccw.pressed = pressed[0], + Flow3rPetal::BOTTOM { number, base, tip } => todo!(), + } + } +} + +#[derive(Debug, Default, Clone, Copy)] +struct Flow3rPetalPart { + pressed: bool, + raw: u16, +} + +#[derive(Debug, Clone, Copy)] +enum Flow3rCaptouchController { + BOTTOM, + TOP, +} + +pub struct CaptouchHandler; + +impl CaptouchHandler { + pub fn top_petals(&self) -> TopPetals { + TopPetals { + petal0: Petal::new(0), + petal2: Petal::new(2), + petal4: Petal::new(4), + petal6: Petal::new(6), + petal8: Petal::new(8), + } + } + + pub fn bottom_petals(&self) -> BottomPetals { + BottomPetals { + petal1: Petal::new(1), + petal3: Petal::new(3), + petal5: Petal::new(5), + petal7: Petal::new(7), + petal9: Petal::new(9), + } + } +} + +pub struct Petal { + number: usize, +} + +impl Petal { + fn new(number: usize) -> Self { + Self { number } + } + + pub async fn pressed(&self) -> bool { + let fp = PETALS.lock().await[self.number]; + fp.pressed() + } + + pub async fn position(&self) -> i32 { + let fp = PETALS.lock().await[self.number]; + fp.position() + } +} + +pub struct TopPetals { + pub petal0: Petal, + pub petal2: Petal, + pub petal4: Petal, + pub petal6: Petal, + pub petal8: Petal, +} + +pub struct BottomPetals { + pub petal1: Petal, + pub petal3: Petal, + pub petal5: Petal, + pub petal7: Petal, + pub petal9: Petal, +} fn init_captouch( i2c_bot: I2cProxy<'static, XtensaMutex<I2C<'static, I2C0>>>, @@ -32,102 +227,114 @@ fn init_captouch( .stages([ StageConfiguration::builder() .calibration_enabled(true) - .conversion_complete_interrupt_enabled(true) .pos_afe_offset(2) .add_input_connection(InputConnection { cin: CapInput::CIN0, cdc: CdcInput::Positive, }) + .sensitivity(StageSensitivity { + neg_threshold_sensitivity: ThresholdSensitivity::Percent90, + neg_peak_detect: PeakDetect::Percent90, + pos_threshold_sensitivity: ThresholdSensitivity::Percent90, + pos_peak_detect: PeakDetect::Percent90, + }) + .initial_offset_high(55000) .build(), StageConfiguration::builder() .calibration_enabled(true) - .conversion_complete_interrupt_enabled(true) .pos_afe_offset(2) .add_input_connection(InputConnection { cin: CapInput::CIN1, cdc: CdcInput::Positive, }) + .sensitivity(StageSensitivity { + neg_threshold_sensitivity: ThresholdSensitivity::Percent90, + neg_peak_detect: PeakDetect::Percent90, + pos_threshold_sensitivity: ThresholdSensitivity::Percent90, + pos_peak_detect: PeakDetect::Percent90, + }) + .initial_offset_high(55000) .build(), StageConfiguration::builder() .calibration_enabled(true) - .conversion_complete_interrupt_enabled(true) .pos_afe_offset(2) .add_input_connection(InputConnection { cin: CapInput::CIN2, cdc: CdcInput::Positive, }) + .initial_offset_high(55000) .build(), StageConfiguration::builder() .calibration_enabled(true) - .conversion_complete_interrupt_enabled(true) .pos_afe_offset(2) .add_input_connection(InputConnection { cin: CapInput::CIN3, cdc: CdcInput::Positive, }) + .initial_offset_high(55000) .build(), StageConfiguration::builder() .calibration_enabled(true) - .conversion_complete_interrupt_enabled(true) .pos_afe_offset(2) .add_input_connection(InputConnection { cin: CapInput::CIN4, cdc: CdcInput::Positive, }) + .initial_offset_high(55000) .build(), StageConfiguration::builder() .calibration_enabled(true) - .conversion_complete_interrupt_enabled(true) .pos_afe_offset(2) .add_input_connection(InputConnection { cin: CapInput::CIN5, cdc: CdcInput::Positive, }) + .initial_offset_high(55000) .build(), StageConfiguration::builder() .calibration_enabled(true) - .conversion_complete_interrupt_enabled(true) .pos_afe_offset(2) .add_input_connection(InputConnection { cin: CapInput::CIN6, cdc: CdcInput::Positive, }) + .initial_offset_high(55000) .build(), StageConfiguration::builder() .calibration_enabled(true) - .conversion_complete_interrupt_enabled(true) .pos_afe_offset(2) .add_input_connection(InputConnection { cin: CapInput::CIN7, cdc: CdcInput::Positive, }) + .initial_offset_high(55000) .build(), StageConfiguration::builder() .calibration_enabled(true) - .conversion_complete_interrupt_enabled(true) .pos_afe_offset(2) .add_input_connection(InputConnection { cin: CapInput::CIN8, cdc: CdcInput::Positive, }) + .initial_offset_high(55000) .build(), StageConfiguration::builder() .calibration_enabled(true) - .conversion_complete_interrupt_enabled(true) .pos_afe_offset(2) .add_input_connection(InputConnection { cin: CapInput::CIN9, cdc: CdcInput::Positive, }) + .initial_offset_high(55000) .build(), StageConfiguration::builder() .calibration_enabled(true) - .conversion_complete_interrupt_enabled(true) .pos_afe_offset(30) .add_input_connection(InputConnection { cin: CapInput::CIN10, cdc: CdcInput::Positive, }) + .initial_offset_high(55000) .build(), StageConfiguration::builder() .calibration_enabled(true) @@ -137,6 +344,7 @@ fn init_captouch( cin: CapInput::CIN11, cdc: CdcInput::Positive, }) + .initial_offset_high(55000) .build(), ]) .build(); @@ -155,7 +363,6 @@ fn init_captouch( .build(), StageConfiguration::builder() .calibration_enabled(true) - .conversion_complete_interrupt_enabled(true) .pos_afe_offset(30) .add_input_connection(InputConnection { cin: CapInput::CIN1, @@ -164,7 +371,6 @@ fn init_captouch( .build(), StageConfiguration::builder() .calibration_enabled(true) - .conversion_complete_interrupt_enabled(true) .pos_afe_offset(28) .add_input_connection(InputConnection { cin: CapInput::CIN2, @@ -173,7 +379,6 @@ fn init_captouch( .build(), StageConfiguration::builder() .calibration_enabled(true) - .conversion_complete_interrupt_enabled(true) .pos_afe_offset(28) .add_input_connection(InputConnection { cin: CapInput::CIN3, @@ -182,7 +387,6 @@ fn init_captouch( .build(), StageConfiguration::builder() .calibration_enabled(true) - .conversion_complete_interrupt_enabled(true) .pos_afe_offset(28) .add_input_connection(InputConnection { cin: CapInput::CIN4, @@ -191,7 +395,6 @@ fn init_captouch( .build(), StageConfiguration::builder() .calibration_enabled(true) - .conversion_complete_interrupt_enabled(true) .pos_afe_offset(28) .add_input_connection(InputConnection { cin: CapInput::CIN5, @@ -200,7 +403,6 @@ fn init_captouch( .build(), StageConfiguration::builder() .calibration_enabled(true) - .conversion_complete_interrupt_enabled(true) .pos_afe_offset(28) .add_input_connection(InputConnection { cin: CapInput::CIN6, @@ -209,7 +411,6 @@ fn init_captouch( .build(), StageConfiguration::builder() .calibration_enabled(true) - .conversion_complete_interrupt_enabled(true) .pos_afe_offset(10) .add_input_connection(InputConnection { cin: CapInput::CIN7, @@ -218,7 +419,6 @@ fn init_captouch( .build(), StageConfiguration::builder() .calibration_enabled(true) - .conversion_complete_interrupt_enabled(true) .pos_afe_offset(20) .add_input_connection(InputConnection { cin: CapInput::CIN8, @@ -227,7 +427,6 @@ fn init_captouch( .build(), StageConfiguration::builder() .calibration_enabled(true) - .conversion_complete_interrupt_enabled(true) .pos_afe_offset(20) .add_input_connection(InputConnection { cin: CapInput::CIN9, @@ -236,7 +435,7 @@ fn init_captouch( .build(), StageConfiguration::builder() .calibration_enabled(true) - .conversion_complete_interrupt_enabled(true) + .conversion_complete_interrupt_enabled(false) .pos_afe_offset(8) .add_input_connection(InputConnection { cin: CapInput::CIN10, @@ -277,43 +476,146 @@ pub async fn captouch_controller( println!("captouch top device id: {:016b}", device_id); println!("initialized captouch"); loop { - //println!("int pin: {}", cap_b_int.is_input_high()); - select(cap_bot_int.wait_for_low(), cap_top_int.wait_for_low()); - let _ = ad7147_bot.read_interrupt_registers().unwrap(); - let _ = ad7147_top.read_interrupt_registers().unwrap(); - //println!("ints: {:?}", ints); - let measurement = ad7147_bot.read_all_stages().unwrap(); - /*println!( - "{} {} {} {} {} {} {} {} {} {} {} {}", - measurement[0], - measurement[1], - measurement[2], - measurement[3], - measurement[4], - measurement[5], - measurement[6], - measurement[7], - measurement[8], - measurement[9], - measurement[10], - measurement[11] - );*/ - let measurement = ad7147_top.read_all_stages().unwrap(); - /*println!( - "{} {} {} {} {} {} {} {} {} {} {} {}", - measurement[0], - measurement[1], - measurement[2], - measurement[3], - measurement[4], - measurement[5], - measurement[6], - measurement[7], - measurement[8], - measurement[9], - measurement[10], - measurement[11] - );*/ - Timer::after(Duration::from_millis(1000)).await; + match select(cap_bot_int.wait_for_low(), cap_top_int.wait_for_low()).await { + Either::First(_) => { + let interrupts = ad7147_bot.read_interrupt_registers().unwrap(); + let measurements_bot = ad7147_bot.read_all_stages().unwrap(); + // println!("pad 0: {}, {}, {}, {}", measurements_bot[0], measurements_bot[1], interrupts[1] & (1 << 0) != 0, interrupts[1] & (1 << 1) != 0); + update_petals_bot(interrupts[1], measurements_bot).await; + } + Either::Second(_) => { + let interrupts = ad7147_top.read_interrupt_registers().unwrap(); + let measurements_top = ad7147_top.read_all_stages().unwrap(); + update_petals_top(interrupts[1], measurements_top).await; + } + } + } +} + +#[derive(Debug, Copy, Clone)] +enum PetalPosition { + CCW, + BASE, + CW, + TIP, +} + +static PETAL_MAPPING_TOP: [(usize, PetalPosition); 12] = [ + (0, PetalPosition::BASE), + (0, PetalPosition::CCW), + (0, PetalPosition::CW), + (8, PetalPosition::CW), + (8, PetalPosition::CCW), + (8, PetalPosition::BASE), + (4, PetalPosition::BASE), + (4, PetalPosition::CCW), + (4, PetalPosition::CW), + (6, PetalPosition::CW), + (6, PetalPosition::CCW), + (6, PetalPosition::BASE), +]; + +async fn update_petals_top(interrupts: u16, measurements: [u16; 12]) { + let mut petals = PETALS.lock().await; + for (i, m) in PETAL_MAPPING_TOP.iter().enumerate() { + let pressed = measurements[i] > 50000; + match petals[m.0] { + Flow3rPetal::TOP { + ref mut ccw, + ref mut base, + ref mut cw, + .. + } => match m.1 { + PetalPosition::CCW => { + ccw.pressed = pressed; + ccw.raw = measurements[i] + } + PetalPosition::BASE => { + base.pressed = pressed; + base.raw = measurements[i] + } + PetalPosition::CW => { + cw.pressed = pressed; + cw.raw = measurements[i] + } + PetalPosition::TIP => panic!("top petal does not have tip"), + }, + Flow3rPetal::BOTTOM { + ref mut base, + ref mut tip, + .. + } => match m.1 { + PetalPosition::CCW => panic!("bottom petal does not have ccw"), + PetalPosition::BASE => { + base.pressed = pressed; + base.raw = measurements[i] + } + PetalPosition::CW => panic!("bottom petal does not have cw"), + PetalPosition::TIP => { + tip.pressed = pressed; + tip.raw = measurements[i] + } + }, + } + } +} + +static PETAL_MAPPPING_BOT: [(usize, PetalPosition); 12] = [ + (9, PetalPosition::BASE), + (9, PetalPosition::TIP), + (7, PetalPosition::BASE), + (7, PetalPosition::TIP), + (5, PetalPosition::BASE), + (5, PetalPosition::TIP), + (3, PetalPosition::BASE), + (3, PetalPosition::TIP), + (1, PetalPosition::BASE), + (1, PetalPosition::TIP), + (2, PetalPosition::BASE), + (2, PetalPosition::CW), +]; + +async fn update_petals_bot(interrupts: u16, measurements: [u16; 12]) { + let mut petals = PETALS.lock().await; + for (i, m) in PETAL_MAPPPING_BOT.iter().enumerate() { + let pressed = measurements[i] > 40000; + match petals[m.0] { + Flow3rPetal::TOP { + ref mut ccw, + ref mut base, + ref mut cw, + .. + } => match m.1 { + PetalPosition::CCW => { + ccw.pressed = pressed; + ccw.raw = measurements[i] + } + PetalPosition::BASE => { + base.pressed = pressed; + base.raw = measurements[i] + } + PetalPosition::CW => { + cw.pressed = pressed; + cw.raw = measurements[i] + } + PetalPosition::TIP => panic!("top petal does not have tip"), + }, + Flow3rPetal::BOTTOM { + ref mut base, + ref mut tip, + .. + } => match m.1 { + PetalPosition::CCW => panic!("bottom petal does not have ccw"), + PetalPosition::BASE => { + base.pressed = pressed; + base.raw = measurements[i] + } + PetalPosition::CW => panic!("bottom petal does not have cw"), + PetalPosition::TIP => { + tip.pressed = pressed; + tip.raw = measurements[i] + } + }, + } } } diff --git a/src/flow3r/imu.rs b/src/flow3r/imu.rs index b1ed43a13b6fca9e47462516896c4cd5490c2c08..2b06347ec9c4d2e5cfdd8b64b17aa7d443e541dc 100644 --- a/src/flow3r/imu.rs +++ b/src/flow3r/imu.rs @@ -1,5 +1,4 @@ use bmi270::{interface::I2cInterface, AuxData, AxisData, Bmi270, Error, PwrCtrl}; -use embassy_time::{Duration, Timer}; use esp_println::println; use hal::{i2c::I2C, peripherals::I2C0}; use shared_bus::{I2cProxy, XtensaMutex}; diff --git a/src/flow3r/input.rs b/src/flow3r/input.rs index ae557b14860b5eafa4d02ba83975efd217faadff..88553c48fc916ef804857cf4d2dbfd61ccfe7c2c 100644 --- a/src/flow3r/input.rs +++ b/src/flow3r/input.rs @@ -3,7 +3,6 @@ use embassy_sync::{ blocking_mutex::raw::CriticalSectionRawMutex, pubsub::{PubSubChannel, Subscriber, WaitResult}, }; -use embedded_hal_1::digital::ErrorKind; use embedded_hal_async::digital::Wait; use esp_println::println; use hal::{ diff --git a/src/flow3r/mod.rs b/src/flow3r/mod.rs index 185d2bcb0b1d0b334daffe863e3df0c01d2ec324..691711fefb361ec1f01fe29079f2f20cf1caab4b 100644 --- a/src/flow3r/mod.rs +++ b/src/flow3r/mod.rs @@ -1,7 +1,7 @@ use self::badgelink::BadgeLink; -use self::captouch::CapTouchHandler; +use self::captouch::CaptouchHandler; use self::display::Display; -use self::imu::ImuHandler; +//use self::imu::ImuHandler; use self::input::InputHandler; use self::leds::Leds; @@ -16,7 +16,7 @@ pub mod ui; pub struct Flow3r { pub badgelink: BadgeLink, - pub captouch: CapTouchHandler, + pub captouch: CaptouchHandler, pub display: Display, //pub imu: ImuHandler, pub inputs: InputHandler, @@ -26,7 +26,7 @@ pub struct Flow3r { impl Flow3r { pub fn new( badgelink: BadgeLink, - captouch: CapTouchHandler, + captouch: CaptouchHandler, display: Display, //imu: ImuHandler, inputs: InputHandler, diff --git a/src/flow3r/ui/main_menu.rs b/src/flow3r/ui/main_menu.rs index 9d68791cbf3b64aef3207841c51b455403d5fb50..aefd1b3f479ba4f60749163e1d99071b489ada7d 100644 --- a/src/flow3r/ui/main_menu.rs +++ b/src/flow3r/ui/main_menu.rs @@ -6,10 +6,9 @@ use embedded_graphics::{ prelude::*, text::Text, }; -use esp_println::println; use crate::{ - demo_tasks::{display_demo, imu_demo}, + demo_tasks::{display_demo, imu_demo, captouch_demo}, flow3r::{display::Display, input::InputHandler}, }; @@ -108,7 +107,7 @@ async fn start_current_app(app: &str, display: &mut Display) { match app { "input_test" => display_demo(display).await, "imu_test" => imu_demo(display).await, - "captouch_test" => {} + "captouch_test" => captouch_demo(display).await, _ => (), } } diff --git a/src/runtime.rs b/src/runtime.rs index 2794f2126796099b19b1f9b5d4a398c6a927f5b4..456dd2dbfb2390d3fdc56bd8a6ab1708ecfa8f75 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -18,9 +18,9 @@ use static_cell::StaticCell; use crate::flow3r::{ badgelink::BadgeLink, - captouch::{captouch_controller, CapTouchHandler}, + captouch::{captouch_controller, CaptouchHandler}, display::{display_refresh, Display}, - imu::ImuHandler, + //imu::ImuHandler, input::{input_controller, InputHandler}, leds::init_leds, Flow3r, @@ -178,7 +178,7 @@ async fn init_runtime() { let badgelink = BadgeLink::new(i2c_busmanager.acquire_i2c()); //let imu = ImuHandler::new(i2c_busmanager.acquire_i2c()); let inputs = InputHandler; - let captouch = CapTouchHandler; + let captouch = CaptouchHandler; let leds = init_leds(pulse.channel0, io.pins.gpio14); let flow3r = Flow3r::new(badgelink, captouch, display, inputs, leds);