Merge remote-tracking branch 'origin/master' into feature_pwm_interrupt

This commit is contained in:
anton smeenk 2023-11-10 14:13:20 +01:00
commit 1b9cbe47aa
66 changed files with 1231 additions and 421 deletions

View File

@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 0.3.2 - 2023-11-06
- Use `atomic-polyfill` for `riscv32`
- Removed unused dependencies (static_cell, futures-util)
## 0.3.1 - 2023-11-01 ## 0.3.1 - 2023-11-01
- Fix spurious "Found waker not created by the Embassy executor" error in recent nightlies. - Fix spurious "Found waker not created by the Embassy executor" error in recent nightlies.

View File

@ -1,6 +1,6 @@
[package] [package]
name = "embassy-executor" name = "embassy-executor"
version = "0.3.1" version = "0.3.2"
edition = "2021" edition = "2021"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
description = "async/await executor designed for embedded usage" description = "async/await executor designed for embedded usage"
@ -57,7 +57,6 @@ defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true } log = { version = "0.4.14", optional = true }
rtos-trace = { version = "0.1.2", optional = true } rtos-trace = { version = "0.1.2", optional = true }
futures-util = { version = "0.3.17", default-features = false }
embassy-macros = { version = "0.2.1", path = "../embassy-macros" } embassy-macros = { version = "0.2.1", path = "../embassy-macros" }
embassy-time = { version = "0.1.5", path = "../embassy-time", optional = true} embassy-time = { version = "0.1.5", path = "../embassy-time", optional = true}
atomic-polyfill = "1.0.1" atomic-polyfill = "1.0.1"

View File

@ -6,8 +6,8 @@ pub use thread::*;
#[cfg(feature = "executor-thread")] #[cfg(feature = "executor-thread")]
mod thread { mod thread {
use core::marker::PhantomData; use core::marker::PhantomData;
use core::sync::atomic::{AtomicBool, Ordering};
use atomic_polyfill::{AtomicBool, Ordering};
#[cfg(feature = "nightly")] #[cfg(feature = "nightly")]
pub use embassy_macros::main_riscv as main; pub use embassy_macros::main_riscv as main;

View File

@ -15,7 +15,7 @@ embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-
embedded-hal = { version = "1.0.0-rc.1" } embedded-hal = { version = "1.0.0-rc.1" }
embedded-hal-async = { version = "=1.0.0-rc.1" } embedded-hal-async = { version = "=1.0.0-rc.1" }
noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] } noproto = { git="https://github.com/embassy-rs/noproto", rev = "c90f3a78d7b5642415e0a07af401320b84d8ab6f", default-features = false, features = ["derive"] }
#noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] } #noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] }
heapless = "0.7.16" heapless = "0.7.16"

View File

@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
- Avoid never resolving `TcpIo::read` when the output buffer is empty.
## 0.2.1 - 2023-10-31 ## 0.2.1 - 2023-10-31
- Re-add impl_trait_projections - Re-add impl_trait_projections

View File

