Skip to content

Commit

Permalink
add USB serial example
Browse files Browse the repository at this point in the history
- modify library to demo USB serial on Portenta using USB_HTG_HS
- add "usb" feature to resolve conflict with pin PB0, which is used in UART0 and USB
- lock `embassy` dependencies to specific commit to avoid conflicts
  • Loading branch information
dstric-aqueduct committed Dec 11, 2023
1 parent b3a2937 commit 36c08f0
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 10 deletions.
5 changes: 5 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,15 @@ oe = "objcopy --example"
blinky = "be blinky"
blinky-probe = "ee blinky"
blinky-bin = "oe blinky --release -- -O binary target/thumbv7em-none-eabihf/release/examples/blinky.bin"

serial = "be serial"
serial-probe = "ee serial"
serial-bin = "oe serial --release -- -O binary target/thumbv7em-none-eabihf/release/examples/serial.bin"

usb-serial = "be usb_serial --features=usb"
usb-serial-probe = "ee usb_serial --features=usb"
usb-serial-bin = "oe usb_serial --release --features=usb -- -O binary target/thumbv7em-none-eabihf/release/examples/usb_serial.bin"

[build]
target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)

Expand Down
25 changes: 17 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,21 @@ name = "portenta-h7-async"
version = "0.1.0"
edition = "2021"

[[example]]
name = "usb_serial"
required-features = ["usb"]

[features]
usb = []

[dependencies]
cortex-m = { version = "0.7.6", features = [
"inline-asm",
"critical-section-single-core",
] }
cortex-m-rt = "0.7"

