Merge branch 'embassy-rs:main' into u5_adc

This commit is contained in:
Olof
2024-12-18 01:48:25 +01:00
committed by GitHub
518 changed files with 23345 additions and 11146 deletions

View File

@@ -1,6 +1,6 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# replace STM32U585AIIx with your chip as listed in `probe-rs chip list`
runner = "probe-rs run --chip STM32U585AIIx"
# replace STM32U5G9ZJTxQ with your chip as listed in `probe-rs chip list`
runner = "probe-rs run --chip STM32U5G9ZJTxQ"
[build]
target = "thumbv8m.main-none-eabihf"

View File

@@ -5,10 +5,10 @@ version = "0.1.0"
license = "MIT OR Apache-2.0"
[dependencies]
# Change stm32u585ai to your chip name, if necessary.
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] }
embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
# Change stm32u5g9zj to your chip name, if necessary.
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "unstable-pac", "stm32u5g9zj", "time-driver-any", "memory-x" ] }
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] }
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
@@ -21,6 +21,8 @@ cortex-m-rt = "0.7.0"
embedded-hal = "0.2.6"
panic-probe = { version = "0.3", features = ["print-defmt"] }
heapless = { version = "0.8", default-features = false }
embedded-graphics = { version = "0.8.1" }
tinybmp = { version = "0.6.0" }
micromath = "2.0.0"

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@@ -13,7 +13,7 @@ const WHOAMI: u8 = 0x0F;
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
let mut i2c = I2c::new_blocking(p.I2C2, p.PH4, p.PH5, Hertz(100_000), Default::default());
let mut i2c = I2c::new_blocking(p.I2C2, p.PF1, p.PF0, Hertz(100_000), Default::default());
let mut data = [0u8; 1];
unwrap!(i2c.blocking_write_read(HTS221_ADDRESS, &[WHOAMI], &mut data));

View File