@ -4,7 +4,7 @@
It builds on [`smoltcp`](https://github.com/smoltcp-rs/smoltcp). It provides a higher-level and more opinionated It builds on [`smoltcp`](https://github.com/smoltcp-rs/smoltcp). It provides a higher-level and more opinionated
API. It glues together the components provided by `smoltcp`, handling the low-level details with defaults and API. It glues together the components provided by `smoltcp`, handling the low-level details with defaults and
memory management designed to work well for embedded systems, aiiming for a more "Just Works" experience. memory management designed to work well for embedded systems, aiming for a more "Just Works" experience.
## Features ## Features

View File

@ -390,6 +390,13 @@ impl<'d> TcpIo<'d> {
// CAUTION: smoltcp semantics around EOF are different to what you'd expect // CAUTION: smoltcp semantics around EOF are different to what you'd expect
// from posix-like IO, so we have to tweak things here. // from posix-like IO, so we have to tweak things here.
self.with_mut(|s, _| match s.recv_slice(buf) { self.with_mut(|s, _| match s.recv_slice(buf) {
// Reading into empty buffer
Ok(0) if buf.is_empty() => {
// embedded_io_async::Read's contract is to not block if buf is empty. While
// this function is not a direct implementor of the trait method, we still don't
// want our future to never resolve.
Poll::Ready(Ok(0))
}
// No data ready // No data ready
Ok(0) => { Ok(0) => {
s.register_recv_waker(cx.waker()); s.register_recv_waker(cx.waker());

View File

@ -18,7 +18,7 @@ flavors = [
{ regex_feature = "stm32f7.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32f7.*", target = "thumbv7em-none-eabi" },
{ regex_feature = "stm32c0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32c0.*", target = "thumbv6m-none-eabi" },
{ regex_feature = "stm32g0.*", target = "thumbv6m-none-eabi" }, { regex_feature = "stm32g0.*", target = "thumbv6m-none-eabi" },
{ regex_feature = "stm32g4.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32g4.*", target = "thumbv7em-none-eabi", features = ["low-power"] },
{ regex_feature = "stm32h5.*", target = "thumbv8m.main-none-eabihf" }, { regex_feature = "stm32h5.*", target = "thumbv8m.main-none-eabihf" },
{ regex_feature = "stm32h7.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32h7.*", target = "thumbv7em-none-eabi" },
{ regex_feature = "stm32l0.*", target = "thumbv6m-none-eabi", features = ["low-power"] }, { regex_feature = "stm32l0.*", target = "thumbv6m-none-eabi", features = ["low-power"] },
@ -58,7 +58,7 @@ rand_core = "0.6.3"
sdio-host = "0.5.0" sdio-host = "0.5.0"
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
critical-section = "1.1" critical-section = "1.1"
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-73b8c37ae74fc28b247188c989fd99400611bd6b" } stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-1374ed622714ef4702826699ca21cc1f741f4133" }
vcell = "0.1.3" vcell = "0.1.3"
bxcan = "0.7.0" bxcan = "0.7.0"
nb = "1.0.0" nb = "1.0.0"
@ -76,7 +76,7 @@ critical-section = { version = "1.1", features = ["std"] }
[build-dependencies] [build-dependencies]
proc-macro2 = "1.0.36" proc-macro2 = "1.0.36"
quote = "1.0.15" quote = "1.0.15"
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-73b8c37ae74fc28b247188c989fd99400611bd6b", default-features = false, features = ["metadata"]} stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-1374ed622714ef4702826699ca21cc1f741f4133", default-features = false, features = ["metadata"]}
[features] [features]

View File

@ -6,7 +6,7 @@ use std::{env, fs};
use proc_macro2::{Ident, TokenStream}; use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote}; use quote::{format_ident, quote};
use stm32_metapac::metadata::ir::{BlockItemInner, Enum, FieldSet}; use stm32_metapac::metadata::ir::{BlockItemInner, Enum, FieldSet};
use stm32_metapac::metadata::{MemoryRegionKind, PeripheralRccRegister, METADATA}; use stm32_metapac::metadata::{MemoryRegionKind, PeripheralRccRegister, StopMode, METADATA};
fn main() { fn main() {
let target = env::var("TARGET").unwrap(); let target = env::var("TARGET").unwrap();
@ -557,18 +557,18 @@ fn main() {
}; };
/* /*
If LP and non-LP peripherals share the same RCC enable bit, then a refcount leak will result. A refcount leak can result if the same field is shared by peripherals with different stop modes
This should be checked in stm32-data-gen. This condition should be checked in stm32-data
*/ */
let stop_refcount = if p.name.starts_with("LP") { let stop_refcount = match rcc.stop_mode {
quote! { REFCOUNT_STOP2 } StopMode::Standby => None,
} else { StopMode::Stop2 => Some(quote! { REFCOUNT_STOP2 }),
quote! { REFCOUNT_STOP1 } StopMode::Stop1 => Some(quote! { REFCOUNT_STOP1 }),
}; };
let (incr_stop_refcount, decr_stop_refcount) = if p.name != "RTC" { let (incr_stop_refcount, decr_stop_refcount) = match stop_refcount {
( Some(stop_refcount) => (
quote! { quote! {
#[cfg(feature = "low-power")] #[cfg(feature = "low-power")]
unsafe { crate::rcc::#stop_refcount += 1 }; unsafe { crate::rcc::#stop_refcount += 1 };
@ -577,9 +577,8 @@ fn main() {
#[cfg(feature = "low-power")] #[cfg(feature = "low-power")]
unsafe { crate::rcc::#stop_refcount -= 1 }; unsafe { crate::rcc::#stop_refcount -= 1 };
}, },
) ),
} else { None => (TokenStream::new(), TokenStream::new()),
(quote! {}, quote! {})
}; };
g.extend(quote! { g.extend(quote! {
@ -828,7 +827,7 @@ fn main() {
(("fmc", "NCE"), quote!(crate::fmc::NCEPin)), (("fmc", "NCE"), quote!(crate::fmc::NCEPin)),
(("fmc", "NOE"), quote!(crate::fmc::NOEPin)), (("fmc", "NOE"), quote!(crate::fmc::NOEPin)),
(("fmc", "NWE"), quote!(crate::fmc::NWEPin)), (("fmc", "NWE"), quote!(crate::fmc::NWEPin)),
(("fmc", "Clk"), quote!(crate::fmc::ClkPin)), (("fmc", "CLK"), quote!(crate::fmc::ClkPin)),
(("fmc", "BA0"), quote!(crate::fmc::BA0Pin)), (("fmc", "BA0"), quote!(crate::fmc::BA0Pin)),
(("fmc", "BA1"), quote!(crate::fmc::BA1Pin)), (("fmc", "BA1"), quote!(crate::fmc::BA1Pin)),
(("timer", "CH1"), quote!(crate::timer::Channel1Pin)), (("timer", "CH1"), quote!(crate::timer::Channel1Pin)),
@ -944,17 +943,23 @@ fn main() {
} }
if regs.kind == "opamp" { if regs.kind == "opamp" {
if !pin.signal.starts_with("VP") { if pin.signal.starts_with("VP") {
continue; // Impl NonInvertingPin for the VP* signals (VP0, VP1, VP2, etc)
}
let peri = format_ident!("{}", p.name); let peri = format_ident!("{}", p.name);
let pin_name = format_ident!("{}", pin.pin); let pin_name = format_ident!("{}", pin.pin);
let ch: u8 = pin.signal.strip_prefix("VP").unwrap().parse().unwrap(); let ch: u8 = pin.signal.strip_prefix("VP").unwrap().parse().unwrap();
g.extend(quote! { g.extend(quote! {
impl_opamp_pin!( #peri, #pin_name, #ch); impl_opamp_vp_pin!( #peri, #pin_name, #ch);
}) })
} else if pin.signal == "VOUT" {
// Impl OutputPin for the VOUT pin
let peri = format_ident!("{}", p.name);
let pin_name = format_ident!("{}", pin.pin);
g.extend(quote! {
impl_opamp_vout_pin!( #peri, #pin_name );
})
}
} }
// DAC is special // DAC is special

View File

@ -119,13 +119,11 @@ impl<'a> TDesRing<'a> {
// "Preceding reads and writes cannot be moved past subsequent writes." // "Preceding reads and writes cannot be moved past subsequent writes."
fence(Ordering::Release); fence(Ordering::Release);
self.index = self.index + 1;
if self.index == self.descriptors.len() {
self.index = 0;
}
// signal DMA it can try again. // signal DMA it can try again.
ETH.ethernet_dma().dmactx_dtpr().write(|w| w.0 = 0) // See issue #2129
ETH.ethernet_dma().dmactx_dtpr().write(|w| w.0 = &td as *const _ as u32);
self.index = (self.index + 1) % self.descriptors.len();
} }
} }
@ -237,21 +235,19 @@ impl<'a> RDesRing<'a> {
/// Pop the packet previously returned by `available`. /// Pop the packet previously returned by `available`.
pub(crate) fn pop_packet(&mut self) { pub(crate) fn pop_packet(&mut self) {
let descriptor = &mut self.descriptors[self.index]; let rd = &mut self.descriptors[self.index];
assert!(descriptor.available()); assert!(rd.available());
self.descriptors[self.index].set_ready(self.buffers[self.index].0.as_mut_ptr()); rd.set_ready(self.buffers[self.index].0.as_mut_ptr());
// "Preceding reads and writes cannot be moved past subsequent writes." // "Preceding reads and writes cannot be moved past subsequent writes."
fence(Ordering::Release); fence(Ordering::Release);
// signal DMA it can try again. // signal DMA it can try again.
ETH.ethernet_dma().dmacrx_dtpr().write(|w| w.0 = 0); // See issue #2129
ETH.ethernet_dma().dmacrx_dtpr().write(|w| w.0 = &rd as *const _ as u32);
// Increment index. // Increment index.
self.index += 1; self.index = (self.index + 1) % self.descriptors.len();
if self.index == self.descriptors.len() {
self.index = 0
}
} }
} }

View File

@ -12,6 +12,37 @@ pub struct Fmc<'d, T: Instance> {
unsafe impl<'d, T> Send for Fmc<'d, T> where T: Instance {} unsafe impl<'d, T> Send for Fmc<'d, T> where T: Instance {}
impl<'d, T> Fmc<'d, T>
where
T: Instance,
{
/// Create a raw FMC instance.
///
/// **Note:** This is currently used to provide access to some basic FMC functions
/// for manual configuration for memory types that stm32-fmc does not support.
pub fn new_raw(_instance: impl Peripheral<P = T> + 'd) -> Self {
Self { peri: PhantomData }
}
/// Enable the FMC peripheral and reset it.
pub fn enable(&mut self) {
T::enable_and_reset();
}
/// Enable the memory controller on applicable chips.
pub fn memory_controller_enable(&mut self) {
// fmc v1 and v2 does not have the fmcen bit
// fsmc v1, v2 and v3 does not have the fmcen bit
// This is a "not" because it is expected that all future versions have this bit
#[cfg(not(any(fmc_v1x3, fmc_v2x1, fsmc_v1x0, fsmc_v1x3, fsmc_v2x3, fsmc_v3x1)))]
T::REGS.bcr1().modify(|r| r.set_fmcen(true));
}
pub fn source_clock_hz(&self) -> u32 {
<T as crate::rcc::sealed::RccPeripheral>::frequency().0
}
}
unsafe impl<'d, T> stm32_fmc::FmcPeripheral for Fmc<'d, T> unsafe impl<'d, T> stm32_fmc::FmcPeripheral for Fmc<'d, T>
where where
T: Instance, T: Instance,

View File

@ -13,21 +13,50 @@ pub enum OpAmpGain {
Mul16, Mul16,
} }
pub struct OpAmpOutput<'d, 'p, T: Instance, P: NonInvertingPin<T>> { #[derive(Clone, Copy)]
_inner: &'d OpAmp<'d, T>, pub enum OpAmpSpeed {
_input: &'p mut P, Normal,
HighSpeed,
} }
#[cfg(opamp_g4)]
impl From<OpAmpSpeed> for crate::pac::opamp::vals::OpampCsrOpahsm {
fn from(v: OpAmpSpeed) -> Self {
match v {
OpAmpSpeed::Normal => crate::pac::opamp::vals::OpampCsrOpahsm::NORMAL,
OpAmpSpeed::HighSpeed => crate::pac::opamp::vals::OpampCsrOpahsm::HIGHSPEED,
}
}
}
/// OpAmp external outputs, wired to a GPIO pad.
///
/// The GPIO output pad is held by this struct to ensure it cannot be used elsewhere.
///
/// This struct can also be used as an ADC input.
pub struct OpAmpOutput<'d, 'p, T: Instance, P: OutputPin<T>> {
_inner: &'d OpAmp<'d, T>,
_output: &'p mut P,
}
/// OpAmp internal outputs, wired directly to ADC inputs.
///
/// This struct can be used as an ADC input.
pub struct OpAmpInternalOutput<'d, T: Instance> {
_inner: &'d OpAmp<'d, T>,
}
/// OpAmp driver.
pub struct OpAmp<'d, T: Instance> { pub struct OpAmp<'d, T: Instance> {
_inner: PeripheralRef<'d, T>, _inner: PeripheralRef<'d, T>,
} }
impl<'d, T: Instance> OpAmp<'d, T> { impl<'d, T: Instance> OpAmp<'d, T> {
pub fn new(opamp: impl Peripheral<P = T> + 'd) -> Self { /// Create a new driver instance.
Self::new_inner(opamp) ///
} /// Enables the OpAmp and configures the speed, but
/// does not set any other configuration.
fn new_inner(opamp: impl Peripheral<P = T> + 'd) -> Self { pub fn new(opamp: impl Peripheral<P = T> + 'd, #[cfg(opamp_g4)] speed: OpAmpSpeed) -> Self {
into_ref!(opamp); into_ref!(opamp);
#[cfg(opamp_f3)] #[cfg(opamp_f3)]
@ -38,15 +67,34 @@ impl<'d, T: Instance> OpAmp<'d, T> {
#[cfg(opamp_g4)] #[cfg(opamp_g4)]
T::regs().opamp_csr().modify(|w| { T::regs().opamp_csr().modify(|w| {
w.set_opaen(true); w.set_opaen(true);
w.set_opahsm(speed.into());
}); });
Self { _inner: opamp } Self { _inner: opamp }
} }
pub fn buffer_for<'a, 'b, P>(&'a mut self, pin: &'b mut P, gain: OpAmpGain) -> OpAmpOutput<'a, 'b, T, P> /// Configure the OpAmp as a buffer for the provided input pin,
/// outputting to the provided output pin.
///
/// The input pin is configured for analogue mode but not consumed,
/// so it may subsequently be used for ADC or comparator inputs.
///
/// The output pin is held within the returned [`OpAmpOutput`] struct,
/// preventing it being used elsewhere. The `OpAmpOutput` can then be
/// directly used as an ADC input.
pub fn buffer_ext<'a, 'b, IP, OP>(
&'a mut self,
in_pin: &IP,
out_pin: &'b mut OP,
gain: OpAmpGain,
) -> OpAmpOutput<'a, 'b, T, OP>
where where
P: NonInvertingPin<T>, IP: NonInvertingPin<T> + crate::gpio::sealed::Pin,
OP: OutputPin<T> + crate::gpio::sealed::Pin,
{ {
in_pin.set_as_analog();
out_pin.set_as_analog();
let (vm_sel, pga_gain) = match gain { let (vm_sel, pga_gain) = match gain {
OpAmpGain::Mul1 => (0b11, 0b00), OpAmpGain::Mul1 => (0b11, 0b00),
OpAmpGain::Mul2 => (0b10, 0b00), OpAmpGain::Mul2 => (0b10, 0b00),
@ -57,25 +105,76 @@ impl<'d, T: Instance> OpAmp<'d, T> {
#[cfg(opamp_f3)] #[cfg(opamp_f3)]
T::regs().opampcsr().modify(|w| { T::regs().opampcsr().modify(|w| {
w.set_vp_sel(pin.channel()); w.set_vp_sel(in_pin.channel());
w.set_vm_sel(vm_sel); w.set_vm_sel(vm_sel);
w.set_pga_gain(pga_gain); w.set_pga_gain(pga_gain);
w.set_opampen(true);
}); });
#[cfg(opamp_g4)] #[cfg(opamp_g4)]
T::regs().opamp_csr().modify(|w| { T::regs().opamp_csr().modify(|w| {
use crate::pac::opamp::vals::*; use crate::pac::opamp::vals::*;
w.set_vp_sel(OpampCsrVpSel::from_bits(pin.channel())); w.set_vp_sel(OpampCsrVpSel::from_bits(in_pin.channel()));
w.set_vm_sel(OpampCsrVmSel::from_bits(vm_sel)); w.set_vm_sel(OpampCsrVmSel::from_bits(vm_sel));
w.set_pga_gain(OpampCsrPgaGain::from_bits(pga_gain)); w.set_pga_gain(OpampCsrPgaGain::from_bits(pga_gain));
w.set_opaintoen(OpampCsrOpaintoen::OUTPUTPIN);
w.set_opaen(true);
}); });
OpAmpOutput { OpAmpOutput {
_inner: self, _inner: self,
_input: pin, _output: out_pin,
} }
} }
/// Configure the OpAmp as a buffer for the provided input pin,
/// with the output only used internally.
///
/// The input pin is configured for analogue mode but not consumed,
/// so it may be subsequently used for ADC or comparator inputs.
///
/// The returned `OpAmpInternalOutput` struct may be used as an ADC input.
#[cfg(opamp_g4)]
pub fn buffer_int<'a, P>(&'a mut self, pin: &P, gain: OpAmpGain) -> OpAmpInternalOutput<'a, T>
where
P: NonInvertingPin<T> + crate::gpio::sealed::Pin,
{
pin.set_as_analog();
let (vm_sel, pga_gain) = match gain {
OpAmpGain::Mul1 => (0b11, 0b00),
OpAmpGain::Mul2 => (0b10, 0b00),
OpAmpGain::Mul4 => (0b10, 0b01),
OpAmpGain::Mul8 => (0b10, 0b10),
OpAmpGain::Mul16 => (0b10, 0b11),
};
T::regs().opamp_csr().modify(|w| {
use crate::pac::opamp::vals::*;
w.set_vp_sel(OpampCsrVpSel::from_bits(pin.channel()));
w.set_vm_sel(OpampCsrVmSel::from_bits(vm_sel));
w.set_pga_gain(OpampCsrPgaGain::from_bits(pga_gain));
w.set_opaintoen(OpampCsrOpaintoen::ADCCHANNEL);
w.set_opaen(true);
});
OpAmpInternalOutput { _inner: self }
}
}
impl<'d, T: Instance> Drop for OpAmp<'d, T> {
fn drop(&mut self) {
#[cfg(opamp_f3)]
T::regs().opampcsr().modify(|w| {
w.set_opampen(false);
});
#[cfg(opamp_g4)]
T::regs().opamp_csr().modify(|w| {
w.set_opaen(false);
});
}
} }
pub trait Instance: sealed::Instance + 'static {} pub trait Instance: sealed::Instance + 'static {}
@ -92,18 +191,19 @@ pub(crate) mod sealed {
pub trait InvertingPin<T: Instance> { pub trait InvertingPin<T: Instance> {
fn channel(&self) -> u8; fn channel(&self) -> u8;
} }
pub trait OutputPin<T: Instance> {}
} }
pub trait NonInvertingPin<T: Instance>: sealed::NonInvertingPin<T> {} pub trait NonInvertingPin<T: Instance>: sealed::NonInvertingPin<T> {}
pub trait InvertingPin<T: Instance>: sealed::InvertingPin<T> {} pub trait InvertingPin<T: Instance>: sealed::InvertingPin<T> {}
pub trait OutputPin<T: Instance>: sealed::OutputPin<T> {}
#[cfg(opamp_f3)] macro_rules! impl_opamp_external_output {
macro_rules! impl_opamp_output {
($inst:ident, $adc:ident, $ch:expr) => { ($inst:ident, $adc:ident, $ch:expr) => {
foreach_adc!( foreach_adc!(
($adc, $common_inst:ident, $adc_clock:ident) => { ($adc, $common_inst:ident, $adc_clock:ident) => {
impl<'d, 'p, P: NonInvertingPin<crate::peripherals::$inst>> crate::adc::sealed::AdcPin<crate::peripherals::$adc> impl<'d, 'p, P: OutputPin<crate::peripherals::$inst>> crate::adc::sealed::AdcPin<crate::peripherals::$adc>
for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P> for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P>
{ {
fn channel(&self) -> u8 { fn channel(&self) -> u8 {
@ -111,7 +211,7 @@ macro_rules! impl_opamp_output {
} }
} }
impl<'d, 'p, P: NonInvertingPin<crate::peripherals::$inst>> crate::adc::AdcPin<crate::peripherals::$adc> impl<'d, 'p, P: OutputPin<crate::peripherals::$inst>> crate::adc::AdcPin<crate::peripherals::$adc>
for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P> for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P>
{ {
} }
@ -120,19 +220,79 @@ macro_rules! impl_opamp_output {
}; };
} }
#[cfg(opamp_f3)]
foreach_peripheral!( foreach_peripheral!(
(opamp, OPAMP1) => { (opamp, OPAMP1) => {
impl_opamp_output!(OPAMP1, ADC1, 3); impl_opamp_external_output!(OPAMP1, ADC1, 3);
}; };
(opamp, OPAMP2) => { (opamp, OPAMP2) => {
impl_opamp_output!(OPAMP2, ADC2, 3); impl_opamp_external_output!(OPAMP2, ADC2, 3);
}; };
(opamp, OPAMP3) => { (opamp, OPAMP3) => {
impl_opamp_output!(OPAMP3, ADC3, 1); impl_opamp_external_output!(OPAMP3, ADC3, 1);
}; };
// OPAMP4 only in STM32G4 Cat 3 devices
(opamp, OPAMP4) => { (opamp, OPAMP4) => {
impl_opamp_output!(OPAMP4, ADC4, 3); impl_opamp_external_output!(OPAMP4, ADC4, 3);
};
// OPAMP5 only in STM32G4 Cat 3 devices
(opamp, OPAMP5) => {
impl_opamp_external_output!(OPAMP5, ADC5, 1);
};
// OPAMP6 only in STM32G4 Cat 3/4 devices
(opamp, OPAMP6) => {
impl_opamp_external_output!(OPAMP6, ADC1, 14);
};
);
#[cfg(opamp_g4)]
macro_rules! impl_opamp_internal_output {
($inst:ident, $adc:ident, $ch:expr) => {
foreach_adc!(
($adc, $common_inst:ident, $adc_clock:ident) => {
impl<'d> crate::adc::sealed::AdcPin<crate::peripherals::$adc>
for OpAmpInternalOutput<'d, crate::peripherals::$inst>
{
fn channel(&self) -> u8 {
$ch
}
}
impl<'d> crate::adc::AdcPin<crate::peripherals::$adc>
for OpAmpInternalOutput<'d, crate::peripherals::$inst>
{
}
};
);
};
}
#[cfg(opamp_g4)]
foreach_peripheral!(
(opamp, OPAMP1) => {
impl_opamp_internal_output!(OPAMP1, ADC1, 13);
};
(opamp, OPAMP2) => {
impl_opamp_internal_output!(OPAMP2, ADC2, 16);
};
(opamp, OPAMP3) => {
impl_opamp_internal_output!(OPAMP3, ADC2, 18);
// Only in Cat 3/4 devices
impl_opamp_internal_output!(OPAMP3, ADC3, 13);
};
// OPAMP4 only in Cat 3 devices
(opamp, OPAMP4) => {
impl_opamp_internal_output!(OPAMP4, ADC5, 5);
};
// OPAMP5 only in Cat 3 devices
(opamp, OPAMP5) => {
impl_opamp_internal_output!(OPAMP5, ADC5, 3);
};
// OPAMP6 only in Cat 3/4 devices
(opamp, OPAMP6) => {
// Only in Cat 3 devices
impl_opamp_internal_output!(OPAMP6, ADC4, 17);
// Only in Cat 4 devices
impl_opamp_internal_output!(OPAMP6, ADC3, 17);
}; };
); );
@ -145,13 +305,12 @@ foreach_peripheral! {
} }
impl Instance for crate::peripherals::$inst { impl Instance for crate::peripherals::$inst {
} }
}; };
} }
#[allow(unused_macros)] #[allow(unused_macros)]
macro_rules! impl_opamp_pin { macro_rules! impl_opamp_vp_pin {
($inst:ident, $pin:ident, $ch:expr) => { ($inst:ident, $pin:ident, $ch:expr) => {
impl crate::opamp::NonInvertingPin<peripherals::$inst> for crate::peripherals::$pin {} impl crate::opamp::NonInvertingPin<peripherals::$inst> for crate::peripherals::$pin {}
impl crate::opamp::sealed::NonInvertingPin<peripherals::$inst> for crate::peripherals::$pin { impl crate::opamp::sealed::NonInvertingPin<peripherals::$inst> for crate::peripherals::$pin {
@ -161,3 +320,11 @@ macro_rules! impl_opamp_pin {
} }
}; };
} }
#[allow(unused_macros)]
macro_rules! impl_opamp_vout_pin {
($inst:ident, $pin:ident) => {
impl crate::opamp::OutputPin<peripherals::$inst> for crate::peripherals::$pin {}
impl crate::opamp::sealed::OutputPin<peripherals::$inst> for crate::peripherals::$pin {}
};
}

View File

@ -1,8 +1,9 @@
use crate::pac::pwr::vals::Vos;
pub use crate::pac::rcc::vals::{ pub use crate::pac::rcc::vals::{
Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp, Pllq, Pllr, Pllsrc as PllSource, Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp, Pllq, Pllr, Pllsrc as PllSource,
Ppre as APBPrescaler, Sw as Sysclk, Ppre as APBPrescaler, Sw as Sysclk,
}; };
use crate::pac::{FLASH, RCC}; use crate::pac::{FLASH, PWR, RCC};
use crate::rcc::{set_freqs, Clocks}; use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz; use crate::time::Hertz;
@ -100,12 +101,17 @@ impl Default for Config {
} }
pub(crate) unsafe fn init(config: Config) { pub(crate) unsafe fn init(config: Config) {
// set VOS to SCALE1, if use PLL
// TODO: check real clock speed before set VOS
if config.pll.is_some() {
PWR.cr1().modify(|w| w.set_vos(Vos::SCALE1));
}
// always enable overdrive for now. Make it configurable in the future. // always enable overdrive for now. Make it configurable in the future.
#[cfg(not(any( #[cfg(not(any(
stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f405, stm32f407, stm32f415, stm32f417 stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f405, stm32f407, stm32f415, stm32f417
)))] )))]
{ {
use crate::pac::PWR;
PWR.cr1().modify(|w| w.set_oden(true)); PWR.cr1().modify(|w| w.set_oden(true));
while !PWR.csr1().read().odrdy() {} while !PWR.csr1().read().odrdy() {}

View File

@ -7,7 +7,6 @@ pub use crate::pac::rcc::vals::{
Pllr as PllR, Ppre as APBPrescaler, Pllr as PllR, Ppre as APBPrescaler,
}; };
use crate::pac::{PWR, RCC}; use crate::pac::{PWR, RCC};
use crate::rcc::sealed::RccPeripheral;
use crate::rcc::{set_freqs, Clocks}; use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz; use crate::time::Hertz;
@ -67,23 +66,13 @@ pub struct Pll {
pub enum Clock48MhzSrc { pub enum Clock48MhzSrc {
/// Use the High Speed Internal Oscillator. For USB usage, the CRS must be used to calibrate the /// Use the High Speed Internal Oscillator. For USB usage, the CRS must be used to calibrate the
/// oscillator to comply with the USB specification for oscillator tolerance. /// oscillator to comply with the USB specification for oscillator tolerance.
Hsi48(Option<CrsConfig>), Hsi48(super::Hsi48Config),
/// Use the PLLQ output. The PLL must be configured to output a 48MHz clock. For USB usage the /// Use the PLLQ output. The PLL must be configured to output a 48MHz clock. For USB usage the
/// PLL needs to be using the HSE source to comply with the USB specification for oscillator /// PLL needs to be using the HSE source to comply with the USB specification for oscillator
/// tolerance. /// tolerance.
PllQ, PllQ,
} }
/// Sets the sync source for the Clock Recovery System (CRS).
pub enum CrsSyncSource {
/// Use an external GPIO to sync the CRS.
Gpio,
/// Use the Low Speed External oscillator to sync the CRS.
Lse,
/// Use the USB SOF to sync the CRS.
Usb,
}
/// Clocks configutation /// Clocks configutation
pub struct Config { pub struct Config {
pub mux: ClockSrc, pub mux: ClockSrc,
@ -102,12 +91,6 @@ pub struct Config {
pub ls: super::LsConfig, pub ls: super::LsConfig,
} }
/// Configuration for the Clock Recovery System (CRS) used to trim the HSI48 oscillator.
pub struct CrsConfig {
/// Sync source for the CRS.
pub sync_src: CrsSyncSource,
}
impl Default for Config { impl Default for Config {
#[inline] #[inline]
fn default() -> Config { fn default() -> Config {
@ -118,7 +101,7 @@ impl Default for Config {
apb2_pre: APBPrescaler::DIV1, apb2_pre: APBPrescaler::DIV1,
low_power_run: false, low_power_run: false,
pll: None, pll: None,
clock_48mhz_src: Some(Clock48MhzSrc::Hsi48(None)), clock_48mhz_src: Some(Clock48MhzSrc::Hsi48(Default::default())),
adc12_clock_source: Adcsel::DISABLE, adc12_clock_source: Adcsel::DISABLE,
adc345_clock_source: Adcsel::DISABLE, adc345_clock_source: Adcsel::DISABLE,
ls: Default::default(), ls: Default::default(),
@ -288,33 +271,8 @@ pub(crate) unsafe fn init(config: Config) {
crate::pac::rcc::vals::Clk48sel::PLL1_Q crate::pac::rcc::vals::Clk48sel::PLL1_Q
} }
Clock48MhzSrc::Hsi48(crs_config) => { Clock48MhzSrc::Hsi48(config) => {
// Enable HSI48 super::init_hsi48(config);
RCC.crrcr().modify(|w| w.set_hsi48on(true));
// Wait for HSI48 to turn on
while RCC.crrcr().read().hsi48rdy() == false {}
// Enable and setup CRS if needed
if let Some(crs_config) = crs_config {
crate::peripherals::CRS::enable_and_reset();
let sync_src = match crs_config.sync_src {
CrsSyncSource::Gpio => crate::pac::crs::vals::Syncsrc::GPIO,
CrsSyncSource::Lse => crate::pac::crs::vals::Syncsrc::LSE,
CrsSyncSource::Usb => crate::pac::crs::vals::Syncsrc::USB,
};
crate::pac::CRS.cfgr().modify(|w| {
w.set_syncsrc(sync_src);
});
// These are the correct settings for standard USB operation. If other settings
// are needed there will need to be additional config options for the CRS.
crate::pac::CRS.cr().modify(|w| {
w.set_autotrimen(true);
w.set_cen(true);
});
}
crate::pac::rcc::vals::Clk48sel::HSI48 crate::pac::rcc::vals::Clk48sel::HSI48
} }
}; };

View File

@ -21,9 +21,6 @@ pub const HSI_FREQ: Hertz = Hertz(64_000_000);
/// CSI speed /// CSI speed
pub const CSI_FREQ: Hertz = Hertz(4_000_000); pub const CSI_FREQ: Hertz = Hertz(4_000_000);
/// HSI48 speed
pub const HSI48_FREQ: Hertz = Hertz(48_000_000);
const VCO_RANGE: RangeInclusive<Hertz> = Hertz(150_000_000)..=Hertz(420_000_000); const VCO_RANGE: RangeInclusive<Hertz> = Hertz(150_000_000)..=Hertz(420_000_000);
#[cfg(any(stm32h5, pwr_h7rm0455))] #[cfg(any(stm32h5, pwr_h7rm0455))]
const VCO_WIDE_RANGE: RangeInclusive<Hertz> = Hertz(128_000_000)..=Hertz(560_000_000); const VCO_WIDE_RANGE: RangeInclusive<Hertz> = Hertz(128_000_000)..=Hertz(560_000_000);
@ -126,7 +123,7 @@ pub struct Config {
pub hsi: Option<HSIPrescaler>, pub hsi: Option<HSIPrescaler>,
pub hse: Option<Hse>, pub hse: Option<Hse>,
pub csi: bool, pub csi: bool,
pub hsi48: bool, pub hsi48: Option<super::Hsi48Config>,
pub sys: Sysclk, pub sys: Sysclk,
pub pll1: Option<Pll>, pub pll1: Option<Pll>,
@ -155,7 +152,7 @@ impl Default for Config {
hsi: Some(HSIPrescaler::DIV1), hsi: Some(HSIPrescaler::DIV1),
hse: None, hse: None,
csi: false, csi: false,
hsi48: false, hsi48: Some(Default::default()),
sys: Sysclk::HSI, sys: Sysclk::HSI,
pll1: None, pll1: None,
pll2: None, pll2: None,
@ -301,14 +298,7 @@ pub(crate) unsafe fn init(config: Config) {
}; };
// Configure HSI48. // Configure HSI48.
RCC.cr().modify(|w| w.set_hsi48on(config.hsi48)); let _hsi48 = config.hsi48.map(super::init_hsi48);
let _hsi48 = match config.hsi48 {
false => None,
true => {
while !RCC.cr().read().hsi48rdy() {}
Some(CSI_FREQ)
}
};
// Configure CSI. // Configure CSI.
RCC.cr().modify(|w| w.set_csion(config.csi)); RCC.cr().modify(|w| w.set_csion(config.csi));

View File

@ -0,0 +1,62 @@
#![allow(unused)]
use crate::pac::crs::vals::Syncsrc;
use crate::pac::{CRS, RCC};
use crate::rcc::sealed::RccPeripheral;
use crate::time::Hertz;
/// HSI48 speed
pub const HSI48_FREQ: Hertz = Hertz(48_000_000);
/// Configuration for the HSI48 clock
#[derive(Clone, Copy, Debug)]
pub struct Hsi48Config {
/// Enable CRS Sync from USB Start Of Frame (SOF) events.
/// Required if HSI48 is going to be used as USB clock.
///
/// Other use cases of CRS are not supported yet.
pub sync_from_usb: bool,
}
impl Default for Hsi48Config {
fn default() -> Self {
Self { sync_from_usb: false }
}
}
pub(crate) fn init_hsi48(config: Hsi48Config) -> Hertz {
// Enable VREFINT reference for HSI48 oscillator
#[cfg(stm32l0)]
crate::pac::SYSCFG.cfgr3().modify(|w| {
w.set_enref_hsi48(true);
w.set_en_vrefint(true);
});
// Enable HSI48
#[cfg(not(any(stm32u5, stm32g0, stm32h5, stm32h7, stm32u5, stm32wba, stm32f0)))]
let r = RCC.crrcr();
#[cfg(any(stm32u5, stm32g0, stm32h5, stm32h7, stm32u5, stm32wba))]
let r = RCC.cr();
#[cfg(any(stm32f0))]
let r = RCC.cr2();
r.modify(|w| w.set_hsi48on(true));
while r.read().hsi48rdy() == false {}
if config.sync_from_usb {
crate::peripherals::CRS::enable_and_reset();
CRS.cfgr().modify(|w| {
w.set_syncsrc(Syncsrc::USB);
});
// These are the correct settings for standard USB operation. If other settings
// are needed there will need to be additional config options for the CRS.
crate::pac::CRS.cr().modify(|w| {
w.set_autotrimen(true);
w.set_cen(true);
});
}
HSI48_FREQ
}

View File

@ -3,8 +3,6 @@ pub use crate::pac::rcc::vals::{
Hpre as AHBPrescaler, Msirange as MSIRange, Plldiv as PLLDiv, Plldiv as PllDiv, Pllmul as PLLMul, Pllmul as PllMul, Hpre as AHBPrescaler, Msirange as MSIRange, Plldiv as PLLDiv, Plldiv as PllDiv, Pllmul as PLLMul, Pllmul as PllMul,
Pllsrc as PLLSource, Ppre as APBPrescaler, Sw as ClockSrc, Pllsrc as PLLSource, Ppre as APBPrescaler, Sw as ClockSrc,
}; };
#[cfg(crs)]
use crate::pac::{crs, CRS, SYSCFG};
use crate::pac::{FLASH, PWR, RCC}; use crate::pac::{FLASH, PWR, RCC};
use crate::rcc::{set_freqs, Clocks}; use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz; use crate::time::Hertz;
@ -47,7 +45,7 @@ pub struct Config {
pub hsi: bool, pub hsi: bool,
pub hse: Option<Hse>, pub hse: Option<Hse>,
#[cfg(crs)] #[cfg(crs)]
pub hsi48: bool, pub hsi48: Option<super::Hsi48Config>,
pub pll: Option<Pll>, pub pll: Option<Pll>,
@ -68,7 +66,7 @@ impl Default for Config {
hse: None, hse: None,
hsi: false, hsi: false,
#[cfg(crs)] #[cfg(crs)]
hsi48: false, hsi48: Some(Default::default()),
pll: None, pll: None,
@ -174,37 +172,11 @@ pub(crate) unsafe fn init(config: Config) {
let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk1, config.apb2_pre); let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk1, config.apb2_pre);
#[cfg(crs)] #[cfg(crs)]
if config.hsi48 { let _hsi48 = config.hsi48.map(|config| {
// Reset CRS peripheral
RCC.apb1rstr().modify(|w| w.set_crsrst(true));
RCC.apb1rstr().modify(|w| w.set_crsrst(false));
// Enable CRS peripheral
RCC.apb1enr().modify(|w| w.set_crsen(true));
// Initialize CRS
CRS.cfgr().write(|w|
// Select LSE as synchronization source
w.set_syncsrc(crs::vals::Syncsrc::LSE));
CRS.cr().modify(|w| {
w.set_autotrimen(true);
w.set_cen(true);
});
// Enable VREFINT reference for HSI48 oscillator
SYSCFG.cfgr3().modify(|w| {
w.set_enref_hsi48(true);
w.set_en_vrefint(true);
});
// Select HSI48 as USB clock // Select HSI48 as USB clock
RCC.ccipr().modify(|w| w.set_hsi48msel(true)); RCC.ccipr().modify(|w| w.set_hsi48msel(true));
super::init_hsi48(config)
// Enable dedicated USB clock });
RCC.crrcr().modify(|w| w.set_hsi48on(true));
while !RCC.crrcr().read().hsi48rdy() {}
}
set_freqs(Clocks { set_freqs(Clocks {
sys: sys_clk, sys: sys_clk,

View File

@ -58,8 +58,8 @@ pub struct Config {
pub msi: Option<MSIRange>, pub msi: Option<MSIRange>,
pub hsi: bool, pub hsi: bool,
pub hse: Option<Hse>, pub hse: Option<Hse>,
#[cfg(any(all(stm32l4, not(any(stm32l47x, stm32l48x))), stm32l5, stm32wb))] #[cfg(crs)]
pub hsi48: bool, pub hsi48: Option<super::Hsi48Config>,
// pll // pll
pub pll: Option<Pll>, pub pll: Option<Pll>,
@ -108,8 +108,8 @@ impl Default for Config {
pllsai1: None, pllsai1: None,
#[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))] #[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]
pllsai2: None, pllsai2: None,
#[cfg(any(all(stm32l4, not(any(stm32l47x, stm32l48x))), stm32l5, stm32wb))] #[cfg(crs)]
hsi48: true, hsi48: Some(Default::default()),
#[cfg(any(stm32l4, stm32l5, stm32wb))] #[cfg(any(stm32l4, stm32l5, stm32wb))]
clk48_src: Clk48Src::HSI48, clk48_src: Clk48Src::HSI48,
ls: Default::default(), ls: Default::default(),
@ -126,7 +126,8 @@ pub const WPAN_DEFAULT: Config = Config {
prescaler: HsePrescaler::DIV1, prescaler: HsePrescaler::DIV1,
}), }),
mux: ClockSrc::PLL1_R, mux: ClockSrc::PLL1_R,
hsi48: true, #[cfg(crs)]
hsi48: Some(super::Hsi48Config { sync_from_usb: false }),
msi: None, msi: None,
hsi: false, hsi: false,
clk48_src: Clk48Src::PLL1_Q, clk48_src: Clk48Src::PLL1_Q,
@ -216,15 +217,10 @@ pub(crate) unsafe fn init(config: Config) {
hse.freq hse.freq
}); });
#[cfg(any(all(stm32l4, not(any(stm32l47x, stm32l48x))), stm32l5, stm32wb))] #[cfg(crs)]
let hsi48 = config.hsi48.then(|| { let _hsi48 = config.hsi48.map(super::init_hsi48);
RCC.crrcr().modify(|w| w.set_hsi48on(true)); #[cfg(not(crs))]
while !RCC.crrcr().read().hsi48rdy() {} let _hsi48: Option<Hertz> = None;
Hertz(48_000_000)
});
#[cfg(any(stm32l47x, stm32l48x))]
let hsi48 = None;
let _plls = [ let _plls = [
&config.pll, &config.pll,
@ -275,7 +271,7 @@ pub(crate) unsafe fn init(config: Config) {
RCC.ccipr1().modify(|w| w.set_clk48sel(config.clk48_src)); RCC.ccipr1().modify(|w| w.set_clk48sel(config.clk48_src));
#[cfg(any(stm32l4, stm32l5, stm32wb))] #[cfg(any(stm32l4, stm32l5, stm32wb))]
let _clk48 = match config.clk48_src { let _clk48 = match config.clk48_src {
Clk48Src::HSI48 => hsi48, Clk48Src::HSI48 => _hsi48,
Clk48Src::MSI => msi, Clk48Src::MSI => msi,
Clk48Src::PLLSAI1_Q => pllsai1.q, Clk48Src::PLLSAI1_Q => pllsai1.q,
Clk48Src::PLL1_Q => pll.q, Clk48Src::PLL1_Q => pll.q,

View File

@ -9,6 +9,11 @@ mod mco;
pub use bd::*; pub use bd::*;
pub use mco::*; pub use mco::*;
#[cfg(crs)]
mod hsi48;
#[cfg(crs)]
pub use hsi48::*;
#[cfg_attr(rcc_f0, path = "f0.rs")] #[cfg_attr(rcc_f0, path = "f0.rs")]
#[cfg_attr(any(rcc_f1, rcc_f100, rcc_f1cl), path = "f1.rs")] #[cfg_attr(any(rcc_f1, rcc_f100, rcc_f1cl), path = "f1.rs")]
#[cfg_attr(rcc_f2, path = "f2.rs")] #[cfg_attr(rcc_f2, path = "f2.rs")]

View File

@ -115,7 +115,7 @@ pub struct Config {
pub apb1_pre: APBPrescaler, pub apb1_pre: APBPrescaler,
pub apb2_pre: APBPrescaler, pub apb2_pre: APBPrescaler,
pub apb3_pre: APBPrescaler, pub apb3_pre: APBPrescaler,
pub hsi48: bool, pub hsi48: Option<super::Hsi48Config>,
/// The voltage range influences the maximum clock frequencies for different parts of the /// The voltage range influences the maximum clock frequencies for different parts of the
/// device. In particular, system clocks exceeding 110 MHz require `RANGE1`, and system clocks /// device. In particular, system clocks exceeding 110 MHz require `RANGE1`, and system clocks
/// exceeding 55 MHz require at least `RANGE2`. /// exceeding 55 MHz require at least `RANGE2`.
@ -189,7 +189,7 @@ impl Default for Config {
apb1_pre: APBPrescaler::DIV1, apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1, apb2_pre: APBPrescaler::DIV1,
apb3_pre: APBPrescaler::DIV1, apb3_pre: APBPrescaler::DIV1,
hsi48: true, hsi48: Some(Default::default()),
voltage_range: VoltageScale::RANGE3, voltage_range: VoltageScale::RANGE3,
ls: Default::default(), ls: Default::default(),
} }
@ -322,10 +322,7 @@ pub(crate) unsafe fn init(config: Config) {
} }
}; };
if config.hsi48 { let _hsi48 = config.hsi48.map(super::init_hsi48);
RCC.cr().modify(|w| w.set_hsi48on(true));
while !RCC.cr().read().hsi48rdy() {}
}
// The clock source is ready // The clock source is ready
// Calculate and set the flash wait states // Calculate and set the flash wait states

View File

@ -33,6 +33,61 @@ use embassy_hal_internal::Peripheral;
use crate::peripherals::RTC; use crate::peripherals::RTC;
use crate::rtc::sealed::Instance; use crate::rtc::sealed::Instance;
#[allow(dead_code)]
#[repr(u8)]
#[derive(Clone, Copy, Debug)]
pub(crate) enum WakeupPrescaler {
Div2 = 2,
Div4 = 4,
Div8 = 8,
Div16 = 16,
}
#[cfg(any(stm32wb, stm32f4, stm32l0, stm32g4))]
impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel {
fn from(val: WakeupPrescaler) -> Self {
use crate::pac::rtc::vals::Wucksel;
match val {
WakeupPrescaler::Div2 => Wucksel::DIV2,
WakeupPrescaler::Div4 => Wucksel::DIV4,
WakeupPrescaler::Div8 => Wucksel::DIV8,
WakeupPrescaler::Div16 => Wucksel::DIV16,
}
}
}
#[cfg(any(stm32wb, stm32f4, stm32l0, stm32g4))]
impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler {
fn from(val: crate::pac::rtc::vals::Wucksel) -> Self {
use crate::pac::rtc::vals::Wucksel;
match val {
Wucksel::DIV2 => WakeupPrescaler::Div2,
Wucksel::DIV4 => WakeupPrescaler::Div4,
Wucksel::DIV8 => WakeupPrescaler::Div8,
Wucksel::DIV16 => WakeupPrescaler::Div16,
_ => unreachable!(),
}
}
}
#[cfg(feature = "low-power")]
impl WakeupPrescaler {
pub fn compute_min(val: u32) -> Self {
*[
WakeupPrescaler::Div2,
WakeupPrescaler::Div4,
WakeupPrescaler::Div8,
WakeupPrescaler::Div16,
]
.iter()
.skip_while(|psc| **psc as u32 <= val)
.next()
.unwrap_or(&WakeupPrescaler::Div16)
}
}
/// Errors that can occur on methods on [RtcClock] /// Errors that can occur on methods on [RtcClock]
#[non_exhaustive] #[non_exhaustive]
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
@ -277,6 +332,114 @@ impl Rtc {
pub fn write_backup_register(&self, register: usize, value: u32) { pub fn write_backup_register(&self, register: usize, value: u32) {
RTC::write_backup_register(&RTC::regs(), register, value) RTC::write_backup_register(&RTC::regs(), register, value)
} }
#[cfg(feature = "low-power")]
/// start the wakeup alarm and wtih a duration that is as close to but less than
/// the requested duration, and record the instant the wakeup alarm was started
pub(crate) fn start_wakeup_alarm(
&self,
requested_duration: embassy_time::Duration,
cs: critical_section::CriticalSection,
) {
use embassy_time::{Duration, TICK_HZ};
#[cfg(any(rtc_v3, rtc_v3u5))]
use crate::pac::rtc::vals::Calrf;
// Panic if the rcc mod knows we're not using low-power rtc
#[cfg(any(rcc_wb, rcc_f4, rcc_f410))]
unsafe { crate::rcc::get_freqs() }.rtc.unwrap();
let requested_duration = requested_duration.as_ticks().clamp(0, u32::MAX as u64);
let rtc_hz = Self::frequency().0 as u64;
let rtc_ticks = requested_duration * rtc_hz / TICK_HZ;
let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32);
// adjust the rtc ticks to the prescaler and subtract one rtc tick
let rtc_ticks = rtc_ticks / prescaler as u64;
let rtc_ticks = rtc_ticks.clamp(0, (u16::MAX - 1) as u64).saturating_sub(1) as u16;
self.write(false, |regs| {
regs.cr().modify(|w| w.set_wute(false));
#[cfg(any(
rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb
))]
{
regs.isr().modify(|w| w.set_wutf(false));
while !regs.isr().read().wutwf() {}
}
#[cfg(any(rtc_v3, rtc_v3u5))]
{
regs.scr().write(|w| w.set_cwutf(Calrf::CLEAR));
while !regs.icsr().read().wutwf() {}
}
regs.cr().modify(|w| w.set_wucksel(prescaler.into()));
regs.wutr().write(|w| w.set_wut(rtc_ticks));
regs.cr().modify(|w| w.set_wute(true));
regs.cr().modify(|w| w.set_wutie(true));
});
let instant = self.instant().unwrap();
trace!(
"rtc: start wakeup alarm for {} ms (psc: {}, ticks: {}) at {}",
Duration::from_ticks(rtc_ticks as u64 * TICK_HZ * prescaler as u64 / rtc_hz).as_millis(),
prescaler as u32,
rtc_ticks,
instant,
);
assert!(self.stop_time.borrow(cs).replace(Some(instant)).is_none())
}
#[cfg(feature = "low-power")]
/// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm`
/// was called, otherwise none
pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option<embassy_time::Duration> {
use crate::interrupt::typelevel::Interrupt;
#[cfg(any(rtc_v3, rtc_v3u5))]
use crate::pac::rtc::vals::Calrf;
let instant = self.instant().unwrap();
if RTC::regs().cr().read().wute() {
trace!("rtc: stop wakeup alarm at {}", instant);
self.write(false, |regs| {
regs.cr().modify(|w| w.set_wutie(false));
regs.cr().modify(|w| w.set_wute(false));
#[cfg(any(
rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb
))]
regs.isr().modify(|w| w.set_wutf(false));
#[cfg(any(rtc_v3, rtc_v3u5))]
regs.scr().write(|w| w.set_cwutf(Calrf::CLEAR));
crate::pac::EXTI
.pr(0)
.modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));
<RTC as crate::rtc::sealed::Instance>::WakeupInterrupt::unpend();
});
}
self.stop_time.borrow(cs).take().map(|stop_time| instant - stop_time)
}
#[cfg(feature = "low-power")]
pub(crate) fn enable_wakeup_line(&self) {
use crate::interrupt::typelevel::Interrupt;
use crate::pac::EXTI;
<RTC as crate::rtc::sealed::Instance>::WakeupInterrupt::unpend();
unsafe { <RTC as crate::rtc::sealed::Instance>::WakeupInterrupt::enable() };
EXTI.rtsr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));
EXTI.imr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));
}
} }
pub(crate) fn byte_to_bcd2(byte: u8) -> (u8, u8) { pub(crate) fn byte_to_bcd2(byte: u8) -> (u8, u8) {

View File

@ -6,145 +6,7 @@ use crate::peripherals::RTC;
use crate::rtc::sealed::Instance; use crate::rtc::sealed::Instance;
#[allow(dead_code)] #[allow(dead_code)]
#[repr(u8)]
#[derive(Clone, Copy, Debug)]
pub(crate) enum WakeupPrescaler {
Div2 = 2,
Div4 = 4,
Div8 = 8,
Div16 = 16,
}
#[cfg(any(stm32wb, stm32f4, stm32l0))]
impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel {
fn from(val: WakeupPrescaler) -> Self {
use crate::pac::rtc::vals::Wucksel;
match val {
WakeupPrescaler::Div2 => Wucksel::DIV2,
WakeupPrescaler::Div4 => Wucksel::DIV4,
WakeupPrescaler::Div8 => Wucksel::DIV8,
WakeupPrescaler::Div16 => Wucksel::DIV16,
}
}
}
#[cfg(any(stm32wb, stm32f4, stm32l0))]
impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler {
fn from(val: crate::pac::rtc::vals::Wucksel) -> Self {
use crate::pac::rtc::vals::Wucksel;
match val {
Wucksel::DIV2 => WakeupPrescaler::Div2,
Wucksel::DIV4 => WakeupPrescaler::Div4,
Wucksel::DIV8 => WakeupPrescaler::Div8,
Wucksel::DIV16 => WakeupPrescaler::Div16,
_ => unreachable!(),
}
}
}
#[allow(dead_code)]
impl WakeupPrescaler {
pub fn compute_min(val: u32) -> Self {
*[
WakeupPrescaler::Div2,
WakeupPrescaler::Div4,
WakeupPrescaler::Div8,
WakeupPrescaler::Div16,
]
.iter()
.skip_while(|psc| **psc as u32 <= val)
.next()
.unwrap_or(&WakeupPrescaler::Div16)
}
}
impl super::Rtc { impl super::Rtc {
#[cfg(feature = "low-power")]
/// start the wakeup alarm and wtih a duration that is as close to but less than
/// the requested duration, and record the instant the wakeup alarm was started
pub(crate) fn start_wakeup_alarm(
&self,
requested_duration: embassy_time::Duration,
cs: critical_section::CriticalSection,
) {
use embassy_time::{Duration, TICK_HZ};
// Panic if the rcc mod knows we're not using low-power rtc
#[cfg(any(rcc_wb, rcc_f4, rcc_f410))]
unsafe { crate::rcc::get_freqs() }.rtc.unwrap();
let requested_duration = requested_duration.as_ticks().clamp(0, u32::MAX as u64);
let rtc_hz = Self::frequency().0 as u64;
let rtc_ticks = requested_duration * rtc_hz / TICK_HZ;
let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32);
// adjust the rtc ticks to the prescaler and subtract one rtc tick
let rtc_ticks = rtc_ticks / prescaler as u64;
let rtc_ticks = rtc_ticks.clamp(0, (u16::MAX - 1) as u64).saturating_sub(1) as u16;
self.write(false, |regs| {
regs.cr().modify(|w| w.set_wute(false));
regs.isr().modify(|w| w.set_wutf(false));
while !regs.isr().read().wutwf() {}
regs.cr().modify(|w| w.set_wucksel(prescaler.into()));
regs.wutr().write(|w| w.set_wut(rtc_ticks));
regs.cr().modify(|w| w.set_wute(true));
regs.cr().modify(|w| w.set_wutie(true));
});
let instant = self.instant().unwrap();
trace!(
"rtc: start wakeup alarm for {} ms (psc: {}, ticks: {}) at {}",
Duration::from_ticks(rtc_ticks as u64 * TICK_HZ * prescaler as u64 / rtc_hz).as_millis(),
prescaler as u32,
rtc_ticks,
instant,
);
assert!(self.stop_time.borrow(cs).replace(Some(instant)).is_none())
}
#[cfg(feature = "low-power")]
/// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm`
/// was called, otherwise none
pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option<embassy_time::Duration> {
use crate::interrupt::typelevel::Interrupt;
let instant = self.instant().unwrap();
if RTC::regs().cr().read().wute() {
trace!("rtc: stop wakeup alarm at {}", instant);
self.write(false, |regs| {
regs.cr().modify(|w| w.set_wutie(false));
regs.cr().modify(|w| w.set_wute(false));
regs.isr().modify(|w| w.set_wutf(false));
crate::pac::EXTI
.pr(0)
.modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));
<RTC as crate::rtc::sealed::Instance>::WakeupInterrupt::unpend();
});
}
self.stop_time.borrow(cs).take().map(|stop_time| instant - stop_time)
}
#[cfg(feature = "low-power")]
pub(crate) fn enable_wakeup_line(&self) {
use crate::interrupt::typelevel::Interrupt;
use crate::pac::EXTI;
<RTC as crate::rtc::sealed::Instance>::WakeupInterrupt::unpend();
unsafe { <RTC as crate::rtc::sealed::Instance>::WakeupInterrupt::enable() };
EXTI.rtsr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));
EXTI.imr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));
}
/// Applies the RTC config /// Applies the RTC config
/// It this changes the RTC clock source the time will be reset /// It this changes the RTC clock source the time will be reset
pub(super) fn configure(&mut self, async_psc: u8, sync_psc: u16) { pub(super) fn configure(&mut self, async_psc: u8, sync_psc: u16) {

View File

@ -95,7 +95,7 @@ impl super::Rtc {
}) })
} }
pub(super) fn write<F, R>(&mut self, init_mode: bool, f: F) -> R pub(super) fn write<F, R>(&self, init_mode: bool, f: F) -> R
where where
F: FnOnce(&crate::pac::rtc::Rtc) -> R, F: FnOnce(&crate::pac::rtc::Rtc) -> R,
{ {
@ -129,6 +129,12 @@ impl super::Rtc {
impl sealed::Instance for crate::peripherals::RTC { impl sealed::Instance for crate::peripherals::RTC {
const BACKUP_REGISTER_COUNT: usize = 32; const BACKUP_REGISTER_COUNT: usize = 32;
#[cfg(all(feature = "low-power", stm32g4))]
const EXTI_WAKEUP_LINE: usize = 20;
#[cfg(all(feature = "low-power", stm32g4))]
type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP;
fn read_backup_register(_rtc: &Rtc, register: usize) -> Option<u32> { fn read_backup_register(_rtc: &Rtc, register: usize) -> Option<u32> {
#[allow(clippy::if_same_then_else)] #[allow(clippy::if_same_then_else)]
if register < Self::BACKUP_REGISTER_COUNT { if register < Self::BACKUP_REGISTER_COUNT {

View File

@ -40,6 +40,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
// Handle RX // Handle RX
while r.gintsts().read().rxflvl() { while r.gintsts().read().rxflvl() {
let status = r.grxstsp().read(); let status = r.grxstsp().read();
trace!("=== status {:08x}", status.0);
let ep_num = status.epnum() as usize; let ep_num = status.epnum() as usize;
let len = status.bcnt() as usize; let len = status.bcnt() as usize;
@ -51,6 +52,15 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
assert!(len == 8, "invalid SETUP packet length={}", len); assert!(len == 8, "invalid SETUP packet length={}", len);
assert!(ep_num == 0, "invalid SETUP packet endpoint={}", ep_num); assert!(ep_num == 0, "invalid SETUP packet endpoint={}", ep_num);
// flushing TX if something stuck in control endpoint
if r.dieptsiz(ep_num).read().pktcnt() != 0 {
r.grstctl().modify(|w| {
w.set_txfnum(ep_num as _);
w.set_txfflsh(true);
});
while r.grstctl().read().txfflsh() {}
}
if state.ep0_setup_ready.load(Ordering::Relaxed) == false { if state.ep0_setup_ready.load(Ordering::Relaxed) == false {
// SAFETY: exclusive access ensured by atomic bool // SAFETY: exclusive access ensured by atomic bool
let data = unsafe { &mut *state.ep0_setup_data.get() }; let data = unsafe { &mut *state.ep0_setup_data.get() };
@ -96,6 +106,11 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
} }
vals::Pktstsd::SETUP_DATA_DONE => { vals::Pktstsd::SETUP_DATA_DONE => {
trace!("SETUP_DATA_DONE ep={}", ep_num); trace!("SETUP_DATA_DONE ep={}", ep_num);
if quirk_setup_late_cnak(r) {
// Clear NAK to indicate we are ready to receive more data
r.doepctl(ep_num).modify(|w| w.set_cnak(true));
}
} }
x => trace!("unknown PKTSTS: {}", x.to_bits()), x => trace!("unknown PKTSTS: {}", x.to_bits()),
} }
@ -911,11 +926,9 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
trace!("enumdne"); trace!("enumdne");
let speed = r.dsts().read().enumspd(); let speed = r.dsts().read().enumspd();
trace!(" speed={}", speed.to_bits()); let trdt = calculate_trdt(speed, T::frequency());
trace!(" speed={} trdt={}", speed.to_bits(), trdt);
r.gusbcfg().modify(|w| { r.gusbcfg().modify(|w| w.set_trdt(trdt));
w.set_trdt(calculate_trdt(speed, T::frequency()));
});
r.gintsts().write(|w| w.set_enumdne(true)); // clear r.gintsts().write(|w| w.set_enumdne(true)); // clear
Self::restore_irqs(); Self::restore_irqs();
@ -1304,20 +1317,22 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> {
state.ep_out_wakers[0].register(cx.waker()); state.ep_out_wakers[0].register(cx.waker());
let r = T::regs();
if state.ep0_setup_ready.load(Ordering::Relaxed) { if state.ep0_setup_ready.load(Ordering::Relaxed) {
let data = unsafe { *state.ep0_setup_data.get() }; let data = unsafe { *state.ep0_setup_data.get() };
state.ep0_setup_ready.store(false, Ordering::Release); state.ep0_setup_ready.store(false, Ordering::Release);
// EP0 should not be controlled by `Bus` so this RMW does not need a critical section // EP0 should not be controlled by `Bus` so this RMW does not need a critical section
// Receive 1 SETUP packet // Receive 1 SETUP packet
T::regs().doeptsiz(self.ep_out.info.addr.index()).modify(|w| { r.doeptsiz(self.ep_out.info.addr.index()).modify(|w| {
w.set_rxdpid_stupcnt(1); w.set_rxdpid_stupcnt(1);
}); });
// Clear NAK to indicate we are ready to receive more data // Clear NAK to indicate we are ready to receive more data
T::regs().doepctl(self.ep_out.info.addr.index()).modify(|w| { if !quirk_setup_late_cnak(r) {
w.set_cnak(true); r.doepctl(self.ep_out.info.addr.index()).modify(|w| w.set_cnak(true));
}); }
trace!("SETUP received: {:?}", data); trace!("SETUP received: {:?}", data);
Poll::Ready(data) Poll::Ready(data)
@ -1453,3 +1468,7 @@ fn calculate_trdt(speed: vals::Dspd, ahb_freq: Hertz) -> u8 {
_ => unimplemented!(), _ => unimplemented!(),
} }
} }
fn quirk_setup_late_cnak(r: crate::pac::otg::Otg) -> bool {
r.cid().read().0 & 0xf000 == 0x1000
}

View File

@ -19,6 +19,7 @@ pub struct LoggerState<'d> {
device_descriptor: [u8; 32], device_descriptor: [u8; 32],
config_descriptor: [u8; 128], config_descriptor: [u8; 128],
bos_descriptor: [u8; 16], bos_descriptor: [u8; 16],
msos_descriptor: [u8; 256],
control_buf: [u8; 64], control_buf: [u8; 64],
} }
@ -30,6 +31,7 @@ impl<'d> LoggerState<'d> {
device_descriptor: [0; 32], device_descriptor: [0; 32],
config_descriptor: [0; 128], config_descriptor: [0; 128],
bos_descriptor: [0; 16], bos_descriptor: [0; 16],
msos_descriptor: [0; 256],
control_buf: [0; 64], control_buf: [0; 64],
} }
} }
@ -73,6 +75,7 @@ impl<const N: usize> UsbLogger<N> {
&mut state.device_descriptor, &mut state.device_descriptor,
&mut state.config_descriptor, &mut state.config_descriptor,
&mut state.bos_descriptor, &mut state.bos_descriptor,
&mut state.msos_descriptor,
&mut state.control_buf, &mut state.control_buf,
); );

View File

@ -13,7 +13,6 @@ target = "thumbv7em-none-eabi"
[features] [features]
defmt = ["dep:defmt", "embassy-usb-driver/defmt"] defmt = ["dep:defmt", "embassy-usb-driver/defmt"]
usbd-hid = ["dep:usbd-hid", "dep:ssmarshal"] usbd-hid = ["dep:usbd-hid", "dep:ssmarshal"]
msos-descriptor = []
default = ["usbd-hid"] default = ["usbd-hid"]
# BEGIN AUTOGENERATED CONFIG FEATURES # BEGIN AUTOGENERATED CONFIG FEATURES

View File

@ -3,7 +3,6 @@ use heapless::Vec;
use crate::config::MAX_HANDLER_COUNT; use crate::config::MAX_HANDLER_COUNT;
use crate::descriptor::{BosWriter, DescriptorWriter}; use crate::descriptor::{BosWriter, DescriptorWriter};
use crate::driver::{Driver, Endpoint, EndpointType}; use crate::driver::{Driver, Endpoint, EndpointType};
#[cfg(feature = "msos-descriptor")]
use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter}; use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter};
use crate::types::{InterfaceNumber, StringIndex}; use crate::types::{InterfaceNumber, StringIndex};
use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START};
@ -133,7 +132,6 @@ pub struct Builder<'d, D: Driver<'d>> {
config_descriptor: DescriptorWriter<'d>, config_descriptor: DescriptorWriter<'d>,
bos_descriptor: BosWriter<'d>, bos_descriptor: BosWriter<'d>,
#[cfg(feature = "msos-descriptor")]
msos_descriptor: MsOsDescriptorWriter<'d>, msos_descriptor: MsOsDescriptorWriter<'d>,
} }
@ -149,7 +147,7 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
device_descriptor_buf: &'d mut [u8], device_descriptor_buf: &'d mut [u8],
config_descriptor_buf: &'d mut [u8], config_descriptor_buf: &'d mut [u8],
bos_descriptor_buf: &'d mut [u8], bos_descriptor_buf: &'d mut [u8],
#[cfg(feature = "msos-descriptor")] msos_descriptor_buf: &'d mut [u8], msos_descriptor_buf: &'d mut [u8],
control_buf: &'d mut [u8], control_buf: &'d mut [u8],
) -> Self { ) -> Self {
// Magic values specified in USB-IF ECN on IADs. // Magic values specified in USB-IF ECN on IADs.
@ -189,14 +187,12 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
config_descriptor, config_descriptor,
bos_descriptor, bos_descriptor,
#[cfg(feature = "msos-descriptor")]
msos_descriptor: MsOsDescriptorWriter::new(msos_descriptor_buf), msos_descriptor: MsOsDescriptorWriter::new(msos_descriptor_buf),
} }
} }
/// Creates the [`UsbDevice`] instance with the configuration in this builder. /// Creates the [`UsbDevice`] instance with the configuration in this builder.
pub fn build(mut self) -> UsbDevice<'d, D> { pub fn build(mut self) -> UsbDevice<'d, D> {
#[cfg(feature = "msos-descriptor")]
let msos_descriptor = self.msos_descriptor.build(&mut self.bos_descriptor); let msos_descriptor = self.msos_descriptor.build(&mut self.bos_descriptor);
self.config_descriptor.end_configuration(); self.config_descriptor.end_configuration();
@ -206,7 +202,6 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
info!("USB: device_descriptor used: {}", self.device_descriptor.position()); info!("USB: device_descriptor used: {}", self.device_descriptor.position());
info!("USB: config_descriptor used: {}", self.config_descriptor.position()); info!("USB: config_descriptor used: {}", self.config_descriptor.position());
info!("USB: bos_descriptor used: {}", self.bos_descriptor.writer.position()); info!("USB: bos_descriptor used: {}", self.bos_descriptor.writer.position());
#[cfg(feature = "msos-descriptor")]
info!("USB: msos_descriptor used: {}", msos_descriptor.len()); info!("USB: msos_descriptor used: {}", msos_descriptor.len());
info!("USB: control_buf size: {}", self.control_buf.len()); info!("USB: control_buf size: {}", self.control_buf.len());
@ -217,10 +212,9 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
self.device_descriptor.into_buf(), self.device_descriptor.into_buf(),
self.config_descriptor.into_buf(), self.config_descriptor.into_buf(),
self.bos_descriptor.writer.into_buf(), self.bos_descriptor.writer.into_buf(),
msos_descriptor,
self.interfaces, self.interfaces,
self.control_buf, self.control_buf,
#[cfg(feature = "msos-descriptor")]
msos_descriptor,
) )
} }
@ -251,7 +245,6 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
builder: self, builder: self,
iface_count_index, iface_count_index,
#[cfg(feature = "msos-descriptor")]
first_interface, first_interface,
} }
} }
@ -275,7 +268,6 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
StringIndex::new(index) StringIndex::new(index)
} }
#[cfg(feature = "msos-descriptor")]
/// Add an MS OS 2.0 Descriptor Set. /// Add an MS OS 2.0 Descriptor Set.
/// ///
/// Panics if called more than once. /// Panics if called more than once.
@ -283,13 +275,11 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
self.msos_descriptor.header(windows_version, vendor_code); self.msos_descriptor.header(windows_version, vendor_code);
} }
#[cfg(feature = "msos-descriptor")]
/// Add an MS OS 2.0 Device Level Feature Descriptor. /// Add an MS OS 2.0 Device Level Feature Descriptor.
pub fn msos_feature<T: DeviceLevelDescriptor>(&mut self, desc: T) { pub fn msos_feature<T: DeviceLevelDescriptor>(&mut self, desc: T) {
self.msos_descriptor.device_feature(desc); self.msos_descriptor.device_feature(desc);
} }
#[cfg(feature = "msos-descriptor")]
/// Gets the underlying [`MsOsDescriptorWriter`] to allow adding subsets and features for classes that /// Gets the underlying [`MsOsDescriptorWriter`] to allow adding subsets and features for classes that
/// do not add their own. /// do not add their own.
pub fn msos_writer(&mut self) -> &mut MsOsDescriptorWriter<'d> { pub fn msos_writer(&mut self) -> &mut MsOsDescriptorWriter<'d> {
@ -306,13 +296,11 @@ pub struct FunctionBuilder<'a, 'd, D: Driver<'d>> {
builder: &'a mut Builder<'d, D>, builder: &'a mut Builder<'d, D>,
iface_count_index: Option<usize>, iface_count_index: Option<usize>,
#[cfg(feature = "msos-descriptor")]
first_interface: InterfaceNumber, first_interface: InterfaceNumber,
} }
impl<'a, 'd, D: Driver<'d>> Drop for FunctionBuilder<'a, 'd, D> { impl<'a, 'd, D: Driver<'d>> Drop for FunctionBuilder<'a, 'd, D> {
fn drop(&mut self) { fn drop(&mut self) {
#[cfg(feature = "msos-descriptor")]
self.builder.msos_descriptor.end_function(); self.builder.msos_descriptor.end_function();
} }
} }
@ -344,7 +332,6 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> {
} }
} }
#[cfg(feature = "msos-descriptor")]
/// Add an MS OS 2.0 Function Level Feature Descriptor. /// Add an MS OS 2.0 Function Level Feature Descriptor.
pub fn msos_feature<T: FunctionLevelDescriptor>(&mut self, desc: T) { pub fn msos_feature<T: FunctionLevelDescriptor>(&mut self, desc: T) {
if !self.builder.msos_descriptor.is_in_config_subset() { if !self.builder.msos_descriptor.is_in_config_subset() {
@ -355,7 +342,6 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> {
self.builder.msos_descriptor.function(self.first_interface); self.builder.msos_descriptor.function(self.first_interface);
} }
#[cfg(feature = "msos-descriptor")]
self.builder.msos_descriptor.function_feature(desc); self.builder.msos_descriptor.function_feature(desc);
} }
} }

View File

@ -175,10 +175,7 @@ pub struct UsbBufferReport {
/// Number of bos descriptor bytes used /// Number of bos descriptor bytes used
pub bos_descriptor_used: usize, pub bos_descriptor_used: usize,
/// Number of msos descriptor bytes used /// Number of msos descriptor bytes used
/// pub msos_descriptor_used: usize,
/// Will be `None` if the "msos-descriptor" feature is not active.
/// Otherwise will return Some(bytes).
pub msos_descriptor_used: Option<usize>,
/// Size of the control buffer /// Size of the control buffer
pub control_buffer_size: usize, pub control_buffer_size: usize,
} }
@ -197,6 +194,7 @@ struct Inner<'d, D: Driver<'d>> {
device_descriptor: &'d [u8], device_descriptor: &'d [u8],
config_descriptor: &'d [u8], config_descriptor: &'d [u8],
bos_descriptor: &'d [u8], bos_descriptor: &'d [u8],
msos_descriptor: crate::msos::MsOsDescriptorSet<'d>,
device_state: UsbDeviceState, device_state: UsbDeviceState,
suspended: bool, suspended: bool,
@ -212,9 +210,6 @@ struct Inner<'d, D: Driver<'d>> {
interfaces: Vec<Interface, MAX_INTERFACE_COUNT>, interfaces: Vec<Interface, MAX_INTERFACE_COUNT>,
handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>, handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>,
#[cfg(feature = "msos-descriptor")]
msos_descriptor: crate::msos::MsOsDescriptorSet<'d>,
} }
impl<'d, D: Driver<'d>> UsbDevice<'d, D> { impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
@ -225,9 +220,9 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
device_descriptor: &'d [u8], device_descriptor: &'d [u8],
config_descriptor: &'d [u8], config_descriptor: &'d [u8],
bos_descriptor: &'d [u8], bos_descriptor: &'d [u8],
msos_descriptor: crate::msos::MsOsDescriptorSet<'d>,
interfaces: Vec<Interface, MAX_INTERFACE_COUNT>, interfaces: Vec<Interface, MAX_INTERFACE_COUNT>,
control_buf: &'d mut [u8], control_buf: &'d mut [u8],
#[cfg(feature = "msos-descriptor")] msos_descriptor: crate::msos::MsOsDescriptorSet<'d>,
) -> UsbDevice<'d, D> { ) -> UsbDevice<'d, D> {
// Start the USB bus. // Start the USB bus.
// This prevent further allocation by consuming the driver. // This prevent further allocation by consuming the driver.
@ -242,6 +237,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
device_descriptor, device_descriptor,
config_descriptor, config_descriptor,
bos_descriptor, bos_descriptor,
msos_descriptor,
device_state: UsbDeviceState::Unpowered, device_state: UsbDeviceState::Unpowered,
suspended: false, suspended: false,
@ -251,8 +247,6 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
set_address_pending: false, set_address_pending: false,
interfaces, interfaces,
handlers, handlers,
#[cfg(feature = "msos-descriptor")]
msos_descriptor,
}, },
} }
} }
@ -261,16 +255,11 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
/// ///
/// Useful for tuning buffer sizes for actual usage /// Useful for tuning buffer sizes for actual usage
pub fn buffer_usage(&self) -> UsbBufferReport { pub fn buffer_usage(&self) -> UsbBufferReport {
#[cfg(not(feature = "msos-descriptor"))]
let mdu = None;
#[cfg(feature = "msos-descriptor")]
let mdu = Some(self.inner.msos_descriptor.len());
UsbBufferReport { UsbBufferReport {
device_descriptor_used: self.inner.device_descriptor.len(), device_descriptor_used: self.inner.device_descriptor.len(),
config_descriptor_used: self.inner.config_descriptor.len(), config_descriptor_used: self.inner.config_descriptor.len(),
bos_descriptor_used: self.inner.bos_descriptor.len(), bos_descriptor_used: self.inner.bos_descriptor.len(),
msos_descriptor_used: mdu, msos_descriptor_used: self.inner.msos_descriptor.len(),
control_buffer_size: self.control_buf.len(), control_buffer_size: self.control_buf.len(),
} }
} }
@ -406,6 +395,16 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
let max_packet_size = self.control.max_packet_size(); let max_packet_size = self.control.max_packet_size();
let mut total = 0; let mut total = 0;
if req_length > self.control_buf.len() {
warn!(
"got CONTROL OUT with length {} higher than the control_buf len {}, rejecting.",
req_length,
self.control_buf.len()
);
self.control.reject().await;
return;
}
let chunks = self.control_buf[..req_length].chunks_mut(max_packet_size); let chunks = self.control_buf[..req_length].chunks_mut(max_packet_size);
for (first, last, chunk) in first_last(chunks) { for (first, last, chunk) in first_last(chunks) {
let size = match self.control.data_out(chunk, first, last).await { let size = match self.control.data_out(chunk, first, last).await {
@ -674,7 +673,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
} }
_ => InResponse::Rejected, _ => InResponse::Rejected,
}, },
#[cfg(feature = "msos-descriptor")]
(RequestType::Vendor, Recipient::Device) => { (RequestType::Vendor, Recipient::Device) => {
if !self.msos_descriptor.is_empty() if !self.msos_descriptor.is_empty()
&& req.request == self.msos_descriptor.vendor_code() && req.request == self.msos_descriptor.vendor_code()

View File

@ -1,5 +1,3 @@
#![cfg(feature = "msos-descriptor")]
//! Microsoft OS Descriptors //! Microsoft OS Descriptors
//! //!
//! <https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-os-2-0-descriptors-specification> //! <https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-os-2-0-descriptors-specification>

View File

@ -34,7 +34,7 @@ embassy-executor = { version = "0.3.1", path = "../../embassy-executor", feature
embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true }
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor",], optional = true } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true }
embedded-io = { version = "0.6.0", features = ["defmt-03"] } embedded-io = { version = "0.6.0", features = ["defmt-03"] }
embedded-io-async = { version = "0.6.0", optional = true, features = ["defmt-03"] } embedded-io-async = { version = "0.6.0", optional = true, features = ["defmt-03"] }
embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"], optional = true } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"], optional = true }

View File

@ -75,6 +75,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );

View File

@ -71,6 +71,7 @@ async fn main(spawner: Spawner) {
&mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..],
&mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..],
&mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..],
&mut [], // no msos descriptors
&mut make_static!([0; 128])[..], &mut make_static!([0; 128])[..],
); );

View File

@ -41,7 +41,7 @@ async fn main(_spawner: Spawner) {
let mut config_descriptor = [0; 256]; let mut config_descriptor = [0; 256];
let mut bos_descriptor = [0; 256]; let mut bos_descriptor = [0; 256];
// You can also add a Microsoft OS descriptor. // You can also add a Microsoft OS descriptor.
// let mut msos_descriptor = [0; 256]; let mut msos_descriptor = [0; 256];
let mut control_buf = [0; 64]; let mut control_buf = [0; 64];
let request_handler = MyRequestHandler {}; let request_handler = MyRequestHandler {};
let mut device_handler = MyDeviceHandler::new(); let mut device_handler = MyDeviceHandler::new();
@ -54,7 +54,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
// &mut msos_descriptor, &mut msos_descriptor,
&mut control_buf, &mut control_buf,
); );

View File

@ -58,6 +58,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );

View File

@ -0,0 +1,199 @@
//! Example of using USB without a pre-defined class, but instead responding to
//! raw USB control requests.
//!
//! The host computer can either:
//! * send a command, with a 16-bit request ID, a 16-bit value, and an optional data buffer
//! * request some data, with a 16-bit request ID, a 16-bit value, and a length of data to receive
//!
//! For higher throughput data, you can add some bulk endpoints after creating the alternate,
//! but for low rate command/response, plain control transfers can be very simple and effective.
//!
//! Example code to send/receive data using `nusb`:
//!
//! ```ignore
//! use futures_lite::future::block_on;
//! use nusb::transfer::{ControlIn, ControlOut, ControlType, Recipient};
//!
//! fn main() {
//! let di = nusb::list_devices()
//! .unwrap()
//! .find(|d| d.vendor_id() == 0xc0de && d.product_id() == 0xcafe)
//! .expect("no device found");
//! let device = di.open().expect("error opening device");
//! let interface = device.claim_interface(0).expect("error claiming interface");
//!
//! // Send "hello world" to device
//! let result = block_on(interface.control_out(ControlOut {
//! control_type: ControlType::Vendor,
//! recipient: Recipient::Interface,
//! request: 100,
//! value: 200,
//! index: 0,
//! data: b"hello world",
//! }));
//! println!("{result:?}");
//!
//! // Receive "hello" from device
//! let result = block_on(interface.control_in(ControlIn {
//! control_type: ControlType::Vendor,
//! recipient: Recipient::Interface,
//! request: 101,
//! value: 201,
//! index: 0,
//! length: 5,
//! }));
//! println!("{result:?}");
//! }
//! ```
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
use defmt::info;
use embassy_executor::Spawner;
use embassy_rp::bind_interrupts;
use embassy_rp::peripherals::USB;
use embassy_rp::usb::{Driver, InterruptHandler};
use embassy_usb::control::{InResponse, OutResponse, Recipient, Request, RequestType};
use embassy_usb::msos::{self, windows_version};
use embassy_usb::types::InterfaceNumber;
use embassy_usb::{Builder, Config, Handler};
use {defmt_rtt as _, panic_probe as _};
// This is a randomly generated GUID to allow clients on Windows to find our device
const DEVICE_INTERFACE_GUIDS: &[&str] = &["{AFB9A6FB-30BA-44BC-9232-806CFC875321}"];
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 raw 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 msos_descriptor = [0; 256];
let mut control_buf = [0; 64];
let mut handler = ControlHandler {
if_num: InterfaceNumber(0),
};
let mut builder = Builder::new(
driver,
config,
&mut device_descriptor,
&mut config_descriptor,
&mut bos_descriptor,
&mut msos_descriptor,
&mut control_buf,
);
// Add the Microsoft OS Descriptor (MSOS/MOD) descriptor.
// We tell Windows that this entire device is compatible with the "WINUSB" feature,
// which causes it to use the built-in WinUSB driver automatically, which in turn
// can be used by libusb/rusb software without needing a custom driver or INF file.
// In principle you might want to call msos_feature() just on a specific function,
// if your device also has other functions that still use standard class drivers.
builder.msos_descriptor(windows_version::WIN8_1, 0);
builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(
"DeviceInterfaceGUIDs",
msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),
));
// Add a vendor-specific function (class 0xFF), and corresponding interface,
// that uses our custom handler.
let mut function = builder.function(0xFF, 0, 0);
let mut interface = function.interface();
let _alt = interface.alt_setting(0xFF, 0, 0, None);
handler.if_num = interface.interface_number();
drop(function);
builder.handler(&mut handler);
// Build the builder.
let mut usb = builder.build();
// Run the USB device.
usb.run().await;
}
/// Handle CONTROL endpoint requests and responses. For many simple requests and responses
/// you can get away with only using the control endpoint.
struct ControlHandler {
if_num: InterfaceNumber,
}
impl Handler for ControlHandler {
/// Respond to HostToDevice control messages, where the host sends us a command and
/// optionally some data, and we can only acknowledge or reject it.
fn control_out<'a>(&'a mut self, req: Request, buf: &'a [u8]) -> Option<OutResponse> {
// Log the request before filtering to help with debugging.
info!("Got control_out, request={}, buf={:a}", req, buf);
// Only handle Vendor request types to an Interface.
if req.request_type != RequestType::Vendor || req.recipient != Recipient::Interface {
return None;
}
// Ignore requests to other interfaces.
if req.index != self.if_num.0 as u16 {
return None;
}
// Accept request 100, value 200, reject others.
if req.request == 100 && req.value == 200 {
Some(OutResponse::Accepted)
} else {
Some(OutResponse::Rejected)
}
}
/// Respond to DeviceToHost control messages, where the host requests some data from us.
fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> {
info!("Got control_in, request={}", req);
// Only handle Vendor request types to an Interface.
if req.request_type != RequestType::Vendor || req.recipient != Recipient::Interface {
return None;
}
// Ignore requests to other interfaces.
if req.index != self.if_num.0 as u16 {
return None;
}
// Respond "hello" to request 101, value 201, when asked for 5 bytes, otherwise reject.
if req.request == 101 && req.value == 201 && req.length == 5 {
buf[..5].copy_from_slice(b"hello");
Some(InResponse::Accepted(&buf[..5]))
} else {
Some(InResponse::Rejected)
}
}
}

View File

@ -0,0 +1,142 @@
//! Example of using USB without a pre-defined class, but instead using raw USB bulk transfers.
//!
//! Example code to send/receive data using `nusb`:
//!
//! ```ignore
//! use futures_lite::future::block_on;
//! use nusb::transfer::RequestBuffer;
//!
//! const BULK_OUT_EP: u8 = 0x01;
//! const BULK_IN_EP: u8 = 0x81;
//!
//! fn main() {
//! let di = nusb::list_devices()
//! .unwrap()
//! .find(|d| d.vendor_id() == 0xc0de && d.product_id() == 0xcafe)
//! .expect("no device found");
//! let device = di.open().expect("error opening device");
//! let interface = device.claim_interface(0).expect("error claiming interface");
//!
//! let result = block_on(interface.bulk_out(BULK_OUT_EP, b"hello world".into()));
//! println!("{result:?}");
//! let result = block_on(interface.bulk_in(BULK_IN_EP, RequestBuffer::new(64)));
//! println!("{result:?}");
//! }
//! ```
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
use defmt::info;
use embassy_executor::Spawner;
use embassy_futures::join::join;
use embassy_rp::bind_interrupts;
use embassy_rp::peripherals::USB;
use embassy_rp::usb::{Driver, InterruptHandler};
use embassy_usb::driver::{Endpoint, EndpointIn, EndpointOut};
use embassy_usb::msos::{self, windows_version};
use embassy_usb::{Builder, Config};
use {defmt_rtt as _, panic_probe as _};
// This is a randomly generated GUID to allow clients on Windows to find our device
const DEVICE_INTERFACE_GUIDS: &[&str] = &["{AFB9A6FB-30BA-44BC-9232-806CFC875321}"];
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 raw 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 msos_descriptor = [0; 256];
let mut control_buf = [0; 64];
let mut builder = Builder::new(
driver,
config,
&mut device_descriptor,
&mut config_descriptor,
&mut bos_descriptor,
&mut msos_descriptor,
&mut control_buf,
);
// Add the Microsoft OS Descriptor (MSOS/MOD) descriptor.
// We tell Windows that this entire device is compatible with the "WINUSB" feature,
// which causes it to use the built-in WinUSB driver automatically, which in turn
// can be used by libusb/rusb software without needing a custom driver or INF file.
// In principle you might want to call msos_feature() just on a specific function,
// if your device also has other functions that still use standard class drivers.
builder.msos_descriptor(windows_version::WIN8_1, 0);
builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(
"DeviceInterfaceGUIDs",
msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),
));
// Add a vendor-specific function (class 0xFF), and corresponding interface,
// that uses our custom handler.
let mut function = builder.function(0xFF, 0, 0);
let mut interface = function.interface();
let mut alt = interface.alt_setting(0xFF, 0, 0, None);
let mut read_ep = alt.endpoint_bulk_out(64);
let mut write_ep = alt.endpoint_bulk_in(64);
drop(function);
// 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 {
read_ep.wait_enabled().await;
info!("Connected");
loop {
let mut data = [0; 64];
match read_ep.read(&mut data).await {
Ok(n) => {
info!("Got bulk: {:a}", data[..n]);
// Echo back to the host:
write_ep.write(&data[..n]).await.ok();
}
Err(_) => break,
}
}
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;
}

