This commit is contained in:
Bjorn 2024-04-20 22:12:28 -07:00
commit 7658966486
33 changed files with 764 additions and 118 deletions

1
ci.sh
View file

@ -189,6 +189,7 @@ cargo batch \
--- build --release --manifest-path examples/stm32l1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32l1 \
--- build --release --manifest-path examples/stm32l4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32l4 \
--- build --release --manifest-path examples/stm32l5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32l5 \
--- build --release --manifest-path examples/stm32u0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32u0 \
--- build --release --manifest-path examples/stm32u5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32u5 \
--- build --release --manifest-path examples/stm32wb/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32wb \
--- build --release --manifest-path examples/stm32wba/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32wba \

View file

@ -231,3 +231,34 @@ Please refer to the STM32 documentation for the specific values suitable for you
Look for the `MEMORY` section and try to determine the FLASH and RAM sizes and section start.
If you find a case where the memory.x is wrong, please report it on [this Github issue](https://github.com/embassy-rs/stm32-data/issues/301) so other users are not caught by surprise.
== Known issues (details and/or mitigations)
These are issues that are commonly reported. Help wanted fixing them, or improving the UX when possible!
=== STM32H5 and STM32H7 power issues
STM32 chips with built-in power management (SMPS and LDO) settings often cause user problems when the configuration does not match how the board was designed.
Settings from the examples, or even from other working boards, may not work on YOUR board, because they are wired differently.
Additionally, some PWR settings require a full device reboot (and enough time to discharge any power capacitors!), making this hard to troubleshoot. Also, some
"wrong" power settings will ALMOST work, meaning it will sometimes work on some boots, or for a while, but crash unexpectedly.
There is not a fix for this yet, as it is board/hardware dependant. See link:https://github.com/embassy-rs/embassy/issues/2806[this tracking issue] for more details
=== STM32 BDMA only work out of some RAM regions
The STM32 BDMA controller included in some chips (TODO: list which ones) has a limitation in that it only works out of certain regions of RAM (TODO: list which ones), otherwise the transfer
will fail.
If you see errors that look like this:
[source,plain]
----
DMA: error on BDMA@1234ABCD channel 4
----
You likely need to set up your linker script to define a special region for this area, and copy data to that region before using with BDMA.
TODO: show how to do that

View file

@ -63,7 +63,7 @@ Luckily, there is an elegant solution to this problem when using Embassy.
== Async version
It's time to use the Embassy capabilities to its fullest. At the core, Embassy has an async excecutor, or a runtime for async tasks if you will. The executor polls a set of tasks (defined at compile time), and whenever a task `blocks`, the executor will run another task, or put the microcontroller to sleep.
It's time to use the Embassy capabilities to its fullest. At the core, Embassy has an async executor, or a runtime for async tasks if you will. The executor polls a set of tasks (defined at compile time), and whenever a task `blocks`, the executor will run another task, or put the microcontroller to sleep.
[source,rust]
----

View file

@ -1,4 +1,4 @@
//! Pulse Density Modulation (PDM) mirophone driver
//! Pulse Density Modulation (PDM) microphone driver
#![macro_use]

View file

@ -71,7 +71,7 @@ rand_core = "0.6.3"
sdio-host = "0.5.0"
critical-section = "1.1"
#stm32-metapac = { version = "15" }
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-d674277b78ca7400ecfeeb1b5af4e460a65c1a61" }
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-01ac9bfd035961dc75f32dcd6080501538246d5c" }
vcell = "0.1.3"
nb = "1.0.0"
@ -97,7 +97,7 @@ proc-macro2 = "1.0.36"
quote = "1.0.15"
#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]}
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-d674277b78ca7400ecfeeb1b5af4e460a65c1a61", default-features = false, features = ["metadata"]}
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-01ac9bfd035961dc75f32dcd6080501538246d5c", default-features = false, features = ["metadata"]}
[features]
default = ["rt"]

View file

@ -170,7 +170,7 @@ impl<'d, T: Instance> Adc<'d, T> {
fn configure_differential_inputs(&mut self) {
T::regs().difsel().modify(|w| {
for n in 0..20 {
for n in 0..18 {
w.set_difsel(n, Difsel::SINGLEENDED);
}
});

View file

@ -10,7 +10,7 @@
#[cfg_attr(adc_v1, path = "v1.rs")]
#[cfg_attr(adc_l0, path = "v1.rs")]
#[cfg_attr(adc_v2, path = "v2.rs")]
#[cfg_attr(any(adc_v3, adc_g0, adc_h5), path = "v3.rs")]
#[cfg_attr(any(adc_v3, adc_g0, adc_h5, adc_u0), path = "v3.rs")]
#[cfg_attr(adc_v4, path = "v4.rs")]
#[cfg_attr(adc_g4, path = "g4.rs")]
mod _version;
@ -76,7 +76,12 @@ pub(crate) fn blocking_delay_us(us: u32) {
#[cfg(time)]
embassy_time::block_for(embassy_time::Duration::from_micros(us));
#[cfg(not(time))]
cortex_m::asm::delay(unsafe { crate::rcc::get_freqs() }.sys.unwrap().0 * us / 1_000_000);
{
let freq = unsafe { crate::rcc::get_freqs() }.sys.unwrap().0 as u64;
let us = us as u64;
let cycles = freq * us / 1_000_000;
cortex_m::asm::delay(cycles as u32);
}
}
/// ADC instance.
@ -91,6 +96,7 @@ pub(crate) fn blocking_delay_us(us: u32) {
adc_f3,
adc_f3_v1_1,
adc_g0,
adc_u0,
adc_h5
)))]
#[allow(private_bounds)]
@ -109,6 +115,7 @@ pub trait Instance: SealedInstance + crate::Peripheral<P = Self> {
adc_f3,
adc_f3_v1_1,
adc_g0,
adc_u0,
adc_h5
))]
#[allow(private_bounds)]

