mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-21 22:32:29 +00:00
Merge branch 'embassy-rs:main' into cryp
This commit is contained in:
commit
998532c33e
@ -17,15 +17,6 @@ The first thing you’ll 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:
|
||||
|
@ -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, there’s 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 it’s programming, so you’ll 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, there’s 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 it’s programming, so you’ll have to edit this file if you want to run examples on other chips.
|
||||
|
||||
=== It didn’t 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 you’re 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 didn’t 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 you’re using a raspberry pi pico-w, make sure you’re running `+cargo run --bin wifi_blinky --release+` rather than the regular blinky. The pico-w’s on-board LED is connected to the WiFi chip, which needs to be initialized before the LED can be blinked.
|
||||
|
||||
If you’re 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 you’re 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].
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
438
embassy-nrf/src/radio/ble.rs
Normal file
438
embassy-nrf/src/radio/ble.rs
Normal 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();
|
||||
}
|
||||
}
|
75
embassy-nrf/src/radio/mod.rs
Normal file
75
embassy-nrf/src/radio/mod.rs
Normal 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;
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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)]
|
||||
{
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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 }
|
||||
|
@ -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" }
|
||||
|
@ -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]
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user