View File

@ -60,6 +60,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );

View File

@ -60,6 +60,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );

View File

@ -57,6 +57,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );

View File

@ -39,7 +39,7 @@ async fn main(_spawner: Spawner) -> ! {
let mut vrefint = adc.enable_vref(&mut Delay); let mut vrefint = adc.enable_vref(&mut Delay);
let mut temperature = adc.enable_temperature(); let mut temperature = adc.enable_temperature();
let mut buffer = opamp.buffer_for(&mut p.PA7, OpAmpGain::Mul1); let mut buffer = opamp.buffer_ext(&p.PA7, &mut p.PA6, OpAmpGain::Mul1);
loop { loop {
let vref = adc.read(&mut vrefint).await; let vref = adc.read(&mut vrefint).await;

View File

@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["
embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["defmt"] } embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.3.1", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-executor = { version = "0.3.1", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] }
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt" ] }
embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] }
defmt = "0.3" defmt = "0.3"

View File

@ -94,6 +94,7 @@ async fn main(spawner: Spawner) {
&mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..],
&mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..],
&mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..],
&mut [], // no msos descriptors
&mut make_static!([0; 128])[..], &mut make_static!([0; 128])[..],
); );

View File

@ -0,0 +1,222 @@
//! Example of using USB without a pre-defined class, but instead responding to
//! raw USB control requests.
//!
//! The host computer can either:
//! * send a command, with a 16-bit request ID, a 16-bit value, and an optional data buffer
//! * request some data, with a 16-bit request ID, a 16-bit value, and a length of data to receive
//!
//! For higher throughput data, you can add some bulk endpoints after creating the alternate,
//! but for low rate command/response, plain control transfers can be very simple and effective.
//!
//! Example code to send/receive data using `nusb`:
//!
//! ```ignore
//! use futures_lite::future::block_on;
//! use nusb::transfer::{ControlIn, ControlOut, ControlType, Recipient};
//!
//! fn main() {
//! let di = nusb::list_devices()
//! .unwrap()
//! .find(|d| d.vendor_id() == 0xc0de && d.product_id() == 0xcafe)
//! .expect("no device found");
//! let device = di.open().expect("error opening device");
//! let interface = device.claim_interface(0).expect("error claiming interface");
//!
//! // Send "hello world" to device
//! let result = block_on(interface.control_out(ControlOut {
//! control_type: ControlType::Vendor,
//! recipient: Recipient::Interface,
//! request: 100,
//! value: 200,
//! index: 0,
//! data: b"hello world",
//! }));
//! println!("{result:?}");
//!
//! // Receive "hello" from device
//! let result = block_on(interface.control_in(ControlIn {
//! control_type: ControlType::Vendor,
//! recipient: Recipient::Interface,
//! request: 101,
//! value: 201,
//! index: 0,
//! length: 5,
//! }));
//! println!("{result:?}");
//! }
//! ```
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::time::Hertz;
use embassy_stm32::usb_otg::Driver;
use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config};
use embassy_usb::control::{InResponse, OutResponse, Recipient, Request, RequestType};
use embassy_usb::msos::{self, windows_version};
use embassy_usb::types::InterfaceNumber;
use embassy_usb::{Builder, Handler};
use {defmt_rtt as _, panic_probe as _};
// Randomly generated UUID because Windows requires you provide one to use WinUSB.
// In principle WinUSB-using software could find this device (or a specific interface
// on it) by its GUID instead of using the VID/PID, but in practice that seems unhelpful.
const DEVICE_INTERFACE_GUIDS: &[&str] = &["{DAC2087C-63FA-458D-A55D-827C0762DEC7}"];
bind_interrupts!(struct Irqs {
OTG_FS => usb_otg::InterruptHandler<peripherals::USB_OTG_FS>;
});
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
info!("Hello World!");
let mut config = Config::default();
{
use embassy_stm32::rcc::*;
config.rcc.hse = Some(Hse {
freq: Hertz(8_000_000),
mode: HseMode::Bypass,
});
config.rcc.pll_src = PllSource::HSE;
config.rcc.pll = Some(Pll {
prediv: PllPreDiv::DIV4,
mul: PllMul::MUL168,
divp: Some(Pllp::DIV2), // 8mhz / 4 * 168 / 2 = 168Mhz.
divq: Some(Pllq::DIV7), // 8mhz / 4 * 168 / 7 = 48Mhz.
divr: None,
});
config.rcc.ahb_pre = AHBPrescaler::DIV1;
config.rcc.apb1_pre = APBPrescaler::DIV4;
config.rcc.apb2_pre = APBPrescaler::DIV2;
config.rcc.sys = Sysclk::PLL1_P;
}
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_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);
// Create embassy-usb Config
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
config.manufacturer = Some("Embassy");
config.product = Some("USB-raw 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 device_descriptor = [0; 256];
let mut config_descriptor = [0; 256];
let mut bos_descriptor = [0; 256];
let mut msos_descriptor = [0; 256];
let mut control_buf = [0; 64];
let mut handler = ControlHandler {
if_num: InterfaceNumber(0),
};
let mut builder = Builder::new(
driver,
config,
&mut device_descriptor,
&mut config_descriptor,
&mut bos_descriptor,
&mut msos_descriptor,
&mut control_buf,
);
// Add the Microsoft OS Descriptor (MSOS/MOD) descriptor.
// We tell Windows that this entire device is compatible with the "WINUSB" feature,
// which causes it to use the built-in WinUSB driver automatically, which in turn
// can be used by libusb/rusb software without needing a custom driver or INF file.
// In principle you might want to call msos_feature() just on a specific function,
// if your device also has other functions that still use standard class drivers.
builder.msos_descriptor(windows_version::WIN8_1, 0);
builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(
"DeviceInterfaceGUIDs",
msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),
));
// Add a vendor-specific function (class 0xFF), and corresponding interface,
// that uses our custom handler.
let mut function = builder.function(0xFF, 0, 0);
let mut interface = function.interface();
let _alternate = interface.alt_setting(0xFF, 0, 0, None);
handler.if_num = interface.interface_number();
drop(function);
builder.handler(&mut handler);
// Build the builder.
let mut usb = builder.build();
// Run the USB device.
usb.run().await;
}
/// Handle CONTROL endpoint requests and responses. For many simple requests and responses
/// you can get away with only using the control endpoint.
struct ControlHandler {
if_num: InterfaceNumber,
}
impl Handler for ControlHandler {
/// Respond to HostToDevice control messages, where the host sends us a command and
/// optionally some data, and we can only acknowledge or reject it.
fn control_out<'a>(&'a mut self, req: Request, buf: &'a [u8]) -> Option<OutResponse> {
// Log the request before filtering to help with debugging.
info!("Got control_out, request={}, buf={:a}", req, buf);
// Only handle Vendor request types to an Interface.
if req.request_type != RequestType::Vendor || req.recipient != Recipient::Interface {
return None;
}
// Ignore requests to other interfaces.
if req.index != self.if_num.0 as u16 {
return None;
}
// Accept request 100, value 200, reject others.
if req.request == 100 && req.value == 200 {
Some(OutResponse::Accepted)
} else {
Some(OutResponse::Rejected)
}
}
/// Respond to DeviceToHost control messages, where the host requests some data from us.
fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> {
info!("Got control_in, request={}", req);
// Only handle Vendor request types to an Interface.
if req.request_type != RequestType::Vendor || req.recipient != Recipient::Interface {
return None;
}
// Ignore requests to other interfaces.
if req.index != self.if_num.0 as u16 {
return None;
}
// Respond "hello" to request 101, value 201, when asked for 5 bytes, otherwise reject.
if req.request == 101 && req.value == 201 && req.length == 5 {
buf[..5].copy_from_slice(b"hello");
Some(InResponse::Accepted(&buf[..5]))
} else {
Some(InResponse::Rejected)
}
}
}

