-
Notifications
You must be signed in to change notification settings - Fork 839
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2414 from JomerDev/usb-logger-without-device
Adds function and macro to create usb logger without device (also fixes a logger issue)
- Loading branch information
Showing
2 changed files
with
184 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip as well as how to create multiple usb classes for one device | ||
//! | ||
//! This creates a USB serial port that echos. It will also print out logging information on a separate serial device | ||
#![no_std] | ||
#![no_main] | ||
|
||
use defmt::{info, panic}; | ||
use embassy_executor::Spawner; | ||
use embassy_futures::join::join; | ||
use embassy_rp::bind_interrupts; | ||
use embassy_rp::peripherals::USB; | ||
use embassy_rp::usb::{Driver, Instance, InterruptHandler}; | ||
use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||
use embassy_usb::driver::EndpointError; | ||
use embassy_usb::{Builder, Config}; | ||
use {defmt_rtt as _, panic_probe as _}; | ||
|
||
bind_interrupts!(struct Irqs { | ||
USBCTRL_IRQ => InterruptHandler<USB>; | ||
}); | ||
|
||
#[embassy_executor::main] | ||
async fn main(_spawner: Spawner) { | ||
info!("Hello there!"); | ||
|
||
let p = embassy_rp::init(Default::default()); | ||
|
||
// Create the driver, from the HAL. | ||
let driver = Driver::new(p.USB, Irqs); | ||
|
||
// Create embassy-usb Config | ||
let mut config = Config::new(0xc0de, 0xcafe); | ||
config.manufacturer = Some("Embassy"); | ||
config.product = Some("USB-serial example"); | ||
config.serial_number = Some("12345678"); | ||
config.max_power = 100; | ||
config.max_packet_size_0 = 64; | ||
|
||
// Required for windows compatibility. | ||
// https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
config.device_class = 0xEF; | ||
config.device_sub_class = 0x02; | ||
config.device_protocol = 0x01; | ||
config.composite_with_iads = true; | ||
|
||
// Create embassy-usb DeviceBuilder using the driver and config. | ||
// It needs some buffers for building the descriptors. | ||
let mut device_descriptor = [0; 256]; | ||
let mut config_descriptor = [0; 256]; | ||
let mut bos_descriptor = [0; 256]; | ||
let mut control_buf = [0; 64]; | ||
|
||
let mut state = State::new(); | ||
let mut logger_state = State::new(); | ||
|
||
let mut builder = Builder::new( | ||
driver, | ||
config, | ||
&mut device_descriptor, | ||
&mut config_descriptor, | ||
&mut bos_descriptor, | ||
&mut [], // no msos descriptors | ||
&mut control_buf, | ||
); | ||
|
||
// Create classes on the builder. | ||
let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
|
||
// Create a class for the logger | ||
let logger_class = CdcAcmClass::new(&mut builder, &mut logger_state, 64); | ||
|
||
// Creates the logger and returns the logger future | ||
// Note: You'll need to use log::info! afterwards instead of info! for this to work (this also applies to all the other log::* macros) | ||
let log_fut = embassy_usb_logger::with_class!(1024, log::LevelFilter::Info, logger_class); | ||
|
||
// Build the builder. | ||
let mut usb = builder.build(); | ||
|
||
// Run the USB device. | ||
let usb_fut = usb.run(); | ||
|
||
// Do stuff with the class! | ||
let echo_fut = async { | ||
loop { | ||
class.wait_connection().await; | ||
log::info!("Connected"); | ||
let _ = echo(&mut class).await; | ||
log::info!("Disconnected"); | ||
} | ||
}; | ||
|
||
// Run everything concurrently. | ||
// If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
join(usb_fut, join(echo_fut, log_fut)).await; | ||
} | ||
|
||
struct Disconnected {} | ||
|
||
impl From<EndpointError> for Disconnected { | ||
fn from(val: EndpointError) -> Self { | ||
match val { | ||
EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
EndpointError::Disabled => Disconnected {}, | ||
} | ||
} | ||
} | ||
|
||
async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { | ||
let mut buf = [0; 64]; | ||
loop { | ||
let n = class.read_packet(&mut buf).await?; | ||
let data = &buf[..n]; | ||
info!("data: {:x}", data); | ||
class.write_packet(data).await?; | ||
} | ||
} |