@@ -0,0 +1,461 @@
#![no_std]
#![no_main]
#![macro_use]
#![allow(static_mut_refs)]
/// This example was derived from examples\stm32h735\src\bin\ltdc.rs
/// It demonstrates the LTDC lcd display peripheral and was tested on an STM32U5G9J-DK2 demo board (embassy-stm32 feature "stm32u5g9zj" and probe-rs chip "STM32U5G9ZJTxQ")
///
use bouncy_box::BouncyBox;
use defmt::{info, unwrap};
use embassy_executor::Spawner;
use embassy_stm32::gpio::{Level, Output, Speed};
use embassy_stm32::ltdc::{self, Ltdc, LtdcConfiguration, LtdcLayer, LtdcLayerConfig, PolarityActive, PolarityEdge};
use embassy_stm32::{bind_interrupts, peripherals};
use embassy_time::{Duration, Timer};
use embedded_graphics::draw_target::DrawTarget;
use embedded_graphics::geometry::{OriginDimensions, Point, Size};
use embedded_graphics::image::Image;
use embedded_graphics::pixelcolor::raw::RawU24;
use embedded_graphics::pixelcolor::Rgb888;
use embedded_graphics::prelude::*;
use embedded_graphics::primitives::Rectangle;
use embedded_graphics::Pixel;
use heapless::{Entry, FnvIndexMap};
use tinybmp::Bmp;
use {defmt_rtt as _, panic_probe as _};
const DISPLAY_WIDTH: usize = 800;
const DISPLAY_HEIGHT: usize = 480;
const MY_TASK_POOL_SIZE: usize = 2;
// the following two display buffers consume 261120 bytes that just about fits into axis ram found on the mcu
pub static mut FB1: [TargetPixelType; DISPLAY_WIDTH * DISPLAY_HEIGHT] = [0; DISPLAY_WIDTH * DISPLAY_HEIGHT];
pub static mut FB2: [TargetPixelType; DISPLAY_WIDTH * DISPLAY_HEIGHT] = [0; DISPLAY_WIDTH * DISPLAY_HEIGHT];
bind_interrupts!(struct Irqs {
LTDC => ltdc::InterruptHandler<peripherals::LTDC>;
});
const NUM_COLORS: usize = 256;
#[embassy_executor::main]
async fn main(spawner: Spawner) {
let p = rcc_setup::stm32u5g9zj_init();
// enable ICACHE
embassy_stm32::pac::ICACHE.cr().write(|w| {
w.set_en(true);
});
// blink the led on another task
let led = Output::new(p.PD2, Level::High, Speed::Low);
unwrap!(spawner.spawn(led_task(led)));
// numbers from STM32U5G9J-DK2.ioc
const RK050HR18H_HSYNC: u16 = 5; // Horizontal synchronization
const RK050HR18H_HBP: u16 = 8; // Horizontal back porch
const RK050HR18H_HFP: u16 = 8; // Horizontal front porch
const RK050HR18H_VSYNC: u16 = 5; // Vertical synchronization
const RK050HR18H_VBP: u16 = 8; // Vertical back porch
const RK050HR18H_VFP: u16 = 8; // Vertical front porch
// NOTE: all polarities have to be reversed with respect to the STM32U5G9J-DK2 CubeMX parametrization
let ltdc_config = LtdcConfiguration {
active_width: DISPLAY_WIDTH as _,
active_height: DISPLAY_HEIGHT as _,
h_back_porch: RK050HR18H_HBP,
h_front_porch: RK050HR18H_HFP,
v_back_porch: RK050HR18H_VBP,
v_front_porch: RK050HR18H_VFP,
h_sync: RK050HR18H_HSYNC,
v_sync: RK050HR18H_VSYNC,
h_sync_polarity: PolarityActive::ActiveHigh,
v_sync_polarity: PolarityActive::ActiveHigh,
data_enable_polarity: PolarityActive::ActiveHigh,
pixel_clock_polarity: PolarityEdge::RisingEdge,
};
info!("init ltdc");
let mut ltdc_de = Output::new(p.PD6, Level::Low, Speed::High);
let mut ltdc_disp_ctrl = Output::new(p.PE4, Level::Low, Speed::High);
let mut ltdc_bl_ctrl = Output::new(p.PE6, Level::Low, Speed::High);
let mut ltdc = Ltdc::new_with_pins(
p.LTDC, // PERIPHERAL
Irqs, // IRQS
p.PD3, // CLK
p.PE0, // HSYNC
p.PD13, // VSYNC
p.PB9, // B0
p.PB2, // B1
p.PD14, // B2
p.PD15, // B3
p.PD0, // B4
p.PD1, // B5
p.PE7, // B6
p.PE8, // B7
p.PC8, // G0
p.PC9, // G1
p.PE9, // G2
p.PE10, // G3
p.PE11, // G4
p.PE12, // G5
p.PE13, // G6
p.PE14, // G7
p.PC6, // R0
p.PC7, // R1
p.PE15, // R2
p.PD8, // R3
p.PD9, // R4
p.PD10, // R5
p.PD11, // R6
p.PD12, // R7
);
ltdc.init(&ltdc_config);
ltdc_de.set_low();
ltdc_bl_ctrl.set_high();
ltdc_disp_ctrl.set_high();
// we only need to draw on one layer for this example (not to be confused with the double buffer)
info!("enable bottom layer");
let layer_config = LtdcLayerConfig {
pixel_format: ltdc::PixelFormat::L8, // 1 byte per pixel
layer: LtdcLayer::Layer1,
window_x0: 0,
window_x1: DISPLAY_WIDTH as _,
window_y0: 0,
window_y1: DISPLAY_HEIGHT as _,
};
let ferris_bmp: Bmp<Rgb888> = Bmp::from_slice(include_bytes!("./ferris.bmp")).unwrap();
let color_map = build_color_lookup_map(&ferris_bmp);
let clut = build_clut(&color_map);
// enable the bottom layer with a 256 color lookup table
ltdc.init_layer(&layer_config, Some(&clut));
// Safety: the DoubleBuffer controls access to the statically allocated frame buffers
// and it is the only thing that mutates their content
let mut double_buffer = DoubleBuffer::new(
unsafe { FB1.as_mut() },
unsafe { FB2.as_mut() },
layer_config,
color_map,
);
// this allows us to perform some simple animation for every frame
let mut bouncy_box = BouncyBox::new(
ferris_bmp.bounding_box(),
Rectangle::new(Point::zero(), Size::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32)),
2,
);
loop {
// cpu intensive drawing to the buffer that is NOT currently being copied to the LCD screen
double_buffer.clear();
let position = bouncy_box.next_point();
let ferris = Image::new(&ferris_bmp, position);
unwrap!(ferris.draw(&mut double_buffer));
// perform async dma data transfer to the lcd screen
unwrap!(double_buffer.swap(&mut ltdc).await);
}
}
/// builds the color look-up table from all unique colors found in the bitmap. This should be a 256 color indexed bitmap to work.
fn build_color_lookup_map(bmp: &Bmp<Rgb888>) -> FnvIndexMap<u32, u8, NUM_COLORS> {
let mut color_map: FnvIndexMap<u32, u8, NUM_COLORS> = heapless::FnvIndexMap::new();
let mut counter: u8 = 0;
// add black to position 0
color_map.insert(Rgb888::new(0, 0, 0).into_storage(), counter).unwrap();
counter += 1;
for Pixel(_point, color) in bmp.pixels() {
let raw = color.into_storage();
if let Entry::Vacant(v) = color_map.entry(raw) {
v.insert(counter).expect("more than 256 colors detected");
counter += 1;
}
}
color_map
}
/// builds the color look-up table from the color map provided
fn build_clut(color_map: &FnvIndexMap<u32, u8, NUM_COLORS>) -> [ltdc::RgbColor; NUM_COLORS] {
let mut clut = [ltdc::RgbColor::default(); NUM_COLORS];
for (color, index) in color_map.iter() {
let color = Rgb888::from(RawU24::new(*color));
clut[*index as usize] = ltdc::RgbColor {
red: color.r(),
green: color.g(),
blue: color.b(),
};
}
clut
}
#[embassy_executor::task(pool_size = MY_TASK_POOL_SIZE)]
async fn led_task(mut led: Output<'static>) {
let mut counter = 0;
loop {
info!("blink: {}", counter);
counter += 1;
// on
led.set_low();
Timer::after(Duration::from_millis(50)).await;
// off
led.set_high();
Timer::after(Duration::from_millis(450)).await;
}
}
pub type TargetPixelType = u8;
// A simple double buffer
pub struct DoubleBuffer {
buf0: &'static mut [TargetPixelType],
buf1: &'static mut [TargetPixelType],
is_buf0: bool,
layer_config: LtdcLayerConfig,
color_map: FnvIndexMap<u32, u8, NUM_COLORS>,
}
impl DoubleBuffer {
pub fn new(
buf0: &'static mut [TargetPixelType],
buf1: &'static mut [TargetPixelType],
layer_config: LtdcLayerConfig,
color_map: FnvIndexMap<u32, u8, NUM_COLORS>,
) -> Self {
Self {
buf0,
buf1,
is_buf0: true,
layer_config,
color_map,
}
}
pub fn current(&mut self) -> (&FnvIndexMap<u32, u8, NUM_COLORS>, &mut [TargetPixelType]) {
if self.is_buf0 {
(&self.color_map, self.buf0)
} else {
(&self.color_map, self.buf1)
}
}
pub async fn swap<T: ltdc::Instance>(&mut self, ltdc: &mut Ltdc<'_, T>) -> Result<(), ltdc::Error> {
let (_, buf) = self.current();
let frame_buffer = buf.as_ptr();
self.is_buf0 = !self.is_buf0;
ltdc.set_buffer(self.layer_config.layer, frame_buffer as *const _).await
}
/// Clears the buffer
pub fn clear(&mut self) {
let (color_map, buf) = self.current();
let black = Rgb888::new(0, 0, 0).into_storage();
let color_index = color_map.get(&black).expect("no black found in the color map");
for a in buf.iter_mut() {
*a = *color_index; // solid black
}
}
}
// Implement DrawTarget for
impl DrawTarget for DoubleBuffer {
type Color = Rgb888;
type Error = ();
/// Draw a pixel
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Pixel<Self::Color>>,
{
let size = self.size();
let width = size.width as i32;
let height = size.height as i32;
let (color_map, buf) = self.current();
for pixel in pixels {
let Pixel(point, color) = pixel;
if point.x >= 0 && point.y >= 0 && point.x < width && point.y < height {
let index = point.y * width + point.x;
let raw_color = color.into_storage();
match color_map.get(&raw_color) {
Some(x) => {
buf[index as usize] = *x;
}
None => panic!("color not found in color map: {}", raw_color),
};
} else {
// Ignore invalid points
}
}
Ok(())
}
}
impl OriginDimensions for DoubleBuffer {
/// Return the size of the display
fn size(&self) -> Size {
Size::new(
(self.layer_config.window_x1 - self.layer_config.window_x0) as _,
(self.layer_config.window_y1 - self.layer_config.window_y0) as _,
)
}
}
mod rcc_setup {
use embassy_stm32::time::Hertz;
use embassy_stm32::{rcc, Config, Peripherals};
/// Sets up clocks for the stm32u5g9zj mcu
/// change this if you plan to use a different microcontroller
pub fn stm32u5g9zj_init() -> Peripherals {
// setup power and clocks for an STM32U5G9J-DK2 run from an external 16 Mhz external oscillator
let mut config = Config::default();
config.rcc.hse = Some(rcc::Hse {
freq: Hertz(16_000_000),
mode: rcc::HseMode::Oscillator,
});
config.rcc.pll1 = Some(rcc::Pll {
source: rcc::PllSource::HSE,
prediv: rcc::PllPreDiv::DIV1,
mul: rcc::PllMul::MUL10,
divp: None,
divq: None,
divr: Some(rcc::PllDiv::DIV1),
});
config.rcc.sys = rcc::Sysclk::PLL1_R; // 160 Mhz
config.rcc.pll3 = Some(rcc::Pll {
source: rcc::PllSource::HSE,
prediv: rcc::PllPreDiv::DIV4, // PLL_M
mul: rcc::PllMul::MUL125, // PLL_N
divp: None,
divq: None,
divr: Some(rcc::PllDiv::DIV20),
});
config.rcc.mux.ltdcsel = rcc::mux::Ltdcsel::PLL3_R; // 25 MHz
embassy_stm32::init(config)
}
}
mod bouncy_box {
use embedded_graphics::geometry::Point;
use embedded_graphics::primitives::Rectangle;
enum Direction {
DownLeft,
DownRight,
UpLeft,
UpRight,
}
pub struct BouncyBox {
direction: Direction,
child_rect: Rectangle,
parent_rect: Rectangle,
current_point: Point,
move_by: usize,
}
// This calculates the coordinates of a chile rectangle bounced around inside a parent bounded box
impl BouncyBox {
pub fn new(child_rect: Rectangle, parent_rect: Rectangle, move_by: usize) -> Self {
let center_box = parent_rect.center();
let center_img = child_rect.center();
let current_point = Point::new(center_box.x - center_img.x / 2, center_box.y - center_img.y / 2);
Self {
direction: Direction::DownRight,
child_rect,
parent_rect,
current_point,
move_by,
}
}
pub fn next_point(&mut self) -> Point {
let direction = &self.direction;
let img_height = self.child_rect.size.height as i32;
let box_height = self.parent_rect.size.height as i32;
let img_width = self.child_rect.size.width as i32;
let box_width = self.parent_rect.size.width as i32;
let move_by = self.move_by as i32;
match direction {
Direction::DownLeft => {
self.current_point.x -= move_by;
self.current_point.y += move_by;
let x_out_of_bounds = self.current_point.x < 0;
let y_out_of_bounds = (self.current_point.y + img_height) > box_height;
if x_out_of_bounds && y_out_of_bounds {
self.direction = Direction::UpRight
} else if x_out_of_bounds && !y_out_of_bounds {
self.direction = Direction::DownRight
} else if !x_out_of_bounds && y_out_of_bounds {
self.direction = Direction::UpLeft
}
}
Direction::DownRight => {
self.current_point.x += move_by;
self.current_point.y += move_by;
let x_out_of_bounds = (self.current_point.x + img_width) > box_width;
let y_out_of_bounds = (self.current_point.y + img_height) > box_height;
if x_out_of_bounds && y_out_of_bounds {
self.direction = Direction::UpLeft
} else if x_out_of_bounds && !y_out_of_bounds {
self.direction = Direction::DownLeft
} else if !x_out_of_bounds && y_out_of_bounds {
self.direction = Direction::UpRight
}
}
Direction::UpLeft => {
self.current_point.x -= move_by;
self.current_point.y -= move_by;
let x_out_of_bounds = self.current_point.x < 0;
let y_out_of_bounds = self.current_point.y < 0;
if x_out_of_bounds && y_out_of_bounds {
self.direction = Direction::DownRight
} else if x_out_of_bounds && !y_out_of_bounds {
self.direction = Direction::UpRight
} else if !x_out_of_bounds && y_out_of_bounds {
self.direction = Direction::DownLeft
}
}
Direction::UpRight => {
self.current_point.x += move_by;
self.current_point.y -= move_by;
let x_out_of_bounds = (self.current_point.x + img_width) > box_width;
let y_out_of_bounds = self.current_point.y < 0;
if x_out_of_bounds && y_out_of_bounds {
self.direction = Direction::DownLeft
} else if x_out_of_bounds && !y_out_of_bounds {
self.direction = Direction::UpLeft
} else if !x_out_of_bounds && y_out_of_bounds {
self.direction = Direction::DownRight
}
}
}
self.current_point
}
}
}

