Skip to content
Snippets Groups Projects
Commit f6adeba4 authored by zdmx's avatar zdmx :crab:
Browse files

Rework input controller a bit

parent 807e6aa6
No related branches found
No related tags found
No related merge requests found
......@@ -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",
......
......@@ -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"] }
......
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();
}
......@@ -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
}
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;
}
}
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);
......
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,
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 == (InputEvent { input }) {
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();
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(InputEvent {
input: FlowerInput::SW1Left,
inputs_publisher.publish_immediate(Flow3rInputEvent {
input: FlowerInputEventSource::SW1Left,
etype: Flow3rInputEventType::Press,
});
}
if !values[1] {
inputs_publisher.publish_immediate(InputEvent {
input: FlowerInput::SW1Right,
inputs_publisher.publish_immediate(Flow3rInputEvent {
input: FlowerInputEventSource::SW1Right,
etype: Flow3rInputEventType::Press,
});
}
if !values[2] {
inputs_publisher.publish_immediate(InputEvent {
input: FlowerInput::SW2Left,
inputs_publisher.publish_immediate(Flow3rInputEvent {
input: FlowerInputEventSource::SW2Left,
etype: Flow3rInputEventType::Press,
});
}
if !values[3] {
inputs_publisher.publish_immediate(InputEvent {
input: FlowerInput::SW2Right,
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,
}),
}
}
}
#[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),
}
}
}
......@@ -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()
......
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();
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment