Merge branch 'main' of https://github.com/embassy-rs/embassy into hrtim

This commit is contained in:
xoviat 2023-07-22 14:49:31 -05:00
commit d42dff45de
217 changed files with 6491 additions and 1635 deletions

41
.gitattributes vendored Normal file
View File

@ -0,0 +1,41 @@
* text=auto
*.adoc text
*.html text
*.in text
*.json text
*.md text
*.proto text
*.py text
*.rs text
*.service text
*.sh text
*.toml text
*.txt text
*.x text
*.yml text
*.raw binary
*.bin binary
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.mov binary
*.mp4 binary
*.mp3 binary
*.flv binary
*.fla binary
*.swf binary
*.gz binary
*.zip binary
*.7z binary
*.ttf binary
*.eot binary
*.woff binary
*.pyc binary
*.pdf binary
*.ez binary
*.bz2 binary
*.swp binary

17
.github/ci/crlf.sh vendored Executable file
View File

@ -0,0 +1,17 @@
#!/bin/bash
## on push branch~=gh-readonly-queue/main/.*
## on pull_request
set -euo pipefail
FILES_WITH_CRLF=$(find ! -path "./.git/*" -not -type d | xargs file -N | (grep " CRLF " || true))
if [ -z "$FILES_WITH_CRLF" ]; then
echo -e "No files with CRLF endings found."
exit 0
else
NR_FILES=$(echo "$FILES_WITH_CRLF" | wc -l)
echo -e "ERROR: Found ${NR_FILES} files with CRLF endings."
echo "$FILES_WITH_CRLF"
exit "$NR_FILES"
fi

52
.github/ci/doc.sh vendored
View File

@ -6,7 +6,7 @@ set -euo pipefail
export RUSTUP_HOME=/ci/cache/rustup
export CARGO_HOME=/ci/cache/cargo
export CARGO_TARGET_DIR=/ci/cache/target
export BUILDER_THREADS=6
export BUILDER_THREADS=4
export BUILDER_COMPRESS=true
# force rustup to download the toolchain before starting building.
@ -15,30 +15,32 @@ export BUILDER_COMPRESS=true
# which makes rustup very sad
rustc --version > /dev/null
docserver-builder -i ./embassy-stm32 -o crates/embassy-stm32/git.zup
docserver-builder -i ./embassy-boot/boot -o crates/embassy-boot/git.zup
docserver-builder -i ./embassy-boot/nrf -o crates/embassy-boot-nrf/git.zup
docserver-builder -i ./embassy-boot/rp -o crates/embassy-boot-rp/git.zup
docserver-builder -i ./embassy-boot/stm32 -o crates/embassy-boot-stm32/git.zup
docserver-builder -i ./embassy-embedded-hal -o crates/embassy-embedded-hal/git.zup
docserver-builder -i ./embassy-executor -o crates/embassy-executor/git.zup
docserver-builder -i ./embassy-futures -o crates/embassy-futures/git.zup
docserver-builder -i ./embassy-lora -o crates/embassy-lora/git.zup
docserver-builder -i ./embassy-net -o crates/embassy-net/git.zup
docserver-builder -i ./embassy-net-driver -o crates/embassy-net-driver/git.zup
docserver-builder -i ./embassy-net-driver-channel -o crates/embassy-net-driver-channel/git.zup
docserver-builder -i ./embassy-nrf -o crates/embassy-nrf/git.zup
docserver-builder -i ./embassy-rp -o crates/embassy-rp/git.zup
docserver-builder -i ./embassy-sync -o crates/embassy-sync/git.zup
docserver-builder -i ./embassy-time -o crates/embassy-time/git.zup
docserver-builder -i ./embassy-usb -o crates/embassy-usb/git.zup
docserver-builder -i ./embassy-usb-driver -o crates/embassy-usb-driver/git.zup
docserver-builder -i ./embassy-usb-logger -o crates/embassy-usb-logger/git.zup
docserver-builder -i ./cyw43 -o crates/cyw43/git.zup
docserver-builder -i ./cyw43-pio -o crates/cyw43-pio/git.zup
docserver-builder -i ./embassy-net-w5500 -o crates/embassy-net-w5500/git.zup
docserver-builder -i ./embassy-stm32-wpan -o crates/embassy-stm32-wpan/git.zup
docserver-builder -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup
docserver-builder -i ./embassy-boot/boot -o webroot/crates/embassy-boot/git.zup
docserver-builder -i ./embassy-boot/nrf -o webroot/crates/embassy-boot-nrf/git.zup
docserver-builder -i ./embassy-boot/rp -o webroot/crates/embassy-boot-rp/git.zup
docserver-builder -i ./embassy-boot/stm32 -o webroot/crates/embassy-boot-stm32/git.zup
docserver-builder -i ./embassy-embedded-hal -o webroot/crates/embassy-embedded-hal/git.zup
docserver-builder -i ./embassy-executor -o webroot/crates/embassy-executor/git.zup
docserver-builder -i ./embassy-futures -o webroot/crates/embassy-futures/git.zup
docserver-builder -i ./embassy-lora -o webroot/crates/embassy-lora/git.zup
docserver-builder -i ./embassy-net -o webroot/crates/embassy-net/git.zup
docserver-builder -i ./embassy-net-driver -o webroot/crates/embassy-net-driver/git.zup
docserver-builder -i ./embassy-net-driver-channel -o webroot/crates/embassy-net-driver-channel/git.zup
docserver-builder -i ./embassy-nrf -o webroot/crates/embassy-nrf/git.zup
docserver-builder -i ./embassy-rp -o webroot/crates/embassy-rp/git.zup
docserver-builder -i ./embassy-sync -o webroot/crates/embassy-sync/git.zup
docserver-builder -i ./embassy-time -o webroot/crates/embassy-time/git.zup
docserver-builder -i ./embassy-usb -o webroot/crates/embassy-usb/git.zup
docserver-builder -i ./embassy-usb-driver -o webroot/crates/embassy-usb-driver/git.zup
docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/git.zup
docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup
docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup
docserver-builder -i ./embassy-net-w5500 -o webroot/crates/embassy-net-w5500/git.zup
docserver-builder -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup
docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static
export KUBECONFIG=/ci/secrets/kubeconfig.yml
POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name})
kubectl cp crates $POD:/data
kubectl cp webroot/crates $POD:/data
kubectl cp webroot/static $POD:/data

4
ci.sh
View File

@ -5,10 +5,6 @@ set -euo pipefail
export RUSTFLAGS=-Dwarnings
export DEFMT_LOG=trace,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info
# needed by wifi examples
export WIFI_NETWORK=x
export WIFI_PASSWORD=x
TARGET=$(rustc -vV | sed -n 's|host: ||p')
BUILD_EXTRA=""

View File

@ -11,7 +11,7 @@ log = ["dep:log"]
firmware-logs = []
[dependencies]
embassy-time = { version = "0.1.0", path = "../embassy-time"}
embassy-time = { version = "0.1.2", path = "../embassy-time"}
embassy-sync = { version = "0.2.0", path = "../embassy-sync"}
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
@ -24,7 +24,7 @@ cortex-m = "0.7.6"
cortex-m-rt = "0.7.0"
futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] }
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.10" }
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.11" }
num_enum = { version = "0.5.7", default-features = false }
[package.metadata.embassy_docs]

View File

