Merge branch 'embassy-rs:main' into cryp

This commit is contained in:
Caleb Garrett 2024-02-29 15:21:06 -05:00 committed by GitHub
commit 998532c33e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 716 additions and 247 deletions

View File

@ -17,15 +17,6 @@ The first thing youll notice are two attributes at the top of the file. These
include::example$basic/src/main.rs[lines="1..2"]
----
=== Rust Nightly
The next declaration is a Rust Unstable feature, which means that Embassy requires Rust Nightly:
[source,rust]
----
include::example$basic/src/main.rs[lines="3"]
----
=== Dealing with errors
Then, what follows are some declarations on how to deal with panics and faults. During development, a good practice is to rely on `defmt-rtt` and `panic-probe` to print diagnostics to the terminal:
@ -41,7 +32,7 @@ After a bit of import declaration, the tasks run by the application should be de
[source,rust]
----
include::example$basic/src/main.rs[lines="12..20"]
include::example$basic/src/main.rs[lines="10..18"]
----
An embassy task must be declared `async`, and may NOT take generic arguments. In this case, we are handed the LED that should be blinked and the interval of the blinking.
@ -56,7 +47,7 @@ We then initialize the HAL with a default config, which gives us a `Peripherals`
[source,rust]
----
include::example$basic/src/main.rs[lines="22..-1"]
include::example$basic/src/main.rs[lines="20..-1"]
----
What happens when the `blinker` task has been spawned and main returns? Well, the main entry point is actually just like any other task, except that you can only have one and it takes some specific type arguments. The magic lies within the `#[embassy_executor::main]` macro. The macro does the following:

View File

