Merge branch 'main' of https://github.com/embassy-rs/embassy into hrtim
This commit is contained in:
.gitattributesrust-toolchain.toml
.github/ci
ci.shcyw43
embassy-embedded-hal
embassy-executor
embassy-hal-common/src
embassy-lora
embassy-net-driver/src
embassy-net-esp-hosted
embassy-net-w5500
embassy-net
embassy-nrf
embassy-rp
Cargo.toml
src
embassy-stm32-wpan
Cargo.toml
src
embassy-stm32
embassy-sync/src
embassy-time
examples
boot
application
nrf
rp
stm32f3
stm32f7
stm32h7
stm32l0
stm32l1
stm32l4
stm32wl
nrf-rtos-trace
nrf52840-rtic
nrf52840
nrf5340
rp
Cargo.toml
src
bin
adc.rsblinky.rsbutton.rsethernet_w5500_multisocket.rsethernet_w5500_tcp_client.rsethernet_w5500_tcp_server.rsethernet_w5500_udp.rsflash.rsgpio_async.rsgpout.rsi2c_async.rsi2c_blocking.rslora_lorawan.rslora_p2p_receive.rslora_p2p_send.rslora_p2p_send_multicore.rsmulticore.rspio_async.rspio_dma.rspio_hd44780.rspio_ws2812.rspwm.rsrtc.rsspi.rsspi_async.rsspi_display.rsuart.rsuart_buffered_split.rsuart_unidir.rsusb_ethernet.rsusb_hid_keyboard.rsusb_logger.rsusb_serial.rswatchdog.rswifi_ap_tcp_server.rswifi_blinky.rswifi_scan.rswifi_tcp_server.rs
std
stm32c0
stm32f0
stm32f1
stm32f2
stm32f3
stm32f4
stm32f7
stm32g0
stm32g4
stm32h5
stm32h7
stm32l0
stm32l1
stm32l4
stm32l5
stm32u5
stm32wb
stm32wl
wasm
tests
nrf
riscv32
rp
stm32
41
.gitattributes
vendored
Normal file
41
.gitattributes
vendored
Normal 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
17
.github/ci/crlf.sh
vendored
Executable 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
52
.github/ci/doc.sh
vendored
@ -6,7 +6,7 @@ set -euo pipefail
|
|||||||
export RUSTUP_HOME=/ci/cache/rustup
|
export RUSTUP_HOME=/ci/cache/rustup
|
||||||
export CARGO_HOME=/ci/cache/cargo
|
export CARGO_HOME=/ci/cache/cargo
|
||||||
export CARGO_TARGET_DIR=/ci/cache/target
|
export CARGO_TARGET_DIR=/ci/cache/target
|
||||||
export BUILDER_THREADS=6
|
export BUILDER_THREADS=4
|
||||||
export BUILDER_COMPRESS=true
|
export BUILDER_COMPRESS=true
|
||||||
|
|
||||||
# force rustup to download the toolchain before starting building.
|
# force rustup to download the toolchain before starting building.
|
||||||
@ -15,30 +15,32 @@ export BUILDER_COMPRESS=true
|
|||||||
# which makes rustup very sad
|
# which makes rustup very sad
|
||||||
rustc --version > /dev/null
|
rustc --version > /dev/null
|
||||||
|
|
||||||
docserver-builder -i ./embassy-stm32 -o crates/embassy-stm32/git.zup
|
docserver-builder -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup
|
||||||
docserver-builder -i ./embassy-boot/boot -o crates/embassy-boot/git.zup
|
docserver-builder -i ./embassy-boot/boot -o webroot/crates/embassy-boot/git.zup
|
||||||
docserver-builder -i ./embassy-boot/nrf -o crates/embassy-boot-nrf/git.zup
|
docserver-builder -i ./embassy-boot/nrf -o webroot/crates/embassy-boot-nrf/git.zup
|
||||||
docserver-builder -i ./embassy-boot/rp -o crates/embassy-boot-rp/git.zup
|
docserver-builder -i ./embassy-boot/rp -o webroot/crates/embassy-boot-rp/git.zup
|
||||||
docserver-builder -i ./embassy-boot/stm32 -o crates/embassy-boot-stm32/git.zup
|
docserver-builder -i ./embassy-boot/stm32 -o webroot/crates/embassy-boot-stm32/git.zup
|
||||||
docserver-builder -i ./embassy-embedded-hal -o crates/embassy-embedded-hal/git.zup
|
docserver-builder -i ./embassy-embedded-hal -o webroot/crates/embassy-embedded-hal/git.zup
|
||||||
docserver-builder -i ./embassy-executor -o crates/embassy-executor/git.zup
|
docserver-builder -i ./embassy-executor -o webroot/crates/embassy-executor/git.zup
|
||||||
docserver-builder -i ./embassy-futures -o crates/embassy-futures/git.zup
|
docserver-builder -i ./embassy-futures -o webroot/crates/embassy-futures/git.zup
|
||||||
docserver-builder -i ./embassy-lora -o crates/embassy-lora/git.zup
|
docserver-builder -i ./embassy-lora -o webroot/crates/embassy-lora/git.zup
|
||||||
docserver-builder -i ./embassy-net -o crates/embassy-net/git.zup
|
docserver-builder -i ./embassy-net -o webroot/crates/embassy-net/git.zup
|
||||||
docserver-builder -i ./embassy-net-driver -o crates/embassy-net-driver/git.zup
|
docserver-builder -i ./embassy-net-driver -o webroot/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-net-driver-channel -o webroot/crates/embassy-net-driver-channel/git.zup
|
||||||
docserver-builder -i ./embassy-nrf -o crates/embassy-nrf/git.zup
|
docserver-builder -i ./embassy-nrf -o webroot/crates/embassy-nrf/git.zup
|
||||||
docserver-builder -i ./embassy-rp -o crates/embassy-rp/git.zup
|
docserver-builder -i ./embassy-rp -o webroot/crates/embassy-rp/git.zup
|
||||||
docserver-builder -i ./embassy-sync -o crates/embassy-sync/git.zup
|
docserver-builder -i ./embassy-sync -o webroot/crates/embassy-sync/git.zup
|
||||||
docserver-builder -i ./embassy-time -o crates/embassy-time/git.zup
|
docserver-builder -i ./embassy-time -o webroot/crates/embassy-time/git.zup
|
||||||
docserver-builder -i ./embassy-usb -o crates/embassy-usb/git.zup
|
docserver-builder -i ./embassy-usb -o webroot/crates/embassy-usb/git.zup
|
||||||
docserver-builder -i ./embassy-usb-driver -o crates/embassy-usb-driver/git.zup
|
docserver-builder -i ./embassy-usb-driver -o webroot/crates/embassy-usb-driver/git.zup
|
||||||
docserver-builder -i ./embassy-usb-logger -o crates/embassy-usb-logger/git.zup
|
docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/git.zup
|
||||||
docserver-builder -i ./cyw43 -o crates/cyw43/git.zup
|
docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup
|
||||||
docserver-builder -i ./cyw43-pio -o crates/cyw43-pio/git.zup
|
docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup
|
||||||
docserver-builder -i ./embassy-net-w5500 -o crates/embassy-net-w5500/git.zup
|
docserver-builder -i ./embassy-net-w5500 -o webroot/crates/embassy-net-w5500/git.zup
|
||||||
docserver-builder -i ./embassy-stm32-wpan -o crates/embassy-stm32-wpan/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
|
export KUBECONFIG=/ci/secrets/kubeconfig.yml
|
||||||
POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name})
|
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
4
ci.sh
@ -5,10 +5,6 @@ set -euo pipefail
|
|||||||
export RUSTFLAGS=-Dwarnings
|
export RUSTFLAGS=-Dwarnings
|
||||||
export DEFMT_LOG=trace,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info
|
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')
|
TARGET=$(rustc -vV | sed -n 's|host: ||p')
|
||||||
|
|
||||||
BUILD_EXTRA=""
|
BUILD_EXTRA=""
|
||||||
|
@ -11,7 +11,7 @@ log = ["dep:log"]
|
|||||||
firmware-logs = []
|
firmware-logs = []
|
||||||
|
|
||||||
[dependencies]
|
[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-sync = { version = "0.2.0", path = "../embassy-sync"}
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
|
||||||
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
|
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"
|
cortex-m-rt = "0.7.0"
|
||||||
futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] }
|
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 }
|
num_enum = { version = "0.5.7", default-features = false }
|
||||||
|
|
||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
|
@ -30,7 +30,7 @@ TODO:
|
|||||||
### Example 2: Create an access point (IP and credentials in the code)
|
### Example 2: Create an access point (IP and credentials in the code)
|
||||||
- `cargo run --release --bin wifi_ap_tcp_server`
|
- `cargo run --release --bin wifi_ap_tcp_server`
|
||||||
### Example 3: Connect to an existing network and create a 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
|
After a few seconds, you should see that DHCP picks up an IP address like this
|
||||||
```
|
```
|
||||||
|
@ -345,7 +345,9 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn rx(&mut self, packet: &mut [u8]) {
|
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);
|
self.update_credit(&sdpcm_header);
|
||||||
|
|
||||||
@ -353,7 +355,9 @@ where
|
|||||||
|
|
||||||
match channel {
|
match channel {
|
||||||
CHANNEL_TYPE_CONTROL => {
|
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);
|
trace!(" {:?}", cdc_header);
|
||||||
|
|
||||||
if cdc_header.id == self.ioctl_id {
|
if cdc_header.id == self.ioctl_id {
|
||||||
@ -417,8 +421,12 @@ where
|
|||||||
let status = event_packet.msg.status;
|
let status = event_packet.msg.status;
|
||||||
let event_payload = match evt_type {
|
let event_payload = match evt_type {
|
||||||
Event::ESCAN_RESULT if status == EStatus::PARTIAL => {
|
Event::ESCAN_RESULT if status == EStatus::PARTIAL => {
|
||||||
let Some((_, bss_info)) = ScanResults::parse(evt_data) else { return };
|
let Some((_, bss_info)) = ScanResults::parse(evt_data) else {
|
||||||
let Some(bss_info) = BssInfo::parse(bss_info) else { return };
|
return;
|
||||||
|
};
|
||||||
|
let Some(bss_info) = BssInfo::parse(bss_info) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
events::Payload::BssInfo(*bss_info)
|
events::Payload::BssInfo(*bss_info)
|
||||||
}
|
}
|
||||||
Event::ESCAN_RESULT => events::Payload::None,
|
Event::ESCAN_RESULT => events::Payload::None,
|
||||||
@ -439,7 +447,9 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
CHANNEL_TYPE_DATA => {
|
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)]));
|
trace!("rx pkt {:02x}", Bytes(&packet[..packet.len().min(48)]));
|
||||||
|
|
||||||
match self.ch.try_rx_buf() {
|
match self.ch.try_rx_buf() {
|
||||||
|
@ -15,15 +15,18 @@ target = "x86_64-unknown-linux-gnu"
|
|||||||
std = []
|
std = []
|
||||||
# Enable nightly-only features
|
# Enable nightly-only features
|
||||||
nightly = ["embassy-futures", "embedded-hal-async", "embedded-storage-async"]
|
nightly = ["embassy-futures", "embedded-hal-async", "embedded-storage-async"]
|
||||||
|
time = ["dep:embassy-time"]
|
||||||
|
default = ["time"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures", optional = true }
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures", optional = true }
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
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 = [
|
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [
|
||||||
"unproven",
|
"unproven",
|
||||||
] }
|
] }
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" }
|
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" }
|
||||||
embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true }
|
embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true }
|
||||||
embedded-storage = "0.3.0"
|
embedded-storage = "0.3.0"
|
||||||
embedded-storage-async = { version = "0.4.0", optional = true }
|
embedded-storage-async = { version = "0.4.0", optional = true }
|
||||||
nb = "1.0.0"
|
nb = "1.0.0"
|
||||||
|
@ -74,7 +74,21 @@ where
|
|||||||
E: embedded_hal_1::spi::Error + 'static,
|
E: embedded_hal_1::spi::Error + 'static,
|
||||||
T: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>,
|
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
|
// Ensure we write the expected bytes
|
||||||
for i in 0..core::cmp::min(read.len(), write.len()) {
|
for i in 0..core::cmp::min(read.len(), write.len()) {
|
||||||
read[i] = write[i].clone();
|
read[i] = write[i].clone();
|
||||||
@ -83,38 +97,7 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn transfer_in_place<'a>(&'a mut self, _: &'a mut [u8]) -> Result<(), Self::Error> {
|
async fn transfer_in_place(&mut self, data: &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> {
|
|
||||||
self.wrapped.transfer(data)?;
|
self.wrapped.transfer(data)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -69,54 +69,39 @@ where
|
|||||||
type Error = T::Error;
|
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
|
where
|
||||||
T: embedded_hal_async::spi::SpiBus,
|
T: embedded_hal_async::spi::SpiBus<Word>,
|
||||||
{
|
|
||||||
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,
|
|
||||||
{
|
{
|
||||||
async fn flush(&mut self) -> Result<(), Self::Error> {
|
async fn flush(&mut self) -> Result<(), Self::Error> {
|
||||||
self.wrapped.flush().await?;
|
self.wrapped.flush().await?;
|
||||||
yield_now().await;
|
yield_now().await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> embedded_hal_async::spi::SpiBusWrite<u8> for YieldingAsync<T>
|
async fn write(&mut self, data: &[Word]) -> Result<(), Self::Error> {
|
||||||
where
|
|
||||||
T: embedded_hal_async::spi::SpiBusWrite<u8>,
|
|
||||||
{
|
|
||||||
async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> {
|
|
||||||
self.wrapped.write(data).await?;
|
self.wrapped.write(data).await?;
|
||||||
yield_now().await;
|
yield_now().await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> embedded_hal_async::spi::SpiBusRead<u8> for YieldingAsync<T>
|
async fn read(&mut self, data: &mut [Word]) -> Result<(), Self::Error> {
|
||||||
where
|
|
||||||
T: embedded_hal_async::spi::SpiBusRead<u8>,
|
|
||||||
{
|
|
||||||
async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
|
|
||||||
self.wrapped.read(data).await?;
|
self.wrapped.read(data).await?;
|
||||||
yield_now().await;
|
yield_now().await;
|
||||||
Ok(())
|
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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -56,62 +56,6 @@ where
|
|||||||
type Error = SpiDeviceError<BUS::Error, CS::Error>;
|
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>
|
impl<M, BUS, CS> spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
|
||||||
where
|
where
|
||||||
M: RawMutex,
|
M: RawMutex,
|
||||||
@ -129,6 +73,12 @@ where
|
|||||||
Operation::Write(buf) => bus.write(buf).await?,
|
Operation::Write(buf) => bus.write(buf).await?,
|
||||||
Operation::Transfer(read, write) => bus.transfer(read, write).await?,
|
Operation::Transfer(read, write) => bus.transfer(read, write).await?,
|
||||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).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>;
|
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>
|
impl<M, BUS, CS> spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
|
||||||
where
|
where
|
||||||
M: RawMutex,
|
M: RawMutex,
|
||||||
@ -248,6 +140,12 @@ where
|
|||||||
Operation::Write(buf) => bus.write(buf).await?,
|
Operation::Write(buf) => bus.write(buf).await?,
|
||||||
Operation::Transfer(read, write) => bus.transfer(read, write).await?,
|
Operation::Transfer(read, write) => bus.transfer(read, write).await?,
|
||||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -22,7 +22,7 @@ use core::cell::RefCell;
|
|||||||
use embassy_sync::blocking_mutex::raw::RawMutex;
|
use embassy_sync::blocking_mutex::raw::RawMutex;
|
||||||
use embassy_sync::blocking_mutex::Mutex;
|
use embassy_sync::blocking_mutex::Mutex;
|
||||||
use embedded_hal_1::digital::OutputPin;
|
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::shared_bus::SpiDeviceError;
|
||||||
use crate::SetConfig;
|
use crate::SetConfig;
|
||||||
@ -48,58 +48,6 @@ where
|
|||||||
type Error = SpiDeviceError<BUS::Error, CS::Error>;
|
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>
|
impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
|
||||||
where
|
where
|
||||||
M: RawMutex,
|
M: RawMutex,
|
||||||
@ -116,6 +64,13 @@ where
|
|||||||
Operation::Write(buf) => bus.write(buf),
|
Operation::Write(buf) => bus.write(buf),
|
||||||
Operation::Transfer(read, write) => bus.transfer(read, write),
|
Operation::Transfer(read, write) => bus.transfer(read, write),
|
||||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
|
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.
|
// On failure, it's important to still flush and deassert CS.
|
||||||
@ -199,58 +154,6 @@ where
|
|||||||
type Error = SpiDeviceError<BUS::Error, CS::Error>;
|
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>
|
impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
|
||||||
where
|
where
|
||||||
M: RawMutex,
|
M: RawMutex,
|
||||||
@ -268,6 +171,13 @@ where
|
|||||||
Operation::Write(buf) => bus.write(buf),
|
Operation::Write(buf) => bus.write(buf),
|
||||||
Operation::Transfer(read, write) => bus.transfer(read, write),
|
Operation::Transfer(read, write) => bus.transfer(read, write),
|
||||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
|
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.
|
// On failure, it's important to still flush and deassert CS.
|
||||||
|
@ -30,11 +30,14 @@ where
|
|||||||
/// Error returned by SPI device implementations in this crate.
|
/// Error returned by SPI device implementations in this crate.
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum SpiDeviceError<BUS, CS> {
|
pub enum SpiDeviceError<BUS, CS> {
|
||||||
/// An operation on the inner SPI bus failed.
|
/// An operation on the inner SPI bus failed.
|
||||||
Spi(BUS),
|
Spi(BUS),
|
||||||
/// Setting the value of the Chip Select (CS) pin failed.
|
/// Setting the value of the Chip Select (CS) pin failed.
|
||||||
Cs(CS),
|
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>
|
impl<BUS, CS> spi::Error for SpiDeviceError<BUS, CS>
|
||||||
@ -46,6 +49,7 @@ where
|
|||||||
match self {
|
match self {
|
||||||
Self::Spi(e) => e.kind(),
|
Self::Spi(e) => e.kind(),
|
||||||
Self::Cs(_) => spi::ErrorKind::Other,
|
Self::Cs(_) => spi::ErrorKind::Other,
|
||||||
|
Self::DelayUsNotSupported => spi::ErrorKind::Other,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,8 +61,8 @@ log = { version = "0.4.14", optional = true }
|
|||||||
rtos-trace = { version = "0.1.2", optional = true }
|
rtos-trace = { version = "0.1.2", optional = true }
|
||||||
|
|
||||||
futures-util = { version = "0.3.17", default-features = false }
|
futures-util = { version = "0.3.17", default-features = false }
|
||||||
embassy-macros = { version = "0.2.0", path = "../embassy-macros" }
|
embassy-macros = { version = "0.2.0", path = "../embassy-macros" }
|
||||||
embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true}
|
embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true}
|
||||||
atomic-polyfill = "1.0.1"
|
atomic-polyfill = "1.0.1"
|
||||||
critical-section = "1.1"
|
critical-section = "1.1"
|
||||||
static_cell = "1.1"
|
static_cell = "1.1"
|
||||||
|
@ -165,6 +165,9 @@ impl<F: Future + 'static> TaskStorage<F> {
|
|||||||
Poll::Ready(_) => {
|
Poll::Ready(_) => {
|
||||||
this.future.drop_in_place();
|
this.future.drop_in_place();
|
||||||
this.raw.state.fetch_and(!STATE_SPAWNED, Ordering::AcqRel);
|
this.raw.state.fetch_and(!STATE_SPAWNED, Ordering::AcqRel);
|
||||||
|
|
||||||
|
#[cfg(feature = "integrated-timers")]
|
||||||
|
this.raw.expires_at.set(Instant::MAX);
|
||||||
}
|
}
|
||||||
Poll::Pending => {}
|
Poll::Pending => {}
|
||||||
}
|
}
|
||||||
|
@ -161,7 +161,7 @@ pub trait Peripheral: Sized {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'b, T: Deref> Peripheral for T
|
impl<'b, T: DerefMut> Peripheral for T
|
||||||
where
|
where
|
||||||
T::Target: Peripheral,
|
T::Target: Peripheral,
|
||||||
{
|
{
|
||||||
|
@ -20,11 +20,11 @@ defmt = ["dep:defmt", "lorawan-device/defmt"]
|
|||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
log = { version = "0.4.14", 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-sync = { version = "0.2.0", path = "../embassy-sync" }
|
||||||
embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true }
|
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-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" }
|
||||||
embedded-hal-async = { version = "=0.2.0-alpha.1" }
|
embedded-hal-async = { version = "=0.2.0-alpha.2" }
|
||||||
embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common", default-features = false }
|
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" ] }
|
futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
|
||||||
embedded-hal = { version = "0.2", features = ["unproven"] }
|
embedded-hal = { version = "0.2", features = ["unproven"] }
|
||||||
@ -32,3 +32,6 @@ bit_field = { version = "0.10" }
|
|||||||
|
|
||||||
lora-phy = { version = "1" }
|
lora-phy = { version = "1" }
|
||||||
lorawan-device = { version = "0.10.0", default-features = false, features = ["async"] }
|
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" }
|
||||||
|
@ -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.
|
/// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode.
|
||||||
Ip,
|
Ip,
|
||||||
|
|
||||||
|
/// IEEE 802_15_4 medium
|
||||||
|
Ieee802154,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Medium {
|
impl Default for Medium {
|
||||||
|
@ -7,14 +7,20 @@ edition = "2021"
|
|||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
log = { version = "0.4.14", 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-sync = { version = "0.2.0", path = "../embassy-sync"}
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
|
||||||
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
|
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
|
||||||
|
|
||||||
embedded-hal = { version = "1.0.0-alpha.10" }
|
embedded-hal = { version = "1.0.0-alpha.11" }
|
||||||
embedded-hal-async = { version = "=0.2.0-alpha.1" }
|
embedded-hal-async = { version = "=0.2.0-alpha.2" }
|
||||||
|
|
||||||
noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] }
|
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"] }
|
#noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] }
|
||||||
heapless = "0.7.16"
|
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"]
|
@ -54,7 +54,9 @@ impl<'a> Control<'a> {
|
|||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
let resp = self.ioctl(req).await;
|
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));
|
debug!("======= {:?}", Debug2Format(&resp));
|
||||||
assert_eq!(resp.resp, 0);
|
assert_eq!(resp.resp, 0);
|
||||||
self.state_ch.set_link_state(LinkState::Up);
|
self.state_ch.set_link_state(LinkState::Up);
|
||||||
@ -71,7 +73,9 @@ impl<'a> Control<'a> {
|
|||||||
)),
|
)),
|
||||||
};
|
};
|
||||||
let resp = self.ioctl(req).await;
|
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);
|
assert_eq!(resp.resp, 0);
|
||||||
|
|
||||||
// WHY IS THIS A STRING? WHYYYY
|
// WHY IS THIS A STRING? WHYYYY
|
||||||
@ -100,7 +104,9 @@ impl<'a> Control<'a> {
|
|||||||
payload: Some(proto::CtrlMsgPayload::ReqSetWifiMode(proto::CtrlMsgReqSetMode { mode })),
|
payload: Some(proto::CtrlMsgPayload::ReqSetWifiMode(proto::CtrlMsgReqSetMode { mode })),
|
||||||
};
|
};
|
||||||
let resp = self.ioctl(req).await;
|
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);
|
assert_eq!(resp.resp, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,14 +311,14 @@ where
|
|||||||
fn handle_event(&self, data: &[u8]) {
|
fn handle_event(&self, data: &[u8]) {
|
||||||
let Ok(event) = noproto::read::<CtrlMsg>(data) else {
|
let Ok(event) = noproto::read::<CtrlMsg>(data) else {
|
||||||
warn!("failed to parse event");
|
warn!("failed to parse event");
|
||||||
return
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("event: {:?}", &event);
|
debug!("event: {:?}", &event);
|
||||||
|
|
||||||
let Some(payload) = &event.payload else {
|
let Some(payload) = &event.payload else {
|
||||||
warn!("event without payload?");
|
warn!("event without payload?");
|
||||||
return
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
match payload {
|
match payload {
|
||||||
|
@ -8,11 +8,11 @@ license = "MIT OR Apache-2.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embedded-hal = { version = "1.0.0-alpha.10" }
|
embedded-hal = { version = "1.0.0-alpha.11" }
|
||||||
embedded-hal-async = { version = "=0.2.0-alpha.1" }
|
embedded-hal-async = { version = "=0.2.0-alpha.2" }
|
||||||
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
|
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" }
|
||||||
embassy-time = { version = "0.1.0" }
|
embassy-time = { version = "0.1.2", path = "../embassy-time" }
|
||||||
embassy-futures = { version = "0.1.0" }
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
|
|
||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
|
@ -22,7 +22,11 @@ impl<SPI: SpiDevice> SpiInterface<SPI> {
|
|||||||
let address_phase = address.to_be_bytes();
|
let address_phase = address.to_be_bytes();
|
||||||
let control_phase = [(block as u8) << 3 | 0b0000_0100];
|
let control_phase = [(block as u8) << 3 | 0b0000_0100];
|
||||||
let data_phase = data;
|
let data_phase = data;
|
||||||
let operations = &[&address_phase[..], &control_phase, &data_phase];
|
let operations = &mut [
|
||||||
self.0.write_transaction(operations).await
|
Operation::Write(&address_phase[..]),
|
||||||
|
Operation::Write(&control_phase),
|
||||||
|
Operation::Write(&data_phase),
|
||||||
|
];
|
||||||
|
self.0.transaction(operations).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,7 @@ proto-ipv4 = ["smoltcp/proto-ipv4"]
|
|||||||
proto-ipv6 = ["smoltcp/proto-ipv6"]
|
proto-ipv6 = ["smoltcp/proto-ipv6"]
|
||||||
medium-ethernet = ["smoltcp/medium-ethernet"]
|
medium-ethernet = ["smoltcp/medium-ethernet"]
|
||||||
medium-ip = ["smoltcp/medium-ip"]
|
medium-ip = ["smoltcp/medium-ip"]
|
||||||
|
medium-ieee802154 = ["smoltcp/medium-ieee802154"]
|
||||||
igmp = ["smoltcp/proto-igmp"]
|
igmp = ["smoltcp/proto-igmp"]
|
||||||
|
|
||||||
[dependencies]
|
[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-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" }
|
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
||||||
embedded-io = { version = "0.4.0", optional = true }
|
embedded-io = { version = "0.4.0", optional = true }
|
||||||
|
|
||||||
|
@ -51,6 +51,8 @@ where
|
|||||||
Medium::Ethernet => phy::Medium::Ethernet,
|
Medium::Ethernet => phy::Medium::Ethernet,
|
||||||
#[cfg(feature = "medium-ip")]
|
#[cfg(feature = "medium-ip")]
|
||||||
Medium::Ip => phy::Medium::Ip,
|
Medium::Ip => phy::Medium::Ip,
|
||||||
|
#[cfg(feature = "medium-ieee802154")]
|
||||||
|
Medium::Ieee802154 => phy::Medium::Ieee802154,
|
||||||
#[allow(unreachable_patterns)]
|
#[allow(unreachable_patterns)]
|
||||||
_ => panic!(
|
_ => panic!(
|
||||||
"Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.",
|
"Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.",
|
||||||
|
@ -24,9 +24,11 @@ use embassy_net_driver::{Driver, LinkState, Medium};
|
|||||||
use embassy_sync::waitqueue::WakerRegistration;
|
use embassy_sync::waitqueue::WakerRegistration;
|
||||||
use embassy_time::{Instant, Timer};
|
use embassy_time::{Instant, Timer};
|
||||||
use futures::pin_mut;
|
use futures::pin_mut;
|
||||||
|
#[allow(unused_imports)]
|
||||||
use heapless::Vec;
|
use heapless::Vec;
|
||||||
#[cfg(feature = "igmp")]
|
#[cfg(feature = "igmp")]
|
||||||
pub use smoltcp::iface::MulticastError;
|
pub use smoltcp::iface::MulticastError;
|
||||||
|
#[allow(unused_imports)]
|
||||||
use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage};
|
use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage};
|
||||||
#[cfg(feature = "dhcpv4")]
|
#[cfg(feature = "dhcpv4")]
|
||||||
use smoltcp::socket::dhcpv4::{self, RetryConfig};
|
use smoltcp::socket::dhcpv4::{self, RetryConfig};
|
||||||
@ -34,7 +36,9 @@ use smoltcp::socket::dhcpv4::{self, RetryConfig};
|
|||||||
pub use smoltcp::wire::IpListenEndpoint;
|
pub use smoltcp::wire::IpListenEndpoint;
|
||||||
#[cfg(feature = "medium-ethernet")]
|
#[cfg(feature = "medium-ethernet")]
|
||||||
pub use smoltcp::wire::{EthernetAddress, HardwareAddress};
|
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")]
|
#[cfg(feature = "proto-ipv4")]
|
||||||
pub use smoltcp::wire::{Ipv4Address, Ipv4Cidr};
|
pub use smoltcp::wire::{Ipv4Address, Ipv4Cidr};
|
||||||
#[cfg(feature = "proto-ipv6")]
|
#[cfg(feature = "proto-ipv6")]
|
||||||
@ -232,7 +236,7 @@ impl<D: Driver + 'static> Stack<D> {
|
|||||||
resources: &'static mut StackResources<SOCK>,
|
resources: &'static mut StackResources<SOCK>,
|
||||||
random_seed: u64,
|
random_seed: u64,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
#[cfg(feature = "medium-ethernet")]
|
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
|
||||||
let medium = device.capabilities().medium;
|
let medium = device.capabilities().medium;
|
||||||
|
|
||||||
let hardware_addr = match medium {
|
let hardware_addr = match medium {
|
||||||
@ -240,6 +244,8 @@ impl<D: Driver + 'static> Stack<D> {
|
|||||||
Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress(device.ethernet_address())),
|
Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress(device.ethernet_address())),
|
||||||
#[cfg(feature = "medium-ip")]
|
#[cfg(feature = "medium-ip")]
|
||||||
Medium::Ip => HardwareAddress::Ip,
|
Medium::Ip => HardwareAddress::Ip,
|
||||||
|
#[cfg(feature = "medium-ieee802154")]
|
||||||
|
Medium::Ieee802154 => HardwareAddress::Ieee802154(Ieee802154Address::Absent),
|
||||||
#[allow(unreachable_patterns)]
|
#[allow(unreachable_patterns)]
|
||||||
_ => panic!(
|
_ => panic!(
|
||||||
"Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.",
|
"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;
|
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 {
|
let mut socket = SocketStack {
|
||||||
sockets,
|
sockets,
|
||||||
iface,
|
iface,
|
||||||
@ -269,6 +276,7 @@ impl<D: Driver + 'static> Stack<D> {
|
|||||||
next_local_port,
|
next_local_port,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "medium-ieee802154", allow(unused_mut))]
|
||||||
let mut inner = Inner {
|
let mut inner = Inner {
|
||||||
device,
|
device,
|
||||||
link_up: false,
|
link_up: false,
|
||||||
@ -287,6 +295,9 @@ impl<D: Driver + 'static> Stack<D> {
|
|||||||
dns_waker: WakerRegistration::new(),
|
dns_waker: WakerRegistration::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "medium-ieee802154")]
|
||||||
|
let _ = config;
|
||||||
|
|
||||||
#[cfg(feature = "proto-ipv4")]
|
#[cfg(feature = "proto-ipv4")]
|
||||||
match config.ipv4 {
|
match config.ipv4 {
|
||||||
ConfigV4::Static(config) => {
|
ConfigV4::Static(config) => {
|
||||||
@ -479,30 +490,78 @@ impl<D: Driver + 'static> Stack<D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "igmp")]
|
#[cfg(feature = "igmp")]
|
||||||
impl<D: Driver + smoltcp::phy::Device + 'static> Stack<D> {
|
impl<D: Driver + 'static> Stack<D> {
|
||||||
/// Join a multicast group.
|
/// 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
|
where
|
||||||
T: Into<IpAddress>,
|
T: Into<IpAddress>,
|
||||||
{
|
{
|
||||||
let addr = addr.into();
|
let addr = addr.into();
|
||||||
|
|
||||||
self.with_mut(|s, i| {
|
self.with_mut(|s, i| {
|
||||||
s.iface
|
let mut smoldev = DriverAdapter {
|
||||||
.join_multicast_group(&mut i.device, addr, instant_to_smoltcp(Instant::now()))
|
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.
|
/// 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
|
where
|
||||||
T: Into<IpAddress>,
|
T: Into<IpAddress>,
|
||||||
{
|
{
|
||||||
let addr = addr.into();
|
let addr = addr.into();
|
||||||
|
|
||||||
self.with_mut(|s, i| {
|
self.with_mut(|s, i| {
|
||||||
s.iface
|
let mut smoldev = DriverAdapter {
|
||||||
.leave_multicast_group(&mut i.device, addr, instant_to_smoltcp(Instant::now()))
|
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);
|
debug!(" IP address: {}", config.address);
|
||||||
s.iface.update_ip_addrs(|addrs| {
|
s.iface.update_ip_addrs(|addrs| {
|
||||||
if addrs.is_empty() {
|
if let Some((index, _)) = addrs
|
||||||
addrs.push(IpCidr::Ipv4(config.address)).unwrap();
|
.iter()
|
||||||
} else {
|
.enumerate()
|
||||||
addrs[0] = IpCidr::Ipv4(config.address);
|
.find(|(_, &addr)| matches!(addr, IpCidr::Ipv4(_)))
|
||||||
|
{
|
||||||
|
addrs.remove(index);
|
||||||
}
|
}
|
||||||
|
addrs.push(IpCidr::Ipv4(config.address)).unwrap();
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(feature = "medium-ethernet")]
|
#[cfg(feature = "medium-ethernet")]
|
||||||
@ -570,11 +632,14 @@ impl<D: Driver + 'static> Inner<D> {
|
|||||||
|
|
||||||
debug!(" IP address: {}", config.address);
|
debug!(" IP address: {}", config.address);
|
||||||
s.iface.update_ip_addrs(|addrs| {
|
s.iface.update_ip_addrs(|addrs| {
|
||||||
if addrs.is_empty() {
|
if let Some((index, _)) = addrs
|
||||||
addrs.push(IpCidr::Ipv6(config.address)).unwrap();
|
.iter()
|
||||||
} else {
|
.enumerate()
|
||||||
addrs[0] = IpCidr::Ipv6(config.address);
|
.find(|(_, &addr)| matches!(addr, IpCidr::Ipv6(_)))
|
||||||
|
{
|
||||||
|
addrs.remove(index);
|
||||||
}
|
}
|
||||||
|
addrs.push(IpCidr::Ipv6(config.address)).unwrap();
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(feature = "medium-ethernet")]
|
#[cfg(feature = "medium-ethernet")]
|
||||||
@ -642,13 +707,21 @@ impl<D: Driver + 'static> Inner<D> {
|
|||||||
socket.set_retry_config(config.retry_config);
|
socket.set_retry_config(config.retry_config);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)] // used only with dhcp
|
#[cfg(feature = "dhcpv4")]
|
||||||
fn unapply_config(&mut self, s: &mut SocketStack) {
|
fn unapply_config_v4(&mut self, s: &mut SocketStack) {
|
||||||
#[cfg(feature = "medium-ethernet")]
|
#[cfg(feature = "medium-ethernet")]
|
||||||
let medium = self.device.capabilities().medium;
|
let medium = self.device.capabilities().medium;
|
||||||
|
|
||||||
debug!("Lost IP configuration");
|
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")]
|
#[cfg(feature = "medium-ethernet")]
|
||||||
if medium == Medium::Ethernet {
|
if medium == Medium::Ethernet {
|
||||||
#[cfg(feature = "proto-ipv4")]
|
#[cfg(feature = "proto-ipv4")]
|
||||||
@ -695,7 +768,7 @@ impl<D: Driver + 'static> Inner<D> {
|
|||||||
if self.link_up {
|
if self.link_up {
|
||||||
match socket.poll() {
|
match socket.poll() {
|
||||||
None => {}
|
None => {}
|
||||||
Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s),
|
Some(dhcpv4::Event::Deconfigured) => self.unapply_config_v4(s),
|
||||||
Some(dhcpv4::Event::Configured(config)) => {
|
Some(dhcpv4::Event::Configured(config)) => {
|
||||||
let config = StaticConfigV4 {
|
let config = StaticConfigV4 {
|
||||||
address: config.address,
|
address: config.address,
|
||||||
@ -707,7 +780,7 @@ impl<D: Driver + 'static> Inner<D> {
|
|||||||
}
|
}
|
||||||
} else if old_link_up {
|
} else if old_link_up {
|
||||||
socket.reset();
|
socket.reset();
|
||||||
self.unapply_config(s);
|
self.unapply_config_v4(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//if old_link_up || self.link_up {
|
//if old_link_up || self.link_up {
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
use core::future::poll_fn;
|
use core::future::poll_fn;
|
||||||
use core::mem;
|
use core::mem;
|
||||||
use core::task::Poll;
|
use core::task::{Context, Poll};
|
||||||
|
|
||||||
use embassy_net_driver::Driver;
|
use embassy_net_driver::Driver;
|
||||||
use smoltcp::iface::{Interface, SocketHandle};
|
use smoltcp::iface::{Interface, SocketHandle};
|
||||||
@ -102,37 +102,61 @@ impl<'a> UdpSocket<'a> {
|
|||||||
///
|
///
|
||||||
/// Returns the number of bytes received and the remote endpoint.
|
/// Returns the number of bytes received and the remote endpoint.
|
||||||
pub async fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, IpEndpoint), Error> {
|
pub async fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, IpEndpoint), Error> {
|
||||||
poll_fn(move |cx| {
|
poll_fn(move |cx| self.poll_recv_from(buf, cx)).await
|
||||||
self.with_mut(|s, _| match s.recv_slice(buf) {
|
}
|
||||||
Ok((n, meta)) => Poll::Ready(Ok((n, meta.endpoint))),
|
|
||||||
// No data ready
|
/// Receive a datagram.
|
||||||
Err(udp::RecvError::Exhausted) => {
|
///
|
||||||
s.register_recv_waker(cx.waker());
|
/// When no datagram is available, this method will return `Poll::Pending` and
|
||||||
Poll::Pending
|
/// 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.
|
/// 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>
|
pub async fn send_to<T>(&self, buf: &[u8], remote_endpoint: T) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
T: Into<IpEndpoint>,
|
T: Into<IpEndpoint>,
|
||||||
{
|
{
|
||||||
let remote_endpoint = remote_endpoint.into();
|
let remote_endpoint: IpEndpoint = remote_endpoint.into();
|
||||||
poll_fn(move |cx| {
|
poll_fn(move |cx| self.poll_send_to(buf, remote_endpoint, cx)).await
|
||||||
self.with_mut(|s, _| match s.send_slice(buf, remote_endpoint) {
|
}
|
||||||
// Entire datagram has been sent
|
|
||||||
Ok(()) => Poll::Ready(Ok(())),
|
/// Send a datagram to the specified remote endpoint.
|
||||||
Err(udp::SendError::BufferFull) => {
|
///
|
||||||
s.register_send_waker(cx.waker());
|
/// When the datagram has been sent, this method will return `Poll::Ready(Ok())`.
|
||||||
Poll::Pending
|
///
|
||||||
}
|
/// When the socket's send buffer is full, this method will return `Poll::Pending`
|
||||||
Err(udp::SendError::Unaddressable) => Poll::Ready(Err(Error::NoRoute)),
|
/// 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.
|
/// Returns the local endpoint of the socket.
|
||||||
|
@ -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 = "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/"
|
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 = [
|
flavors = [
|
||||||
{ regex_feature = "nrf52.*", target = "thumbv7em-none-eabihf" },
|
{ regex_feature = "nrf52.*", target = "thumbv7em-none-eabihf" },
|
||||||
{ regex_feature = "nrf53.*", target = "thumbv8m.main-none-eabihf" },
|
{ regex_feature = "nrf53.*", target = "thumbv8m.main-none-eabihf" },
|
||||||
@ -91,22 +91,22 @@ _dppi = []
|
|||||||
_gpio-p1 = []
|
_gpio-p1 = []
|
||||||
|
|
||||||
[dependencies]
|
[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-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-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-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
|
||||||
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true }
|
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-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-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true}
|
||||||
embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true}
|
embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true}
|
||||||
embedded-io = { version = "0.4.0", features = ["async"], optional = true }
|
embedded-io = { version = "0.4.0", features = ["async"], optional = true }
|
||||||
|
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
log = { version = "0.4.14", optional = true }
|
log = { version = "0.4.14", optional = true }
|
||||||
cortex-m-rt = ">=0.6.15,<0.8"
|
cortex-m-rt = ">=0.6.15,<0.8"
|
||||||
cortex-m = "0.7.6"
|
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"
|
critical-section = "1.1"
|
||||||
rand_core = "0.6.3"
|
rand_core = "0.6.3"
|
||||||
fixed = "1.10.0"
|
fixed = "1.10.0"
|
||||||
@ -114,13 +114,13 @@ embedded-storage = "0.3.0"
|
|||||||
embedded-storage-async = { version = "0.4.0", optional = true }
|
embedded-storage-async = { version = "0.4.0", optional = true }
|
||||||
cfg-if = "1.0.0"
|
cfg-if = "1.0.0"
|
||||||
|
|
||||||
nrf52805-pac = { version = "0.12.0", optional = true }
|
nrf52805-pac = { version = "0.12.0", optional = true }
|
||||||
nrf52810-pac = { version = "0.12.0", optional = true }
|
nrf52810-pac = { version = "0.12.0", optional = true }
|
||||||
nrf52811-pac = { version = "0.12.0", optional = true }
|
nrf52811-pac = { version = "0.12.0", optional = true }
|
||||||
nrf52820-pac = { version = "0.12.0", optional = true }
|
nrf52820-pac = { version = "0.12.0", optional = true }
|
||||||
nrf52832-pac = { version = "0.12.0", optional = true }
|
nrf52832-pac = { version = "0.12.0", optional = true }
|
||||||
nrf52833-pac = { version = "0.12.0", optional = true }
|
nrf52833-pac = { version = "0.12.0", optional = true }
|
||||||
nrf52840-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-app-pac = { version = "0.12.0", optional = true }
|
||||||
nrf5340-net-pac = { version = "0.12.0", optional = true }
|
nrf5340-net-pac = { version = "0.12.0", optional = true }
|
||||||
nrf9160-pac = { version = "0.12.0", optional = true }
|
nrf9160-pac = { version = "0.12.0", optional = true }
|
||||||
|
@ -221,7 +221,7 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the IN event, for use with PPI.
|
/// Returns the IN event, for use with PPI.
|
||||||
pub fn event_in(&self) -> Event {
|
pub fn event_in(&self) -> Event<'d> {
|
||||||
let g = regs();
|
let g = regs();
|
||||||
Event::from_reg(&g.events_in[self.ch.number()])
|
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.
|
/// Returns the OUT task, for use with PPI.
|
||||||
pub fn task_out(&self) -> Task {
|
pub fn task_out(&self) -> Task<'d> {
|
||||||
let g = regs();
|
let g = regs();
|
||||||
Task::from_reg(&g.tasks_out[self.ch.number()])
|
Task::from_reg(&g.tasks_out[self.ch.number()])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the CLR task, for use with PPI.
|
/// Returns the CLR task, for use with PPI.
|
||||||
#[cfg(not(feature = "nrf51"))]
|
#[cfg(not(feature = "nrf51"))]
|
||||||
pub fn task_clr(&self) -> Task {
|
pub fn task_clr(&self) -> Task<'d> {
|
||||||
let g = regs();
|
let g = regs();
|
||||||
Task::from_reg(&g.tasks_clr[self.ch.number()])
|
Task::from_reg(&g.tasks_clr[self.ch.number()])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the SET task, for use with PPI.
|
/// Returns the SET task, for use with PPI.
|
||||||
#[cfg(not(feature = "nrf51"))]
|
#[cfg(not(feature = "nrf51"))]
|
||||||
pub fn task_set(&self) -> Task {
|
pub fn task_set(&self) -> Task<'d> {
|
||||||
let g = regs();
|
let g = regs();
|
||||||
Task::from_reg(&g.tasks_set[self.ch.number()])
|
Task::from_reg(&g.tasks_set[self.ch.number()])
|
||||||
}
|
}
|
||||||
|
@ -1,294 +1,500 @@
|
|||||||
//! Pulse Density Modulation (PDM) mirophone driver.
|
//! Pulse Density Modulation (PDM) mirophone driver.
|
||||||
|
|
||||||
#![macro_use]
|
#![macro_use]
|
||||||
|
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::sync::atomic::{compiler_fence, Ordering};
|
use core::sync::atomic::{compiler_fence, Ordering};
|
||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
|
|
||||||
use embassy_hal_common::drop::OnDrop;
|
use embassy_hal_common::drop::OnDrop;
|
||||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||||
use futures::future::poll_fn;
|
use fixed::types::I7F1;
|
||||||
|
use futures::future::poll_fn;
|
||||||
use crate::chip::EASY_DMA_SIZE;
|
|
||||||
use crate::gpio::sealed::Pin;
|
use crate::chip::EASY_DMA_SIZE;
|
||||||
use crate::gpio::{AnyPin, Pin as GpioPin};
|
use crate::gpio::sealed::Pin;
|
||||||
use crate::interrupt::typelevel::Interrupt;
|
use crate::gpio::{AnyPin, Pin as GpioPin};
|
||||||
use crate::{interrupt, Peripheral};
|
use crate::interrupt::typelevel::Interrupt;
|
||||||
|
use crate::pac::pdm::mode::{EDGE_A, OPERATION_A};
|
||||||
/// Interrupt handler.
|
pub use crate::pac::pdm::pdmclkctrl::FREQ_A as Frequency;
|
||||||
pub struct InterruptHandler<T: Instance> {
|
#[cfg(any(
|
||||||
_phantom: PhantomData<T>,
|
feature = "nrf52840",
|
||||||
}
|
feature = "nrf52833",
|
||||||
|
feature = "_nrf5340-app",
|
||||||
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
|
feature = "_nrf9160",
|
||||||
unsafe fn on_interrupt() {
|
))]
|
||||||
T::regs().intenclr.write(|w| w.end().clear());
|
pub use crate::pac::pdm::ratio::RATIO_A as Ratio;
|
||||||
T::state().waker.wake();
|
use crate::{interrupt, Peripheral};
|
||||||
}
|
|
||||||
}
|
/// Interrupt handler.
|
||||||
|
pub struct InterruptHandler<T: Instance> {
|
||||||
/// PDM microphone interface
|
_phantom: PhantomData<T>,
|
||||||
pub struct Pdm<'d, T: Instance> {
|
}
|
||||||
_peri: PeripheralRef<'d, T>,
|
|
||||||
}
|
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
|
||||||
|
unsafe fn on_interrupt() {
|
||||||
/// PDM error.
|
let r = T::regs();
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
if r.events_end.read().bits() != 0 {
|
||||||
#[non_exhaustive]
|
r.intenclr.write(|w| w.end().clear());
|
||||||
pub enum Error {
|
}
|
||||||
/// Buffer is too long.
|
|
||||||
BufferTooLong,
|
if r.events_started.read().bits() != 0 {
|
||||||
/// Buffer is empty
|
r.intenclr.write(|w| w.started().clear());
|
||||||
BufferZeroLength,
|
}
|
||||||
/// PDM is not running
|
|
||||||
NotRunning,
|
if r.events_stopped.read().bits() != 0 {
|
||||||
}
|
r.intenclr.write(|w| w.stopped().clear());
|
||||||
|
}
|
||||||
static DUMMY_BUFFER: [i16; 1] = [0; 1];
|
|
||||||
|
T::state().waker.wake();
|
||||||
impl<'d, T: Instance> Pdm<'d, T> {
|
}
|
||||||
/// Create PDM driver
|
}
|
||||||
pub fn new(
|
|
||||||
pdm: impl Peripheral<P = T> + 'd,
|
/// PDM microphone interface
|
||||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
pub struct Pdm<'d, T: Instance> {
|
||||||
clk: impl Peripheral<P = impl GpioPin> + 'd,
|
_peri: PeripheralRef<'d, T>,
|
||||||
din: impl Peripheral<P = impl GpioPin> + 'd,
|
}
|
||||||
config: Config,
|
|
||||||
) -> Self {
|
/// PDM error.
|
||||||
into_ref!(pdm, clk, din);
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
Self::new_inner(pdm, clk.map_into(), din.map_into(), config)
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
}
|
#[non_exhaustive]
|
||||||
|
pub enum Error {
|
||||||
fn new_inner(
|
/// Buffer is too long.
|
||||||
pdm: PeripheralRef<'d, T>,
|
BufferTooLong,
|
||||||
clk: PeripheralRef<'d, AnyPin>,
|
/// Buffer is empty
|
||||||
din: PeripheralRef<'d, AnyPin>,
|
BufferZeroLength,
|
||||||
config: Config,
|
/// PDM is not running
|
||||||
) -> Self {
|
NotRunning,
|
||||||
into_ref!(pdm);
|
/// PDM is already running
|
||||||
|
AlreadyRunning,
|
||||||
let r = T::regs();
|
}
|
||||||
|
|
||||||
// setup gpio pins
|
static DUMMY_BUFFER: [i16; 1] = [0; 1];
|
||||||
din.conf().write(|w| w.input().set_bit());
|
|
||||||
r.psel.din.write(|w| unsafe { w.bits(din.psel_bits()) });
|
/// The state of a continuously running sampler. While it reflects
|
||||||
clk.set_low();
|
/// the progress of a sampler, it also signals what should be done
|
||||||
clk.conf().write(|w| w.dir().output());
|
/// next. For example, if the sampler has stopped then the Pdm implementation
|
||||||
r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) });
|
/// can then tear down its infrastructure.
|
||||||
|
#[derive(PartialEq)]
|
||||||
// configure
|
pub enum SamplerState {
|
||||||
// use default for
|
/// The sampler processed the samples and is ready for more.
|
||||||
// - gain right
|
Sampled,
|
||||||
// - gain left
|
/// The sampler is done processing samples.
|
||||||
// - clk
|
Stopped,
|
||||||
// - ratio
|
}
|
||||||
r.mode.write(|w| {
|
|
||||||
w.edge().bit(config.edge == Edge::LeftRising);
|
impl<'d, T: Instance> Pdm<'d, T> {
|
||||||
w.operation().bit(config.operation_mode == OperationMode::Mono);
|
/// Create PDM driver
|
||||||
w
|
pub fn new(
|
||||||
});
|
pdm: impl Peripheral<P = T> + 'd,
|
||||||
r.gainl.write(|w| w.gainl().default_gain());
|
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
||||||
r.gainr.write(|w| w.gainr().default_gain());
|
clk: impl Peripheral<P = impl GpioPin> + 'd,
|
||||||
|
din: impl Peripheral<P = impl GpioPin> + 'd,
|
||||||
// IRQ
|
config: Config,
|
||||||
T::Interrupt::unpend();
|
) -> Self {
|
||||||
unsafe { T::Interrupt::enable() };
|
into_ref!(pdm, clk, din);
|
||||||
|
Self::new_inner(pdm, clk.map_into(), din.map_into(), config)
|
||||||
r.enable.write(|w| w.enable().set_bit());
|
}
|
||||||
|
|
||||||
Self { _peri: pdm }
|
fn new_inner(
|
||||||
}
|
pdm: PeripheralRef<'d, T>,
|
||||||
|
clk: PeripheralRef<'d, AnyPin>,
|
||||||
/// Start sampling microphon data into a dummy buffer
|
din: PeripheralRef<'d, AnyPin>,
|
||||||
/// Usefull to start the microphon and keep it active between recording samples
|
config: Config,
|
||||||
pub async fn start(&mut self) {
|
) -> Self {
|
||||||
let r = T::regs();
|
into_ref!(pdm);
|
||||||
|
|
||||||
// start dummy sampling because microphon needs some setup time
|
let r = T::regs();
|
||||||
r.sample
|
|
||||||
.ptr
|
// setup gpio pins
|
||||||
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
|
din.conf().write(|w| w.input().set_bit());
|
||||||
r.sample
|
r.psel.din.write(|w| unsafe { w.bits(din.psel_bits()) });
|
||||||
.maxcnt
|
clk.set_low();
|
||||||
.write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
|
clk.conf().write(|w| w.dir().output());
|
||||||
|
r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) });
|
||||||
r.tasks_start.write(|w| unsafe { w.bits(1) });
|
|
||||||
}
|
// configure
|
||||||
|
r.pdmclkctrl.write(|w| w.freq().variant(config.frequency));
|
||||||
/// Stop sampling microphon data inta a dummy buffer
|
#[cfg(any(
|
||||||
pub async fn stop(&mut self) {
|
feature = "nrf52840",
|
||||||
let r = T::regs();
|
feature = "nrf52833",
|
||||||
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
feature = "_nrf5340-app",
|
||||||
r.events_started.reset();
|
feature = "_nrf9160",
|
||||||
}
|
))]
|
||||||
|
r.ratio.write(|w| w.ratio().variant(config.ratio));
|
||||||
/// Sample data into the given buffer.
|
r.mode.write(|w| {
|
||||||
pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> {
|
w.operation().variant(config.operation_mode.into());
|
||||||
if buffer.len() == 0 {
|
w.edge().variant(config.edge.into());
|
||||||
return Err(Error::BufferZeroLength);
|
w
|
||||||
}
|
});
|
||||||
if buffer.len() > EASY_DMA_SIZE {
|
|
||||||
return Err(Error::BufferTooLong);
|
Self::_set_gain(r, config.gain_left, config.gain_right);
|
||||||
}
|
|
||||||
|
// Disable all events interrupts
|
||||||
let r = T::regs();
|
r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) });
|
||||||
|
|
||||||
if r.events_started.read().bits() == 0 {
|
// IRQ
|
||||||
return Err(Error::NotRunning);
|
T::Interrupt::unpend();
|
||||||
}
|
unsafe { T::Interrupt::enable() };
|
||||||
|
|
||||||
let drop = OnDrop::new(move || {
|
r.enable.write(|w| w.enable().set_bit());
|
||||||
r.intenclr.write(|w| w.end().clear());
|
|
||||||
r.events_stopped.reset();
|
Self { _peri: pdm }
|
||||||
|
}
|
||||||
// reset to dummy buffer
|
|
||||||
r.sample
|
fn _set_gain(r: &crate::pac::pdm::RegisterBlock, gain_left: I7F1, gain_right: I7F1) {
|
||||||
.ptr
|
let gain_left = gain_left
|
||||||
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
|
.saturating_add(I7F1::from_bits(40))
|
||||||
r.sample
|
.saturating_to_num::<u8>()
|
||||||
.maxcnt
|
.clamp(0, 0x50);
|
||||||
.write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
|
let gain_right = gain_right
|
||||||
|
.saturating_add(I7F1::from_bits(40))
|
||||||
while r.events_stopped.read().bits() == 0 {}
|
.saturating_to_num::<u8>()
|
||||||
});
|
.clamp(0, 0x50);
|
||||||
|
|
||||||
// setup user buffer
|
r.gainl.write(|w| unsafe { w.gainl().bits(gain_left) });
|
||||||
let ptr = buffer.as_ptr();
|
r.gainr.write(|w| unsafe { w.gainr().bits(gain_right) });
|
||||||
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 _) });
|
/// Adjust the gain of the PDM microphone on the fly
|
||||||
|
pub fn set_gain(&mut self, gain_left: I7F1, gain_right: I7F1) {
|
||||||
// wait till the current sample is finished and the user buffer sample is started
|
Self::_set_gain(T::regs(), gain_left, gain_right)
|
||||||
Self::wait_for_sample().await;
|
}
|
||||||
|
|
||||||
// reset the buffer back to the dummy buffer
|
/// Start sampling microphon data into a dummy buffer
|
||||||
r.sample
|
/// Usefull to start the microphon and keep it active between recording samples
|
||||||
.ptr
|
pub async fn start(&mut self) {
|
||||||
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
|
let r = T::regs();
|
||||||
r.sample
|
|
||||||
.maxcnt
|
// start dummy sampling because microphon needs some setup time
|
||||||
.write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
|
r.sample
|
||||||
|
.ptr
|
||||||
// wait till the user buffer is sampled
|
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
|
||||||
Self::wait_for_sample().await;
|
r.sample
|
||||||
|
.maxcnt
|
||||||
drop.defuse();
|
.write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
|
||||||
|
|
||||||
Ok(())
|
r.tasks_start.write(|w| unsafe { w.bits(1) });
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn wait_for_sample() {
|
/// Stop sampling microphon data inta a dummy buffer
|
||||||
let r = T::regs();
|
pub async fn stop(&mut self) {
|
||||||
|
let r = T::regs();
|
||||||
r.events_end.reset();
|
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||||
r.intenset.write(|w| w.end().set());
|
r.events_started.reset();
|
||||||
|
}
|
||||||
compiler_fence(Ordering::SeqCst);
|
|
||||||
|
/// Sample data into the given buffer.
|
||||||
poll_fn(|cx| {
|
pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> {
|
||||||
T::state().waker.register(cx.waker());
|
if buffer.len() == 0 {
|
||||||
if r.events_end.read().bits() != 0 {
|
return Err(Error::BufferZeroLength);
|
||||||
return Poll::Ready(());
|
}
|
||||||
}
|
if buffer.len() > EASY_DMA_SIZE {
|
||||||
Poll::Pending
|
return Err(Error::BufferTooLong);
|
||||||
})
|
}
|
||||||
.await;
|
|
||||||
|
let r = T::regs();
|
||||||
compiler_fence(Ordering::SeqCst);
|
|
||||||
}
|
if r.events_started.read().bits() == 0 {
|
||||||
}
|
return Err(Error::NotRunning);
|
||||||
|
}
|
||||||
/// PDM microphone driver Config
|
|
||||||
pub struct Config {
|
let drop = OnDrop::new(move || {
|
||||||
/// Use stero or mono operation
|
r.intenclr.write(|w| w.end().clear());
|
||||||
pub operation_mode: OperationMode,
|
r.events_stopped.reset();
|
||||||
/// On which edge the left channel should be samples
|
|
||||||
pub edge: Edge,
|
// reset to dummy buffer
|
||||||
}
|
r.sample
|
||||||
|
.ptr
|
||||||
impl Default for Config {
|
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
|
||||||
fn default() -> Self {
|
r.sample
|
||||||
Self {
|
.maxcnt
|
||||||
operation_mode: OperationMode::Mono,
|
.write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
|
||||||
edge: Edge::LeftFalling,
|
|
||||||
}
|
while r.events_stopped.read().bits() == 0 {}
|
||||||
}
|
});
|
||||||
}
|
|
||||||
|
// setup user buffer
|
||||||
/// PDM operation mode.
|
let ptr = buffer.as_ptr();
|
||||||
#[derive(PartialEq)]
|
let len = buffer.len();
|
||||||
pub enum OperationMode {
|
r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(ptr as u32) });
|
||||||
/// Mono (1 channel)
|
r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(len as _) });
|
||||||
Mono,
|
|
||||||
/// Stereo (2 channels)
|
// wait till the current sample is finished and the user buffer sample is started
|
||||||
Stereo,
|
Self::wait_for_sample().await;
|
||||||
}
|
|
||||||
|
// reset the buffer back to the dummy buffer
|
||||||
/// PDM edge polarity
|
r.sample
|
||||||
#[derive(PartialEq)]
|
.ptr
|
||||||
pub enum Edge {
|
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
|
||||||
/// Left edge is rising
|
r.sample
|
||||||
LeftRising,
|
.maxcnt
|
||||||
/// Left edge is falling
|
.write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
|
||||||
LeftFalling,
|
|
||||||
}
|
// wait till the user buffer is sampled
|
||||||
|
Self::wait_for_sample().await;
|
||||||
impl<'d, T: Instance> Drop for Pdm<'d, T> {
|
|
||||||
fn drop(&mut self) {
|
drop.defuse();
|
||||||
let r = T::regs();
|
|
||||||
|
Ok(())
|
||||||
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
}
|
||||||
|
|
||||||
r.enable.write(|w| w.enable().disabled());
|
async fn wait_for_sample() {
|
||||||
|
let r = T::regs();
|
||||||
r.psel.din.reset();
|
|
||||||
r.psel.clk.reset();
|
r.events_end.reset();
|
||||||
}
|
r.intenset.write(|w| w.end().set());
|
||||||
}
|
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
pub(crate) mod sealed {
|
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
poll_fn(|cx| {
|
||||||
|
T::state().waker.register(cx.waker());
|
||||||
/// Peripheral static state
|
if r.events_end.read().bits() != 0 {
|
||||||
pub struct State {
|
return Poll::Ready(());
|
||||||
pub waker: AtomicWaker,
|
}
|
||||||
}
|
Poll::Pending
|
||||||
|
})
|
||||||
impl State {
|
.await;
|
||||||
pub const fn new() -> Self {
|
|
||||||
Self {
|
compiler_fence(Ordering::SeqCst);
|
||||||
waker: AtomicWaker::new(),
|
}
|
||||||
}
|
|
||||||
}
|
/// Continuous sampling with double buffers.
|
||||||
}
|
///
|
||||||
|
/// A sampler closure is provided that receives the buffer of samples, noting
|
||||||
pub trait Instance {
|
/// that the size of this buffer can be less than the original buffer's size.
|
||||||
fn regs() -> &'static crate::pac::pdm::RegisterBlock;
|
/// A command is return from the closure that indicates whether the sampling
|
||||||
fn state() -> &'static State;
|
/// 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
|
||||||
/// PDM peripheral instance.
|
/// time taken by the callback and set the sample buffer size accordingly.
|
||||||
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
|
/// Exceeding this time can lead to samples becoming dropped.
|
||||||
/// Interrupt for this peripheral.
|
pub async fn run_task_sampler<S, const N: usize>(
|
||||||
type Interrupt: interrupt::typelevel::Interrupt;
|
&mut self,
|
||||||
}
|
bufs: &mut [[i16; N]; 2],
|
||||||
|
mut sampler: S,
|
||||||
macro_rules! impl_pdm {
|
) -> Result<(), Error>
|
||||||
($type:ident, $pac_type:ident, $irq:ident) => {
|
where
|
||||||
impl crate::pdm::sealed::Instance for peripherals::$type {
|
S: FnMut(&[i16; N]) -> SamplerState,
|
||||||
fn regs() -> &'static crate::pac::pdm::RegisterBlock {
|
{
|
||||||
unsafe { &*pac::$pac_type::ptr() }
|
let r = T::regs();
|
||||||
}
|
|
||||||
fn state() -> &'static crate::pdm::sealed::State {
|
if r.events_started.read().bits() != 0 {
|
||||||
static STATE: crate::pdm::sealed::State = crate::pdm::sealed::State::new();
|
return Err(Error::AlreadyRunning);
|
||||||
&STATE
|
}
|
||||||
}
|
|
||||||
}
|
r.sample
|
||||||
impl crate::pdm::Instance for peripherals::$type {
|
.ptr
|
||||||
type Interrupt = crate::interrupt::typelevel::$irq;
|
.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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -12,14 +12,14 @@ pub(crate) fn regs() -> &'static pac::dppic::RegisterBlock {
|
|||||||
|
|
||||||
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
|
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
|
||||||
/// Configure PPI channel to trigger `task` on `event`.
|
/// 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])
|
Ppi::new_many_to_many(ch, [event], [task])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {
|
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {
|
||||||
/// Configure PPI channel to trigger both `task1` and `task2` on `event`.
|
/// 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])
|
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.
|
/// Configure a DPPI channel to trigger all `tasks` when any of the `events` fires.
|
||||||
pub fn new_many_to_many(
|
pub fn new_many_to_many(
|
||||||
ch: impl Peripheral<P = C> + 'd,
|
ch: impl Peripheral<P = C> + 'd,
|
||||||
events: [Event; EVENT_COUNT],
|
events: [Event<'d>; EVENT_COUNT],
|
||||||
tasks: [Task; TASK_COUNT],
|
tasks: [Task<'d>; TASK_COUNT],
|
||||||
) -> Self {
|
) -> Self {
|
||||||
into_ref!(ch);
|
into_ref!(ch);
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
//! many tasks and events, but any single task or event can only be coupled with one channel.
|
//! 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 core::ptr::NonNull;
|
||||||
|
|
||||||
use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef};
|
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> {
|
pub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> {
|
||||||
ch: PeripheralRef<'d, C>,
|
ch: PeripheralRef<'d, C>,
|
||||||
#[cfg(feature = "_dppi")]
|
#[cfg(feature = "_dppi")]
|
||||||
events: [Event; EVENT_COUNT],
|
events: [Event<'d>; EVENT_COUNT],
|
||||||
#[cfg(feature = "_dppi")]
|
#[cfg(feature = "_dppi")]
|
||||||
tasks: [Task; TASK_COUNT],
|
tasks: [Task<'d>; TASK_COUNT],
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PPI channel group driver.
|
/// PPI channel group driver.
|
||||||
@ -95,7 +96,7 @@ impl<'d, G: Group> PpiGroup<'d, G> {
|
|||||||
/// Get a reference to the "enable all" task.
|
/// Get a reference to the "enable all" task.
|
||||||
///
|
///
|
||||||
/// When triggered, it will enable all the channels in this group.
|
/// 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();
|
let n = self.g.number();
|
||||||
Task::from_reg(®s().tasks_chg[n].en)
|
Task::from_reg(®s().tasks_chg[n].en)
|
||||||
}
|
}
|
||||||
@ -103,7 +104,7 @@ impl<'d, G: Group> PpiGroup<'d, G> {
|
|||||||
/// Get a reference to the "disable all" task.
|
/// Get a reference to the "disable all" task.
|
||||||
///
|
///
|
||||||
/// When triggered, it will disable all the channels in this group.
|
/// 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();
|
let n = self.g.number();
|
||||||
Task::from_reg(®s().tasks_chg[n].dis)
|
Task::from_reg(®s().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
|
/// When a task is subscribed to a PPI channel, it will run when the channel is triggered by
|
||||||
/// a published event.
|
/// a published event.
|
||||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
#[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
|
/// Create a new `Task` from a task register pointer
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// `ptr` must be a pointer to a valid `TASKS_*` register from an nRF peripheral.
|
/// `ptr` must be a pointer to a valid `TASKS_*` register from an nRF peripheral.
|
||||||
pub unsafe fn new_unchecked(ptr: NonNull<u32>) -> Self {
|
pub unsafe fn new_unchecked(ptr: NonNull<u32>) -> Self {
|
||||||
Self(ptr)
|
Self(ptr, PhantomData)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Triggers this task.
|
/// Triggers this task.
|
||||||
@ -143,7 +144,10 @@ impl Task {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_reg<T>(reg: &T) -> Self {
|
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.
|
/// Address of subscription register for this task.
|
||||||
@ -156,26 +160,29 @@ impl Task {
|
|||||||
/// # Safety
|
/// # 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.
|
/// 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.
|
/// Represents an event that a peripheral can publish.
|
||||||
///
|
///
|
||||||
/// An event can be set to publish on a PPI channel when the event happens.
|
/// An event can be set to publish on a PPI channel when the event happens.
|
||||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
#[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
|
/// Create a new `Event` from an event register pointer
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// `ptr` must be a pointer to a valid `EVENTS_*` register from an nRF peripheral.
|
/// `ptr` must be a pointer to a valid `EVENTS_*` register from an nRF peripheral.
|
||||||
pub unsafe fn new_unchecked(ptr: NonNull<u32>) -> Self {
|
pub unsafe fn new_unchecked(ptr: NonNull<u32>) -> Self {
|
||||||
Self(ptr)
|
Self(ptr, PhantomData)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_reg<T>(reg: &T) -> Self {
|
pub(crate) fn from_reg<T>(reg: &'d T) -> Self {
|
||||||
Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
|
Self(
|
||||||
|
unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) },
|
||||||
|
PhantomData,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Describes whether this Event is currently in a triggered state.
|
/// Describes whether this Event is currently in a triggered state.
|
||||||
@ -198,7 +205,7 @@ impl Event {
|
|||||||
/// # Safety
|
/// # 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.
|
/// 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
|
// traits
|
||||||
|
@ -3,12 +3,12 @@ use embassy_hal_common::into_ref;
|
|||||||
use super::{Channel, ConfigurableChannel, Event, Ppi, StaticChannel, Task};
|
use super::{Channel, ConfigurableChannel, Event, Ppi, StaticChannel, Task};
|
||||||
use crate::{pac, Peripheral};
|
use crate::{pac, Peripheral};
|
||||||
|
|
||||||
impl Task {
|
impl<'d> Task<'d> {
|
||||||
fn reg_val(&self) -> u32 {
|
fn reg_val(&self) -> u32 {
|
||||||
self.0.as_ptr() as _
|
self.0.as_ptr() as _
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Event {
|
impl<'d> Event<'d> {
|
||||||
fn reg_val(&self) -> u32 {
|
fn reg_val(&self) -> u32 {
|
||||||
self.0.as_ptr() as _
|
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> {
|
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
|
||||||
/// Configure PPI channel to trigger `task` on `event`.
|
/// 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);
|
into_ref!(ch);
|
||||||
|
|
||||||
let r = regs();
|
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
|
#[cfg(not(feature = "nrf51"))] // Not for nrf51 because of the fork task
|
||||||
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {
|
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {
|
||||||
/// Configure PPI channel to trigger both `task1` and `task2` on `event`.
|
/// 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);
|
into_ref!(ch);
|
||||||
|
|
||||||
let r = regs();
|
let r = regs();
|
||||||
|
@ -181,7 +181,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
|
|||||||
|
|
||||||
/// Returns reference to `Stopped` event endpoint for PPI.
|
/// Returns reference to `Stopped` event endpoint for PPI.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn event_stopped(&self) -> Event {
|
pub fn event_stopped(&self) -> Event<'d> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
Event::from_reg(&r.events_stopped)
|
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.
|
/// Returns reference to `LoopsDone` event endpoint for PPI.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn event_loops_done(&self) -> Event {
|
pub fn event_loops_done(&self) -> Event<'d> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
Event::from_reg(&r.events_loopsdone)
|
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.
|
/// Returns reference to `PwmPeriodEnd` event endpoint for PPI.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn event_pwm_period_end(&self) -> Event {
|
pub fn event_pwm_period_end(&self) -> Event<'d> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
Event::from_reg(&r.events_pwmperiodend)
|
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.
|
/// Returns reference to `Seq0 End` event endpoint for PPI.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn event_seq_end(&self) -> Event {
|
pub fn event_seq_end(&self) -> Event<'d> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
Event::from_reg(&r.events_seqend[0])
|
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.
|
/// Returns reference to `Seq1 End` event endpoint for PPI.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn event_seq1_end(&self) -> Event {
|
pub fn event_seq1_end(&self) -> Event<'d> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
Event::from_reg(&r.events_seqend[1])
|
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.
|
/// Returns reference to `Seq0 Started` event endpoint for PPI.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn event_seq0_started(&self) -> Event {
|
pub fn event_seq0_started(&self) -> Event<'d> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
Event::from_reg(&r.events_seqstarted[0])
|
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.
|
/// Returns reference to `Seq1 Started` event endpoint for PPI.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn event_seq1_started(&self) -> Event {
|
pub fn event_seq1_started(&self) -> Event<'d> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
Event::from_reg(&r.events_seqstarted[1])
|
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
|
/// Interacting with the sequence while it runs puts it in an unknown state
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn task_start_seq0(&self) -> Task {
|
pub unsafe fn task_start_seq0(&self) -> Task<'d> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
Task::from_reg(&r.tasks_seqstart[0])
|
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
|
/// Interacting with the sequence while it runs puts it in an unknown state
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn task_start_seq1(&self) -> Task {
|
pub unsafe fn task_start_seq1(&self) -> Task<'d> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
Task::from_reg(&r.tasks_seqstart[1])
|
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
|
/// Interacting with the sequence while it runs puts it in an unknown state
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn task_next_step(&self) -> Task {
|
pub unsafe fn task_next_step(&self) -> Task<'d> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
Task::from_reg(&r.tasks_nextstep)
|
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
|
/// Interacting with the sequence while it runs puts it in an unknown state
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn task_stop(&self) -> Task {
|
pub unsafe fn task_stop(&self) -> Task<'d> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
Task::from_reg(&r.tasks_stop)
|
Task::from_reg(&r.tasks_stop)
|
||||||
|
@ -320,7 +320,9 @@ impl<'d, const N: usize> Saadc<'d, N> {
|
|||||||
timer.cc(0).write(sample_counter);
|
timer.cc(0).write(sample_counter);
|
||||||
timer.cc(0).short_compare_clear();
|
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();
|
timer.start();
|
||||||
|
|
||||||
|
@ -468,25 +468,19 @@ mod eh1 {
|
|||||||
type Error = Error;
|
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> {
|
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||||
Ok(())
|
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> {
|
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
self.blocking_transfer(words, &[])
|
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> {
|
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
|
||||||
self.blocking_write(words)
|
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> {
|
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
|
||||||
self.blocking_transfer(read, write)
|
self.blocking_transfer(read, write)
|
||||||
}
|
}
|
||||||
@ -502,25 +496,19 @@ mod eha {
|
|||||||
|
|
||||||
use super::*;
|
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> {
|
async fn flush(&mut self) -> Result<(), Error> {
|
||||||
Ok(())
|
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> {
|
async fn read(&mut self, words: &mut [u8]) -> Result<(), Error> {
|
||||||
self.read(words).await
|
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> {
|
async fn write(&mut self, data: &[u8]) -> Result<(), Error> {
|
||||||
self.write(data).await
|
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> {
|
async fn transfer(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> {
|
||||||
self.transfer(rx, tx).await
|
self.transfer(rx, tx).await
|
||||||
}
|
}
|
||||||
|
@ -168,21 +168,21 @@ impl<'d, T: Instance> Timer<'d, T> {
|
|||||||
/// Returns the START task, for use with PPI.
|
/// Returns the START task, for use with PPI.
|
||||||
///
|
///
|
||||||
/// When triggered, this task starts the timer.
|
/// 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)
|
Task::from_reg(&T::regs().tasks_start)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the STOP task, for use with PPI.
|
/// Returns the STOP task, for use with PPI.
|
||||||
///
|
///
|
||||||
/// When triggered, this task stops the timer.
|
/// 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)
|
Task::from_reg(&T::regs().tasks_stop)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the CLEAR task, for use with PPI.
|
/// Returns the CLEAR task, for use with PPI.
|
||||||
///
|
///
|
||||||
/// When triggered, this task resets the timer's counter to 0.
|
/// 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)
|
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.
|
/// When triggered, this task increments the timer's counter by 1.
|
||||||
/// Only works in counter mode.
|
/// 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)
|
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.
|
/// 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.
|
/// 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)
|
Task::from_reg(&T::regs().tasks_capture)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns this CC register's COMPARE event, for use with PPI.
|
/// 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.
|
/// 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])
|
Event::from_reg(&T::regs().events_compare[self.n])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ unstable-traits = ["embedded-hal-1", "embedded-hal-nb"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
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-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-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" }
|
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
|
||||||
@ -79,9 +79,9 @@ fixed = "1.23.1"
|
|||||||
rp-pac = { version = "6" }
|
rp-pac = { version = "6" }
|
||||||
|
|
||||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
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-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true}
|
||||||
embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true}
|
embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true}
|
||||||
embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true}
|
embedded-hal-nb = { version = "=1.0.0-alpha.3", optional = true}
|
||||||
|
|
||||||
paste = "1.0"
|
paste = "1.0"
|
||||||
pio-proc = {version= "0.2" }
|
pio-proc = {version= "0.2" }
|
||||||
|
@ -3,22 +3,17 @@ use core::marker::PhantomData;
|
|||||||
use core::sync::atomic::{compiler_fence, Ordering};
|
use core::sync::atomic::{compiler_fence, Ordering};
|
||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
|
|
||||||
|
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
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::typelevel::Binding;
|
||||||
use crate::interrupt::InterruptExt;
|
use crate::interrupt::InterruptExt;
|
||||||
use crate::peripherals::ADC;
|
use crate::peripherals::ADC;
|
||||||
use crate::{interrupt, pac, peripherals, Peripheral};
|
use crate::{interrupt, pac, peripherals, Peripheral};
|
||||||
static WAKER: AtomicWaker = AtomicWaker::new();
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
static WAKER: AtomicWaker = AtomicWaker::new();
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub enum Error {
|
|
||||||
// No errors for now
|
|
||||||
}
|
|
||||||
|
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct Config {}
|
pub struct Config {}
|
||||||
@ -28,11 +23,75 @@ impl Default for Config {
|
|||||||
Self {}
|
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 pin’s
|
||||||
|
// > digital functions must be disabled by setting IE low and OD
|
||||||
|
// > high in the pin’s 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]
|
#[inline]
|
||||||
fn regs() -> pac::adc::Adc {
|
fn regs() -> pac::adc::Adc {
|
||||||
pac::ADC
|
pac::ADC
|
||||||
@ -45,11 +104,7 @@ impl<'d> Adc<'d> {
|
|||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(
|
fn setup() {
|
||||||
_inner: impl Peripheral<P = ADC> + 'd,
|
|
||||||
_irq: impl Binding<interrupt::typelevel::ADC_IRQ_FIFO, InterruptHandler>,
|
|
||||||
_config: Config,
|
|
||||||
) -> Self {
|
|
||||||
let reset = Self::reset();
|
let reset = Self::reset();
|
||||||
crate::reset::reset(reset);
|
crate::reset::reset(reset);
|
||||||
crate::reset::unreset_wait(reset);
|
crate::reset::unreset_wait(reset);
|
||||||
@ -58,6 +113,43 @@ impl<'d> Adc<'d> {
|
|||||||
r.cs().write(|w| w.set_en(true));
|
r.cs().write(|w| w.set_en(true));
|
||||||
// Wait for ADC ready
|
// Wait for ADC ready
|
||||||
while !r.cs().read().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
|
// Setup IRQ
|
||||||
interrupt::ADC_IRQ_FIFO.unpend();
|
interrupt::ADC_IRQ_FIFO.unpend();
|
||||||
@ -80,76 +172,42 @@ impl<'d> Adc<'d> {
|
|||||||
.await;
|
.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();
|
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| {
|
r.cs().modify(|w| {
|
||||||
w.set_ainsel(PIN::channel());
|
w.set_ainsel(channel);
|
||||||
w.set_start_once(true)
|
w.set_start_once(true);
|
||||||
|
w.set_err(true);
|
||||||
});
|
});
|
||||||
Self::wait_for_ready().await;
|
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();
|
let r = Self::regs();
|
||||||
r.cs().modify(|w| w.set_ts_en(true));
|
r.cs().modify(|w| w.set_ts_en(true));
|
||||||
if !r.cs().read().ready() {
|
if !r.cs().read().ready() {
|
||||||
Self::wait_for_ready().await;
|
Self::wait_for_ready().await;
|
||||||
}
|
}
|
||||||
r.cs().modify(|w| {
|
let result = Self::sample_async(4).await;
|
||||||
w.set_ainsel(4);
|
r.cs().modify(|w| w.set_ts_en(false));
|
||||||
w.set_start_once(true)
|
result
|
||||||
});
|
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_pin {
|
impl<'d> Adc<'d, Blocking> {
|
||||||
($pin:ident, $channel:expr) => {
|
pub fn new_blocking(_inner: impl Peripheral<P = ADC> + 'd, _config: Config) -> Self {
|
||||||
impl Channel<Adc<'static>> for peripherals::$pin {
|
Self::setup();
|
||||||
type ID = u8;
|
|
||||||
fn channel() -> u8 {
|
Self { phantom: PhantomData }
|
||||||
$channel
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InterruptHandler {
|
pub struct InterruptHandler {
|
||||||
@ -158,24 +216,33 @@ pub struct InterruptHandler {
|
|||||||
|
|
||||||
impl interrupt::typelevel::Handler<interrupt::typelevel::ADC_IRQ_FIFO> for InterruptHandler {
|
impl interrupt::typelevel::Handler<interrupt::typelevel::ADC_IRQ_FIFO> for InterruptHandler {
|
||||||
unsafe fn on_interrupt() {
|
unsafe fn on_interrupt() {
|
||||||
let r = Adc::regs();
|
let r = Adc::<Async>::regs();
|
||||||
r.inte().write(|w| w.set_fifo(false));
|
r.inte().write(|w| w.set_fifo(false));
|
||||||
WAKER.wake();
|
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_26, 0);
|
||||||
impl_pin!(PIN_27, 1);
|
impl_pin!(PIN_27, 1);
|
||||||
impl_pin!(PIN_28, 2);
|
impl_pin!(PIN_28, 2);
|
||||||
impl_pin!(PIN_29, 3);
|
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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -308,6 +308,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
|
|||||||
// - QSPI (we're using it to run this code!)
|
// - QSPI (we're using it to run this code!)
|
||||||
// - PLLs (it may be suicide if that's what's clocking us)
|
// - PLLs (it may be suicide if that's what's clocking us)
|
||||||
// - USB, SYSCFG (breaks usb-to-swd on core1)
|
// - USB, SYSCFG (breaks usb-to-swd on core1)
|
||||||
|
// - RTC (else there would be no more time...)
|
||||||
let mut peris = reset::ALL_PERIPHERALS;
|
let mut peris = reset::ALL_PERIPHERALS;
|
||||||
peris.set_io_qspi(false);
|
peris.set_io_qspi(false);
|
||||||
// peris.set_io_bank0(false); // might be suicide if we're clocked from gpin
|
// 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
|
// TODO investigate if usb should be unreset here
|
||||||
peris.set_usbctrl(false);
|
peris.set_usbctrl(false);
|
||||||
peris.set_syscfg(false);
|
peris.set_syscfg(false);
|
||||||
|
peris.set_rtc(false);
|
||||||
reset::reset(peris);
|
reset::reset(peris);
|
||||||
|
|
||||||
// Disable resus that may be enabled from previous software
|
// Disable resus that may be enabled from previous software
|
||||||
|
@ -41,7 +41,7 @@ impl From<Level> for bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a pull setting for an input.
|
/// Represents a pull setting for an input.
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
pub enum Pull {
|
pub enum Pull {
|
||||||
None,
|
None,
|
||||||
Up,
|
Up,
|
||||||
@ -566,13 +566,13 @@ impl<'d, T: Pin> Flex<'d, T> {
|
|||||||
/// Is the output level high?
|
/// Is the output level high?
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_set_high(&self) -> bool {
|
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?
|
/// Is the output level low?
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_set_low(&self) -> bool {
|
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
|
/// What level output is set to
|
||||||
|
@ -716,6 +716,9 @@ mod nightly {
|
|||||||
async fn transaction(&mut self, address: A, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> {
|
async fn transaction(&mut self, address: A, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> {
|
||||||
let addr: u16 = address.into();
|
let addr: u16 = address.into();
|
||||||
|
|
||||||
|
if operations.len() > 0 {
|
||||||
|
Self::setup(addr)?;
|
||||||
|
}
|
||||||
let mut iterator = operations.iter_mut();
|
let mut iterator = operations.iter_mut();
|
||||||
|
|
||||||
while let Some(op) = iterator.next() {
|
while let Some(op) = iterator.next() {
|
||||||
@ -723,11 +726,9 @@ mod nightly {
|
|||||||
|
|
||||||
match op {
|
match op {
|
||||||
Operation::Read(buffer) => {
|
Operation::Read(buffer) => {
|
||||||
Self::setup(addr)?;
|
|
||||||
self.read_async_internal(buffer, false, last).await?;
|
self.read_async_internal(buffer, false, last).await?;
|
||||||
}
|
}
|
||||||
Operation::Write(buffer) => {
|
Operation::Write(buffer) => {
|
||||||
Self::setup(addr)?;
|
|
||||||
self.write_async_internal(buffer.into_iter().cloned(), last).await?;
|
self.write_async_internal(buffer.into_iter().cloned(), last).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -252,7 +252,6 @@ pub fn init(config: config::Config) -> Peripherals {
|
|||||||
#[cfg(feature = "time-driver")]
|
#[cfg(feature = "time-driver")]
|
||||||
timer::init();
|
timer::init();
|
||||||
dma::init();
|
dma::init();
|
||||||
pio::init();
|
|
||||||
gpio::init();
|
gpio::init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,12 +16,12 @@ use pio::{SideSet, Wrap};
|
|||||||
use crate::dma::{Channel, Transfer, Word};
|
use crate::dma::{Channel, Transfer, Word};
|
||||||
use crate::gpio::sealed::Pin as SealedPin;
|
use crate::gpio::sealed::Pin as SealedPin;
|
||||||
use crate::gpio::{self, AnyPin, Drive, Level, Pull, SlewRate};
|
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::pac::dma::vals::TreqSel;
|
||||||
use crate::relocate::RelocatedProgram;
|
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 {
|
impl Wakers {
|
||||||
#[inline(always)]
|
#[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)]
|
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
@ -85,42 +81,20 @@ const RXNEMPTY_MASK: u32 = 1 << 0;
|
|||||||
const TXNFULL_MASK: u32 = 1 << 4;
|
const TXNFULL_MASK: u32 = 1 << 4;
|
||||||
const SMIRQ_MASK: u32 = 1 << 8;
|
const SMIRQ_MASK: u32 = 1 << 8;
|
||||||
|
|
||||||
#[cfg(feature = "rt")]
|
pub struct InterruptHandler<PIO: Instance> {
|
||||||
#[interrupt]
|
_pio: PhantomData<PIO>,
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rt")]
|
impl<PIO: Instance> Handler<PIO::Interrupt> for InterruptHandler<PIO> {
|
||||||
#[interrupt]
|
unsafe fn on_interrupt() {
|
||||||
fn PIO1_IRQ_0() {
|
let ints = PIO::PIO.irqs(0).ints().read().0;
|
||||||
use crate::pac;
|
for bit in 0..12 {
|
||||||
let ints = pac::PIO1.irqs(0).ints().read().0;
|
if ints & (1 << bit) != 0 {
|
||||||
for bit in 0..12 {
|
PIO::wakers().0[bit].wake();
|
||||||
if ints & (1 << bit) != 0 {
|
}
|
||||||
WAKERS[1].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
|
/// 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) {
|
if self.get_mut().sm_tx.try_push(value) {
|
||||||
Poll::Ready(())
|
Poll::Ready(())
|
||||||
} else {
|
} 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| {
|
PIO::PIO.irqs(0).inte().write_set(|m| {
|
||||||
m.0 = TXNFULL_MASK << SM;
|
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() {
|
if let Some(v) = self.sm_rx.try_pull() {
|
||||||
Poll::Ready(v)
|
Poll::Ready(v)
|
||||||
} else {
|
} 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| {
|
PIO::PIO.irqs(0).inte().write_set(|m| {
|
||||||
m.0 = RXNEMPTY_MASK << SM;
|
m.0 = RXNEMPTY_MASK << SM;
|
||||||
});
|
});
|
||||||
@ -217,7 +191,7 @@ impl<'a, 'd, PIO: Instance> Future for IrqFuture<'a, 'd, PIO> {
|
|||||||
return Poll::Ready(());
|
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| {
|
PIO::PIO.irqs(0).inte().write_set(|m| {
|
||||||
m.0 = SMIRQ_MASK << self.irq_no;
|
m.0 = SMIRQ_MASK << self.irq_no;
|
||||||
});
|
});
|
||||||
@ -949,9 +923,11 @@ pub struct Pio<'d, PIO: Instance> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, PIO: Instance> Pio<'d, PIO> {
|
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().users.store(5, Ordering::Release);
|
||||||
PIO::state().used_pins.store(0, Ordering::Release);
|
PIO::state().used_pins.store(0, Ordering::Release);
|
||||||
|
PIO::Interrupt::unpend();
|
||||||
|
unsafe { PIO::Interrupt::enable() };
|
||||||
Self {
|
Self {
|
||||||
common: Common {
|
common: Common {
|
||||||
instructions_used: 0,
|
instructions_used: 0,
|
||||||
@ -1017,6 +993,15 @@ mod sealed {
|
|||||||
const PIO_NO: u8;
|
const PIO_NO: u8;
|
||||||
const PIO: &'static crate::pac::pio::Pio;
|
const PIO: &'static crate::pac::pio::Pio;
|
||||||
const FUNCSEL: crate::pac::io::vals::Gpio0ctrlFuncsel;
|
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]
|
#[inline]
|
||||||
fn state() -> &'static State {
|
fn state() -> &'static State {
|
||||||
@ -1033,18 +1018,19 @@ mod sealed {
|
|||||||
pub trait Instance: sealed::Instance + Sized + Unpin {}
|
pub trait Instance: sealed::Instance + Sized + Unpin {}
|
||||||
|
|
||||||
macro_rules! impl_pio {
|
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 {
|
impl sealed::Instance for peripherals::$name {
|
||||||
const PIO_NO: u8 = $pio;
|
const PIO_NO: u8 = $pio;
|
||||||
const PIO: &'static pac::pio::Pio = &pac::$pac;
|
const PIO: &'static pac::pio::Pio = &pac::$pac;
|
||||||
const FUNCSEL: pac::io::vals::Gpio0ctrlFuncsel = pac::io::vals::Gpio0ctrlFuncsel::$funcsel;
|
const FUNCSEL: pac::io::vals::Gpio0ctrlFuncsel = pac::io::vals::Gpio0ctrlFuncsel::$funcsel;
|
||||||
|
type Interrupt = crate::interrupt::typelevel::$irq;
|
||||||
}
|
}
|
||||||
impl Instance for peripherals::$name {}
|
impl Instance for peripherals::$name {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_pio!(PIO0, 0, PIO0, PIO0_0);
|
impl_pio!(PIO0, 0, PIO0, PIO0_0, PIO0_IRQ_0);
|
||||||
impl_pio!(PIO1, 1, PIO1, PIO1_0);
|
impl_pio!(PIO1, 1, PIO1, PIO1_0, PIO1_IRQ_0);
|
||||||
|
|
||||||
pub trait PioPin: sealed::PioPin + gpio::Pin {}
|
pub trait PioPin: sealed::PioPin + gpio::Pin {}
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ pub enum Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Structure containing date and time information
|
/// Structure containing date and time information
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct DateTime {
|
pub struct DateTime {
|
||||||
/// 0..4095
|
/// 0..4095
|
||||||
pub year: u16,
|
pub year: u16,
|
||||||
|
@ -12,26 +12,24 @@ pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError};
|
|||||||
use crate::clocks::clk_rtc_freq;
|
use crate::clocks::clk_rtc_freq;
|
||||||
|
|
||||||
/// A reference to the real time clock of the system
|
/// 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>,
|
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.
|
/// Create a new instance of the real time clock, with the given date as an initial value.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.
|
/// 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);
|
into_ref!(inner);
|
||||||
|
|
||||||
// Set the RTC divider
|
// Set the RTC divider
|
||||||
inner.regs().clkdiv_m1().write(|w| w.set_clkdiv_m1(clk_rtc_freq() - 1));
|
inner.regs().clkdiv_m1().write(|w| w.set_clkdiv_m1(clk_rtc_freq() - 1));
|
||||||
|
|
||||||
let mut result = Self { inner };
|
let result = Self { inner };
|
||||||
result.set_leap_year_check(true); // should be on by default, make sure this is the case.
|
result
|
||||||
result.set_datetime(initial_date)?;
|
|
||||||
Ok(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.
|
/// 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 {
|
pub fn is_running(&self) -> bool {
|
||||||
self.inner.regs().ctrl().read().rtc_active()
|
self.inner.regs().ctrl().read().rtc_active()
|
||||||
}
|
}
|
||||||
@ -113,8 +141,8 @@ impl<'d, T: Instance> RealTimeClock<'d, T> {
|
|||||||
/// # fn main() { }
|
/// # fn main() { }
|
||||||
/// # #[cfg(not(feature = "chrono"))]
|
/// # #[cfg(not(feature = "chrono"))]
|
||||||
/// # fn main() {
|
/// # fn main() {
|
||||||
/// # use embassy_rp::rtc::{RealTimeClock, DateTimeFilter};
|
/// # use embassy_rp::rtc::{Rtc, DateTimeFilter};
|
||||||
/// # let mut real_time_clock: RealTimeClock<embassy_rp::peripherals::RTC> = unsafe { core::mem::zeroed() };
|
/// # let mut real_time_clock: Rtc<embassy_rp::peripherals::RTC> = unsafe { core::mem::zeroed() };
|
||||||
/// let now = real_time_clock.now().unwrap();
|
/// let now = real_time_clock.now().unwrap();
|
||||||
/// real_time_clock.schedule_alarm(
|
/// real_time_clock.schedule_alarm(
|
||||||
/// DateTimeFilter::default()
|
/// 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)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum RtcError {
|
pub enum RtcError {
|
||||||
/// An invalid DateTime was given or stored on the hardware.
|
/// An invalid DateTime was given or stored on the hardware.
|
||||||
|
@ -545,25 +545,19 @@ mod eh1 {
|
|||||||
type Error = Error;
|
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> {
|
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||||
Ok(())
|
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> {
|
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
self.blocking_transfer(words, &[])
|
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> {
|
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
|
||||||
self.blocking_write(words)
|
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> {
|
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
|
||||||
self.blocking_transfer(read, write)
|
self.blocking_transfer(read, write)
|
||||||
}
|
}
|
||||||
@ -578,30 +572,24 @@ mod eh1 {
|
|||||||
mod eha {
|
mod eha {
|
||||||
use super::*;
|
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> {
|
async fn flush(&mut self) -> Result<(), Self::Error> {
|
||||||
Ok(())
|
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> {
|
async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
|
||||||
self.write(words).await
|
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> {
|
async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
self.read(words).await
|
self.read(words).await
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance> embedded_hal_async::spi::SpiBus<u8> for Spi<'d, T, Async> {
|
async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
|
||||||
async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> {
|
|
||||||
self.transfer(read, write).await
|
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
|
self.transfer_in_place(words).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -361,6 +361,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
|||||||
|
|
||||||
let regs = T::regs();
|
let regs = T::regs();
|
||||||
let siestatus = regs.sie_status().read();
|
let siestatus = regs.sie_status().read();
|
||||||
|
let intrstatus = regs.intr().read();
|
||||||
|
|
||||||
if siestatus.resume() {
|
if siestatus.resume() {
|
||||||
regs.sie_status().write(|w| w.set_resume(true));
|
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);
|
return Poll::Ready(Event::Reset);
|
||||||
}
|
}
|
||||||
|
|
||||||
if siestatus.suspended() {
|
if siestatus.suspended() && intrstatus.dev_suspend() {
|
||||||
regs.sie_status().write(|w| w.set_suspended(true));
|
regs.sie_status().write(|w| w.set_suspended(true));
|
||||||
return Poll::Ready(Event::Suspend);
|
return Poll::Ready(Event::Suspend);
|
||||||
}
|
}
|
||||||
|
@ -107,4 +107,36 @@ impl Watchdog {
|
|||||||
w.set_trigger(true);
|
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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,18 +5,19 @@ edition = "2021"
|
|||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-wpan-v$VERSION/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"
|
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32-wpan/src/"
|
||||||
target = "thumbv7em-none-eabihf"
|
target = "thumbv7em-none-eabihf"
|
||||||
features = ["stm32wb55rg"]
|
features = ["stm32wb55rg"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32" }
|
embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32" }
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
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-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||||
embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" }
|
embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" }
|
||||||
embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" }
|
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 }
|
defmt = { version = "0.3", optional = true }
|
||||||
cortex-m = "0.7.6"
|
cortex-m = "0.7.6"
|
||||||
@ -25,13 +26,15 @@ aligned = "0.4.1"
|
|||||||
|
|
||||||
bit_field = "0.10.2"
|
bit_field = "0.10.2"
|
||||||
stm32-device-signature = { version = "0.3.3", features = ["stm32wb5x"] }
|
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]
|
[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"]
|
ble = ["dep:stm32wb-hci"]
|
||||||
mac = []
|
mac = ["dep:bitflags", "dep:embassy-net-driver" ]
|
||||||
|
|
||||||
stm32wb10cc = [ "embassy-stm32/stm32wb10cc" ]
|
stm32wb10cc = [ "embassy-stm32/stm32wb10cc" ]
|
||||||
stm32wb15cc = [ "embassy-stm32/stm32wb15cc" ]
|
stm32wb15cc = [ "embassy-stm32/stm32wb15cc" ]
|
||||||
@ -48,4 +51,4 @@ stm32wb55rg = [ "embassy-stm32/stm32wb55rg" ]
|
|||||||
stm32wb55vc = [ "embassy-stm32/stm32wb55vc" ]
|
stm32wb55vc = [ "embassy-stm32/stm32wb55vc" ]
|
||||||
stm32wb55ve = [ "embassy-stm32/stm32wb55ve" ]
|
stm32wb55ve = [ "embassy-stm32/stm32wb55ve" ]
|
||||||
stm32wb55vg = [ "embassy-stm32/stm32wb55vg" ]
|
stm32wb55vg = [ "embassy-stm32/stm32wb55vg" ]
|
||||||
stm32wb55vy = [ "embassy-stm32/stm32wb55vy" ]
|
stm32wb55vy = [ "embassy-stm32/stm32wb55vy" ]
|
||||||
|
@ -37,7 +37,7 @@ pub struct CmdSerialStub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Default)]
|
#[derive(Copy, Clone, Default)]
|
||||||
#[repr(C, packed(4))]
|
#[repr(C, packed)]
|
||||||
pub struct CmdPacket {
|
pub struct CmdPacket {
|
||||||
pub header: PacketHeader,
|
pub header: PacketHeader,
|
||||||
pub cmdserial: CmdSerial,
|
pub cmdserial: CmdSerial,
|
||||||
|
@ -6,6 +6,8 @@ use crate::PacketHeader;
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub enum TlPacketType {
|
pub enum TlPacketType {
|
||||||
|
MacCmd = 0x00,
|
||||||
|
|
||||||
BleCmd = 0x01,
|
BleCmd = 0x01,
|
||||||
AclData = 0x02,
|
AclData = 0x02,
|
||||||
BleEvt = 0x04,
|
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 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 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 {
|
pub const fn divc(x: usize, y: usize) -> usize {
|
||||||
(x + y - 1) / y
|
(x + y - 1) / y
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#![no_std]
|
#![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.
|
// This must go FIRST so that all the other modules see its macros.
|
||||||
pub mod fmt;
|
pub mod fmt;
|
||||||
@ -26,6 +27,9 @@ pub mod sub;
|
|||||||
pub mod tables;
|
pub mod tables;
|
||||||
pub mod unsafe_linked_list;
|
pub mod unsafe_linked_list;
|
||||||
|
|
||||||
|
#[cfg(feature = "mac")]
|
||||||
|
pub mod mac;
|
||||||
|
|
||||||
#[cfg(feature = "ble")]
|
#[cfg(feature = "ble")]
|
||||||
pub use crate::sub::ble::hci;
|
pub use crate::sub::ble::hci;
|
||||||
|
|
||||||
@ -60,9 +64,9 @@ impl<'d> TlMbox<'d> {
|
|||||||
mem_manager_table: TL_MEM_MANAGER_TABLE.as_ptr(),
|
mem_manager_table: TL_MEM_MANAGER_TABLE.as_ptr(),
|
||||||
traces_table: TL_TRACES_TABLE.as_ptr(),
|
traces_table: TL_TRACES_TABLE.as_ptr(),
|
||||||
mac_802_15_4_table: TL_MAC_802_15_4_TABLE.as_ptr(),
|
mac_802_15_4_table: TL_MAC_802_15_4_TABLE.as_ptr(),
|
||||||
// zigbee_table: TL_ZIGBEE_TABLE.as_ptr(),
|
zigbee_table: TL_ZIGBEE_TABLE.as_ptr(),
|
||||||
// lld_tests_table: TL_LLD_TESTS_TABLE.as_ptr(),
|
lld_tests_table: TL_LLD_TESTS_TABLE.as_ptr(),
|
||||||
// ble_lld_table: TL_BLE_LLD_TABLE.as_ptr(),
|
ble_lld_table: TL_BLE_LLD_TABLE.as_ptr(),
|
||||||
});
|
});
|
||||||
|
|
||||||
TL_SYS_TABLE
|
TL_SYS_TABLE
|
||||||
@ -87,15 +91,15 @@ impl<'d> TlMbox<'d> {
|
|||||||
TL_MAC_802_15_4_TABLE
|
TL_MAC_802_15_4_TABLE
|
||||||
.as_mut_ptr()
|
.as_mut_ptr()
|
||||||
.write_volatile(MaybeUninit::zeroed().assume_init());
|
.write_volatile(MaybeUninit::zeroed().assume_init());
|
||||||
// TL_ZIGBEE_TABLE
|
TL_ZIGBEE_TABLE
|
||||||
// .as_mut_ptr()
|
.as_mut_ptr()
|
||||||
// .write_volatile(MaybeUninit::zeroed().assume_init());
|
.write_volatile(MaybeUninit::zeroed().assume_init());
|
||||||
// TL_LLD_TESTS_TABLE
|
TL_LLD_TESTS_TABLE
|
||||||
// .as_mut_ptr()
|
.as_mut_ptr()
|
||||||
// .write_volatile(MaybeUninit::zeroed().assume_init());
|
.write_volatile(MaybeUninit::zeroed().assume_init());
|
||||||
// TL_BLE_LLD_TABLE
|
TL_BLE_LLD_TABLE
|
||||||
// .as_mut_ptr()
|
.as_mut_ptr()
|
||||||
// .write_volatile(MaybeUninit::zeroed().assume_init());
|
.write_volatile(MaybeUninit::zeroed().assume_init());
|
||||||
|
|
||||||
EVT_POOL
|
EVT_POOL
|
||||||
.as_mut_ptr()
|
.as_mut_ptr()
|
||||||
@ -103,18 +107,30 @@ impl<'d> TlMbox<'d> {
|
|||||||
SYS_SPARE_EVT_BUF
|
SYS_SPARE_EVT_BUF
|
||||||
.as_mut_ptr()
|
.as_mut_ptr()
|
||||||
.write_volatile(MaybeUninit::zeroed().assume_init());
|
.write_volatile(MaybeUninit::zeroed().assume_init());
|
||||||
BLE_SPARE_EVT_BUF
|
CS_BUFFER
|
||||||
.as_mut_ptr()
|
.as_mut_ptr()
|
||||||
.write_volatile(MaybeUninit::zeroed().assume_init());
|
.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
|
BLE_CMD_BUFFER
|
||||||
.as_mut_ptr()
|
.as_mut_ptr()
|
||||||
.write_volatile(MaybeUninit::zeroed().assume_init());
|
.write_volatile(MaybeUninit::zeroed().assume_init());
|
||||||
HCI_ACL_DATA_BUFFER
|
HCI_ACL_DATA_BUFFER
|
||||||
.as_mut_ptr()
|
.as_mut_ptr()
|
||||||
.write_volatile(MaybeUninit::zeroed().assume_init());
|
.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()
|
.as_mut_ptr()
|
||||||
.write_volatile(MaybeUninit::zeroed().assume_init());
|
.write_volatile(MaybeUninit::zeroed().assume_init());
|
||||||
}
|
}
|
||||||
|
476
embassy-stm32-wpan/src/mac/commands.rs
Normal file
476
embassy-stm32-wpan/src/mac/commands.rs
Normal 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;
|
||||||
|
}
|
4
embassy-stm32-wpan/src/mac/consts.rs
Normal file
4
embassy-stm32-wpan/src/mac/consts.rs
Normal 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;
|
95
embassy-stm32-wpan/src/mac/control.rs
Normal file
95
embassy-stm32-wpan/src/mac/control.rs
Normal 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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
122
embassy-stm32-wpan/src/mac/driver.rs
Normal file
122
embassy-stm32-wpan/src/mac/driver.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
153
embassy-stm32-wpan/src/mac/event.rs
Normal file
153
embassy-stm32-wpan/src/mac/event.rs
Normal 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()) };
|
||||||
|
}
|
||||||
|
}
|
265
embassy-stm32-wpan/src/mac/indications.rs
Normal file
265
embassy-stm32-wpan/src/mac/indications.rs
Normal 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 {}
|
32
embassy-stm32-wpan/src/mac/macros.rs
Normal file
32
embassy-stm32-wpan/src/mac/macros.rs
Normal 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,)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
embassy-stm32-wpan/src/mac/mod.rs
Normal file
21
embassy-stm32-wpan/src/mac/mod.rs
Normal 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))
|
||||||
|
}
|
92
embassy-stm32-wpan/src/mac/opcodes.rs
Normal file
92
embassy-stm32-wpan/src/mac/opcodes.rs
Normal 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(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
273
embassy-stm32-wpan/src/mac/responses.rs
Normal file
273
embassy-stm32-wpan/src/mac/responses.rs
Normal 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 {}
|
109
embassy-stm32-wpan/src/mac/runner.rs
Normal file
109
embassy-stm32-wpan/src/mac/runner.rs
Normal 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 {}
|
||||||
|
}
|
||||||
|
}
|
381
embassy-stm32-wpan/src/mac/typedefs.rs
Normal file
381
embassy-stm32-wpan/src/mac/typedefs.rs
Normal 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]);
|
||||||
|
}
|
@ -11,9 +11,10 @@ use embassy_sync::waitqueue::AtomicWaker;
|
|||||||
use crate::cmd::CmdPacket;
|
use crate::cmd::CmdPacket;
|
||||||
use crate::consts::TlPacketType;
|
use crate::consts::TlPacketType;
|
||||||
use crate::evt::{EvtBox, EvtPacket};
|
use crate::evt::{EvtBox, EvtPacket};
|
||||||
use crate::tables::{
|
use crate::mac::commands::MacCommand;
|
||||||
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::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};
|
use crate::{channels, evt};
|
||||||
|
|
||||||
static MAC_WAKER: AtomicWaker = AtomicWaker::new();
|
static MAC_WAKER: AtomicWaker = AtomicWaker::new();
|
||||||
@ -25,21 +26,13 @@ pub struct Mac {
|
|||||||
|
|
||||||
impl Mac {
|
impl Mac {
|
||||||
pub(crate) fn new() -> Self {
|
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 }
|
Self { phantom: PhantomData }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `HW_IPCC_MAC_802_15_4_EvtNot`
|
/// `HW_IPCC_MAC_802_15_4_EvtNot`
|
||||||
///
|
///
|
||||||
/// This function will stall if the previous `EvtBox` has not been dropped
|
/// 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
|
// Wait for the last event box to be dropped
|
||||||
poll_fn(|cx| {
|
poll_fn(|cx| {
|
||||||
MAC_WAKER.register(cx.waker());
|
MAC_WAKER.register(cx.waker());
|
||||||
@ -63,9 +56,9 @@ impl Mac {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// `HW_IPCC_MAC_802_15_4_CmdEvtNot`
|
/// `HW_IPCC_MAC_802_15_4_CmdEvtNot`
|
||||||
pub async fn write_and_get_response(&self, opcode: u16, payload: &[u8]) -> u8 {
|
pub async fn tl_write_and_get_response(&self, opcode: u16, payload: &[u8]) -> u8 {
|
||||||
self.write(opcode, payload).await;
|
self.tl_write(opcode, payload).await;
|
||||||
Ipcc::flush(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL).await;
|
Ipcc::flush(channels::cpu1::IPCC_MAC_802_15_4_CMD_RSP_CHANNEL).await;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let p_event_packet = MAC_802_15_4_CMD_BUFFER.as_ptr() as *const EvtPacket;
|
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`
|
/// `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 {
|
Ipcc::send(channels::cpu1::IPCC_MAC_802_15_4_CMD_RSP_CHANNEL, || unsafe {
|
||||||
CmdPacket::write_into(
|
CmdPacket::write_into(
|
||||||
MAC_802_15_4_CMD_BUFFER.as_mut_ptr(),
|
MAC_802_15_4_CMD_BUFFER.as_mut_ptr(),
|
||||||
TlPacketType::OtCmd,
|
TlPacketType::MacCmd,
|
||||||
opcode,
|
opcode,
|
||||||
payload,
|
payload,
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.await;
|
.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 {
|
impl evt::MemoryManager for Mac {
|
||||||
/// SAFETY: passing a pointer to something other than a managed event packet is UB
|
/// SAFETY: passing a pointer to something other than a managed event packet is UB
|
||||||
unsafe fn drop_event_packet(_: *mut EvtPacket) {
|
unsafe fn drop_event_packet(_: *mut EvtPacket) {
|
||||||
|
trace!("mac drop event");
|
||||||
|
|
||||||
// Write the ack
|
// Write the ack
|
||||||
CmdPacket::write_into(
|
CmdPacket::write_into(
|
||||||
MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _,
|
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
|
// 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,
|
channels::cpu2::IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL,
|
||||||
|| None,
|
|| None,
|
||||||
));
|
));
|
||||||
|
@ -4,20 +4,21 @@ use core::marker::PhantomData;
|
|||||||
use core::mem::MaybeUninit;
|
use core::mem::MaybeUninit;
|
||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
|
|
||||||
|
use aligned::{Aligned, A4};
|
||||||
use cortex_m::interrupt;
|
use cortex_m::interrupt;
|
||||||
use embassy_stm32::ipcc::Ipcc;
|
use embassy_stm32::ipcc::Ipcc;
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
|
||||||
use crate::consts::POOL_SIZE;
|
use crate::consts::POOL_SIZE;
|
||||||
use crate::evt::EvtPacket;
|
use crate::evt::EvtPacket;
|
||||||
use crate::tables::{
|
#[cfg(feature = "ble")]
|
||||||
MemManagerTable, BLE_SPARE_EVT_BUF, EVT_POOL, FREE_BUF_QUEUE, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE,
|
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::unsafe_linked_list::LinkedListNode;
|
||||||
use crate::{channels, evt};
|
use crate::{channels, evt};
|
||||||
|
|
||||||
static MM_WAKER: AtomicWaker = AtomicWaker::new();
|
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 {
|
pub struct MemoryManager {
|
||||||
phantom: PhantomData<MemoryManager>,
|
phantom: PhantomData<MemoryManager>,
|
||||||
@ -30,7 +31,10 @@ impl MemoryManager {
|
|||||||
LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr());
|
LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr());
|
||||||
|
|
||||||
TL_MEM_MANAGER_TABLE.as_mut_ptr().write_volatile(MemManagerTable {
|
TL_MEM_MANAGER_TABLE.as_mut_ptr().write_volatile(MemManagerTable {
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
spare_ble_buffer: BLE_SPARE_EVT_BUF.as_ptr().cast(),
|
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(),
|
spare_sys_buffer: SYS_SPARE_EVT_BUF.as_ptr().cast(),
|
||||||
blepool: EVT_POOL.as_ptr().cast(),
|
blepool: EVT_POOL.as_ptr().cast(),
|
||||||
blepoolsize: POOL_SIZE as u32,
|
blepoolsize: POOL_SIZE as u32,
|
||||||
|
@ -50,7 +50,7 @@ impl Sys {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// `HW_IPCC_SYS_CmdEvtNot`
|
/// `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;
|
self.write(opcode, payload).await;
|
||||||
Ipcc::flush(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL).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_command_event = &((*p_event_packet).evt_serial.evt.payload) as *const _ as *const CcEvt;
|
||||||
let p_payload = &((*p_command_event).payload) as *const u8;
|
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")]
|
#[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
|
self.write_and_get_response(ShciOpcode::Mac802_15_4Init, &[]).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ble")]
|
#[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
|
self.write_and_get_response(ShciOpcode::BleInit, param.payload()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ use aligned::{Aligned, A4};
|
|||||||
use bit_field::BitField;
|
use bit_field::BitField;
|
||||||
|
|
||||||
use crate::cmd::{AclDataPacket, CmdPacket};
|
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::consts::{POOL_SIZE, TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE};
|
||||||
use crate::unsafe_linked_list::LinkedListNode;
|
use crate::unsafe_linked_list::LinkedListNode;
|
||||||
|
|
||||||
@ -80,7 +82,7 @@ impl WirelessFwInfoTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[repr(C, align(4))]
|
#[repr(C)]
|
||||||
pub struct DeviceInfoTable {
|
pub struct DeviceInfoTable {
|
||||||
pub safe_boot_info_table: SafeBootInfoTable,
|
pub safe_boot_info_table: SafeBootInfoTable,
|
||||||
pub rss_info_table: RssInfoTable,
|
pub rss_info_table: RssInfoTable,
|
||||||
@ -88,7 +90,7 @@ pub struct DeviceInfoTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[repr(C, align(4))]
|
#[repr(C)]
|
||||||
pub struct BleTable {
|
pub struct BleTable {
|
||||||
pub pcmd_buffer: *mut CmdPacket,
|
pub pcmd_buffer: *mut CmdPacket,
|
||||||
pub pcs_buffer: *const u8,
|
pub pcs_buffer: *const u8,
|
||||||
@ -97,16 +99,15 @@ pub struct BleTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[repr(C, align(4))]
|
#[repr(C)]
|
||||||
pub struct ThreadTable {
|
pub struct ThreadTable {
|
||||||
pub nostack_buffer: *const u8,
|
pub nostack_buffer: *const u8,
|
||||||
pub clicmdrsp_buffer: *const u8,
|
pub clicmdrsp_buffer: *const u8,
|
||||||
pub otcmdrsp_buffer: *const u8,
|
pub otcmdrsp_buffer: *const u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use later
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[repr(C, align(4))]
|
#[repr(C)]
|
||||||
pub struct LldTestsTable {
|
pub struct LldTestsTable {
|
||||||
pub clicmdrsp_buffer: *const u8,
|
pub clicmdrsp_buffer: *const u8,
|
||||||
pub m0cmd_buffer: *const u8,
|
pub m0cmd_buffer: *const u8,
|
||||||
@ -114,7 +115,7 @@ pub struct LldTestsTable {
|
|||||||
|
|
||||||
// TODO: use later
|
// TODO: use later
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[repr(C, align(4))]
|
#[repr(C)]
|
||||||
pub struct BleLldTable {
|
pub struct BleLldTable {
|
||||||
pub cmdrsp_buffer: *const u8,
|
pub cmdrsp_buffer: *const u8,
|
||||||
pub m0cmd_buffer: *const u8,
|
pub m0cmd_buffer: *const u8,
|
||||||
@ -122,7 +123,7 @@ pub struct BleLldTable {
|
|||||||
|
|
||||||
// TODO: use later
|
// TODO: use later
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[repr(C, align(4))]
|
#[repr(C)]
|
||||||
pub struct ZigbeeTable {
|
pub struct ZigbeeTable {
|
||||||
pub notif_m0_to_m4_buffer: *const u8,
|
pub notif_m0_to_m4_buffer: *const u8,
|
||||||
pub appli_cmd_m4_to_m0_bufer: *const u8,
|
pub appli_cmd_m4_to_m0_bufer: *const u8,
|
||||||
@ -130,14 +131,14 @@ pub struct ZigbeeTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[repr(C, align(4))]
|
#[repr(C)]
|
||||||
pub struct SysTable {
|
pub struct SysTable {
|
||||||
pub pcmd_buffer: *mut CmdPacket,
|
pub pcmd_buffer: *mut CmdPacket,
|
||||||
pub sys_queue: *const LinkedListNode,
|
pub sys_queue: *const LinkedListNode,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[repr(C, align(4))]
|
#[repr(C)]
|
||||||
pub struct MemManagerTable {
|
pub struct MemManagerTable {
|
||||||
pub spare_ble_buffer: *const u8,
|
pub spare_ble_buffer: *const u8,
|
||||||
pub spare_sys_buffer: *const u8,
|
pub spare_sys_buffer: *const u8,
|
||||||
@ -152,13 +153,13 @@ pub struct MemManagerTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[repr(C, align(4))]
|
#[repr(C)]
|
||||||
pub struct TracesTable {
|
pub struct TracesTable {
|
||||||
pub traces_queue: *const u8,
|
pub traces_queue: *const u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[repr(C, align(4))]
|
#[repr(C)]
|
||||||
pub struct Mac802_15_4Table {
|
pub struct Mac802_15_4Table {
|
||||||
pub p_cmdrsp_buffer: *const u8,
|
pub p_cmdrsp_buffer: *const u8,
|
||||||
pub p_notack_buffer: *const u8,
|
pub p_notack_buffer: *const u8,
|
||||||
@ -176,6 +177,9 @@ pub struct RefTable {
|
|||||||
pub mem_manager_table: *const MemManagerTable,
|
pub mem_manager_table: *const MemManagerTable,
|
||||||
pub traces_table: *const TracesTable,
|
pub traces_table: *const TracesTable,
|
||||||
pub mac_802_15_4_table: *const Mac802_15_4Table,
|
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 ---------------------
|
// --------------------- ref table ---------------------
|
||||||
@ -183,57 +187,57 @@ pub struct RefTable {
|
|||||||
pub static mut TL_REF_TABLE: MaybeUninit<RefTable> = MaybeUninit::uninit();
|
pub static mut TL_REF_TABLE: MaybeUninit<RefTable> = MaybeUninit::uninit();
|
||||||
|
|
||||||
#[link_section = "MB_MEM1"]
|
#[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"]
|
#[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"]
|
#[link_section = "MB_MEM1"]
|
||||||
pub static mut TL_THREAD_TABLE: MaybeUninit<ThreadTable> = MaybeUninit::uninit();
|
pub static mut TL_THREAD_TABLE: Aligned<A4, MaybeUninit<ThreadTable>> = Aligned(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();
|
|
||||||
|
|
||||||
#[link_section = "MB_MEM1"]
|
#[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"]
|
#[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"]
|
#[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"]
|
#[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"]
|
#[link_section = "MB_MEM1"]
|
||||||
// pub static mut TL_ZIGBEE_TABLE: MaybeUninit<ZigbeeTable> = MaybeUninit::uninit();
|
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 ---------------------
|
// --------------------- tables ---------------------
|
||||||
#[link_section = "MB_MEM1"]
|
#[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)]
|
#[allow(dead_code)]
|
||||||
#[link_section = "MB_MEM1"]
|
#[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"]
|
#[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]>> =
|
pub static mut CS_BUFFER: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE]>> =
|
||||||
MaybeUninit::uninit();
|
Aligned(MaybeUninit::uninit());
|
||||||
|
|
||||||
#[link_section = "MB_MEM2"]
|
#[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"]
|
#[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 ---------------------
|
// --------------------- app tables ---------------------
|
||||||
#[cfg(feature = "mac")]
|
#[cfg(feature = "mac")]
|
||||||
#[link_section = "MB_MEM2"]
|
#[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")]
|
#[cfg(feature = "mac")]
|
||||||
#[link_section = "MB_MEM2"]
|
#[link_section = "MB_MEM2"]
|
||||||
@ -242,23 +246,31 @@ pub static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit<
|
|||||||
> = MaybeUninit::uninit();
|
> = MaybeUninit::uninit();
|
||||||
|
|
||||||
#[link_section = "MB_MEM2"]
|
#[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"]
|
#[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"]
|
#[link_section = "MB_MEM2"]
|
||||||
pub static mut SYS_SPARE_EVT_BUF: MaybeUninit<Aligned<A4, [u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> =
|
pub static mut SYS_SPARE_EVT_BUF: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> =
|
||||||
MaybeUninit::uninit();
|
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"]
|
#[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"]
|
#[link_section = "MB_MEM2"]
|
||||||
pub static mut BLE_SPARE_EVT_BUF: MaybeUninit<Aligned<A4, [u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> =
|
pub static mut BLE_SPARE_EVT_BUF: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> =
|
||||||
MaybeUninit::uninit();
|
Aligned(MaybeUninit::uninit());
|
||||||
|
|
||||||
|
#[cfg(feature = "ble")]
|
||||||
#[link_section = "MB_MEM2"]
|
#[link_section = "MB_MEM2"]
|
||||||
// fuck these "magic" numbers from ST ---v---v
|
// 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]>> =
|
pub static mut HCI_ACL_DATA_BUFFER: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]>> =
|
||||||
MaybeUninit::uninit();
|
Aligned(MaybeUninit::uninit());
|
||||||
|
@ -32,7 +32,7 @@ flavors = [
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
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-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-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" }
|
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 }
|
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-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-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true}
|
||||||
embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true}
|
embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true}
|
||||||
embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true}
|
embedded-hal-nb = { version = "=1.0.0-alpha.3", optional = true}
|
||||||
|
|
||||||
embedded-storage = "0.3.0"
|
embedded-storage = "0.3.0"
|
||||||
embedded-storage-async = { version = "0.4.0", optional = true }
|
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 }
|
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
|
||||||
critical-section = "1.1"
|
critical-section = "1.1"
|
||||||
atomic-polyfill = "1.0.1"
|
atomic-polyfill = "1.0.1"
|
||||||
stm32-metapac = "12"
|
stm32-metapac = "13"
|
||||||
vcell = "0.1.3"
|
vcell = "0.1.3"
|
||||||
bxcan = "0.7.0"
|
bxcan = "0.7.0"
|
||||||
nb = "1.0.0"
|
nb = "1.0.0"
|
||||||
@ -74,7 +74,7 @@ critical-section = { version = "1.1", features = ["std"] }
|
|||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
proc-macro2 = "1.0.36"
|
proc-macro2 = "1.0.36"
|
||||||
quote = "1.0.15"
|
quote = "1.0.15"
|
||||||
stm32-metapac = { version = "12", default-features = false, features = ["metadata"]}
|
stm32-metapac = { version = "13", default-features = false, features = ["metadata"]}
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["rt"]
|
default = ["rt"]
|
||||||
|
@ -348,9 +348,7 @@ fn main() {
|
|||||||
g.extend(quote! {
|
g.extend(quote! {
|
||||||
impl crate::rcc::sealed::RccPeripheral for peripherals::#pname {
|
impl crate::rcc::sealed::RccPeripheral for peripherals::#pname {
|
||||||
fn frequency() -> crate::time::Hertz {
|
fn frequency() -> crate::time::Hertz {
|
||||||
critical_section::with(|_| unsafe {
|
unsafe { crate::rcc::get_freqs().#clk }
|
||||||
crate::rcc::get_freqs().#clk
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
fn enable() {
|
fn enable() {
|
||||||
critical_section::with(|_| {
|
critical_section::with(|_| {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
|
|
||||||
|
#[cfg(not(adc_f3))]
|
||||||
#[cfg_attr(adc_f1, path = "f1.rs")]
|
#[cfg_attr(adc_f1, path = "f1.rs")]
|
||||||
#[cfg_attr(adc_v1, path = "v1.rs")]
|
#[cfg_attr(adc_v1, path = "v1.rs")]
|
||||||
#[cfg_attr(adc_v2, path = "v2.rs")]
|
#[cfg_attr(adc_v2, path = "v2.rs")]
|
||||||
@ -7,14 +8,16 @@
|
|||||||
#[cfg_attr(adc_v4, path = "v4.rs")]
|
#[cfg_attr(adc_v4, path = "v4.rs")]
|
||||||
mod _version;
|
mod _version;
|
||||||
|
|
||||||
#[cfg(not(adc_f1))]
|
#[cfg(not(any(adc_f1, adc_f3)))]
|
||||||
mod resolution;
|
mod resolution;
|
||||||
mod sample_time;
|
mod sample_time;
|
||||||
|
|
||||||
|
#[cfg(not(adc_f3))]
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub use _version::*;
|
pub use _version::*;
|
||||||
#[cfg(not(adc_f1))]
|
#[cfg(not(any(adc_f1, adc_f3)))]
|
||||||
pub use resolution::Resolution;
|
pub use resolution::Resolution;
|
||||||
|
#[cfg(not(adc_f3))]
|
||||||
pub use sample_time::SampleTime;
|
pub use sample_time::SampleTime;
|
||||||
|
|
||||||
use crate::peripherals;
|
use crate::peripherals;
|
||||||
@ -22,13 +25,14 @@ use crate::peripherals;
|
|||||||
pub struct Adc<'d, T: Instance> {
|
pub struct Adc<'d, T: Instance> {
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
adc: crate::PeripheralRef<'d, T>,
|
adc: crate::PeripheralRef<'d, T>,
|
||||||
|
#[cfg(not(adc_f3))]
|
||||||
sample_time: SampleTime,
|
sample_time: SampleTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) mod sealed {
|
pub(crate) mod sealed {
|
||||||
pub trait Instance {
|
pub trait Instance {
|
||||||
fn regs() -> crate::pac::adc::Adc;
|
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;
|
fn common_regs() -> crate::pac::adccommon::AdcCommon;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +60,7 @@ foreach_peripheral!(
|
|||||||
fn regs() -> crate::pac::adc::Adc {
|
fn regs() -> crate::pac::adc::Adc {
|
||||||
crate::pac::$inst
|
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 {
|
fn common_regs() -> crate::pac::adccommon::AdcCommon {
|
||||||
foreach_peripheral!{
|
foreach_peripheral!{
|
||||||
(adccommon, $common_inst:ident) => {
|
(adccommon, $common_inst:ident) => {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#[cfg(not(adc_f3))]
|
||||||
macro_rules! impl_sample_time {
|
macro_rules! impl_sample_time {
|
||||||
($default_doc:expr, $default:ident, ($(($doc:expr, $variant:ident, $pac_variant:ident)),*)) => {
|
($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.")]
|
#[doc = concat!("ADC sample time\n\nThe default setting is ", $default_doc, " ADC clock cycles.")]
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use core::cell::{RefCell, RefMut};
|
||||||
use core::future::poll_fn;
|
use core::future::poll_fn;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::ops::{Deref, DerefMut};
|
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> {
|
pub struct Can<'d, T: Instance> {
|
||||||
can: bxcan::Can<BxcanInstance<'d, T>>,
|
pub can: RefCell<bxcan::Can<BxcanInstance<'d, T>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -147,19 +148,24 @@ impl<'d, T: Instance> Can<'d, T> {
|
|||||||
tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
|
tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
|
||||||
|
|
||||||
let can = bxcan::Can::builder(BxcanInstance(peri)).leave_disabled();
|
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) {
|
pub fn set_bitrate(&mut self, bitrate: u32) {
|
||||||
let bit_timing = Self::calc_bxcan_timings(T::frequency(), bitrate).unwrap();
|
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
|
/// Queues the message to be sent but exerts backpressure
|
||||||
pub async fn write(&mut self, frame: &Frame) -> bxcan::TransmitStatus {
|
pub async fn write(&mut self, frame: &Frame) -> bxcan::TransmitStatus {
|
||||||
poll_fn(|cx| {
|
poll_fn(|cx| {
|
||||||
T::state().tx_waker.register(cx.waker());
|
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);
|
return Poll::Ready(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -341,6 +347,79 @@ impl<'d, T: Instance> Can<'d, T> {
|
|||||||
// Pack into BTR register values
|
// Pack into BTR register values
|
||||||
Some((sjw - 1) << 24 | (bs1 as u32 - 1) << 16 | (bs2 as u32 - 1) << 20 | (prescaler as u32 - 1))
|
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 {
|
enum RxFifo {
|
||||||
@ -358,7 +437,7 @@ impl<'d, T: Instance> Drop for Can<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> Deref 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 {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.can
|
&self.can
|
||||||
|
66
embassy-stm32/src/can/fdcan.rs
Normal file
66
embassy-stm32/src/can/fdcan.rs
Normal 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);
|
@ -1,5 +1,6 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
|
|
||||||
#[cfg_attr(can_bxcan, path = "bxcan.rs")]
|
#[cfg_attr(can_bxcan, path = "bxcan.rs")]
|
||||||
|
#[cfg_attr(can_fdcan, path = "fdcan.rs")]
|
||||||
mod _version;
|
mod _version;
|
||||||
pub use _version::*;
|
pub use _version::*;
|
||||||
|
@ -51,7 +51,10 @@ impl Ch1Trigger {
|
|||||||
fn tsel(&self) -> dac::vals::Tsel1 {
|
fn tsel(&self) -> dac::vals::Tsel1 {
|
||||||
match self {
|
match self {
|
||||||
Ch1Trigger::Tim6 => dac::vals::Tsel1::TIM6_TRGO,
|
Ch1Trigger::Tim6 => dac::vals::Tsel1::TIM6_TRGO,
|
||||||
|
#[cfg(not(dac_v3))]
|
||||||
Ch1Trigger::Tim3 => dac::vals::Tsel1::TIM3_TRGO,
|
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::Tim7 => dac::vals::Tsel1::TIM7_TRGO,
|
||||||
Ch1Trigger::Tim15 => dac::vals::Tsel1::TIM15_TRGO,
|
Ch1Trigger::Tim15 => dac::vals::Tsel1::TIM15_TRGO,
|
||||||
Ch1Trigger::Tim2 => dac::vals::Tsel1::TIM2_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 tx_request = self.dma.request();
|
||||||
let dma_channel = &self.dma;
|
let dma_channel = &mut self.dma;
|
||||||
|
|
||||||
let tx_options = crate::dma::TransferOptions {
|
let tx_options = crate::dma::TransferOptions {
|
||||||
circular,
|
circular,
|
||||||
@ -376,7 +379,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let tx_request = self.dma.request();
|
let tx_request = self.dma.request();
|
||||||
let dma_channel = &self.dma;
|
let dma_channel = &mut self.dma;
|
||||||
|
|
||||||
let tx_options = crate::dma::TransferOptions {
|
let tx_options = crate::dma::TransferOptions {
|
||||||
circular,
|
circular,
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
use core::future::Future;
|
use core::future::Future;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::pin::Pin;
|
use core::pin::Pin;
|
||||||
use core::sync::atomic::{fence, Ordering};
|
use core::sync::atomic::{fence, AtomicUsize, Ordering};
|
||||||
use core::task::{Context, Poll, Waker};
|
use core::task::{Context, Poll, Waker};
|
||||||
|
|
||||||
use atomic_polyfill::AtomicUsize;
|
|
||||||
use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
|
use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
//! Generic SMI Ethernet PHY
|
//! 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};
|
use super::{StationManagement, PHY};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -36,25 +42,47 @@ mod phy_consts {
|
|||||||
use self::phy_consts::*;
|
use self::phy_consts::*;
|
||||||
|
|
||||||
/// Generic SMI Ethernet PHY
|
/// 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 {
|
unsafe impl PHY for GenericSMI {
|
||||||
/// Reset PHY and wait for it to come out of reset.
|
/// 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);
|
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 {}
|
while sm.smi_read(PHY_REG_BCR) & PHY_REG_BCR_RESET == PHY_REG_BCR_RESET {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PHY initialisation.
|
/// PHY initialisation.
|
||||||
fn phy_init<S: StationManagement>(sm: &mut S) {
|
fn phy_init<S: StationManagement>(&mut self, sm: &mut S) {
|
||||||
// Clear WU CSR
|
// Clear WU CSR
|
||||||
Self::smi_write_ext(sm, PHY_REG_WUCSR, 0);
|
self.smi_write_ext(sm, PHY_REG_WUCSR, 0);
|
||||||
|
|
||||||
// Enable auto-negotiation
|
// Enable auto-negotiation
|
||||||
sm.smi_write(PHY_REG_BCR, PHY_REG_BCR_AN | PHY_REG_BCR_ANRST | PHY_REG_BCR_100M);
|
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);
|
let bsr = sm.smi_read(PHY_REG_BSR);
|
||||||
|
|
||||||
// No link without autonegotiate
|
// No link without autonegotiate
|
||||||
@ -73,8 +101,13 @@ unsafe impl PHY for GenericSMI {
|
|||||||
|
|
||||||
/// Public functions for the PHY
|
/// Public functions for the PHY
|
||||||
impl GenericSMI {
|
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
|
// 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_CTL, 0x0003); // set address
|
||||||
sm.smi_write(PHY_REG_ADDAR, reg_addr);
|
sm.smi_write(PHY_REG_ADDAR, reg_addr);
|
||||||
sm.smi_write(PHY_REG_CTL, 0x4003); // set data
|
sm.smi_write(PHY_REG_CTL, 0x4003); // set data
|
||||||
|
@ -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 {
|
fn link_state(&mut self, cx: &mut Context) -> LinkState {
|
||||||
// TODO: wake cx.waker on link state change
|
if self.phy.poll_link(&mut self.station_management, cx) {
|
||||||
cx.waker().wake_by_ref();
|
|
||||||
if P::poll_link(self) {
|
|
||||||
LinkState::Up
|
LinkState::Up
|
||||||
} else {
|
} else {
|
||||||
LinkState::Down
|
LinkState::Down
|
||||||
@ -148,11 +146,11 @@ pub unsafe trait StationManagement {
|
|||||||
/// The methods cannot move S
|
/// The methods cannot move S
|
||||||
pub unsafe trait PHY {
|
pub unsafe trait PHY {
|
||||||
/// Reset PHY and wait for it to come out of reset.
|
/// 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.
|
/// 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
|
/// 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 {
|
pub(crate) mod sealed {
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
mod rx_desc;
|
mod rx_desc;
|
||||||
mod tx_desc;
|
mod tx_desc;
|
||||||
|
|
||||||
|
use core::marker::PhantomData;
|
||||||
use core::sync::atomic::{fence, Ordering};
|
use core::sync::atomic::{fence, Ordering};
|
||||||
|
|
||||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||||
@ -48,9 +49,8 @@ pub struct Ethernet<'d, T: Instance, P: PHY> {
|
|||||||
pub(crate) rx: RDesRing<'d>,
|
pub(crate) rx: RDesRing<'d>,
|
||||||
|
|
||||||
pins: [PeripheralRef<'d, AnyPin>; 9],
|
pins: [PeripheralRef<'d, AnyPin>; 9],
|
||||||
_phy: P,
|
pub(crate) phy: P,
|
||||||
clock_range: Cr,
|
pub(crate) station_management: EthernetStationManagement<T>,
|
||||||
phy_addr: u8,
|
|
||||||
pub(crate) mac_addr: [u8; 6],
|
pub(crate) mac_addr: [u8; 6],
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,9 +224,12 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
|||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
_peri: peri,
|
_peri: peri,
|
||||||
pins,
|
pins,
|
||||||
_phy: phy,
|
phy: phy,
|
||||||
clock_range,
|
station_management: EthernetStationManagement {
|
||||||
phy_addr,
|
peri: PhantomData,
|
||||||
|
clock_range: clock_range,
|
||||||
|
phy_addr: phy_addr,
|
||||||
|
},
|
||||||
mac_addr,
|
mac_addr,
|
||||||
tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf),
|
tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf),
|
||||||
rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_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);
|
w.set_tie(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
P::phy_reset(&mut this);
|
this.phy.phy_reset(&mut this.station_management);
|
||||||
P::phy_init(&mut this);
|
this.phy.phy_init(&mut this.station_management);
|
||||||
|
|
||||||
interrupt::ETH.unpend();
|
interrupt::ETH.unpend();
|
||||||
unsafe { interrupt::ETH.enable() };
|
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 {
|
fn smi_read(&mut self, reg: u8) -> u16 {
|
||||||
let mac = ETH.ethernet_mac();
|
let mac = ETH.ethernet_mac();
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
mod descriptors;
|
mod descriptors;
|
||||||
|
|
||||||
|
use core::marker::PhantomData;
|
||||||
use core::sync::atomic::{fence, Ordering};
|
use core::sync::atomic::{fence, Ordering};
|
||||||
|
|
||||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
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) tx: TDesRing<'d>,
|
||||||
pub(crate) rx: RDesRing<'d>,
|
pub(crate) rx: RDesRing<'d>,
|
||||||
pins: [PeripheralRef<'d, AnyPin>; 9],
|
pins: [PeripheralRef<'d, AnyPin>; 9],
|
||||||
_phy: P,
|
pub(crate) phy: P,
|
||||||
clock_range: u8,
|
pub(crate) station_management: EthernetStationManagement<T>,
|
||||||
phy_addr: u8,
|
|
||||||
pub(crate) mac_addr: [u8; 6],
|
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),
|
tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf),
|
||||||
rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf),
|
rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf),
|
||||||
pins,
|
pins,
|
||||||
_phy: phy,
|
phy: phy,
|
||||||
clock_range,
|
station_management: EthernetStationManagement {
|
||||||
phy_addr,
|
peri: PhantomData,
|
||||||
|
clock_range: clock_range,
|
||||||
|
phy_addr: phy_addr,
|
||||||
|
},
|
||||||
mac_addr,
|
mac_addr,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -229,8 +232,8 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
|||||||
w.set_tie(true);
|
w.set_tie(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
P::phy_reset(&mut this);
|
this.phy.phy_reset(&mut this.station_management);
|
||||||
P::phy_init(&mut this);
|
this.phy.phy_init(&mut this.station_management);
|
||||||
|
|
||||||
interrupt::ETH.unpend();
|
interrupt::ETH.unpend();
|
||||||
unsafe { interrupt::ETH.enable() };
|
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 {
|
fn smi_read(&mut self, reg: u8) -> u16 {
|
||||||
let mac = ETH.ethernet_mac();
|
let mac = ETH.ethernet_mac();
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use core::marker::PhantomData;
|
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::drop::OnDrop;
|
||||||
use embassy_hal_common::into_ref;
|
use embassy_hal_common::into_ref;
|
||||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use core::marker::PhantomData;
|
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::drop::OnDrop;
|
||||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||||
use stm32_metapac::FLASH_BASE;
|
use stm32_metapac::FLASH_BASE;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use core::convert::TryInto;
|
use core::convert::TryInto;
|
||||||
use core::ptr::write_volatile;
|
use core::ptr::write_volatile;
|
||||||
|
use core::sync::atomic::{fence, Ordering};
|
||||||
use atomic_polyfill::{fence, Ordering};
|
|
||||||
|
|
||||||
use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
|
use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
|
||||||
use crate::flash::Error;
|
use crate::flash::Error;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use core::convert::TryInto;
|
use core::convert::TryInto;
|
||||||
use core::ptr::write_volatile;
|
use core::ptr::write_volatile;
|
||||||
|
use core::sync::atomic::{fence, Ordering};
|
||||||
use atomic_polyfill::{fence, Ordering};
|
|
||||||
|
|
||||||
use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
|
use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
|
||||||
use crate::flash::Error;
|
use crate::flash::Error;
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
use core::convert::TryInto;
|
use core::convert::TryInto;
|
||||||
use core::ptr::write_volatile;
|
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 embassy_sync::waitqueue::AtomicWaker;
|
||||||
use pac::flash::regs::Sr;
|
use pac::flash::regs::Sr;
|
||||||
use pac::FLASH_SIZE;
|
use pac::FLASH_SIZE;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use core::convert::TryInto;
|
use core::convert::TryInto;
|
||||||
use core::ptr::write_volatile;
|
use core::ptr::write_volatile;
|
||||||
|
use core::sync::atomic::{fence, Ordering};
|
||||||
use atomic_polyfill::{fence, Ordering};
|
|
||||||
|
|
||||||
use super::{FlashRegion, FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE};
|
use super::{FlashRegion, FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE};
|
||||||
use crate::flash::Error;
|
use crate::flash::Error;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
use core::ptr::write_volatile;
|
use core::ptr::write_volatile;
|
||||||
|
use core::sync::atomic::{fence, Ordering};
|
||||||
use atomic_polyfill::{fence, Ordering};
|
|
||||||
|
|
||||||
use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
|
use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
|
||||||
use crate::flash::Error;
|
use crate::flash::Error;
|
||||||
|
@ -86,6 +86,24 @@ macro_rules! fmc_sdram_constructor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> Fmc<'d, T> {
|
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: (
|
fmc_sdram_constructor!(sdram_a12bits_d32bits_4banks_bank1: (
|
||||||
bank: stm32_fmc::SdramTargetBank::Bank1,
|
bank: stm32_fmc::SdramTargetBank::Bank1,
|
||||||
addr: [
|
addr: [
|
||||||
|
@ -382,13 +382,18 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
|||||||
// I2C start
|
// I2C start
|
||||||
//
|
//
|
||||||
// ST SAD+W
|
// ST SAD+W
|
||||||
Self::master_write(
|
if let Err(err) = Self::master_write(
|
||||||
address,
|
address,
|
||||||
write.len().min(255),
|
write.len().min(255),
|
||||||
Stop::Software,
|
Stop::Software,
|
||||||
last_chunk_idx != 0,
|
last_chunk_idx != 0,
|
||||||
&check_timeout,
|
&check_timeout,
|
||||||
)?;
|
) {
|
||||||
|
if send_stop {
|
||||||
|
self.master_stop();
|
||||||
|
}
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
|
||||||
for (number, chunk) in write.chunks(255).enumerate() {
|
for (number, chunk) in write.chunks(255).enumerate() {
|
||||||
if number != 0 {
|
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
|
// Wait until we are allowed to send data
|
||||||
// (START has been ACKed or last byte when
|
// (START has been ACKed or last byte when
|
||||||
// through)
|
// 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));
|
T::regs().txdr().write(|w| w.set_txdata(*byte));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Wait until the write finishes
|
// Wait until the write finishes
|
||||||
self.wait_tc(&check_timeout)?;
|
let result = self.wait_tc(&check_timeout);
|
||||||
|
|
||||||
if send_stop {
|
if send_stop {
|
||||||
self.master_stop();
|
self.master_stop();
|
||||||
}
|
}
|
||||||
Ok(())
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn write_dma_internal(
|
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 first_length = write[0].len();
|
||||||
let last_slice_index = write.len() - 1;
|
let last_slice_index = write.len() - 1;
|
||||||
|
|
||||||
Self::master_write(
|
if let Err(err) = Self::master_write(
|
||||||
address,
|
address,
|
||||||
first_length.min(255),
|
first_length.min(255),
|
||||||
Stop::Software,
|
Stop::Software,
|
||||||
(first_length > 255) || (last_slice_index != 0),
|
(first_length > 255) || (last_slice_index != 0),
|
||||||
&check_timeout,
|
&check_timeout,
|
||||||
)?;
|
) {
|
||||||
|
self.master_stop();
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
|
||||||
for (idx, slice) in write.iter().enumerate() {
|
for (idx, slice) in write.iter().enumerate() {
|
||||||
let slice_len = slice.len();
|
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);
|
let last_chunk_idx = total_chunks.saturating_sub(1);
|
||||||
|
|
||||||
if idx != 0 {
|
if idx != 0 {
|
||||||
Self::master_continue(
|
if let Err(err) = Self::master_continue(
|
||||||
slice_len.min(255),
|
slice_len.min(255),
|
||||||
(idx != last_slice_index) || (slice_len > 255),
|
(idx != last_slice_index) || (slice_len > 255),
|
||||||
&check_timeout,
|
&check_timeout,
|
||||||
)?;
|
) {
|
||||||
|
self.master_stop();
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (number, chunk) in slice.chunks(255).enumerate() {
|
for (number, chunk) in slice.chunks(255).enumerate() {
|
||||||
if number != 0 {
|
if number != 0 {
|
||||||
Self::master_continue(
|
if let Err(err) = Self::master_continue(
|
||||||
chunk.len(),
|
chunk.len(),
|
||||||
(number != last_chunk_idx) || (idx != last_slice_index),
|
(number != last_chunk_idx) || (idx != last_slice_index),
|
||||||
&check_timeout,
|
&check_timeout,
|
||||||
)?;
|
) {
|
||||||
|
self.master_stop();
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for byte in chunk {
|
for byte in chunk {
|
||||||
// Wait until we are allowed to send data
|
// Wait until we are allowed to send data
|
||||||
// (START has been ACKed or last byte when
|
// (START has been ACKed or last byte when
|
||||||
// through)
|
// 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
|
// Put byte on the wire
|
||||||
//self.i2c.txdr.write(|w| w.txdata().bits(*byte));
|
//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
|
// Wait until the write finishes
|
||||||
self.wait_tc(&check_timeout)?;
|
let result = self.wait_tc(&check_timeout);
|
||||||
self.master_stop();
|
self.master_stop();
|
||||||
|
result
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn blocking_write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> {
|
pub fn blocking_write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> {
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
use core::future::poll_fn;
|
use core::future::poll_fn;
|
||||||
|
use core::sync::atomic::{compiler_fence, Ordering};
|
||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
|
|
||||||
use atomic_polyfill::{compiler_fence, Ordering};
|
|
||||||
|
|
||||||
use self::sealed::Instance;
|
use self::sealed::Instance;
|
||||||
use crate::interrupt;
|
use crate::interrupt;
|
||||||
use crate::interrupt::typelevel::Interrupt;
|
use crate::interrupt::typelevel::Interrupt;
|
||||||
|
@ -1,332 +1,332 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
|
|
||||||
pub mod enums;
|
pub mod enums;
|
||||||
|
|
||||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||||
use enums::*;
|
use enums::*;
|
||||||
|
|
||||||
use crate::dma::Transfer;
|
use crate::dma::Transfer;
|
||||||
use crate::gpio::sealed::AFType;
|
use crate::gpio::sealed::AFType;
|
||||||
use crate::gpio::AnyPin;
|
use crate::gpio::AnyPin;
|
||||||
use crate::pac::quadspi::Quadspi as Regs;
|
use crate::pac::quadspi::Quadspi as Regs;
|
||||||
use crate::rcc::RccPeripheral;
|
use crate::rcc::RccPeripheral;
|
||||||
use crate::{peripherals, Peripheral};
|
use crate::{peripherals, Peripheral};
|
||||||
|
|
||||||
pub struct TransferConfig {
|
pub struct TransferConfig {
|
||||||
/// Instraction width (IMODE)
|
/// Instraction width (IMODE)
|
||||||
pub iwidth: QspiWidth,
|
pub iwidth: QspiWidth,
|
||||||
/// Address width (ADMODE)
|
/// Address width (ADMODE)
|
||||||
pub awidth: QspiWidth,
|
pub awidth: QspiWidth,
|
||||||
/// Data width (DMODE)
|
/// Data width (DMODE)
|
||||||
pub dwidth: QspiWidth,
|
pub dwidth: QspiWidth,
|
||||||
/// Instruction Id
|
/// Instruction Id
|
||||||
pub instruction: u8,
|
pub instruction: u8,
|
||||||
/// Flash memory address
|
/// Flash memory address
|
||||||
pub address: Option<u32>,
|
pub address: Option<u32>,
|
||||||
/// Number of dummy cycles (DCYC)
|
/// Number of dummy cycles (DCYC)
|
||||||
pub dummy: DummyCycles,
|
pub dummy: DummyCycles,
|
||||||
/// Length of data
|
/// Length of data
|
||||||
pub data_len: Option<usize>,
|
pub data_len: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TransferConfig {
|
impl Default for TransferConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
iwidth: QspiWidth::NONE,
|
iwidth: QspiWidth::NONE,
|
||||||
awidth: QspiWidth::NONE,
|
awidth: QspiWidth::NONE,
|
||||||
dwidth: QspiWidth::NONE,
|
dwidth: QspiWidth::NONE,
|
||||||
instruction: 0,
|
instruction: 0,
|
||||||
address: None,
|
address: None,
|
||||||
dummy: DummyCycles::_0,
|
dummy: DummyCycles::_0,
|
||||||
data_len: None,
|
data_len: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
/// Flash memory size representend as 2^[0-32], as reasonable minimum 1KiB(9) was chosen.
|
/// 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.
|
/// If you need other value the whose predefined use `Other` variant.
|
||||||
pub memory_size: MemorySize,
|
pub memory_size: MemorySize,
|
||||||
/// Address size (8/16/24/32-bit)
|
/// Address size (8/16/24/32-bit)
|
||||||
pub address_size: AddressSize,
|
pub address_size: AddressSize,
|
||||||
/// Scalar factor for generating CLK [0-255]
|
/// Scalar factor for generating CLK [0-255]
|
||||||
pub prescaler: u8,
|
pub prescaler: u8,
|
||||||
/// Number of bytes to trigger FIFO threshold flag.
|
/// Number of bytes to trigger FIFO threshold flag.
|
||||||
pub fifo_threshold: FIFOThresholdLevel,
|
pub fifo_threshold: FIFOThresholdLevel,
|
||||||
/// Minimum number of cycles that chip select must be high between issued commands
|
/// Minimum number of cycles that chip select must be high between issued commands
|
||||||
pub cs_high_time: ChipSelectHightTime,
|
pub cs_high_time: ChipSelectHightTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
memory_size: MemorySize::Other(0),
|
memory_size: MemorySize::Other(0),
|
||||||
address_size: AddressSize::_24bit,
|
address_size: AddressSize::_24bit,
|
||||||
prescaler: 128,
|
prescaler: 128,
|
||||||
fifo_threshold: FIFOThresholdLevel::_17Bytes,
|
fifo_threshold: FIFOThresholdLevel::_17Bytes,
|
||||||
cs_high_time: ChipSelectHightTime::_5Cycle,
|
cs_high_time: ChipSelectHightTime::_5Cycle,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct Qspi<'d, T: Instance, Dma> {
|
pub struct Qspi<'d, T: Instance, Dma> {
|
||||||
_peri: PeripheralRef<'d, T>,
|
_peri: PeripheralRef<'d, T>,
|
||||||
sck: Option<PeripheralRef<'d, AnyPin>>,
|
sck: Option<PeripheralRef<'d, AnyPin>>,
|
||||||
d0: Option<PeripheralRef<'d, AnyPin>>,
|
d0: Option<PeripheralRef<'d, AnyPin>>,
|
||||||
d1: Option<PeripheralRef<'d, AnyPin>>,
|
d1: Option<PeripheralRef<'d, AnyPin>>,
|
||||||
d2: Option<PeripheralRef<'d, AnyPin>>,
|
d2: Option<PeripheralRef<'d, AnyPin>>,
|
||||||
d3: Option<PeripheralRef<'d, AnyPin>>,
|
d3: Option<PeripheralRef<'d, AnyPin>>,
|
||||||
nss: Option<PeripheralRef<'d, AnyPin>>,
|
nss: Option<PeripheralRef<'d, AnyPin>>,
|
||||||
dma: PeripheralRef<'d, Dma>,
|
dma: PeripheralRef<'d, Dma>,
|
||||||
config: Config,
|
config: Config,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
|
impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
|
d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
|
||||||
d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
|
d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
|
||||||
d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
|
d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
|
||||||
d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
|
d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
|
||||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||||
nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
|
nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
|
||||||
dma: impl Peripheral<P = Dma> + 'd,
|
dma: impl Peripheral<P = Dma> + 'd,
|
||||||
config: Config,
|
config: Config,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
into_ref!(peri, d0, d1, d2, d3, sck, nss);
|
into_ref!(peri, d0, d1, d2, d3, sck, nss);
|
||||||
|
|
||||||
sck.set_as_af(sck.af_num(), AFType::OutputPushPull);
|
sck.set_as_af(sck.af_num(), AFType::OutputPushPull);
|
||||||
sck.set_speed(crate::gpio::Speed::VeryHigh);
|
sck.set_speed(crate::gpio::Speed::VeryHigh);
|
||||||
nss.set_as_af(nss.af_num(), AFType::OutputPushPull);
|
nss.set_as_af(nss.af_num(), AFType::OutputPushPull);
|
||||||
nss.set_speed(crate::gpio::Speed::VeryHigh);
|
nss.set_speed(crate::gpio::Speed::VeryHigh);
|
||||||
d0.set_as_af(d0.af_num(), AFType::OutputPushPull);
|
d0.set_as_af(d0.af_num(), AFType::OutputPushPull);
|
||||||
d0.set_speed(crate::gpio::Speed::VeryHigh);
|
d0.set_speed(crate::gpio::Speed::VeryHigh);
|
||||||
d1.set_as_af(d1.af_num(), AFType::OutputPushPull);
|
d1.set_as_af(d1.af_num(), AFType::OutputPushPull);
|
||||||
d1.set_speed(crate::gpio::Speed::VeryHigh);
|
d1.set_speed(crate::gpio::Speed::VeryHigh);
|
||||||
d2.set_as_af(d2.af_num(), AFType::OutputPushPull);
|
d2.set_as_af(d2.af_num(), AFType::OutputPushPull);
|
||||||
d2.set_speed(crate::gpio::Speed::VeryHigh);
|
d2.set_speed(crate::gpio::Speed::VeryHigh);
|
||||||
d3.set_as_af(d3.af_num(), AFType::OutputPushPull);
|
d3.set_as_af(d3.af_num(), AFType::OutputPushPull);
|
||||||
d3.set_speed(crate::gpio::Speed::VeryHigh);
|
d3.set_speed(crate::gpio::Speed::VeryHigh);
|
||||||
|
|
||||||
Self::new_inner(
|
Self::new_inner(
|
||||||
peri,
|
peri,
|
||||||
Some(d0.map_into()),
|
Some(d0.map_into()),
|
||||||
Some(d1.map_into()),
|
Some(d1.map_into()),
|
||||||
Some(d2.map_into()),
|
Some(d2.map_into()),
|
||||||
Some(d3.map_into()),
|
Some(d3.map_into()),
|
||||||
Some(sck.map_into()),
|
Some(sck.map_into()),
|
||||||
Some(nss.map_into()),
|
Some(nss.map_into()),
|
||||||
dma,
|
dma,
|
||||||
config,
|
config,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_inner(
|
fn new_inner(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
d0: Option<PeripheralRef<'d, AnyPin>>,
|
d0: Option<PeripheralRef<'d, AnyPin>>,
|
||||||
d1: Option<PeripheralRef<'d, AnyPin>>,
|
d1: Option<PeripheralRef<'d, AnyPin>>,
|
||||||
d2: Option<PeripheralRef<'d, AnyPin>>,
|
d2: Option<PeripheralRef<'d, AnyPin>>,
|
||||||
d3: Option<PeripheralRef<'d, AnyPin>>,
|
d3: Option<PeripheralRef<'d, AnyPin>>,
|
||||||
sck: Option<PeripheralRef<'d, AnyPin>>,
|
sck: Option<PeripheralRef<'d, AnyPin>>,
|
||||||
nss: Option<PeripheralRef<'d, AnyPin>>,
|
nss: Option<PeripheralRef<'d, AnyPin>>,
|
||||||
dma: impl Peripheral<P = Dma> + 'd,
|
dma: impl Peripheral<P = Dma> + 'd,
|
||||||
config: Config,
|
config: Config,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
into_ref!(peri, dma);
|
into_ref!(peri, dma);
|
||||||
|
|
||||||
T::enable();
|
T::enable();
|
||||||
T::REGS.cr().write(|w| w.set_fthres(config.fifo_threshold.into()));
|
T::REGS.cr().write(|w| w.set_fthres(config.fifo_threshold.into()));
|
||||||
|
|
||||||
while T::REGS.sr().read().busy() {}
|
while T::REGS.sr().read().busy() {}
|
||||||
|
|
||||||
T::REGS.cr().write(|w| {
|
T::REGS.cr().write(|w| {
|
||||||
w.set_prescaler(config.prescaler);
|
w.set_prescaler(config.prescaler);
|
||||||
w.set_en(true);
|
w.set_en(true);
|
||||||
});
|
});
|
||||||
T::REGS.dcr().write(|w| {
|
T::REGS.dcr().write(|w| {
|
||||||
w.set_fsize(config.memory_size.into());
|
w.set_fsize(config.memory_size.into());
|
||||||
w.set_csht(config.cs_high_time.into());
|
w.set_csht(config.cs_high_time.into());
|
||||||
w.set_ckmode(false);
|
w.set_ckmode(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
_peri: peri,
|
_peri: peri,
|
||||||
sck,
|
sck,
|
||||||
d0,
|
d0,
|
||||||
d1,
|
d1,
|
||||||
d2,
|
d2,
|
||||||
d3,
|
d3,
|
||||||
nss,
|
nss,
|
||||||
dma,
|
dma,
|
||||||
config,
|
config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn command(&mut self, transaction: TransferConfig) {
|
pub fn command(&mut self, transaction: TransferConfig) {
|
||||||
T::REGS.cr().modify(|v| v.set_dmaen(false));
|
T::REGS.cr().modify(|v| v.set_dmaen(false));
|
||||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||||
|
|
||||||
while !T::REGS.sr().read().tcf() {}
|
while !T::REGS.sr().read().tcf() {}
|
||||||
T::REGS.fcr().modify(|v| v.set_ctcf(true));
|
T::REGS.fcr().modify(|v| v.set_ctcf(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) {
|
pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) {
|
||||||
T::REGS.cr().modify(|v| v.set_dmaen(false));
|
T::REGS.cr().modify(|v| v.set_dmaen(false));
|
||||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||||
|
|
||||||
if let Some(len) = transaction.data_len {
|
if let Some(len) = transaction.data_len {
|
||||||
let current_ar = T::REGS.ar().read().address();
|
let current_ar = T::REGS.ar().read().address();
|
||||||
T::REGS.ccr().modify(|v| {
|
T::REGS.ccr().modify(|v| {
|
||||||
v.set_fmode(QspiMode::IndirectRead.into());
|
v.set_fmode(QspiMode::IndirectRead.into());
|
||||||
});
|
});
|
||||||
T::REGS.ar().write(|v| {
|
T::REGS.ar().write(|v| {
|
||||||
v.set_address(current_ar);
|
v.set_address(current_ar);
|
||||||
});
|
});
|
||||||
|
|
||||||
for idx in 0..len {
|
for idx in 0..len {
|
||||||
while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {}
|
while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {}
|
||||||
buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() };
|
buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while !T::REGS.sr().read().tcf() {}
|
while !T::REGS.sr().read().tcf() {}
|
||||||
T::REGS.fcr().modify(|v| v.set_ctcf(true));
|
T::REGS.fcr().modify(|v| v.set_ctcf(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) {
|
pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) {
|
||||||
T::REGS.cr().modify(|v| v.set_dmaen(false));
|
T::REGS.cr().modify(|v| v.set_dmaen(false));
|
||||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||||
|
|
||||||
if let Some(len) = transaction.data_len {
|
if let Some(len) = transaction.data_len {
|
||||||
T::REGS.ccr().modify(|v| {
|
T::REGS.ccr().modify(|v| {
|
||||||
v.set_fmode(QspiMode::IndirectWrite.into());
|
v.set_fmode(QspiMode::IndirectWrite.into());
|
||||||
});
|
});
|
||||||
|
|
||||||
for idx in 0..len {
|
for idx in 0..len {
|
||||||
while !T::REGS.sr().read().ftf() {}
|
while !T::REGS.sr().read().ftf() {}
|
||||||
unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(buf[idx]) };
|
unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(buf[idx]) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while !T::REGS.sr().read().tcf() {}
|
while !T::REGS.sr().read().tcf() {}
|
||||||
T::REGS.fcr().modify(|v| v.set_ctcf(true));
|
T::REGS.fcr().modify(|v| v.set_ctcf(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig)
|
pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig)
|
||||||
where
|
where
|
||||||
Dma: QuadDma<T>,
|
Dma: QuadDma<T>,
|
||||||
{
|
{
|
||||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||||
|
|
||||||
T::REGS.ccr().modify(|v| {
|
T::REGS.ccr().modify(|v| {
|
||||||
v.set_fmode(QspiMode::IndirectRead.into());
|
v.set_fmode(QspiMode::IndirectRead.into());
|
||||||
});
|
});
|
||||||
let current_ar = T::REGS.ar().read().address();
|
let current_ar = T::REGS.ar().read().address();
|
||||||
T::REGS.ar().write(|v| {
|
T::REGS.ar().write(|v| {
|
||||||
v.set_address(current_ar);
|
v.set_address(current_ar);
|
||||||
});
|
});
|
||||||
|
|
||||||
let request = self.dma.request();
|
let request = self.dma.request();
|
||||||
let transfer = unsafe {
|
let transfer = unsafe {
|
||||||
Transfer::new_read(
|
Transfer::new_read(
|
||||||
&mut self.dma,
|
&mut self.dma,
|
||||||
request,
|
request,
|
||||||
T::REGS.dr().as_ptr() as *mut u8,
|
T::REGS.dr().as_ptr() as *mut u8,
|
||||||
buf,
|
buf,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
T::REGS.cr().modify(|v| v.set_dmaen(true));
|
T::REGS.cr().modify(|v| v.set_dmaen(true));
|
||||||
|
|
||||||
transfer.blocking_wait();
|
transfer.blocking_wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig)
|
pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig)
|
||||||
where
|
where
|
||||||
Dma: QuadDma<T>,
|
Dma: QuadDma<T>,
|
||||||
{
|
{
|
||||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||||
|
|
||||||
T::REGS.ccr().modify(|v| {
|
T::REGS.ccr().modify(|v| {
|
||||||
v.set_fmode(QspiMode::IndirectWrite.into());
|
v.set_fmode(QspiMode::IndirectWrite.into());
|
||||||
});
|
});
|
||||||
|
|
||||||
let request = self.dma.request();
|
let request = self.dma.request();
|
||||||
let transfer = unsafe {
|
let transfer = unsafe {
|
||||||
Transfer::new_write(
|
Transfer::new_write(
|
||||||
&mut self.dma,
|
&mut self.dma,
|
||||||
request,
|
request,
|
||||||
buf,
|
buf,
|
||||||
T::REGS.dr().as_ptr() as *mut u8,
|
T::REGS.dr().as_ptr() as *mut u8,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
T::REGS.cr().modify(|v| v.set_dmaen(true));
|
T::REGS.cr().modify(|v| v.set_dmaen(true));
|
||||||
|
|
||||||
transfer.blocking_wait();
|
transfer.blocking_wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig) {
|
fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig) {
|
||||||
T::REGS.fcr().modify(|v| {
|
T::REGS.fcr().modify(|v| {
|
||||||
v.set_csmf(true);
|
v.set_csmf(true);
|
||||||
v.set_ctcf(true);
|
v.set_ctcf(true);
|
||||||
v.set_ctef(true);
|
v.set_ctef(true);
|
||||||
v.set_ctof(true);
|
v.set_ctof(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
while T::REGS.sr().read().busy() {}
|
while T::REGS.sr().read().busy() {}
|
||||||
|
|
||||||
if let Some(len) = transaction.data_len {
|
if let Some(len) = transaction.data_len {
|
||||||
T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1));
|
T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
T::REGS.ccr().write(|v| {
|
T::REGS.ccr().write(|v| {
|
||||||
v.set_fmode(fmode.into());
|
v.set_fmode(fmode.into());
|
||||||
v.set_imode(transaction.iwidth.into());
|
v.set_imode(transaction.iwidth.into());
|
||||||
v.set_instruction(transaction.instruction);
|
v.set_instruction(transaction.instruction);
|
||||||
v.set_admode(transaction.awidth.into());
|
v.set_admode(transaction.awidth.into());
|
||||||
v.set_adsize(self.config.address_size.into());
|
v.set_adsize(self.config.address_size.into());
|
||||||
v.set_dmode(transaction.dwidth.into());
|
v.set_dmode(transaction.dwidth.into());
|
||||||
v.set_abmode(QspiWidth::NONE.into());
|
v.set_abmode(QspiWidth::NONE.into());
|
||||||
v.set_dcyc(transaction.dummy.into());
|
v.set_dcyc(transaction.dummy.into());
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(addr) = transaction.address {
|
if let Some(addr) = transaction.address {
|
||||||
T::REGS.ar().write(|v| {
|
T::REGS.ar().write(|v| {
|
||||||
v.set_address(addr);
|
v.set_address(addr);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) mod sealed {
|
pub(crate) mod sealed {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub trait Instance {
|
pub trait Instance {
|
||||||
const REGS: Regs;
|
const REGS: Regs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {}
|
pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {}
|
||||||
|
|
||||||
pin_trait!(SckPin, Instance);
|
pin_trait!(SckPin, Instance);
|
||||||
pin_trait!(D0Pin, Instance);
|
pin_trait!(D0Pin, Instance);
|
||||||
pin_trait!(D1Pin, Instance);
|
pin_trait!(D1Pin, Instance);
|
||||||
pin_trait!(D2Pin, Instance);
|
pin_trait!(D2Pin, Instance);
|
||||||
pin_trait!(D3Pin, Instance);
|
pin_trait!(D3Pin, Instance);
|
||||||
pin_trait!(NSSPin, Instance);
|
pin_trait!(NSSPin, Instance);
|
||||||
|
|
||||||
dma_trait!(QuadDma, Instance);
|
dma_trait!(QuadDma, Instance);
|
||||||
|
|
||||||
foreach_peripheral!(
|
foreach_peripheral!(
|
||||||
(quadspi, $inst:ident) => {
|
(quadspi, $inst:ident) => {
|
||||||
impl sealed::Instance for peripherals::$inst {
|
impl sealed::Instance for peripherals::$inst {
|
||||||
const REGS: Regs = crate::pac::$inst;
|
const REGS: Regs = crate::pac::$inst;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instance for peripherals::$inst {}
|
impl Instance for peripherals::$inst {}
|
||||||
};
|
};
|
||||||
);
|
);
|
||||||
|
@ -473,11 +473,11 @@ fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
|
|||||||
w.set_divm(0);
|
w.set_divm(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
return PllOutput{
|
return PllOutput {
|
||||||
p: None,
|
p: None,
|
||||||
q: None,
|
q: None,
|
||||||
r: None,
|
r: None,
|
||||||
}
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(1 <= config.prediv && config.prediv <= 63);
|
assert!(1 <= config.prediv && config.prediv <= 63);
|
||||||
|
@ -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!(pll_x_p < 128);
|
||||||
assert!(vco_ck >= VCO_MIN);
|
assert!(vco_ck >= VCO_MIN);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use embassy_hal_common::into_ref;
|
use embassy_hal_common::into_ref;
|
||||||
|
use stm32_metapac::rcc::regs::Cfgr;
|
||||||
use stm32_metapac::rcc::vals::{Lsedrv, Mcopre, Mcosel};
|
use stm32_metapac::rcc::vals::{Lsedrv, Mcopre, Mcosel};
|
||||||
|
|
||||||
use crate::gpio::sealed::AFType;
|
use crate::gpio::sealed::AFType;
|
||||||
@ -439,6 +440,26 @@ impl<'d, T: McoInstance> Mco<'d, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn init(config: Config) {
|
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 {
|
match config.rtc_mux {
|
||||||
RtcClockSource::LSE32 => {
|
RtcClockSource::LSE32 => {
|
||||||
// 1. Unlock the backup domain
|
// 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 {
|
set_freqs(Clocks {
|
||||||
sys: Hertz(sys_clk),
|
sys: Hertz(sys_clk),
|
||||||
ahb1: Hertz(ahb_freq),
|
ahb1: Hertz(ahb_freq),
|
||||||
|
@ -83,12 +83,12 @@ static mut CLOCK_FREQS: MaybeUninit<Clocks> = MaybeUninit::uninit();
|
|||||||
/// Safety: Sets a mutable global.
|
/// Safety: Sets a mutable global.
|
||||||
pub(crate) unsafe fn set_freqs(freqs: Clocks) {
|
pub(crate) unsafe fn set_freqs(freqs: Clocks) {
|
||||||
debug!("rcc: {:?}", freqs);
|
debug!("rcc: {:?}", freqs);
|
||||||
CLOCK_FREQS.as_mut_ptr().write(freqs);
|
CLOCK_FREQS = MaybeUninit::new(freqs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Safety: Reads a mutable global.
|
/// Safety: Reads a mutable global.
|
||||||
pub(crate) unsafe fn get_freqs() -> &'static Clocks {
|
pub(crate) unsafe fn get_freqs() -> &'static Clocks {
|
||||||
&*CLOCK_FREQS.as_ptr()
|
CLOCK_FREQS.assume_init_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "unstable-pac")]
|
#[cfg(feature = "unstable-pac")]
|
||||||
|
@ -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::rcc::{set_freqs, Clocks};
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
|
|
||||||
@ -184,6 +185,8 @@ pub struct Config {
|
|||||||
pub apb1_pre: APBPrescaler,
|
pub apb1_pre: APBPrescaler,
|
||||||
pub apb2_pre: APBPrescaler,
|
pub apb2_pre: APBPrescaler,
|
||||||
pub enable_lsi: bool,
|
pub enable_lsi: bool,
|
||||||
|
pub enable_rtc_apb: bool,
|
||||||
|
pub rtc_mux: RtcClockSource,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
@ -196,10 +199,25 @@ impl Default for Config {
|
|||||||
apb1_pre: APBPrescaler::NotDivided,
|
apb1_pre: APBPrescaler::NotDivided,
|
||||||
apb2_pre: APBPrescaler::NotDivided,
|
apb2_pre: APBPrescaler::NotDivided,
|
||||||
enable_lsi: false,
|
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) {
|
pub(crate) unsafe fn init(config: Config) {
|
||||||
let (sys_clk, sw, vos) = match config.mux {
|
let (sys_clk, sw, vos) = match config.mux {
|
||||||
ClockSrc::HSI16 => (HSI_FREQ.0, 0x01, VoltageScale::Range2),
|
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 {}
|
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 {
|
match config.mux {
|
||||||
ClockSrc::HSI16 => {
|
ClockSrc::HSI16 => {
|
||||||
// Enable HSI16
|
// Enable HSI16
|
||||||
@ -287,11 +331,26 @@ pub(crate) unsafe fn init(config: Config) {
|
|||||||
w.set_msirgsel(true);
|
w.set_msirgsel(true);
|
||||||
w.set_msirange(range.into());
|
w.set_msirange(range.into());
|
||||||
w.set_msion(true);
|
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() {}
|
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| {
|
RCC.extcfgr().modify(|w| {
|
||||||
if config.shd_ahb_pre == AHBPrescaler::NotDivided {
|
if config.shd_ahb_pre == AHBPrescaler::NotDivided {
|
||||||
w.set_shdhpre(0);
|
w.set_shdhpre(0);
|
||||||
|
@ -172,6 +172,7 @@ impl sealed::Instance for crate::peripherals::RTC {
|
|||||||
const BACKUP_REGISTER_COUNT: usize = 32;
|
const BACKUP_REGISTER_COUNT: usize = 32;
|
||||||
|
|
||||||
fn read_backup_register(_rtc: &Rtc, register: usize) -> Option<u32> {
|
fn read_backup_register(_rtc: &Rtc, register: usize) -> Option<u32> {
|
||||||
|
#[allow(clippy::if_same_then_else)]
|
||||||
if register < Self::BACKUP_REGISTER_COUNT {
|
if register < Self::BACKUP_REGISTER_COUNT {
|
||||||
//Some(rtc.bkpr()[register].read().bits())
|
//Some(rtc.bkpr()[register].read().bits())
|
||||||
None // RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC
|
None // RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC
|
||||||
|
@ -852,25 +852,19 @@ mod eh1 {
|
|||||||
type Error = Error;
|
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> {
|
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||||
Ok(())
|
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> {
|
fn read(&mut self, words: &mut [W]) -> Result<(), Self::Error> {
|
||||||
self.blocking_read(words)
|
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> {
|
fn write(&mut self, words: &[W]) -> Result<(), Self::Error> {
|
||||||
self.blocking_write(words)
|
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> {
|
fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Self::Error> {
|
||||||
self.blocking_transfer(read, write)
|
self.blocking_transfer(read, write)
|
||||||
}
|
}
|
||||||
@ -895,32 +889,25 @@ mod eh1 {
|
|||||||
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
|
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
|
||||||
mod eha {
|
mod eha {
|
||||||
use super::*;
|
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> {
|
async fn flush(&mut self) -> Result<(), Self::Error> {
|
||||||
Ok(())
|
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> {
|
async fn write(&mut self, words: &[W]) -> Result<(), Self::Error> {
|
||||||
self.write(words).await
|
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> {
|
async fn read(&mut self, words: &mut [W]) -> Result<(), Self::Error> {
|
||||||
self.read(words).await
|
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(&mut self, read: &mut [W], write: &[W]) -> Result<(), Self::Error> {
|
||||||
async fn transfer<'a>(&'a mut self, read: &'a mut [W], write: &'a [W]) -> Result<(), Self::Error> {
|
|
||||||
self.transfer(read, write).await
|
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
|
self.transfer_in_place(words).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user