View file

@ -19,6 +19,8 @@ impl<T: Instance> super::SealedAdcPin<T> for VrefInt {
let val = 13;
} else if #[cfg(adc_h5)] {
let val = 17;
} else if #[cfg(adc_u0)] {
let val = 12;
} else {
let val = 0;
}
@ -36,6 +38,8 @@ impl<T: Instance> super::SealedAdcPin<T> for Temperature {
let val = 12;
} else if #[cfg(adc_h5)] {
let val = 16;
} else if #[cfg(adc_u0)] {
let val = 11;
} else {
let val = 17;
}
@ -53,6 +57,8 @@ impl<T: Instance> super::SealedAdcPin<T> for Vbat {
let val = 14;
} else if #[cfg(adc_h5)] {
let val = 2;
} else if #[cfg(adc_h5)] {
let val = 13;
} else {
let val = 18;
}
@ -73,17 +79,29 @@ cfg_if! {
}
}
cfg_if! {
if #[cfg(adc_u0)] {
pub struct DacOut;
impl<T: Instance> AdcPin<T> for DacOut {}
impl<T: Instance> super::SealedAdcPin<T> for DacOut {
fn channel(&self) -> u8 {
19
}
}
}
}
impl<'d, T: Instance> Adc<'d, T> {
pub fn new(adc: impl Peripheral<P = T> + 'd) -> Self {
into_ref!(adc);
T::enable_and_reset();
T::regs().cr().modify(|reg| {
#[cfg(not(adc_g0))]
#[cfg(not(any(adc_g0, adc_u0)))]
reg.set_deeppwd(false);
reg.set_advregen(true);
});
#[cfg(adc_g0)]
#[cfg(any(adc_g0, adc_u0))]
T::regs().cfgr1().modify(|reg| {
reg.set_chselrmod(false);
});
@ -107,11 +125,11 @@ impl<'d, T: Instance> Adc<'d, T> {
}
pub fn enable_vrefint(&self) -> VrefInt {
#[cfg(not(adc_g0))]
#[cfg(not(any(adc_g0, adc_u0)))]
T::common_regs().ccr().modify(|reg| {
reg.set_vrefen(true);
});
#[cfg(adc_g0)]
#[cfg(any(adc_g0, adc_u0))]
T::regs().ccr().modify(|reg| {
reg.set_vrefen(true);
});
@ -125,7 +143,7 @@ impl<'d, T: Instance> Adc<'d, T> {
pub fn enable_temperature(&self) -> Temperature {
cfg_if! {
if #[cfg(adc_g0)] {
if #[cfg(any(adc_g0, adc_u0))] {
T::regs().ccr().modify(|reg| {
reg.set_tsen(true);
});
@ -145,7 +163,7 @@ impl<'d, T: Instance> Adc<'d, T> {
pub fn enable_vbat(&self) -> Vbat {
cfg_if! {
if #[cfg(adc_g0)] {
if #[cfg(any(adc_g0, adc_u0))] {
T::regs().ccr().modify(|reg| {
reg.set_vbaten(true);
});
@ -168,9 +186,9 @@ impl<'d, T: Instance> Adc<'d, T> {
}
pub fn set_resolution(&mut self, resolution: Resolution) {
#[cfg(not(adc_g0))]
#[cfg(not(any(adc_g0, adc_u0)))]
T::regs().cfgr().modify(|reg| reg.set_res(resolution.into()));
#[cfg(adc_g0)]
#[cfg(any(adc_g0, adc_u0))]
T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into()));
}
@ -231,9 +249,9 @@ impl<'d, T: Instance> Adc<'d, T> {
Self::set_channel_sample_time(pin.channel(), self.sample_time);
// Select channel
#[cfg(not(adc_g0))]
#[cfg(not(any(adc_g0, adc_u0)))]
T::regs().sqr1().write(|reg| reg.set_sq(0, pin.channel()));
#[cfg(adc_g0)]
#[cfg(any(adc_g0, adc_u0))]
T::regs().chselr().write(|reg| reg.set_chsel(1 << pin.channel()));
// Some models are affected by an erratum:
@ -261,7 +279,7 @@ impl<'d, T: Instance> Adc<'d, T> {
fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) {
cfg_if! {
if #[cfg(adc_g0)] {
if #[cfg(any(adc_g0, adc_u0))] {
T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into()));
} else if #[cfg(adc_h5)] {
match _ch {

View file

@ -368,6 +368,7 @@ impl Registers {
w.set_rfne(0, true); // Rx Fifo 0 New Msg
w.set_rfne(1, true); // Rx Fifo 1 New Msg
w.set_tce(true); // Tx Complete
w.set_boe(true); // Bus-Off Status Changed
});
self.regs.ile().modify(|w| {
w.set_eint0(true); // Interrupt Line 0

View file

@ -44,53 +44,51 @@ impl<T: Instance> interrupt::typelevel::Handler<T::IT0Interrupt> for IT0Interrup
let ir = regs.ir().read();
{
if ir.tc() {
regs.ir().write(|w| w.set_tc(true));
}
if ir.tefn() {
regs.ir().write(|w| w.set_tefn(true));
}
match &T::state().tx_mode {
TxMode::NonBuffered(waker) => waker.wake(),
TxMode::ClassicBuffered(buf) => {
if !T::registers().tx_queue_is_full() {
match buf.tx_receiver.try_receive() {
Ok(frame) => {
_ = T::registers().write(&frame);
}
Err(_) => {}
}
}
}
TxMode::FdBuffered(buf) => {
if !T::registers().tx_queue_is_full() {
match buf.tx_receiver.try_receive() {
Ok(frame) => {
_ = T::registers().write(&frame);
}
Err(_) => {}
}
}
}
}
if ir.tc() {
regs.ir().write(|w| w.set_tc(true));
}
if ir.tefn() {
regs.ir().write(|w| w.set_tefn(true));
}
if ir.ped() || ir.pea() {
regs.ir().write(|w| {
w.set_ped(true);
w.set_pea(true);
});
match &T::state().tx_mode {
TxMode::NonBuffered(waker) => waker.wake(),
TxMode::ClassicBuffered(buf) => {
if !T::registers().tx_queue_is_full() {
match buf.tx_receiver.try_receive() {
Ok(frame) => {
_ = T::registers().write(&frame);
}
Err(_) => {}
}
}
}
TxMode::FdBuffered(buf) => {
if !T::registers().tx_queue_is_full() {
match buf.tx_receiver.try_receive() {
Ok(frame) => {
_ = T::registers().write(&frame);
}
Err(_) => {}
}
}
}
}
if ir.rfn(0) {
T::state().rx_mode.on_interrupt::<T>(0);
}
if ir.rfn(1) {
T::state().rx_mode.on_interrupt::<T>(1);
}
if ir.bo() {
regs.ir().write(|w| w.set_bo(true));
if regs.psr().read().bo() {
// Initiate bus-off recovery sequence by resetting CCCR.INIT
regs.cccr().modify(|w| w.set_init(false));
}
}
}
}

View file

@ -13,6 +13,8 @@ pub struct Crc<'d> {
}
/// CRC configuration errlr
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ConfigError {
/// The selected polynomial is invalid.
InvalidPolynomial,

View file

@ -235,6 +235,23 @@ pub enum TriggerSel {
Exti9 = 13,
}
/// Trigger selection for U0.
#[cfg(stm32u0)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum TriggerSel {
Software = 0,
Tim1 = 1,
Tim2 = 2,
Tim3 = 3,
Tim6 = 5,
Tim7 = 6,
Tim15 = 8,
Lptim1 = 11,
Lptim2 = 12,
Exti9 = 14,
}
/// Trigger selection for G4.
#[cfg(stm32g4)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]

View file

@ -101,10 +101,11 @@ pub enum FlashBank {
#[cfg_attr(flash_h7ab, path = "h7.rs")]
#[cfg_attr(flash_u5, path = "u5.rs")]
#[cfg_attr(flash_h50, path = "h50.rs")]
#[cfg_attr(flash_u0, path = "u0.rs")]
#[cfg_attr(
not(any(
flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f0, flash_f1, flash_f3, flash_f4, flash_f7, flash_g0,
flash_g4, flash_h7, flash_h7ab, flash_u5, flash_h50
flash_g4, flash_h7, flash_h7ab, flash_u5, flash_h50, flash_u0
)),
path = "other.rs"
)]

View file

@ -0,0 +1,96 @@
use core::ptr::write_volatile;
use core::sync::atomic::{fence, Ordering};
use cortex_m::interrupt;
use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
use crate::flash::Error;
use crate::pac;
pub(crate) const fn is_default_layout() -> bool {
true
}
pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] {
&FLASH_REGIONS
}
pub(crate) unsafe fn lock() {
pac::FLASH.cr().modify(|w| w.set_lock(true));
}
pub(crate) unsafe fn unlock() {
// Wait, while the memory interface is busy.
while pac::FLASH.sr().read().bsy1() {}
// Unlock flash
if pac::FLASH.cr().read().lock() {
pac::FLASH.keyr().write(|w| w.set_key(0x4567_0123));
pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB));
}
}
pub(crate) unsafe fn enable_blocking_write() {
assert_eq!(0, WRITE_SIZE % 4);
pac::FLASH.cr().write(|w| w.set_pg(true));
}
pub(crate) unsafe fn disable_blocking_write() {
pac::FLASH.cr().write(|w| w.set_pg(false));
}
pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
let mut address = start_address;
for val in buf.chunks(4) {
write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap()));
address += val.len() as u32;
// prevents parallelism errors
fence(Ordering::SeqCst);
}
wait_ready_blocking()
}
pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {
let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32;
while pac::FLASH.sr().read().bsy1() {}
clear_all_err();
interrupt::free(|_| {
pac::FLASH.cr().modify(|w| {
w.set_per(true);
w.set_pnb(idx as u8);
w.set_strt(true);
});
});
let ret: Result<(), Error> = wait_ready_blocking();
pac::FLASH.cr().modify(|w| w.set_per(false));
ret
}
pub(crate) unsafe fn wait_ready_blocking() -> Result<(), Error> {
while pac::FLASH.sr().read().bsy1() {}
let sr = pac::FLASH.sr().read();
if sr.progerr() {
return Err(Error::Prog);
}
if sr.wrperr() {
return Err(Error::Protected);
}
if sr.pgaerr() {
return Err(Error::Unaligned);
}
Ok(())
}
pub(crate) unsafe fn clear_all_err() {
// read and write back the same value.
// This clears all "write 1 to clear" bits.
pac::FLASH.sr().modify(|_| {});
}

View file

@ -933,7 +933,6 @@ impl<'d, T: BasicInstance> Uart<'d, T, Async> {
/// I/O in idle or in reception.
/// Apart from this, the communication protocol is similar to normal USART mode. Any conflict
/// on the line must be managed by software (for instance by using a centralized arbiter).
#[cfg(not(any(usart_v1, usart_v2)))]
#[doc(alias("HDSEL"))]
pub fn new_half_duplex(
peri: impl Peripheral<P = T> + 'd,
@ -943,7 +942,10 @@ impl<'d, T: BasicInstance> Uart<'d, T, Async> {
rx_dma: impl Peripheral<P = impl RxDma<T>> + 'd,
mut config: Config,
) -> Result<Self, ConfigError> {
config.swap_rx_tx = false;
#[cfg(not(any(usart_v1, usart_v2)))]
{
config.swap_rx_tx = false;
}
config.half_duplex = true;
Self::new_inner(
@ -1084,14 +1086,16 @@ impl<'d, T: BasicInstance> Uart<'d, T, Blocking> {
/// I/O in idle or in reception.
/// Apart from this, the communication protocol is similar to normal USART mode. Any conflict
/// on the line must be managed by software (for instance by using a centralized arbiter).
#[cfg(not(any(usart_v1, usart_v2)))]
#[doc(alias("HDSEL"))]
pub fn new_blocking_half_duplex(
peri: impl Peripheral<P = T> + 'd,
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
mut config: Config,
) -> Result<Self, ConfigError> {
config.swap_rx_tx = false;
#[cfg(not(any(usart_v1, usart_v2)))]
{
config.swap_rx_tx = false;
}
config.half_duplex = true;
Self::new_inner(
@ -1354,10 +1358,9 @@ fn configure(
}
});
#[cfg(not(usart_v1))]
r.cr3().modify(|w| {
#[cfg(not(usart_v1))]
w.set_onebit(config.assume_noise_free);
#[cfg(any(usart_v3, usart_v4))]
w.set_hdsel(config.half_duplex);
});

View file

@ -23,7 +23,7 @@ fn common_init<T: Instance>() {
)
}
#[cfg(any(stm32l4, stm32l5, stm32wb))]
#[cfg(any(stm32l4, stm32l5, stm32wb, stm32u0))]
critical_section::with(|_| crate::pac::PWR.cr2().modify(|w| w.set_usv(true)));
#[cfg(pwr_h5)]

View file

@ -672,45 +672,51 @@ impl<'d, T: Instance> Bus<'d, T> {
let r = T::regs();
// Configure RX fifo size. All endpoints share the same FIFO area.
let rx_fifo_size_words = RX_FIFO_EXTRA_SIZE_WORDS + ep_fifo_size(&self.ep_out);
trace!("configuring rx fifo size={}", rx_fifo_size_words);
// ERRATA NOTE: Don't interrupt FIFOs being written to. The interrupt
// handler COULD interrupt us here and do FIFO operations, so ensure
// the interrupt does not occur.
critical_section::with(|_| {
// Configure RX fifo size. All endpoints share the same FIFO area.
let rx_fifo_size_words = RX_FIFO_EXTRA_SIZE_WORDS + ep_fifo_size(&self.ep_out);
trace!("configuring rx fifo size={}", rx_fifo_size_words);
r.grxfsiz().modify(|w| w.set_rxfd(rx_fifo_size_words));
r.grxfsiz().modify(|w| w.set_rxfd(rx_fifo_size_words));
// Configure TX (USB in direction) fifo size for each endpoint
let mut fifo_top = rx_fifo_size_words;
for i in 0..T::ENDPOINT_COUNT {
if let Some(ep) = self.ep_in[i] {
trace!(
"configuring tx fifo ep={}, offset={}, size={}",
i,
fifo_top,
ep.fifo_size_words
);
// Configure TX (USB in direction) fifo size for each endpoint
let mut fifo_top = rx_fifo_size_words;
for i in 0..T::ENDPOINT_COUNT {
if let Some(ep) = self.ep_in[i] {
trace!(
"configuring tx fifo ep={}, offset={}, size={}",
i,
fifo_top,
ep.fifo_size_words
);
let dieptxf = if i == 0 { r.dieptxf0() } else { r.dieptxf(i - 1) };
let dieptxf = if i == 0 { r.dieptxf0() } else { r.dieptxf(i - 1) };
dieptxf.write(|w| {
w.set_fd(ep.fifo_size_words);
w.set_sa(fifo_top);
});
dieptxf.write(|w| {
w.set_fd(ep.fifo_size_words);
w.set_sa(fifo_top);
});
fifo_top += ep.fifo_size_words;
fifo_top += ep.fifo_size_words;
}
}
}
assert!(
fifo_top <= T::FIFO_DEPTH_WORDS,
"FIFO allocations exceeded maximum capacity"
);
assert!(
fifo_top <= T::FIFO_DEPTH_WORDS,
"FIFO allocations exceeded maximum capacity"
);
// Flush fifos
r.grstctl().write(|w| {
w.set_rxfflsh(true);
w.set_txfflsh(true);
w.set_txfnum(0x10);
// Flush fifos
r.grstctl().write(|w| {
w.set_rxfflsh(true);
w.set_txfflsh(true);
w.set_txfnum(0x10);
});
});
loop {
let x = r.grstctl().read();
if !x.rxfflsh() && !x.txfflsh() {
@ -1208,27 +1214,31 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> {
.await
}
// Setup transfer size
r.dieptsiz(index).write(|w| {
w.set_mcnt(1);
w.set_pktcnt(1);
w.set_xfrsiz(buf.len() as _);
});
// ERRATA: Transmit data FIFO is corrupted when a write sequence to the FIFO is interrupted with
// accesses to certain OTG_FS registers.
//
// Prevent the interrupt (which might poke FIFOs) from executing while copying data to FIFOs.
critical_section::with(|_| {
// Setup transfer size
r.dieptsiz(index).write(|w| {
w.set_mcnt(1);
w.set_pktcnt(1);
w.set_xfrsiz(buf.len() as _);
});
// Enable endpoint
r.diepctl(index).modify(|w| {
w.set_cnak(true);
w.set_epena(true);
});
});
// Write data to FIFO
for chunk in buf.chunks(4) {
let mut tmp = [0u8; 4];
tmp[0..chunk.len()].copy_from_slice(chunk);
r.fifo(index).write_value(regs::Fifo(u32::from_ne_bytes(tmp)));
}
// Write data to FIFO
for chunk in buf.chunks(4) {
let mut tmp = [0u8; 4];
tmp[0..chunk.len()].copy_from_slice(chunk);
r.fifo(index).write_value(regs::Fifo(u32::from_ne_bytes(tmp)));
}
});
trace!("write done ep={:?}", self.info.addr);

View file

@ -107,14 +107,14 @@ const EP_COUNT: usize = 8;
#[cfg(any(usbram_16x1_512, usbram_16x2_512))]
const USBRAM_SIZE: usize = 512;
#[cfg(usbram_16x2_1024)]
#[cfg(any(usbram_16x2_1024, usbram_32_1024))]
const USBRAM_SIZE: usize = 1024;
#[cfg(usbram_32_2048)]
const USBRAM_SIZE: usize = 2048;
#[cfg(not(usbram_32_2048))]
#[cfg(not(any(usbram_32_2048, usbram_32_1024)))]
const USBRAM_ALIGN: usize = 2;
#[cfg(usbram_32_2048)]
#[cfg(any(usbram_32_2048, usbram_32_1024))]
const USBRAM_ALIGN: usize = 4;
const NEW_AW: AtomicWaker = AtomicWaker::new();
@ -159,7 +159,7 @@ fn calc_out_len(len: u16) -> (u16, u16) {
}
}
#[cfg(not(usbram_32_2048))]
#[cfg(not(any(usbram_32_2048, usbram_32_1024)))]
mod btable {
use super::*;
@ -180,7 +180,7 @@ mod btable {
USBRAM.mem(index * 4 + 3).read()
}
}
#[cfg(usbram_32_2048)]
#[cfg(any(usbram_32_2048, usbram_32_1024))]
mod btable {
use super::*;
@ -224,9 +224,9 @@ impl<T: Instance> EndpointBuffer<T> {
let n = USBRAM_ALIGN.min(buf.len() - i * USBRAM_ALIGN);
val[..n].copy_from_slice(&buf[i * USBRAM_ALIGN..][..n]);
#[cfg(not(usbram_32_2048))]
#[cfg(not(any(usbram_32_2048, usbram_32_1024)))]
let val = u16::from_le_bytes(val);
#[cfg(usbram_32_2048)]
#[cfg(any(usbram_32_2048, usbram_32_1024))]
let val = u32::from_le_bytes(val);
USBRAM.mem(self.addr as usize / USBRAM_ALIGN + i).write_value(val);
}

View file

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

View file

@ -2,7 +2,7 @@
# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`
#runner = "probe-rs run --chip STM32L475VGT6"
#runner = "probe-rs run --chip STM32L475VG"
runner = "probe-run --chip STM32L4S5QI"
runner = "probe-rs run --chip STM32L4S5QI"
[build]
target = "thumbv7em-none-eabi"

View file

@ -6,10 +6,11 @@ license = "MIT OR Apache-2.0"
[dependencies]
# Change stm32u083rc to your chip name, if necessary.
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083rc", "memory-x", "unstable-pac", "exti"] }
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083rc", "memory-x", "unstable-pac", "exti", "chrono"] }
embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] }
defmt = "0.3"
defmt-rtt = "0.4"
@ -21,5 +22,8 @@ panic-probe = { version = "0.3", features = ["print-defmt"] }
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
heapless = { version = "0.8", default-features = false }
micromath = "2.0.0"
chrono = { version = "0.4.38", default-features = false }
[profile.release]
debug = 2

View file

@ -0,0 +1,30 @@
#![no_std]
#![no_main]
use defmt::*;
use embassy_stm32::adc::{Adc, Resolution};
use embassy_stm32::Config;
use embassy_time::Duration;
use {defmt_rtt as _, panic_probe as _};
#[cortex_m_rt::entry]
fn main() -> ! {
info!("Hello World!");
let mut config = Config::default();
{
use embassy_stm32::rcc::*;
config.rcc.mux.adcsel = mux::Adcsel::SYS;
}
let p = embassy_stm32::init(config);
let mut adc = Adc::new(p.ADC1);
adc.set_resolution(Resolution::BITS8);
let mut channel = p.PC0;
loop {
let v = adc.read(&mut channel);
info!("--> {}", v);
embassy_time::block_for(Duration::from_millis(200));
}
}

View file

@ -0,0 +1,31 @@
#![no_std]
#![no_main]
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::crc::{Config, Crc, InputReverseConfig, PolySize};
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
info!("Hello World!");
// Setup for: https://crccalc.com/?crc=Life, it never dieWomen are my favorite guy&method=crc32&datatype=ascii&outtype=0
let mut crc = Crc::new(
p.CRC,
unwrap!(Config::new(
InputReverseConfig::Byte,
true,
PolySize::Width32,
0xFFFFFFFF,
0x04C11DB7
)),
);
let output = crc.feed_bytes(b"Life, it never die\nWomen are my favorite guy") ^ 0xFFFFFFFF;
defmt::assert_eq!(output, 0x33F0E26B);
cortex_m::asm::bkpt();
}

View file

@ -0,0 +1,35 @@
#![no_std]
#![no_main]
use defmt::*;
use embassy_stm32::dac::{DacCh1, Value};
use embassy_stm32::dma::NoDma;
use {defmt_rtt as _, panic_probe as _};
#[cortex_m_rt::entry]
fn main() -> ! {
let p = embassy_stm32::init(Default::default());
info!("Hello World!");
let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4);
loop {
for v in 0..=255 {
dac.set(Value::Bit8(to_sine_wave(v)));
}
}
}
use micromath::F32Ext;
fn to_sine_wave(v: u8) -> u8 {
if v >= 128 {
// top half
let r = 3.14 * ((v - 128) as f32 / 128.0);
(r.sin() * 128.0 + 127.0) as u8
} else {
// bottom half
let r = 3.14 + 3.14 * (v as f32 / 128.0);
(r.sin() * 128.0 + 127.0) as u8
}
}

View file

@ -0,0 +1,43 @@
#![no_std]
#![no_main]
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::flash::Flash;
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
info!("Hello World!");
let addr: u32 = 0x40000 - 2 * 1024;
let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank1_region;
info!("Reading...");
let mut buf = [0u8; 32];
unwrap!(f.blocking_read(addr, &mut buf));
info!("Read: {=[u8]:x}", buf);
info!("Erasing...");
unwrap!(f.blocking_erase(addr, addr + 2 * 1024));
info!("Reading...");
let mut buf = [0u8; 32];
unwrap!(f.blocking_read(addr, &mut buf));
info!("Read after erase: {=[u8]:x}", buf);
info!("Writing...");
unwrap!(f.blocking_write(
addr,
&[
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32
]
));
info!("Reading...");
let mut buf = [0u8; 32];
unwrap!(f.blocking_read(addr, &mut buf));
info!("Read: {=[u8]:x}", buf);
}

View file

@ -0,0 +1,21 @@
#![no_std]
#![no_main]
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::i2c::I2c;
use embassy_stm32::time::Hertz;
use {defmt_rtt as _, panic_probe as _};
const ADDRESS: u8 = 0x5F;
const WHOAMI: u8 = 0x0F;
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
let mut i2c = I2c::new_blocking(p.I2C2, p.PB10, p.PB11, Hertz(100_000), Default::default());
let mut data = [0u8; 1];
unwrap!(i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data));
info!("Whoami: {}", data[0]);
}

View file

@ -0,0 +1,43 @@
#![no_std]
#![no_main]
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::rcc::mux::Clk48sel;
use embassy_stm32::rng::Rng;
use embassy_stm32::{bind_interrupts, peripherals, rng, Config};
use {defmt_rtt as _, panic_probe as _};
bind_interrupts!(struct Irqs {
RNG_CRYP => rng::InterruptHandler<peripherals::RNG>;
});
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let mut config = Config::default();
{
use embassy_stm32::rcc::*;
config.rcc.hsi = true;
config.rcc.pll = Some(Pll {
source: PllSource::HSI, // 16 MHz
prediv: PllPreDiv::DIV1,
mul: PllMul::MUL7, // 16 * 7 = 112 MHz
divp: None,
divq: None,
divr: Some(PllRDiv::DIV2), // 112 / 2 = 56 MHz
});
config.rcc.sys = Sysclk::PLL1_R;
config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: false }); // needed for RNG
config.rcc.mux.clk48sel = Clk48sel::HSI48; // needed for RNG (or use MSI or PLLQ if you want)
}
let p = embassy_stm32::init(config);
info!("Hello World!");
let mut rng = Rng::new(p.RNG, Irqs);
let mut buf = [0u8; 16];
unwrap!(rng.async_fill_bytes(&mut buf).await);
info!("random bytes: {:02x}", buf);
}