@ -3,7 +3,7 @@
So you want to try Embassy, great! To get started, there are a few tools you need to install:
* link:https://rustup.rs/[rustup] - the Rust toolchain is needed to compile Rust code.
* link:https://crates.io/crates/probe-rs[probe-rs] - to flash the firmware on your device. If you already have other tools like `OpenOCD` setup, you can use that as well.
* link:https://probe.rs/[probe-rs] - to flash the firmware on your device. If you already have other tools like `OpenOCD` setup, you can use that as well.
If you don't have any supported board, don't worry: you can also run embassy on your PC using the `std` examples.
@ -82,19 +82,19 @@ If everything worked correctly, you should see a blinking LED on your board, and
└─ blinky::__embassy_main::task::{generator#0} @ src/bin/blinky.rs:27
----
NOTE: How does the `cargo run` command know how to connect to our board and program it? In each `examples` folder, theres a `.cargo/config.toml` file which tells cargo to use link:https://probe.rs/[probe-rs] as the runner for ARM binaries in that folder. probe-rs handles communication with the debug probe and MCU. In order for this to work, probe-rs needs to know which chip its programming, so youll have to edit this file if you want to run examples on other chips.
NOTE: How does the `+cargo run+` command know how to connect to our board and program it? In each `examples` folder, theres a `.cargo/config.toml` file which tells cargo to use link:https://probe.rs/[probe-rs] as the runner for ARM binaries in that folder. probe-rs handles communication with the debug probe and MCU. In order for this to work, probe-rs needs to know which chip its programming, so youll have to edit this file if you want to run examples on other chips.
=== It didnt work!
If you hare having issues when running `cargo run --release`, please check the following:
If you hare having issues when running `+cargo run --release+`, please check the following:
* You are specifying the correct `--chip` on the command line, OR
* You have set `.cargo/config.toml`'s run line to the correct chip, AND
* You have changed `examples/Cargo.toml`'s HAL (e.g. embassy-stm32) dependency's feature to use the correct chip (replace the existing stm32xxxx feature)
* You are specifying the correct `+--chip+` on the command line, OR
* You have set `+.cargo/config.toml+`s run line to the correct chip, AND
* You have changed `+examples/Cargo.toml+`s HAL (e.g. embassy-stm32) dependency's feature to use the correct chip (replace the existing stm32xxxx feature)
At this point the project should run. If you do not see a blinky LED for blinky, for example, be sure to check the code is toggling your board's LED pin.
If you are trying to run an example with `cargo run --release` and you see the following output:
If you are trying to run an example with `+cargo run --release+` and you see the following output:
[source]
----
0.000000 INFO Hello World!
@ -115,6 +115,22 @@ To get rid of the frame-index error add the following to your `Cargo.toml`:
debug = 2
----
If youre getting an extremely long error message containing something like the following:
[source]
----
error[E0463]: can't find crate for `std`
|
= note: the `thumbv6m-none-eabi` target may not support the standard library
= note: `std` is required by `stable_deref_trait` because it does not declare `#![no_std]`
----
Make sure that you didnt accidentally run `+cargo add probe-rs+` (which adds it as a dependency) instead of link:https://probe.rs/docs/getting-started/installation/[correctly installing probe-rs].
If youre using a raspberry pi pico-w, make sure youre running `+cargo run --bin wifi_blinky --release+` rather than the regular blinky. The pico-ws on-board LED is connected to the WiFi chip, which needs to be initialized before the LED can be blinked.
If youre using an rp2040 debug probe (e.g. the pico probe) and are having issues after running `probe-rs info`, unplug and reconnect the probe, letting it power cycle. Running `probe-rs info` is link:https://github.com/probe-rs/probe-rs/issues/1849[known to put the pico probe into an unusable state].
If youre still having problems, check the link:https://embassy.dev/book/dev/faq.html[FAQ], or ask for help in the link:https://matrix.to/#/#embassy-rs:matrix.org[Embassy Chat Room].
== What's next?
@ -124,3 +140,4 @@ Congratulations, you have your first Embassy application running! Here are some
* Read more about the xref:runtime.adoc[executor].
* Read more about the xref:hal.adoc[HAL].
* Start xref:basic_application.adoc[writing your application].
* Learn how to xref:new_project.adoc[start a new embassy project by adapting an example].

View File

@ -146,7 +146,7 @@ critical-section = "1.1"
rand_core = "0.6.3"
fixed = "1.10.0"
embedded-storage = "0.3.1"
embedded-storage-async = "0.4.0"
embedded-storage-async = "0.4.1"
cfg-if = "1.0.0"
document-features = "0.2.7"

View File

@ -173,6 +173,9 @@ embassy_hal_internal::peripherals! {
// I2S
I2S,
// Radio
RADIO,
}
impl_usb!(USBD, USBD, USBD);
@ -311,6 +314,8 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7);
impl_i2s!(I2S, I2S, I2S);
impl_radio!(RADIO, RADIO, RADIO);
embassy_hal_internal::interrupt_mod!(
POWER_CLOCK,
RADIO,

View File

@ -45,6 +45,11 @@ pub mod buffered_uarte;
pub mod gpio;
#[cfg(feature = "gpiote")]
pub mod gpiote;
// TODO: tested on other chips
#[cfg(any(feature = "nrf52840"))]
pub mod radio;
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
pub mod i2s;
pub mod nvmc;

View File

@ -0,0 +1,438 @@
//! Radio driver implementation focused on Bluetooth Low-Energy transmission.
use core::future::poll_fn;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_hal_internal::drop::OnDrop;
use embassy_hal_internal::{into_ref, PeripheralRef};
pub use pac::radio::mode::MODE_A as Mode;
use pac::radio::pcnf0::PLEN_A as PreambleLength;
use pac::radio::state::STATE_A as RadioState;
pub use pac::radio::txpower::TXPOWER_A as TxPower;
use crate::interrupt::typelevel::Interrupt;
use crate::radio::*;
use crate::util::slice_in_ram_or;
/// RADIO error.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
/// Buffer was too long.
BufferTooLong,
/// Buffer was to short.
BufferTooShort,
/// The buffer is not in data RAM. It is most likely in flash, and nRF's DMA cannot access flash.
BufferNotInRAM,
}
/// Radio driver.
pub struct Radio<'d, T: Instance> {
_p: PeripheralRef<'d, T>,
}
impl<'d, T: Instance> Radio<'d, T> {
/// Create a new radio driver.
pub fn new(
radio: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
) -> Self {
into_ref!(radio);
let r = T::regs();
r.pcnf1.write(|w| unsafe {
// It is 0 bytes long in a standard BLE packet
w.statlen()
.bits(0)
// MaxLen configures the maximum packet payload plus add-on size in
// number of bytes that can be transmitted or received by the RADIO. This feature can be used to ensure
// that the RADIO does not overwrite, or read beyond, the RAM assigned to the packet payload. This means
// that if the packet payload length defined by PCNF1.STATLEN and the LENGTH field in the packet specifies a
// packet larger than MAXLEN, the payload will be truncated at MAXLEN
//
// To simplify the implementation, It is setted as the maximum value
// and the length of the packet is controlled only by the LENGTH field in the packet
.maxlen()
.bits(255)
// Configure the length of the address field in the packet
// The prefix after the address fields is always appended, so is always 1 byte less than the size of the address
// The base address is truncated from the least significant byte if the BALEN is less than 4
//
// BLE address is always 4 bytes long
.balen()
.bits(3) // 3 bytes base address (+ 1 prefix);
// Configure the endianess
// For BLE is always little endian (LSB first)
.endian()
.little()
// Data whitening is used to avoid long sequences of zeros or
// ones, e.g., 0b0000000 or 0b1111111, in the data bit stream.
// The whitener and de-whitener are defined the same way,
// using a 7-bit linear feedback shift register with the
// polynomial x7 + x4 + 1.
//
// In BLE Whitening shall be applied on the PDU and CRC of all
// Link Layer packets and is performed after the CRC generation
// in the transmitter. No other parts of the packets are whitened.
// De-whitening is performed before the CRC checking in the receiver
// Before whitening or de-whitening, the shift register should be
// initialized based on the channel index.
.whiteen()
.set_bit()
});
// Configure CRC
r.crccnf.write(|w| {
// In BLE the CRC shall be calculated on the PDU of all Link Layer
// packets (even if the packet is encrypted).
// It skips the address field
w.skipaddr()
.skip()
// In BLE 24-bit CRC = 3 bytes
.len()
.three()
});
// Ch map between 2400 MHZ .. 2500 MHz
// All modes use this range
r.frequency.write(|w| w.map().default());
// Configure shortcuts to simplify and speed up sending and receiving packets.
r.shorts.write(|w| {
// start transmission/recv immediately after ramp-up
// disable radio when transmission/recv is done
w.ready_start().enabled().end_disable().enabled()
});
// Enable NVIC interrupt
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
Self { _p: radio }
}
fn state(&self) -> RadioState {
match T::regs().state.read().state().variant() {
Some(s) => s,
None => unreachable!(),
}
}
#[allow(dead_code)]
fn trace_state(&self) {
match self.state() {
RadioState::DISABLED => trace!("radio:state:DISABLED"),
RadioState::RX_RU => trace!("radio:state:RX_RU"),
RadioState::RX_IDLE => trace!("radio:state:RX_IDLE"),
RadioState::RX => trace!("radio:state:RX"),
RadioState::RX_DISABLE => trace!("radio:state:RX_DISABLE"),
RadioState::TX_RU => trace!("radio:state:TX_RU"),
RadioState::TX_IDLE => trace!("radio:state:TX_IDLE"),
RadioState::TX => trace!("radio:state:TX"),
RadioState::TX_DISABLE => trace!("radio:state:TX_DISABLE"),
}
}
/// Set the radio mode
///
/// The radio must be disabled before calling this function
pub fn set_mode(&mut self, mode: Mode) {
assert!(self.state() == RadioState::DISABLED);
let r = T::regs();
r.mode.write(|w| w.mode().variant(mode));
r.pcnf0.write(|w| {
w.plen().variant(match mode {
Mode::BLE_1MBIT => PreambleLength::_8BIT,
Mode::BLE_2MBIT => PreambleLength::_16BIT,
Mode::BLE_LR125KBIT | Mode::BLE_LR500KBIT => PreambleLength::LONG_RANGE,
_ => unimplemented!(),
})
});
}
/// Set the header size changing the S1's len field
///
/// The radio must be disabled before calling this function
pub fn set_header_expansion(&mut self, use_s1_field: bool) {
assert!(self.state() == RadioState::DISABLED);
let r = T::regs();
// s1 len in bits
let s1len: u8 = match use_s1_field {
false => 0,
true => 8,
};
r.pcnf0.write(|w| unsafe {
w
// Configure S0 to 1 byte length, this will represent the Data/Adv header flags
.s0len()
.set_bit()
// Configure the length (in bits) field to 1 byte length, this will represent the length of the payload
// and also be used to know how many bytes to read/write from/to the buffer
.lflen()
.bits(8)
// Configure the lengh (in bits) of bits in the S1 field. It could be used to represent the CTEInfo for data packages in BLE.
.s1len()
.bits(s1len)
});
}
/// Set initial data whitening value
/// Data whitening is used to avoid long sequences of zeros or ones, e.g., 0b0000000 or 0b1111111, in the data bit stream
/// On BLE the initial value is the channel index | 0x40
///
/// The radio must be disabled before calling this function
pub fn set_whitening_init(&mut self, whitening_init: u8) {
assert!(self.state() == RadioState::DISABLED);
let r = T::regs();
r.datawhiteiv.write(|w| unsafe { w.datawhiteiv().bits(whitening_init) });
}
/// Set the central frequency to be used
/// It should be in the range 2400..2500
///
/// [The radio must be disabled before calling this function](https://devzone.nordicsemi.com/f/nordic-q-a/15829/radio-frequency-change)
pub fn set_frequency(&mut self, frequency: u32) {
assert!(self.state() == RadioState::DISABLED);
assert!((2400..=2500).contains(&frequency));
let r = T::regs();
r.frequency
.write(|w| unsafe { w.frequency().bits((frequency - 2400) as u8) });
}
/// Set the acess address
/// This address is always constants for advertising
/// And a random value generate on each connection
/// It is used to filter the packages
///
/// The radio must be disabled before calling this function
pub fn set_access_address(&mut self, access_address: u32) {
assert!(self.state() == RadioState::DISABLED);
let r = T::regs();
// Configure logical address
// The byte ordering on air is always least significant byte first for the address
// So for the address 0xAA_BB_CC_DD, the address on air will be DD CC BB AA
// The package order is BASE, PREFIX so BASE=0xBB_CC_DD and PREFIX=0xAA
r.prefix0
.write(|w| unsafe { w.ap0().bits((access_address >> 24) as u8) });
// The base address is truncated from the least significant byte (because the BALEN is less than 4)
// So it shifts the address to the right
r.base0.write(|w| unsafe { w.bits(access_address << 8) });
// Don't match tx address
r.txaddress.write(|w| unsafe { w.txaddress().bits(0) });
// Match on logical address
// This config only filter the packets by the address,
// so only packages send to the previous address
// will finish the reception (TODO: check the explanation)
r.rxaddresses.write(|w| {
w.addr0()
.enabled()
.addr1()
.enabled()
.addr2()
.enabled()
.addr3()
.enabled()
.addr4()
.enabled()
});
}
/// Set the CRC polynomial
/// It only uses the 24 least significant bits
///
/// The radio must be disabled before calling this function
pub fn set_crc_poly(&mut self, crc_poly: u32) {
assert!(self.state() == RadioState::DISABLED);
let r = T::regs();
r.crcpoly.write(|w| unsafe {
// Configure the CRC polynomial
// Each term in the CRC polynomial is mapped to a bit in this
// register which index corresponds to the term's exponent.
// The least significant term/bit is hard-wired internally to
// 1, and bit number 0 of the register content is ignored by
// the hardware. The following example is for an 8 bit CRC
// polynomial: x8 + x7 + x3 + x2 + 1 = 1 1000 1101 .
w.crcpoly().bits(crc_poly & 0xFFFFFF)
});
}
/// Set the CRC init value
/// It only uses the 24 least significant bits
/// The CRC initial value varies depending of the PDU type
///
/// The radio must be disabled before calling this function
pub fn set_crc_init(&mut self, crc_init: u32) {
assert!(self.state() == RadioState::DISABLED);
let r = T::regs();
r.crcinit.write(|w| unsafe { w.crcinit().bits(crc_init & 0xFFFFFF) });
}
/// Set the radio tx power
///
/// The radio must be disabled before calling this function
pub fn set_tx_power(&mut self, tx_power: TxPower) {
assert!(self.state() == RadioState::DISABLED);
let r = T::regs();
r.txpower.write(|w| w.txpower().variant(tx_power));
}
/// Set buffer to read/write
///
/// This method is unsound. You should guarantee that the buffer will live
/// for the life time of the transmission or if the buffer will be modified.
/// Also if the buffer is smaller than the packet length, the radio will
/// read/write memory out of the buffer bounds.
fn set_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> {
slice_in_ram_or(buffer, Error::BufferNotInRAM)?;
let r = T::regs();
// Here it consider that the length of the packet is
// correctly set in the buffer, otherwise it will send
// unowned regions of memory
let ptr = buffer.as_ptr();
// Configure the payload
r.packetptr.write(|w| unsafe { w.bits(ptr as u32) });
Ok(())
}
/// Send packet
/// If the length byte in the package is greater than the buffer length
/// the radio will read memory out of the buffer bounds
pub async fn transmit(&mut self, buffer: &[u8]) -> Result<(), Error> {
self.set_buffer(buffer)?;
let r = T::regs();
self.trigger_and_wait_end(move || {
// Initialize the transmission
// trace!("txen");
r.tasks_txen.write(|w| w.tasks_txen().set_bit());
})
.await;
Ok(())
}
/// Receive packet
/// If the length byte in the received package is greater than the buffer length
/// the radio will write memory out of the buffer bounds
pub async fn receive(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
self.set_buffer(buffer)?;
let r = T::regs();
self.trigger_and_wait_end(move || {
// Initialize the transmission
// trace!("rxen");
r.tasks_rxen.write(|w| w.tasks_rxen().set_bit());
})
.await;
Ok(())
}
async fn trigger_and_wait_end(&mut self, trigger: impl FnOnce()) {
//self.trace_state();
let r = T::regs();
let s = T::state();
// If the Future is dropped before the end of the transmission
// it disable the interrupt and stop the transmission
// to keep the state consistent
let drop = OnDrop::new(|| {
trace!("radio drop: stopping");
r.intenclr.write(|w| w.end().clear());
r.events_end.reset();
r.tasks_stop.write(|w| w.tasks_stop().set_bit());
// The docs don't explicitly mention any event to acknowledge the stop task
while r.events_end.read().events_end().bit_is_clear() {}
trace!("radio drop: stopped");
});
// trace!("radio:enable interrupt");
// Clear some remnant side-effects (TODO: check if this is necessary)
r.events_end.reset();
// Enable interrupt
r.intenset.write(|w| w.end().set());
compiler_fence(Ordering::SeqCst);
// Trigger the transmission
trigger();
// self.trace_state();
// On poll check if interrupt happen
poll_fn(|cx| {
s.end_waker.register(cx.waker());
if r.events_end.read().events_end().bit_is_set() {
// trace!("radio:end");
return core::task::Poll::Ready(());
}
Poll::Pending
})
.await;
compiler_fence(Ordering::SeqCst);
r.events_disabled.reset(); // ACK
// Everthing ends fine, so it disable the drop
drop.defuse();
}
/// Disable the radio
fn disable(&mut self) {
let r = T::regs();
compiler_fence(Ordering::SeqCst);
// If it is already disabled, do nothing
if self.state() != RadioState::DISABLED {
trace!("radio:disable");
// Trigger the disable task
r.tasks_disable.write(|w| w.tasks_disable().set_bit());
// Wait until the radio is disabled
while r.events_disabled.read().events_disabled().bit_is_clear() {}
compiler_fence(Ordering::SeqCst);
// Acknowledge it
r.events_disabled.reset();
}
}
}
impl<'d, T: Instance> Drop for Radio<'d, T> {
fn drop(&mut self) {
self.disable();
}
}