View File

@ -77,6 +77,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );

View File

@ -77,6 +77,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );

View File

@ -4,7 +4,7 @@
use defmt::{panic, *}; use defmt::{panic, *};
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_stm32::rcc::{Clock48MhzSrc, ClockSrc, CrsConfig, CrsSyncSource, Pll, PllM, PllN, PllQ, PllR, PllSrc}; use embassy_stm32::rcc::{Clock48MhzSrc, ClockSrc, Hsi48Config, Pll, PllM, PllN, PllQ, PllR, PllSrc};
use embassy_stm32::time::Hertz; use embassy_stm32::time::Hertz;
use embassy_stm32::usb::{self, Driver, Instance}; use embassy_stm32::usb::{self, Driver, Instance};
use embassy_stm32::{bind_interrupts, peripherals, Config}; use embassy_stm32::{bind_interrupts, peripherals, Config};
@ -41,9 +41,7 @@ async fn main(_spawner: Spawner) {
if USE_HSI48 { if USE_HSI48 {
// Sets up the Clock Recovery System (CRS) to use the USB SOF to trim the HSI48 oscillator. // Sets up the Clock Recovery System (CRS) to use the USB SOF to trim the HSI48 oscillator.
config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::Hsi48(Some(CrsConfig { config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::Hsi48(Hsi48Config { sync_from_usb: true }));
sync_src: CrsSyncSource::Usb,
})));
} else { } else {
config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::PllQ); config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::PllQ);
} }
@ -77,6 +75,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );

View File

@ -37,7 +37,7 @@ async fn net_task(stack: &'static Stack<Device>) -> ! {
async fn main(spawner: Spawner) -> ! { async fn main(spawner: Spawner) -> ! {
let mut config = Config::default(); let mut config = Config::default();
config.rcc.hsi = None; config.rcc.hsi = None;
config.rcc.hsi48 = true; // needed for rng config.rcc.hsi48 = Some(Default::default()); // needed for RNG
config.rcc.hse = Some(Hse { config.rcc.hse = Some(Hse {
freq: Hertz(8_000_000), freq: Hertz(8_000_000),
mode: HseMode::BypassDigital, mode: HseMode::BypassDigital,

View File

@ -4,9 +4,6 @@
use defmt::{panic, *}; use defmt::{panic, *};
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_stm32::rcc::{
AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,
};
use embassy_stm32::time::Hertz; use embassy_stm32::time::Hertz;
use embassy_stm32::usb::{Driver, Instance}; use embassy_stm32::usb::{Driver, Instance};
use embassy_stm32::{bind_interrupts, pac, peripherals, usb, Config}; use embassy_stm32::{bind_interrupts, pac, peripherals, usb, Config};
@ -23,8 +20,10 @@ bind_interrupts!(struct Irqs {
#[embassy_executor::main] #[embassy_executor::main]
async fn main(_spawner: Spawner) { async fn main(_spawner: Spawner) {
let mut config = Config::default(); let mut config = Config::default();
{
use embassy_stm32::rcc::*;
config.rcc.hsi = None; config.rcc.hsi = None;
config.rcc.hsi48 = true; // needed for usb config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB
config.rcc.hse = Some(Hse { config.rcc.hse = Some(Hse {
freq: Hertz(8_000_000), freq: Hertz(8_000_000),
mode: HseMode::BypassDigital, mode: HseMode::BypassDigital,
@ -43,6 +42,7 @@ async fn main(_spawner: Spawner) {
config.rcc.apb3_pre = APBPrescaler::DIV4; config.rcc.apb3_pre = APBPrescaler::DIV4;
config.rcc.sys = Sysclk::PLL1_P; config.rcc.sys = Sysclk::PLL1_P;
config.rcc.voltage_scale = VoltageScale::Scale0; config.rcc.voltage_scale = VoltageScale::Scale0;
}
let p = embassy_stm32::init(config); let p = embassy_stm32::init(config);
info!("Hello World!"); info!("Hello World!");
@ -82,6 +82,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );

View File

@ -36,7 +36,7 @@ async fn main(spawner: Spawner) -> ! {
use embassy_stm32::rcc::*; use embassy_stm32::rcc::*;
config.rcc.hsi = Some(HSIPrescaler::DIV1); config.rcc.hsi = Some(HSIPrescaler::DIV1);
config.rcc.csi = true; config.rcc.csi = true;
config.rcc.hsi48 = true; // needed for RNG config.rcc.hsi48 = Some(Default::default()); // needed for RNG
config.rcc.pll1 = Some(Pll { config.rcc.pll1 = Some(Pll {
source: PllSource::HSI, source: PllSource::HSI,
prediv: PllPreDiv::DIV4, prediv: PllPreDiv::DIV4,

View File

@ -37,7 +37,7 @@ async fn main(spawner: Spawner) -> ! {
use embassy_stm32::rcc::*; use embassy_stm32::rcc::*;
config.rcc.hsi = Some(HSIPrescaler::DIV1); config.rcc.hsi = Some(HSIPrescaler::DIV1);
config.rcc.csi = true; config.rcc.csi = true;
config.rcc.hsi48 = true; // needed for RNG config.rcc.hsi48 = Some(Default::default()); // needed for RNG
config.rcc.pll1 = Some(Pll { config.rcc.pll1 = Some(Pll {
source: PllSource::HSI, source: PllSource::HSI,
prediv: PllPreDiv::DIV4, prediv: PllPreDiv::DIV4,

View File

@ -19,7 +19,6 @@ async fn main(_spawner: Spawner) {
use embassy_stm32::rcc::*; use embassy_stm32::rcc::*;
config.rcc.hsi = Some(HSIPrescaler::DIV1); config.rcc.hsi = Some(HSIPrescaler::DIV1);
config.rcc.csi = true; config.rcc.csi = true;
config.rcc.hsi48 = true; // needed for RNG
config.rcc.pll1 = Some(Pll { config.rcc.pll1 = Some(Pll {
source: PllSource::HSI, source: PllSource::HSI,
prediv: PllPreDiv::DIV4, prediv: PllPreDiv::DIV4,

View File

@ -15,7 +15,7 @@ bind_interrupts!(struct Irqs {
#[embassy_executor::main] #[embassy_executor::main]
async fn main(_spawner: Spawner) { async fn main(_spawner: Spawner) {
let mut config = Config::default(); let mut config = Config::default();
config.rcc.hsi48 = true; // needed for RNG. config.rcc.hsi48 = Some(Default::default()); // needed for RNG
let p = embassy_stm32::init(config); let p = embassy_stm32::init(config);
info!("Hello World!"); info!("Hello World!");

View File

@ -25,7 +25,7 @@ async fn main(_spawner: Spawner) {
use embassy_stm32::rcc::*; use embassy_stm32::rcc::*;
config.rcc.hsi = Some(HSIPrescaler::DIV1); config.rcc.hsi = Some(HSIPrescaler::DIV1);
config.rcc.csi = true; config.rcc.csi = true;
config.rcc.hsi48 = true; // needed for USB config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB
config.rcc.pll1 = Some(Pll { config.rcc.pll1 = Some(Pll {
source: PllSource::HSI, source: PllSource::HSI,
prediv: PllPreDiv::DIV4, prediv: PllPreDiv::DIV4,
@ -78,6 +78,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );

View File

@ -11,8 +11,7 @@ use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main] #[embassy_executor::main]
async fn main(_spawner: Spawner) { async fn main(_spawner: Spawner) {
let mut config = Config::default(); let config = Config::default();
config.rcc.hsi48 = true;
let p = embassy_stm32::init(config); let p = embassy_stm32::init(config);
let button = Input::new(p.PB2, Pull::Up); let button = Input::new(p.PB2, Pull::Up);

View File

@ -23,8 +23,8 @@ const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriatel
#[embassy_executor::main] #[embassy_executor::main]
async fn main(_spawner: Spawner) { async fn main(_spawner: Spawner) {
let mut config = embassy_stm32::Config::default(); let mut config = embassy_stm32::Config::default();
config.rcc.hsi = true;
config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI; config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI;
config.rcc.hsi48 = true;
let p = embassy_stm32::init(config); let p = embassy_stm32::init(config);
let mut spi_config = spi::Config::default(); let mut spi_config = spi::Config::default();

View File

@ -33,8 +33,8 @@ const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set th
#[embassy_executor::main] #[embassy_executor::main]
async fn main(_spawner: Spawner) { async fn main(_spawner: Spawner) {
let mut config = embassy_stm32::Config::default(); let mut config = embassy_stm32::Config::default();
config.rcc.hsi = true;
config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI; config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI;
config.rcc.hsi48 = true;
let p = embassy_stm32::init(config); let p = embassy_stm32::init(config);
let mut spi_config = spi::Config::default(); let mut spi_config = spi::Config::default();

View File

@ -23,8 +23,8 @@ const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriatel
#[embassy_executor::main] #[embassy_executor::main]
async fn main(_spawner: Spawner) { async fn main(_spawner: Spawner) {
let mut config = embassy_stm32::Config::default(); let mut config = embassy_stm32::Config::default();
config.rcc.hsi = true;
config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI; config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI;
config.rcc.hsi48 = true;
let p = embassy_stm32::init(config); let p = embassy_stm32::init(config);
let mut spi_config = spi::Config::default(); let mut spi_config = spi::Config::default();

View File

@ -23,8 +23,8 @@ const LORA_FREQUENCY_IN_HZ: u32 = 903_900_000; // warning: set this appropriatel
#[embassy_executor::main] #[embassy_executor::main]
async fn main(_spawner: Spawner) { async fn main(_spawner: Spawner) {
let mut config = embassy_stm32::Config::default(); let mut config = embassy_stm32::Config::default();
config.rcc.hsi = true;
config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI; config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI;
config.rcc.hsi48 = true;
let p = embassy_stm32::init(config); let p = embassy_stm32::init(config);
let mut spi_config = spi::Config::default(); let mut spi_config = spi::Config::default();

View File

@ -90,7 +90,7 @@ async fn main(spawner: Spawner) {
divq: None, divq: None,
divr: Some(PllRDiv::DIV2), // sysclk 80Mhz clock (8 / 1 * 20 / 2) divr: Some(PllRDiv::DIV2), // sysclk 80Mhz clock (8 / 1 * 20 / 2)
}); });
config.rcc.hsi48 = true; // needed for rng config.rcc.hsi48 = Some(Default::default()); // needed for RNG
} }
let dp = embassy_stm32::init(config); let dp = embassy_stm32::init(config);

View File

@ -23,7 +23,7 @@ async fn main(_spawner: Spawner) {
info!("Hello World!"); info!("Hello World!");
let mut config = Config::default(); let mut config = Config::default();
config.rcc.hsi48 = true; config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB
config.rcc.mux = ClockSrc::PLL1_R; config.rcc.mux = ClockSrc::PLL1_R;
config.rcc.hsi = true; config.rcc.hsi = true;
config.rcc.pll = Some(Pll { config.rcc.pll = Some(Pll {
@ -72,6 +72,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );

View File

@ -82,6 +82,7 @@ async fn main(spawner: Spawner) {
&mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..],
&mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..],
&mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..],
&mut [], // no msos descriptors
&mut make_static!([0; 128])[..], &mut make_static!([0; 128])[..],
); );

View File

@ -62,6 +62,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );

View File

@ -57,6 +57,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );

View File

@ -29,8 +29,7 @@ async fn main(_spawner: Spawner) {
n: Plln::MUL10, n: Plln::MUL10,
r: Plldiv::DIV1, r: Plldiv::DIV1,
}); });
//config.rcc.mux = ClockSrc::MSI(MSIRange::Range48mhz); config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB
config.rcc.hsi48 = true;
let p = embassy_stm32::init(config); let p = embassy_stm32::init(config);
@ -68,6 +67,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );

View File

@ -10,7 +10,7 @@ stm32f103c8 = ["embassy-stm32/stm32f103c8", "not-gpdma"]
stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "eth", "stop", "can", "not-gpdma", "dac-adc-pin", "rng"] stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "eth", "stop", "can", "not-gpdma", "dac-adc-pin", "rng"]
stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac-adc-pin"] stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac-adc-pin"]
stm32c031c6 = ["embassy-stm32/stm32c031c6", "cm0", "not-gpdma"] stm32c031c6 = ["embassy-stm32/stm32c031c6", "cm0", "not-gpdma"]
stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "not-gpdma", "rng"] stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "stop", "not-gpdma", "rng"]
stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac-adc-pin", "rng"] stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac-adc-pin", "rng"]
stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng"] stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng"]
stm32h7a3zi = ["embassy-stm32/stm32h7a3zi", "not-gpdma", "rng"] stm32h7a3zi = ["embassy-stm32/stm32h7a3zi", "not-gpdma", "rng"]

View File

@ -306,7 +306,7 @@ pub fn config() -> Config {
{ {
use embassy_stm32::rcc::*; use embassy_stm32::rcc::*;
config.rcc.hsi = None; config.rcc.hsi = None;
config.rcc.hsi48 = true; // needed for rng config.rcc.hsi48 = Some(Default::default()); // needed for RNG
config.rcc.hse = Some(Hse { config.rcc.hse = Some(Hse {
freq: Hertz(8_000_000), freq: Hertz(8_000_000),
mode: HseMode::BypassDigital, mode: HseMode::BypassDigital,
@ -332,7 +332,7 @@ pub fn config() -> Config {
use embassy_stm32::rcc::*; use embassy_stm32::rcc::*;
config.rcc.hsi = Some(HSIPrescaler::DIV1); config.rcc.hsi = Some(HSIPrescaler::DIV1);
config.rcc.csi = true; config.rcc.csi = true;
config.rcc.hsi48 = true; // needed for RNG config.rcc.hsi48 = Some(Default::default()); // needed for RNG
config.rcc.pll1 = Some(Pll { config.rcc.pll1 = Some(Pll {
source: PllSource::HSI, source: PllSource::HSI,
prediv: PllPreDiv::DIV4, prediv: PllPreDiv::DIV4,
@ -364,7 +364,7 @@ pub fn config() -> Config {
use embassy_stm32::rcc::*; use embassy_stm32::rcc::*;
config.rcc.hsi = Some(HSIPrescaler::DIV1); config.rcc.hsi = Some(HSIPrescaler::DIV1);
config.rcc.csi = true; config.rcc.csi = true;
config.rcc.hsi48 = true; // needed for RNG config.rcc.hsi48 = Some(Default::default()); // needed for RNG
config.rcc.pll1 = Some(Pll { config.rcc.pll1 = Some(Pll {
source: PllSource::HSI, source: PllSource::HSI,
prediv: PllPreDiv::DIV4, prediv: PllPreDiv::DIV4,