@ -30,7 +30,7 @@ TODO:
### Example 2: Create an access point (IP and credentials in the code)
- `cargo run --release --bin wifi_ap_tcp_server`
### Example 3: Connect to an existing network and create a server
- `WIFI_NETWORK=MyWifiNetwork WIFI_PASSWORD=MyWifiPassword cargo run --release --bin wifi_tcp_server`
- `cargo run --release --bin wifi_tcp_server`
After a few seconds, you should see that DHCP picks up an IP address like this
```

View File

@ -345,7 +345,9 @@ where
}
fn rx(&mut self, packet: &mut [u8]) {
let Some((sdpcm_header, payload)) = SdpcmHeader::parse(packet) else { return };
let Some((sdpcm_header, payload)) = SdpcmHeader::parse(packet) else {
return;
};
self.update_credit(&sdpcm_header);
@ -353,7 +355,9 @@ where
match channel {
CHANNEL_TYPE_CONTROL => {
let Some((cdc_header, response)) = CdcHeader::parse(payload) else { return; };
let Some((cdc_header, response)) = CdcHeader::parse(payload) else {
return;
};
trace!(" {:?}", cdc_header);
if cdc_header.id == self.ioctl_id {
@ -417,8 +421,12 @@ where
let status = event_packet.msg.status;
let event_payload = match evt_type {
Event::ESCAN_RESULT if status == EStatus::PARTIAL => {
let Some((_, bss_info)) = ScanResults::parse(evt_data) else { return };
let Some(bss_info) = BssInfo::parse(bss_info) else { return };
let Some((_, bss_info)) = ScanResults::parse(evt_data) else {
return;
};
let Some(bss_info) = BssInfo::parse(bss_info) else {
return;
};
events::Payload::BssInfo(*bss_info)
}
Event::ESCAN_RESULT => events::Payload::None,
@ -439,7 +447,9 @@ where
}
}
CHANNEL_TYPE_DATA => {
let Some((_, packet)) = BdcHeader::parse(payload) else { return };
let Some((_, packet)) = BdcHeader::parse(payload) else {
return;
};
trace!("rx pkt {:02x}", Bytes(&packet[..packet.len().min(48)]));
match self.ch.try_rx_buf() {

View File

@ -15,15 +15,18 @@ target = "x86_64-unknown-linux-gnu"
std = []
# Enable nightly-only features
nightly = ["embassy-futures", "embedded-hal-async", "embedded-storage-async"]
time = ["dep:embassy-time"]
default = ["time"]
[dependencies]
embassy-futures = { version = "0.1.0", path = "../embassy-futures", optional = true }
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [
"unproven",
] }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" }
embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" }
embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true }
embedded-storage = "0.3.0"
embedded-storage-async = { version = "0.4.0", optional = true }
nb = "1.0.0"

View File

@ -74,7 +74,21 @@ where
E: embedded_hal_1::spi::Error + 'static,
T: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>,
{
async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> {
async fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> {
self.wrapped.write(data)?;
Ok(())
}
async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
self.wrapped.transfer(data)?;
Ok(())
}
async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
// Ensure we write the expected bytes
for i in 0..core::cmp::min(read.len(), write.len()) {
read[i] = write[i].clone();
@ -83,38 +97,7 @@ where
Ok(())
}
async fn transfer_in_place<'a>(&'a mut self, _: &'a mut [u8]) -> Result<(), Self::Error> {
todo!()
}
}
impl<T, E> embedded_hal_async::spi::SpiBusFlush for BlockingAsync<T>
where
E: embedded_hal_1::spi::Error + 'static,
T: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>,
{
async fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl<T, E> embedded_hal_async::spi::SpiBusWrite<u8> for BlockingAsync<T>
where
E: embedded_hal_1::spi::Error + 'static,
T: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>,
{
async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> {
self.wrapped.write(data)?;
Ok(())
}
}
impl<T, E> embedded_hal_async::spi::SpiBusRead<u8> for BlockingAsync<T>
where
E: embedded_hal_1::spi::Error + 'static,
T: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>,
{
async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
async fn transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
self.wrapped.transfer(data)?;
Ok(())
}

View File

@ -69,54 +69,39 @@ where
type Error = T::Error;
}
impl<T> embedded_hal_async::spi::SpiBus<u8> for YieldingAsync<T>
impl<T, Word: 'static + Copy> embedded_hal_async::spi::SpiBus<Word> for YieldingAsync<T>
where
T: embedded_hal_async::spi::SpiBus,
{
async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> {
self.wrapped.transfer(read, write).await?;
yield_now().await;
Ok(())
}
async fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Result<(), Self::Error> {
self.wrapped.transfer_in_place(words).await?;
yield_now().await;
Ok(())
}
}
impl<T> embedded_hal_async::spi::SpiBusFlush for YieldingAsync<T>
where
T: embedded_hal_async::spi::SpiBusFlush,
T: embedded_hal_async::spi::SpiBus<Word>,
{
async fn flush(&mut self) -> Result<(), Self::Error> {
self.wrapped.flush().await?;
yield_now().await;
Ok(())
}
}
impl<T> embedded_hal_async::spi::SpiBusWrite<u8> for YieldingAsync<T>
where
T: embedded_hal_async::spi::SpiBusWrite<u8>,
{
async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> {
async fn write(&mut self, data: &[Word]) -> Result<(), Self::Error> {
self.wrapped.write(data).await?;
yield_now().await;
Ok(())
}
}
impl<T> embedded_hal_async::spi::SpiBusRead<u8> for YieldingAsync<T>
where
T: embedded_hal_async::spi::SpiBusRead<u8>,
{
async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
async fn read(&mut self, data: &mut [Word]) -> Result<(), Self::Error> {
self.wrapped.read(data).await?;
yield_now().await;
Ok(())
}
async fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> {
self.wrapped.transfer(read, write).await?;
yield_now().await;
Ok(())
}
async fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
self.wrapped.transfer_in_place(words).await?;
yield_now().await;
Ok(())
}
}
///

View File

@ -56,62 +56,6 @@ where
type Error = SpiDeviceError<BUS::Error, CS::Error>;
}
impl<M, BUS, CS> spi::SpiDeviceRead for SpiDevice<'_, M, BUS, CS>
where
M: RawMutex,
BUS: spi::SpiBusRead,
CS: OutputPin,
{
async fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> {
let mut bus = self.bus.lock().await;
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
let op_res: Result<(), BUS::Error> = try {
for buf in operations {
bus.read(buf).await?;
}
};
// On failure, it's important to still flush and deassert CS.
let flush_res = bus.flush().await;
let cs_res = self.cs.set_high();
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
flush_res.map_err(SpiDeviceError::Spi)?;
cs_res.map_err(SpiDeviceError::Cs)?;
Ok(op_res)
}
}
impl<M, BUS, CS> spi::SpiDeviceWrite for SpiDevice<'_, M, BUS, CS>
where
M: RawMutex,
BUS: spi::SpiBusWrite,
CS: OutputPin,
{
async fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> {
let mut bus = self.bus.lock().await;
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
let op_res: Result<(), BUS::Error> = try {
for buf in operations {
bus.write(buf).await?;
}
};
// On failure, it's important to still flush and deassert CS.
let flush_res = bus.flush().await;
let cs_res = self.cs.set_high();
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
flush_res.map_err(SpiDeviceError::Spi)?;
cs_res.map_err(SpiDeviceError::Cs)?;
Ok(op_res)
}
}
impl<M, BUS, CS> spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
where
M: RawMutex,
@ -129,6 +73,12 @@ where
Operation::Write(buf) => bus.write(buf).await?,
Operation::Transfer(read, write) => bus.transfer(read, write).await?,
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?,
#[cfg(not(feature = "time"))]
Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported),
#[cfg(feature = "time")]
Operation::DelayUs(us) => {
embassy_time::Timer::after(embassy_time::Duration::from_micros(*us as _)).await
}
}
}
};
@ -172,64 +122,6 @@ where
type Error = SpiDeviceError<BUS::Error, CS::Error>;
}
impl<M, BUS, CS> spi::SpiDeviceWrite for SpiDeviceWithConfig<'_, M, BUS, CS>
where
M: RawMutex,
BUS: spi::SpiBusWrite + SetConfig,
CS: OutputPin,
{
async fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> {
let mut bus = self.bus.lock().await;
bus.set_config(&self.config);
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
let op_res: Result<(), BUS::Error> = try {
for buf in operations {
bus.write(buf).await?;
}
};
// On failure, it's important to still flush and deassert CS.
let flush_res = bus.flush().await;
let cs_res = self.cs.set_high();
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
flush_res.map_err(SpiDeviceError::Spi)?;
cs_res.map_err(SpiDeviceError::Cs)?;
Ok(op_res)
}
}
impl<M, BUS, CS> spi::SpiDeviceRead for SpiDeviceWithConfig<'_, M, BUS, CS>
where
M: RawMutex,
BUS: spi::SpiBusRead + SetConfig,
CS: OutputPin,
{
async fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> {
let mut bus = self.bus.lock().await;
bus.set_config(&self.config);
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
let op_res: Result<(), BUS::Error> = try {
for buf in operations {
bus.read(buf).await?;
}
};
// On failure, it's important to still flush and deassert CS.
let flush_res = bus.flush().await;
let cs_res = self.cs.set_high();
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
flush_res.map_err(SpiDeviceError::Spi)?;
cs_res.map_err(SpiDeviceError::Cs)?;
Ok(op_res)
}
}
impl<M, BUS, CS> spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
where
M: RawMutex,
@ -248,6 +140,12 @@ where
Operation::Write(buf) => bus.write(buf).await?,
Operation::Transfer(read, write) => bus.transfer(read, write).await?,
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?,
#[cfg(not(feature = "time"))]
Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported),
#[cfg(feature = "time")]
Operation::DelayUs(us) => {
embassy_time::Timer::after(embassy_time::Duration::from_micros(*us as _)).await
}
}
}
};

View File

@ -22,7 +22,7 @@ use core::cell::RefCell;
use embassy_sync::blocking_mutex::raw::RawMutex;
use embassy_sync::blocking_mutex::Mutex;
use embedded_hal_1::digital::OutputPin;
use embedded_hal_1::spi::{self, Operation, SpiBus, SpiBusRead, SpiBusWrite};
use embedded_hal_1::spi::{self, Operation, SpiBus};
use crate::shared_bus::SpiDeviceError;
use crate::SetConfig;
@ -48,58 +48,6 @@ where
type Error = SpiDeviceError<BUS::Error, CS::Error>;
}
impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceRead for SpiDevice<'_, M, BUS, CS>
where
M: RawMutex,
BUS: SpiBusRead,
CS: OutputPin,
{
fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> {
self.bus.lock(|bus| {
let mut bus = bus.borrow_mut();
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
let op_res = operations.iter_mut().try_for_each(|buf| bus.read(buf));
// On failure, it's important to still flush and deassert CS.
let flush_res = bus.flush();
let cs_res = self.cs.set_high();
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
flush_res.map_err(SpiDeviceError::Spi)?;
cs_res.map_err(SpiDeviceError::Cs)?;
Ok(op_res)
})
}
}
impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceWrite for SpiDevice<'_, M, BUS, CS>
where
M: RawMutex,
BUS: SpiBusWrite,
CS: OutputPin,
{
fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> {
self.bus.lock(|bus| {
let mut bus = bus.borrow_mut();
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
let op_res = operations.iter().try_for_each(|buf| bus.write(buf));
// On failure, it's important to still flush and deassert CS.
let flush_res = bus.flush();
let cs_res = self.cs.set_high();
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
flush_res.map_err(SpiDeviceError::Spi)?;
cs_res.map_err(SpiDeviceError::Cs)?;
Ok(op_res)
})
}
}
impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
where
M: RawMutex,
@ -116,6 +64,13 @@ where
Operation::Write(buf) => bus.write(buf),
Operation::Transfer(read, write) => bus.transfer(read, write),
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
#[cfg(not(feature = "time"))]
Operation::DelayUs(_) => Err(SpiDeviceError::DelayUsNotSupported),
#[cfg(feature = "time")]
Operation::DelayUs(us) => {
embassy_time::block_for(embassy_time::Duration::from_micros(*us as _));
Ok(())
}
});
// On failure, it's important to still flush and deassert CS.
@ -199,58 +154,6 @@ where
type Error = SpiDeviceError<BUS::Error, CS::Error>;
}
impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceRead for SpiDeviceWithConfig<'_, M, BUS, CS>
where
M: RawMutex,
BUS: SpiBusRead + SetConfig,
CS: OutputPin,
{
fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> {
self.bus.lock(|bus| {
let mut bus = bus.borrow_mut();
bus.set_config(&self.config);
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
let op_res = operations.iter_mut().try_for_each(|buf| bus.read(buf));
// On failure, it's important to still flush and deassert CS.
let flush_res = bus.flush();
let cs_res = self.cs.set_high();
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
flush_res.map_err(SpiDeviceError::Spi)?;
cs_res.map_err(SpiDeviceError::Cs)?;
Ok(op_res)
})
}
}
impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceWrite for SpiDeviceWithConfig<'_, M, BUS, CS>
where
M: RawMutex,
BUS: SpiBusWrite + SetConfig,
CS: OutputPin,
{
fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> {
self.bus.lock(|bus| {
let mut bus = bus.borrow_mut();
bus.set_config(&self.config);
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
let op_res = operations.iter().try_for_each(|buf| bus.write(buf));
// On failure, it's important to still flush and deassert CS.
let flush_res = bus.flush();
let cs_res = self.cs.set_high();
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
flush_res.map_err(SpiDeviceError::Spi)?;
cs_res.map_err(SpiDeviceError::Cs)?;
Ok(op_res)
})
}
}
impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
where
M: RawMutex,
@ -268,6 +171,13 @@ where
Operation::Write(buf) => bus.write(buf),
Operation::Transfer(read, write) => bus.transfer(read, write),
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
#[cfg(not(feature = "time"))]
Operation::DelayUs(_) => Err(SpiDeviceError::DelayUsNotSupported),
#[cfg(feature = "time")]
Operation::DelayUs(us) => {
embassy_time::block_for(embassy_time::Duration::from_micros(*us as _));
Ok(())
}
});
// On failure, it's important to still flush and deassert CS.

View File

@ -30,11 +30,14 @@ where
/// Error returned by SPI device implementations in this crate.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum SpiDeviceError<BUS, CS> {
/// An operation on the inner SPI bus failed.
Spi(BUS),
/// Setting the value of the Chip Select (CS) pin failed.
Cs(CS),
/// DelayUs operations are not supported when the `time` Cargo feature is not enabled.
DelayUsNotSupported,
}
impl<BUS, CS> spi::Error for SpiDeviceError<BUS, CS>
@ -46,6 +49,7 @@ where
match self {
Self::Spi(e) => e.kind(),
Self::Cs(_) => spi::ErrorKind::Other,
Self::DelayUsNotSupported => spi::ErrorKind::Other,
}
}
}

View File

@ -61,8 +61,8 @@ log = { version = "0.4.14", optional = true }
rtos-trace = { version = "0.1.2", optional = true }
futures-util = { version = "0.3.17", default-features = false }
embassy-macros = { version = "0.2.0", path = "../embassy-macros" }
embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true}
embassy-macros = { version = "0.2.0", path = "../embassy-macros" }
embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true}
atomic-polyfill = "1.0.1"
critical-section = "1.1"
static_cell = "1.1"

View File

@ -165,6 +165,9 @@ impl<F: Future + 'static> TaskStorage<F> {
Poll::Ready(_) => {
this.future.drop_in_place();
this.raw.state.fetch_and(!STATE_SPAWNED, Ordering::AcqRel);
#[cfg(feature = "integrated-timers")]
this.raw.expires_at.set(Instant::MAX);
}
Poll::Pending => {}
}

View File

@ -161,7 +161,7 @@ pub trait Peripheral: Sized {
}
}
impl<'b, T: Deref> Peripheral for T
impl<'b, T: DerefMut> Peripheral for T
where
T::Target: Peripheral,
{

View File

@ -20,11 +20,11 @@ defmt = ["dep:defmt", "lorawan-device/defmt"]
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
embassy-time = { version = "0.1.0", path = "../embassy-time" }
embassy-time = { version = "0.1.2", path = "../embassy-time" }
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" }
embedded-hal-async = { version = "=0.2.0-alpha.1" }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" }
embedded-hal-async = { version = "=0.2.0-alpha.2" }
embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common", default-features = false }
futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
embedded-hal = { version = "0.2", features = ["unproven"] }
@ -32,3 +32,6 @@ bit_field = { version = "0.10" }
lora-phy = { version = "1" }
lorawan-device = { version = "0.10.0", default-features = false, features = ["async"] }
[patch.crates-io]
lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" }

View File

@ -164,6 +164,9 @@ pub enum Medium {
///
/// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode.
Ip,
/// IEEE 802_15_4 medium
Ieee802154,
}
impl Default for Medium {

View File

@ -7,14 +7,20 @@ edition = "2021"
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
embassy-time = { version = "0.1.0", path = "../embassy-time" }
embassy-time = { version = "0.1.2", path = "../embassy-time" }
embassy-sync = { version = "0.2.0", path = "../embassy-sync"}
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
embedded-hal = { version = "1.0.0-alpha.10" }
embedded-hal-async = { version = "=0.2.0-alpha.1" }
embedded-hal = { version = "1.0.0-alpha.11" }
embedded-hal-async = { version = "=0.2.0-alpha.2" }
noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] }
#noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] }
heapless = "0.7.16"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-esp-hosted-v$VERSION/embassy-net-esp-hosted/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-esp-hosted/src/"
target = "thumbv7em-none-eabi"
features = ["defmt"]

View File

@ -54,7 +54,9 @@ impl<'a> Control<'a> {
})),
};
let resp = self.ioctl(req).await;
let proto::CtrlMsgPayload::RespConnectAp(resp) = resp.payload.unwrap() else { panic!("unexpected resp") };
let proto::CtrlMsgPayload::RespConnectAp(resp) = resp.payload.unwrap() else {
panic!("unexpected resp")
};
debug!("======= {:?}", Debug2Format(&resp));
assert_eq!(resp.resp, 0);
self.state_ch.set_link_state(LinkState::Up);
@ -71,7 +73,9 @@ impl<'a> Control<'a> {
)),
};
let resp = self.ioctl(req).await;
let proto::CtrlMsgPayload::RespGetMacAddress(resp) = resp.payload.unwrap() else { panic!("unexpected resp") };
let proto::CtrlMsgPayload::RespGetMacAddress(resp) = resp.payload.unwrap() else {
panic!("unexpected resp")
};
assert_eq!(resp.resp, 0);
// WHY IS THIS A STRING? WHYYYY
@ -100,7 +104,9 @@ impl<'a> Control<'a> {
payload: Some(proto::CtrlMsgPayload::ReqSetWifiMode(proto::CtrlMsgReqSetMode { mode })),
};
let resp = self.ioctl(req).await;
let proto::CtrlMsgPayload::RespSetWifiMode(resp) = resp.payload.unwrap() else { panic!("unexpected resp") };
let proto::CtrlMsgPayload::RespSetWifiMode(resp) = resp.payload.unwrap() else {
panic!("unexpected resp")
};
assert_eq!(resp.resp, 0);
}

View File

@ -311,14 +311,14 @@ where
fn handle_event(&self, data: &[u8]) {
let Ok(event) = noproto::read::<CtrlMsg>(data) else {
warn!("failed to parse event");
return
return;
};
debug!("event: {:?}", &event);
let Some(payload) = &event.payload else {
warn!("event without payload?");
return
return;
};
match payload {

View File

@ -8,11 +8,11 @@ license = "MIT OR Apache-2.0"
edition = "2021"
[dependencies]
embedded-hal = { version = "1.0.0-alpha.10" }
embedded-hal-async = { version = "=0.2.0-alpha.1" }
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
embassy-time = { version = "0.1.0" }
embassy-futures = { version = "0.1.0" }
embedded-hal = { version = "1.0.0-alpha.11" }
embedded-hal-async = { version = "=0.2.0-alpha.2" }
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" }
embassy-time = { version = "0.1.2", path = "../embassy-time" }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
defmt = { version = "0.3", optional = true }
[package.metadata.embassy_docs]

View File

@ -22,7 +22,11 @@ impl<SPI: SpiDevice> SpiInterface<SPI> {
let address_phase = address.to_be_bytes();
let control_phase = [(block as u8) << 3 | 0b0000_0100];
let data_phase = data;
let operations = &[&address_phase[..], &control_phase, &data_phase];
self.0.write_transaction(operations).await
let operations = &mut [
Operation::Write(&address_phase[..]),
Operation::Write(&control_phase),
Operation::Write(&data_phase),
];
self.0.transaction(operations).await
}
}

View File

@ -37,6 +37,7 @@ proto-ipv4 = ["smoltcp/proto-ipv4"]
proto-ipv6 = ["smoltcp/proto-ipv6"]
medium-ethernet = ["smoltcp/medium-ethernet"]
medium-ip = ["smoltcp/medium-ip"]
medium-ieee802154 = ["smoltcp/medium-ieee802154"]
igmp = ["smoltcp/proto-igmp"]
[dependencies]
@ -50,7 +51,7 @@ smoltcp = { version = "0.10.0", default-features = false, features = [
] }
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
embassy-time = { version = "0.1.0", path = "../embassy-time" }
embassy-time = { version = "0.1.2", path = "../embassy-time" }
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
embedded-io = { version = "0.4.0", optional = true }

View File

@ -51,6 +51,8 @@ where
Medium::Ethernet => phy::Medium::Ethernet,
#[cfg(feature = "medium-ip")]
Medium::Ip => phy::Medium::Ip,
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => phy::Medium::Ieee802154,
#[allow(unreachable_patterns)]
_ => panic!(
"Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.",

View File

@ -24,9 +24,11 @@ use embassy_net_driver::{Driver, LinkState, Medium};
use embassy_sync::waitqueue::WakerRegistration;
use embassy_time::{Instant, Timer};
use futures::pin_mut;
#[allow(unused_imports)]
use heapless::Vec;
#[cfg(feature = "igmp")]
pub use smoltcp::iface::MulticastError;
#[allow(unused_imports)]
use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage};
#[cfg(feature = "dhcpv4")]
use smoltcp::socket::dhcpv4::{self, RetryConfig};
@ -34,7 +36,9 @@ use smoltcp::socket::dhcpv4::{self, RetryConfig};
pub use smoltcp::wire::IpListenEndpoint;
#[cfg(feature = "medium-ethernet")]
pub use smoltcp::wire::{EthernetAddress, HardwareAddress};
pub use smoltcp::wire::{IpAddress, IpCidr};
#[cfg(feature = "medium-ieee802154")]
pub use smoltcp::wire::{HardwareAddress, Ieee802154Address};
pub use smoltcp::wire::{IpAddress, IpCidr, IpEndpoint};
#[cfg(feature = "proto-ipv4")]
pub use smoltcp::wire::{Ipv4Address, Ipv4Cidr};
#[cfg(feature = "proto-ipv6")]
@ -232,7 +236,7 @@ impl<D: Driver + 'static> Stack<D> {
resources: &'static mut StackResources<SOCK>,
random_seed: u64,
) -> Self {
#[cfg(feature = "medium-ethernet")]
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
let medium = device.capabilities().medium;
let hardware_addr = match medium {
@ -240,6 +244,8 @@ impl<D: Driver + 'static> Stack<D> {
Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress(device.ethernet_address())),
#[cfg(feature = "medium-ip")]
Medium::Ip => HardwareAddress::Ip,
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => HardwareAddress::Ieee802154(Ieee802154Address::Absent),
#[allow(unreachable_patterns)]
_ => panic!(
"Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.",
@ -262,6 +268,7 @@ impl<D: Driver + 'static> Stack<D> {
let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN;
#[cfg_attr(feature = "medium-ieee802154", allow(unused_mut))]
let mut socket = SocketStack {
sockets,
iface,
@ -269,6 +276,7 @@ impl<D: Driver + 'static> Stack<D> {
next_local_port,
};
#[cfg_attr(feature = "medium-ieee802154", allow(unused_mut))]
let mut inner = Inner {
device,
link_up: false,
@ -287,6 +295,9 @@ impl<D: Driver + 'static> Stack<D> {
dns_waker: WakerRegistration::new(),
};
#[cfg(feature = "medium-ieee802154")]
let _ = config;
#[cfg(feature = "proto-ipv4")]
match config.ipv4 {
ConfigV4::Static(config) => {
@ -479,30 +490,78 @@ impl<D: Driver + 'static> Stack<D> {
}
#[cfg(feature = "igmp")]
impl<D: Driver + smoltcp::phy::Device + 'static> Stack<D> {
impl<D: Driver + 'static> Stack<D> {
/// Join a multicast group.
pub fn join_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError>
pub async fn join_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError>
where
T: Into<IpAddress>,
{
let addr = addr.into();
poll_fn(move |cx| self.poll_join_multicast_group(addr, cx)).await
}
/// Join a multicast group.
///
/// When the send queue is full, this method will return `Poll::Pending`
/// and register the current task to be notified when the queue has space available.
pub fn poll_join_multicast_group<T>(&self, addr: T, cx: &mut Context<'_>) -> Poll<Result<bool, MulticastError>>
where
T: Into<IpAddress>,
{
let addr = addr.into();
self.with_mut(|s, i| {
s.iface
.join_multicast_group(&mut i.device, addr, instant_to_smoltcp(Instant::now()))
let mut smoldev = DriverAdapter {
cx: Some(cx),
inner: &mut i.device,
};
match s
.iface
.join_multicast_group(&mut smoldev, addr, instant_to_smoltcp(Instant::now()))
{
Ok(announce_sent) => Poll::Ready(Ok(announce_sent)),
Err(MulticastError::Exhausted) => Poll::Pending,
Err(other) => Poll::Ready(Err(other)),
}
})
}
/// Leave a multicast group.
pub fn leave_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError>
pub async fn leave_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError>
where
T: Into<IpAddress>,
{
let addr = addr.into();
poll_fn(move |cx| self.poll_leave_multicast_group(addr, cx)).await
}
/// Leave a multicast group.
///
/// When the send queue is full, this method will return `Poll::Pending`
/// and register the current task to be notified when the queue has space available.
pub fn poll_leave_multicast_group<T>(&self, addr: T, cx: &mut Context<'_>) -> Poll<Result<bool, MulticastError>>
where
T: Into<IpAddress>,
{
let addr = addr.into();
self.with_mut(|s, i| {
s.iface
.leave_multicast_group(&mut i.device, addr, instant_to_smoltcp(Instant::now()))
let mut smoldev = DriverAdapter {
cx: Some(cx),
inner: &mut i.device,
};
match s
.iface
.leave_multicast_group(&mut smoldev, addr, instant_to_smoltcp(Instant::now()))
{
Ok(leave_sent) => Poll::Ready(Ok(leave_sent)),
Err(MulticastError::Exhausted) => Poll::Pending,
Err(other) => Poll::Ready(Err(other)),
}
})
}
@ -531,11 +590,14 @@ impl<D: Driver + 'static> Inner<D> {
debug!(" IP address: {}", config.address);
s.iface.update_ip_addrs(|addrs| {
if addrs.is_empty() {
addrs.push(IpCidr::Ipv4(config.address)).unwrap();
} else {
addrs[0] = IpCidr::Ipv4(config.address);
if let Some((index, _)) = addrs
.iter()
.enumerate()
.find(|(_, &addr)| matches!(addr, IpCidr::Ipv4(_)))
{
addrs.remove(index);
}
addrs.push(IpCidr::Ipv4(config.address)).unwrap();
});
#[cfg(feature = "medium-ethernet")]
@ -570,11 +632,14 @@ impl<D: Driver + 'static> Inner<D> {
debug!(" IP address: {}", config.address);
s.iface.update_ip_addrs(|addrs| {
if addrs.is_empty() {
addrs.push(IpCidr::Ipv6(config.address)).unwrap();
} else {
addrs[0] = IpCidr::Ipv6(config.address);
if let Some((index, _)) = addrs
.iter()
.enumerate()
.find(|(_, &addr)| matches!(addr, IpCidr::Ipv6(_)))
{
addrs.remove(index);
}
addrs.push(IpCidr::Ipv6(config.address)).unwrap();
});
#[cfg(feature = "medium-ethernet")]
@ -642,13 +707,21 @@ impl<D: Driver + 'static> Inner<D> {
socket.set_retry_config(config.retry_config);
}
#[allow(unused)] // used only with dhcp
fn unapply_config(&mut self, s: &mut SocketStack) {
#[cfg(feature = "dhcpv4")]
fn unapply_config_v4(&mut self, s: &mut SocketStack) {
#[cfg(feature = "medium-ethernet")]
let medium = self.device.capabilities().medium;
debug!("Lost IP configuration");
s.iface.update_ip_addrs(|ip_addrs| ip_addrs.clear());
s.iface.update_ip_addrs(|ip_addrs| {
#[cfg(feature = "proto-ipv4")]
if let Some((index, _)) = ip_addrs
.iter()
.enumerate()
.find(|(_, &addr)| matches!(addr, IpCidr::Ipv4(_)))
{
ip_addrs.remove(index);
}
});
#[cfg(feature = "medium-ethernet")]
if medium == Medium::Ethernet {
#[cfg(feature = "proto-ipv4")]
@ -695,7 +768,7 @@ impl<D: Driver + 'static> Inner<D> {
if self.link_up {
match socket.poll() {
None => {}
Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s),
Some(dhcpv4::Event::Deconfigured) => self.unapply_config_v4(s),
Some(dhcpv4::Event::Configured(config)) => {
let config = StaticConfigV4 {
address: config.address,
@ -707,7 +780,7 @@ impl<D: Driver + 'static> Inner<D> {
}
} else if old_link_up {
socket.reset();
self.unapply_config(s);
self.unapply_config_v4(s);
}
}
//if old_link_up || self.link_up {

View File

@ -3,7 +3,7 @@
use core::cell::RefCell;
use core::future::poll_fn;
use core::mem;
use core::task::Poll;
use core::task::{Context, Poll};
use embassy_net_driver::Driver;
use smoltcp::iface::{Interface, SocketHandle};
@ -102,37 +102,61 @@ impl<'a> UdpSocket<'a> {
///
/// Returns the number of bytes received and the remote endpoint.
pub async fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, IpEndpoint), Error> {
poll_fn(move |cx| {
self.with_mut(|s, _| match s.recv_slice(buf) {
Ok((n, meta)) => Poll::Ready(Ok((n, meta.endpoint))),
// No data ready
Err(udp::RecvError::Exhausted) => {
s.register_recv_waker(cx.waker());
Poll::Pending
}
})
poll_fn(move |cx| self.poll_recv_from(buf, cx)).await
}
/// Receive a datagram.
///
/// When no datagram is available, this method will return `Poll::Pending` and
/// register the current task to be notified when a datagram is received.
///
/// When a datagram is received, this method will return `Poll::Ready` with the
/// number of bytes received and the remote endpoint.
pub fn poll_recv_from(&self, buf: &mut [u8], cx: &mut Context<'_>) -> Poll<Result<(usize, IpEndpoint), Error>> {
self.with_mut(|s, _| match s.recv_slice(buf) {
Ok((n, meta)) => Poll::Ready(Ok((n, meta.endpoint))),
// No data ready
Err(udp::RecvError::Exhausted) => {
s.register_recv_waker(cx.waker());
Poll::Pending
}
})
.await
}
/// Send a datagram to the specified remote endpoint.
///
/// This method will wait until the datagram has been sent.
///
/// When the remote endpoint is not reachable, this method will return `Err(Error::NoRoute)`
pub async fn send_to<T>(&self, buf: &[u8], remote_endpoint: T) -> Result<(), Error>
where
T: Into<IpEndpoint>,
{
let remote_endpoint = remote_endpoint.into();
poll_fn(move |cx| {
self.with_mut(|s, _| match s.send_slice(buf, remote_endpoint) {
// Entire datagram has been sent
Ok(()) => Poll::Ready(Ok(())),
Err(udp::SendError::BufferFull) => {
s.register_send_waker(cx.waker());
Poll::Pending
}
Err(udp::SendError::Unaddressable) => Poll::Ready(Err(Error::NoRoute)),
})
let remote_endpoint: IpEndpoint = remote_endpoint.into();
poll_fn(move |cx| self.poll_send_to(buf, remote_endpoint, cx)).await
}
/// Send a datagram to the specified remote endpoint.
///
/// When the datagram has been sent, this method will return `Poll::Ready(Ok())`.
///
/// When the socket's send buffer is full, this method will return `Poll::Pending`
/// and register the current task to be notified when the buffer has space available.
///
/// When the remote endpoint is not reachable, this method will return `Poll::Ready(Err(Error::NoRoute))`.
pub fn poll_send_to<T>(&self, buf: &[u8], remote_endpoint: T, cx: &mut Context<'_>) -> Poll<Result<(), Error>>
where
T: Into<IpEndpoint>,
{
self.with_mut(|s, _| match s.send_slice(buf, remote_endpoint) {
// Entire datagram has been sent
Ok(()) => Poll::Ready(Ok(())),
Err(udp::SendError::BufferFull) => {
s.register_send_waker(cx.waker());
Poll::Pending
}
Err(udp::SendError::Unaddressable) => Poll::Ready(Err(Error::NoRoute)),
})
.await
}
/// Returns the local endpoint of the socket.

View File

@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-nrf-v$VERSION/embassy-nrf/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-nrf/src/"
features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "gpiote", "time-driver-rtc1"]
features = ["nightly", "time", "defmt", "unstable-pac", "unstable-traits", "gpiote", "time-driver-rtc1"]
flavors = [
{ regex_feature = "nrf52.*", target = "thumbv7em-none-eabihf" },
{ regex_feature = "nrf53.*", target = "thumbv8m.main-none-eabihf" },
@ -91,22 +91,22 @@ _dppi = []
_gpio-p1 = []
[dependencies]
embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true }
embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true }
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common", features = ["cortex-m", "prio-bits-3"] }
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true}
embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true}
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true}
embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true}
embedded-io = { version = "0.4.0", features = ["async"], optional = true }
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
cortex-m-rt = ">=0.6.15,<0.8"
cortex-m = "0.7.6"
futures = { version = "0.3.17", default-features = false }
futures = { version = "0.3.17", default-features = false }
critical-section = "1.1"
rand_core = "0.6.3"
fixed = "1.10.0"
@ -114,13 +114,13 @@ embedded-storage = "0.3.0"
embedded-storage-async = { version = "0.4.0", optional = true }
cfg-if = "1.0.0"
nrf52805-pac = { version = "0.12.0", optional = true }
nrf52810-pac = { version = "0.12.0", optional = true }
nrf52811-pac = { version = "0.12.0", optional = true }
nrf52820-pac = { version = "0.12.0", optional = true }
nrf52832-pac = { version = "0.12.0", optional = true }
nrf52833-pac = { version = "0.12.0", optional = true }
nrf52840-pac = { version = "0.12.0", optional = true }
nrf52805-pac = { version = "0.12.0", optional = true }
nrf52810-pac = { version = "0.12.0", optional = true }
nrf52811-pac = { version = "0.12.0", optional = true }
nrf52820-pac = { version = "0.12.0", optional = true }
nrf52832-pac = { version = "0.12.0", optional = true }
nrf52833-pac = { version = "0.12.0", optional = true }
nrf52840-pac = { version = "0.12.0", optional = true }
nrf5340-app-pac = { version = "0.12.0", optional = true }
nrf5340-net-pac = { version = "0.12.0", optional = true }
nrf9160-pac = { version = "0.12.0", optional = true }

View File

@ -221,7 +221,7 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> {
}
/// Returns the IN event, for use with PPI.
pub fn event_in(&self) -> Event {
pub fn event_in(&self) -> Event<'d> {
let g = regs();
Event::from_reg(&g.events_in[self.ch.number()])
}
@ -292,21 +292,21 @@ impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> {
}
/// Returns the OUT task, for use with PPI.
pub fn task_out(&self) -> Task {
pub fn task_out(&self) -> Task<'d> {
let g = regs();
Task::from_reg(&g.tasks_out[self.ch.number()])
}
/// Returns the CLR task, for use with PPI.
#[cfg(not(feature = "nrf51"))]
pub fn task_clr(&self) -> Task {
pub fn task_clr(&self) -> Task<'d> {
let g = regs();
Task::from_reg(&g.tasks_clr[self.ch.number()])
}
/// Returns the SET task, for use with PPI.
#[cfg(not(feature = "nrf51"))]
pub fn task_set(&self) -> Task {
pub fn task_set(&self) -> Task<'d> {
let g = regs();
Task::from_reg(&g.tasks_set[self.ch.number()])
}

View File

@ -1,294 +1,500 @@
//! Pulse Density Modulation (PDM) mirophone driver.
#![macro_use]
use core::marker::PhantomData;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef};
use futures::future::poll_fn;
use crate::chip::EASY_DMA_SIZE;
use crate::gpio::sealed::Pin;
use crate::gpio::{AnyPin, Pin as GpioPin};
use crate::interrupt::typelevel::Interrupt;
use crate::{interrupt, 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() {
T::regs().intenclr.write(|w| w.end().clear());
T::state().waker.wake();
}
}
/// PDM microphone interface
pub struct Pdm<'d, T: Instance> {
_peri: PeripheralRef<'d, T>,
}
/// PDM error.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
/// Buffer is too long.
BufferTooLong,
/// Buffer is empty
BufferZeroLength,
/// PDM is not running
NotRunning,
}
static DUMMY_BUFFER: [i16; 1] = [0; 1];
impl<'d, T: Instance> Pdm<'d, T> {
/// Create PDM driver
pub fn new(
pdm: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
clk: impl Peripheral<P = impl GpioPin> + 'd,
din: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(pdm, clk, din);
Self::new_inner(pdm, clk.map_into(), din.map_into(), config)
}
fn new_inner(
pdm: PeripheralRef<'d, T>,
clk: PeripheralRef<'d, AnyPin>,
din: PeripheralRef<'d, AnyPin>,
config: Config,
) -> Self {
into_ref!(pdm);
let r = T::regs();
// setup gpio pins
din.conf().write(|w| w.input().set_bit());
r.psel.din.write(|w| unsafe { w.bits(din.psel_bits()) });
clk.set_low();
clk.conf().write(|w| w.dir().output());
r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) });
// configure
// use default for
// - gain right
// - gain left
// - clk
// - ratio
r.mode.write(|w| {
w.edge().bit(config.edge == Edge::LeftRising);
w.operation().bit(config.operation_mode == OperationMode::Mono);
w
});
r.gainl.write(|w| w.gainl().default_gain());
r.gainr.write(|w| w.gainr().default_gain());
// IRQ
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
r.enable.write(|w| w.enable().set_bit());
Self { _peri: pdm }
}
/// Start sampling microphon data into a dummy buffer
/// Usefull to start the microphon and keep it active between recording samples
pub async fn start(&mut self) {
let r = T::regs();
// start dummy sampling because microphon needs some setup time
r.sample
.ptr
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
r.sample
.maxcnt
.write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
r.tasks_start.write(|w| unsafe { w.bits(1) });
}
/// Stop sampling microphon data inta a dummy buffer
pub async fn stop(&mut self) {
let r = T::regs();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
r.events_started.reset();
}
/// Sample data into the given buffer.
pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> {
if buffer.len() == 0 {
return Err(Error::BufferZeroLength);
}
if buffer.len() > EASY_DMA_SIZE {
return Err(Error::BufferTooLong);
}
let r = T::regs();
if r.events_started.read().bits() == 0 {
return Err(Error::NotRunning);
}
let drop = OnDrop::new(move || {
r.intenclr.write(|w| w.end().clear());
r.events_stopped.reset();
// reset to dummy buffer
r.sample
.ptr
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
r.sample
.maxcnt
.write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
while r.events_stopped.read().bits() == 0 {}
});
// setup user buffer
let ptr = buffer.as_ptr();
let len = buffer.len();
r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(ptr as u32) });
r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(len as _) });
// wait till the current sample is finished and the user buffer sample is started
Self::wait_for_sample().await;
// reset the buffer back to the dummy buffer
r.sample
.ptr
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
r.sample
.maxcnt
.write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
// wait till the user buffer is sampled
Self::wait_for_sample().await;
drop.defuse();
Ok(())
}
async fn wait_for_sample() {
let r = T::regs();
r.events_end.reset();
r.intenset.write(|w| w.end().set());
compiler_fence(Ordering::SeqCst);
poll_fn(|cx| {
T::state().waker.register(cx.waker());
if r.events_end.read().bits() != 0 {
return Poll::Ready(());
}
Poll::Pending
})
.await;
compiler_fence(Ordering::SeqCst);
}
}
/// PDM microphone driver Config
pub struct Config {
/// Use stero or mono operation
pub operation_mode: OperationMode,
/// On which edge the left channel should be samples
pub edge: Edge,
}
impl Default for Config {
fn default() -> Self {
Self {
operation_mode: OperationMode::Mono,
edge: Edge::LeftFalling,
}
}
}
/// PDM operation mode.
#[derive(PartialEq)]
pub enum OperationMode {
/// Mono (1 channel)
Mono,
/// Stereo (2 channels)
Stereo,
}
/// PDM edge polarity
#[derive(PartialEq)]
pub enum Edge {
/// Left edge is rising
LeftRising,
/// Left edge is falling
LeftFalling,
}
impl<'d, T: Instance> Drop for Pdm<'d, T> {
fn drop(&mut self) {
let r = T::regs();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
r.enable.write(|w| w.enable().disabled());
r.psel.din.reset();
r.psel.clk.reset();
}
}
pub(crate) mod sealed {
use embassy_sync::waitqueue::AtomicWaker;
/// Peripheral static state
pub struct State {
pub waker: AtomicWaker,
}
impl State {
pub const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
}
}
pub trait Instance {
fn regs() -> &'static crate::pac::pdm::RegisterBlock;
fn state() -> &'static State;
}
}
/// PDM peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_pdm {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::pdm::sealed::Instance for peripherals::$type {
fn regs() -> &'static crate::pac::pdm::RegisterBlock {
unsafe { &*pac::$pac_type::ptr() }
}
fn state() -> &'static crate::pdm::sealed::State {
static STATE: crate::pdm::sealed::State = crate::pdm::sealed::State::new();
&STATE
}
}
impl crate::pdm::Instance for peripherals::$type {
type Interrupt = crate::interrupt::typelevel::$irq;
}
};
}
//! Pulse Density Modulation (PDM) mirophone driver.
#![macro_use]
use core::marker::PhantomData;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef};
use fixed::types::I7F1;
use futures::future::poll_fn;
use crate::chip::EASY_DMA_SIZE;
use crate::gpio::sealed::Pin;
use crate::gpio::{AnyPin, Pin as GpioPin};
use crate::interrupt::typelevel::Interrupt;
use crate::pac::pdm::mode::{EDGE_A, OPERATION_A};
pub use crate::pac::pdm::pdmclkctrl::FREQ_A as Frequency;
#[cfg(any(
feature = "nrf52840",
feature = "nrf52833",
feature = "_nrf5340-app",
feature = "_nrf9160",
))]
pub use crate::pac::pdm::ratio::RATIO_A as Ratio;
use crate::{interrupt, 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();
if r.events_end.read().bits() != 0 {
r.intenclr.write(|w| w.end().clear());
}
if r.events_started.read().bits() != 0 {
r.intenclr.write(|w| w.started().clear());
}
if r.events_stopped.read().bits() != 0 {
r.intenclr.write(|w| w.stopped().clear());
}
T::state().waker.wake();
}
}
/// PDM microphone interface
pub struct Pdm<'d, T: Instance> {
_peri: PeripheralRef<'d, T>,
}
/// PDM error.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
/// Buffer is too long.
BufferTooLong,
/// Buffer is empty
BufferZeroLength,
/// PDM is not running
NotRunning,
/// PDM is already running
AlreadyRunning,
}
static DUMMY_BUFFER: [i16; 1] = [0; 1];
/// The state of a continuously running sampler. While it reflects
/// the progress of a sampler, it also signals what should be done
/// next. For example, if the sampler has stopped then the Pdm implementation
/// can then tear down its infrastructure.
#[derive(PartialEq)]
pub enum SamplerState {
/// The sampler processed the samples and is ready for more.
Sampled,
/// The sampler is done processing samples.
Stopped,
}
impl<'d, T: Instance> Pdm<'d, T> {
/// Create PDM driver
pub fn new(
pdm: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
clk: impl Peripheral<P = impl GpioPin> + 'd,
din: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(pdm, clk, din);
Self::new_inner(pdm, clk.map_into(), din.map_into(), config)
}
fn new_inner(
pdm: PeripheralRef<'d, T>,
clk: PeripheralRef<'d, AnyPin>,
din: PeripheralRef<'d, AnyPin>,
config: Config,
) -> Self {
into_ref!(pdm);
let r = T::regs();
// setup gpio pins
din.conf().write(|w| w.input().set_bit());
r.psel.din.write(|w| unsafe { w.bits(din.psel_bits()) });
clk.set_low();
clk.conf().write(|w| w.dir().output());
r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) });
// configure
r.pdmclkctrl.write(|w| w.freq().variant(config.frequency));
#[cfg(any(
feature = "nrf52840",
feature = "nrf52833",
feature = "_nrf5340-app",
feature = "_nrf9160",
))]
r.ratio.write(|w| w.ratio().variant(config.ratio));
r.mode.write(|w| {
w.operation().variant(config.operation_mode.into());
w.edge().variant(config.edge.into());
w
});
Self::_set_gain(r, config.gain_left, config.gain_right);
// Disable all events interrupts
r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) });
// IRQ
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
r.enable.write(|w| w.enable().set_bit());
Self { _peri: pdm }
}
fn _set_gain(r: &crate::pac::pdm::RegisterBlock, gain_left: I7F1, gain_right: I7F1) {
let gain_left = gain_left
.saturating_add(I7F1::from_bits(40))
.saturating_to_num::<u8>()
.clamp(0, 0x50);
let gain_right = gain_right
.saturating_add(I7F1::from_bits(40))
.saturating_to_num::<u8>()
.clamp(0, 0x50);
r.gainl.write(|w| unsafe { w.gainl().bits(gain_left) });
r.gainr.write(|w| unsafe { w.gainr().bits(gain_right) });
}
/// Adjust the gain of the PDM microphone on the fly
pub fn set_gain(&mut self, gain_left: I7F1, gain_right: I7F1) {
Self::_set_gain(T::regs(), gain_left, gain_right)
}
/// Start sampling microphon data into a dummy buffer
/// Usefull to start the microphon and keep it active between recording samples
pub async fn start(&mut self) {
let r = T::regs();
// start dummy sampling because microphon needs some setup time
r.sample
.ptr
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
r.sample
.maxcnt
.write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
r.tasks_start.write(|w| unsafe { w.bits(1) });
}
/// Stop sampling microphon data inta a dummy buffer
pub async fn stop(&mut self) {
let r = T::regs();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
r.events_started.reset();
}
/// Sample data into the given buffer.
pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> {
if buffer.len() == 0 {
return Err(Error::BufferZeroLength);
}
if buffer.len() > EASY_DMA_SIZE {
return Err(Error::BufferTooLong);
}
let r = T::regs();
if r.events_started.read().bits() == 0 {
return Err(Error::NotRunning);
}
let drop = OnDrop::new(move || {
r.intenclr.write(|w| w.end().clear());
r.events_stopped.reset();
// reset to dummy buffer
r.sample
.ptr
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
r.sample
.maxcnt
.write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
while r.events_stopped.read().bits() == 0 {}
});
// setup user buffer
let ptr = buffer.as_ptr();
let len = buffer.len();
r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(ptr as u32) });
r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(len as _) });
// wait till the current sample is finished and the user buffer sample is started
Self::wait_for_sample().await;
// reset the buffer back to the dummy buffer
r.sample
.ptr
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
r.sample
.maxcnt
.write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
// wait till the user buffer is sampled
Self::wait_for_sample().await;
drop.defuse();
Ok(())
}
async fn wait_for_sample() {
let r = T::regs();
r.events_end.reset();
r.intenset.write(|w| w.end().set());
compiler_fence(Ordering::SeqCst);
poll_fn(|cx| {
T::state().waker.register(cx.waker());
if r.events_end.read().bits() != 0 {
return Poll::Ready(());
}
Poll::Pending
})
.await;
compiler_fence(Ordering::SeqCst);
}
/// Continuous sampling with double buffers.
///
/// A sampler closure is provided that receives the buffer of samples, noting
/// that the size of this buffer can be less than the original buffer's size.
/// A command is return from the closure that indicates whether the sampling
/// should continue or stop.
///
/// NOTE: The time spent within the callback supplied should not exceed the time
/// taken to acquire the samples into a single buffer. You should measure the
/// time taken by the callback and set the sample buffer size accordingly.
/// Exceeding this time can lead to samples becoming dropped.
pub async fn run_task_sampler<S, const N: usize>(
&mut self,
bufs: &mut [[i16; N]; 2],
mut sampler: S,
) -> Result<(), Error>
where
S: FnMut(&[i16; N]) -> SamplerState,
{
let r = T::regs();
if r.events_started.read().bits() != 0 {
return Err(Error::AlreadyRunning);
}
r.sample
.ptr
.write(|w| unsafe { w.sampleptr().bits(bufs[0].as_mut_ptr() as u32) });
r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(N as _) });
// Reset and enable the events
r.events_end.reset();
r.events_started.reset();
r.events_stopped.reset();
r.intenset.write(|w| {
w.end().set();
w.started().set();
w.stopped().set();
w
});
// Don't reorder the start event before the previous writes. Hopefully self
// wouldn't happen anyway.
compiler_fence(Ordering::SeqCst);
r.tasks_start.write(|w| unsafe { w.bits(1) });
let mut current_buffer = 0;
let mut done = false;
let drop = OnDrop::new(|| {
r.tasks_stop.write(|w| unsafe { w.bits(1) });
// N.B. It would be better if this were async, but Drop only support sync code.
while r.events_stopped.read().bits() != 0 {}
});
// Wait for events and complete when the sampler indicates it has had enough.
poll_fn(|cx| {
let r = T::regs();
T::state().waker.register(cx.waker());
if r.events_end.read().bits() != 0 {
compiler_fence(Ordering::SeqCst);
r.events_end.reset();
r.intenset.write(|w| w.end().set());
if !done {
// Discard the last buffer after the user requested a stop.
if sampler(&bufs[current_buffer]) == SamplerState::Sampled {
let next_buffer = 1 - current_buffer;
current_buffer = next_buffer;
} else {
r.tasks_stop.write(|w| unsafe { w.bits(1) });
done = true;
};
};
}
if r.events_started.read().bits() != 0 {
r.events_started.reset();
r.intenset.write(|w| w.started().set());
let next_buffer = 1 - current_buffer;
r.sample
.ptr
.write(|w| unsafe { w.sampleptr().bits(bufs[next_buffer].as_mut_ptr() as u32) });
}
if r.events_stopped.read().bits() != 0 {
return Poll::Ready(());
}
Poll::Pending
})
.await;
drop.defuse();
Ok(())
}
}
/// PDM microphone driver Config
pub struct Config {
/// Use stero or mono operation
pub operation_mode: OperationMode,
/// On which edge the left channel should be samples
pub edge: Edge,
/// Clock frequency
pub frequency: Frequency,
/// Clock ratio
#[cfg(any(
feature = "nrf52840",
feature = "nrf52833",
feature = "_nrf5340-app",
feature = "_nrf9160",
))]
pub ratio: Ratio,
/// Gain left in dB
pub gain_left: I7F1,
/// Gain right in dB
pub gain_right: I7F1,
}
impl Default for Config {
fn default() -> Self {
Self {
operation_mode: OperationMode::Mono,
edge: Edge::LeftFalling,
frequency: Frequency::DEFAULT,
#[cfg(any(
feature = "nrf52840",
feature = "nrf52833",
feature = "_nrf5340-app",
feature = "_nrf9160",
))]
ratio: Ratio::RATIO80,
gain_left: I7F1::ZERO,
gain_right: I7F1::ZERO,
}
}
}
/// PDM operation mode.
#[derive(PartialEq)]
pub enum OperationMode {
/// Mono (1 channel)
Mono,
/// Stereo (2 channels)
Stereo,
}
impl From<OperationMode> for OPERATION_A {
fn from(mode: OperationMode) -> Self {
match mode {
OperationMode::Mono => OPERATION_A::MONO,
OperationMode::Stereo => OPERATION_A::STEREO,
}
}
}
/// PDM edge polarity
#[derive(PartialEq)]
pub enum Edge {
/// Left edge is rising
LeftRising,
/// Left edge is falling
LeftFalling,
}
impl From<Edge> for EDGE_A {
fn from(edge: Edge) -> Self {
match edge {
Edge::LeftRising => EDGE_A::LEFT_RISING,
Edge::LeftFalling => EDGE_A::LEFT_FALLING,
}
}
}
impl<'d, T: Instance> Drop for Pdm<'d, T> {
fn drop(&mut self) {
let r = T::regs();
r.tasks_stop.write(|w| unsafe { w.bits(1) });
r.enable.write(|w| w.enable().disabled());
r.psel.din.reset();
r.psel.clk.reset();
}
}
pub(crate) mod sealed {
use embassy_sync::waitqueue::AtomicWaker;
/// Peripheral static state
pub struct State {
pub waker: AtomicWaker,
}
impl State {
pub const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
}
}
pub trait Instance {
fn regs() -> &'static crate::pac::pdm::RegisterBlock;
fn state() -> &'static State;
}
}
/// PDM peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
/// Interrupt for this peripheral.
type Interrupt: interrupt::typelevel::Interrupt;
}
macro_rules! impl_pdm {
($type:ident, $pac_type:ident, $irq:ident) => {
impl crate::pdm::sealed::Instance for peripherals::$type {
fn regs() -> &'static crate::pac::pdm::RegisterBlock {
unsafe { &*pac::$pac_type::ptr() }
}
fn state() -> &'static crate::pdm::sealed::State {
static STATE: crate::pdm::sealed::State = crate::pdm::sealed::State::new();
&STATE
}
}
impl crate::pdm::Instance for peripherals::$type {
type Interrupt = crate::interrupt::typelevel::$irq;
}
};
}

View File

@ -12,14 +12,14 @@ pub(crate) fn regs() -> &'static pac::dppic::RegisterBlock {
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
/// Configure PPI channel to trigger `task` on `event`.
pub fn new_one_to_one(ch: impl Peripheral<P = C> + 'd, event: Event, task: Task) -> Self {
pub fn new_one_to_one(ch: impl Peripheral<P = C> + 'd, event: Event<'d>, task: Task<'d>) -> Self {
Ppi::new_many_to_many(ch, [event], [task])
}
}
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {
/// Configure PPI channel to trigger both `task1` and `task2` on `event`.
pub fn new_one_to_two(ch: impl Peripheral<P = C> + 'd, event: Event, task1: Task, task2: Task) -> Self {
pub fn new_one_to_two(ch: impl Peripheral<P = C> + 'd, event: Event<'d>, task1: Task<'d>, task2: Task<'d>) -> Self {
Ppi::new_many_to_many(ch, [event], [task1, task2])
}
}
@ -30,8 +30,8 @@ impl<'d, C: ConfigurableChannel, const EVENT_COUNT: usize, const TASK_COUNT: usi
/// Configure a DPPI channel to trigger all `tasks` when any of the `events` fires.
pub fn new_many_to_many(
ch: impl Peripheral<P = C> + 'd,
events: [Event; EVENT_COUNT],
tasks: [Task; TASK_COUNT],
events: [Event<'d>; EVENT_COUNT],
tasks: [Task<'d>; TASK_COUNT],
) -> Self {
into_ref!(ch);

View File

@ -15,6 +15,7 @@
//! many tasks and events, but any single task or event can only be coupled with one channel.
//!
use core::marker::PhantomData;
use core::ptr::NonNull;
use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef};
@ -30,9 +31,9 @@ pub(crate) use _version::*;
pub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> {
ch: PeripheralRef<'d, C>,
#[cfg(feature = "_dppi")]
events: [Event; EVENT_COUNT],
events: [Event<'d>; EVENT_COUNT],
#[cfg(feature = "_dppi")]
tasks: [Task; TASK_COUNT],
tasks: [Task<'d>; TASK_COUNT],
}
/// PPI channel group driver.
@ -95,7 +96,7 @@ impl<'d, G: Group> PpiGroup<'d, G> {
/// Get a reference to the "enable all" task.
///
/// When triggered, it will enable all the channels in this group.
pub fn task_enable_all(&self) -> Task {
pub fn task_enable_all(&self) -> Task<'d> {
let n = self.g.number();
Task::from_reg(&regs().tasks_chg[n].en)
}
@ -103,7 +104,7 @@ impl<'d, G: Group> PpiGroup<'d, G> {
/// Get a reference to the "disable all" task.
///
/// When triggered, it will disable all the channels in this group.
pub fn task_disable_all(&self) -> Task {
pub fn task_disable_all(&self) -> Task<'d> {
let n = self.g.number();
Task::from_reg(&regs().tasks_chg[n].dis)
}
@ -125,16 +126,16 @@ const REGISTER_DPPI_CONFIG_OFFSET: usize = 0x80 / core::mem::size_of::<u32>();
/// When a task is subscribed to a PPI channel, it will run when the channel is triggered by
/// a published event.
#[derive(PartialEq, Eq, Clone, Copy)]
pub struct Task(NonNull<u32>);
pub struct Task<'d>(NonNull<u32>, PhantomData<&'d ()>);
impl Task {
impl<'d> Task<'d> {
/// Create a new `Task` from a task register pointer
///
/// # Safety
///
/// `ptr` must be a pointer to a valid `TASKS_*` register from an nRF peripheral.
pub unsafe fn new_unchecked(ptr: NonNull<u32>) -> Self {
Self(ptr)
Self(ptr, PhantomData)
}
/// Triggers this task.
@ -143,7 +144,10 @@ impl Task {
}
pub(crate) fn from_reg<T>(reg: &T) -> Self {
Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
Self(
unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) },
PhantomData,
)
}
/// Address of subscription register for this task.
@ -156,26 +160,29 @@ impl Task {
/// # Safety
///
/// NonNull is not send, but this event is only allowed to point at registers and those exist in any context on the same core.
unsafe impl Send for Task {}
unsafe impl Send for Task<'_> {}
/// Represents an event that a peripheral can publish.
///
/// An event can be set to publish on a PPI channel when the event happens.
#[derive(PartialEq, Eq, Clone, Copy)]
pub struct Event(NonNull<u32>);
pub struct Event<'d>(NonNull<u32>, PhantomData<&'d ()>);
impl Event {
impl<'d> Event<'d> {
/// Create a new `Event` from an event register pointer
///
/// # Safety
///
/// `ptr` must be a pointer to a valid `EVENTS_*` register from an nRF peripheral.
pub unsafe fn new_unchecked(ptr: NonNull<u32>) -> Self {
Self(ptr)
Self(ptr, PhantomData)
}
pub(crate) fn from_reg<T>(reg: &T) -> Self {
Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
pub(crate) fn from_reg<T>(reg: &'d T) -> Self {
Self(
unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) },
PhantomData,
)
}
/// Describes whether this Event is currently in a triggered state.
@ -198,7 +205,7 @@ impl Event {
/// # Safety
///
/// NonNull is not send, but this event is only allowed to point at registers and those exist in any context on the same core.
unsafe impl Send for Event {}
unsafe impl Send for Event<'_> {}
// ======================
// traits

View File

@ -3,12 +3,12 @@ use embassy_hal_common::into_ref;
use super::{Channel, ConfigurableChannel, Event, Ppi, StaticChannel, Task};
use crate::{pac, Peripheral};
impl Task {
impl<'d> Task<'d> {
fn reg_val(&self) -> u32 {
self.0.as_ptr() as _
}
}
impl Event {
impl<'d> Event<'d> {
fn reg_val(&self) -> u32 {
self.0.as_ptr() as _
}
@ -34,7 +34,7 @@ impl<'d, C: StaticChannel> Ppi<'d, C, 0, 1> {
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
/// Configure PPI channel to trigger `task` on `event`.
pub fn new_one_to_one(ch: impl Peripheral<P = C> + 'd, event: Event, task: Task) -> Self {
pub fn new_one_to_one(ch: impl Peripheral<P = C> + 'd, event: Event<'d>, task: Task<'d>) -> Self {
into_ref!(ch);
let r = regs();
@ -49,7 +49,7 @@ impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
#[cfg(not(feature = "nrf51"))] // Not for nrf51 because of the fork task
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {
/// Configure PPI channel to trigger both `task1` and `task2` on `event`.
pub fn new_one_to_two(ch: impl Peripheral<P = C> + 'd, event: Event, task1: Task, task2: Task) -> Self {
pub fn new_one_to_two(ch: impl Peripheral<P = C> + 'd, event: Event<'d>, task1: Task<'d>, task2: Task<'d>) -> Self {
into_ref!(ch);
let r = regs();

View File

@ -181,7 +181,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
/// Returns reference to `Stopped` event endpoint for PPI.
#[inline(always)]
pub fn event_stopped(&self) -> Event {
pub fn event_stopped(&self) -> Event<'d> {
let r = T::regs();
Event::from_reg(&r.events_stopped)
@ -189,7 +189,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
/// Returns reference to `LoopsDone` event endpoint for PPI.
#[inline(always)]
pub fn event_loops_done(&self) -> Event {
pub fn event_loops_done(&self) -> Event<'d> {
let r = T::regs();
Event::from_reg(&r.events_loopsdone)
@ -197,7 +197,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
/// Returns reference to `PwmPeriodEnd` event endpoint for PPI.
#[inline(always)]
pub fn event_pwm_period_end(&self) -> Event {
pub fn event_pwm_period_end(&self) -> Event<'d> {
let r = T::regs();
Event::from_reg(&r.events_pwmperiodend)
@ -205,7 +205,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
/// Returns reference to `Seq0 End` event endpoint for PPI.
#[inline(always)]
pub fn event_seq_end(&self) -> Event {
pub fn event_seq_end(&self) -> Event<'d> {
let r = T::regs();
Event::from_reg(&r.events_seqend[0])
@ -213,7 +213,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
/// Returns reference to `Seq1 End` event endpoint for PPI.
#[inline(always)]
pub fn event_seq1_end(&self) -> Event {
pub fn event_seq1_end(&self) -> Event<'d> {
let r = T::regs();
Event::from_reg(&r.events_seqend[1])
@ -221,7 +221,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
/// Returns reference to `Seq0 Started` event endpoint for PPI.
#[inline(always)]
pub fn event_seq0_started(&self) -> Event {
pub fn event_seq0_started(&self) -> Event<'d> {
let r = T::regs();
Event::from_reg(&r.events_seqstarted[0])
@ -229,7 +229,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
/// Returns reference to `Seq1 Started` event endpoint for PPI.
#[inline(always)]
pub fn event_seq1_started(&self) -> Event {
pub fn event_seq1_started(&self) -> Event<'d> {
let r = T::regs();
Event::from_reg(&r.events_seqstarted[1])
@ -240,7 +240,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
///
/// Interacting with the sequence while it runs puts it in an unknown state
#[inline(always)]
pub unsafe fn task_start_seq0(&self) -> Task {
pub unsafe fn task_start_seq0(&self) -> Task<'d> {
let r = T::regs();
Task::from_reg(&r.tasks_seqstart[0])
@ -251,7 +251,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
///
/// Interacting with the sequence while it runs puts it in an unknown state
#[inline(always)]
pub unsafe fn task_start_seq1(&self) -> Task {
pub unsafe fn task_start_seq1(&self) -> Task<'d> {
let r = T::regs();
Task::from_reg(&r.tasks_seqstart[1])
@ -262,7 +262,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
///
/// Interacting with the sequence while it runs puts it in an unknown state
#[inline(always)]
pub unsafe fn task_next_step(&self) -> Task {
pub unsafe fn task_next_step(&self) -> Task<'d> {
let r = T::regs();
Task::from_reg(&r.tasks_nextstep)
@ -273,7 +273,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
///
/// Interacting with the sequence while it runs puts it in an unknown state
#[inline(always)]
pub unsafe fn task_stop(&self) -> Task {
pub unsafe fn task_stop(&self) -> Task<'d> {
let r = T::regs();
Task::from_reg(&r.tasks_stop)

View File

@ -320,7 +320,9 @@ impl<'d, const N: usize> Saadc<'d, N> {
timer.cc(0).write(sample_counter);
timer.cc(0).short_compare_clear();
let mut sample_ppi = Ppi::new_one_to_one(ppi_ch2, timer.cc(0).event_compare(), Task::from_reg(&r.tasks_sample));
let timer_cc = timer.cc(0);
let mut sample_ppi = Ppi::new_one_to_one(ppi_ch2, timer_cc.event_compare(), Task::from_reg(&r.tasks_sample));
timer.start();

View File

@ -468,25 +468,19 @@ mod eh1 {
type Error = Error;
}
impl<'d, T: Instance> embedded_hal_1::spi::SpiBusFlush for Spim<'d, T> {
impl<'d, T: Instance> embedded_hal_1::spi::SpiBus<u8> for Spim<'d, T> {
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl<'d, T: Instance> embedded_hal_1::spi::SpiBusRead<u8> for Spim<'d, T> {
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_transfer(words, &[])
}
}
impl<'d, T: Instance> embedded_hal_1::spi::SpiBusWrite<u8> for Spim<'d, T> {
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(words)
}
}
impl<'d, T: Instance> embedded_hal_1::spi::SpiBus<u8> for Spim<'d, T> {
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
self.blocking_transfer(read, write)
}
@ -502,25 +496,19 @@ mod eha {
use super::*;
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusFlush for Spim<'d, T> {
impl<'d, T: Instance> embedded_hal_async::spi::SpiBus<u8> for Spim<'d, T> {
async fn flush(&mut self) -> Result<(), Error> {
Ok(())
}
}
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusRead<u8> for Spim<'d, T> {
async fn read(&mut self, words: &mut [u8]) -> Result<(), Error> {
self.read(words).await
}
}
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusWrite<u8> for Spim<'d, T> {
async fn write(&mut self, data: &[u8]) -> Result<(), Error> {
self.write(data).await
}
}
impl<'d, T: Instance> embedded_hal_async::spi::SpiBus<u8> for Spim<'d, T> {
async fn transfer(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> {
self.transfer(rx, tx).await
}

View File

@ -168,21 +168,21 @@ impl<'d, T: Instance> Timer<'d, T> {
/// Returns the START task, for use with PPI.
///
/// When triggered, this task starts the timer.
pub fn task_start(&self) -> Task {
pub fn task_start(&self) -> Task<'d> {
Task::from_reg(&T::regs().tasks_start)
}
/// Returns the STOP task, for use with PPI.
///
/// When triggered, this task stops the timer.
pub fn task_stop(&self) -> Task {
pub fn task_stop(&self) -> Task<'d> {
Task::from_reg(&T::regs().tasks_stop)
}
/// Returns the CLEAR task, for use with PPI.
///
/// When triggered, this task resets the timer's counter to 0.
pub fn task_clear(&self) -> Task {
pub fn task_clear(&self) -> Task<'d> {
Task::from_reg(&T::regs().tasks_clear)
}
@ -190,7 +190,7 @@ impl<'d, T: Instance> Timer<'d, T> {
///
/// When triggered, this task increments the timer's counter by 1.
/// Only works in counter mode.
pub fn task_count(&self) -> Task {
pub fn task_count(&self) -> Task<'d> {
Task::from_reg(&T::regs().tasks_count)
}
@ -258,14 +258,14 @@ impl<'d, T: Instance> Cc<'d, T> {
/// Returns this CC register's CAPTURE task, for use with PPI.
///
/// When triggered, this task will capture the current value of the timer's counter in this register.
pub fn task_capture(&self) -> Task {
pub fn task_capture(&self) -> Task<'d> {
Task::from_reg(&T::regs().tasks_capture)
}
/// Returns this CC register's COMPARE event, for use with PPI.
///
/// This event will fire when the timer's counter reaches the value in this CC register.
pub fn event_compare(&self) -> Event {
pub fn event_compare(&self) -> Event<'d> {
Event::from_reg(&T::regs().events_compare[self.n])
}

View File

@ -56,7 +56,7 @@ unstable-traits = ["embedded-hal-1", "embedded-hal-nb"]
[dependencies]
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] }
embassy-time = { version = "0.1.2", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common", features = ["cortex-m", "prio-bits-2"] }
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
@ -79,9 +79,9 @@ fixed = "1.23.1"
rp-pac = { version = "6" }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true}
embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true}
embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true}
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true}
embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true}
embedded-hal-nb = { version = "=1.0.0-alpha.3", optional = true}
paste = "1.0"
pio-proc = {version= "0.2" }

View File

@ -3,22 +3,17 @@ use core::marker::PhantomData;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use embedded_hal_02::adc::{Channel, OneShot};
use crate::gpio::Pin;
use crate::gpio::sealed::Pin as GpioPin;
use crate::gpio::{self, AnyPin, Pull};
use crate::interrupt::typelevel::Binding;
use crate::interrupt::InterruptExt;
use crate::peripherals::ADC;
use crate::{interrupt, pac, peripherals, Peripheral};
static WAKER: AtomicWaker = AtomicWaker::new();
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
// No errors for now
}
static WAKER: AtomicWaker = AtomicWaker::new();
#[non_exhaustive]
pub struct Config {}
@ -28,11 +23,75 @@ impl Default for Config {
Self {}
}
}
pub struct Adc<'d> {
phantom: PhantomData<&'d ADC>,
pub struct Pin<'p> {
pin: PeripheralRef<'p, AnyPin>,
}
impl<'d> Adc<'d> {
impl<'p> Pin<'p> {
pub fn new(pin: impl Peripheral<P = impl AdcPin + 'p> + 'p, pull: Pull) -> Self {
into_ref!(pin);
pin.pad_ctrl().modify(|w| {
// manual says:
//
// > When using an ADC input shared with a GPIO pin, the pins
// > digital functions must be disabled by setting IE low and OD
// > high in the pins pad control register
w.set_ie(false);
w.set_od(true);
w.set_pue(pull == Pull::Up);
w.set_pde(pull == Pull::Down);
});
Self { pin: pin.map_into() }
}
fn channel(&self) -> u8 {
// this requires adc pins to be sequential and matching the adc channels,
// which is the case for rp2040
self.pin._pin() - 26
}
}
impl<'d> Drop for Pin<'d> {
fn drop(&mut self) {
self.pin.pad_ctrl().modify(|w| {
w.set_ie(true);
w.set_od(false);
w.set_pue(false);
w.set_pde(true);
});
}
}
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
ConversionFailed,
}
pub trait Mode {}
pub struct Async;
impl Mode for Async {}
pub struct Blocking;
impl Mode for Blocking {}
pub struct Adc<'d, M: Mode> {
phantom: PhantomData<(&'d ADC, M)>,
}
impl<'d, M: Mode> Drop for Adc<'d, M> {
fn drop(&mut self) {
let r = Self::regs();
// disable ADC. leaving it enabled comes with a ~150µA static
// current draw. the temperature sensor has already been disabled
// by the temperature-reading methods, so we don't need to touch that.
r.cs().write(|w| w.set_en(false));
}
}
impl<'d, M: Mode> Adc<'d, M> {
#[inline]
fn regs() -> pac::adc::Adc {
pac::ADC
@ -45,11 +104,7 @@ impl<'d> Adc<'d> {
ret
}
pub fn new(
_inner: impl Peripheral<P = ADC> + 'd,
_irq: impl Binding<interrupt::typelevel::ADC_IRQ_FIFO, InterruptHandler>,
_config: Config,
) -> Self {
fn setup() {
let reset = Self::reset();
crate::reset::reset(reset);
crate::reset::unreset_wait(reset);
@ -58,6 +113,43 @@ impl<'d> Adc<'d> {
r.cs().write(|w| w.set_en(true));
// Wait for ADC ready
while !r.cs().read().ready() {}
}
fn sample_blocking(channel: u8) -> Result<u16, Error> {
let r = Self::regs();
r.cs().modify(|w| {
w.set_ainsel(channel);
w.set_start_once(true);
w.set_err(true);
});
while !r.cs().read().ready() {}
match r.cs().read().err() {
true => Err(Error::ConversionFailed),
false => Ok(r.result().read().result().into()),
}
}
pub fn blocking_read(&mut self, pin: &mut Pin) -> Result<u16, Error> {
Self::sample_blocking(pin.channel())
}
pub fn blocking_read_temperature(&mut self) -> Result<u16, Error> {
let r = Self::regs();
r.cs().modify(|w| w.set_ts_en(true));
while !r.cs().read().ready() {}
let result = Self::sample_blocking(4);
r.cs().modify(|w| w.set_ts_en(false));
result
}
}
impl<'d> Adc<'d, Async> {
pub fn new(
_inner: impl Peripheral<P = ADC> + 'd,
_irq: impl Binding<interrupt::typelevel::ADC_IRQ_FIFO, InterruptHandler>,
_config: Config,
) -> Self {
Self::setup();
// Setup IRQ
interrupt::ADC_IRQ_FIFO.unpend();
@ -80,76 +172,42 @@ impl<'d> Adc<'d> {
.await;
}
pub async fn read<PIN: Channel<Adc<'d>, ID = u8> + Pin>(&mut self, pin: &mut PIN) -> u16 {
async fn sample_async(channel: u8) -> Result<u16, Error> {
let r = Self::regs();
// disable pull-down and pull-up resistors
// pull-down resistors are enabled by default
pin.pad_ctrl().modify(|w| {
w.set_ie(true);
let (pu, pd) = (false, false);
w.set_pue(pu);
w.set_pde(pd);
});
r.cs().modify(|w| {
w.set_ainsel(PIN::channel());
w.set_start_once(true)
w.set_ainsel(channel);
w.set_start_once(true);
w.set_err(true);
});
Self::wait_for_ready().await;
r.result().read().result().into()
match r.cs().read().err() {
true => Err(Error::ConversionFailed),
false => Ok(r.result().read().result().into()),
}
}
pub async fn read_temperature(&mut self) -> u16 {
pub async fn read(&mut self, pin: &mut Pin<'_>) -> Result<u16, Error> {
Self::sample_async(pin.channel()).await
}
pub async fn read_temperature(&mut self) -> Result<u16, Error> {
let r = Self::regs();
r.cs().modify(|w| w.set_ts_en(true));
if !r.cs().read().ready() {
Self::wait_for_ready().await;
}
r.cs().modify(|w| {
w.set_ainsel(4);
w.set_start_once(true)
});
Self::wait_for_ready().await;
r.result().read().result().into()
}
pub fn blocking_read<PIN: Channel<Adc<'d>, ID = u8> + Pin>(&mut self, pin: &mut PIN) -> u16 {
let r = Self::regs();
pin.pad_ctrl().modify(|w| {
w.set_ie(true);
let (pu, pd) = (false, false);
w.set_pue(pu);
w.set_pde(pd);
});
r.cs().modify(|w| {
w.set_ainsel(PIN::channel());
w.set_start_once(true)
});
while !r.cs().read().ready() {}
r.result().read().result().into()
}
pub fn blocking_read_temperature(&mut self) -> u16 {
let r = Self::regs();
r.cs().modify(|w| w.set_ts_en(true));
while !r.cs().read().ready() {}
r.cs().modify(|w| {
w.set_ainsel(4);
w.set_start_once(true)
});
while !r.cs().read().ready() {}
r.result().read().result().into()
let result = Self::sample_async(4).await;
r.cs().modify(|w| w.set_ts_en(false));
result
}
}
macro_rules! impl_pin {
($pin:ident, $channel:expr) => {
impl Channel<Adc<'static>> for peripherals::$pin {
type ID = u8;
fn channel() -> u8 {
$channel
}
}
};
impl<'d> Adc<'d, Blocking> {
pub fn new_blocking(_inner: impl Peripheral<P = ADC> + 'd, _config: Config) -> Self {
Self::setup();
Self { phantom: PhantomData }
}
}
pub struct InterruptHandler {
@ -158,24 +216,33 @@ pub struct InterruptHandler {
impl interrupt::typelevel::Handler<interrupt::typelevel::ADC_IRQ_FIFO> for InterruptHandler {
unsafe fn on_interrupt() {
let r = Adc::regs();
let r = Adc::<Async>::regs();
r.inte().write(|w| w.set_fifo(false));
WAKER.wake();
}
}
mod sealed {
pub trait AdcPin: crate::gpio::sealed::Pin {
fn channel(&mut self) -> u8;
}
}
pub trait AdcPin: sealed::AdcPin + gpio::Pin {}
macro_rules! impl_pin {
($pin:ident, $channel:expr) => {
impl sealed::AdcPin for peripherals::$pin {
fn channel(&mut self) -> u8 {
$channel
}
}
impl AdcPin for peripherals::$pin {}
};
}
impl_pin!(PIN_26, 0);
impl_pin!(PIN_27, 1);
impl_pin!(PIN_28, 2);
impl_pin!(PIN_29, 3);
impl<WORD, PIN> OneShot<Adc<'static>, WORD, PIN> for Adc<'static>
where
WORD: From<u16>,
PIN: Channel<Adc<'static>, ID = u8> + Pin,
{
type Error = ();
fn read(&mut self, pin: &mut PIN) -> nb::Result<WORD, Self::Error> {
Ok(self.blocking_read(pin).into())
}
}

View File

@ -308,6 +308,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
// - QSPI (we're using it to run this code!)
// - PLLs (it may be suicide if that's what's clocking us)
// - USB, SYSCFG (breaks usb-to-swd on core1)
// - RTC (else there would be no more time...)
let mut peris = reset::ALL_PERIPHERALS;
peris.set_io_qspi(false);
// peris.set_io_bank0(false); // might be suicide if we're clocked from gpin
@ -317,6 +318,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
// TODO investigate if usb should be unreset here
peris.set_usbctrl(false);
peris.set_syscfg(false);
peris.set_rtc(false);
reset::reset(peris);
// Disable resus that may be enabled from previous software

View File

@ -41,7 +41,7 @@ impl From<Level> for bool {
}
/// Represents a pull setting for an input.
#[derive(Debug, Eq, PartialEq)]
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum Pull {
None,
Up,
@ -566,13 +566,13 @@ impl<'d, T: Pin> Flex<'d, T> {
/// Is the output level high?
#[inline]
pub fn is_set_high(&self) -> bool {
(self.pin.sio_out().value().read() & self.bit()) == 0
!self.is_set_low()
}
/// Is the output level low?
#[inline]
pub fn is_set_low(&self) -> bool {
!self.is_set_high()
(self.pin.sio_out().value().read() & self.bit()) == 0
}
/// What level output is set to

View File

@ -716,6 +716,9 @@ mod nightly {
async fn transaction(&mut self, address: A, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> {
let addr: u16 = address.into();
if operations.len() > 0 {
Self::setup(addr)?;
}
let mut iterator = operations.iter_mut();
while let Some(op) = iterator.next() {
@ -723,11 +726,9 @@ mod nightly {
match op {
Operation::Read(buffer) => {
Self::setup(addr)?;
self.read_async_internal(buffer, false, last).await?;
}
Operation::Write(buffer) => {
Self::setup(addr)?;
self.write_async_internal(buffer.into_iter().cloned(), last).await?;
}
}

View File

@ -252,7 +252,6 @@ pub fn init(config: config::Config) -> Peripherals {
#[cfg(feature = "time-driver")]
timer::init();
dma::init();
pio::init();
gpio::init();
}

View File

@ -16,12 +16,12 @@ use pio::{SideSet, Wrap};
use crate::dma::{Channel, Transfer, Word};
use crate::gpio::sealed::Pin as SealedPin;
use crate::gpio::{self, AnyPin, Drive, Level, Pull, SlewRate};
use crate::interrupt::InterruptExt;
use crate::interrupt::typelevel::{Binding, Handler, Interrupt};
use crate::pac::dma::vals::TreqSel;
use crate::relocate::RelocatedProgram;
use crate::{interrupt, pac, peripherals, pio_instr_util, RegExt};
use crate::{pac, peripherals, pio_instr_util, RegExt};
struct Wakers([AtomicWaker; 12]);
pub struct Wakers([AtomicWaker; 12]);
impl Wakers {
#[inline(always)]
@ -38,10 +38,6 @@ impl Wakers {
}
}
const NEW_AW: AtomicWaker = AtomicWaker::new();
const PIO_WAKERS_INIT: Wakers = Wakers([NEW_AW; 12]);
static WAKERS: [Wakers; 2] = [PIO_WAKERS_INIT; 2];
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
@ -85,42 +81,20 @@ const RXNEMPTY_MASK: u32 = 1 << 0;
const TXNFULL_MASK: u32 = 1 << 4;
const SMIRQ_MASK: u32 = 1 << 8;
#[cfg(feature = "rt")]
#[interrupt]
fn PIO0_IRQ_0() {
use crate::pac;
let ints = pac::PIO0.irqs(0).ints().read().0;
for bit in 0..12 {
if ints & (1 << bit) != 0 {
WAKERS[0].0[bit].wake();
}
}
pac::PIO0.irqs(0).inte().write_clear(|m| m.0 = ints);
pub struct InterruptHandler<PIO: Instance> {
_pio: PhantomData<PIO>,
}
#[cfg(feature = "rt")]
#[interrupt]
fn PIO1_IRQ_0() {
use crate::pac;
let ints = pac::PIO1.irqs(0).ints().read().0;
for bit in 0..12 {
if ints & (1 << bit) != 0 {
WAKERS[1].0[bit].wake();
impl<PIO: Instance> Handler<PIO::Interrupt> for InterruptHandler<PIO> {
unsafe fn on_interrupt() {
let ints = PIO::PIO.irqs(0).ints().read().0;
for bit in 0..12 {
if ints & (1 << bit) != 0 {
PIO::wakers().0[bit].wake();
}
}
PIO::PIO.irqs(0).inte().write_clear(|m| m.0 = ints);
}
pac::PIO1.irqs(0).inte().write_clear(|m| m.0 = ints);
}
pub(crate) unsafe fn init() {
interrupt::PIO0_IRQ_0.disable();
interrupt::PIO0_IRQ_0.set_priority(interrupt::Priority::P3);
pac::PIO0.irqs(0).inte().write(|m| m.0 = 0);
interrupt::PIO0_IRQ_0.enable();
interrupt::PIO1_IRQ_0.disable();
interrupt::PIO1_IRQ_0.set_priority(interrupt::Priority::P3);
pac::PIO1.irqs(0).inte().write(|m| m.0 = 0);
interrupt::PIO1_IRQ_0.enable();
}
/// Future that waits for TX-FIFO to become writable
@ -144,7 +118,7 @@ impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoOutFuture<'a, 'd, PI
if self.get_mut().sm_tx.try_push(value) {
Poll::Ready(())
} else {
WAKERS[PIO::PIO_NO as usize].fifo_out()[SM].register(cx.waker());
PIO::wakers().fifo_out()[SM].register(cx.waker());
PIO::PIO.irqs(0).inte().write_set(|m| {
m.0 = TXNFULL_MASK << SM;
});
@ -181,7 +155,7 @@ impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoInFuture<'a, 'd, PIO
if let Some(v) = self.sm_rx.try_pull() {
Poll::Ready(v)
} else {
WAKERS[PIO::PIO_NO as usize].fifo_in()[SM].register(cx.waker());
PIO::wakers().fifo_in()[SM].register(cx.waker());
PIO::PIO.irqs(0).inte().write_set(|m| {
m.0 = RXNEMPTY_MASK << SM;
});
@ -217,7 +191,7 @@ impl<'a, 'd, PIO: Instance> Future for IrqFuture<'a, 'd, PIO> {
return Poll::Ready(());
}
WAKERS[PIO::PIO_NO as usize].irq()[self.irq_no as usize].register(cx.waker());
PIO::wakers().irq()[self.irq_no as usize].register(cx.waker());
PIO::PIO.irqs(0).inte().write_set(|m| {
m.0 = SMIRQ_MASK << self.irq_no;
});
@ -949,9 +923,11 @@ pub struct Pio<'d, PIO: Instance> {
}
impl<'d, PIO: Instance> Pio<'d, PIO> {
pub fn new(_pio: impl Peripheral<P = PIO> + 'd) -> Self {
pub fn new(_pio: impl Peripheral<P = PIO> + 'd, _irq: impl Binding<PIO::Interrupt, InterruptHandler<PIO>>) -> Self {
PIO::state().users.store(5, Ordering::Release);
PIO::state().used_pins.store(0, Ordering::Release);
PIO::Interrupt::unpend();
unsafe { PIO::Interrupt::enable() };
Self {
common: Common {
instructions_used: 0,
@ -1017,6 +993,15 @@ mod sealed {
const PIO_NO: u8;
const PIO: &'static crate::pac::pio::Pio;
const FUNCSEL: crate::pac::io::vals::Gpio0ctrlFuncsel;
type Interrupt: crate::interrupt::typelevel::Interrupt;
#[inline]
fn wakers() -> &'static Wakers {
const NEW_AW: AtomicWaker = AtomicWaker::new();
static WAKERS: Wakers = Wakers([NEW_AW; 12]);
&WAKERS
}
#[inline]
fn state() -> &'static State {
@ -1033,18 +1018,19 @@ mod sealed {
pub trait Instance: sealed::Instance + Sized + Unpin {}
macro_rules! impl_pio {
($name:ident, $pio:expr, $pac:ident, $funcsel:ident) => {
($name:ident, $pio:expr, $pac:ident, $funcsel:ident, $irq:ident) => {
impl sealed::Instance for peripherals::$name {
const PIO_NO: u8 = $pio;
const PIO: &'static pac::pio::Pio = &pac::$pac;
const FUNCSEL: pac::io::vals::Gpio0ctrlFuncsel = pac::io::vals::Gpio0ctrlFuncsel::$funcsel;
type Interrupt = crate::interrupt::typelevel::$irq;
}
impl Instance for peripherals::$name {}
};
}
impl_pio!(PIO0, 0, PIO0, PIO0_0);
impl_pio!(PIO1, 1, PIO1, PIO1_0);
impl_pio!(PIO0, 0, PIO0, PIO0_0, PIO0_IRQ_0);
impl_pio!(PIO1, 1, PIO1, PIO1_0, PIO1_IRQ_0);
pub trait PioPin: sealed::PioPin + gpio::Pin {}

View File

@ -25,6 +25,7 @@ pub enum Error {
}
/// Structure containing date and time information
#[derive(Clone, Debug)]
pub struct DateTime {
/// 0..4095
pub year: u16,

View File

@ -12,26 +12,24 @@ pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError};
use crate::clocks::clk_rtc_freq;
/// A reference to the real time clock of the system
pub struct RealTimeClock<'d, T: Instance> {
pub struct Rtc<'d, T: Instance> {
inner: PeripheralRef<'d, T>,
}
impl<'d, T: Instance> RealTimeClock<'d, T> {
impl<'d, T: Instance> Rtc<'d, T> {
/// Create a new instance of the real time clock, with the given date as an initial value.
///
/// # Errors
///
/// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.
pub fn new(inner: impl Peripheral<P = T> + 'd, initial_date: DateTime) -> Result<Self, RtcError> {
pub fn new(inner: impl Peripheral<P = T> + 'd) -> Self {
into_ref!(inner);
// Set the RTC divider
inner.regs().clkdiv_m1().write(|w| w.set_clkdiv_m1(clk_rtc_freq() - 1));
let mut result = Self { inner };
result.set_leap_year_check(true); // should be on by default, make sure this is the case.
result.set_datetime(initial_date)?;
Ok(result)
let result = Self { inner };
result
}
/// Enable or disable the leap year check. The rp2040 chip will always add a Feb 29th on every year that is divisable by 4, but this may be incorrect (e.g. on century years). This function allows you to disable this check.
@ -43,7 +41,37 @@ impl<'d, T: Instance> RealTimeClock<'d, T> {
});
}
/// Checks to see if this RealTimeClock is running
/// Set the time from internal format
pub fn restore(&mut self, ymd: rp_pac::rtc::regs::Rtc1, hms: rp_pac::rtc::regs::Rtc0) {
// disable RTC while we configure it
self.inner.regs().ctrl().modify(|w| w.set_rtc_enable(false));
while self.inner.regs().ctrl().read().rtc_active() {
core::hint::spin_loop();
}
self.inner.regs().setup_0().write(|w| {
*w = rp_pac::rtc::regs::Setup0(ymd.0);
});
self.inner.regs().setup_1().write(|w| {
*w = rp_pac::rtc::regs::Setup1(hms.0);
});
// Load the new datetime and re-enable RTC
self.inner.regs().ctrl().write(|w| w.set_load(true));
self.inner.regs().ctrl().write(|w| w.set_rtc_enable(true));
while !self.inner.regs().ctrl().read().rtc_active() {
core::hint::spin_loop();
}
}
/// Get the time in internal format
pub fn save(&mut self) -> (rp_pac::rtc::regs::Rtc1, rp_pac::rtc::regs::Rtc0) {
let rtc_0: rp_pac::rtc::regs::Rtc0 = self.inner.regs().rtc_0().read();
let rtc_1 = self.inner.regs().rtc_1().read();
(rtc_1, rtc_0)
}
/// Checks to see if this Rtc is running
pub fn is_running(&self) -> bool {
self.inner.regs().ctrl().read().rtc_active()
}
@ -113,8 +141,8 @@ impl<'d, T: Instance> RealTimeClock<'d, T> {
/// # fn main() { }
/// # #[cfg(not(feature = "chrono"))]
/// # fn main() {
/// # use embassy_rp::rtc::{RealTimeClock, DateTimeFilter};
/// # let mut real_time_clock: RealTimeClock<embassy_rp::peripherals::RTC> = unsafe { core::mem::zeroed() };
/// # use embassy_rp::rtc::{Rtc, DateTimeFilter};
/// # let mut real_time_clock: Rtc<embassy_rp::peripherals::RTC> = unsafe { core::mem::zeroed() };
/// let now = real_time_clock.now().unwrap();
/// real_time_clock.schedule_alarm(
/// DateTimeFilter::default()
@ -150,7 +178,7 @@ impl<'d, T: Instance> RealTimeClock<'d, T> {
}
}
/// Errors that can occur on methods on [RealTimeClock]
/// Errors that can occur on methods on [Rtc]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RtcError {
/// An invalid DateTime was given or stored on the hardware.

View File

@ -545,25 +545,19 @@ mod eh1 {
type Error = Error;
}
impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBusFlush for Spi<'d, T, M> {
impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBus<u8> for Spi<'d, T, M> {
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBusRead<u8> for Spi<'d, T, M> {
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_transfer(words, &[])
}
}
impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBusWrite<u8> for Spi<'d, T, M> {
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(words)
}
}
impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBus<u8> for Spi<'d, T, M> {
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
self.blocking_transfer(read, write)
}
@ -578,30 +572,24 @@ mod eh1 {
mod eha {
use super::*;
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusFlush for Spi<'d, T, Async> {
impl<'d, T: Instance> embedded_hal_async::spi::SpiBus<u8> for Spi<'d, T, Async> {
async fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusWrite<u8> for Spi<'d, T, Async> {
async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
self.write(words).await
}
}
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusRead<u8> for Spi<'d, T, Async> {
async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
self.read(words).await
}
}
impl<'d, T: Instance> embedded_hal_async::spi::SpiBus<u8> for Spi<'d, T, Async> {
async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> {
async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
self.transfer(read, write).await
}
async fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Result<(), Self::Error> {
async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
self.transfer_in_place(words).await
}
}

View File

@ -361,6 +361,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
let regs = T::regs();
let siestatus = regs.sie_status().read();
let intrstatus = regs.intr().read();
if siestatus.resume() {
regs.sie_status().write(|w| w.set_resume(true));
@ -389,7 +390,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
return Poll::Ready(Event::Reset);
}
if siestatus.suspended() {
if siestatus.suspended() && intrstatus.dev_suspend() {
regs.sie_status().write(|w| w.set_suspended(true));
return Poll::Ready(Event::Suspend);
}

View File

@ -107,4 +107,36 @@ impl Watchdog {
w.set_trigger(true);
})
}
/// Store data in scratch register
pub fn set_scratch(&mut self, index: usize, value: u32) {
let watchdog = pac::WATCHDOG;
match index {
0 => watchdog.scratch0().write(|w| *w = value),
1 => watchdog.scratch1().write(|w| *w = value),
2 => watchdog.scratch2().write(|w| *w = value),
3 => watchdog.scratch3().write(|w| *w = value),
4 => watchdog.scratch4().write(|w| *w = value),
5 => watchdog.scratch5().write(|w| *w = value),
6 => watchdog.scratch6().write(|w| *w = value),
7 => watchdog.scratch7().write(|w| *w = value),
_ => panic!("Invalid watchdog scratch index"),
}
}
/// Read data from scratch register
pub fn get_scratch(&mut self, index: usize) -> u32 {
let watchdog = pac::WATCHDOG;
match index {
0 => watchdog.scratch0().read(),
1 => watchdog.scratch1().read(),
2 => watchdog.scratch2().read(),
3 => watchdog.scratch3().read(),
4 => watchdog.scratch4().read(),
5 => watchdog.scratch5().read(),
6 => watchdog.scratch6().read(),
7 => watchdog.scratch7().read(),
_ => panic!("Invalid watchdog scratch index"),
}
}
}

View File

@ -5,18 +5,19 @@ edition = "2021"
license = "MIT OR Apache-2.0"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-wpan-v$VERSION/embassy-stm32-wpan/src"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32-wpan/src"
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-wpan-v$VERSION/embassy-stm32-wpan/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32-wpan/src/"
target = "thumbv7em-none-eabihf"
features = ["stm32wb55rg"]
[dependencies]
embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32" }
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true }
embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" }
embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" }
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver", optional=true }
defmt = { version = "0.3", optional = true }
cortex-m = "0.7.6"
@ -25,13 +26,15 @@ aligned = "0.4.1"
bit_field = "0.10.2"
stm32-device-signature = { version = "0.3.3", features = ["stm32wb5x"] }
stm32wb-hci = { version = "0.1.2", features = ["version-5-0"], optional = true }
stm32wb-hci = { version = "0.1.3", optional = true }
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
bitflags = { version = "2.3.3", optional = true }
[features]
defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt"]
defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "stm32wb-hci?/defmt"]
ble = ["dep:stm32wb-hci"]
mac = []
mac = ["dep:bitflags", "dep:embassy-net-driver" ]
stm32wb10cc = [ "embassy-stm32/stm32wb10cc" ]
stm32wb15cc = [ "embassy-stm32/stm32wb15cc" ]
@ -48,4 +51,4 @@ stm32wb55rg = [ "embassy-stm32/stm32wb55rg" ]
stm32wb55vc = [ "embassy-stm32/stm32wb55vc" ]
stm32wb55ve = [ "embassy-stm32/stm32wb55ve" ]
stm32wb55vg = [ "embassy-stm32/stm32wb55vg" ]
stm32wb55vy = [ "embassy-stm32/stm32wb55vy" ]
stm32wb55vy = [ "embassy-stm32/stm32wb55vy" ]

View File

@ -37,7 +37,7 @@ pub struct CmdSerialStub {
}
#[derive(Copy, Clone, Default)]
#[repr(C, packed(4))]
#[repr(C, packed)]
pub struct CmdPacket {
pub header: PacketHeader,
pub cmdserial: CmdSerial,

View File

@ -6,6 +6,8 @@ use crate::PacketHeader;
#[derive(Debug)]
#[repr(C)]
pub enum TlPacketType {
MacCmd = 0x00,
BleCmd = 0x01,
AclData = 0x02,
BleEvt = 0x04,
@ -79,6 +81,7 @@ pub const CFG_TL_BLE_MOST_EVENT_PAYLOAD_SIZE: usize = 255;
pub const TL_BLE_EVENT_FRAME_SIZE: usize = TL_EVT_HEADER_SIZE + CFG_TL_BLE_MOST_EVENT_PAYLOAD_SIZE;
pub const POOL_SIZE: usize = CFG_TL_BLE_EVT_QUEUE_LENGTH * 4 * divc(TL_PACKET_HEADER_SIZE + TL_BLE_EVENT_FRAME_SIZE, 4);
pub const C_SIZE_CMD_STRING: usize = 256;
pub const fn divc(x: usize, y: usize) -> usize {
(x + y - 1) / y

View File

@ -1,5 +1,6 @@
#![no_std]
#![cfg_attr(feature = "ble", feature(async_fn_in_trait))]
#![cfg_attr(any(feature = "ble", feature = "mac"), feature(async_fn_in_trait))]
#![cfg_attr(feature = "mac", feature(type_alias_impl_trait, concat_bytes))]
// This must go FIRST so that all the other modules see its macros.
pub mod fmt;
@ -26,6 +27,9 @@ pub mod sub;
pub mod tables;
pub mod unsafe_linked_list;
#[cfg(feature = "mac")]
pub mod mac;
#[cfg(feature = "ble")]
pub use crate::sub::ble::hci;
@ -60,9 +64,9 @@ impl<'d> TlMbox<'d> {
mem_manager_table: TL_MEM_MANAGER_TABLE.as_ptr(),
traces_table: TL_TRACES_TABLE.as_ptr(),
mac_802_15_4_table: TL_MAC_802_15_4_TABLE.as_ptr(),
// zigbee_table: TL_ZIGBEE_TABLE.as_ptr(),
// lld_tests_table: TL_LLD_TESTS_TABLE.as_ptr(),
// ble_lld_table: TL_BLE_LLD_TABLE.as_ptr(),
zigbee_table: TL_ZIGBEE_TABLE.as_ptr(),
lld_tests_table: TL_LLD_TESTS_TABLE.as_ptr(),
ble_lld_table: TL_BLE_LLD_TABLE.as_ptr(),
});
TL_SYS_TABLE
@ -87,15 +91,15 @@ impl<'d> TlMbox<'d> {
TL_MAC_802_15_4_TABLE
.as_mut_ptr()
.write_volatile(MaybeUninit::zeroed().assume_init());
// TL_ZIGBEE_TABLE
// .as_mut_ptr()
// .write_volatile(MaybeUninit::zeroed().assume_init());
// TL_LLD_TESTS_TABLE
// .as_mut_ptr()
// .write_volatile(MaybeUninit::zeroed().assume_init());
// TL_BLE_LLD_TABLE
// .as_mut_ptr()
// .write_volatile(MaybeUninit::zeroed().assume_init());
TL_ZIGBEE_TABLE
.as_mut_ptr()
.write_volatile(MaybeUninit::zeroed().assume_init());
TL_LLD_TESTS_TABLE
.as_mut_ptr()
.write_volatile(MaybeUninit::zeroed().assume_init());
TL_BLE_LLD_TABLE
.as_mut_ptr()
.write_volatile(MaybeUninit::zeroed().assume_init());
EVT_POOL
.as_mut_ptr()
@ -103,18 +107,30 @@ impl<'d> TlMbox<'d> {
SYS_SPARE_EVT_BUF
.as_mut_ptr()
.write_volatile(MaybeUninit::zeroed().assume_init());
BLE_SPARE_EVT_BUF
CS_BUFFER
.as_mut_ptr()
.write_volatile(MaybeUninit::zeroed().assume_init());
#[cfg(feature = "ble")]
{
BLE_SPARE_EVT_BUF
.as_mut_ptr()
.write_volatile(MaybeUninit::zeroed().assume_init());
BLE_CMD_BUFFER
.as_mut_ptr()
.write_volatile(MaybeUninit::zeroed().assume_init());
HCI_ACL_DATA_BUFFER
.as_mut_ptr()
.write_volatile(MaybeUninit::zeroed().assume_init());
CS_BUFFER
}
#[cfg(feature = "mac")]
{
MAC_802_15_4_CMD_BUFFER
.as_mut_ptr()
.write_volatile(MaybeUninit::zeroed().assume_init());
MAC_802_15_4_NOTIF_RSP_EVT_BUFFER
.as_mut_ptr()
.write_volatile(MaybeUninit::zeroed().assume_init());
}

View File

@ -0,0 +1,476 @@
use core::{mem, slice};
use super::opcodes::OpcodeM4ToM0;
use super::typedefs::{
AddressMode, Capabilities, DisassociationReason, GtsCharacteristics, KeyIdMode, MacAddress, MacChannel, MacStatus,
PanId, PibId, ScanType, SecurityLevel,
};
pub trait MacCommand: Sized {
const OPCODE: OpcodeM4ToM0;
fn payload<'a>(&'a self) -> &'a [u8] {
unsafe { slice::from_raw_parts(self as *const _ as *const u8, mem::size_of::<Self>()) }
}
}
/// MLME ASSOCIATE Request used to request an association
#[repr(C)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct AssociateRequest {
/// the logical channel on which to attempt association
pub channel_number: MacChannel,
/// the channel page on which to attempt association
pub channel_page: u8,
/// coordinator addressing mode
pub coord_addr_mode: AddressMode,
/// operational capabilities of the associating device
pub capability_information: Capabilities,
/// the identifier of the PAN with which to associate
pub coord_pan_id: PanId,
/// the security level to be used
pub security_level: SecurityLevel,
/// the mode used to identify the key to be used
pub key_id_mode: KeyIdMode,
/// the originator of the key to be used
pub key_source: [u8; 8],
/// Coordinator address
pub coord_address: MacAddress,
/// the index of the key to be used
pub key_index: u8,
}
impl MacCommand for AssociateRequest {
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeAssociateReq;
}
/// MLME DISASSOCIATE Request sed to request a disassociation
#[repr(C)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DisassociateRequest {
/// device addressing mode used
pub device_addr_mode: AddressMode,
/// the identifier of the PAN of the device
pub device_pan_id: PanId,
/// the reason for the disassociation
pub disassociation_reason: DisassociationReason,
/// device address
pub device_address: MacAddress,
/// `true` if the disassociation notification command is to be sent indirectly
pub tx_indirect: bool,
/// the security level to be used
pub security_level: SecurityLevel,
/// the mode to be used to indetify the key to be used
pub key_id_mode: KeyIdMode,
/// the index of the key to be used
pub key_index: u8,
/// the originator of the key to be used
pub key_source: [u8; 8],
}
impl MacCommand for DisassociateRequest {
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeDisassociateReq;
}
/// MLME GET Request used to request a PIB value
#[repr(C)]
#[derive(Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct GetRequest {
/// the name of the PIB attribute to read
pub pib_attribute: PibId,
/// byte stuffing to keep 32 bit alignment
pub a_stuffing: [u8; 3],
}
impl MacCommand for GetRequest {
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeGetReq;
}
/// MLME GTS Request used to request and maintain GTSs
#[repr(C)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct GtsRequest {
/// the characteristics of the GTS
pub characteristics: GtsCharacteristics,
/// the security level to be used
pub security_level: SecurityLevel,
/// the mode used to identify the key to be used
pub key_id_mode: KeyIdMode,
/// the index of the key to be used
pub key_index: u8,
/// the originator of the key to be used
pub key_source: [u8; 8],
}
impl MacCommand for GtsRequest {
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeGetReq;
}
#[repr(C)]
#[derive(Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ResetRequest {
/// MAC PIB attributes are set to their default values or not during reset
pub set_default_pib: bool,
/// byte stuffing to keep 32 bit alignment
pub a_stuffing: [u8; 3],
}
impl MacCommand for ResetRequest {
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeResetReq;
}
/// MLME RX ENABLE Request used to request that the receiver is either enabled
/// for a finite period of time or disabled
#[repr(C)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct RxEnableRequest {
/// the request operation can be deferred or not
pub defer_permit: bool,
/// configure the transceiver to RX with ranging for a value of
/// RANGING_ON or to not enable ranging for RANGING_OFF
pub ranging_rx_control: u8,
/// byte stuffing to keep 32 bit alignment
pub a_stuffing: [u8; 2],
/// number of symbols measured before the receiver is to be enabled or disabled
pub rx_on_time: [u8; 4],
/// number of symbols for which the receiver is to be enabled
pub rx_on_duration: [u8; 4],
}
impl MacCommand for RxEnableRequest {
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeRxEnableReq;
}
/// MLME SCAN Request used to initiate a channel scan over a given list of channels
#[repr(C)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ScanRequest {
/// the type of scan to be performed
pub scan_type: ScanType,
/// the time spent on scanning each channel
pub scan_duration: u8,
/// channel page on which to perform the scan
pub channel_page: u8,
/// security level to be used
pub security_level: SecurityLevel,
/// indicate which channels are to be scanned
pub scan_channels: [u8; 4],
/// originator the key to be used
pub key_source: [u8; 8],
/// mode used to identify the key to be used
pub key_id_mode: KeyIdMode,
/// index of the key to be used
pub key_index: u8,
/// byte stuffing to keep 32 bit alignment
pub a_stuffing: [u8; 2],
}
impl MacCommand for ScanRequest {
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeScanReq;
}
/// MLME SET Request used to attempt to write the given value to the indicated PIB attribute
#[repr(C)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SetRequest {
/// the pointer to the value of the PIB attribute to set
pub pib_attribute_ptr: *const u8,
/// the name of the PIB attribute to set
pub pib_attribute: PibId,
}
impl MacCommand for SetRequest {
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSetReq;
}
/// MLME START Request used by the FFDs to intiate a new PAN or to begin using a new superframe
/// configuration
#[repr(C)]
#[derive(Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct StartRequest {
/// PAN indentifier to used by the device
pub pan_id: PanId,
/// logical channel on which to begin
pub channel_number: MacChannel,
/// channel page on which to begin
pub channel_page: u8,
/// time at which to begin transmitting beacons
pub start_time: [u8; 4],
/// indicated how often the beacon is to be transmitted
pub beacon_order: u8,
/// length of the active portion of the superframe
pub superframe_order: u8,
/// indicated wheter the device is a PAN coordinator or not
pub pan_coordinator: bool,
/// indicates if the receiver of the beaconing device is disabled or not
pub battery_life_extension: bool,
/// indicated if the coordinator realignment command is to be trasmitted
pub coord_realignment: u8,
/// indicated if the coordinator realignment command is to be trasmitted
pub coord_realign_security_level: SecurityLevel,
/// index of the key to be used
pub coord_realign_key_id_index: u8,
/// originator of the key to be used
pub coord_realign_key_source: [u8; 8],
/// security level to be used for beacon frames
pub beacon_security_level: SecurityLevel,
/// mode used to identify the key to be used
pub beacon_key_id_mode: KeyIdMode,
/// index of the key to be used
pub beacon_key_index: u8,
/// originator of the key to be used
pub beacon_key_source: [u8; 8],
}
impl MacCommand for StartRequest {
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeStartReq;
}
/// MLME SYNC Request used to synchronize with the coordinator by acquiring and, if
/// specified, tracking its beacons
#[repr(C)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SyncRequest {
/// the channel number on which to attempt coordinator synchronization
pub channel_number: MacChannel,
/// the channel page on which to attempt coordinator synchronization
pub channel_page: u8,
/// `true` if the MLME is to synchronize with the next beacon and attempts
/// to track all future beacons.
///
/// `false` if the MLME is to synchronize with only the next beacon
pub track_beacon: bool,
/// byte stuffing to keep 32 bit alignment
pub a_stuffing: [u8; 1],
}
impl MacCommand for SyncRequest {
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSyncReq;
}
/// MLME POLL Request propmts the device to request data from the coordinator
#[repr(C)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PollRequest {
/// addressing mode of the coordinator
pub coord_addr_mode: AddressMode,
/// security level to be used
pub security_level: SecurityLevel,
/// mode used to identify the key to be used
pub key_id_mode: KeyIdMode,
/// index of the key to be used
pub key_index: u8,
/// coordinator address
pub coord_address: MacAddress,
/// originator of the key to be used
pub key_source: [u8; 8],
/// PAN identifier of the coordinator
pub coord_pan_id: PanId,
/// byte stuffing to keep 32 bit alignment
pub a_stuffing: [u8; 2],
}
impl MacCommand for PollRequest {
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmePollReq;
}
/// MLME DPS Request allows the next higher layer to request that the PHY utilize a
/// given pair of preamble codes for a single use pending expiration of the DPSIndexDuration
#[repr(C)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DpsRequest {
/// the index value for the transmitter
tx_dps_index: u8,
/// the index value of the receiver
rx_dps_index: u8,
/// the number of symbols for which the transmitter and receiver will utilize the
/// respective DPS indices
dps_index_duration: u8,
/// byte stuffing to keep 32 bit alignment
pub a_stuffing: [u8; 1],
}
impl MacCommand for DpsRequest {
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeDpsReq;
}
/// MLME SOUNDING request primitive which is used by the next higher layer to request that
/// the PHY respond with channel sounding information
#[repr(C)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SoundingRequest {
/// byte stuffing to keep 32 bit alignment
pub a_stuffing: [u8; 4],
}
impl MacCommand for SoundingRequest {
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSoundingReq;
}
/// MLME CALIBRATE request primitive which used to obtain the results of a ranging
/// calibration request from an RDEV
#[repr(C)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CalibrateRequest {
/// byte stuffing to keep 32 bit alignment
pub a_stuffing: [u8; 4],
}
impl MacCommand for CalibrateRequest {
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeCalibrateReq;
}
/// MCPS DATA Request used for MAC data related requests from the application
#[repr(C)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DataRequest {
/// the handle assocated with the MSDU to be transmitted
pub msdu_ptr: *const u8,
/// source addressing mode used
pub src_addr_mode: AddressMode,
/// destination addressing mode used
pub dst_addr_mode: AddressMode,
/// destination PAN Id
pub dst_pan_id: PanId,
/// destination address
pub dst_address: MacAddress,
/// the number of octets contained in the MSDU
pub msdu_length: u8,
/// the handle assocated with the MSDU to be transmitted
pub msdu_handle: u8,
/// the ACK transmittion options for the MSDU
pub ack_tx: u8,
/// `true` if a GTS is to be used for transmission
///
/// `false` indicates that the CAP will be used
pub gts_tx: bool,
/// the pending bit transmission options for the MSDU
pub indirect_tx: u8,
/// the security level to be used
pub security_level: SecurityLevel,
/// the mode used to indentify the key to be used
pub key_id_mode: KeyIdMode,
/// the index of the key to be used
pub key_index: u8,
/// the originator of the key to be used
pub key_source: [u8; 8],
/// 2011 - the pulse repitition value
pub uwbprf: u8,
/// 2011 - the ranging configuration
pub ranging: u8,
/// 2011 - the preamble symbol repititions
pub uwb_preamble_symbol_repetitions: u8,
/// 2011 - indicates the data rate
pub datrate: u8,
}
impl DataRequest {
pub fn set_buffer<'a>(&'a mut self, buf: &'a [u8]) -> &mut Self {
self.msdu_ptr = buf as *const _ as *const u8;
self.msdu_length = buf.len() as u8;
self
}
}
impl Default for DataRequest {
fn default() -> Self {
Self {
msdu_ptr: 0 as *const u8,
src_addr_mode: AddressMode::NoAddress,
dst_addr_mode: AddressMode::NoAddress,
dst_pan_id: PanId([0, 0]),
dst_address: MacAddress { short: [0, 0] },
msdu_length: 0,
msdu_handle: 0,
ack_tx: 0,
gts_tx: false,
indirect_tx: 0,
security_level: SecurityLevel::Unsecure,
key_id_mode: KeyIdMode::Implicite,
key_index: 0,
key_source: [0u8; 8],
uwbprf: 0,
ranging: 0,
uwb_preamble_symbol_repetitions: 0,
datrate: 0,
}
}
}
impl MacCommand for DataRequest {
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::McpsDataReq;
}
/// for MCPS PURGE Request used to purge an MSDU from the transaction queue
#[repr(C)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PurgeRequest {
/// the handle associated with the MSDU to be purged from the transaction
/// queue
pub msdu_handle: u8,
/// byte stuffing to keep 32 bit alignment
pub a_stuffing: [u8; 3],
}
impl MacCommand for PurgeRequest {
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::McpsPurgeReq;
}
/// MLME ASSOCIATE Response used to initiate a response to an MLME-ASSOCIATE.indication
#[repr(C)]
#[derive(Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct AssociateResponse {
/// extended address of the device requesting association
pub device_address: [u8; 8],
/// 16-bitshort device address allocated by the coordinator on successful
/// association
pub assoc_short_address: [u8; 2],
/// status of the association attempt
pub status: MacStatus,
/// security level to be used
pub security_level: SecurityLevel,
/// the originator of the key to be used
pub key_source: [u8; 8],
/// the mode used to identify the key to be used
pub key_id_mode: KeyIdMode,
/// the index of the key to be used
pub key_index: u8,
/// byte stuffing to keep 32 bit alignment
pub a_stuffing: [u8; 2],
}
impl MacCommand for AssociateResponse {
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeAssociateRes;
}
/// MLME ORPHAN Response used to respond to the MLME ORPHAN Indication
#[repr(C)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct OrphanResponse {
/// extended address of the orphaned device
pub orphan_address: [u8; 8],
/// short address allocated to the orphaned device
pub short_address: [u8; 2],
/// if the orphaned device is associated with coordinator or not
pub associated_member: bool,
/// security level to be used
pub security_level: SecurityLevel,
/// the originator of the key to be used
pub key_source: [u8; 8],
/// the mode used to identify the key to be used
pub key_id_mode: KeyIdMode,
/// the index of the key to be used
pub key_index: u8,
/// byte stuffing to keep 32 bit alignment
pub a_stuffing: [u8; 2],
}
impl MacCommand for OrphanResponse {
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeOrphanRes;
}

View File

@ -0,0 +1,4 @@
pub const MAX_PAN_DESC_SUPPORTED: usize = 6;
pub const MAX_SOUNDING_LIST_SUPPORTED: usize = 6;
pub const MAX_PENDING_ADDRESS: usize = 7;
pub const MAX_ED_SCAN_RESULTS_SUPPORTED: usize = 16;

View File

@ -0,0 +1,95 @@
use core::future::Future;
use core::task;
use core::task::Poll;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::mutex::MutexGuard;
use embassy_sync::signal::Signal;
use futures::FutureExt;
use super::commands::MacCommand;
use super::event::MacEvent;
use super::typedefs::MacError;
use crate::mac::runner::Runner;
pub struct Control<'a> {
runner: &'a Runner<'a>,
}
impl<'a> Control<'a> {
pub(crate) fn new(runner: &'a Runner<'a>) -> Self {
Self { runner: runner }
}
pub async fn send_command<T>(&self, cmd: &T) -> Result<(), MacError>
where
T: MacCommand,
{
let _wm = self.runner.write_mutex.lock().await;
self.runner.mac_subsystem.send_command(cmd).await
}
pub async fn send_command_and_get_response<T>(&self, cmd: &T) -> Result<EventToken<'a>, MacError>
where
T: MacCommand,
{
let rm = self.runner.read_mutex.lock().await;
let _wm = self.runner.write_mutex.lock().await;
let token = EventToken::new(self.runner, rm);
self.runner.mac_subsystem.send_command(cmd).await?;
Ok(token)
}
}
pub struct EventToken<'a> {
runner: &'a Runner<'a>,
_mutex_guard: MutexGuard<'a, CriticalSectionRawMutex, ()>,
}
impl<'a> EventToken<'a> {
pub(crate) fn new(runner: &'a Runner<'a>, mutex_guard: MutexGuard<'a, CriticalSectionRawMutex, ()>) -> Self {
// Enable event receiving
runner.rx_event_channel.lock(|s| {
*s.borrow_mut() = Some(Signal::new());
});
Self {
runner: runner,
_mutex_guard: mutex_guard,
}
}
}
impl<'a> Future for EventToken<'a> {
type Output = MacEvent<'a>;
fn poll(self: core::pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
self.get_mut().runner.rx_event_channel.lock(|s| {
let signal = s.borrow_mut();
let signal = match &*signal {
Some(s) => s,
_ => unreachable!(),
};
let result = match signal.wait().poll_unpin(cx) {
Poll::Ready(mac_event) => Poll::Ready(mac_event),
Poll::Pending => Poll::Pending,
};
result
})
}
}
impl<'a> Drop for EventToken<'a> {
fn drop(&mut self) {
// Disable event receiving
// This will also drop the contained event, if it exists, and will free up receiving the next event
self.runner.rx_event_channel.lock(|s| {
*s.borrow_mut() = None;
});
}
}

View File

@ -0,0 +1,122 @@
#![allow(incomplete_features)]
#![deny(unused_must_use)]
use core::task::Context;
use embassy_net_driver::{Capabilities, LinkState, Medium};
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::channel::Channel;
use crate::mac::event::MacEvent;
use crate::mac::runner::Runner;
use crate::mac::MTU;
pub struct Driver<'d> {
runner: &'d Runner<'d>,
}
impl<'d> Driver<'d> {
pub(crate) fn new(runner: &'d Runner<'d>) -> Self {
Self { runner: runner }
}
}
impl<'d> embassy_net_driver::Driver for Driver<'d> {
// type RxToken<'a> = RxToken<'a, 'd> where Self: 'a;
// type TxToken<'a> = TxToken<'a, 'd> where Self: 'a;
type RxToken<'a> = RxToken<'d> where Self: 'a;
type TxToken<'a> = TxToken<'d> where Self: 'a;
fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
if self.runner.rx_channel.poll_ready_to_receive(cx) && self.runner.tx_buf_channel.poll_ready_to_receive(cx) {
Some((
RxToken {
rx: &self.runner.rx_channel,
},
TxToken {
tx: &self.runner.tx_channel,
tx_buf: &self.runner.tx_buf_channel,
},
))
} else {
None
}
}
fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> {
if self.runner.tx_buf_channel.poll_ready_to_receive(cx) {
Some(TxToken {
tx: &self.runner.tx_channel,
tx_buf: &self.runner.tx_buf_channel,
})
} else {
None
}
}
fn capabilities(&self) -> Capabilities {
let mut caps = Capabilities::default();
caps.max_transmission_unit = MTU;
// caps.max_burst_size = Some(self.tx.len());
caps.medium = Medium::Ieee802154;
caps
}
fn link_state(&mut self, _cx: &mut Context) -> LinkState {
// if self.phy.poll_link(&mut self.station_management, cx) {
// LinkState::Up
// } else {
// LinkState::Down
// }
LinkState::Down
}
fn ethernet_address(&self) -> [u8; 6] {
// self.mac_addr
[0; 6]
}
}
pub struct RxToken<'d> {
rx: &'d Channel<CriticalSectionRawMutex, MacEvent<'d>, 1>,
}
impl<'d> embassy_net_driver::RxToken for RxToken<'d> {
fn consume<R, F>(self, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
// Only valid data events should be put into the queue
let data_event = match self.rx.try_recv().unwrap() {
MacEvent::McpsDataInd(data_event) => data_event,
_ => unreachable!(),
};
f(&mut data_event.payload())
}
}
pub struct TxToken<'d> {
tx: &'d Channel<CriticalSectionRawMutex, (&'d mut [u8; MTU], usize), 5>,
tx_buf: &'d Channel<CriticalSectionRawMutex, &'d mut [u8; MTU], 5>,
}
impl<'d> embassy_net_driver::TxToken for TxToken<'d> {
fn consume<R, F>(self, len: usize, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
// Only valid tx buffers should be put into the queue
let buf = self.tx_buf.try_recv().unwrap();
let r = f(&mut buf[..len]);
// The tx channel should always be of equal capacity to the tx_buf channel
self.tx.try_send((buf, len)).unwrap();
r
}
}

View File

@ -0,0 +1,153 @@
use core::{mem, ptr};
use super::indications::{
AssociateIndication, BeaconNotifyIndication, CommStatusIndication, DataIndication, DisassociateIndication,
DpsIndication, GtsIndication, OrphanIndication, PollIndication, SyncLossIndication,
};
use super::responses::{
AssociateConfirm, CalibrateConfirm, DataConfirm, DisassociateConfirm, DpsConfirm, GetConfirm, GtsConfirm,
PollConfirm, PurgeConfirm, ResetConfirm, RxEnableConfirm, ScanConfirm, SetConfirm, SoundingConfirm, StartConfirm,
};
use crate::evt::{EvtBox, MemoryManager};
use crate::mac::opcodes::OpcodeM0ToM4;
use crate::sub::mac::{self, Mac};
pub(crate) trait ParseableMacEvent: Sized {
fn from_buffer<'a>(buf: &'a [u8]) -> Result<&'a Self, ()> {
if buf.len() < mem::size_of::<Self>() {
Err(())
} else {
Ok(unsafe { &*(buf as *const _ as *const Self) })
}
}
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug)]
pub enum MacEvent<'a> {
MlmeAssociateCnf(&'a AssociateConfirm),
MlmeDisassociateCnf(&'a DisassociateConfirm),
MlmeGetCnf(&'a GetConfirm),
MlmeGtsCnf(&'a GtsConfirm),
MlmeResetCnf(&'a ResetConfirm),
MlmeRxEnableCnf(&'a RxEnableConfirm),
MlmeScanCnf(&'a ScanConfirm),
MlmeSetCnf(&'a SetConfirm),
MlmeStartCnf(&'a StartConfirm),
MlmePollCnf(&'a PollConfirm),
MlmeDpsCnf(&'a DpsConfirm),
MlmeSoundingCnf(&'a SoundingConfirm),
MlmeCalibrateCnf(&'a CalibrateConfirm),
McpsDataCnf(&'a DataConfirm),
McpsPurgeCnf(&'a PurgeConfirm),
MlmeAssociateInd(&'a AssociateIndication),
MlmeDisassociateInd(&'a DisassociateIndication),
MlmeBeaconNotifyInd(&'a BeaconNotifyIndication),
MlmeCommStatusInd(&'a CommStatusIndication),
MlmeGtsInd(&'a GtsIndication),
MlmeOrphanInd(&'a OrphanIndication),
MlmeSyncLossInd(&'a SyncLossIndication),
MlmeDpsInd(&'a DpsIndication),
McpsDataInd(&'a DataIndication),
MlmePollInd(&'a PollIndication),
}
impl<'a> MacEvent<'a> {
pub(crate) fn new(event_box: EvtBox<Mac>) -> Result<Self, ()> {
let payload = event_box.payload();
let opcode = u16::from_le_bytes(payload[0..2].try_into().unwrap());
let opcode = OpcodeM0ToM4::try_from(opcode)?;
let buf = &payload[2..];
// To avoid re-parsing the opcode, we store the result of the parse
// this requires use of unsafe because rust cannot assume that a reference will become
// invalid when the underlying result is moved. However, because we refer to a "heap"
// allocation, the underlying reference will not move until the struct is dropped.
let mac_event = match opcode {
OpcodeM0ToM4::MlmeAssociateCnf => {
MacEvent::MlmeAssociateCnf(unsafe { &*(AssociateConfirm::from_buffer(buf)? as *const _) })
}
OpcodeM0ToM4::MlmeDisassociateCnf => {
MacEvent::MlmeDisassociateCnf(unsafe { &*(DisassociateConfirm::from_buffer(buf)? as *const _) })
}
OpcodeM0ToM4::MlmeGetCnf => MacEvent::MlmeGetCnf(unsafe { &*(GetConfirm::from_buffer(buf)? as *const _) }),
OpcodeM0ToM4::MlmeGtsCnf => MacEvent::MlmeGtsCnf(unsafe { &*(GtsConfirm::from_buffer(buf)? as *const _) }),
OpcodeM0ToM4::MlmeResetCnf => {
MacEvent::MlmeResetCnf(unsafe { &*(ResetConfirm::from_buffer(buf)? as *const _) })
}
OpcodeM0ToM4::MlmeRxEnableCnf => {
MacEvent::MlmeRxEnableCnf(unsafe { &*(RxEnableConfirm::from_buffer(buf)? as *const _) })
}
OpcodeM0ToM4::MlmeScanCnf => {
MacEvent::MlmeScanCnf(unsafe { &*(ScanConfirm::from_buffer(buf)? as *const _) })
}
OpcodeM0ToM4::MlmeSetCnf => MacEvent::MlmeSetCnf(unsafe { &*(SetConfirm::from_buffer(buf)? as *const _) }),
OpcodeM0ToM4::MlmeStartCnf => {
MacEvent::MlmeStartCnf(unsafe { &*(StartConfirm::from_buffer(buf)? as *const _) })
}
OpcodeM0ToM4::MlmePollCnf => {
MacEvent::MlmePollCnf(unsafe { &*(PollConfirm::from_buffer(buf)? as *const _) })
}
OpcodeM0ToM4::MlmeDpsCnf => MacEvent::MlmeDpsCnf(unsafe { &*(DpsConfirm::from_buffer(buf)? as *const _) }),
OpcodeM0ToM4::MlmeSoundingCnf => {
MacEvent::MlmeSoundingCnf(unsafe { &*(SoundingConfirm::from_buffer(buf)? as *const _) })
}
OpcodeM0ToM4::MlmeCalibrateCnf => {
MacEvent::MlmeCalibrateCnf(unsafe { &*(CalibrateConfirm::from_buffer(buf)? as *const _) })
}
OpcodeM0ToM4::McpsDataCnf => {
MacEvent::McpsDataCnf(unsafe { &*(DataConfirm::from_buffer(buf)? as *const _) })
}
OpcodeM0ToM4::McpsPurgeCnf => {
MacEvent::McpsPurgeCnf(unsafe { &*(PurgeConfirm::from_buffer(buf)? as *const _) })
}
OpcodeM0ToM4::MlmeAssociateInd => {
MacEvent::MlmeAssociateInd(unsafe { &*(AssociateIndication::from_buffer(buf)? as *const _) })
}
OpcodeM0ToM4::MlmeDisassociateInd => {
MacEvent::MlmeDisassociateInd(unsafe { &*(DisassociateIndication::from_buffer(buf)? as *const _) })
}
OpcodeM0ToM4::MlmeBeaconNotifyInd => {
MacEvent::MlmeBeaconNotifyInd(unsafe { &*(BeaconNotifyIndication::from_buffer(buf)? as *const _) })
}
OpcodeM0ToM4::MlmeCommStatusInd => {
MacEvent::MlmeCommStatusInd(unsafe { &*(CommStatusIndication::from_buffer(buf)? as *const _) })
}
OpcodeM0ToM4::MlmeGtsInd => {
MacEvent::MlmeGtsInd(unsafe { &*(GtsIndication::from_buffer(buf)? as *const _) })
}
OpcodeM0ToM4::MlmeOrphanInd => {
MacEvent::MlmeOrphanInd(unsafe { &*(OrphanIndication::from_buffer(buf)? as *const _) })
}
OpcodeM0ToM4::MlmeSyncLossInd => {
MacEvent::MlmeSyncLossInd(unsafe { &*(SyncLossIndication::from_buffer(buf)? as *const _) })
}
OpcodeM0ToM4::MlmeDpsInd => {
MacEvent::MlmeDpsInd(unsafe { &*(DpsIndication::from_buffer(buf)? as *const _) })
}
OpcodeM0ToM4::McpsDataInd => {
MacEvent::McpsDataInd(unsafe { &*(DataIndication::from_buffer(buf)? as *const _) })
}
OpcodeM0ToM4::MlmePollInd => {
MacEvent::MlmePollInd(unsafe { &*(PollIndication::from_buffer(buf)? as *const _) })
}
};
// Forget the event box so that drop isn't called
// We want to handle the lifetime ourselves
mem::forget(event_box);
Ok(mac_event)
}
}
unsafe impl<'a> Send for MacEvent<'a> {}
impl<'a> Drop for MacEvent<'a> {
fn drop(&mut self) {
unsafe { mac::Mac::drop_event_packet(ptr::null_mut()) };
}
}

View File

@ -0,0 +1,265 @@
use core::slice;
use super::consts::MAX_PENDING_ADDRESS;
use super::event::ParseableMacEvent;
use super::typedefs::{
AddressMode, Capabilities, DisassociationReason, KeyIdMode, MacAddress, MacChannel, MacStatus, PanDescriptor,
PanId, SecurityLevel,
};
/// MLME ASSOCIATE Indication which will be used by the MAC
/// to indicate the reception of an association request command
#[repr(C)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct AssociateIndication {
/// Extended address of the device requesting association
pub device_address: [u8; 8],
/// Operational capabilities of the device requesting association
pub capability_information: Capabilities,
/// Security level purportedly used by the received MAC command frame
pub security_level: SecurityLevel,
/// The mode used to identify the key used by the originator of frame
pub key_id_mode: KeyIdMode,
/// Index of the key used by the originator of the received frame
pub key_index: u8,
/// The originator of the key used by the originator of the received frame
pub key_source: [u8; 8],
}
impl ParseableMacEvent for AssociateIndication {}
/// MLME DISASSOCIATE indication which will be used to send
/// disassociation indication to the application.
#[repr(C)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DisassociateIndication {
/// Extended address of the device requesting association
pub device_address: [u8; 8],
/// The reason for the disassociation
pub disassociation_reason: DisassociationReason,
/// The security level to be used
pub security_level: SecurityLevel,
/// The mode used to identify the key to be used
pub key_id_mode: KeyIdMode,
/// The index of the key to be used
pub key_index: u8,
/// The originator of the key to be used
pub key_source: [u8; 8],
}
impl ParseableMacEvent for DisassociateIndication {}
/// MLME BEACON NOTIIFY Indication which is used to send parameters contained
/// within a beacon frame received by the MAC to the application
#[repr(C)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct BeaconNotifyIndication {
/// he set of octets comprising the beacon payload to be transferred
/// from the MAC sublayer entity to the next higher layer
pub sdu_ptr: *const u8,
/// The PAN Descriptor for the received beacon
pub pan_descriptor: PanDescriptor,
/// The list of addresses of the devices
pub addr_list: [MacAddress; MAX_PENDING_ADDRESS],
/// Beacon Sequence Number
pub bsn: u8,
/// The beacon pending address specification
pub pend_addr_spec: u8,
/// Number of octets contained in the beacon payload of the beacon frame
pub sdu_length: u8,
}
impl ParseableMacEvent for BeaconNotifyIndication {}
/// MLME COMM STATUS Indication which is used by the MAC to indicate a communications status
#[repr(C)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CommStatusIndication {
/// The 16-bit PAN identifier of the device from which the frame
/// was received or to which the frame was being sent
pub pan_id: PanId,
/// Source addressing mode
pub src_addr_mode: AddressMode,
/// Destination addressing mode
pub dst_addr_mode: AddressMode,
/// Source address
pub src_address: MacAddress,
/// Destination address
pub dst_address: MacAddress,
/// The communications status
pub status: MacStatus,
/// Security level to be used
pub security_level: SecurityLevel,
/// Mode used to identify the key to be used
pub key_id_mode: KeyIdMode,
/// Index of the key to be used
pub key_index: u8,
/// Originator of the key to be used
pub key_source: [u8; 8],
}
impl ParseableMacEvent for CommStatusIndication {}
/// MLME GTS Indication indicates that a GTS has been allocated or that a
/// previously allocated GTS has been deallocated
#[repr(C)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct GtsIndication {
/// The short address of the device that has been allocated or deallocated a GTS
pub device_address: [u8; 2],
/// The characteristics of the GTS
pub gts_characteristics: u8,
/// Security level to be used
pub security_level: SecurityLevel,
/// Mode used to identify the key to be used
pub key_id_mode: KeyIdMode,
/// Index of the key to be used
pub key_index: u8,
/// byte stuffing to keep 32 bit alignment
a_stuffing: [u8; 2],
/// Originator of the key to be used
pub key_source: [u8; 8],
}
impl ParseableMacEvent for GtsIndication {}
/// MLME ORPHAN Indication which is used by the coordinator to notify the
/// application of the presence of an orphaned device
#[repr(C)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct OrphanIndication {
/// Extended address of the orphaned device
pub orphan_address: [u8; 8],
/// Originator of the key used by the originator of the received frame
pub key_source: [u8; 8],
/// Security level purportedly used by the received MAC command frame
pub security_level: SecurityLevel,
/// Mode used to identify the key used by originator of received frame
pub key_id_mode: KeyIdMode,
/// Index of the key used by the originator of the received frame
pub key_index: u8,
/// byte stuffing to keep 32 bit alignment
a_stuffing: [u8; 1],
}
impl ParseableMacEvent for OrphanIndication {}
/// MLME SYNC LOSS Indication which is used by the MAC to indicate the loss
/// of synchronization with the coordinator
#[repr(C)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SyncLossIndication {
/// The PAN identifier with which the device lost synchronization or to which it was realigned
pub pan_id: PanId,
/// The reason that synchronization was lost
pub loss_reason: u8,
/// The logical channel on which the device lost synchronization or to whi
pub channel_number: MacChannel,
/// The channel page on which the device lost synchronization or to which
pub channel_page: u8,
/// The security level used by the received MAC frame
pub security_level: SecurityLevel,
/// Mode used to identify the key used by originator of received frame
pub key_id_mode: KeyIdMode,
/// Index of the key used by the originator of the received frame
pub key_index: u8,
/// Originator of the key used by the originator of the received frame
pub key_source: [u8; 8],
}
impl ParseableMacEvent for SyncLossIndication {}
/// MLME DPS Indication which indicates the expiration of the DPSIndexDuration
/// and the resetting of the DPS values in the PHY
#[repr(C)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DpsIndication {
/// byte stuffing to keep 32 bit alignment
a_stuffing: [u8; 4],
}
impl ParseableMacEvent for DpsIndication {}
#[repr(C)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DataIndication {
/// Pointer to the set of octets forming the MSDU being indicated
pub msdu_ptr: *const u8,
/// Source addressing mode used
pub src_addr_mode: AddressMode,
/// Source PAN ID
pub src_pan_id: PanId,
/// Source address
pub src_address: MacAddress,
/// Destination addressing mode used
pub dst_addr_mode: AddressMode,
/// Destination PAN ID
pub dst_pan_id: PanId,
/// Destination address
pub dst_address: MacAddress,
/// The number of octets contained in the MSDU being indicated
pub msdu_length: u8,
/// QI value measured during reception of the MPDU
pub mpdu_link_quality: u8,
/// The data sequence number of the received data frame
pub dsn: u8,
/// The time, in symbols, at which the data were received
pub time_stamp: [u8; 4],
/// The security level purportedly used by the received data frame
security_level: SecurityLevel,
/// Mode used to identify the key used by originator of received frame
key_id_mode: KeyIdMode,
/// The originator of the key
pub key_source: [u8; 8],
/// The index of the key
pub key_index: u8,
/// he pulse repetition value of the received PPDU
pub uwbprf: u8,
/// The preamble symbol repetitions of the UWB PHY frame
pub uwn_preamble_symbol_repetitions: u8,
/// Indicates the data rate
pub datrate: u8,
/// time units corresponding to an RMARKER at the antenna at the end of a ranging exchange,
pub ranging_received: u8,
pub ranging_counter_start: u32,
pub ranging_counter_stop: u32,
/// ime units in a message exchange over which the tracking offset was measured
pub ranging_tracking_interval: u32,
/// time units slipped or advanced by the radio tracking system
pub ranging_offset: u32,
/// The FoM characterizing the ranging measurement
pub ranging_fom: u8,
/// The Received Signal Strength Indicator measured
pub rssi: u8,
}
impl ParseableMacEvent for DataIndication {}
impl DataIndication {
pub fn payload<'a>(&'a self) -> &'a mut [u8] {
unsafe { slice::from_raw_parts_mut(self.msdu_ptr as *mut _, self.msdu_length as usize) }
}
}
/// MLME POLL Indication which will be used for indicating the Data Request
/// reception to upper layer as defined in Zigbee r22 - D.8.2
#[repr(C)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PollIndication {
/// addressing mode used
pub addr_mode: AddressMode,
/// Poll requester address
pub request_address: MacAddress,
}
impl ParseableMacEvent for PollIndication {}

View File

@ -0,0 +1,32 @@
#[macro_export]
macro_rules! numeric_enum {
(#[repr($repr:ident)]
$(#$attrs:tt)* $vis:vis enum $name:ident {
$($(#$enum_attrs:tt)* $enum:ident = $constant:expr),* $(,)?
} ) => {
#[repr($repr)]
$(#$attrs)*
$vis enum $name {
$($(#$enum_attrs)* $enum = $constant),*
}
impl ::core::convert::TryFrom<$repr> for $name {
type Error = ();
fn try_from(value: $repr) -> ::core::result::Result<Self, ()> {
match value {
$($constant => Ok( $name :: $enum ),)*
_ => Err(())
}
}
}
impl ::core::convert::From<$name> for $repr {
fn from(value: $name) -> $repr {
match value {
$($name :: $enum => $constant,)*
}
}
}
}
}

View File

@ -0,0 +1,21 @@
pub mod commands;
mod consts;
pub mod control;
mod driver;
pub mod event;
pub mod indications;
mod macros;
mod opcodes;
pub mod responses;
pub mod runner;
pub mod typedefs;
pub use crate::mac::control::Control;
use crate::mac::driver::Driver;
pub use crate::mac::runner::Runner;
const MTU: usize = 127;
pub async fn new<'a>(runner: &'a Runner<'a>) -> (Control<'a>, Driver<'a>) {
(Control::new(runner), Driver::new(runner))
}

View File

@ -0,0 +1,92 @@
const ST_VENDOR_OGF: u16 = 0x3F;
const MAC_802_15_4_CMD_OPCODE_OFFSET: u16 = 0x280;
const fn opcode(ocf: u16) -> isize {
((ST_VENDOR_OGF << 9) | (MAC_802_15_4_CMD_OPCODE_OFFSET + ocf)) as isize
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum OpcodeM4ToM0 {
MlmeAssociateReq = opcode(0x00),
MlmeAssociateRes = opcode(0x01),
MlmeDisassociateReq = opcode(0x02),
MlmeGetReq = opcode(0x03),
MlmeGtsReq = opcode(0x04),
MlmeOrphanRes = opcode(0x05),
MlmeResetReq = opcode(0x06),
MlmeRxEnableReq = opcode(0x07),
MlmeScanReq = opcode(0x08),
MlmeSetReq = opcode(0x09),
MlmeStartReq = opcode(0x0A),
MlmeSyncReq = opcode(0x0B),
MlmePollReq = opcode(0x0C),
MlmeDpsReq = opcode(0x0D),
MlmeSoundingReq = opcode(0x0E),
MlmeCalibrateReq = opcode(0x0F),
McpsDataReq = opcode(0x10),
McpsPurgeReq = opcode(0x11),
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum OpcodeM0ToM4 {
MlmeAssociateCnf = 0x00,
MlmeDisassociateCnf,
MlmeGetCnf,
MlmeGtsCnf,
MlmeResetCnf,
MlmeRxEnableCnf,
MlmeScanCnf,
MlmeSetCnf,
MlmeStartCnf,
MlmePollCnf,
MlmeDpsCnf,
MlmeSoundingCnf,
MlmeCalibrateCnf,
McpsDataCnf,
McpsPurgeCnf,
MlmeAssociateInd,
MlmeDisassociateInd,
MlmeBeaconNotifyInd,
MlmeCommStatusInd,
MlmeGtsInd,
MlmeOrphanInd,
MlmeSyncLossInd,
MlmeDpsInd,
McpsDataInd,
MlmePollInd,
}
impl TryFrom<u16> for OpcodeM0ToM4 {
type Error = ();
fn try_from(value: u16) -> Result<Self, Self::Error> {
match value {
0 => Ok(Self::MlmeAssociateCnf),
1 => Ok(Self::MlmeDisassociateCnf),
2 => Ok(Self::MlmeGetCnf),
3 => Ok(Self::MlmeGtsCnf),
4 => Ok(Self::MlmeResetCnf),
5 => Ok(Self::MlmeRxEnableCnf),
6 => Ok(Self::MlmeScanCnf),
7 => Ok(Self::MlmeSetCnf),
8 => Ok(Self::MlmeStartCnf),
9 => Ok(Self::MlmePollCnf),
10 => Ok(Self::MlmeDpsCnf),
11 => Ok(Self::MlmeSoundingCnf),
12 => Ok(Self::MlmeCalibrateCnf),
13 => Ok(Self::McpsDataCnf),
14 => Ok(Self::McpsPurgeCnf),
15 => Ok(Self::MlmeAssociateInd),
16 => Ok(Self::MlmeDisassociateInd),
17 => Ok(Self::MlmeBeaconNotifyInd),
18 => Ok(Self::MlmeCommStatusInd),
19 => Ok(Self::MlmeGtsInd),
20 => Ok(Self::MlmeOrphanInd),
21 => Ok(Self::MlmeSyncLossInd),
22 => Ok(Self::MlmeDpsInd),
23 => Ok(Self::McpsDataInd),
24 => Ok(Self::MlmePollInd),
_ => Err(()),
}
}
}

View File

@ -0,0 +1,273 @@
use super::consts::{MAX_ED_SCAN_RESULTS_SUPPORTED, MAX_PAN_DESC_SUPPORTED, MAX_SOUNDING_LIST_SUPPORTED};
use super::event::ParseableMacEvent;
use super::typedefs::{
AddressMode, AssociationStatus, KeyIdMode, MacAddress, MacStatus, PanDescriptor, PanId, PibId, ScanType,
SecurityLevel,
};
/// MLME ASSOCIATE Confirm used to inform of the initiating device whether
/// its request to associate was successful or unsuccessful
#[repr(C)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct AssociateConfirm {
/// short address allocated by the coordinator on successful association
pub assoc_short_address: [u8; 2],
/// status of the association request
pub status: AssociationStatus,
/// security level to be used
pub security_level: SecurityLevel,
/// the originator of the key to be used
pub key_source: [u8; 8],
/// the mode used to identify the key to be used
pub key_id_mode: KeyIdMode,
/// the index of the key to be used
pub key_index: u8,
/// byte stuffing to keep 32 bit alignment
a_stuffing: [u8; 2],
}
impl ParseableMacEvent for AssociateConfirm {}
/// MLME DISASSOCIATE Confirm used to send disassociation Confirmation to the application.
#[repr(C)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DisassociateConfirm {
/// status of the disassociation attempt
pub status: MacStatus,
/// device addressing mode used
pub device_addr_mode: AddressMode,
/// the identifier of the PAN of the device
pub device_pan_id: PanId,
/// device address
pub device_address: MacAddress,
}
impl ParseableMacEvent for DisassociateConfirm {}
/// MLME GET Confirm which requests information about a given PIB attribute
#[repr(C)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct GetConfirm {
/// The pointer to the value of the PIB attribute attempted to read
pub pib_attribute_value_ptr: *const u8,
/// Status of the GET attempt
pub status: MacStatus,
/// The name of the PIB attribute attempted to read
pub pib_attribute: PibId,
/// The lenght of the PIB attribute Value return
pub pib_attribute_value_len: u8,
/// byte stuffing to keep 32 bit alignment
a_stuffing: [u8; 1],
}
impl ParseableMacEvent for GetConfirm {}
/// MLME GTS Confirm which eports the results of a request to allocate a new GTS
/// or to deallocate an existing GTS
#[repr(C)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct GtsConfirm {
/// The characteristics of the GTS
pub gts_characteristics: u8,
/// The status of the GTS reques
pub status: MacStatus,
/// byte stuffing to keep 32 bit alignment
a_stuffing: [u8; 2],
}
impl ParseableMacEvent for GtsConfirm {}
/// MLME RESET Confirm which is used to report the results of the reset operation
#[repr(C)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ResetConfirm {
/// The result of the reset operation
pub status: MacStatus,
/// byte stuffing to keep 32 bit alignment
a_stuffing: [u8; 3],
}
impl ParseableMacEvent for ResetConfirm {}
/// MLME RX ENABLE Confirm which is used to report the results of the attempt
/// to enable or disable the receiver
#[repr(C)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct RxEnableConfirm {
/// Result of the request to enable or disable the receiver
pub status: MacStatus,
/// byte stuffing to keep 32 bit alignment
a_stuffing: [u8; 3],
}
impl ParseableMacEvent for RxEnableConfirm {}
/// MLME SCAN Confirm which is used to report the result of the channel scan request
#[repr(C)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ScanConfirm {
/// Status of the scan request
pub status: MacStatus,
/// The type of scan performed
pub scan_type: ScanType,
/// Channel page on which the scan was performed
pub channel_page: u8,
/// Channels given in the request which were not scanned
pub unscanned_channels: [u8; 4],
/// Number of elements returned in the appropriate result lists
pub result_list_size: u8,
/// List of energy measurements
pub energy_detect_list: [u8; MAX_ED_SCAN_RESULTS_SUPPORTED],
/// List of PAN descriptors
pub pan_descriptor_list: [PanDescriptor; MAX_PAN_DESC_SUPPORTED],
/// Categorization of energy detected in channel
pub detected_category: u8,
/// For UWB PHYs, the list of energy measurements taken
pub uwb_energy_detect_list: [u8; MAX_ED_SCAN_RESULTS_SUPPORTED],
}
impl ParseableMacEvent for ScanConfirm {}
/// MLME SET Confirm which reports the result of an attempt to write a value to a PIB attribute
#[repr(C)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SetConfirm {
/// The result of the set operation
pub status: MacStatus,
/// The name of the PIB attribute that was written
pub pin_attribute: PibId,
/// byte stuffing to keep 32 bit alignment
a_stuffing: [u8; 2],
}
impl ParseableMacEvent for SetConfirm {}
/// MLME START Confirm which is used to report the results of the attempt to
/// start using a new superframe configuration
#[repr(C)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct StartConfirm {
/// Result of the attempt to start using an updated superframe configuration
pub status: MacStatus,
/// byte stuffing to keep 32 bit alignment
a_stuffing: [u8; 3],
}
impl ParseableMacEvent for StartConfirm {}
/// MLME POLL Confirm which is used to report the result of a request to poll the coordinator for data
#[repr(C)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PollConfirm {
/// The status of the data request
pub status: MacStatus,
/// byte stuffing to keep 32 bit alignment
a_stuffing: [u8; 3],
}
impl ParseableMacEvent for PollConfirm {}
/// MLME DPS Confirm which reports the results of the attempt to enable or disable the DPS
#[repr(C)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DpsConfirm {
/// The status of the DPS request
pub status: MacStatus,
/// byte stuffing to keep 32 bit alignment
a_stuffing: [u8; 3],
}
impl ParseableMacEvent for DpsConfirm {}
/// MLME SOUNDING Confirm which reports the result of a request to the PHY to provide
/// channel sounding information
#[repr(C)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SoundingConfirm {
/// Results of the sounding measurement
pub sounding_list: [u8; MAX_SOUNDING_LIST_SUPPORTED],
status: u8,
}
impl ParseableMacEvent for SoundingConfirm {}
/// MLME CALIBRATE Confirm which reports the result of a request to the PHY
/// to provide internal propagation path information
#[repr(C)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CalibrateConfirm {
/// The status of the attempt to return sounding data
pub status: MacStatus,
/// byte stuffing to keep 32 bit alignment
a_stuffing: [u8; 3],
/// A count of the propagation time from the ranging counter
/// to the transmit antenna
pub cal_tx_rmaker_offset: u32,
/// A count of the propagation time from the receive antenna
/// to the ranging counter
pub cal_rx_rmaker_offset: u32,
}
impl ParseableMacEvent for CalibrateConfirm {}
/// MCPS DATA Confirm which will be used for reporting the results of
/// MAC data related requests from the application
#[repr(C)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DataConfirm {
/// The handle associated with the MSDU being confirmed
pub msdu_handle: u8,
/// The time, in symbols, at which the data were transmitted
pub time_stamp: [u8; 4],
/// ranging status
pub ranging_received: u8,
/// The status of the last MSDU transmission
pub status: MacStatus,
/// time units corresponding to an RMARKER at the antenna at
/// the beginning of a ranging exchange
pub ranging_counter_start: u32,
/// time units corresponding to an RMARKER at the antenna
/// at the end of a ranging exchange
pub ranging_counter_stop: u32,
/// time units in a message exchange over which the tracking offset was measured
pub ranging_tracking_interval: u32,
/// time units slipped or advanced by the radio tracking system
pub ranging_offset: u32,
/// The FoM characterizing the ranging measurement
pub ranging_fom: u8,
/// byte stuffing to keep 32 bit alignment
a_stuffing: [u8; 3],
}
impl ParseableMacEvent for DataConfirm {}
/// MCPS PURGE Confirm which will be used by the MAC to notify the application of
/// the status of its request to purge an MSDU from the transaction queue
#[repr(C)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PurgeConfirm {
/// Handle associated with the MSDU requested to be purged from the transaction queue
pub msdu_handle: u8,
/// The status of the request
pub status: MacStatus,
/// byte stuffing to keep 32 bit alignment
a_stuffing: [u8; 2],
}
impl ParseableMacEvent for PurgeConfirm {}

View File

@ -0,0 +1,109 @@
use core::cell::RefCell;
use embassy_futures::join;
use embassy_sync::blocking_mutex;
use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex};
use embassy_sync::channel::Channel;
use embassy_sync::mutex::Mutex;
use embassy_sync::signal::Signal;
use crate::mac::commands::DataRequest;
use crate::mac::event::MacEvent;
use crate::mac::typedefs::{AddressMode, MacAddress, PanId, SecurityLevel};
use crate::mac::MTU;
use crate::sub::mac::Mac;
type ZeroCopyPubSub<M, T> = blocking_mutex::Mutex<M, RefCell<Option<Signal<NoopRawMutex, T>>>>;
pub struct Runner<'a> {
pub(crate) mac_subsystem: Mac,
// rx event backpressure is already provided through the MacEvent drop mechanism
// therefore, we don't need to worry about overwriting events
pub(crate) rx_event_channel: ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>,
pub(crate) read_mutex: Mutex<CriticalSectionRawMutex, ()>,
pub(crate) write_mutex: Mutex<CriticalSectionRawMutex, ()>,
pub(crate) rx_channel: Channel<CriticalSectionRawMutex, MacEvent<'a>, 1>,
pub(crate) tx_channel: Channel<CriticalSectionRawMutex, (&'a mut [u8; MTU], usize), 5>,
pub(crate) tx_buf_channel: Channel<CriticalSectionRawMutex, &'a mut [u8; MTU], 5>,
}
impl<'a> Runner<'a> {
pub fn new(mac: Mac, tx_buf_queue: [&'a mut [u8; MTU]; 5]) -> Self {
let this = Self {
mac_subsystem: mac,
rx_event_channel: blocking_mutex::Mutex::new(RefCell::new(None)),
read_mutex: Mutex::new(()),
write_mutex: Mutex::new(()),
rx_channel: Channel::new(),
tx_channel: Channel::new(),
tx_buf_channel: Channel::new(),
};
for buf in tx_buf_queue {
this.tx_buf_channel.try_send(buf).unwrap();
}
this
}
pub async fn run(&'a self) -> ! {
join::join(
async {
loop {
if let Ok(mac_event) = self.mac_subsystem.read().await {
match mac_event {
MacEvent::McpsDataInd(_) => {
self.rx_channel.send(mac_event).await;
}
_ => {
self.rx_event_channel.lock(|s| {
match &*s.borrow() {
Some(signal) => {
signal.signal(mac_event);
}
None => {}
};
});
}
}
}
}
},
async {
let mut msdu_handle = 0x02;
loop {
let (buf, len) = self.tx_channel.recv().await;
let _wm = self.write_mutex.lock().await;
// The mutex should be dropped on the next loop iteration
self.mac_subsystem
.send_command(
DataRequest {
src_addr_mode: AddressMode::Short,
dst_addr_mode: AddressMode::Short,
dst_pan_id: PanId([0x1A, 0xAA]),
dst_address: MacAddress::BROADCAST,
msdu_handle: msdu_handle,
ack_tx: 0x00,
gts_tx: false,
security_level: SecurityLevel::Unsecure,
..Default::default()
}
.set_buffer(&buf[..len]),
)
.await
.unwrap();
msdu_handle = msdu_handle.wrapping_add(1);
// The tx channel should always be of equal capacity to the tx_buf channel
self.tx_buf_channel.try_send(buf).unwrap();
}
},
)
.await;
loop {}
}
}

View File

@ -0,0 +1,381 @@
use core::fmt::Debug;
use crate::numeric_enum;
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum MacError {
Error = 0x01,
NotImplemented = 0x02,
NotSupported = 0x03,
HardwareNotSupported = 0x04,
Undefined = 0x05,
}
impl From<u8> for MacError {
fn from(value: u8) -> Self {
match value {
0x01 => Self::Error,
0x02 => Self::NotImplemented,
0x03 => Self::NotSupported,
0x04 => Self::HardwareNotSupported,
0x05 => Self::Undefined,
_ => Self::Undefined,
}
}
}
numeric_enum! {
#[repr(u8)]
#[derive(Debug, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum MacStatus {
#[default]
Success = 0x00,
Failure = 0xFF
}
}
numeric_enum! {
#[repr(u8)]
/// this enum contains all the MAC PIB Ids
#[derive(Default, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PibId {
// PHY
#[default]
CurrentChannel = 0x00,
ChannelsSupported = 0x01,
TransmitPower = 0x02,
CCAMode = 0x03,
CurrentPage = 0x04,
MaxFrameDuration = 0x05,
SHRDuration = 0x06,
SymbolsPerOctet = 0x07,
// MAC
AckWaitDuration = 0x40,
AssociationPermit = 0x41,
AutoRequest = 0x42,
BeaconPayload = 0x45,
BeaconPayloadLength = 0x46,
BeaconOrder = 0x47,
Bsn = 0x49,
CoordExtendedAdddress = 0x4A,
CoordShortAddress = 0x4B,
Dsn = 0x4C,
MaxFrameTotalWaitTime = 0x58,
MaxFrameRetries = 0x59,
PanId = 0x50,
ResponseWaitTime = 0x5A,
RxOnWhenIdle = 0x52,
SecurityEnabled = 0x5D,
ShortAddress = 0x53,
SuperframeOrder = 0x54,
TimestampSupported = 0x5C,
TransactionPersistenceTime = 0x55,
MaxBe = 0x57,
LifsPeriod = 0x5E,
SifsPeriod = 0x5F,
MaxCsmaBackoffs = 0x4E,
MinBe = 0x4F,
PanCoordinator = 0x10,
AssocPanCoordinator = 0x11,
ExtendedAddress = 0x6F,
AclEntryDescriptor = 0x70,
AclEntryDescriptorSize = 0x71,
DefaultSecurity = 0x72,
DefaultSecurityMaterialLength = 0x73,
DefaultSecurityMaterial = 0x74,
DefaultSecuritySuite = 0x75,
SecurityMode = 0x76,
CurrentAclEntries = 0x80,
DefaultSecurityExtendedAddress = 0x81,
AssociatedPanCoordinator = 0x56,
PromiscuousMode = 0x51,
}
}
numeric_enum! {
#[repr(u8)]
#[derive(Default, Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum AddressMode {
#[default]
NoAddress = 0x00,
Reserved = 0x01,
Short = 0x02,
Extended = 0x03,
}
}
#[derive(Clone, Copy)]
pub union MacAddress {
pub short: [u8; 2],
pub extended: [u8; 8],
}
impl Debug for MacAddress {
fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
unsafe {
write!(
fmt,
"MacAddress {{ short: {:?}, extended: {:?} }}",
self.short, self.extended
)
}
}
}
#[cfg(feature = "defmt")]
impl defmt::Format for MacAddress {
fn format(&self, fmt: defmt::Formatter) {
unsafe {
defmt::write!(
fmt,
"MacAddress {{ short: {}, extended: {} }}",
self.short,
self.extended
)
}
}
}
impl Default for MacAddress {
fn default() -> Self {
Self { short: [0, 0] }
}
}
impl MacAddress {
pub const BROADCAST: Self = Self { short: [0xFF, 0xFF] };
}
impl TryFrom<&[u8]> for MacAddress {
type Error = ();
fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
const SIZE: usize = 8;
if buf.len() < SIZE {
return Err(());
}
Ok(Self {
extended: [buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]],
})
}
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct GtsCharacteristics {
pub fields: u8,
}
/// MAC PAN Descriptor which contains the network details of the device from
/// which the beacon is received
#[derive(Default, Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PanDescriptor {
/// PAN identifier of the coordinator
pub coord_pan_id: PanId,
/// Coordinator addressing mode
pub coord_addr_mode: AddressMode,
/// The current logical channel occupied by the network
pub logical_channel: MacChannel,
/// Coordinator address
pub coord_addr: MacAddress,
/// The current channel page occupied by the network
pub channel_page: u8,
/// PAN coordinator is accepting GTS requests or not
pub gts_permit: bool,
/// Superframe specification as specified in the received beacon frame
pub superframe_spec: [u8; 2],
/// The time at which the beacon frame was received, in symbols
pub time_stamp: [u8; 4],
/// The LQI at which the network beacon was received
pub link_quality: u8,
/// Security level purportedly used by the received beacon frame
pub security_level: u8,
}
impl TryFrom<&[u8]> for PanDescriptor {
type Error = ();
fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
const SIZE: usize = 22;
if buf.len() < SIZE {
return Err(());
}
let coord_addr_mode = AddressMode::try_from(buf[2])?;
let coord_addr = match coord_addr_mode {
AddressMode::NoAddress => MacAddress { short: [0, 0] },
AddressMode::Reserved => MacAddress { short: [0, 0] },
AddressMode::Short => MacAddress {
short: [buf[4], buf[5]],
},
AddressMode::Extended => MacAddress {
extended: [buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]],
},
};
Ok(Self {
coord_pan_id: PanId([buf[0], buf[1]]),
coord_addr_mode,
logical_channel: MacChannel::try_from(buf[3])?,
coord_addr,
channel_page: buf[12],
gts_permit: buf[13] != 0,
superframe_spec: [buf[14], buf[15]],
time_stamp: [buf[16], buf[17], buf[18], buf[19]],
link_quality: buf[20],
security_level: buf[21],
// 2 byte stuffing
})
}
}
numeric_enum! {
#[repr(u8)]
#[derive(Default, Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
/// Building wireless applications with STM32WB series MCUs - Application note 13.10.3
pub enum MacChannel {
Channel11 = 0x0B,
Channel12 = 0x0C,
Channel13 = 0x0D,
Channel14 = 0x0E,
Channel15 = 0x0F,
#[default]
Channel16 = 0x10,
Channel17 = 0x11,
Channel18 = 0x12,
Channel19 = 0x13,
Channel20 = 0x14,
Channel21 = 0x15,
Channel22 = 0x16,
Channel23 = 0x17,
Channel24 = 0x18,
Channel25 = 0x19,
Channel26 = 0x1A,
}
}
#[cfg(not(feature = "defmt"))]
bitflags::bitflags! {
pub struct Capabilities: u8 {
/// 1 if the device is capabaleof becoming a PAN coordinator
const IS_COORDINATOR_CAPABLE = 0b00000001;
/// 1 if the device is an FFD, 0 if it is an RFD
const IS_FFD = 0b00000010;
/// 1 if the device is receiving power from mains, 0 if it is battery-powered
const IS_MAINS_POWERED = 0b00000100;
/// 1 if the device does not disable its receiver to conserver power during idle periods
const RECEIVER_ON_WHEN_IDLE = 0b00001000;
// 0b00010000 reserved
// 0b00100000 reserved
/// 1 if the device is capable of sending and receiving secured MAC frames
const IS_SECURE = 0b01000000;
/// 1 if the device wishes the coordinator to allocate a short address as a result of the association
const ALLOCATE_ADDRESS = 0b10000000;
}
}
#[cfg(feature = "defmt")]
defmt::bitflags! {
pub struct Capabilities: u8 {
/// 1 if the device is capabaleof becoming a PAN coordinator
const IS_COORDINATOR_CAPABLE = 0b00000001;
/// 1 if the device is an FFD, 0 if it is an RFD
const IS_FFD = 0b00000010;
/// 1 if the device is receiving power from mains, 0 if it is battery-powered
const IS_MAINS_POWERED = 0b00000100;
/// 1 if the device does not disable its receiver to conserver power during idle periods
const RECEIVER_ON_WHEN_IDLE = 0b00001000;
// 0b00010000 reserved
// 0b00100000 reserved
/// 1 if the device is capable of sending and receiving secured MAC frames
const IS_SECURE = 0b01000000;
/// 1 if the device wishes the coordinator to allocate a short address as a result of the association
const ALLOCATE_ADDRESS = 0b10000000;
}
}
numeric_enum! {
#[repr(u8)]
#[derive(Default, Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum KeyIdMode {
#[default]
/// the key is determined implicitly from the originator and recipient(s) of the frame
Implicite = 0x00,
/// the key is determined explicitly using a 1 bytes key source and a 1 byte key index
Explicite1Byte = 0x01,
/// the key is determined explicitly using a 4 bytes key source and a 1 byte key index
Explicite4Byte = 0x02,
/// the key is determined explicitly using a 8 bytes key source and a 1 byte key index
Explicite8Byte = 0x03,
}
}
numeric_enum! {
#[repr(u8)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum AssociationStatus {
/// Association successful
Success = 0x00,
/// PAN at capacity
PanAtCapacity = 0x01,
/// PAN access denied
PanAccessDenied = 0x02
}
}
numeric_enum! {
#[repr(u8)]
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DisassociationReason {
/// The coordinator wishes the device to leave the PAN.
CoordRequested = 0x01,
/// The device wishes to leave the PAN.
DeviceRequested = 0x02,
}
}
numeric_enum! {
#[repr(u8)]
#[derive(Default, Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SecurityLevel {
/// MAC Unsecured Mode Security
#[default]
Unsecure = 0x00,
/// MAC ACL Mode Security
AclMode = 0x01,
/// MAC Secured Mode Security
Secured = 0x02,
}
}
numeric_enum! {
#[repr(u8)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ScanType {
EdScan = 0x00,
Active = 0x01,
Passive = 0x02,
Orphan = 0x03
}
}
/// newtype for Pan Id
#[derive(Default, Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PanId(pub [u8; 2]);
impl PanId {
pub const BROADCAST: Self = Self([0xFF, 0xFF]);
}

View File

@ -11,9 +11,10 @@ use embassy_sync::waitqueue::AtomicWaker;
use crate::cmd::CmdPacket;
use crate::consts::TlPacketType;
use crate::evt::{EvtBox, EvtPacket};
use crate::tables::{
Mac802_15_4Table, MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER, TL_MAC_802_15_4_TABLE,
};
use crate::mac::commands::MacCommand;
use crate::mac::event::MacEvent;
use crate::mac::typedefs::MacError;
use crate::tables::{MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER};
use crate::{channels, evt};
static MAC_WAKER: AtomicWaker = AtomicWaker::new();
@ -25,21 +26,13 @@ pub struct Mac {
impl Mac {
pub(crate) fn new() -> Self {
unsafe {
TL_MAC_802_15_4_TABLE.as_mut_ptr().write_volatile(Mac802_15_4Table {
p_cmdrsp_buffer: MAC_802_15_4_CMD_BUFFER.as_mut_ptr().cast(),
p_notack_buffer: MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr().cast(),
evt_queue: ptr::null_mut(),
});
}
Self { phantom: PhantomData }
}
/// `HW_IPCC_MAC_802_15_4_EvtNot`
///
/// This function will stall if the previous `EvtBox` has not been dropped
pub async fn read(&self) -> EvtBox<Self> {
pub async fn tl_read(&self) -> EvtBox<Self> {
// Wait for the last event box to be dropped
poll_fn(|cx| {
MAC_WAKER.register(cx.waker());
@ -63,9 +56,9 @@ impl Mac {
}
/// `HW_IPCC_MAC_802_15_4_CmdEvtNot`
pub async fn write_and_get_response(&self, opcode: u16, payload: &[u8]) -> u8 {
self.write(opcode, payload).await;
Ipcc::flush(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL).await;
pub async fn tl_write_and_get_response(&self, opcode: u16, payload: &[u8]) -> u8 {
self.tl_write(opcode, payload).await;
Ipcc::flush(channels::cpu1::IPCC_MAC_802_15_4_CMD_RSP_CHANNEL).await;
unsafe {
let p_event_packet = MAC_802_15_4_CMD_BUFFER.as_ptr() as *const EvtPacket;
@ -76,22 +69,41 @@ impl Mac {
}
/// `TL_MAC_802_15_4_SendCmd`
pub async fn write(&self, opcode: u16, payload: &[u8]) {
pub async fn tl_write(&self, opcode: u16, payload: &[u8]) {
Ipcc::send(channels::cpu1::IPCC_MAC_802_15_4_CMD_RSP_CHANNEL, || unsafe {
CmdPacket::write_into(
MAC_802_15_4_CMD_BUFFER.as_mut_ptr(),
TlPacketType::OtCmd,
TlPacketType::MacCmd,
opcode,
payload,
);
})
.await;
}
pub async fn send_command<T>(&self, cmd: &T) -> Result<(), MacError>
where
T: MacCommand,
{
let response = self.tl_write_and_get_response(T::OPCODE as u16, cmd.payload()).await;
if response == 0x00 {
Ok(())
} else {
Err(MacError::from(response))
}
}
pub async fn read(&self) -> Result<MacEvent<'_>, ()> {
MacEvent::new(self.tl_read().await)
}
}
impl evt::MemoryManager for Mac {
/// SAFETY: passing a pointer to something other than a managed event packet is UB
unsafe fn drop_event_packet(_: *mut EvtPacket) {
trace!("mac drop event");
// Write the ack
CmdPacket::write_into(
MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _,
@ -101,7 +113,7 @@ impl evt::MemoryManager for Mac {
);
// Clear the rx flag
let _ = poll_once(Ipcc::receive::<bool>(
let _ = poll_once(Ipcc::receive::<()>(
channels::cpu2::IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL,
|| None,
));

View File

@ -4,20 +4,21 @@ use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::task::Poll;
use aligned::{Aligned, A4};
use cortex_m::interrupt;
use embassy_stm32::ipcc::Ipcc;
use embassy_sync::waitqueue::AtomicWaker;
use crate::consts::POOL_SIZE;
use crate::evt::EvtPacket;
use crate::tables::{
MemManagerTable, BLE_SPARE_EVT_BUF, EVT_POOL, FREE_BUF_QUEUE, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE,
};
#[cfg(feature = "ble")]
use crate::tables::BLE_SPARE_EVT_BUF;
use crate::tables::{MemManagerTable, EVT_POOL, FREE_BUF_QUEUE, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE};
use crate::unsafe_linked_list::LinkedListNode;
use crate::{channels, evt};
static MM_WAKER: AtomicWaker = AtomicWaker::new();
static mut LOCAL_FREE_BUF_QUEUE: MaybeUninit<LinkedListNode> = MaybeUninit::uninit();
static mut LOCAL_FREE_BUF_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit());
pub struct MemoryManager {
phantom: PhantomData<MemoryManager>,
@ -30,7 +31,10 @@ impl MemoryManager {
LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr());
TL_MEM_MANAGER_TABLE.as_mut_ptr().write_volatile(MemManagerTable {
#[cfg(feature = "ble")]
spare_ble_buffer: BLE_SPARE_EVT_BUF.as_ptr().cast(),
#[cfg(not(feature = "ble"))]
spare_ble_buffer: core::ptr::null(),
spare_sys_buffer: SYS_SPARE_EVT_BUF.as_ptr().cast(),
blepool: EVT_POOL.as_ptr().cast(),
blepoolsize: POOL_SIZE as u32,

View File

@ -50,7 +50,7 @@ impl Sys {
}
/// `HW_IPCC_SYS_CmdEvtNot`
pub async fn write_and_get_response(&self, opcode: ShciOpcode, payload: &[u8]) -> SchiCommandStatus {
pub async fn write_and_get_response(&self, opcode: ShciOpcode, payload: &[u8]) -> Result<SchiCommandStatus, ()> {
self.write(opcode, payload).await;
Ipcc::flush(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL).await;
@ -59,17 +59,36 @@ impl Sys {
let p_command_event = &((*p_event_packet).evt_serial.evt.payload) as *const _ as *const CcEvt;
let p_payload = &((*p_command_event).payload) as *const u8;
ptr::read_volatile(p_payload).try_into().unwrap()
ptr::read_volatile(p_payload).try_into()
}
}
#[cfg(feature = "mac")]
pub async fn shci_c2_mac_802_15_4_init(&self) -> SchiCommandStatus {
pub async fn shci_c2_mac_802_15_4_init(&self) -> Result<SchiCommandStatus, ()> {
use crate::tables::{
Mac802_15_4Table, TracesTable, MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER,
TL_MAC_802_15_4_TABLE, TL_TRACES_TABLE, TRACES_EVT_QUEUE,
};
unsafe {
LinkedListNode::init_head(TRACES_EVT_QUEUE.as_mut_ptr() as *mut _);
TL_TRACES_TABLE.as_mut_ptr().write_volatile(TracesTable {
traces_queue: TRACES_EVT_QUEUE.as_ptr() as *const _,
});
TL_MAC_802_15_4_TABLE.as_mut_ptr().write_volatile(Mac802_15_4Table {
p_cmdrsp_buffer: MAC_802_15_4_CMD_BUFFER.as_mut_ptr().cast(),
p_notack_buffer: MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr().cast(),
evt_queue: core::ptr::null_mut(),
});
};
self.write_and_get_response(ShciOpcode::Mac802_15_4Init, &[]).await
}
#[cfg(feature = "ble")]
pub async fn shci_c2_ble_init(&self, param: ShciBleInitCmdParam) -> SchiCommandStatus {
pub async fn shci_c2_ble_init(&self, param: ShciBleInitCmdParam) -> Result<SchiCommandStatus, ()> {
self.write_and_get_response(ShciOpcode::BleInit, param.payload()).await
}

View File

@ -4,6 +4,8 @@ use aligned::{Aligned, A4};
use bit_field::BitField;
use crate::cmd::{AclDataPacket, CmdPacket};
#[cfg(feature = "mac")]
use crate::consts::C_SIZE_CMD_STRING;
use crate::consts::{POOL_SIZE, TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE};
use crate::unsafe_linked_list::LinkedListNode;
@ -80,7 +82,7 @@ impl WirelessFwInfoTable {
}
#[derive(Debug, Clone)]
#[repr(C, align(4))]
#[repr(C)]
pub struct DeviceInfoTable {
pub safe_boot_info_table: SafeBootInfoTable,
pub rss_info_table: RssInfoTable,
@ -88,7 +90,7 @@ pub struct DeviceInfoTable {
}
#[derive(Debug)]
#[repr(C, align(4))]
#[repr(C)]
pub struct BleTable {
pub pcmd_buffer: *mut CmdPacket,
pub pcs_buffer: *const u8,
@ -97,16 +99,15 @@ pub struct BleTable {
}
#[derive(Debug)]
#[repr(C, align(4))]
#[repr(C)]
pub struct ThreadTable {
pub nostack_buffer: *const u8,
pub clicmdrsp_buffer: *const u8,
pub otcmdrsp_buffer: *const u8,
}
// TODO: use later
#[derive(Debug)]
#[repr(C, align(4))]
#[repr(C)]
pub struct LldTestsTable {
pub clicmdrsp_buffer: *const u8,
pub m0cmd_buffer: *const u8,
@ -114,7 +115,7 @@ pub struct LldTestsTable {
// TODO: use later
#[derive(Debug)]
#[repr(C, align(4))]
#[repr(C)]
pub struct BleLldTable {
pub cmdrsp_buffer: *const u8,
pub m0cmd_buffer: *const u8,
@ -122,7 +123,7 @@ pub struct BleLldTable {
// TODO: use later
#[derive(Debug)]
#[repr(C, align(4))]
#[repr(C)]
pub struct ZigbeeTable {
pub notif_m0_to_m4_buffer: *const u8,
pub appli_cmd_m4_to_m0_bufer: *const u8,
@ -130,14 +131,14 @@ pub struct ZigbeeTable {
}
#[derive(Debug)]
#[repr(C, align(4))]
#[repr(C)]
pub struct SysTable {
pub pcmd_buffer: *mut CmdPacket,
pub sys_queue: *const LinkedListNode,
}
#[derive(Debug)]
#[repr(C, align(4))]
#[repr(C)]
pub struct MemManagerTable {
pub spare_ble_buffer: *const u8,
pub spare_sys_buffer: *const u8,
@ -152,13 +153,13 @@ pub struct MemManagerTable {
}
#[derive(Debug)]
#[repr(C, align(4))]
#[repr(C)]
pub struct TracesTable {
pub traces_queue: *const u8,
}
#[derive(Debug)]
#[repr(C, align(4))]
#[repr(C)]
pub struct Mac802_15_4Table {
pub p_cmdrsp_buffer: *const u8,
pub p_notack_buffer: *const u8,
@ -176,6 +177,9 @@ pub struct RefTable {
pub mem_manager_table: *const MemManagerTable,
pub traces_table: *const TracesTable,
pub mac_802_15_4_table: *const Mac802_15_4Table,
pub zigbee_table: *const ZigbeeTable,
pub lld_tests_table: *const LldTestsTable,
pub ble_lld_table: *const BleLldTable,
}
// --------------------- ref table ---------------------
@ -183,57 +187,57 @@ pub struct RefTable {
pub static mut TL_REF_TABLE: MaybeUninit<RefTable> = MaybeUninit::uninit();
#[link_section = "MB_MEM1"]
pub static mut TL_DEVICE_INFO_TABLE: MaybeUninit<DeviceInfoTable> = MaybeUninit::uninit();
pub static mut TL_DEVICE_INFO_TABLE: Aligned<A4, MaybeUninit<DeviceInfoTable>> = Aligned(MaybeUninit::uninit());
#[link_section = "MB_MEM1"]
pub static mut TL_BLE_TABLE: MaybeUninit<BleTable> = MaybeUninit::uninit();
pub static mut TL_BLE_TABLE: Aligned<A4, MaybeUninit<BleTable>> = Aligned(MaybeUninit::uninit());
#[link_section = "MB_MEM1"]
pub static mut TL_THREAD_TABLE: MaybeUninit<ThreadTable> = MaybeUninit::uninit();
// #[link_section = "MB_MEM1"]
// pub static mut TL_LLD_TESTS_TABLE: MaybeUninit<LldTestTable> = MaybeUninit::uninit();
// #[link_section = "MB_MEM1"]
// pub static mut TL_BLE_LLD_TABLE: MaybeUninit<BleLldTable> = MaybeUninit::uninit();
pub static mut TL_THREAD_TABLE: Aligned<A4, MaybeUninit<ThreadTable>> = Aligned(MaybeUninit::uninit());
#[link_section = "MB_MEM1"]
pub static mut TL_SYS_TABLE: MaybeUninit<SysTable> = MaybeUninit::uninit();
pub static mut TL_LLD_TESTS_TABLE: Aligned<A4, MaybeUninit<LldTestsTable>> = Aligned(MaybeUninit::uninit());
#[link_section = "MB_MEM1"]
pub static mut TL_MEM_MANAGER_TABLE: MaybeUninit<MemManagerTable> = MaybeUninit::uninit();
pub static mut TL_BLE_LLD_TABLE: Aligned<A4, MaybeUninit<BleLldTable>> = Aligned(MaybeUninit::uninit());
#[link_section = "MB_MEM1"]
pub static mut TL_TRACES_TABLE: MaybeUninit<TracesTable> = MaybeUninit::uninit();
pub static mut TL_SYS_TABLE: Aligned<A4, MaybeUninit<SysTable>> = Aligned(MaybeUninit::uninit());
#[link_section = "MB_MEM1"]
pub static mut TL_MAC_802_15_4_TABLE: MaybeUninit<Mac802_15_4Table> = MaybeUninit::uninit();
pub static mut TL_MEM_MANAGER_TABLE: Aligned<A4, MaybeUninit<MemManagerTable>> = Aligned(MaybeUninit::uninit());
// #[link_section = "MB_MEM1"]
// pub static mut TL_ZIGBEE_TABLE: MaybeUninit<ZigbeeTable> = MaybeUninit::uninit();
#[link_section = "MB_MEM1"]
pub static mut TL_TRACES_TABLE: Aligned<A4, MaybeUninit<TracesTable>> = Aligned(MaybeUninit::uninit());
#[link_section = "MB_MEM1"]
pub static mut TL_MAC_802_15_4_TABLE: Aligned<A4, MaybeUninit<Mac802_15_4Table>> = Aligned(MaybeUninit::uninit());
#[link_section = "MB_MEM1"]
pub static mut TL_ZIGBEE_TABLE: Aligned<A4, MaybeUninit<ZigbeeTable>> = Aligned(MaybeUninit::uninit());
// --------------------- tables ---------------------
#[link_section = "MB_MEM1"]
pub static mut FREE_BUF_QUEUE: MaybeUninit<LinkedListNode> = MaybeUninit::uninit();
pub static mut FREE_BUF_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit());
#[allow(dead_code)]
#[link_section = "MB_MEM1"]
pub static mut TRACES_EVT_QUEUE: MaybeUninit<LinkedListNode> = MaybeUninit::uninit();
pub static mut TRACES_EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit());
#[link_section = "MB_MEM2"]
pub static mut CS_BUFFER: MaybeUninit<Aligned<A4, [u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE]>> =
MaybeUninit::uninit();
pub static mut CS_BUFFER: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE]>> =
Aligned(MaybeUninit::uninit());
#[link_section = "MB_MEM2"]
pub static mut EVT_QUEUE: MaybeUninit<LinkedListNode> = MaybeUninit::uninit();
pub static mut EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit());
#[link_section = "MB_MEM2"]
pub static mut SYSTEM_EVT_QUEUE: MaybeUninit<LinkedListNode> = MaybeUninit::uninit();
pub static mut SYSTEM_EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit());
// --------------------- app tables ---------------------
#[cfg(feature = "mac")]
#[link_section = "MB_MEM2"]
pub static mut MAC_802_15_4_CMD_BUFFER: MaybeUninit<CmdPacket> = MaybeUninit::uninit();
pub static mut MAC_802_15_4_CMD_BUFFER: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::uninit());
#[cfg(feature = "mac")]
#[link_section = "MB_MEM2"]
@ -242,23 +246,31 @@ pub static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit<
> = MaybeUninit::uninit();
#[link_section = "MB_MEM2"]
pub static mut EVT_POOL: MaybeUninit<Aligned<A4, [u8; POOL_SIZE]>> = MaybeUninit::uninit();
pub static mut EVT_POOL: Aligned<A4, MaybeUninit<[u8; POOL_SIZE]>> = Aligned(MaybeUninit::uninit());
#[link_section = "MB_MEM2"]
pub static mut SYS_CMD_BUF: MaybeUninit<CmdPacket> = MaybeUninit::uninit();
pub static mut SYS_CMD_BUF: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::uninit());
#[link_section = "MB_MEM2"]
pub static mut SYS_SPARE_EVT_BUF: MaybeUninit<Aligned<A4, [u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> =
MaybeUninit::uninit();
pub static mut SYS_SPARE_EVT_BUF: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> =
Aligned(MaybeUninit::uninit());
#[cfg(feature = "mac")]
#[link_section = "MB_MEM2"]
pub static mut MAC_802_15_4_CNFINDNOT: Aligned<A4, MaybeUninit<[u8; C_SIZE_CMD_STRING]>> =
Aligned(MaybeUninit::uninit());
#[cfg(feature = "ble")]
#[link_section = "MB_MEM1"]
pub static mut BLE_CMD_BUFFER: MaybeUninit<CmdPacket> = MaybeUninit::uninit();
pub static mut BLE_CMD_BUFFER: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::uninit());
#[cfg(feature = "ble")]
#[link_section = "MB_MEM2"]
pub static mut BLE_SPARE_EVT_BUF: MaybeUninit<Aligned<A4, [u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> =
MaybeUninit::uninit();
pub static mut BLE_SPARE_EVT_BUF: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> =
Aligned(MaybeUninit::uninit());
#[cfg(feature = "ble")]
#[link_section = "MB_MEM2"]
// fuck these "magic" numbers from ST ---v---v
pub static mut HCI_ACL_DATA_BUFFER: MaybeUninit<Aligned<A4, [u8; TL_PACKET_HEADER_SIZE + 5 + 251]>> =
MaybeUninit::uninit();
pub static mut HCI_ACL_DATA_BUFFER: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]>> =
Aligned(MaybeUninit::uninit());

View File

@ -32,7 +32,7 @@ flavors = [
[dependencies]
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true }
embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common", features = ["cortex-m", "prio-bits-4"] }
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
@ -40,9 +40,9 @@ embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true}
embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true}
embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true}
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true}
embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true}
embedded-hal-nb = { version = "=1.0.0-alpha.3", optional = true}
embedded-storage = "0.3.0"
embedded-storage-async = { version = "0.4.0", optional = true }
@ -57,7 +57,7 @@ sdio-host = "0.5.0"
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
critical-section = "1.1"
atomic-polyfill = "1.0.1"
stm32-metapac = "12"
stm32-metapac = "13"
vcell = "0.1.3"
bxcan = "0.7.0"
nb = "1.0.0"
@ -74,7 +74,7 @@ critical-section = { version = "1.1", features = ["std"] }
[build-dependencies]
proc-macro2 = "1.0.36"
quote = "1.0.15"
stm32-metapac = { version = "12", default-features = false, features = ["metadata"]}
stm32-metapac = { version = "13", default-features = false, features = ["metadata"]}
[features]
default = ["rt"]

View File

@ -348,9 +348,7 @@ fn main() {
g.extend(quote! {
impl crate::rcc::sealed::RccPeripheral for peripherals::#pname {
fn frequency() -> crate::time::Hertz {
critical_section::with(|_| unsafe {
crate::rcc::get_freqs().#clk
})
unsafe { crate::rcc::get_freqs().#clk }
}
fn enable() {
critical_section::with(|_| {

View File

@ -1,5 +1,6 @@
#![macro_use]
#[cfg(not(adc_f3))]
#[cfg_attr(adc_f1, path = "f1.rs")]
#[cfg_attr(adc_v1, path = "v1.rs")]
#[cfg_attr(adc_v2, path = "v2.rs")]
@ -7,14 +8,16 @@
#[cfg_attr(adc_v4, path = "v4.rs")]
mod _version;
#[cfg(not(adc_f1))]
#[cfg(not(any(adc_f1, adc_f3)))]
mod resolution;
mod sample_time;
#[cfg(not(adc_f3))]
#[allow(unused)]
pub use _version::*;
#[cfg(not(adc_f1))]
#[cfg(not(any(adc_f1, adc_f3)))]
pub use resolution::Resolution;
#[cfg(not(adc_f3))]
pub use sample_time::SampleTime;
use crate::peripherals;
@ -22,13 +25,14 @@ use crate::peripherals;
pub struct Adc<'d, T: Instance> {
#[allow(unused)]
adc: crate::PeripheralRef<'d, T>,
#[cfg(not(adc_f3))]
sample_time: SampleTime,
}
pub(crate) mod sealed {
pub trait Instance {
fn regs() -> crate::pac::adc::Adc;
#[cfg(all(not(adc_f1), not(adc_v1)))]
#[cfg(not(any(adc_f1, adc_v1, adc_f3)))]
fn common_regs() -> crate::pac::adccommon::AdcCommon;
}
@ -56,7 +60,7 @@ foreach_peripheral!(
fn regs() -> crate::pac::adc::Adc {
crate::pac::$inst
}
#[cfg(all(not(adc_f1), not(adc_v1)))]
#[cfg(not(any(adc_f1, adc_v1, adc_f3)))]
fn common_regs() -> crate::pac::adccommon::AdcCommon {
foreach_peripheral!{
(adccommon, $common_inst:ident) => {

View File

@ -1,3 +1,4 @@
#[cfg(not(adc_f3))]
macro_rules! impl_sample_time {
($default_doc:expr, $default:ident, ($(($doc:expr, $variant:ident, $pac_variant:ident)),*)) => {
#[doc = concat!("ADC sample time\n\nThe default setting is ", $default_doc, " ADC clock cycles.")]

View File

@ -1,3 +1,4 @@
use core::cell::{RefCell, RefMut};
use core::future::poll_fn;
use core::marker::PhantomData;
use core::ops::{Deref, DerefMut};
@ -72,7 +73,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::SCEInterrupt> for SceInterrup
}
pub struct Can<'d, T: Instance> {
can: bxcan::Can<BxcanInstance<'d, T>>,
pub can: RefCell<bxcan::Can<BxcanInstance<'d, T>>>,
}
#[derive(Debug)]
@ -147,19 +148,24 @@ impl<'d, T: Instance> Can<'d, T> {
tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
let can = bxcan::Can::builder(BxcanInstance(peri)).leave_disabled();
Self { can }
let can_ref_cell = RefCell::new(can);
Self { can: can_ref_cell }
}
pub fn set_bitrate(&mut self, bitrate: u32) {
let bit_timing = Self::calc_bxcan_timings(T::frequency(), bitrate).unwrap();
self.can.modify_config().set_bit_timing(bit_timing).leave_disabled();
self.can
.borrow_mut()
.modify_config()
.set_bit_timing(bit_timing)
.leave_disabled();
}
/// Queues the message to be sent but exerts backpressure
pub async fn write(&mut self, frame: &Frame) -> bxcan::TransmitStatus {
poll_fn(|cx| {
T::state().tx_waker.register(cx.waker());
if let Ok(status) = self.can.transmit(frame) {
if let Ok(status) = self.can.borrow_mut().transmit(frame) {
return Poll::Ready(status);
}
@ -341,6 +347,79 @@ impl<'d, T: Instance> Can<'d, T> {
// Pack into BTR register values
Some((sjw - 1) << 24 | (bs1 as u32 - 1) << 16 | (bs2 as u32 - 1) << 20 | (prescaler as u32 - 1))
}
pub fn split<'c>(&'c self) -> (CanTx<'c, 'd, T>, CanRx<'c, 'd, T>) {
(CanTx { can: &self.can }, CanRx { can: &self.can })
}
pub fn as_mut(&self) -> RefMut<'_, bxcan::Can<BxcanInstance<'d, T>>> {
self.can.borrow_mut()
}
}
pub struct CanTx<'c, 'd, T: Instance> {
can: &'c RefCell<bxcan::Can<BxcanInstance<'d, T>>>,
}
impl<'c, 'd, T: Instance> CanTx<'c, 'd, T> {
pub async fn write(&mut self, frame: &Frame) -> bxcan::TransmitStatus {
poll_fn(|cx| {
T::state().tx_waker.register(cx.waker());
if let Ok(status) = self.can.borrow_mut().transmit(frame) {
return Poll::Ready(status);
}
Poll::Pending
})
.await
}
pub async fn flush(&self, mb: bxcan::Mailbox) {
poll_fn(|cx| {
T::state().tx_waker.register(cx.waker());
if T::regs().tsr().read().tme(mb.index()) {
return Poll::Ready(());
}
Poll::Pending
})
.await;
}
}
#[allow(dead_code)]
pub struct CanRx<'c, 'd, T: Instance> {
can: &'c RefCell<bxcan::Can<BxcanInstance<'d, T>>>,
}
impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> {
pub async fn read(&mut self) -> Result<(u16, bxcan::Frame), BusError> {
poll_fn(|cx| {
T::state().err_waker.register(cx.waker());
if let Poll::Ready((time, frame)) = T::state().rx_queue.recv().poll_unpin(cx) {
return Poll::Ready(Ok((time, frame)));
} else if let Some(err) = self.curr_error() {
return Poll::Ready(Err(err));
}
Poll::Pending
})
.await
}
fn curr_error(&self) -> Option<BusError> {
let err = { T::regs().esr().read() };
if err.boff() {
return Some(BusError::BusOff);
} else if err.epvf() {
return Some(BusError::BusPassive);
} else if err.ewgf() {
return Some(BusError::BusWarning);
} else if let Some(err) = err.lec().into_bus_err() {
return Some(err);
}
None
}
}
enum RxFifo {
@ -358,7 +437,7 @@ impl<'d, T: Instance> Drop for Can<'d, T> {
}
impl<'d, T: Instance> Deref for Can<'d, T> {
type Target = bxcan::Can<BxcanInstance<'d, T>>;
type Target = RefCell<bxcan::Can<BxcanInstance<'d, T>>>;
fn deref(&self) -> &Self::Target {
&self.can

View File

@ -0,0 +1,66 @@
pub use bxcan;
use embassy_hal_common::PeripheralRef;
use crate::peripherals;
pub(crate) mod sealed {
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::channel::Channel;
use embassy_sync::waitqueue::AtomicWaker;
pub struct State {
pub tx_waker: AtomicWaker,
pub err_waker: AtomicWaker,
pub rx_queue: Channel<CriticalSectionRawMutex, (u16, bxcan::Frame), 32>,
}
impl State {
pub const fn new() -> Self {
Self {
tx_waker: AtomicWaker::new(),
err_waker: AtomicWaker::new(),
rx_queue: Channel::new(),
}
}
}
pub trait Instance {
const REGISTERS: *mut bxcan::RegisterBlock;
fn regs() -> &'static crate::pac::can::Fdcan;
fn state() -> &'static State;
}
}
pub trait InterruptableInstance {}
pub trait Instance: sealed::Instance + InterruptableInstance + 'static {}
pub struct BxcanInstance<'a, T>(PeripheralRef<'a, T>);
unsafe impl<'d, T: Instance> bxcan::Instance for BxcanInstance<'d, T> {
const REGISTERS: *mut bxcan::RegisterBlock = T::REGISTERS;
}
foreach_peripheral!(
(can, $inst:ident) => {
impl sealed::Instance for peripherals::$inst {
const REGISTERS: *mut bxcan::RegisterBlock = crate::pac::$inst.as_ptr() as *mut _;
fn regs() -> &'static crate::pac::can::Fdcan {
&crate::pac::$inst
}
fn state() -> &'static sealed::State {
static STATE: sealed::State = sealed::State::new();
&STATE
}
}
impl Instance for peripherals::$inst {}
impl InterruptableInstance for peripherals::$inst {}
};
);
pin_trait!(RxPin, Instance);
pin_trait!(TxPin, Instance);

View File

@ -1,5 +1,6 @@
#![macro_use]
#[cfg_attr(can_bxcan, path = "bxcan.rs")]
#[cfg_attr(can_fdcan, path = "fdcan.rs")]
mod _version;
pub use _version::*;

View File

@ -51,7 +51,10 @@ impl Ch1Trigger {
fn tsel(&self) -> dac::vals::Tsel1 {
match self {
Ch1Trigger::Tim6 => dac::vals::Tsel1::TIM6_TRGO,
#[cfg(not(dac_v3))]
Ch1Trigger::Tim3 => dac::vals::Tsel1::TIM3_TRGO,
#[cfg(dac_v3)]
Ch1Trigger::Tim3 => dac::vals::Tsel1::TIM1_TRGO,
Ch1Trigger::Tim7 => dac::vals::Tsel1::TIM7_TRGO,
Ch1Trigger::Tim15 => dac::vals::Tsel1::TIM15_TRGO,
Ch1Trigger::Tim2 => dac::vals::Tsel1::TIM2_TRGO,
@ -264,7 +267,7 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> {
});
let tx_request = self.dma.request();
let dma_channel = &self.dma;
let dma_channel = &mut self.dma;
let tx_options = crate::dma::TransferOptions {
circular,
@ -376,7 +379,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> {
});
let tx_request = self.dma.request();
let dma_channel = &self.dma;
let dma_channel = &mut self.dma;
let tx_options = crate::dma::TransferOptions {
circular,

View File

@ -1,10 +1,9 @@
use core::future::Future;
use core::marker::PhantomData;
use core::pin::Pin;
use core::sync::atomic::{fence, Ordering};
use core::sync::atomic::{fence, AtomicUsize, Ordering};
use core::task::{Context, Poll, Waker};
use atomic_polyfill::AtomicUsize;
use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;

View File

@ -1,5 +1,11 @@
//! Generic SMI Ethernet PHY
#[cfg(feature = "time")]
use embassy_time::{Duration, Timer};
use futures::task::Context;
#[cfg(feature = "time")]
use futures::FutureExt;
use super::{StationManagement, PHY};
#[allow(dead_code)]
@ -36,25 +42,47 @@ mod phy_consts {
use self::phy_consts::*;
/// Generic SMI Ethernet PHY
pub struct GenericSMI;
pub struct GenericSMI {
#[cfg(feature = "time")]
poll_interval: Duration,
#[cfg(not(feature = "time"))]
_private: (),
}
impl GenericSMI {
pub fn new() -> Self {
Self {
#[cfg(feature = "time")]
poll_interval: Duration::from_millis(500),
#[cfg(not(feature = "time"))]
_private: (),
}
}
}
unsafe impl PHY for GenericSMI {
/// Reset PHY and wait for it to come out of reset.
fn phy_reset<S: StationManagement>(sm: &mut S) {
fn phy_reset<S: StationManagement>(&mut self, sm: &mut S) {
sm.smi_write(PHY_REG_BCR, PHY_REG_BCR_RESET);
while sm.smi_read(PHY_REG_BCR) & PHY_REG_BCR_RESET == PHY_REG_BCR_RESET {}
}
/// PHY initialisation.
fn phy_init<S: StationManagement>(sm: &mut S) {
fn phy_init<S: StationManagement>(&mut self, sm: &mut S) {
// Clear WU CSR
Self::smi_write_ext(sm, PHY_REG_WUCSR, 0);
self.smi_write_ext(sm, PHY_REG_WUCSR, 0);
// Enable auto-negotiation
sm.smi_write(PHY_REG_BCR, PHY_REG_BCR_AN | PHY_REG_BCR_ANRST | PHY_REG_BCR_100M);
}
fn poll_link<S: StationManagement>(sm: &mut S) -> bool {
fn poll_link<S: StationManagement>(&mut self, sm: &mut S, cx: &mut Context) -> bool {
#[cfg(not(feature = "time"))]
cx.waker().wake_by_ref();
#[cfg(feature = "time")]
let _ = Timer::after(self.poll_interval).poll_unpin(cx);
let bsr = sm.smi_read(PHY_REG_BSR);
// No link without autonegotiate
@ -73,8 +101,13 @@ unsafe impl PHY for GenericSMI {
/// Public functions for the PHY
impl GenericSMI {
#[cfg(feature = "time")]
pub fn set_poll_interval(&mut self, poll_interval: Duration) {
self.poll_interval = poll_interval
}
// Writes a value to an extended PHY register in MMD address space
fn smi_write_ext<S: StationManagement>(sm: &mut S, reg_addr: u16, reg_data: u16) {
fn smi_write_ext<S: StationManagement>(&mut self, sm: &mut S, reg_addr: u16, reg_data: u16) {
sm.smi_write(PHY_REG_CTL, 0x0003); // set address
sm.smi_write(PHY_REG_ADDAR, reg_addr);
sm.smi_write(PHY_REG_CTL, 0x4003); // set data

View File

@ -81,9 +81,7 @@ impl<'d, T: Instance, P: PHY> embassy_net_driver::Driver for Ethernet<'d, T, P>
}
fn link_state(&mut self, cx: &mut Context) -> LinkState {
// TODO: wake cx.waker on link state change
cx.waker().wake_by_ref();
if P::poll_link(self) {
if self.phy.poll_link(&mut self.station_management, cx) {
LinkState::Up
} else {
LinkState::Down
@ -148,11 +146,11 @@ pub unsafe trait StationManagement {
/// The methods cannot move S
pub unsafe trait PHY {
/// Reset PHY and wait for it to come out of reset.
fn phy_reset<S: StationManagement>(sm: &mut S);
fn phy_reset<S: StationManagement>(&mut self, sm: &mut S);
/// PHY initialisation.
fn phy_init<S: StationManagement>(sm: &mut S);
fn phy_init<S: StationManagement>(&mut self, sm: &mut S);
/// Poll link to see if it is up and FD with 100Mbps
fn poll_link<S: StationManagement>(sm: &mut S) -> bool;
fn poll_link<S: StationManagement>(&mut self, sm: &mut S, cx: &mut Context) -> bool;
}
pub(crate) mod sealed {

View File

@ -3,6 +3,7 @@
mod rx_desc;
mod tx_desc;
use core::marker::PhantomData;
use core::sync::atomic::{fence, Ordering};
use embassy_hal_common::{into_ref, PeripheralRef};
@ -48,9 +49,8 @@ pub struct Ethernet<'d, T: Instance, P: PHY> {
pub(crate) rx: RDesRing<'d>,
pins: [PeripheralRef<'d, AnyPin>; 9],
_phy: P,
clock_range: Cr,
phy_addr: u8,
pub(crate) phy: P,
pub(crate) station_management: EthernetStationManagement<T>,
pub(crate) mac_addr: [u8; 6],
}
@ -224,9 +224,12 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
let mut this = Self {
_peri: peri,
pins,
_phy: phy,
clock_range,
phy_addr,
phy: phy,
station_management: EthernetStationManagement {
peri: PhantomData,
clock_range: clock_range,
phy_addr: phy_addr,
},
mac_addr,
tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf),
rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf),
@ -256,8 +259,8 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
w.set_tie(true);
});
P::phy_reset(&mut this);
P::phy_init(&mut this);
this.phy.phy_reset(&mut this.station_management);
this.phy.phy_init(&mut this.station_management);
interrupt::ETH.unpend();
unsafe { interrupt::ETH.enable() };
@ -266,7 +269,13 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
}
}
unsafe impl<'d, T: Instance, P: PHY> StationManagement for Ethernet<'d, T, P> {
pub struct EthernetStationManagement<T: Instance> {
peri: PhantomData<T>,
clock_range: Cr,
phy_addr: u8,
}
unsafe impl<T: Instance> StationManagement for EthernetStationManagement<T> {
fn smi_read(&mut self, reg: u8) -> u16 {
let mac = ETH.ethernet_mac();

View File

@ -1,5 +1,6 @@
mod descriptors;
use core::marker::PhantomData;
use core::sync::atomic::{fence, Ordering};
use embassy_hal_common::{into_ref, PeripheralRef};
@ -40,9 +41,8 @@ pub struct Ethernet<'d, T: Instance, P: PHY> {
pub(crate) tx: TDesRing<'d>,
pub(crate) rx: RDesRing<'d>,
pins: [PeripheralRef<'d, AnyPin>; 9],
_phy: P,
clock_range: u8,
phy_addr: u8,
pub(crate) phy: P,
pub(crate) station_management: EthernetStationManagement<T>,
pub(crate) mac_addr: [u8; 6],
}
@ -201,9 +201,12 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf),
rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf),
pins,
_phy: phy,
clock_range,
phy_addr,
phy: phy,
station_management: EthernetStationManagement {
peri: PhantomData,
clock_range: clock_range,
phy_addr: phy_addr,
},
mac_addr,
};
@ -229,8 +232,8 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
w.set_tie(true);
});
P::phy_reset(&mut this);
P::phy_init(&mut this);
this.phy.phy_reset(&mut this.station_management);
this.phy.phy_init(&mut this.station_management);
interrupt::ETH.unpend();
unsafe { interrupt::ETH.enable() };
@ -239,7 +242,13 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
}
}
unsafe impl<'d, T: Instance, P: PHY> StationManagement for Ethernet<'d, T, P> {
pub struct EthernetStationManagement<T: Instance> {
peri: PhantomData<T>,
clock_range: u8,
phy_addr: u8,
}
unsafe impl<T: Instance> StationManagement for EthernetStationManagement<T> {
fn smi_read(&mut self, reg: u8) -> u16 {
let mac = ETH.ethernet_mac();

View File

@ -1,6 +1,6 @@
use core::marker::PhantomData;
use core::sync::atomic::{fence, Ordering};
use atomic_polyfill::{fence, Ordering};
use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::into_ref;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;

View File

@ -1,6 +1,6 @@
use core::marker::PhantomData;
use core::sync::atomic::{fence, Ordering};
use atomic_polyfill::{fence, Ordering};
use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef};
use stm32_metapac::FLASH_BASE;

View File

@ -1,7 +1,6 @@
use core::convert::TryInto;
use core::ptr::write_volatile;
use atomic_polyfill::{fence, Ordering};
use core::sync::atomic::{fence, Ordering};
use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
use crate::flash::Error;

View File

@ -1,7 +1,6 @@
use core::convert::TryInto;
use core::ptr::write_volatile;
use atomic_polyfill::{fence, Ordering};
use core::sync::atomic::{fence, Ordering};
use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
use crate::flash::Error;

View File

@ -1,8 +1,7 @@
use core::convert::TryInto;
use core::ptr::write_volatile;
use core::sync::atomic::{fence, Ordering};
use core::sync::atomic::{fence, AtomicBool, Ordering};
use atomic_polyfill::AtomicBool;
use embassy_sync::waitqueue::AtomicWaker;
use pac::flash::regs::Sr;
use pac::FLASH_SIZE;

View File

@ -1,7 +1,6 @@
use core::convert::TryInto;
use core::ptr::write_volatile;
use atomic_polyfill::{fence, Ordering};
use core::sync::atomic::{fence, Ordering};
use super::{FlashRegion, FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE};
use crate::flash::Error;

View File

@ -1,6 +1,5 @@
use core::ptr::write_volatile;
use atomic_polyfill::{fence, Ordering};
use core::sync::atomic::{fence, Ordering};
use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
use crate::flash::Error;

View File

@ -86,6 +86,24 @@ macro_rules! fmc_sdram_constructor {
}
impl<'d, T: Instance> Fmc<'d, T> {
fmc_sdram_constructor!(sdram_a12bits_d16bits_4banks_bank1: (
bank: stm32_fmc::SdramTargetBank::Bank1,
addr: [
(a0: A0Pin), (a1: A1Pin), (a2: A2Pin), (a3: A3Pin), (a4: A4Pin), (a5: A5Pin), (a6: A6Pin), (a7: A7Pin), (a8: A8Pin), (a9: A9Pin), (a10: A10Pin), (a11: A11Pin)
],
ba: [(ba0: BA0Pin), (ba1: BA1Pin)],
d: [
(d0: D0Pin), (d1: D1Pin), (d2: D2Pin), (d3: D3Pin), (d4: D4Pin), (d5: D5Pin), (d6: D6Pin), (d7: D7Pin),
(d8: D8Pin), (d9: D9Pin), (d10: D10Pin), (d11: D11Pin), (d12: D12Pin), (d13: D13Pin), (d14: D14Pin), (d15: D15Pin)
],
nbl: [
(nbl0: NBL0Pin), (nbl1: NBL1Pin)
],
ctrl: [
(sdcke: SDCKE0Pin), (sdclk: SDCLKPin), (sdncas: SDNCASPin), (sdne: SDNE0Pin), (sdnras: SDNRASPin), (sdnwe: SDNWEPin)
]
));
fmc_sdram_constructor!(sdram_a12bits_d32bits_4banks_bank1: (
bank: stm32_fmc::SdramTargetBank::Bank1,
addr: [

View File

@ -382,13 +382,18 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
// I2C start
//
// ST SAD+W
Self::master_write(
if let Err(err) = Self::master_write(
address,
write.len().min(255),
Stop::Software,
last_chunk_idx != 0,
&check_timeout,
)?;
) {
if send_stop {
self.master_stop();
}
return Err(err);
}
for (number, chunk) in write.chunks(255).enumerate() {
if number != 0 {
@ -399,18 +404,22 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
// Wait until we are allowed to send data
// (START has been ACKed or last byte when
// through)
self.wait_txe(&check_timeout)?;
if let Err(err) = self.wait_txe(&check_timeout) {
if send_stop {
self.master_stop();
}
return Err(err);
}
T::regs().txdr().write(|w| w.set_txdata(*byte));
}
}
// Wait until the write finishes
self.wait_tc(&check_timeout)?;
let result = self.wait_tc(&check_timeout);
if send_stop {
self.master_stop();
}
Ok(())
result
}
async fn write_dma_internal(
@ -707,13 +716,16 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
let first_length = write[0].len();
let last_slice_index = write.len() - 1;
Self::master_write(
if let Err(err) = Self::master_write(
address,
first_length.min(255),
Stop::Software,
(first_length > 255) || (last_slice_index != 0),
&check_timeout,
)?;
) {
self.master_stop();
return Err(err);
}
for (idx, slice) in write.iter().enumerate() {
let slice_len = slice.len();
@ -726,27 +738,36 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
let last_chunk_idx = total_chunks.saturating_sub(1);
if idx != 0 {
Self::master_continue(
if let Err(err) = Self::master_continue(
slice_len.min(255),
(idx != last_slice_index) || (slice_len > 255),
&check_timeout,
)?;
) {
self.master_stop();
return Err(err);
}
}
for (number, chunk) in slice.chunks(255).enumerate() {
if number != 0 {
Self::master_continue(
if let Err(err) = Self::master_continue(
chunk.len(),
(number != last_chunk_idx) || (idx != last_slice_index),
&check_timeout,
)?;
) {
self.master_stop();
return Err(err);
}
}
for byte in chunk {
// Wait until we are allowed to send data
// (START has been ACKed or last byte when
// through)
self.wait_txe(&check_timeout)?;
if let Err(err) = self.wait_txe(&check_timeout) {
self.master_stop();
return Err(err);
}
// Put byte on the wire
//self.i2c.txdr.write(|w| w.txdata().bits(*byte));
@ -755,10 +776,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
}
}
// Wait until the write finishes
self.wait_tc(&check_timeout)?;
let result = self.wait_tc(&check_timeout);
self.master_stop();
Ok(())
result
}
pub fn blocking_write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> {

View File

@ -1,8 +1,7 @@
use core::future::poll_fn;
use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll;
use atomic_polyfill::{compiler_fence, Ordering};
use self::sealed::Instance;
use crate::interrupt;
use crate::interrupt::typelevel::Interrupt;

View File

@ -1,332 +1,332 @@
#![macro_use]
pub mod enums;
use embassy_hal_common::{into_ref, PeripheralRef};
use enums::*;
use crate::dma::Transfer;
use crate::gpio::sealed::AFType;
use crate::gpio::AnyPin;
use crate::pac::quadspi::Quadspi as Regs;
use crate::rcc::RccPeripheral;
use crate::{peripherals, Peripheral};
pub struct TransferConfig {
/// Instraction width (IMODE)
pub iwidth: QspiWidth,
/// Address width (ADMODE)
pub awidth: QspiWidth,
/// Data width (DMODE)
pub dwidth: QspiWidth,
/// Instruction Id
pub instruction: u8,
/// Flash memory address
pub address: Option<u32>,
/// Number of dummy cycles (DCYC)
pub dummy: DummyCycles,
/// Length of data
pub data_len: Option<usize>,
}
impl Default for TransferConfig {
fn default() -> Self {
Self {
iwidth: QspiWidth::NONE,
awidth: QspiWidth::NONE,
dwidth: QspiWidth::NONE,
instruction: 0,
address: None,
dummy: DummyCycles::_0,
data_len: None,
}
}
}
pub struct Config {
/// Flash memory size representend as 2^[0-32], as reasonable minimum 1KiB(9) was chosen.
/// If you need other value the whose predefined use `Other` variant.
pub memory_size: MemorySize,
/// Address size (8/16/24/32-bit)
pub address_size: AddressSize,
/// Scalar factor for generating CLK [0-255]
pub prescaler: u8,
/// Number of bytes to trigger FIFO threshold flag.
pub fifo_threshold: FIFOThresholdLevel,
/// Minimum number of cycles that chip select must be high between issued commands
pub cs_high_time: ChipSelectHightTime,
}
impl Default for Config {
fn default() -> Self {
Self {
memory_size: MemorySize::Other(0),
address_size: AddressSize::_24bit,
prescaler: 128,
fifo_threshold: FIFOThresholdLevel::_17Bytes,
cs_high_time: ChipSelectHightTime::_5Cycle,
}
}
}
#[allow(dead_code)]
pub struct Qspi<'d, T: Instance, Dma> {
_peri: PeripheralRef<'d, T>,
sck: Option<PeripheralRef<'d, AnyPin>>,
d0: Option<PeripheralRef<'d, AnyPin>>,
d1: Option<PeripheralRef<'d, AnyPin>>,
d2: Option<PeripheralRef<'d, AnyPin>>,
d3: Option<PeripheralRef<'d, AnyPin>>,
nss: Option<PeripheralRef<'d, AnyPin>>,
dma: PeripheralRef<'d, Dma>,
config: Config,
}
impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
pub fn new(
peri: impl Peripheral<P = T> + 'd,
d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
dma: impl Peripheral<P = Dma> + 'd,
config: Config,
) -> Self {
into_ref!(peri, d0, d1, d2, d3, sck, nss);
sck.set_as_af(sck.af_num(), AFType::OutputPushPull);
sck.set_speed(crate::gpio::Speed::VeryHigh);
nss.set_as_af(nss.af_num(), AFType::OutputPushPull);
nss.set_speed(crate::gpio::Speed::VeryHigh);
d0.set_as_af(d0.af_num(), AFType::OutputPushPull);
d0.set_speed(crate::gpio::Speed::VeryHigh);
d1.set_as_af(d1.af_num(), AFType::OutputPushPull);
d1.set_speed(crate::gpio::Speed::VeryHigh);
d2.set_as_af(d2.af_num(), AFType::OutputPushPull);
d2.set_speed(crate::gpio::Speed::VeryHigh);
d3.set_as_af(d3.af_num(), AFType::OutputPushPull);
d3.set_speed(crate::gpio::Speed::VeryHigh);
Self::new_inner(
peri,
Some(d0.map_into()),
Some(d1.map_into()),
Some(d2.map_into()),
Some(d3.map_into()),
Some(sck.map_into()),
Some(nss.map_into()),
dma,
config,
)
}
fn new_inner(
peri: impl Peripheral<P = T> + 'd,
d0: Option<PeripheralRef<'d, AnyPin>>,
d1: Option<PeripheralRef<'d, AnyPin>>,
d2: Option<PeripheralRef<'d, AnyPin>>,
d3: Option<PeripheralRef<'d, AnyPin>>,
sck: Option<PeripheralRef<'d, AnyPin>>,
nss: Option<PeripheralRef<'d, AnyPin>>,
dma: impl Peripheral<P = Dma> + 'd,
config: Config,
) -> Self {
into_ref!(peri, dma);
T::enable();
T::REGS.cr().write(|w| w.set_fthres(config.fifo_threshold.into()));
while T::REGS.sr().read().busy() {}
T::REGS.cr().write(|w| {
w.set_prescaler(config.prescaler);
w.set_en(true);
});
T::REGS.dcr().write(|w| {
w.set_fsize(config.memory_size.into());
w.set_csht(config.cs_high_time.into());
w.set_ckmode(false);
});
Self {
_peri: peri,
sck,
d0,
d1,
d2,
d3,
nss,
dma,
config,
}
}
pub fn command(&mut self, transaction: TransferConfig) {
T::REGS.cr().modify(|v| v.set_dmaen(false));
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
while !T::REGS.sr().read().tcf() {}
T::REGS.fcr().modify(|v| v.set_ctcf(true));
}
pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) {
T::REGS.cr().modify(|v| v.set_dmaen(false));
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
if let Some(len) = transaction.data_len {
let current_ar = T::REGS.ar().read().address();
T::REGS.ccr().modify(|v| {
v.set_fmode(QspiMode::IndirectRead.into());
});
T::REGS.ar().write(|v| {
v.set_address(current_ar);
});
for idx in 0..len {
while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {}
buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() };
}
}
while !T::REGS.sr().read().tcf() {}
T::REGS.fcr().modify(|v| v.set_ctcf(true));
}
pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) {
T::REGS.cr().modify(|v| v.set_dmaen(false));
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
if let Some(len) = transaction.data_len {
T::REGS.ccr().modify(|v| {
v.set_fmode(QspiMode::IndirectWrite.into());
});
for idx in 0..len {
while !T::REGS.sr().read().ftf() {}
unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(buf[idx]) };
}
}
while !T::REGS.sr().read().tcf() {}
T::REGS.fcr().modify(|v| v.set_ctcf(true));
}
pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig)
where
Dma: QuadDma<T>,
{
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
T::REGS.ccr().modify(|v| {
v.set_fmode(QspiMode::IndirectRead.into());
});
let current_ar = T::REGS.ar().read().address();
T::REGS.ar().write(|v| {
v.set_address(current_ar);
});
let request = self.dma.request();
let transfer = unsafe {
Transfer::new_read(
&mut self.dma,
request,
T::REGS.dr().as_ptr() as *mut u8,
buf,
Default::default(),
)
};
T::REGS.cr().modify(|v| v.set_dmaen(true));
transfer.blocking_wait();
}
pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig)
where
Dma: QuadDma<T>,
{
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
T::REGS.ccr().modify(|v| {
v.set_fmode(QspiMode::IndirectWrite.into());
});
let request = self.dma.request();
let transfer = unsafe {
Transfer::new_write(
&mut self.dma,
request,
buf,
T::REGS.dr().as_ptr() as *mut u8,
Default::default(),
)
};
T::REGS.cr().modify(|v| v.set_dmaen(true));
transfer.blocking_wait();
}
fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig) {
T::REGS.fcr().modify(|v| {
v.set_csmf(true);
v.set_ctcf(true);
v.set_ctef(true);
v.set_ctof(true);
});
while T::REGS.sr().read().busy() {}
if let Some(len) = transaction.data_len {
T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1));
}
T::REGS.ccr().write(|v| {
v.set_fmode(fmode.into());
v.set_imode(transaction.iwidth.into());
v.set_instruction(transaction.instruction);
v.set_admode(transaction.awidth.into());
v.set_adsize(self.config.address_size.into());
v.set_dmode(transaction.dwidth.into());
v.set_abmode(QspiWidth::NONE.into());
v.set_dcyc(transaction.dummy.into());
});
if let Some(addr) = transaction.address {
T::REGS.ar().write(|v| {
v.set_address(addr);
});
}
}
}
pub(crate) mod sealed {
use super::*;
pub trait Instance {
const REGS: Regs;
}
}
pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {}
pin_trait!(SckPin, Instance);
pin_trait!(D0Pin, Instance);
pin_trait!(D1Pin, Instance);
pin_trait!(D2Pin, Instance);
pin_trait!(D3Pin, Instance);
pin_trait!(NSSPin, Instance);
dma_trait!(QuadDma, Instance);
foreach_peripheral!(
(quadspi, $inst:ident) => {
impl sealed::Instance for peripherals::$inst {
const REGS: Regs = crate::pac::$inst;
}
impl Instance for peripherals::$inst {}
};
);
#![macro_use]
pub mod enums;
use embassy_hal_common::{into_ref, PeripheralRef};
use enums::*;
use crate::dma::Transfer;
use crate::gpio::sealed::AFType;
use crate::gpio::AnyPin;
use crate::pac::quadspi::Quadspi as Regs;
use crate::rcc::RccPeripheral;
use crate::{peripherals, Peripheral};
pub struct TransferConfig {
/// Instraction width (IMODE)
pub iwidth: QspiWidth,
/// Address width (ADMODE)
pub awidth: QspiWidth,
/// Data width (DMODE)
pub dwidth: QspiWidth,
/// Instruction Id
pub instruction: u8,
/// Flash memory address
pub address: Option<u32>,
/// Number of dummy cycles (DCYC)
pub dummy: DummyCycles,
/// Length of data
pub data_len: Option<usize>,
}
impl Default for TransferConfig {
fn default() -> Self {
Self {
iwidth: QspiWidth::NONE,
awidth: QspiWidth::NONE,
dwidth: QspiWidth::NONE,
instruction: 0,
address: None,
dummy: DummyCycles::_0,
data_len: None,
}
}
}
pub struct Config {
/// Flash memory size representend as 2^[0-32], as reasonable minimum 1KiB(9) was chosen.
/// If you need other value the whose predefined use `Other` variant.
pub memory_size: MemorySize,
/// Address size (8/16/24/32-bit)
pub address_size: AddressSize,
/// Scalar factor for generating CLK [0-255]
pub prescaler: u8,
/// Number of bytes to trigger FIFO threshold flag.
pub fifo_threshold: FIFOThresholdLevel,
/// Minimum number of cycles that chip select must be high between issued commands
pub cs_high_time: ChipSelectHightTime,
}
impl Default for Config {
fn default() -> Self {
Self {
memory_size: MemorySize::Other(0),
address_size: AddressSize::_24bit,
prescaler: 128,
fifo_threshold: FIFOThresholdLevel::_17Bytes,
cs_high_time: ChipSelectHightTime::_5Cycle,
}
}
}
#[allow(dead_code)]
pub struct Qspi<'d, T: Instance, Dma> {
_peri: PeripheralRef<'d, T>,
sck: Option<PeripheralRef<'d, AnyPin>>,
d0: Option<PeripheralRef<'d, AnyPin>>,
d1: Option<PeripheralRef<'d, AnyPin>>,
d2: Option<PeripheralRef<'d, AnyPin>>,
d3: Option<PeripheralRef<'d, AnyPin>>,
nss: Option<PeripheralRef<'d, AnyPin>>,
dma: PeripheralRef<'d, Dma>,
config: Config,
}
impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
pub fn new(
peri: impl Peripheral<P = T> + 'd,
d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
dma: impl Peripheral<P = Dma> + 'd,
config: Config,
) -> Self {
into_ref!(peri, d0, d1, d2, d3, sck, nss);
sck.set_as_af(sck.af_num(), AFType::OutputPushPull);
sck.set_speed(crate::gpio::Speed::VeryHigh);
nss.set_as_af(nss.af_num(), AFType::OutputPushPull);
nss.set_speed(crate::gpio::Speed::VeryHigh);
d0.set_as_af(d0.af_num(), AFType::OutputPushPull);
d0.set_speed(crate::gpio::Speed::VeryHigh);
d1.set_as_af(d1.af_num(), AFType::OutputPushPull);
d1.set_speed(crate::gpio::Speed::VeryHigh);
d2.set_as_af(d2.af_num(), AFType::OutputPushPull);
d2.set_speed(crate::gpio::Speed::VeryHigh);
d3.set_as_af(d3.af_num(), AFType::OutputPushPull);
d3.set_speed(crate::gpio::Speed::VeryHigh);
Self::new_inner(
peri,
Some(d0.map_into()),
Some(d1.map_into()),
Some(d2.map_into()),
Some(d3.map_into()),
Some(sck.map_into()),
Some(nss.map_into()),
dma,
config,
)
}
fn new_inner(
peri: impl Peripheral<P = T> + 'd,
d0: Option<PeripheralRef<'d, AnyPin>>,
d1: Option<PeripheralRef<'d, AnyPin>>,
d2: Option<PeripheralRef<'d, AnyPin>>,
d3: Option<PeripheralRef<'d, AnyPin>>,
sck: Option<PeripheralRef<'d, AnyPin>>,
nss: Option<PeripheralRef<'d, AnyPin>>,
dma: impl Peripheral<P = Dma> + 'd,
config: Config,
) -> Self {
into_ref!(peri, dma);
T::enable();
T::REGS.cr().write(|w| w.set_fthres(config.fifo_threshold.into()));
while T::REGS.sr().read().busy() {}
T::REGS.cr().write(|w| {
w.set_prescaler(config.prescaler);
w.set_en(true);
});
T::REGS.dcr().write(|w| {
w.set_fsize(config.memory_size.into());
w.set_csht(config.cs_high_time.into());
w.set_ckmode(false);
});
Self {
_peri: peri,
sck,
d0,
d1,
d2,
d3,
nss,
dma,
config,
}
}
pub fn command(&mut self, transaction: TransferConfig) {
T::REGS.cr().modify(|v| v.set_dmaen(false));
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
while !T::REGS.sr().read().tcf() {}
T::REGS.fcr().modify(|v| v.set_ctcf(true));
}
pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) {
T::REGS.cr().modify(|v| v.set_dmaen(false));
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
if let Some(len) = transaction.data_len {
let current_ar = T::REGS.ar().read().address();
T::REGS.ccr().modify(|v| {
v.set_fmode(QspiMode::IndirectRead.into());
});
T::REGS.ar().write(|v| {
v.set_address(current_ar);
});
for idx in 0..len {
while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {}
buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() };
}
}
while !T::REGS.sr().read().tcf() {}
T::REGS.fcr().modify(|v| v.set_ctcf(true));
}
pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) {
T::REGS.cr().modify(|v| v.set_dmaen(false));
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
if let Some(len) = transaction.data_len {
T::REGS.ccr().modify(|v| {
v.set_fmode(QspiMode::IndirectWrite.into());
});
for idx in 0..len {
while !T::REGS.sr().read().ftf() {}
unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(buf[idx]) };
}
}
while !T::REGS.sr().read().tcf() {}
T::REGS.fcr().modify(|v| v.set_ctcf(true));
}
pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig)
where
Dma: QuadDma<T>,
{
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
T::REGS.ccr().modify(|v| {
v.set_fmode(QspiMode::IndirectRead.into());
});
let current_ar = T::REGS.ar().read().address();
T::REGS.ar().write(|v| {
v.set_address(current_ar);
});
let request = self.dma.request();
let transfer = unsafe {
Transfer::new_read(
&mut self.dma,
request,
T::REGS.dr().as_ptr() as *mut u8,
buf,
Default::default(),
)
};
T::REGS.cr().modify(|v| v.set_dmaen(true));
transfer.blocking_wait();
}
pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig)
where
Dma: QuadDma<T>,
{
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
T::REGS.ccr().modify(|v| {
v.set_fmode(QspiMode::IndirectWrite.into());
});
let request = self.dma.request();
let transfer = unsafe {
Transfer::new_write(
&mut self.dma,
request,
buf,
T::REGS.dr().as_ptr() as *mut u8,
Default::default(),
)
};
T::REGS.cr().modify(|v| v.set_dmaen(true));
transfer.blocking_wait();
}
fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig) {
T::REGS.fcr().modify(|v| {
v.set_csmf(true);
v.set_ctcf(true);
v.set_ctef(true);
v.set_ctof(true);
});
while T::REGS.sr().read().busy() {}
if let Some(len) = transaction.data_len {
T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1));
}
T::REGS.ccr().write(|v| {
v.set_fmode(fmode.into());
v.set_imode(transaction.iwidth.into());
v.set_instruction(transaction.instruction);
v.set_admode(transaction.awidth.into());
v.set_adsize(self.config.address_size.into());
v.set_dmode(transaction.dwidth.into());
v.set_abmode(QspiWidth::NONE.into());
v.set_dcyc(transaction.dummy.into());
});
if let Some(addr) = transaction.address {
T::REGS.ar().write(|v| {
v.set_address(addr);
});
}
}
}
pub(crate) mod sealed {
use super::*;
pub trait Instance {
const REGS: Regs;
}
}
pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {}
pin_trait!(SckPin, Instance);
pin_trait!(D0Pin, Instance);
pin_trait!(D1Pin, Instance);
pin_trait!(D2Pin, Instance);
pin_trait!(D3Pin, Instance);
pin_trait!(NSSPin, Instance);
dma_trait!(QuadDma, Instance);
foreach_peripheral!(
(quadspi, $inst:ident) => {
impl sealed::Instance for peripherals::$inst {
const REGS: Regs = crate::pac::$inst;
}
impl Instance for peripherals::$inst {}
};
);

View File

@ -473,11 +473,11 @@ fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
w.set_divm(0);
});
return PllOutput{
return PllOutput {
p: None,
q: None,
r: None,
}
};
};
assert!(1 <= config.prediv && config.prediv <= 63);

View File

@ -740,7 +740,7 @@ mod pll {
}
};
let vco_ck = output + pll_x_p;
let vco_ck = output * pll_x_p;
assert!(pll_x_p < 128);
assert!(vco_ck >= VCO_MIN);

View File

@ -1,6 +1,7 @@
use core::marker::PhantomData;
use embassy_hal_common::into_ref;
use stm32_metapac::rcc::regs::Cfgr;
use stm32_metapac::rcc::vals::{Lsedrv, Mcopre, Mcosel};
use crate::gpio::sealed::AFType;
@ -439,6 +440,26 @@ impl<'d, T: McoInstance> Mco<'d, T> {
}
pub(crate) unsafe fn init(config: Config) {
// Switch to MSI to prevent problems with PLL configuration.
if !RCC.cr().read().msion() {
// Turn on MSI and configure it to 4MHz.
RCC.cr().modify(|w| {
w.set_msirgsel(true); // MSI Range is provided by MSIRANGE[3:0].
w.set_msirange(MSIRange::default().into());
w.set_msipllen(false);
w.set_msion(true)
});
// Wait until MSI is running
while !RCC.cr().read().msirdy() {}
}
if RCC.cfgr().read().sws() != Sw::MSI {
// Set MSI as a clock source, reset prescalers.
RCC.cfgr().write_value(Cfgr::default());
// Wait for clock switch status bits to change.
while RCC.cfgr().read().sws() != Sw::MSI {}
}
match config.rtc_mux {
RtcClockSource::LSE32 => {
// 1. Unlock the backup domain
@ -660,6 +681,8 @@ pub(crate) unsafe fn init(config: Config) {
}
};
RCC.apb1enr1().modify(|w| w.set_pwren(true));
set_freqs(Clocks {
sys: Hertz(sys_clk),
ahb1: Hertz(ahb_freq),

View File

@ -83,12 +83,12 @@ static mut CLOCK_FREQS: MaybeUninit<Clocks> = MaybeUninit::uninit();
/// Safety: Sets a mutable global.
pub(crate) unsafe fn set_freqs(freqs: Clocks) {
debug!("rcc: {:?}", freqs);
CLOCK_FREQS.as_mut_ptr().write(freqs);
CLOCK_FREQS = MaybeUninit::new(freqs);
}
/// Safety: Reads a mutable global.
pub(crate) unsafe fn get_freqs() -> &'static Clocks {
&*CLOCK_FREQS.as_ptr()
CLOCK_FREQS.assume_init_ref()
}
#[cfg(feature = "unstable-pac")]

View File

@ -1,4 +1,5 @@
use crate::pac::{FLASH, RCC};
use crate::pac::pwr::vals::Dbp;
use crate::pac::{FLASH, PWR, RCC};
use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz;
@ -184,6 +185,8 @@ pub struct Config {
pub apb1_pre: APBPrescaler,
pub apb2_pre: APBPrescaler,
pub enable_lsi: bool,
pub enable_rtc_apb: bool,
pub rtc_mux: RtcClockSource,
}
impl Default for Config {
@ -196,10 +199,25 @@ impl Default for Config {
apb1_pre: APBPrescaler::NotDivided,
apb2_pre: APBPrescaler::NotDivided,
enable_lsi: false,
enable_rtc_apb: false,
rtc_mux: RtcClockSource::LSI32,
}
}
}
pub enum RtcClockSource {
LSE32,
LSI32,
}
#[repr(u8)]
pub enum Lsedrv {
Low = 0,
MediumLow = 1,
MediumHigh = 2,
High = 3,
}
pub(crate) unsafe fn init(config: Config) {
let (sys_clk, sw, vos) = match config.mux {
ClockSrc::HSI16 => (HSI_FREQ.0, 0x01, VoltageScale::Range2),
@ -266,6 +284,32 @@ pub(crate) unsafe fn init(config: Config) {
while FLASH.acr().read().latency() != ws {}
match config.rtc_mux {
RtcClockSource::LSE32 => {
// 1. Unlock the backup domain
PWR.cr1().modify(|w| w.set_dbp(Dbp::ENABLED));
// 2. Setup the LSE
RCC.bdcr().modify(|w| {
// Enable LSE
w.set_lseon(true);
// Max drive strength
// TODO: should probably be settable
w.set_lsedrv(Lsedrv::High as u8); //---// PAM - should not be commented
});
// Wait until LSE is running
while !RCC.bdcr().read().lserdy() {}
}
RtcClockSource::LSI32 => {
// Turn on the internal 32 kHz LSI oscillator
RCC.csr().modify(|w| w.set_lsion(true));
// Wait until LSI is running
while !RCC.csr().read().lsirdy() {}
}
}
match config.mux {
ClockSrc::HSI16 => {
// Enable HSI16
@ -287,11 +331,26 @@ pub(crate) unsafe fn init(config: Config) {
w.set_msirgsel(true);
w.set_msirange(range.into());
w.set_msion(true);
if let RtcClockSource::LSE32 = config.rtc_mux {
// If LSE is enabled, enable calibration of MSI
w.set_msipllen(true);
} else {
w.set_msipllen(false);
}
});
while !RCC.cr().read().msirdy() {}
}
}
if config.enable_rtc_apb {
// enable peripheral clock for communication
crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true));
// read to allow the pwr clock to enable
crate::pac::PWR.cr1().read();
}
RCC.extcfgr().modify(|w| {
if config.shd_ahb_pre == AHBPrescaler::NotDivided {
w.set_shdhpre(0);

View File

@ -172,6 +172,7 @@ impl sealed::Instance for crate::peripherals::RTC {
const BACKUP_REGISTER_COUNT: usize = 32;
fn read_backup_register(_rtc: &Rtc, register: usize) -> Option<u32> {
#[allow(clippy::if_same_then_else)]
if register < Self::BACKUP_REGISTER_COUNT {
//Some(rtc.bkpr()[register].read().bits())
None // RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC

View File

@ -852,25 +852,19 @@ mod eh1 {
type Error = Error;
}
impl<'d, T: Instance, Tx, Rx> embedded_hal_1::spi::SpiBusFlush for Spi<'d, T, Tx, Rx> {
impl<'d, T: Instance, W: Word, Tx, Rx> embedded_hal_1::spi::SpiBus<W> for Spi<'d, T, Tx, Rx> {
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl<'d, T: Instance, W: Word, Tx, Rx> embedded_hal_1::spi::SpiBusRead<W> for Spi<'d, T, Tx, Rx> {
fn read(&mut self, words: &mut [W]) -> Result<(), Self::Error> {
self.blocking_read(words)
}
}
impl<'d, T: Instance, W: Word, Tx, Rx> embedded_hal_1::spi::SpiBusWrite<W> for Spi<'d, T, Tx, Rx> {
fn write(&mut self, words: &[W]) -> Result<(), Self::Error> {
self.blocking_write(words)
}
}
impl<'d, T: Instance, W: Word, Tx, Rx> embedded_hal_1::spi::SpiBus<W> for Spi<'d, T, Tx, Rx> {
fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Self::Error> {
self.blocking_transfer(read, write)
}
@ -895,32 +889,25 @@ mod eh1 {
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
mod eha {
use super::*;
impl<'d, T: Instance, Tx, Rx> embedded_hal_async::spi::SpiBusFlush for Spi<'d, T, Tx, Rx> {
impl<'d, T: Instance, Tx: TxDma<T>, Rx: RxDma<T>, W: Word> embedded_hal_async::spi::SpiBus<W> for Spi<'d, T, Tx, Rx> {
async fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl<'d, T: Instance, Tx: TxDma<T>, Rx, W: Word> embedded_hal_async::spi::SpiBusWrite<W> for Spi<'d, T, Tx, Rx> {
async fn write(&mut self, words: &[W]) -> Result<(), Self::Error> {
self.write(words).await
}
}
impl<'d, T: Instance, Tx: TxDma<T>, Rx: RxDma<T>, W: Word> embedded_hal_async::spi::SpiBusRead<W>
for Spi<'d, T, Tx, Rx>
{
async fn read(&mut self, words: &mut [W]) -> Result<(), Self::Error> {
self.read(words).await
}
}
impl<'d, T: Instance, Tx: TxDma<T>, Rx: RxDma<T>, W: Word> embedded_hal_async::spi::SpiBus<W> for Spi<'d, T, Tx, Rx> {
async fn transfer<'a>(&'a mut self, read: &'a mut [W], write: &'a [W]) -> Result<(), Self::Error> {
async fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Self::Error> {
self.transfer(read, write).await
}
async fn transfer_in_place<'a>(&'a mut self, words: &'a mut [W]) -> Result<(), Self::Error> {
async fn transfer_in_place(&mut self, words: &mut [W]) -> Result<(), Self::Error> {
self.transfer_in_place(words).await
}
}

Some files were not shown because too many files have changed in this diff Show More