embassy-stm32 = { version = "0.1.0", git = "https://github.com/embassy-rs/embassy.git", features = [
"nightly",
"unstable-traits",
embassy-stm32 = { rev = "14f41a71b6ea9dedb4ee5b9c741fe10575772c7d", git = "https://github.com/embassy-rs/embassy.git", features = [
"defmt",
"stm32h747xi-cm7",
"unstable-pac",
Expand All @@ -22,26 +27,30 @@ embassy-stm32 = { version = "0.1.0", git = "https://github.com/embassy-rs/embass
"embedded-sdmmc",
"chrono",
] }
embassy-sync = { version = "0.4.0", git = "https://github.com/embassy-rs/embassy.git", features = [

embassy-sync = { rev = "14f41a71b6ea9dedb4ee5b9c741fe10575772c7d", git = "https://github.com/embassy-rs/embassy.git", features = [
"defmt",
] }
embassy-executor = { version = "0.3.1", git = "https://github.com/embassy-rs/embassy.git", features = [

embassy-executor = { rev = "14f41a71b6ea9dedb4ee5b9c741fe10575772c7d", git = "https://github.com/embassy-rs/embassy.git", features = [
"nightly",
"arch-cortex-m",
"executor-thread",
"executor-interrupt",
"defmt",
"integrated-timers",
] }
embassy-time = { version = "0.1.2", git = "https://github.com/embassy-rs/embassy.git", features = [

embassy-time = { rev = "14f41a71b6ea9dedb4ee5b9c741fe10575772c7d", git = "https://github.com/embassy-rs/embassy.git", features = [
"defmt",
"defmt-timestamp-uptime",
"unstable-traits",
"tick-hz-32_768",
] }
embassy-usb = { version = "0.1.0", git = "https://github.com/embassy-rs/embassy.git", features = [

embassy-usb = { rev = "14f41a71b6ea9dedb4ee5b9c741fe10575772c7d", git = "https://github.com/embassy-rs/embassy.git", features = [
"defmt",
] }

defmt = "=0.3.2"
defmt-rtt = "0.4.0"
embedded-hal = "0.2.6"
Expand Down
119 changes: 119 additions & 0 deletions examples/usb_serial.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]

use defmt::info;
use defmt_rtt as _;
use embassy_executor::{main, task, Spawner};
use embassy_time::Timer;
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
use embassy_usb::driver::EndpointError;
use embassy_usb::Builder;
use futures::future::join;
use panic_probe as _;
use portenta_h7_async::led;

use embassy_stm32::usb_otg::{Driver, Instance};

#[main]
async fn main(spawner: Spawner) {
info!("Init");

let portenta_h7_async::Board {
led_blue,
mut led_red,
usb,
..
} = portenta_h7_async::Board::take();

// Create embassy-usb Config
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
config.manufacturer = Some("Embassy");
config.product = Some("USB-serial example");
config.serial_number = Some("12345678");
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 builder = Builder::new(
usb,
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, 512);

// 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;
info!("Connected");
let _ = echo(&mut class, &mut led_red).await;
info!("Disconnected");
}
};

spawner.spawn(blink_led_blue(led_blue)).unwrap();

// Run everything concurrently.
// If we had made everything `'static` above instead, we could do this using separate tasks instead.
join(usb_fut, echo_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>>,
led_red: &mut led::user::Red,
) -> Result<(), Disconnected> {
let mut buf = [0; 512];
loop {
let n = class.read_packet(&mut buf).await?;
let data = &buf[..n];
info!("data: {:x}", data);
led_red.toggle();
class.write_packet(data).await?;
}
}

#[task]
async fn blink_led_blue(mut led: led::user::Blue) {
loop {
led.toggle();
Timer::after_millis(1_000).await;
}
}
63 changes: 61 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![no_std]
#![feature(type_alias_impl_trait)]

pub mod led;
mod sys;
Expand All @@ -12,21 +13,38 @@ use embassy_stm32::{
Config,
};

#[cfg(feature="usb")]
use embassy_stm32::usb_otg::{self, Driver};

#[cfg(not(feature="usb"))]
bind_interrupts!(struct Irqs {
USART1 => usart::InterruptHandler<peripherals::USART1>;
UART4 => usart::InterruptHandler<peripherals::UART4>;
});

#[cfg(feature="usb")]
bind_interrupts!(struct Irqs {
USART1 => usart::InterruptHandler<peripherals::USART1>;
UART4 => usart::InterruptHandler<peripherals::UART4>;
OTG_HS => usb_otg::InterruptHandler<peripherals::USB_OTG_HS>;
});

// Naming according to breakout board
pub type Uart0 = Uart<'static, peripherals::UART4, peripherals::DMA1_CH0, peripherals::DMA1_CH1>;
pub type Uart1 = Uart<'static, peripherals::USART1, peripherals::DMA1_CH2, peripherals::DMA1_CH3>;
#[cfg(feature="usb")]
pub type Usb = Driver<'static, peripherals::USB_OTG_HS>;


pub struct Board {
pub led_red: Output<'static, peripherals::PK5>,
pub led_green: Output<'static, peripherals::PK6>,
pub led_blue: Output<'static, peripherals::PK7>,
#[cfg(not(feature="usb"))]
pub uart0: Uart0,
pub uart1: Uart1,
#[cfg(feature="usb")]
pub usb: Usb,
}

impl Board {
Expand All @@ -39,15 +57,23 @@ impl Board {
pub fn setup() -> Self {
sys::Clk::new().reset().enable_ext_clock();
// TODO Configure 480 MHz (sys) and 240 MHz (per)
let config = Config::default();
let mut config = Config::default();

{
use embassy_stm32::rcc::*;
config.rcc.hsi48 = Some(Hsi48Config {
sync_from_usb: true,
});
}

let p = embassy_stm32::init(config);

// User leds
let led_red = Output::new(p.PK5, Level::High, Speed::Low);
let led_green = Output::new(p.PK6, Level::High, Speed::Low);
let led_blue = Output::new(p.PK7, Level::High, Speed::Low);

// Uart0 of breakout board
#[cfg(not(feature="usb"))]
let uart0 = Uart::new_with_rtscts(
p.UART4,
p.PI9,
Expand Down Expand Up @@ -75,12 +101,45 @@ impl Board {
)
.unwrap();

#[cfg(feature="usb")]
let usb;

#[cfg(feature="usb")]
{
use static_cell::make_static;

// Create the USB driver, from the HAL.
let mut config = embassy_stm32::usb_otg::Config::default();
config.vbus_detection = true;
usb = Driver::new_hs_ulpi(
p.USB_OTG_HS,
Irqs,
p.PA5,
p.PI11,
p.PH4,
p.PC0,
p.PA3,
p.PB0,
p.PB1,
p.PB10,
p.PB11,
p.PB12,
p.PB13,
p.PB5,
&mut make_static!([0; 1024])[..],
config,
);
}

Self {
led_red,
led_green,
led_blue,
#[cfg(not(feature="usb"))]
uart0,
uart1,
#[cfg(feature="usb")]
usb
}
}
}

0 comments on commit 36c08f0

Please sign in to comment.