Flash LEDs on USB events
This commit is contained in:
@ -11,7 +11,7 @@ extern crate cortex_m_rt;
|
|||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_rp::gpio::{Level, Output, Pin};
|
use embassy_rp::gpio::{Level, Output, Pin};
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::Timer;
|
||||||
use log::error;
|
use log::error;
|
||||||
use tangentbord1::{
|
use tangentbord1::{
|
||||||
board::Board,
|
board::Board,
|
||||||
@ -20,7 +20,7 @@ use tangentbord1::{
|
|||||||
keyboard::KeyboardConfig,
|
keyboard::KeyboardConfig,
|
||||||
logger::Logger,
|
logger::Logger,
|
||||||
rgb::Rgb,
|
rgb::Rgb,
|
||||||
util::{stall, wheel},
|
util::stall,
|
||||||
ws2812::Ws2812,
|
ws2812::Ws2812,
|
||||||
{allocator, rtt, uart, usb},
|
{allocator, rtt, uart, usb},
|
||||||
};
|
};
|
||||||
@ -103,10 +103,16 @@ async fn main(_spawner: Spawner) {
|
|||||||
neopixel.write(&[Rgb::new(0x00, 0x99, 0x99)]).await;
|
neopixel.write(&[Rgb::new(0x00, 0x99, 0x99)]).await;
|
||||||
|
|
||||||
usb::setup_logger_and_keyboard(board.USB, events1).await;
|
usb::setup_logger_and_keyboard(board.USB, events1).await;
|
||||||
neopixel.write(&[Rgb::new(0x00, 0x00, 0xFF)]).await;
|
|
||||||
|
|
||||||
for w in 0usize.. {
|
neopixel.write(&[Rgb::new(0x00, 0x00, 0xFF)]).await;
|
||||||
neopixel.write(&[wheel(w as u8) * 0.15]).await;
|
Timer::after_secs(5).await;
|
||||||
Timer::after(Duration::from_millis(10)).await;
|
|
||||||
|
for b in (0u8..0xff).rev() {
|
||||||
|
neopixel.write(&[Rgb::new(0, 0, b)]).await;
|
||||||
|
Timer::after_millis(10).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
Timer::after_secs(5).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,14 @@
|
|||||||
|
use core::future::pending;
|
||||||
|
|
||||||
use embassy_time::{Duration, Instant, Timer};
|
use embassy_time::{Duration, Instant, Timer};
|
||||||
use futures::{select_biased, FutureExt};
|
use futures::{select_biased, FutureExt};
|
||||||
use tgnt::button::Button;
|
use tgnt::button::Button;
|
||||||
|
|
||||||
use crate::{rgb::Rgb, util::wheel};
|
use crate::{
|
||||||
|
rgb::Rgb,
|
||||||
|
usb::{UsbEvent, USB_EVENTS},
|
||||||
|
util::wheel,
|
||||||
|
};
|
||||||
|
|
||||||
use super::{Event, EventKind, KbEvents, State, SWITCH_COUNT};
|
use super::{Event, EventKind, KbEvents, State, SWITCH_COUNT};
|
||||||
|
|
||||||
@ -36,7 +42,21 @@ pub(super) async fn task(mut events: KbEvents, state: &'static State) {
|
|||||||
let mut lights: [LightState; SWITCH_COUNT] = [LightState::None; SWITCH_COUNT];
|
let mut lights: [LightState; SWITCH_COUNT] = [LightState::None; SWITCH_COUNT];
|
||||||
let mut next_frame = Instant::now();
|
let mut next_frame = Instant::now();
|
||||||
let mut idle_at = Instant::now() + UNTIL_IDLE;
|
let mut idle_at = Instant::now() + UNTIL_IDLE;
|
||||||
|
let mut usb_enabled: bool = false;
|
||||||
|
let mut usb_events = USB_EVENTS
|
||||||
|
.subscriber()
|
||||||
|
.expect("USB_EVENTS: out of subscribers");
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
let wait_for_idle = async {
|
||||||
|
if usb_enabled {
|
||||||
|
Timer::at(idle_at).await
|
||||||
|
} else {
|
||||||
|
// if usb is disabled, we never want to start the idle animation
|
||||||
|
pending().await
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
select_biased! {
|
select_biased! {
|
||||||
event = events.recv().fuse() => {
|
event = events.recv().fuse() => {
|
||||||
handle_event(event, state, &mut lights).await;
|
handle_event(event, state, &mut lights).await;
|
||||||
@ -46,17 +66,24 @@ pub(super) async fn task(mut events: KbEvents, state: &'static State) {
|
|||||||
tick(state, &mut lights).await;
|
tick(state, &mut lights).await;
|
||||||
next_frame = Instant::now() + Duration::from_millis(16);
|
next_frame = Instant::now() + Duration::from_millis(16);
|
||||||
}
|
}
|
||||||
_ = Timer::at(idle_at).fuse() => {
|
event = usb_events.next_message_pure().fuse() => {
|
||||||
|
handle_usb_event(event, &mut usb_enabled, &mut lights).await;
|
||||||
|
idle_at = Instant::now() + UNTIL_IDLE;
|
||||||
|
}
|
||||||
|
_ = wait_for_idle.fuse() => {
|
||||||
select_biased! {
|
select_biased! {
|
||||||
event = events.recv().fuse() => {
|
event = events.recv().fuse() => {
|
||||||
state.lights.update(|lights| {
|
state.lights.update(|lights| {
|
||||||
lights.iter_mut().for_each(|rgb| *rgb = Rgb::new(0, 0, 0));
|
lights.iter_mut().for_each(|rgb| *rgb = Rgb::new(0, 0, 0));
|
||||||
}).await;
|
}).await;
|
||||||
handle_event(event, state, &mut lights).await;
|
handle_event(event, state, &mut lights).await;
|
||||||
idle_at = Instant::now() + UNTIL_IDLE;
|
}
|
||||||
|
event = usb_events.next_message_pure().fuse() => {
|
||||||
|
handle_usb_event(event, &mut usb_enabled, &mut lights).await;
|
||||||
}
|
}
|
||||||
_ = idle_animation(state).fuse() => {}
|
_ = idle_animation(state).fuse() => {}
|
||||||
}
|
}
|
||||||
|
idle_at = Instant::now() + UNTIL_IDLE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -196,3 +223,31 @@ async fn handle_event(
|
|||||||
};
|
};
|
||||||
*light = rgb;
|
*light = rgb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn handle_usb_event(
|
||||||
|
event: UsbEvent,
|
||||||
|
is_enabled: &mut bool,
|
||||||
|
lights: &mut [LightState; SWITCH_COUNT],
|
||||||
|
) {
|
||||||
|
match event {
|
||||||
|
UsbEvent::Suspended(false) | UsbEvent::Configured(true) => {
|
||||||
|
let new_state = LightState::SolidThenFade {
|
||||||
|
color: Rgb::new(0, 255, 0),
|
||||||
|
solid_until: Instant::now() + Duration::from_millis(200),
|
||||||
|
fade_by: 0.85,
|
||||||
|
};
|
||||||
|
lights.iter_mut().for_each(|state| *state = new_state);
|
||||||
|
*is_enabled = true;
|
||||||
|
}
|
||||||
|
UsbEvent::Configured(false) | UsbEvent::Suspended(true) | UsbEvent::Reset => {
|
||||||
|
let new_state = LightState::SolidThenFade {
|
||||||
|
color: Rgb::new(255, 0, 0),
|
||||||
|
solid_until: Instant::now() + Duration::from_millis(200),
|
||||||
|
fade_by: 0.85,
|
||||||
|
};
|
||||||
|
lights.iter_mut().for_each(|state| *state = new_state);
|
||||||
|
*is_enabled = false;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -5,6 +5,8 @@ use crc_any::CRCu16;
|
|||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_rp::peripherals::{PIN_0, PIN_1, UART0};
|
use embassy_rp::peripherals::{PIN_0, PIN_1, UART0};
|
||||||
use embassy_rp::uart::{self, BufferedUart, DataBits, Parity, StopBits};
|
use embassy_rp::uart::{self, BufferedUart, DataBits, Parity, StopBits};
|
||||||
|
use embassy_sync::channel::Channel;
|
||||||
|
use embassy_sync::pubsub::PubSubBehavior;
|
||||||
use embedded_io_async::{Read, Write};
|
use embedded_io_async::{Read, Write};
|
||||||
use futures::{select_biased, FutureExt};
|
use futures::{select_biased, FutureExt};
|
||||||
use heapless::Vec;
|
use heapless::Vec;
|
||||||
@ -14,10 +16,16 @@ use static_cell::StaticCell;
|
|||||||
use crate::event::{switch, Half};
|
use crate::event::{switch, Half};
|
||||||
use crate::interrupts::Irqs;
|
use crate::interrupts::Irqs;
|
||||||
use crate::keyboard::KbEvents;
|
use crate::keyboard::KbEvents;
|
||||||
|
use crate::usb::{UsbEvent, USB_EVENTS};
|
||||||
|
use crate::util::CS;
|
||||||
|
|
||||||
|
/// Channel for [UsbEvent]s to be sent on the uart line.
|
||||||
|
pub static UART_USB_EVENTS_OUT: Channel<CS, UsbEvent, 8> = Channel::new();
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
enum Message {
|
enum Message {
|
||||||
KeyboardEvent(switch::Event),
|
KeyboardEvent(switch::Event),
|
||||||
|
UsbEvent(UsbEvent),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn start(tx: PIN_0, rx: PIN_1, uart: UART0, board: Half, events: KbEvents) {
|
pub async fn start(tx: PIN_0, rx: PIN_1, uart: UART0, board: Half, events: KbEvents) {
|
||||||
@ -106,6 +114,7 @@ async fn uart_task(uart: BufferedUart<'static, UART0>, this_half: Half, mut even
|
|||||||
|
|
||||||
match &message {
|
match &message {
|
||||||
Message::KeyboardEvent(event) => events_tx.send(event.clone()),
|
Message::KeyboardEvent(event) => events_tx.send(event.clone()),
|
||||||
|
&Message::UsbEvent(event) => USB_EVENTS.publish_immediate(event),
|
||||||
}
|
}
|
||||||
|
|
||||||
log::info!("got msg: {:?}", message);
|
log::info!("got msg: {:?}", message);
|
||||||
@ -130,14 +139,19 @@ async fn uart_task(uart: BufferedUart<'static, UART0>, this_half: Half, mut even
|
|||||||
let tx_task = async {
|
let tx_task = async {
|
||||||
let mut buf = [0u8; 256 + HEADER_LEN];
|
let mut buf = [0u8; 256 + HEADER_LEN];
|
||||||
let mut counter = 0u8;
|
let mut counter = 0u8;
|
||||||
loop {
|
|
||||||
// forward messages to the other keyboard half
|
// forward messages to the other keyboard half
|
||||||
let event = events_rx.recv().await;
|
loop {
|
||||||
|
let message = select_biased! {
|
||||||
|
event = events_rx.recv().fuse() => {
|
||||||
if event.source != this_half {
|
if event.source != this_half {
|
||||||
continue; // do not forward messages from the other half back to it
|
continue; // do not forward messages from the other half back to it
|
||||||
}
|
}
|
||||||
|
Message::KeyboardEvent(event)
|
||||||
|
}
|
||||||
|
event = UART_USB_EVENTS_OUT.receive().fuse() => Message::UsbEvent(event),
|
||||||
|
};
|
||||||
|
|
||||||
let message = Message::KeyboardEvent(event);
|
|
||||||
let (buf_header, body) = buf.split_array_mut();
|
let (buf_header, body) = buf.split_array_mut();
|
||||||
let serialized = match postcard::to_slice(&message, body) {
|
let serialized = match postcard::to_slice(&message, body) {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
|
|||||||
@ -1,27 +1,43 @@
|
|||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_rp::{peripherals::USB, usb::Driver};
|
use embassy_rp::{peripherals::USB, usb::Driver};
|
||||||
use embassy_usb::{Builder, Config, UsbDevice};
|
use embassy_sync::pubsub::{PubSubBehavior, PubSubChannel};
|
||||||
|
use embassy_usb::{Builder, Config, Handler, UsbDevice};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use static_cell::StaticCell;
|
use static_cell::StaticCell;
|
||||||
|
|
||||||
use crate::{interrupts::Irqs, keyboard::KbEvents};
|
use crate::{interrupts::Irqs, keyboard::KbEvents, uart::UART_USB_EVENTS_OUT, util::CS};
|
||||||
|
|
||||||
pub mod keyboard;
|
pub mod keyboard;
|
||||||
pub mod logger;
|
pub mod logger;
|
||||||
|
|
||||||
pub const MAX_PACKET_SIZE: u8 = 64;
|
pub const MAX_PACKET_SIZE: u8 = 64;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub enum UsbEvent {
|
||||||
|
Enabled(bool),
|
||||||
|
Suspended(bool),
|
||||||
|
Configured(bool),
|
||||||
|
Addressed(u8),
|
||||||
|
Reset,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type UsbEventChannel = PubSubChannel<CS, UsbEvent, 8, 24, 0>;
|
||||||
|
pub static USB_EVENTS: UsbEventChannel = UsbEventChannel::new();
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
device_descriptor: [u8; 256],
|
device_descriptor: [u8; 256],
|
||||||
config_descriptor: [u8; 256],
|
config_descriptor: [u8; 256],
|
||||||
bos_descriptor: [u8; 256],
|
bos_descriptor: [u8; 256],
|
||||||
msos_descriptor: [u8; 256],
|
msos_descriptor: [u8; 256],
|
||||||
control_buf: [u8; 64],
|
control_buf: [u8; 64],
|
||||||
|
handler: UsbHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
static STATE: StaticCell<State> = StaticCell::new();
|
static STATE: StaticCell<State> = StaticCell::new();
|
||||||
|
|
||||||
pub async fn setup_logger_and_keyboard(usb: USB, events: KbEvents) {
|
pub async fn setup_logger_and_keyboard(usb: USB, events: KbEvents) {
|
||||||
let mut builder = builder(usb);
|
let mut builder = builder(usb);
|
||||||
|
|
||||||
//logger::setup(&mut builder).await;
|
//logger::setup(&mut builder).await;
|
||||||
|
|
||||||
keyboard::setup(&mut builder, events).await;
|
keyboard::setup(&mut builder, events).await;
|
||||||
@ -41,6 +57,7 @@ pub fn builder(usb: USB) -> Builder<'static, Driver<'static, USB>> {
|
|||||||
bos_descriptor: [0; 256],
|
bos_descriptor: [0; 256],
|
||||||
msos_descriptor: [0; 256],
|
msos_descriptor: [0; 256],
|
||||||
control_buf: [0; 64],
|
control_buf: [0; 64],
|
||||||
|
handler: UsbHandler,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create embassy-usb Config
|
// Create embassy-usb Config
|
||||||
@ -60,7 +77,7 @@ pub fn builder(usb: USB) -> Builder<'static, Driver<'static, USB>> {
|
|||||||
|
|
||||||
let driver = Driver::new(usb, Irqs);
|
let driver = Driver::new(usb, Irqs);
|
||||||
|
|
||||||
Builder::new(
|
let mut builder = Builder::new(
|
||||||
driver,
|
driver,
|
||||||
config,
|
config,
|
||||||
&mut state.device_descriptor,
|
&mut state.device_descriptor,
|
||||||
@ -68,7 +85,11 @@ pub fn builder(usb: USB) -> Builder<'static, Driver<'static, USB>> {
|
|||||||
&mut state.bos_descriptor,
|
&mut state.bos_descriptor,
|
||||||
&mut state.msos_descriptor,
|
&mut state.msos_descriptor,
|
||||||
&mut state.control_buf,
|
&mut state.control_buf,
|
||||||
)
|
);
|
||||||
|
|
||||||
|
builder.handler(&mut state.handler);
|
||||||
|
|
||||||
|
builder
|
||||||
}
|
}
|
||||||
|
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
@ -76,3 +97,37 @@ pub async fn run(mut device: UsbDevice<'static, Driver<'static, USB>>) {
|
|||||||
log::info!("running usb device");
|
log::info!("running usb device");
|
||||||
device.run().await
|
device.run().await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct UsbHandler;
|
||||||
|
|
||||||
|
impl Handler for UsbHandler {
|
||||||
|
fn enabled(&mut self, enabled: bool) {
|
||||||
|
USB_EVENTS.publish_immediate(UsbEvent::Enabled(enabled));
|
||||||
|
let _ = UART_USB_EVENTS_OUT.try_send(UsbEvent::Enabled(enabled));
|
||||||
|
log::debug!("usb enabled({enabled})");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {
|
||||||
|
USB_EVENTS.publish_immediate(UsbEvent::Reset);
|
||||||
|
let _ = UART_USB_EVENTS_OUT.try_send(UsbEvent::Reset);
|
||||||
|
log::debug!("usb reset()");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn addressed(&mut self, addr: u8) {
|
||||||
|
USB_EVENTS.publish_immediate(UsbEvent::Addressed(addr));
|
||||||
|
let _ = UART_USB_EVENTS_OUT.try_send(UsbEvent::Addressed(addr));
|
||||||
|
log::debug!("usb addressed({addr})");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configured(&mut self, configured: bool) {
|
||||||
|
USB_EVENTS.publish_immediate(UsbEvent::Configured(configured));
|
||||||
|
let _ = UART_USB_EVENTS_OUT.try_send(UsbEvent::Configured(configured));
|
||||||
|
log::debug!("usb configured({configured})");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn suspended(&mut self, suspended: bool) {
|
||||||
|
USB_EVENTS.publish_immediate(UsbEvent::Suspended(suspended));
|
||||||
|
let _ = UART_USB_EVENTS_OUT.try_send(UsbEvent::Suspended(suspended));
|
||||||
|
log::debug!("usb suspended({suspended})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ extern crate cortex_m_rt;
|
|||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_rp::gpio::{Level, Output, Pin};
|
use embassy_rp::gpio::{Level, Output, Pin};
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::Timer;
|
||||||
use log::error;
|
use log::error;
|
||||||
use tangentbord1::{
|
use tangentbord1::{
|
||||||
board::Board,
|
board::Board,
|
||||||
@ -19,7 +19,7 @@ use tangentbord1::{
|
|||||||
keyboard::KeyboardConfig,
|
keyboard::KeyboardConfig,
|
||||||
logger::Logger,
|
logger::Logger,
|
||||||
rgb::Rgb,
|
rgb::Rgb,
|
||||||
util::{stall, wheel},
|
util::stall,
|
||||||
ws2812::Ws2812,
|
ws2812::Ws2812,
|
||||||
{allocator, rtt, uart, usb},
|
{allocator, rtt, uart, usb},
|
||||||
};
|
};
|
||||||
@ -101,10 +101,16 @@ async fn main(_spawner: Spawner) {
|
|||||||
uart::start(board.tx, board.rx, board.UART0, half, events2).await;
|
uart::start(board.tx, board.rx, board.UART0, half, events2).await;
|
||||||
|
|
||||||
usb::setup_logger_and_keyboard(board.USB, events1).await;
|
usb::setup_logger_and_keyboard(board.USB, events1).await;
|
||||||
neopixel.write(&[Rgb::new(0x00, 0x00, 0xFF)]).await;
|
|
||||||
|
|
||||||
for w in 0usize.. {
|
neopixel.write(&[Rgb::new(0x00, 0x00, 0xFF)]).await;
|
||||||
neopixel.write(&[wheel(w as u8) * 0.15]).await;
|
Timer::after_secs(5).await;
|
||||||
Timer::after(Duration::from_millis(10)).await;
|
|
||||||
|
for b in (0u8..0xff).rev() {
|
||||||
|
neopixel.write(&[Rgb::new(0, 0, b)]).await;
|
||||||
|
Timer::after_millis(10).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
Timer::after_secs(5).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user