View File

@ -0,0 +1,75 @@
//! Integrated 2.4 GHz Radio
//!
//! The 2.4 GHz radio transceiver is compatible with multiple radio standards
//! such as 1Mbps, 2Mbps and Long Range Bluetooth Low Energy.
#![macro_use]
/// Bluetooth Low Energy Radio driver.
pub mod ble;
use core::marker::PhantomData;
use crate::{interrupt, pac, Peripheral};
/// Interrupt handler
pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() {
let r = T::regs();
let s = T::state();
if r.events_end.read().events_end().bit_is_set() {
s.end_waker.wake();
r.intenclr.write(|w| w.end().clear());
}
}
}
pub(crate) mod sealed {
use embassy_sync::waitqueue::AtomicWaker;
pub struct State {
/// end packet transmission or reception
pub end_waker: AtomicWaker,
}
impl State {
pub const fn new() -> Self {
Self {
end_waker: AtomicWaker::new(),
}
}
}
pub trait Instance {
fn regs() -> &'static crate::pac::radio::RegisterBlock;
fn state() -> &'static State;
}
}
macro_rules! impl_radio {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::radio::sealed::Instance for peripherals::$type {
fn regs() -> &'static pac::radio::RegisterBlock {
unsafe { &*pac::$pac_type::ptr() }
}
fn state() -> &'static crate::radio::sealed::State {
static STATE: crate::radio::sealed::State = crate::radio::sealed::State::new();
&STATE
}
}
impl crate::radio::Instance for peripherals::$type {
type Interrupt = crate::interrupt::typelevel::$irq;
}
};
}
/// Radio peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}