View file

@ -0,0 +1,49 @@
#![no_std]
#![no_main]
use chrono::{NaiveDate, NaiveDateTime};
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::rtc::{Rtc, RtcConfig};
use embassy_stm32::Config;
use embassy_time::Timer;
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let mut config = Config::default();
{
use embassy_stm32::rcc::*;
config.rcc.sys = Sysclk::PLL1_R;
config.rcc.hsi = true;
config.rcc.pll = Some(Pll {
source: PllSource::HSI, // 16 MHz
prediv: PllPreDiv::DIV1,
mul: PllMul::MUL7, // 16 * 7 = 112 MHz
divp: None,
divq: None,
divr: Some(PllRDiv::DIV2), // 112 / 2 = 56 MHz
});
config.rcc.ls = LsConfig::default();
}
let p = embassy_stm32::init(config);
info!("Hello World!");
let now = NaiveDate::from_ymd_opt(2020, 5, 15)
.unwrap()
.and_hms_opt(10, 30, 15)
.unwrap();
let mut rtc = Rtc::new(p.RTC, RtcConfig::default());
info!("Got RTC! {:?}", now.and_utc().timestamp());
rtc.set_datetime(now.into()).expect("datetime not set");
// In reality the delay would be much longer
Timer::after_millis(20000).await;
let then: NaiveDateTime = rtc.now().unwrap().into();
info!("Got RTC! {:?}", then.and_utc().timestamp());
}