View File

@@ -2,8 +2,8 @@
#![no_main]
use defmt::*;
use embassy_stm32::bind_interrupts;
use embassy_stm32::tsc::{self, *};
use embassy_stm32::{bind_interrupts, peripherals};
use embassy_time::Timer;
use {defmt_rtt as _, panic_probe as _};
@@ -33,63 +33,52 @@ async fn main(_spawner: embassy_executor::Spawner) {
synchro_pin_polarity: false,
acquisition_mode: false,
max_count_interrupt: false,
channel_ios: TscIOPin::Group2Io2 | TscIOPin::Group7Io3,
shield_ios: TscIOPin::Group1Io3.into(),
sampling_ios: TscIOPin::Group1Io2 | TscIOPin::Group2Io1 | TscIOPin::Group7Io2,
};
let mut g1: PinGroup<embassy_stm32::peripherals::TSC, G1> = PinGroup::new();
g1.set_io2(context.PB13, PinType::Sample);
g1.set_io3(context.PB14, PinType::Shield);
let mut g1: PinGroupWithRoles<peripherals::TSC, G1> = PinGroupWithRoles::default();
g1.set_io2::<tsc::pin_roles::Sample>(context.PB13);
g1.set_io3::<tsc::pin_roles::Shield>(context.PB14);
let mut g2: PinGroup<embassy_stm32::peripherals::TSC, G2> = PinGroup::new();
g2.set_io1(context.PB4, PinType::Sample);
g2.set_io2(context.PB5, PinType::Channel);
let mut g2: PinGroupWithRoles<peripherals::TSC, G2> = PinGroupWithRoles::default();
g2.set_io1::<tsc::pin_roles::Sample>(context.PB4);
let sensor0 = g2.set_io2(context.PB5);
let mut g7: PinGroup<embassy_stm32::peripherals::TSC, G7> = PinGroup::new();
g7.set_io2(context.PE3, PinType::Sample);
g7.set_io3(context.PE4, PinType::Channel);
let mut g7: PinGroupWithRoles<peripherals::TSC, G7> = PinGroupWithRoles::default();
g7.set_io2::<tsc::pin_roles::Sample>(context.PE3);
let sensor1 = g7.set_io3(context.PE4);
let mut touch_controller = tsc::Tsc::new_async(
context.TSC,
Some(g1),
Some(g2),
None,
None,
None,
None,
Some(g7),
None,
config,
Irqs,
);
let pin_groups: PinGroups<peripherals::TSC> = PinGroups {
g1: Some(g1.pin_group),
g2: Some(g2.pin_group),
g7: Some(g7.pin_group),
..Default::default()
};
touch_controller.discharge_io(true);
Timer::after_millis(1).await;
let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, config, Irqs).unwrap();
touch_controller.start();
let acquisition_bank = touch_controller.create_acquisition_bank(AcquisitionBankPins {
g2_pin: Some(sensor0),
g7_pin: Some(sensor1),
..Default::default()
});
touch_controller.set_active_channels_bank(&acquisition_bank);
let mut group_two_val = 0;
let mut group_seven_val = 0;
info!("Starting touch_controller interface");
loop {
touch_controller.start();
touch_controller.pend_for_acquisition().await;
touch_controller.discharge_io(true);
Timer::after_millis(1).await;
if touch_controller.group_get_status(Group::Two) == GroupStatus::Complete {
group_two_val = touch_controller.group_get_value(Group::Two);
let status = touch_controller.get_acquisition_bank_status(&acquisition_bank);
if status.all_complete() {
let read_values = touch_controller.get_acquisition_bank_values(&acquisition_bank);
let group2_reading = read_values.get_group_reading(Group::Two).unwrap();
let group7_reading = read_values.get_group_reading(Group::Seven).unwrap();
info!("group 2 value: {}", group2_reading.sensor_value);
info!("group 7 value: {}", group7_reading.sensor_value);
}
if touch_controller.group_get_status(Group::Seven) == GroupStatus::Complete {
group_seven_val = touch_controller.group_get_value(Group::Seven);
}
info!(
"Group Two value: {}, Group Seven value: {},",
group_two_val, group_seven_val
);
touch_controller.start();
}
}