View File

@ -312,7 +312,7 @@ impl<'d, T: Instance> Spim<'d, T> {
match self.blocking_inner_from_ram(rx, tx) {
Ok(_) => Ok(()),
Err(Error::BufferNotInRAM) => {
trace!("Copying SPIM tx buffer into RAM for DMA");
// trace!("Copying SPIM tx buffer into RAM for DMA");
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()];
tx_ram_buf.copy_from_slice(tx);
self.blocking_inner_from_ram(rx, tx_ram_buf)
@ -366,7 +366,7 @@ impl<'d, T: Instance> Spim<'d, T> {
match self.async_inner_from_ram(rx, tx).await {
Ok(_) => Ok(()),
Err(Error::BufferNotInRAM) => {
trace!("Copying SPIM tx buffer into RAM for DMA");
// trace!("Copying SPIM tx buffer into RAM for DMA");
let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..tx.len()];
tx_ram_buf.copy_from_slice(tx);
self.async_inner_from_ram(rx, tx_ram_buf).await

View File

@ -70,7 +70,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-7c8b53413499acc3273b706318777a60f932d77a" }
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-4a0bcec33362449fb733c066936d25cbabab396a" }
vcell = "0.1.3"
bxcan = "0.7.0"
nb = "1.0.0"
@ -94,7 +94,7 @@ critical-section = { version = "1.1", features = ["std"] }
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-7c8b53413499acc3273b706318777a60f932d77a", default-features = false, features = ["metadata"]}
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-4a0bcec33362449fb733c066936d25cbabab396a", default-features = false, features = ["metadata"]}
[features]
@ -130,6 +130,8 @@ _time-driver = ["dep:embassy-time-driver", "time"]
## Use any time driver
time-driver-any = ["_time-driver"]
## Use TIM1 as time driver
time-driver-tim1 = ["_time-driver"]
## Use TIM2 as time driver
time-driver-tim2 = ["_time-driver"]
## Use TIM3 as time driver
@ -138,18 +140,24 @@ time-driver-tim3 = ["_time-driver"]
time-driver-tim4 = ["_time-driver"]
## Use TIM5 as time driver
time-driver-tim5 = ["_time-driver"]
## Use TIM8 as time driver
time-driver-tim8 = ["_time-driver"]
## Use TIM9 as time driver
time-driver-tim9 = ["_time-driver"]
## Use TIM11 as time driver
time-driver-tim11 = ["_time-driver"]
## Use TIM12 as time driver
time-driver-tim12 = ["_time-driver"]
## Use TIM15 as time driver
time-driver-tim15 = ["_time-driver"]
## Use TIM20 as time driver
time-driver-tim20 = ["_time-driver"]
## Use TIM21 as time driver
time-driver-tim21 = ["_time-driver"]
## Use TIM22 as time driver
time-driver-tim22 = ["_time-driver"]
## Use TIM23 as time driver
time-driver-tim23 = ["_time-driver"]
## Use TIM24 as time driver
time-driver-tim24 = ["_time-driver"]
#! ## Analog Switch Pins (Pxy_C) on STM32H7 series

View File

@ -37,7 +37,7 @@ impl Registers {
&mut self.msg_ram_mut().receive[fifonr].fxsa[bufnum]
}
pub fn read_classic(&self, fifonr: usize) -> Option<(ClassicFrame, u16)> {
pub fn read<F: CanHeader>(&self, fifonr: usize) -> Option<(F, u16)> {
// Fill level - do we have a msg?
if self.regs.rxfs(fifonr).read().ffl() < 1 {
return None;
@ -46,7 +46,7 @@ impl Registers {
let read_idx = self.regs.rxfs(fifonr).read().fgi();
let mailbox = self.rx_fifo_element(fifonr, read_idx as usize);
let mut buffer: [u8; 8] = [0; 8];
let mut buffer = [0u8; 64];
let maybe_header = extract_frame(mailbox, &mut buffer);
// Clear FIFO, reduces count and increments read buf
@ -54,43 +54,14 @@ impl Registers {
match maybe_header {
Some((header, ts)) => {
let data = ClassicData::new(&buffer[0..header.len() as usize]);
Some((ClassicFrame::new(header, data.unwrap()), ts))
}
None => None,
}
}
pub fn read_fd(&self, fifonr: usize) -> Option<(FdFrame, u16)> {
// Fill level - do we have a msg?
if self.regs.rxfs(fifonr).read().ffl() < 1 {
return None;
}
let read_idx = self.regs.rxfs(fifonr).read().fgi();
let mailbox = self.rx_fifo_element(fifonr, read_idx as usize);
let mut buffer: [u8; 64] = [0; 64];
let maybe_header = extract_frame(mailbox, &mut buffer);
// Clear FIFO, reduces count and increments read buf
self.regs.rxfa(fifonr).modify(|w| w.set_fai(read_idx));
match maybe_header {
Some((header, ts)) => {
let data = FdData::new(&buffer[0..header.len() as usize]);
Some((FdFrame::new(header, data.unwrap()), ts))
let data = &buffer[0..header.len() as usize];
Some((F::from_header(header, data)?, ts))
}
None => None,
}
}
pub fn put_tx_frame(&self, bufidx: usize, header: &Header, buffer: &[u8]) {
// Fill level - do we have a msg?
//if self.regs.rxfs(fifonr).read().ffl() < 1 { return None; }
//let read_idx = self.regs.rxfs(fifonr).read().fgi();
let mailbox = self.tx_buffer_element(bufidx);
mailbox.reset();
@ -193,11 +164,7 @@ impl Registers {
}
#[inline]
//fn abort_pending_mailbox<PTX, R>(&mut self, idx: Mailbox, pending: PTX) -> Option<R>
pub fn abort_pending_mailbox(&self, bufidx: usize) -> Option<ClassicFrame>
//where
// PTX: FnOnce(Mailbox, TxFrameHeader, &[u32]) -> R,
{
pub fn abort_pending_mailbox_generic<F: embedded_can::Frame>(&self, bufidx: usize) -> Option<F> {
if self.abort(bufidx) {
let mailbox = self.tx_buffer_element(bufidx);
@ -212,12 +179,14 @@ impl Registers {
return None;
}
//let tx_ram = self.tx_msg_ram();
let mut data = [0u8; 64];
data_from_tx_buffer(&mut data, mailbox, len as usize);
let cd = ClassicData::new(&data).unwrap();
Some(ClassicFrame::new(Header::new(id, len, header_reg.rtr().bit()), cd))
if header_reg.rtr().bit() {
F::new_remote(id, len as usize)
} else {
F::new(id, &data)
}
} else {
// Abort request failed because the frame was already sent (or being sent) on
// the bus. All mailboxes are now free. This can happen for small prescaler
@ -227,52 +196,7 @@ impl Registers {
}
}
#[inline]
//fn abort_pending_mailbox<PTX, R>(&mut self, idx: Mailbox, pending: PTX) -> Option<R>
pub fn abort_pending_fd_mailbox(&self, bufidx: usize) -> Option<FdFrame>
//where
// PTX: FnOnce(Mailbox, TxFrameHeader, &[u32]) -> R,
{
if self.abort(bufidx) {
let mailbox = self.tx_buffer_element(bufidx);
let header_reg = mailbox.header.read();
let id = make_id(header_reg.id().bits(), header_reg.xtd().bits());
let len = match header_reg.to_data_length() {
DataLength::Fdcan(len) => len,
DataLength::Classic(len) => len,
};
if len as usize > FdFrame::MAX_DATA_LEN {
return None;
}
//let tx_ram = self.tx_msg_ram();
let mut data = [0u8; 64];
data_from_tx_buffer(&mut data, mailbox, len as usize);
let cd = FdData::new(&data).unwrap();
let header = if header_reg.fdf().frame_format() == FrameFormat::Fdcan {
Header::new_fd(id, len, header_reg.rtr().bit(), header_reg.brs().bit())
} else {
Header::new(id, len, header_reg.rtr().bit())
};
Some(FdFrame::new(header, cd))
} else {
// Abort request failed because the frame was already sent (or being sent) on
// the bus. All mailboxes are now free. This can happen for small prescaler
// values (e.g. 1MBit/s bit timing with a source clock of 8MHz) or when an ISR
// has preempted the execution.
None
}
}
/// As Transmit, but if there is a pending frame, `pending` will be called so that the frame can
/// be preserved.
//pub fn transmit_preserve<PTX, P>(
pub fn write_classic(&self, frame: &ClassicFrame) -> nb::Result<Option<ClassicFrame>, Infallible> {
pub fn write<F: embedded_can::Frame + CanHeader>(&self, frame: &F) -> nb::Result<Option<F>, Infallible> {
let queue_is_full = self.tx_queue_is_full();
let id = frame.header().id();
@ -281,45 +205,11 @@ impl Registers {
// Discard the first slot with a lower priority message
let (idx, pending_frame) = if queue_is_full {
if self.is_available(0, id) {
(0, self.abort_pending_mailbox(0))
(0, self.abort_pending_mailbox_generic(0))
} else if self.is_available(1, id) {
(1, self.abort_pending_mailbox(1))
(1, self.abort_pending_mailbox_generic(1))
} else if self.is_available(2, id) {
(2, self.abort_pending_mailbox(2))
} else {
// For now we bail when there is no lower priority slot available
// Can this lead to priority inversion?
return Err(nb::Error::WouldBlock);
}
} else {
// Read the Write Pointer
let idx = self.regs.txfqs().read().tfqpi();
(idx, None)
};
self.put_tx_frame(idx as usize, frame.header(), frame.data());
Ok(pending_frame)
}
/// As Transmit, but if there is a pending frame, `pending` will be called so that the frame can
/// be preserved.
//pub fn transmit_preserve<PTX, P>(
pub fn write_fd(&self, frame: &FdFrame) -> nb::Result<Option<FdFrame>, Infallible> {
let queue_is_full = self.tx_queue_is_full();
let id = frame.header().id();
// If the queue is full,
// Discard the first slot with a lower priority message
let (idx, pending_frame) = if queue_is_full {
if self.is_available(0, id) {
(0, self.abort_pending_fd_mailbox(0))
} else if self.is_available(1, id) {
(1, self.abort_pending_fd_mailbox(1))
} else if self.is_available(2, id) {
(2, self.abort_pending_fd_mailbox(2))
(2, self.abort_pending_mailbox_generic(2))
} else {
// For now we bail when there is no lower priority slot available
// Can this lead to priority inversion?
@ -413,7 +303,9 @@ impl Registers {
// Framework specific settings are set here
// set TxBuffer to Queue Mode
self.regs.txbc().write(|w| w.set_tfqm(true));
self.regs
.txbc()
.write(|w| w.set_tfqm(crate::pac::can::vals::Tfqm::QUEUE));
// set standard filters list size to 28
// set extended filters list size to 8
@ -557,8 +449,6 @@ impl Registers {
/// parameter to this method.
#[inline]
pub fn set_nominal_bit_timing(&mut self, btr: NominalBitTiming) {
//self.control.config.nbtr = btr;
self.regs.nbtp().write(|w| {
w.set_nbrp(btr.nbrp() - 1);
w.set_ntseg1(btr.ntseg1() - 1);
@ -571,8 +461,6 @@ impl Registers {
/// This is not used when frame_transmit is set to anything other than AllowFdCanAndBRS.
#[inline]
pub fn set_data_bit_timing(&mut self, btr: DataBitTiming) {
//self.control.config.dbtr = btr;
self.regs.dbtp().write(|w| {
w.set_dbrp(btr.dbrp() - 1);
w.set_dtseg1(btr.dtseg1() - 1);
@ -590,7 +478,6 @@ impl Registers {
#[inline]
pub fn set_automatic_retransmit(&mut self, enabled: bool) {
self.regs.cccr().modify(|w| w.set_dar(!enabled));
//self.control.config.automatic_retransmit = enabled;
}
/// Configures the transmit pause feature. See
@ -598,21 +485,18 @@ impl Registers {
#[inline]
pub fn set_transmit_pause(&mut self, enabled: bool) {
self.regs.cccr().modify(|w| w.set_txp(!enabled));
//self.control.config.transmit_pause = enabled;
}
/// Configures non-iso mode. See [`FdCanConfig::set_non_iso_mode`]
#[inline]
pub fn set_non_iso_mode(&mut self, enabled: bool) {
self.regs.cccr().modify(|w| w.set_niso(enabled));
//self.control.config.non_iso_mode = enabled;
}
/// Configures edge filtering. See [`FdCanConfig::set_edge_filtering`]
#[inline]
pub fn set_edge_filtering(&mut self, enabled: bool) {
self.regs.cccr().modify(|w| w.set_efbi(enabled));
//self.control.config.edge_filtering = enabled;
}
/// Configures frame transmission mode. See
@ -632,16 +516,12 @@ impl Registers {
#[cfg(not(stm32h7))]
w.set_brse(brse);
});
//self.control.config.frame_transmit = fts;
}
/// Sets the protocol exception handling on/off
#[inline]
pub fn set_protocol_exception_handling(&mut self, enabled: bool) {
self.regs.cccr().modify(|w| w.set_pxhd(!enabled));
//self.control.config.protocol_exception_handling = enabled;
}
/// Configures and resets the timestamp counter
@ -665,8 +545,6 @@ impl Registers {
w.set_tcp(tcp);
w.set_tss(tss);
});
//self.control.config.timestamp_source = select;
}
#[cfg(not(stm32h7))]
@ -721,13 +599,15 @@ fn make_id(id: u32, extended: bool) -> embedded_can::Id {
if extended {
embedded_can::Id::from(unsafe { embedded_can::ExtendedId::new_unchecked(id & 0x1FFFFFFF) })
} else {
embedded_can::Id::from(unsafe { embedded_can::StandardId::new_unchecked((id & 0x000007FF) as u16) })
// A standard identifier is stored into ID[28:18].
embedded_can::Id::from(unsafe { embedded_can::StandardId::new_unchecked(((id >> 18) & 0x000007FF) as u16) })
}
}
fn put_tx_header(mailbox: &mut TxBufferElement, header: &Header) {
let (id, id_type) = match header.id() {
embedded_can::Id::Standard(id) => (id.as_raw() as u32, IdType::StandardId),
// A standard identifier has to be written to ID[28:18].
embedded_can::Id::Standard(id) => ((id.as_raw() as u32) << 18, IdType::StandardId),
embedded_can::Id::Extended(id) => (id.as_raw() as u32, IdType::ExtendedId),
};
@ -737,7 +617,7 @@ fn put_tx_header(mailbox: &mut TxBufferElement, header: &Header) {
} else {
FrameFormat::Classic
};
let brs = header.len() > 8 || header.bit_rate_switching();
let brs = (frame_format == FrameFormat::Fdcan) && header.bit_rate_switching();
mailbox.header.write(|w| {
unsafe { w.id().bits(id) }
@ -792,22 +672,6 @@ fn data_from_tx_buffer(buffer: &mut [u8], mailbox: &TxBufferElement, len: usize)
}
}
impl From<&RxFifoElement> for ClassicFrame {
fn from(mailbox: &RxFifoElement) -> Self {
let header_reg = mailbox.header.read();
let id = make_id(header_reg.id().bits(), header_reg.xtd().bits());
let dlc = header_reg.to_data_length().len();
let len = dlc as usize;
let mut buffer: [u8; 64] = [0; 64];
data_from_fifo(&mut buffer, mailbox, len);
let data = ClassicData::new(&buffer[0..len]);
let header = Header::new(id, dlc, header_reg.rtr().bits());
ClassicFrame::new(header, data.unwrap())
}
}
fn extract_frame(mailbox: &RxFifoElement, buffer: &mut [u8]) -> Option<(Header, u16)> {
let header_reg = mailbox.header.read();

View File

@ -58,7 +58,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::IT0Interrupt> for IT0Interrup
if !T::registers().tx_queue_is_full() {
match buf.tx_receiver.try_receive() {
Ok(frame) => {
_ = T::registers().write_classic(&frame);
_ = T::registers().write(&frame);
}
Err(_) => {}
}
@ -68,7 +68,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::IT0Interrupt> for IT0Interrup
if !T::registers().tx_queue_is_full() {
match buf.tx_receiver.try_receive() {
Ok(frame) => {
_ = T::registers().write_fd(&frame);
_ = T::registers().write(&frame);
}
Err(_) => {}
}
@ -359,7 +359,7 @@ impl<'d, T: Instance> Fdcan<'d, T> {
/// Returns the next received message frame
pub async fn read(&mut self) -> Result<(ClassicFrame, Timestamp), BusError> {
T::state().rx_mode.read::<T>().await
T::state().rx_mode.read_classic::<T>().await
}
/// Queues the message to be sent but exerts backpressure. If a lower-priority
@ -633,7 +633,7 @@ impl<'c, 'd, T: Instance> FdcanTx<'d, T> {
impl<'c, 'd, T: Instance> FdcanRx<'d, T> {
/// Returns the next received message frame
pub async fn read(&mut self) -> Result<(ClassicFrame, Timestamp), BusError> {
T::state().rx_mode.read::<T>().await
T::state().rx_mode.read_classic::<T>().await
}
/// Returns the next received message frame
@ -649,6 +649,7 @@ pub(crate) mod sealed {
use embassy_sync::channel::{DynamicReceiver, DynamicSender};
use embassy_sync::waitqueue::AtomicWaker;
use super::CanHeader;
use crate::can::_version::{BusError, Timestamp};
use crate::can::frame::{ClassicFrame, FdFrame};
@ -689,13 +690,13 @@ pub(crate) mod sealed {
waker.wake();
}
RxMode::ClassicBuffered(buf) => {
if let Some(r) = T::registers().read_classic(fifonr) {
if let Some(r) = T::registers().read(fifonr) {
let ts = T::calc_timestamp(T::state().ns_per_timer_tick, r.1);
let _ = buf.rx_sender.try_send((r.0, ts));
}
}
RxMode::FdBuffered(buf) => {
if let Some(r) = T::registers().read_fd(fifonr) {
if let Some(r) = T::registers().read(fifonr) {
let ts = T::calc_timestamp(T::state().ns_per_timer_tick, r.1);
let _ = buf.rx_sender.try_send((r.0, ts));
}
@ -703,15 +704,15 @@ pub(crate) mod sealed {
}
}
pub async fn read<T: Instance>(&self) -> Result<(ClassicFrame, Timestamp), BusError> {
async fn read<T: Instance, F: CanHeader>(&self) -> Result<(F, Timestamp), BusError> {
poll_fn(|cx| {
T::state().err_waker.register(cx.waker());
self.register(cx.waker());
if let Some((msg, ts)) = T::registers().read_classic(0) {
if let Some((msg, ts)) = T::registers().read(0) {
let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts);
return Poll::Ready(Ok((msg, ts)));
} else if let Some((msg, ts)) = T::registers().read_classic(1) {
} else if let Some((msg, ts)) = T::registers().read(1) {
let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts);
return Poll::Ready(Ok((msg, ts)));
} else if let Some(err) = T::registers().curr_error() {
@ -723,24 +724,12 @@ pub(crate) mod sealed {
.await
}
pub async fn read_fd<T: Instance>(&self) -> Result<(FdFrame, Timestamp), BusError> {
poll_fn(|cx| {
T::state().err_waker.register(cx.waker());
self.register(cx.waker());
pub async fn read_classic<T: Instance>(&self) -> Result<(ClassicFrame, Timestamp), BusError> {
self.read::<T, _>().await
}
if let Some((msg, ts)) = T::registers().read_fd(0) {
let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts);
return Poll::Ready(Ok((msg, ts)));
} else if let Some((msg, ts)) = T::registers().read_fd(1) {
let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts);
return Poll::Ready(Ok((msg, ts)));
} else if let Some(err) = T::registers().curr_error() {
// TODO: this is probably wrong
return Poll::Ready(Err(err));
}
Poll::Pending
})
.await
pub async fn read_fd<T: Instance>(&self) -> Result<(FdFrame, Timestamp), BusError> {
self.read::<T, _>().await
}
}
@ -766,11 +755,11 @@ pub(crate) mod sealed {
/// frame is dropped from the mailbox, it is returned. If no lower-priority frames
/// can be replaced, this call asynchronously waits for a frame to be successfully
/// transmitted, then tries again.
pub async fn write<T: Instance>(&self, frame: &ClassicFrame) -> Option<ClassicFrame> {
async fn write_generic<T: Instance, F: embedded_can::Frame + CanHeader>(&self, frame: &F) -> Option<F> {
poll_fn(|cx| {
self.register(cx.waker());
if let Ok(dropped) = T::registers().write_classic(frame) {
if let Ok(dropped) = T::registers().write(frame) {
return Poll::Ready(dropped);
}
@ -781,23 +770,20 @@ pub(crate) mod sealed {
.await
}
/// Queues the message to be sent but exerts backpressure. If a lower-priority
/// frame is dropped from the mailbox, it is returned. If no lower-priority frames
/// can be replaced, this call asynchronously waits for a frame to be successfully
/// transmitted, then tries again.
pub async fn write<T: Instance>(&self, frame: &ClassicFrame) -> Option<ClassicFrame> {
self.write_generic::<T, _>(frame).await
}
/// Queues the message to be sent but exerts backpressure. If a lower-priority
/// frame is dropped from the mailbox, it is returned. If no lower-priority frames
/// can be replaced, this call asynchronously waits for a frame to be successfully
/// transmitted, then tries again.
pub async fn write_fd<T: Instance>(&self, frame: &FdFrame) -> Option<FdFrame> {
poll_fn(|cx| {
self.register(cx.waker());
if let Ok(dropped) = T::registers().write_fd(frame) {
return Poll::Ready(dropped);
}
// Couldn't replace any lower priority frames. Need to wait for some mailboxes
// to clear.
Poll::Pending
})
.await
self.write_generic::<T, _>(frame).await
}
}

View File

@ -56,6 +56,16 @@ impl Header {
}
}
/// Trait for FDCAN frame types, providing ability to construct from a Header
/// and to retrieve the Header from a frame
pub trait CanHeader: Sized {
/// Construct frame from header and payload
fn from_header(header: Header, data: &[u8]) -> Option<Self>;
/// Get this frame's header struct
fn header(&self) -> &Header;
}
/// Payload of a classic CAN data frame.
///
/// Contains 0 to 8 Bytes of data.
@ -213,6 +223,16 @@ impl embedded_can::Frame for ClassicFrame {
}
}
impl CanHeader for ClassicFrame {
fn from_header(header: Header, data: &[u8]) -> Option<Self> {
Some(Self::new(header, ClassicData::new(data)?))
}
fn header(&self) -> &Header {
self.header()
}
}
/// Payload of a (FD)CAN data frame.
///
/// Contains 0 to 64 Bytes of data.
@ -272,8 +292,6 @@ pub struct FdFrame {
}
impl FdFrame {
pub(crate) const MAX_DATA_LEN: usize = 64;
/// Create a new CAN classic Frame
pub fn new(can_header: Header, data: FdData) -> FdFrame {
FdFrame { can_header, data }
@ -368,3 +386,13 @@ impl embedded_can::Frame for FdFrame {
&self.data.raw()
}
}
impl CanHeader for FdFrame {
fn from_header(header: Header, data: &[u8]) -> Option<Self> {
Some(Self::new(header, FdData::new(data)?))
}
fn header(&self) -> &Header {
self.header()
}
}

View File

@ -90,6 +90,7 @@ impl<'d, T: Instance> OpAmp<'d, T> {
in_pin.set_as_analog();
out_pin.set_as_analog();
// PGA_GAIN value may have different meaning in different MCU serials, use with caution.
let (vm_sel, pga_gain) = match gain {
OpAmpGain::Mul1 => (0b11, 0b00),
OpAmpGain::Mul2 => (0b10, 0b00),
@ -127,6 +128,7 @@ impl<'d, T: Instance> OpAmp<'d, T> {
into_ref!(pin);
pin.set_as_analog();
// PGA_GAIN value may have different meaning in different MCU serials, use with caution.
let (vm_sel, pga_gain) = match gain {
OpAmpGain::Mul1 => (0b11, 0b00),
OpAmpGain::Mul2 => (0b10, 0b00),

View File

@ -557,6 +557,9 @@ pub(crate) unsafe fn init(config: Config) {
RCC.d3ccipr().modify(|w| {
w.set_adcsel(config.adc_clock_source);
});
RCC.d2ccip1r().modify(|w| {
w.set_fdcansel(config.fdcan_clock_source);
});
}
#[cfg(stm32h5)]
{

View File

@ -16,6 +16,8 @@ use crate::pac::timer::vals;
use crate::rcc::sealed::RccPeripheral;
#[cfg(feature = "low-power")]
use crate::rtc::Rtc;
#[cfg(any(time_driver_tim1, time_driver_tim8, time_driver_tim20))]
use crate::timer::sealed::AdvancedControlInstance;
use crate::timer::sealed::{CoreInstance, GeneralPurpose16bitInstance as Instance};
use crate::{interrupt, peripherals};
@ -38,7 +40,7 @@ cfg_if::cfg_if! {
}
}
#[cfg(time_drvier_tim1)]
#[cfg(time_driver_tim1)]
type T = peripherals::TIM1;
#[cfg(time_driver_tim2)]
type T = peripherals::TIM2;
@ -52,8 +54,6 @@ type T = peripherals::TIM5;
type T = peripherals::TIM8;
#[cfg(time_driver_tim9)]
type T = peripherals::TIM9;
#[cfg(time_driver_tim11)]
type T = peripherals::TIM11;
#[cfg(time_driver_tim12)]
type T = peripherals::TIM12;
#[cfg(time_driver_tim15)]
@ -78,6 +78,14 @@ foreach_interrupt! {
DRIVER.on_interrupt()
}
};
(TIM1, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim1)]
#[cfg(feature = "rt")]
#[interrupt]
fn $irq() {
DRIVER.on_interrupt()
}
};
(TIM2, timer, $block:ident, UP, $irq:ident) => {
#[cfg(time_driver_tim2)]
#[cfg(feature = "rt")]
@ -118,6 +126,14 @@ foreach_interrupt! {
DRIVER.on_interrupt()
}
};
(TIM8, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim8)]
#[cfg(feature = "rt")]
#[interrupt]
fn $irq() {
DRIVER.on_interrupt()
}
};
(TIM9, timer, $block:ident, UP, $irq:ident) => {
#[cfg(time_driver_tim9)]
#[cfg(feature = "rt")]
@ -150,6 +166,14 @@ foreach_interrupt! {
DRIVER.on_interrupt()
}
};
(TIM20, timer, $block:ident, CC, $irq:ident) => {
#[cfg(time_driver_tim20)]
#[cfg(feature = "rt")]
#[interrupt]
fn $irq() {
DRIVER.on_interrupt()
}
};
(TIM21, timer, $block:ident, UP, $irq:ident) => {
#[cfg(time_driver_tim21)]
#[cfg(feature = "rt")]
@ -283,6 +307,14 @@ impl RtcDriver {
<T as CoreInstance>::Interrupt::unpend();
unsafe { <T as CoreInstance>::Interrupt::enable() };
#[cfg(any(time_driver_tim1, time_driver_tim8, time_driver_tim20))]
{
<T as AdvancedControlInstance>::CaptureCompareInterrupt::unpend();
unsafe {
<T as AdvancedControlInstance>::CaptureCompareInterrupt::enable();
}
}
r.cr1().modify(|w| w.set_cen(true));
}

View File

@ -379,7 +379,7 @@ pub(crate) mod sealed {
let regs = Self::regs_gp32();
regs.psc().write(|r| r.set_psc(psc));
regs.arr().write(|r| r.set_arr(arr));
regs.arr().write_value(arr);
regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY));
regs.egr().write(|r| r.set_ug(true));
@ -391,7 +391,7 @@ pub(crate) mod sealed {
let timer_f = Self::frequency();
let regs = Self::regs_gp32();
let arr = regs.arr().read().arr();
let arr = regs.arr().read();
let psc = regs.psc().read().psc();
timer_f / arr / (psc + 1)
@ -399,22 +399,22 @@ pub(crate) mod sealed {
/// Set comapre value for a channel.
fn set_compare_value(&self, channel: Channel, value: u32) {
Self::regs_gp32().ccr(channel.index()).modify(|w| w.set_ccr(value));
Self::regs_gp32().ccr(channel.index()).write_value(value);
}
/// Get capture value for a channel.
fn get_capture_value(&self, channel: Channel) -> u32 {
Self::regs_gp32().ccr(channel.index()).read().ccr()
Self::regs_gp32().ccr(channel.index()).read()
}
/// Get max compare value. This depends on the timer frequency and the clock frequency from RCC.
fn get_max_compare_value(&self) -> u32 {
Self::regs_gp32().arr().read().arr()
Self::regs_gp32().arr().read()
}
/// Get compare value for a channel.
fn get_compare_value(&self, channel: Channel) -> u32 {
Self::regs_gp32().ccr(channel.index()).read().ccr()
Self::regs_gp32().ccr(channel.index()).read()
}
}
@ -464,6 +464,9 @@ pub(crate) mod sealed {
pub trait AdvancedControlInstance:
GeneralPurpose2ChannelComplementaryInstance + GeneralPurpose16bitInstance
{
/// Capture compare interrupt for this timer.
type CaptureCompareInterrupt: interrupt::typelevel::Interrupt;
/// Get access to the advanced timer registers.
fn regs_advanced() -> crate::pac::timer::TimAdv;
@ -831,8 +834,10 @@ macro_rules! impl_2ch_cmp_timer {
#[allow(unused)]
macro_rules! impl_adv_timer {
($inst:ident) => {
($inst:ident, $irq:ident) => {
impl sealed::AdvancedControlInstance for crate::peripherals::$inst {
type CaptureCompareInterrupt = crate::interrupt::typelevel::$irq;
fn regs_advanced() -> crate::pac::timer::TimAdv {
unsafe { crate::pac::timer::TimAdv::from_ptr(crate::pac::$inst.as_ptr()) }
}
@ -905,11 +910,13 @@ foreach_interrupt! {
impl_gp16_timer!($inst);
impl_1ch_cmp_timer!($inst);
impl_2ch_cmp_timer!($inst);
impl_adv_timer!($inst);
impl BasicInstance for crate::peripherals::$inst {}
impl CaptureCompare16bitInstance for crate::peripherals::$inst {}
impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {}
};
($inst:ident, timer, TIM_1CH_CMP, CC, $irq:ident) => {
impl_adv_timer!($inst, $irq);
};
($inst:ident, timer, TIM_2CH_CMP, UP, $irq:ident) => {
@ -921,11 +928,13 @@ foreach_interrupt! {
impl_gp16_timer!($inst);
impl_1ch_cmp_timer!($inst);
impl_2ch_cmp_timer!($inst);
impl_adv_timer!($inst);
impl BasicInstance for crate::peripherals::$inst {}
impl CaptureCompare16bitInstance for crate::peripherals::$inst {}
impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {}
};
($inst:ident, timer, TIM_2CH_CMP, CC, $irq:ident) => {
impl_adv_timer!($inst, $irq);
};
($inst:ident, timer, TIM_ADV, UP, $irq:ident) => {
@ -937,11 +946,13 @@ foreach_interrupt! {
impl_gp16_timer!($inst);
impl_1ch_cmp_timer!($inst);
impl_2ch_cmp_timer!($inst);
impl_adv_timer!($inst);
impl BasicInstance for crate::peripherals::$inst {}
impl CaptureCompare16bitInstance for crate::peripherals::$inst {}
impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {}
};
($inst:ident, timer, TIM_ADV, CC, $irq:ident) => {
impl_adv_timer!($inst, $irq);
};
}
// Update Event trigger DMA for every timer

View File

@ -42,9 +42,13 @@ impl<'d, T: Instance> IndependentWatchdog<'d, T> {
// Prescaler value
let psc = 2u16.pow(psc_power);
#[cfg(not(iwdg_v3))]
assert!(psc <= 256, "IWDG prescaler should be no more than 256");
#[cfg(iwdg_v3)] // H5, U5, WBA
assert!(psc <= 1024, "IWDG prescaler should be no more than 1024");
// Convert prescaler power to PR register value
let pr = psc_power as u8 - 2;
assert!(pr <= 0b110);
// Reload value
let rl = reload_value(psc, timeout_us);

View File

@ -56,5 +56,5 @@ log = { version = "0.4.14", optional = true }
heapless = "0.8"
# for HID
usbd-hid = { version = "0.6.0", optional = true }
usbd-hid = { version = "0.7.0", optional = true }
ssmarshal = { version = "1.0", default-features = false, optional = true }

View File

@ -28,7 +28,7 @@ panic-probe = { version = "0.3", features = ["print-defmt"] }
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
rand = { version = "0.8.4", default-features = false }
embedded-storage = "0.3.1"
usbd-hid = "0.6.0"
usbd-hid = "0.7.0"
serde = { version = "1.0.136", default-features = false }
embedded-hal = { version = "1.0" }
embedded-hal-async = { version = "1.0" }

View File

@ -24,7 +24,7 @@ panic-probe = { version = "0.3", features = ["print-defmt"] }
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
rand = { version = "0.8.4", default-features = false }
embedded-storage = "0.3.1"
usbd-hid = "0.6.0"
usbd-hid = "0.7.0"
serde = { version = "1.0.136", default-features = false }
[profile.release]

View File

@ -36,7 +36,7 @@ display-interface = "0.4.1"
byte-slice-cast = { version = "1.2.0", default-features = false }
smart-leds = "0.3.0"
heapless = "0.8"
usbd-hid = "0.6.1"
usbd-hid = "0.7.0"
embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
embedded-hal-async = "1.0"

View File

@ -12,7 +12,7 @@ embassy-executor = { version = "0.5.0", path = "../../embassy-executor", feature
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", features = ["defmt"] }
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
usbd-hid = "0.6.0"
usbd-hid = "0.7.0"
defmt = "0.3"
defmt-rtt = "0.4"

View File

@ -113,11 +113,11 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> {
}
pub fn get_max_duty(&self) -> u32 {
T::regs_gp32().arr().read().arr()
T::regs_gp32().arr().read()
}
pub fn set_duty(&mut self, channel: Channel, duty: u32) {
defmt::assert!(duty < self.get_max_duty());
T::regs_gp32().ccr(channel.index()).modify(|w| w.set_ccr(duty))
T::regs_gp32().ccr(channel.index()).write_value(duty)
}
}

View File

@ -13,7 +13,7 @@ embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["de
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] }
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
usbd-hid = "0.6.0"
usbd-hid = "0.7.0"
defmt = "0.3"
defmt-rtt = "0.4"