View file

@ -0,0 +1,30 @@
#![no_std]
#![no_main]
use defmt::*;
use embassy_stm32::gpio::{Level, Output, Speed};
use embassy_stm32::spi::{Config, Spi};
use embassy_stm32::time::Hertz;
use {defmt_rtt as _, panic_probe as _};
#[cortex_m_rt::entry]
fn main() -> ! {
info!("Hello World!");
let p = embassy_stm32::init(Default::default());
let mut spi_config = Config::default();
spi_config.frequency = Hertz(1_000_000);
let mut spi = Spi::new_blocking(p.SPI3, p.PC10, p.PC12, p.PC11, spi_config);
let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh);
loop {
let mut buf = [0x0Au8; 4];
cs.set_low();
unwrap!(spi.blocking_transfer_in_place(&mut buf));
cs.set_high();
info!("xfer {=[u8]:x}", buf);
}
}

View file

@ -0,0 +1,25 @@
#![no_std]
#![no_main]
use defmt::*;
use embassy_stm32::usart::{Config, Uart};
use {defmt_rtt as _, panic_probe as _};
#[cortex_m_rt::entry]
fn main() -> ! {
info!("Hello World!");
let p = embassy_stm32::init(Default::default());
let config = Config::default();
let mut usart = Uart::new_blocking(p.USART2, p.PA3, p.PA2, config).unwrap();
unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n"));
info!("wrote Hello, starting echo");
let mut buf = [0u8; 1];
loop {
unwrap!(usart.blocking_read(&mut buf));
unwrap!(usart.blocking_write(&buf));
}
}