View File

@@ -0,0 +1,129 @@
#![no_std]
#![no_main]
use defmt::{panic, *};
use defmt_rtt as _; // global logger
use embassy_executor::Spawner;
use embassy_futures::join::join;
use embassy_stm32::usb::{Driver, Instance};
use embassy_stm32::{bind_interrupts, peripherals, usb, Config};
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
use embassy_usb::driver::EndpointError;
use embassy_usb::Builder;
use panic_probe as _;
bind_interrupts!(struct Irqs {
OTG_HS => usb::InterruptHandler<peripherals::USB_OTG_HS>;
});
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
info!("Hello World!");
let mut config = Config::default();
{
use embassy_stm32::rcc::*;
use embassy_stm32::time::Hertz;
config.rcc.hse = Some(Hse {
freq: Hertz(16_000_000),
mode: HseMode::Oscillator,
});
config.rcc.pll1 = Some(Pll {
source: PllSource::HSE,
prediv: PllPreDiv::DIV2, // HSE / 2 = 8MHz
mul: PllMul::MUL60, // 8MHz * 60 = 480MHz
divr: Some(PllDiv::DIV3), // 480MHz / 3 = 160MHz (sys_ck)
divq: Some(PllDiv::DIV10), // 480MHz / 10 = 48MHz (USB)
divp: Some(PllDiv::DIV15), // 480MHz / 15 = 32MHz (USBOTG)
});
config.rcc.mux.otghssel = mux::Otghssel::PLL1_P;
config.rcc.voltage_range = VoltageScale::RANGE1;
config.rcc.sys = Sysclk::PLL1_R;
}
let p = embassy_stm32::init(config);
// Create the driver, from the HAL.
let mut ep_out_buffer = [0u8; 256];
let mut config = embassy_stm32::usb::Config::default();
// Do not enable vbus_detection. This is a safe default that works in all boards.
// However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need
// to enable vbus_detection to comply with the USB spec. If you enable it, the board
// has to support it or USB won't work at all. See docs on `vbus_detection` for details.
config.vbus_detection = false;
let driver = Driver::new_hs(p.USB_OTG_HS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);
// 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");
// 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 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(
driver,
config,
&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);
// 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).await;
info!("Disconnected");
}
};
// 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>>) -> 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?;
}
}

View File

@@ -13,7 +13,7 @@ use embassy_usb::Builder;
use panic_probe as _;
bind_interrupts!(struct Irqs {
OTG_FS => usb::InterruptHandler<peripherals::USB_OTG_FS>;
OTG_HS => usb::InterruptHandler<peripherals::USB_OTG_HS>;
});
#[embassy_executor::main]
@@ -48,7 +48,7 @@ async fn main(_spawner: Spawner) {
// to enable vbus_detection to comply with the USB spec. If you enable it, the board
// has to support it or USB won't work at all. See docs on `vbus_detection` for details.
config.vbus_detection = false;
let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);
let driver = Driver::new_hs(p.USB_OTG_HS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);
// Create embassy-usb Config
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);