2023-01-11 17:58:15 +01:00
|
|
|
#![no_std]
|
|
|
|
#![no_main]
|
|
|
|
#![feature(type_alias_impl_trait)]
|
|
|
|
|
|
|
|
use defmt::{panic, *};
|
|
|
|
use defmt_rtt as _; // global logger
|
|
|
|
use embassy_executor::Spawner;
|
|
|
|
use embassy_stm32::rcc::*;
|
|
|
|
use embassy_stm32::usb_otg::{Driver, Instance};
|
2023-05-25 00:29:56 +02:00
|
|
|
use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config};
|
2023-01-11 17:58:15 +01:00
|
|
|
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 _;
|
|
|
|
|
2023-05-25 00:29:56 +02:00
|
|
|
bind_interrupts!(struct Irqs {
|
|
|
|
OTG_FS => usb_otg::InterruptHandler<peripherals::USB_OTG_FS>;
|
|
|
|
});
|
|
|
|
|
2023-01-11 17:58:15 +01:00
|
|
|
#[embassy_executor::main]
|
|
|
|
async fn main(_spawner: Spawner) {
|
|
|
|
info!("Hello World!");
|
|
|
|
|
|
|
|
let mut config = Config::default();
|
2023-10-22 22:39:55 +02:00
|
|
|
config.rcc.mux = ClockSrc::PLL1_R(PllConfig {
|
|
|
|
source: PllSrc::HSI,
|
2023-10-09 02:48:22 +02:00
|
|
|
m: Pllm::DIV2,
|
|
|
|
n: Plln::MUL10,
|
|
|
|
r: Plldiv::DIV1,
|
stm32: u5: implement >55 MHz clock speeds
This commit allows STM32U5 devices to operate at 160 MHz.
On STM32U5, MSIS can run at 48 MHz and HSE can reach 50 MHz. Faster
clocks require using PLL1's R output, though PLL1 can serve other
functions besides using the R output for the system clock. This commit
extracts a public `PllConfig` struct, primarily to place associated
constructors on that type, but also with an eye towards enabling the P
and Q outputs in a later commit.
STM32U5 PLLs have various frequency requirements on each stage: after
the `m` prescaler, after the `n` multiplier, and after the `r` divider.
This commit implements the associated checks as assertions.
This commit fixes clock calculation and PLL register configuration
errors in PLL initialization.
STM32U5 has a PWR peripheral which can be configured to push Vcore into
different voltage ranges. System clocks exceeding 55 MHz require range
2, and system clocks exceeding 110 MHz require range 1. This commit
adds `voltage_range` to `Config` and configures PWR as directed.
The voltage range implies different performance limits on various clock
signals, including inside a PLL. This commit implements voltage range
<-> frequency range checks as assertions, and extracts the
otherwise-repeated MSIS, HSI16, and HSE initialization into private
methods on `Config`.
STM32U5 frequencies above 55 MHz require using the PWR EPOD booster.
The EPOD booster requires configuring a second `m` term for PLL1,
`mboost`, such that it falls in a particular range. (Recall that >50
MHz cannot be reached without PLL1, so there is no scenario where EPOD
is needed but PLL1 is not.) This commit configures and enables the EPOD
booster automatically as required.
2023-10-06 05:05:05 +02:00
|
|
|
});
|
2023-11-05 23:35:01 +01:00
|
|
|
config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB
|
2023-01-11 17:58:15 +01:00
|
|
|
|
|
|
|
let p = embassy_stm32::init(config);
|
|
|
|
|
|
|
|
// Create the driver, from the HAL.
|
|
|
|
let mut ep_out_buffer = [0u8; 256];
|
2023-06-27 08:42:51 +02:00
|
|
|
let mut config = embassy_stm32::usb_otg::Config::default();
|
|
|
|
config.vbus_detection = true;
|
|
|
|
let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config);
|
2023-01-11 17:58:15 +01:00
|
|
|
|
|
|
|
// 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");
|
|
|
|
|
2023-05-08 23:25:01 +02:00
|
|
|
// Required for windows compatibility.
|
2023-01-11 17:58:15 +01:00
|
|
|
// 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(
|
|
|
|
driver,
|
|
|
|
config,
|
|
|
|
&mut device_descriptor,
|
|
|
|
&mut config_descriptor,
|
|
|
|
&mut bos_descriptor,
|
|
|
|
&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?;
|
|
|
|
}
|
|
|
|
}
|