View file

@ -0,0 +1,109 @@
#![no_std]
#![no_main]
use defmt::{panic, *};
use defmt_rtt as _; // global logger
use embassy_executor::Spawner;
use embassy_stm32::usb::{Driver, Instance};
use embassy_stm32::{bind_interrupts, peripherals, usb, Config};
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
use embassy_usb::driver::EndpointError;
use embassy_usb::Builder;
use futures::future::join;
use panic_probe as _;
bind_interrupts!(struct Irqs {
USB_DRD_FS => usb::InterruptHandler<peripherals::USB>;
});
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let mut config = Config::default();
{
use embassy_stm32::rcc::*;
config.rcc.hsi = true;
config.rcc.pll = Some(Pll {
source: PllSource::HSI, // 16 MHz
prediv: PllPreDiv::DIV1,
mul: PllMul::MUL7,
divp: None,
divq: None,
divr: Some(PllRDiv::DIV2), // 56 MHz
});
config.rcc.sys = Sysclk::PLL1_R;
config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB
config.rcc.mux.clk48sel = mux::Clk48sel::HSI48; // USB uses ICLK
}
let p = embassy_stm32::init(config);
info!("Hello World!");
// Create the driver, from the HAL.
let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);
// Create embassy-usb Config
let config = embassy_usb::Config::new(0xc0de, 0xcafe);
//config.max_packet_size_0 = 64;
// Create embassy-usb DeviceBuilder using the driver and config.
// It needs some buffers for building the descriptors.
let mut config_descriptor = [0; 256];
let mut bos_descriptor = [0; 256];
let mut control_buf = [0; 7];
let mut state = State::new();
let mut builder = Builder::new(
driver,
config,
&mut config_descriptor,
&mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf,
);
// Create classes on the builder.
let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
// Build the builder.
let mut usb = builder.build();
// Run the USB device.
let usb_fut = usb.run();
// Do stuff with the class!
let echo_fut = async {
loop {
class.wait_connection().await;
info!("Connected");
let _ = echo(&mut class).await;
info!("Disconnected");
}
};
// Run everything concurrently.
// If we had made everything `'static` above instead, we could do this using separate tasks instead.
join(usb_fut, echo_fut).await;
}
struct Disconnected {}
impl From<EndpointError> for Disconnected {
fn from(val: EndpointError) -> Self {
match val {
EndpointError::BufferOverflow => panic!("Buffer overflow"),
EndpointError::Disabled => Disconnected {},
}
}
}
async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {
let mut buf = [0; 64];
loop {
let n = class.read_packet(&mut buf).await?;
let data = &buf[..n];
info!("data: {:x}", data);
class.write_packet(data).await?;
}
}

View file

@ -0,0 +1,41 @@
#![no_std]
#![no_main]
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::gpio::{Level, Output, Speed};
use embassy_stm32::wdg::IndependentWatchdog;
use embassy_time::Timer;
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
info!("Hello World!");
let mut led = Output::new(p.PA5, Level::High, Speed::Low);
let mut wdt = IndependentWatchdog::new(p.IWDG, 1_000_000);
wdt.unleash();
let mut i = 0;
loop {
info!("high");
led.set_high();
Timer::after_millis(300).await;
info!("low");
led.set_low();
Timer::after_millis(300).await;
// Pet watchdog for 5 iterations and then stop.
// MCU should restart in 1 second after the last pet.
if i < 5 {
info!("Petting watchdog");
wdt.pet();
}
i += 1;
}
}

View file

@ -1,6 +1,6 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# replace STM32WB55CCUx with your chip as listed in `probe-rs chip list`
# runner = "probe-run --chip STM32WB55RGVx --speed 1000 --connect-under-reset"
# runner = "probe-rs run --chip STM32WB55RGVx --speed 1000 --connect-under-reset"
runner = "teleprobe local run --chip STM32WB55RG --elf"
[build]