From f6adeba43de63fc52bc61fb85fbd1ae20689f6ab Mon Sep 17 00:00:00 2001 From: zdmx <hi@zdmx.me> Date: Mon, 14 Aug 2023 11:23:14 +0200 Subject: [PATCH] Rework input controller a bit --- Cargo.lock | 1 + Cargo.toml | 1 + src/demo_tasks.rs | 31 +++-- src/flow3r/badgelink/badgenet.rs | 25 +++- src/flow3r/display/mod.rs | 10 ++ src/flow3r/imu.rs | 8 +- src/flow3r/input.rs | 228 ++++++++++++++++++------------- src/main.rs | 7 +- src/runtime.rs | 30 +++- 9 files changed, 222 insertions(+), 119 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e29355c..51d5acb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -687,6 +687,7 @@ dependencies = [ "embedded-graphics", "embedded-graphics-framebuf", "embedded-hal 0.2.7", + "embedded-hal 1.0.0-alpha.10", "embedded-hal-async", "embedded-hal-bus", "esp-backtrace", diff --git a/Cargo.toml b/Cargo.toml index 2f79b6d..bd10eab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ embedded-dma = "0.2.0" embedded-graphics = "0.8.0" embedded-graphics-framebuf = "0.5.0" embedded-hal = "0.2.7" +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } embedded-hal-async = "0.2.0-alpha.1" embedded-hal-bus = "0.1.0-alpha.2" esp-backtrace = { version = "0.7.0", features = ["esp32s3", "panic-handler", "exception-handler", "print-uart"] } diff --git a/src/demo_tasks.rs b/src/demo_tasks.rs index c9d0362..f900b5a 100644 --- a/src/demo_tasks.rs +++ b/src/demo_tasks.rs @@ -1,20 +1,22 @@ +use crate::flow3r::display::Display; use embassy_futures::select::{select, Either}; use embassy_time::{Duration, Timer}; use embedded_graphics::{ + image::Image, mono_font::{ascii::FONT_6X10, MonoTextStyle}, pixelcolor::Rgb565, prelude::*, primitives::{PrimitiveStyle, Rectangle, StyledDrawable}, - text::Text, image::Image, + text::Text, }; +use embedded_hal_async::digital::Wait; use esp_backtrace as _; use esp_println::println; use tinybmp::Bmp; -use crate::flow3r::display::Display; use smart_leds::SmartLedsWrite; -use crate::flow3r::input::{FlowerInput, InputHandler}; +use crate::flow3r::input::{FlowerInputEventSource, InputHandler}; #[embassy_executor::task] pub async fn leds_fade(mut leds: crate::flow3r::leds::Leds) -> ! { @@ -35,11 +37,12 @@ pub async fn leds_fade(mut leds: crate::flow3r::leds::Leds) -> ! { #[embassy_executor::task] pub async fn display_demo(mut display: crate::flow3r::display::Display) -> ! { let input = InputHandler; + let mut inputs = input.split(); println!("running display demo"); display.set_backlight(100).unwrap(); - let mut rect = Rectangle::with_center(Point { x: 0, y: 120 }, Size::new(50, 50)); + let mut rect = Rectangle::with_center(Point { x: 120, y: 120 }, Size::new(50, 50)); let rect_style = PrimitiveStyle::with_fill(Rgb565::BLUE); let text = Text::new( "move the rectangle with \n the left shoulder rocker", @@ -56,8 +59,8 @@ pub async fn display_demo(mut display: crate::flow3r::display::Display) -> ! { loop { match select( - input.wait_for_event(FlowerInput::SW1Left), - input.wait_for_event(FlowerInput::SW1Right), + inputs.sw1_left.wait_for_press(), + inputs.sw1_right.wait_for_press(), ) .await { @@ -92,14 +95,20 @@ async fn move_rectangle<'a>( pub async fn draw_start_screen(display: &mut Display) { let bmp_data = include_bytes!("../img/logo.bmp"); let bmp = Bmp::from_slice(bmp_data).unwrap(); - display.fill_solid(&display.bounding_box(), Rgb565::WHITE).unwrap(); - Image::with_center(&bmp, display.bounding_box().center()).draw(display).unwrap(); + display + .fill_solid(&display.bounding_box(), Rgb565::WHITE) + .unwrap(); + Image::with_center(&bmp, display.bounding_box().center()) + .draw(display) + .unwrap(); Text::with_alignment( "flow3-rs starting", Point { x: 120, y: 180 }, MonoTextStyle::new(&FONT_6X10, Rgb565::BLACK), - embedded_graphics::text::Alignment::Center - ).draw(display).unwrap(); + embedded_graphics::text::Alignment::Center, + ) + .draw(display) + .unwrap(); display.flush().await.unwrap(); display.set_backlight(100).unwrap(); -} \ No newline at end of file +} diff --git a/src/flow3r/badgelink/badgenet.rs b/src/flow3r/badgelink/badgenet.rs index d0c8560..66262b3 100644 --- a/src/flow3r/badgelink/badgenet.rs +++ b/src/flow3r/badgelink/badgenet.rs @@ -13,8 +13,7 @@ static STATE: StaticCell<State<8, 8>> = StaticCell::new(); static STACK: StaticCell<Stack<Device<'static>>> = StaticCell::new(); static STACK_RESOURCES: StaticCell<StackResources<2>> = StaticCell::new(); -#[embassy_executor::task] -pub async fn badgenet(uart: Uart<'static, UART0>, rng: &'static mut Rng<'static>) { +pub async fn start_badgenet_left(uart: Uart<'static, UART0>, rng: &'static mut Rng<'static>) { let mac_addr = Efuse::get_mac_address(); let state = STATE.init(State::<8, 8>::new()); let (device, runner) = embassy_net_badgelink::new(mac_addr, state, uart); @@ -34,6 +33,26 @@ pub async fn badgenet(uart: Uart<'static, UART0>, rng: &'static mut Rng<'static> spawner.spawn(stack_task(stack)).ok(); } +pub async fn start_badgenet_right(uart: Uart<'static, UART1>, rng: &'static mut Rng<'static>) { + let mac_addr = Efuse::get_mac_address(); + let state = STATE.init(State::<8, 8>::new()); + let (device, runner) = embassy_net_badgelink::new(mac_addr, state, uart); + let stack = STACK.init(Stack::new( + device, + Config::ipv6_static(StaticConfigV6 { + address: Ipv6Cidr::new(Ipv6Address::new(0, 0, 0, 0, 0, 0, 0, 0), 128), + gateway: None, + dns_servers: Vec::new(), + }), + STACK_RESOURCES.init(StackResources::new()), + rng.random().into(), + )); + + let spawner = Spawner::for_current_executor().await; + spawner.spawn(runner_1_task(runner)).ok(); + spawner.spawn(stack_task(stack)).ok(); +} + #[embassy_executor::task] pub async fn runner_0_task(runner: Runner<'static, Uart<'static, UART0>>) -> ! { runner.run().await @@ -44,7 +63,7 @@ pub async fn runner_1_task(runner: Runner<'static, Uart<'static, UART1>>) -> ! { runner.run().await } -#[embassy_executor::task] +#[embassy_executor::task(pool_size = 2)] pub async fn stack_task(stack: &'static mut Stack<Device<'static>>) -> ! { stack.run().await } diff --git a/src/flow3r/display/mod.rs b/src/flow3r/display/mod.rs index a863542..a38b4a4 100644 --- a/src/flow3r/display/mod.rs +++ b/src/flow3r/display/mod.rs @@ -1,12 +1,14 @@ use core::convert::Infallible; use ::display_interface::DisplayError; +use embassy_time::{Duration, Timer}; use embedded_graphics::{ pixelcolor::Rgb565, prelude::{Dimensions, DrawTarget, OriginDimensions, RgbColor, Size}, primitives::{PrimitiveStyle, Rectangle, StyledDrawable}, }; use embedded_graphics_framebuf::FrameBuf; +use esp_println::println; use hal::{ gdma::ChannelCreator0, gpio::{Gpio38, Gpio40, Gpio46, Unknown}, @@ -97,3 +99,11 @@ impl DrawTarget for Display { self.framebuffer.draw_iter(pixels) } } + +#[embassy_executor::task] +pub async fn display_refresh() { + loop { + println!("running on second core"); + Timer::after(Duration::from_secs(1)).await; + } +} diff --git a/src/flow3r/imu.rs b/src/flow3r/imu.rs index 107fa41..5038b55 100644 --- a/src/flow3r/imu.rs +++ b/src/flow3r/imu.rs @@ -1,9 +1,15 @@ -use bmi270::{Bmi270, PwrCtrl}; +use bmi270::{interface::I2cInterface, Bmi270, PwrCtrl}; use embassy_time::{Duration, Timer}; use esp_println::println; use hal::{i2c::I2C, peripherals::I2C0}; use shared_bus::{I2cProxy, XtensaMutex}; +pub struct ImuHandler { + imu: Bmi270<I2cInterface<I2cProxy<'static, XtensaMutex<I2C<'static, I2C0>>>>>, +} + +impl ImuHandler {} + #[embassy_executor::task] pub async fn imu_manager(i2c: I2cProxy<'static, XtensaMutex<I2C<'static, I2C0>>>) -> ! { let mut bmi270 = Bmi270::new_i2c(i2c, bmi270::I2cAddr::Default, bmi270::Burst::Max); diff --git a/src/flow3r/input.rs b/src/flow3r/input.rs index d093fb1..fbf5518 100644 --- a/src/flow3r/input.rs +++ b/src/flow3r/input.rs @@ -1,22 +1,23 @@ -use embassy_executor::Spawner; +use embassy_futures::select::{select, select3, Either, Either3}; 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::{ - gpio::{Gpio0, Gpio3, Gpio8, Input, PullUp, Unknown}, + gpio::{Gpio0, Gpio3, Gpio8, Unknown}, i2c::I2C, peripherals::I2C0, }; -use heapless::Vec; use shared_bus::{I2cProxy, XtensaMutex}; -static INPUTS_CHANNEL: PubSubChannel<CriticalSectionRawMutex, InputEvent, 32, 32, 32> = - PubSubChannel::<CriticalSectionRawMutex, InputEvent, 32, 32, 32>::new(); +static INPUTS_CHANNEL: PubSubChannel<CriticalSectionRawMutex, Flow3rInputEvent, 32, 32, 32> = + PubSubChannel::<CriticalSectionRawMutex, Flow3rInputEvent, 32, 32, 32>::new(); -pub type InputEventListener = Subscriber<'static, CriticalSectionRawMutex, InputEvent, 32, 32, 32>; +pub type InputEventListener = + Subscriber<'static, CriticalSectionRawMutex, Flow3rInputEvent, 32, 32, 32>; pub struct InputHandler; @@ -27,31 +28,78 @@ impl InputHandler { INPUTS_CHANNEL.subscriber() } - pub async fn wait_for_event( - &self, - input: FlowerInput, - ) -> Result<(), embassy_sync::pubsub::Error> { - let mut sub = INPUTS_CHANNEL.subscriber()?; - loop { - match sub.next_message().await { - WaitResult::Lagged(_) => (), - WaitResult::Message(msg) => { - if msg == (InputEvent { input }) { - return Ok(()); - } + pub fn split(self) -> InputParts { + InputParts { + sw1_left: Flow3rInput(FlowerInputEventSource::SW1Left), + sw1_center: Flow3rInput(FlowerInputEventSource::SW1Press), + sw1_right: Flow3rInput(FlowerInputEventSource::SW1Right), + sw2_left: Flow3rInput(FlowerInputEventSource::SW2Left), + sw2_center: Flow3rInput(FlowerInputEventSource::SW2Press), + sw2_right: Flow3rInput(FlowerInputEventSource::SW2Right), + } + } +} + +async fn wait_for_event( + input: FlowerInputEventSource, + etype: Flow3rInputEventType, +) -> Result<(), embassy_sync::pubsub::Error> { + let mut sub = INPUTS_CHANNEL.subscriber()?; + loop { + match sub.next_message().await { + WaitResult::Lagged(_) => (), + WaitResult::Message(msg) => { + if msg == (Flow3rInputEvent { input, etype }) { + return Ok(()); } } } } } +pub struct InputParts { + pub sw1_left: Flow3rInput, + pub sw1_center: Flow3rInput, + pub sw1_right: Flow3rInput, + pub sw2_left: Flow3rInput, + pub sw2_center: Flow3rInput, + pub sw2_right: Flow3rInput, +} + +pub struct Flow3rInput(FlowerInputEventSource); + +impl Flow3rInput { + pub async fn wait_for_release(&mut self) -> Result<(), embassy_sync::pubsub::Error> { + wait_for_event(self.0, Flow3rInputEventType::Press) + .await + } + + pub async fn wait_for_press(&mut self) -> Result<(), embassy_sync::pubsub::Error> { + wait_for_event(self.0, Flow3rInputEventType::Press) + .await + } + + pub async fn wait_for_any(&mut self) -> Result<(), embassy_sync::pubsub::Error> { + match select( + wait_for_event(self.0, Flow3rInputEventType::Press), + wait_for_event(self.0, Flow3rInputEventType::Release), + ) + .await + { + Either::First(e) => e, + Either::Second(e) => e, + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub struct InputEvent { - input: FlowerInput, +pub struct Flow3rInputEvent { + input: FlowerInputEventSource, + etype: Flow3rInputEventType, } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub enum FlowerInput { +pub enum FlowerInputEventSource { SW1Left, SW1Press, SW1Right, @@ -60,66 +108,23 @@ pub enum FlowerInput { SW2Right, } +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum Flow3rInputEventType { + Press, + Release, +} + #[embassy_executor::task] -pub async fn input_manager( +pub async fn input_controller( i2c: I2cProxy<'static, XtensaMutex<I2C<'static, I2C0>>>, i2c_int: Gpio8<Unknown>, sw1_button: Gpio0<Unknown>, sw2_button: Gpio3<Unknown>, ) { - let spawner = Spawner::for_current_executor().await; - spawner - .spawn(sw1_press(sw1_button.into_pull_up_input())) - .unwrap(); - spawner - .spawn(sw2_press(sw2_button.into_pull_up_input())) - .unwrap(); - spawner - .spawn(port_expander_io(i2c, i2c_int.into_pull_up_input())) - .unwrap(); - spawner.spawn(input_logger()).unwrap(); -} - -#[embassy_executor::task] -async fn sw1_press(mut button: Gpio0<Input<PullUp>>) -> ! { - let inputs_publisher = INPUTS_CHANNEL.immediate_publisher(); - loop { - button.wait_for_falling_edge().await.unwrap(); - inputs_publisher.publish_immediate(InputEvent { - input: FlowerInput::SW1Press, - }) - } -} - -#[embassy_executor::task] -async fn sw2_press(mut button: Gpio3<Input<PullUp>>) -> ! { - let inputs_publisher = INPUTS_CHANNEL.immediate_publisher(); - loop { - button.wait_for_falling_edge().await.unwrap(); - inputs_publisher.publish_immediate(InputEvent { - input: FlowerInput::SW2Press, - }) - } -} + let mut i2c_int = i2c_int.into_pull_up_input(); + let mut sw1_button = sw1_button.into_pull_up_input(); + let mut sw2_button = sw2_button.into_pull_up_input(); -#[embassy_executor::task] -async fn input_logger() -> ! { - let mut inputs_subscriber = INPUTS_CHANNEL.subscriber().unwrap(); - loop { - let message = inputs_subscriber.next_message().await; - match message { - WaitResult::Message(msg) => println!("input {:?} activated", msg.input), - WaitResult::Lagged(err) => println!("input logger lagged: {}", err), - } - } -} - -// TODO: missed interrupt inactivates this event handler because INT flag on io expander never gets cleared -#[embassy_executor::task] -async fn port_expander_io( - i2c: I2cProxy<'static, XtensaMutex<I2C<'static, I2C0>>>, - mut i2c_int: Gpio8<Input<PullUp>>, -) -> ! { let shared_i2c = shared_bus::BusManagerSimple::new(i2c); let mut pe1 = port_expander::Max7321::new(shared_i2c.acquire_i2c(), true, true, true, false); @@ -147,28 +152,61 @@ async fn port_expander_io( let inputs_publisher = INPUTS_CHANNEL.immediate_publisher(); loop { - i2c_int.wait_for_low().await.unwrap(); - let values = port_expander::read_multiple([&sw1_l, &sw1_r, &sw2_l, &sw2_r]).unwrap(); - let mut events = Vec::<FlowerInput, 4>::new(); - if !values[0] { - inputs_publisher.publish_immediate(InputEvent { - input: FlowerInput::SW1Left, - }); - } - if !values[1] { - inputs_publisher.publish_immediate(InputEvent { - input: FlowerInput::SW1Right, - }); - } - if !values[2] { - inputs_publisher.publish_immediate(InputEvent { - input: FlowerInput::SW2Left, - }); + match select3( + i2c_int.wait_for_low(), + sw1_button.wait_for_any_edge(), + sw2_button.wait_for_any_edge(), + ) + .await + { + Either3::First(_) => { + let values = + port_expander::read_multiple([&sw1_l, &sw1_r, &sw2_l, &sw2_r]).unwrap(); + if !values[0] { + inputs_publisher.publish_immediate(Flow3rInputEvent { + input: FlowerInputEventSource::SW1Left, + etype: Flow3rInputEventType::Press, + }); + } + if !values[1] { + inputs_publisher.publish_immediate(Flow3rInputEvent { + input: FlowerInputEventSource::SW1Right, + etype: Flow3rInputEventType::Press, + }); + } + if !values[2] { + inputs_publisher.publish_immediate(Flow3rInputEvent { + input: FlowerInputEventSource::SW2Left, + etype: Flow3rInputEventType::Press, + }); + } + if !values[3] { + inputs_publisher.publish_immediate(Flow3rInputEvent { + input: FlowerInputEventSource::SW2Right, + etype: Flow3rInputEventType::Press, + }); + } + } + Either3::Second(_) => inputs_publisher.publish_immediate(Flow3rInputEvent { + input: FlowerInputEventSource::SW1Press, + etype: Flow3rInputEventType::Press, + }), + Either3::Third(_) => inputs_publisher.publish_immediate(Flow3rInputEvent { + input: FlowerInputEventSource::SW2Press, + etype: Flow3rInputEventType::Press, + }), } - if !values[3] { - inputs_publisher.publish_immediate(InputEvent { - input: FlowerInput::SW2Right, - }); + } +} + +#[embassy_executor::task] +async fn input_logger() -> ! { + let mut inputs_subscriber = INPUTS_CHANNEL.subscriber().unwrap(); + loop { + let message = inputs_subscriber.next_message().await; + match message { + WaitResult::Message(msg) => println!("input {:?} activated", msg.input), + WaitResult::Lagged(err) => println!("input logger lagged: {}", err), } } } diff --git a/src/main.rs b/src/main.rs index 62fc88b..0f42168 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,9 +4,9 @@ #![feature(async_fn_in_trait)] #![feature(slice_as_chunks)] +mod demo_tasks; mod flow3r; mod runtime; -mod demo_tasks; use demo_tasks::draw_start_screen; use embassy_executor::Spawner; @@ -18,7 +18,6 @@ use hal::prelude::*; use runtime::start_runtime; - #[entry] fn runtime() -> ! { start_runtime() @@ -28,7 +27,7 @@ fn runtime() -> ! { async fn main(mut flow3r: Flow3r) -> ! { draw_start_screen(&mut flow3r.display).await; Timer::after(Duration::from_secs(10)).await; - + let spawner = Spawner::for_current_executor().await; spawner.spawn(demo_tasks::leds_fade(flow3r.leds)).ok(); spawner.spawn(demo_tasks::display_demo(flow3r.display)).ok(); @@ -36,4 +35,4 @@ async fn main(mut flow3r: Flow3r) -> ! { loop { Timer::after(Duration::from_secs(10)).await } -} \ No newline at end of file +} diff --git a/src/runtime.rs b/src/runtime.rs index af50886..3eec15f 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -1,6 +1,8 @@ use embassy_executor::{Executor, Spawner}; +use embassy_time::{Duration, Timer}; use hal::{ clock::{ClockControl, Clocks}, + cpu_control::{self, AppCoreGuard, CpuControl}, embassy, gdma::Gdma, i2c::I2C, @@ -15,8 +17,11 @@ use hal::{ use static_cell::StaticCell; use crate::flow3r::{ - badgelink::badgenet::badgenet, captouch::captouch_controller, display::Display, - input::input_manager, leds::init_leds, Flow3r, + captouch::captouch_controller, + display::{display_refresh, Display}, + input::input_controller, + leds::init_leds, + Flow3r, }; use crate::main; @@ -24,6 +29,7 @@ const READ_BUF_SIZE: usize = 64; static RNG: StaticCell<Rng> = StaticCell::new(); static EXECUTOR: StaticCell<Executor> = StaticCell::new(); +static APP_CORE_EXECUTOR: StaticCell<Executor> = StaticCell::new(); static CLOCKS: StaticCell<Clocks> = StaticCell::new(); pub fn start_runtime() -> ! { @@ -33,6 +39,20 @@ pub fn start_runtime() -> ! { }); } +#[embassy_executor::task] +async fn start_app_core(mut cpu_control: CpuControl) -> ! { + let mut app_core_function = || { + let executor = APP_CORE_EXECUTOR.init(Executor::new()); + executor.run(|spawner| { + spawner.spawn(display_refresh()).ok(); + }) + }; + let _guard = cpu_control.start_app_core(&mut app_core_function).unwrap(); + loop { + Timer::after(Duration::from_secs(100)).await; + } +} + #[embassy_executor::task] async fn init_runtime() { esp_println::println!("Init!"); @@ -62,6 +82,8 @@ async fn init_runtime() { embassy::init(&clocks, SystemTimer::new(peripherals.SYSTIMER)); + let cpu_control = CpuControl::new(system.cpu_control); + // Async requires the GPIO interrupt to wake futures hal::interrupt::enable( hal::peripherals::Interrupt::GPIO, @@ -143,7 +165,7 @@ async fn init_runtime() { let spawner = Spawner::for_current_executor().await; spawner - .spawn(input_manager( + .spawn(input_controller( i2c_busmanager.acquire_i2c(), io.pins.gpio8, io.pins.gpio0, @@ -156,7 +178,5 @@ async fn init_runtime() { io.pins.gpio16, )) .ok(); - // spawner.spawn(imu_manager(i2c_busmanager.acquire_i2c())).ok(); - spawner.spawn(badgenet(uart0, rng)).ok(); spawner.spawn(main(flow3r)).ok(); } -- GitLab