From 7791cd61e58b63b9d65876c7cc5c54bf06470838 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Sun, 16 Jun 2024 22:38:08 +0100 Subject: [PATCH 1/7] We can talk to the keyboard! But the mouse port on my Elecrow board doesn't seem to work - I don't see 0xAA 0x00 on start-up from the mouse (but I do if I put the mouse in the keyboard port). --- neotron-bmc-pico/src/main.rs | 360 +++++++++++++++++++++++++++++++---- 1 file changed, 326 insertions(+), 34 deletions(-) diff --git a/neotron-bmc-pico/src/main.rs b/neotron-bmc-pico/src/main.rs index 8a6033d..1fb0ccf 100644 --- a/neotron-bmc-pico/src/main.rs +++ b/neotron-bmc-pico/src/main.rs @@ -19,7 +19,7 @@ use stm32f0xx_hal::{ gpio::gpioa::{PA10, PA11, PA12, PA15, PA2, PA3, PA4, PA8, PA9}, gpio::gpiob::{PB0, PB3, PB4, PB5}, gpio::gpiof::{PF0, PF1}, - gpio::{Alternate, Floating, Input, Output, PullDown, PullUp, PushPull, AF1}, + gpio::{Alternate, Floating, Input, OpenDrain, Output, PullDown, PullUp, PushPull, AF1}, pac, prelude::*, rcc, serial, @@ -100,6 +100,50 @@ mod app { SpeakerDisable, } + enum Ps2Clk0Pin { + /// We are receiving data from the keyboard + Input(PA15>), + /// We are sending data to the keyboard + Output(PA15>), + /// A temporary state + Neither, + } + + enum Ps2Data0Pin { + /// Data pin is in input mode + Input(PB4>), + /// Data pin is in output mode. + /// + /// Every clock edge shifts out a bit, until the value is all zeroes. + Output(PB4>, u16), + /// We are skipping the acknowledge bit + Skip(PB4>), + /// A temporary state + Neither, + } + + enum Ps2Clk1Pin { + /// We are receiving data from the keyboard + Input(PB3>), + /// We are sending data to the keyboard + Output(PB3>), + /// A temporary state + Neither, + } + + enum Ps2Data1Pin { + /// Data pin is in input mode + Input(PB5>), + /// Data pin is in output mode. + /// + /// Every clock edge shifts out a bit, until the value is all zeroes. + Output(PB5>, u16), + /// We are skipping the acknowledge bit + Skip(PB5>), + /// A temporary state + Neither, + } + #[shared] struct Shared { /// The power LED (D1101) @@ -132,16 +176,16 @@ mod app { pin_sys_reset: PA2>, /// Clock pin for PS/2 Keyboard port #[lock_free] - ps2_clk0: PA15>, + ps2_clk0: Ps2Clk0Pin, /// Clock pin for PS/2 Mouse port #[lock_free] - _ps2_clk1: PB3>, + ps2_clk1: Ps2Clk1Pin, /// Data pin for PS/2 Keyboard port #[lock_free] - ps2_dat0: PB4>, + ps2_dat0: Ps2Data0Pin, /// Data pin for PS/2 Mouse port #[lock_free] - _ps2_dat1: PB5>, + ps2_dat1: Ps2Data1Pin, /// The external interrupt peripheral #[lock_free] exti: pac::EXTI, @@ -154,8 +198,10 @@ mod app { spi: neotron_bmc_pico::spi::SpiPeripheral<5, 64>, /// CS pin pin_cs: PA4>, - /// Keyboard PS/2 decoder + /// Keyboard PS/2 interface kb_decoder: neotron_bmc_pico::ps2::Ps2Decoder, + /// Mouse PS/2 interface + mouse_decoder: neotron_bmc_pico::ps2::Ps2Decoder, } #[local] @@ -228,9 +274,9 @@ mod app { mut pin_dc_on, mut pin_sys_reset, ps2_clk0, - _ps2_clk1, + ps2_clk1, ps2_dat0, - _ps2_dat1, + ps2_dat1, pin_cs, pin_sck, pin_cipo, @@ -260,11 +306,11 @@ mod app { gpioa.pa2.into_push_pull_output(cs), // ps2_clk0, gpioa.pa15.into_floating_input(cs), - // _ps2_clk1, + // ps2_clk1, gpiob.pb3.into_floating_input(cs), // ps2_dat0, gpiob.pb4.into_floating_input(cs), - // _ps2_dat1, + // ps2_dat1, gpiob.pb5.into_floating_input(cs), // pin_cs, gpioa.pa4.into_pull_down_input(cs), @@ -311,7 +357,7 @@ mod app { speaker::RegisterState::default().setup(&mut rcc, &dp.TIM14); - // Set EXTI15 to use PORT A (PA15) - button input + // Set EXTI15 to use PORT A (PA15) - ps2_clk0 dp.SYSCFG.exticr4.modify(|_r, w| w.exti15().pa15()); // Enable EXTI15 interrupt as external falling edge @@ -328,6 +374,14 @@ mod app { dp.EXTI.ftsr.modify(|_r, w| w.tr4().set_bit()); dp.EXTI.rtsr.modify(|_r, w| w.tr4().set_bit()); + // Set EXTI3 to use PORT B (PB3) - ps2_clk1 + dp.SYSCFG.exticr1.modify(|_r, w| w.exti3().pb3()); + + // Enable EXTI3 interrupt as external falling edge + dp.EXTI.imr.modify(|_r, w| w.mr3().set_bit()); + dp.EXTI.emr.modify(|_r, w| w.mr3().set_bit()); + dp.EXTI.ftsr.modify(|_r, w| w.tr3().set_bit()); + // Spawn the tasks that run all the time led_power_blink::spawn().unwrap(); button_poll::spawn().unwrap(); @@ -347,16 +401,17 @@ mod app { state_dc_power_enabled: DcPowerState::Off, pin_dc_on, pin_sys_reset, - ps2_clk0, - _ps2_clk1, - ps2_dat0, - _ps2_dat1, + ps2_clk0: Ps2Clk0Pin::Input(ps2_clk0), + ps2_clk1: Ps2Clk1Pin::Input(ps2_clk1), + ps2_dat0: Ps2Data0Pin::Input(ps2_dat0), + ps2_dat1: Ps2Data1Pin::Input(ps2_dat1), exti: dp.EXTI, msg_q_out, msg_q_in, spi, pin_cs, kb_decoder: neotron_bmc_pico::ps2::Ps2Decoder::new(), + mouse_decoder: neotron_bmc_pico::ps2::Ps2Decoder::new(), }; let local_resources = Local { press_button_power_short: debouncr::debounce_2(false), @@ -384,6 +439,7 @@ mod app { defmt::info!("Idle is running..."); let mut irq_forced_low = true; let mut is_high = false; + let mut mouse_reset = false; loop { if irq_forced_low || !register_state.ps2_kb_bytes.is_empty() { // We need service @@ -404,19 +460,31 @@ mod app { match ctx.shared.msg_q_out.dequeue() { Some(Message::Ps2Data0(word)) => { if let Some(byte) = neotron_bmc_pico::ps2::Ps2Decoder::check_word(word) { - defmt::info!("< KB 0x{:x}", byte); + defmt::info!("< KB 0x{=u8:02x}", byte); if let Err(_x) = register_state.ps2_kb_bytes.push_back(byte) { defmt::warn!("KB overflow!"); } + if byte == 0xAA { + setup_kb_out::spawn().unwrap(); + } } else { - defmt::warn!("< Bad KB 0x{:x}", word); + defmt::warn!("< Bad KB 0x{=u16:03x}", word); } } Some(Message::Ps2Data1(word)) => { if let Some(byte) = neotron_bmc_pico::ps2::Ps2Decoder::check_word(word) { - defmt::info!("< MS 0x{:x}", byte); + defmt::info!("< MS 0x{=u8:02x}", byte); + if byte == 0xAA && !mouse_reset { + mouse_reset = true; + } else if byte == 0x00 && mouse_reset { + // talk to the mouse + setup_mouse_out::spawn().unwrap(); + mouse_reset = false; + } else { + mouse_reset = false; + } } else { - defmt::warn!("< Bad MS 0x{:x}", word); + defmt::warn!("< Bad MS 0x{=u16:03x}", word); } } Some(Message::PowerButtonLongPress) => { @@ -595,24 +663,63 @@ mod app { #[task( binds = EXTI4_15, priority = 4, - shared = [ps2_clk0, msg_q_in, ps2_dat0, exti, pin_cs, kb_decoder], + shared = [msg_q_in, ps2_dat0, exti, pin_cs, kb_decoder], )] fn exti4_15_interrupt(mut ctx: exti4_15_interrupt::Context) { let pr = ctx.shared.exti.pr.read(); // Is this EXT15 (PS/2 Port 0 clock input) if pr.pr15().bit_is_set() { - let data_bit = ctx.shared.ps2_dat0.is_high().unwrap(); - // Do we have a complete word? - if let Some(data) = ctx.shared.kb_decoder.lock(|r| r.add_bit(data_bit)) { - // Don't dump in the ISR - we're busy. Add it to this nice lockless queue instead. - if ctx - .shared - .msg_q_in - .lock(|q| q.enqueue(Message::Ps2Data0(data))) - .is_err() - { - panic!("queue full"); - }; + let mut pin = Ps2Data0Pin::Neither; + core::mem::swap(&mut pin, ctx.shared.ps2_dat0); + match pin { + Ps2Data0Pin::Input(pin) => { + let data_bit = pin.is_high().unwrap(); + // Do we have a complete word? + if let Some(data) = ctx.shared.kb_decoder.lock(|r| r.add_bit(data_bit)) { + // Don't dump in the ISR - we're busy. Add it to this nice lockless queue instead. + if ctx + .shared + .msg_q_in + .lock(|q| q.enqueue(Message::Ps2Data0(data))) + .is_err() + { + panic!("queue full"); + }; + } + *ctx.shared.ps2_dat0 = Ps2Data0Pin::Input(pin); + } + Ps2Data0Pin::Output(mut pin, bits) => { + if (bits & 0x01) == 0 { + pin.set_low().unwrap(); + } else { + pin.set_high().unwrap(); + } + if bits == 0x0001 { + // that was the last bit - skip the ack bit + cortex_m::interrupt::free(|cs| { + *ctx.shared.ps2_dat0 = Ps2Data0Pin::Skip(pin.into_floating_input(cs)); + }); + // flip from rising to falling + ctx.shared.exti.rtsr.modify(|_r, w| { + w.tr4().clear_bit(); + w + }); + ctx.shared.exti.ftsr.modify(|_r, w| { + w.tr4().set_bit(); + w + }); + } else { + // Put it back as it was + *ctx.shared.ps2_dat0 = Ps2Data0Pin::Output(pin, bits >> 1); + } + } + Ps2Data0Pin::Skip(pin) => { + // skipped the ack bit, now we're running again + *ctx.shared.ps2_dat0 = Ps2Data0Pin::Input(pin); + } + Ps2Data0Pin::Neither => { + unreachable!(); + } } // Clear the pending flagĀ for this pin ctx.shared.exti.pr.write(|w| w.pr15().set_bit()); @@ -634,6 +741,79 @@ mod app { } } + /// This is the external GPIO interrupt task for EXTI3. + /// + /// It handles PS/2 clock edges on the mouse port. + /// + /// It is very high priority, as we can't afford to miss a PS/2 clock edge. + #[task( + binds = EXTI2_3, + priority = 4, + shared = [msg_q_in, ps2_dat1, exti, mouse_decoder], + )] + fn exti3_interrupt(mut ctx: exti3_interrupt::Context) { + let pr = ctx.shared.exti.pr.read(); + // Is this EXT3 (PS/2 Port 1 clock input) + defmt::debug!("1"); + if pr.pr3().bit_is_set() { + defmt::debug!("2"); + let mut pin = Ps2Data1Pin::Neither; + core::mem::swap(&mut pin, ctx.shared.ps2_dat1); + match pin { + Ps2Data1Pin::Input(pin) => { + let data_bit = pin.is_high().unwrap(); + // Do we have a complete word? + if let Some(data) = ctx.shared.mouse_decoder.lock(|r| r.add_bit(data_bit)) { + // Don't dump in the ISR - we're busy. Add it to this nice lockless queue instead. + if ctx + .shared + .msg_q_in + .lock(|q| q.enqueue(Message::Ps2Data1(data))) + .is_err() + { + panic!("queue full"); + }; + } + *ctx.shared.ps2_dat1 = Ps2Data1Pin::Skip(pin); + } + Ps2Data1Pin::Output(mut pin, bits) => { + if (bits & 0x01) == 0 { + pin.set_low().unwrap(); + } else { + pin.set_high().unwrap(); + } + if bits == 0x0001 { + // that was the last bit - skip the ack bit + cortex_m::interrupt::free(|cs| { + *ctx.shared.ps2_dat1 = Ps2Data1Pin::Skip(pin.into_floating_input(cs)); + }); + // flip from rising to falling + ctx.shared.exti.rtsr.modify(|_r, w| { + w.tr3().clear_bit(); + w + }); + ctx.shared.exti.ftsr.modify(|_r, w| { + w.tr3().set_bit(); + w + }); + } else { + // Put it back as it was + *ctx.shared.ps2_dat1 = Ps2Data1Pin::Output(pin, bits >> 1); + } + } + Ps2Data1Pin::Skip(pin) => { + // skipped the ack bit, now we're running again + *ctx.shared.ps2_dat1 = Ps2Data1Pin::Input(pin); + } + Ps2Data1Pin::Neither => { + unreachable!(); + } + } + // Clear the pending flagĀ for this pin + ctx.shared.exti.pr.write(|w| w.pr3().set_bit()); + } + } + /// This is the USART1 task. /// /// It fires whenever there is new data received on USART1. We should flag to the host @@ -661,6 +841,7 @@ mod app { speaker_pwm_stop::spawn_after(100.millis()).unwrap(); } + /// Task which stops the speaker from playing #[task(shared = [msg_q_in])] fn speaker_pwm_stop(mut ctx: speaker_pwm_stop::Context) { @@ -715,7 +896,7 @@ mod app { /// interrupt. #[task( shared = [ - led_power, button_power, button_reset, msg_q_in, kb_decoder + led_power, button_power, button_reset, msg_q_in, kb_decoder, mouse_decoder ], local = [ press_button_power_short, press_button_power_long, press_button_reset_short ] )] @@ -724,8 +905,9 @@ mod app { let pwr_pressed: bool = ctx.shared.button_power.is_low().unwrap(); let rst_pressed: bool = ctx.shared.button_reset.is_low().unwrap(); - // Poll PS2 + // Poll PS2 - handles PS/2 timeouts ctx.shared.kb_decoder.lock(|r| r.poll()); + ctx.shared.mouse_decoder.lock(|r| r.poll()); // Update state let pwr_short_edge = ctx.local.press_button_power_short.update(pwr_pressed); @@ -802,6 +984,116 @@ mod app { ctx.shared.pin_sys_reset.lock(|pin| pin.set_high().unwrap()); } } + + #[task(shared = [ps2_clk1, ps2_dat1, exti], priority = 4)] + fn setup_mouse_out(ctx: setup_mouse_out::Context) { + defmt::debug!("Holding clock"); + let mut pin = Ps2Clk1Pin::Neither; + core::mem::swap(&mut pin, ctx.shared.ps2_clk1); + // Hold the clock pin low for 100us + let Ps2Clk1Pin::Input(pin) = pin else { + panic!("PS/2 Clock 1 pin already output?"); + }; + let mut pin = cortex_m::interrupt::free(|cs| pin.into_open_drain_output(cs)); + + pin.set_low().unwrap(); + // wait for 100us + setup_mouse_out2::spawn_after(200u64.millis()).unwrap(); + *ctx.shared.ps2_clk1 = Ps2Clk1Pin::Output(pin); + + let mut pin = Ps2Data1Pin::Neither; + core::mem::swap(&mut pin, ctx.shared.ps2_dat1); + match pin { + Ps2Data1Pin::Input(pin) => { + // Sending 0xF4 'Enable Data Reporting' + cortex_m::interrupt::free(|cs| { + *ctx.shared.ps2_dat1 = + Ps2Data1Pin::Output(pin.into_open_drain_output(cs), 0b10111101000); + }); + // flip from falling to rising edge + ctx.shared.exti.ftsr.modify(|_r, w| { + w.tr3().clear_bit(); + w + }); + ctx.shared.exti.rtsr.modify(|_r, w| { + w.tr3().set_bit(); + w + }); + } + _ => { + // Do nothing - we're already sending data? + core::mem::swap(&mut pin, ctx.shared.ps2_dat1); + } + } + } + + #[task(shared = [ps2_clk1], priority = 4)] + fn setup_mouse_out2(ctx: setup_mouse_out2::Context) { + defmt::debug!("Releasing clock"); + let mut pin = Ps2Clk1Pin::Neither; + core::mem::swap(&mut pin, ctx.shared.ps2_clk1); + // restore clock pin to input + let Ps2Clk1Pin::Output(pin) = pin else { + panic!("PS/2 Clock 1 pin not output?"); + }; + let pin = cortex_m::interrupt::free(|cs| pin.into_floating_input(cs)); + *ctx.shared.ps2_clk1 = Ps2Clk1Pin::Input(pin); + } + + #[task(shared = [ps2_clk0, ps2_dat0, exti], priority = 4)] + fn setup_kb_out(ctx: setup_kb_out::Context) { + defmt::debug!("Holding clock"); + let mut pin = Ps2Clk0Pin::Neither; + core::mem::swap(&mut pin, ctx.shared.ps2_clk0); + // Hold the clock pin low for 100us + let Ps2Clk0Pin::Input(pin) = pin else { + panic!("PS/2 Clock 0 pin already output?"); + }; + let mut pin = cortex_m::interrupt::free(|cs| pin.into_open_drain_output(cs)); + + pin.set_low().unwrap(); + // wait for 100us + setup_kb_out2::spawn_after(200u64.millis()).unwrap(); + *ctx.shared.ps2_clk0 = Ps2Clk0Pin::Output(pin); + + let mut pin = Ps2Data0Pin::Neither; + core::mem::swap(&mut pin, ctx.shared.ps2_dat0); + match pin { + Ps2Data0Pin::Input(pin) => { + // Sending 0xF2 'Read ID' + cortex_m::interrupt::free(|cs| { + *ctx.shared.ps2_dat0 = + Ps2Data0Pin::Output(pin.into_open_drain_output(cs), 0b10111100100); + }); + // flip from falling to rising edge + ctx.shared.exti.ftsr.modify(|_r, w| { + w.tr4().clear_bit(); + w + }); + ctx.shared.exti.rtsr.modify(|_r, w| { + w.tr4().set_bit(); + w + }); + } + _ => { + // Do nothing - we're already sending data? + core::mem::swap(&mut pin, ctx.shared.ps2_dat0); + } + } + } + + #[task(shared = [ps2_clk0], priority = 4)] + fn setup_kb_out2(ctx: setup_kb_out2::Context) { + defmt::debug!("Releasing clock"); + let mut pin = Ps2Clk0Pin::Neither; + core::mem::swap(&mut pin, ctx.shared.ps2_clk0); + // restore clock pin to input + let Ps2Clk0Pin::Output(pin) = pin else { + panic!("PS/2 Clock 0 pin not output?"); + }; + let pin = cortex_m::interrupt::free(|cs| pin.into_floating_input(cs)); + *ctx.shared.ps2_clk0 = Ps2Clk0Pin::Input(pin); + } } /// Process an incoming command, converting a request into a response. From da237681dac056d5ba63961f3dcbd006c708ac4b Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Sun, 16 Jun 2024 22:38:16 +0100 Subject: [PATCH 2/7] Fix some build errors. --- neotron-bmc-pico/src/spi.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/neotron-bmc-pico/src/spi.rs b/neotron-bmc-pico/src/spi.rs index 36b27ca..ce7b0cf 100644 --- a/neotron-bmc-pico/src/spi.rs +++ b/neotron-bmc-pico/src/spi.rs @@ -221,13 +221,15 @@ impl SpiPeripheral { fn raw_read(&mut self) -> u8 { // PAC only supports 16-bit read, but that pops two bytes off the FIFO. // So force an 8-bit read. - unsafe { core::ptr::read_volatile(&self.dev.dr as *const _ as *const u8) } + let ptr = self.dev.dr.as_ptr(); + unsafe { core::ptr::read_volatile(ptr as *const u8) } } fn raw_write(&mut self, data: u8) { // PAC only supports 16-bit read, but that pushes two bytes onto the FIFO. // So force an 8-bit write. - unsafe { core::ptr::write_volatile(&self.dev.dr as *const _ as *mut u8, data) } + let ptr = self.dev.dr.as_ptr(); + unsafe { core::ptr::write_volatile(ptr as *mut u8, data) } } /// Get a slice of data received so far. From 206afc88b890092e213a3b23d07032e8474c13a2 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Sun, 16 Jun 2024 22:38:25 +0100 Subject: [PATCH 3/7] Switch to 2021 edition --- neotron-bmc-pico/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neotron-bmc-pico/Cargo.toml b/neotron-bmc-pico/Cargo.toml index 3043f91..22b0fef 100644 --- a/neotron-bmc-pico/Cargo.toml +++ b/neotron-bmc-pico/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ["Jonathan 'theJPster' Pallant "] description = "Neotron BMC firmware to run on the STM32F030 on the Neotron Pico" -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later" name = "neotron-bmc-pico" readme = "README.md" From 9ea09f13acd1b6b7cf4895db6160bef21cb48c3a Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Sun, 16 Jun 2024 22:38:32 +0100 Subject: [PATCH 4/7] Switch to probe-rs as a runner --- neotron-bmc-pico/.cargo/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neotron-bmc-pico/.cargo/config.toml b/neotron-bmc-pico/.cargo/config.toml index f398624..f981e19 100644 --- a/neotron-bmc-pico/.cargo/config.toml +++ b/neotron-bmc-pico/.cargo/config.toml @@ -1,5 +1,5 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -runner = "probe-run --chip STM32F030K6Tx" +runner = "probe-rs run --chip STM32F030K6Tx" rustflags = [ "-C", "linker=flip-link", "-C", "link-arg=-Tlink.x", From 068e4239bb7e64d0394252d14b0c7bb155a02aea Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Sun, 16 Jun 2024 22:38:55 +0100 Subject: [PATCH 5/7] Use monotonic as the defmt timestamp. --- neotron-bmc-pico/src/lib.rs | 10 ---------- neotron-bmc-pico/src/main.rs | 2 ++ 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/neotron-bmc-pico/src/lib.rs b/neotron-bmc-pico/src/lib.rs index 661dcc5..5c105da 100644 --- a/neotron-bmc-pico/src/lib.rs +++ b/neotron-bmc-pico/src/lib.rs @@ -1,7 +1,5 @@ #![no_std] -use core::sync::atomic::{AtomicUsize, Ordering}; - use defmt_rtt as _; // global logger use panic_probe as _; use stm32f0xx_hal as _; // memory layout // panic handler @@ -17,14 +15,6 @@ fn panic() -> ! { cortex_m::asm::udf() } -static COUNT: AtomicUsize = AtomicUsize::new(0); -defmt::timestamp!("{=usize}", { - // NOTE(no-CAS) `timestamps` runs with interrupts disabled - let n = COUNT.load(Ordering::Relaxed); - COUNT.store(n + 1, Ordering::Relaxed); - n -}); - /// Terminates the application and makes `probe-run` exit with exit-code = 0 pub fn exit() -> ! { loop { diff --git a/neotron-bmc-pico/src/main.rs b/neotron-bmc-pico/src/main.rs index 1fb0ccf..7f560a1 100644 --- a/neotron-bmc-pico/src/main.rs +++ b/neotron-bmc-pico/src/main.rs @@ -75,6 +75,8 @@ mod app { use super::*; use systick_monotonic::*; // Implements the `Monotonic` trait + defmt::timestamp!("{=u64}", monotonics::now().ticks()); + pub enum Message { /// Word from PS/2 port 0 Ps2Data0(u16), From c703005ffe40e6e94bfa3b8fb5db0f07e3285914 Mon Sep 17 00:00:00 2001 From: Jan Niehusmann Date: Mon, 17 Jun 2024 22:02:21 +0000 Subject: [PATCH 6/7] Enable SYSCFG clock --- neotron-bmc-pico/src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/neotron-bmc-pico/src/main.rs b/neotron-bmc-pico/src/main.rs index 7f560a1..366e3b6 100644 --- a/neotron-bmc-pico/src/main.rs +++ b/neotron-bmc-pico/src/main.rs @@ -239,6 +239,8 @@ mod app { let dp: pac::Peripherals = ctx.device; let cp: cortex_m::Peripherals = ctx.core; + dp.RCC.apb2enr.modify(|_, w| w.syscfgen().set_bit()); + let mut flash = dp.FLASH; let mut rcc = dp .RCC From 9d186e2dc5a5818487b4bd6b4f26a8428773afd1 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Tue, 18 Jun 2024 08:35:14 +0100 Subject: [PATCH 7/7] Now we receive mouse events. 1) The clock flipping seems to break things (not sure why) - and I was flipping the wrong interrupt bit on the keyboard anyway. 2) Added an encode function for PS/2 words --- neotron-bmc-pico/src/main.rs | 50 +++++------------------------------- neotron-bmc-pico/src/ps2.rs | 12 +++++++++ 2 files changed, 19 insertions(+), 43 deletions(-) diff --git a/neotron-bmc-pico/src/main.rs b/neotron-bmc-pico/src/main.rs index 366e3b6..0dff62f 100644 --- a/neotron-bmc-pico/src/main.rs +++ b/neotron-bmc-pico/src/main.rs @@ -703,15 +703,6 @@ mod app { cortex_m::interrupt::free(|cs| { *ctx.shared.ps2_dat0 = Ps2Data0Pin::Skip(pin.into_floating_input(cs)); }); - // flip from rising to falling - ctx.shared.exti.rtsr.modify(|_r, w| { - w.tr4().clear_bit(); - w - }); - ctx.shared.exti.ftsr.modify(|_r, w| { - w.tr4().set_bit(); - w - }); } else { // Put it back as it was *ctx.shared.ps2_dat0 = Ps2Data0Pin::Output(pin, bits >> 1); @@ -758,9 +749,7 @@ mod app { fn exti3_interrupt(mut ctx: exti3_interrupt::Context) { let pr = ctx.shared.exti.pr.read(); // Is this EXT3 (PS/2 Port 1 clock input) - defmt::debug!("1"); if pr.pr3().bit_is_set() { - defmt::debug!("2"); let mut pin = Ps2Data1Pin::Neither; core::mem::swap(&mut pin, ctx.shared.ps2_dat1); match pin { @@ -778,7 +767,7 @@ mod app { panic!("queue full"); }; } - *ctx.shared.ps2_dat1 = Ps2Data1Pin::Skip(pin); + *ctx.shared.ps2_dat1 = Ps2Data1Pin::Input(pin); } Ps2Data1Pin::Output(mut pin, bits) => { if (bits & 0x01) == 0 { @@ -791,15 +780,6 @@ mod app { cortex_m::interrupt::free(|cs| { *ctx.shared.ps2_dat1 = Ps2Data1Pin::Skip(pin.into_floating_input(cs)); }); - // flip from rising to falling - ctx.shared.exti.rtsr.modify(|_r, w| { - w.tr3().clear_bit(); - w - }); - ctx.shared.exti.ftsr.modify(|_r, w| { - w.tr3().set_bit(); - w - }); } else { // Put it back as it was *ctx.shared.ps2_dat1 = Ps2Data1Pin::Output(pin, bits >> 1); @@ -991,7 +971,7 @@ mod app { #[task(shared = [ps2_clk1, ps2_dat1, exti], priority = 4)] fn setup_mouse_out(ctx: setup_mouse_out::Context) { - defmt::debug!("Holding clock"); + defmt::debug!("Holding mouse clock"); let mut pin = Ps2Clk1Pin::Neither; core::mem::swap(&mut pin, ctx.shared.ps2_clk1); // Hold the clock pin low for 100us @@ -1010,18 +990,10 @@ mod app { match pin { Ps2Data1Pin::Input(pin) => { // Sending 0xF4 'Enable Data Reporting' + let word = neotron_bmc_pico::ps2::Ps2Decoder::encode_byte(0xF4); cortex_m::interrupt::free(|cs| { *ctx.shared.ps2_dat1 = - Ps2Data1Pin::Output(pin.into_open_drain_output(cs), 0b10111101000); - }); - // flip from falling to rising edge - ctx.shared.exti.ftsr.modify(|_r, w| { - w.tr3().clear_bit(); - w - }); - ctx.shared.exti.rtsr.modify(|_r, w| { - w.tr3().set_bit(); - w + Ps2Data1Pin::Output(pin.into_open_drain_output(cs), word); }); } _ => { @@ -1046,7 +1018,7 @@ mod app { #[task(shared = [ps2_clk0, ps2_dat0, exti], priority = 4)] fn setup_kb_out(ctx: setup_kb_out::Context) { - defmt::debug!("Holding clock"); + defmt::debug!("Holding keyboard clock"); let mut pin = Ps2Clk0Pin::Neither; core::mem::swap(&mut pin, ctx.shared.ps2_clk0); // Hold the clock pin low for 100us @@ -1065,18 +1037,10 @@ mod app { match pin { Ps2Data0Pin::Input(pin) => { // Sending 0xF2 'Read ID' + let word = neotron_bmc_pico::ps2::Ps2Decoder::encode_byte(0xF2); cortex_m::interrupt::free(|cs| { *ctx.shared.ps2_dat0 = - Ps2Data0Pin::Output(pin.into_open_drain_output(cs), 0b10111100100); - }); - // flip from falling to rising edge - ctx.shared.exti.ftsr.modify(|_r, w| { - w.tr4().clear_bit(); - w - }); - ctx.shared.exti.rtsr.modify(|_r, w| { - w.tr4().set_bit(); - w + Ps2Data0Pin::Output(pin.into_open_drain_output(cs), word); }); } _ => { diff --git a/neotron-bmc-pico/src/ps2.rs b/neotron-bmc-pico/src/ps2.rs index 85384f6..8b297d5 100644 --- a/neotron-bmc-pico/src/ps2.rs +++ b/neotron-bmc-pico/src/ps2.rs @@ -91,4 +91,16 @@ impl Ps2Decoder { Some(data) } + + /// Encode a byte, with start, stop and parity bits. + pub const fn encode_byte(byte: u8) -> u16 { + let mut data: u16 = 0; + data |= 0b100_0000_0000; + data |= (byte as u16) << 1; + let need_parity = (data.count_ones() % 2) != 0; + if need_parity { + data |= 0b010_0000_0000; + } + data + } }