diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..4db9edae --- /dev/null +++ b/.gitattributes @@ -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 \ No newline at end of file diff --git a/.github/ci/crlf.sh b/.github/ci/crlf.sh new file mode 100755 index 00000000..45751040 --- /dev/null +++ b/.github/ci/crlf.sh @@ -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 \ No newline at end of file diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index 1402e742..06c6fa00 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh @@ -6,7 +6,7 @@ set -euo pipefail export RUSTUP_HOME=/ci/cache/rustup export CARGO_HOME=/ci/cache/cargo export CARGO_TARGET_DIR=/ci/cache/target -export BUILDER_THREADS=6 +export BUILDER_THREADS=4 export BUILDER_COMPRESS=true # force rustup to download the toolchain before starting building. @@ -15,30 +15,32 @@ export BUILDER_COMPRESS=true # which makes rustup very sad rustc --version > /dev/null -docserver-builder -i ./embassy-stm32 -o crates/embassy-stm32/git.zup -docserver-builder -i ./embassy-boot/boot -o crates/embassy-boot/git.zup -docserver-builder -i ./embassy-boot/nrf -o crates/embassy-boot-nrf/git.zup -docserver-builder -i ./embassy-boot/rp -o crates/embassy-boot-rp/git.zup -docserver-builder -i ./embassy-boot/stm32 -o crates/embassy-boot-stm32/git.zup -docserver-builder -i ./embassy-embedded-hal -o crates/embassy-embedded-hal/git.zup -docserver-builder -i ./embassy-executor -o crates/embassy-executor/git.zup -docserver-builder -i ./embassy-futures -o crates/embassy-futures/git.zup -docserver-builder -i ./embassy-lora -o crates/embassy-lora/git.zup -docserver-builder -i ./embassy-net -o crates/embassy-net/git.zup -docserver-builder -i ./embassy-net-driver -o crates/embassy-net-driver/git.zup -docserver-builder -i ./embassy-net-driver-channel -o crates/embassy-net-driver-channel/git.zup -docserver-builder -i ./embassy-nrf -o crates/embassy-nrf/git.zup -docserver-builder -i ./embassy-rp -o crates/embassy-rp/git.zup -docserver-builder -i ./embassy-sync -o crates/embassy-sync/git.zup -docserver-builder -i ./embassy-time -o crates/embassy-time/git.zup -docserver-builder -i ./embassy-usb -o crates/embassy-usb/git.zup -docserver-builder -i ./embassy-usb-driver -o crates/embassy-usb-driver/git.zup -docserver-builder -i ./embassy-usb-logger -o crates/embassy-usb-logger/git.zup -docserver-builder -i ./cyw43 -o crates/cyw43/git.zup -docserver-builder -i ./cyw43-pio -o crates/cyw43-pio/git.zup -docserver-builder -i ./embassy-net-w5500 -o crates/embassy-net-w5500/git.zup -docserver-builder -i ./embassy-stm32-wpan -o crates/embassy-stm32-wpan/git.zup +docserver-builder -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup +docserver-builder -i ./embassy-boot/boot -o webroot/crates/embassy-boot/git.zup +docserver-builder -i ./embassy-boot/nrf -o webroot/crates/embassy-boot-nrf/git.zup +docserver-builder -i ./embassy-boot/rp -o webroot/crates/embassy-boot-rp/git.zup +docserver-builder -i ./embassy-boot/stm32 -o webroot/crates/embassy-boot-stm32/git.zup +docserver-builder -i ./embassy-embedded-hal -o webroot/crates/embassy-embedded-hal/git.zup +docserver-builder -i ./embassy-executor -o webroot/crates/embassy-executor/git.zup +docserver-builder -i ./embassy-futures -o webroot/crates/embassy-futures/git.zup +docserver-builder -i ./embassy-lora -o webroot/crates/embassy-lora/git.zup +docserver-builder -i ./embassy-net -o webroot/crates/embassy-net/git.zup +docserver-builder -i ./embassy-net-driver -o webroot/crates/embassy-net-driver/git.zup +docserver-builder -i ./embassy-net-driver-channel -o webroot/crates/embassy-net-driver-channel/git.zup +docserver-builder -i ./embassy-nrf -o webroot/crates/embassy-nrf/git.zup +docserver-builder -i ./embassy-rp -o webroot/crates/embassy-rp/git.zup +docserver-builder -i ./embassy-sync -o webroot/crates/embassy-sync/git.zup +docserver-builder -i ./embassy-time -o webroot/crates/embassy-time/git.zup +docserver-builder -i ./embassy-usb -o webroot/crates/embassy-usb/git.zup +docserver-builder -i ./embassy-usb-driver -o webroot/crates/embassy-usb-driver/git.zup +docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/git.zup +docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup +docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup +docserver-builder -i ./embassy-net-w5500 -o webroot/crates/embassy-net-w5500/git.zup +docserver-builder -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup +docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static export KUBECONFIG=/ci/secrets/kubeconfig.yml POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) -kubectl cp crates $POD:/data +kubectl cp webroot/crates $POD:/data +kubectl cp webroot/static $POD:/data \ No newline at end of file diff --git a/ci.sh b/ci.sh index ec6304f1..6c10a063 100755 --- a/ci.sh +++ b/ci.sh @@ -5,10 +5,6 @@ set -euo pipefail export RUSTFLAGS=-Dwarnings export DEFMT_LOG=trace,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info -# needed by wifi examples -export WIFI_NETWORK=x -export WIFI_PASSWORD=x - TARGET=$(rustc -vV | sed -n 's|host: ||p') BUILD_EXTRA="" diff --git a/cyw43/Cargo.toml b/cyw43/Cargo.toml index 61caa027..50fb7c5d 100644 --- a/cyw43/Cargo.toml +++ b/cyw43/Cargo.toml @@ -11,7 +11,7 @@ log = ["dep:log"] firmware-logs = [] [dependencies] -embassy-time = { version = "0.1.0", path = "../embassy-time"} +embassy-time = { version = "0.1.2", path = "../embassy-time"} embassy-sync = { version = "0.2.0", path = "../embassy-sync"} embassy-futures = { version = "0.1.0", path = "../embassy-futures"} embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"} @@ -24,7 +24,7 @@ cortex-m = "0.7.6" cortex-m-rt = "0.7.0" futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } -embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.10" } +embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.11" } num_enum = { version = "0.5.7", default-features = false } [package.metadata.embassy_docs] diff --git a/cyw43/README.md b/cyw43/README.md index e4a81410..5b8f3cf4 100644 --- a/cyw43/README.md +++ b/cyw43/README.md @@ -30,7 +30,7 @@ TODO: ### Example 2: Create an access point (IP and credentials in the code) - `cargo run --release --bin wifi_ap_tcp_server` ### Example 3: Connect to an existing network and create a server -- `WIFI_NETWORK=MyWifiNetwork WIFI_PASSWORD=MyWifiPassword cargo run --release --bin wifi_tcp_server` +- `cargo run --release --bin wifi_tcp_server` After a few seconds, you should see that DHCP picks up an IP address like this ``` diff --git a/cyw43/src/runner.rs b/cyw43/src/runner.rs index 5706696b..1c187faa 100644 --- a/cyw43/src/runner.rs +++ b/cyw43/src/runner.rs @@ -345,7 +345,9 @@ where } fn rx(&mut self, packet: &mut [u8]) { - let Some((sdpcm_header, payload)) = SdpcmHeader::parse(packet) else { return }; + let Some((sdpcm_header, payload)) = SdpcmHeader::parse(packet) else { + return; + }; self.update_credit(&sdpcm_header); @@ -353,7 +355,9 @@ where match channel { CHANNEL_TYPE_CONTROL => { - let Some((cdc_header, response)) = CdcHeader::parse(payload) else { return; }; + let Some((cdc_header, response)) = CdcHeader::parse(payload) else { + return; + }; trace!(" {:?}", cdc_header); if cdc_header.id == self.ioctl_id { @@ -417,8 +421,12 @@ where let status = event_packet.msg.status; let event_payload = match evt_type { Event::ESCAN_RESULT if status == EStatus::PARTIAL => { - let Some((_, bss_info)) = ScanResults::parse(evt_data) else { return }; - let Some(bss_info) = BssInfo::parse(bss_info) else { return }; + let Some((_, bss_info)) = ScanResults::parse(evt_data) else { + return; + }; + let Some(bss_info) = BssInfo::parse(bss_info) else { + return; + }; events::Payload::BssInfo(*bss_info) } Event::ESCAN_RESULT => events::Payload::None, @@ -439,7 +447,9 @@ where } } CHANNEL_TYPE_DATA => { - let Some((_, packet)) = BdcHeader::parse(payload) else { return }; + let Some((_, packet)) = BdcHeader::parse(payload) else { + return; + }; trace!("rx pkt {:02x}", Bytes(&packet[..packet.len().min(48)])); match self.ch.try_rx_buf() { diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index 35c70bb6..bba3d48b 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -15,15 +15,18 @@ target = "x86_64-unknown-linux-gnu" std = [] # Enable nightly-only features nightly = ["embassy-futures", "embedded-hal-async", "embedded-storage-async"] +time = ["dep:embassy-time"] +default = ["time"] [dependencies] embassy-futures = { version = "0.1.0", path = "../embassy-futures", optional = true } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } +embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [ "unproven", ] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } -embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } +embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true } embedded-storage = "0.3.0" embedded-storage-async = { version = "0.4.0", optional = true } nb = "1.0.0" diff --git a/embassy-embedded-hal/src/adapter/blocking_async.rs b/embassy-embedded-hal/src/adapter/blocking_async.rs index b996d6a7..98ae2b02 100644 --- a/embassy-embedded-hal/src/adapter/blocking_async.rs +++ b/embassy-embedded-hal/src/adapter/blocking_async.rs @@ -74,7 +74,21 @@ where E: embedded_hal_1::spi::Error + 'static, T: blocking::spi::Transfer + blocking::spi::Write, { - async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> { + async fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } + + async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> { + self.wrapped.write(data)?; + Ok(()) + } + + async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> { + self.wrapped.transfer(data)?; + Ok(()) + } + + async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { // Ensure we write the expected bytes for i in 0..core::cmp::min(read.len(), write.len()) { read[i] = write[i].clone(); @@ -83,38 +97,7 @@ where Ok(()) } - async fn transfer_in_place<'a>(&'a mut self, _: &'a mut [u8]) -> Result<(), Self::Error> { - todo!() - } -} - -impl embedded_hal_async::spi::SpiBusFlush for BlockingAsync -where - E: embedded_hal_1::spi::Error + 'static, - T: blocking::spi::Transfer + blocking::spi::Write, -{ - async fn flush(&mut self) -> Result<(), Self::Error> { - Ok(()) - } -} - -impl embedded_hal_async::spi::SpiBusWrite for BlockingAsync -where - E: embedded_hal_1::spi::Error + 'static, - T: blocking::spi::Transfer + blocking::spi::Write, -{ - async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> { - self.wrapped.write(data)?; - Ok(()) - } -} - -impl embedded_hal_async::spi::SpiBusRead for BlockingAsync -where - E: embedded_hal_1::spi::Error + 'static, - T: blocking::spi::Transfer + blocking::spi::Write, -{ - async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> { + async fn transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Self::Error> { self.wrapped.transfer(data)?; Ok(()) } diff --git a/embassy-embedded-hal/src/adapter/yielding_async.rs b/embassy-embedded-hal/src/adapter/yielding_async.rs index f51e4076..fe9c9c34 100644 --- a/embassy-embedded-hal/src/adapter/yielding_async.rs +++ b/embassy-embedded-hal/src/adapter/yielding_async.rs @@ -69,54 +69,39 @@ where type Error = T::Error; } -impl embedded_hal_async::spi::SpiBus for YieldingAsync +impl embedded_hal_async::spi::SpiBus for YieldingAsync where - T: embedded_hal_async::spi::SpiBus, -{ - async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> { - self.wrapped.transfer(read, write).await?; - yield_now().await; - Ok(()) - } - - async fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Result<(), Self::Error> { - self.wrapped.transfer_in_place(words).await?; - yield_now().await; - Ok(()) - } -} - -impl embedded_hal_async::spi::SpiBusFlush for YieldingAsync -where - T: embedded_hal_async::spi::SpiBusFlush, + T: embedded_hal_async::spi::SpiBus, { async fn flush(&mut self) -> Result<(), Self::Error> { self.wrapped.flush().await?; yield_now().await; Ok(()) } -} -impl embedded_hal_async::spi::SpiBusWrite for YieldingAsync -where - T: embedded_hal_async::spi::SpiBusWrite, -{ - async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> { + async fn write(&mut self, data: &[Word]) -> Result<(), Self::Error> { self.wrapped.write(data).await?; yield_now().await; Ok(()) } -} -impl embedded_hal_async::spi::SpiBusRead for YieldingAsync -where - T: embedded_hal_async::spi::SpiBusRead, -{ - async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> { + async fn read(&mut self, data: &mut [Word]) -> Result<(), Self::Error> { self.wrapped.read(data).await?; yield_now().await; Ok(()) } + + async fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { + self.wrapped.transfer(read, write).await?; + yield_now().await; + Ok(()) + } + + async fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { + self.wrapped.transfer_in_place(words).await?; + yield_now().await; + Ok(()) + } } /// diff --git a/embassy-embedded-hal/src/shared_bus/asynch/spi.rs b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs index b5549a6c..03039218 100644 --- a/embassy-embedded-hal/src/shared_bus/asynch/spi.rs +++ b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs @@ -56,62 +56,6 @@ where type Error = SpiDeviceError; } -impl 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 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 spi::SpiDevice for SpiDevice<'_, M, BUS, CS> where M: RawMutex, @@ -129,6 +73,12 @@ where Operation::Write(buf) => bus.write(buf).await?, Operation::Transfer(read, write) => bus.transfer(read, write).await?, Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?, + #[cfg(not(feature = "time"))] + Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported), + #[cfg(feature = "time")] + Operation::DelayUs(us) => { + embassy_time::Timer::after(embassy_time::Duration::from_micros(*us as _)).await + } } } }; @@ -172,64 +122,6 @@ where type Error = SpiDeviceError; } -impl 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 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 spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> where M: RawMutex, @@ -248,6 +140,12 @@ where Operation::Write(buf) => bus.write(buf).await?, Operation::Transfer(read, write) => bus.transfer(read, write).await?, Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?, + #[cfg(not(feature = "time"))] + Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported), + #[cfg(feature = "time")] + Operation::DelayUs(us) => { + embassy_time::Timer::after(embassy_time::Duration::from_micros(*us as _)).await + } } } }; diff --git a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs index 22e013be..6d03d626 100644 --- a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs +++ b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs @@ -22,7 +22,7 @@ use core::cell::RefCell; use embassy_sync::blocking_mutex::raw::RawMutex; use embassy_sync::blocking_mutex::Mutex; use embedded_hal_1::digital::OutputPin; -use embedded_hal_1::spi::{self, Operation, SpiBus, SpiBusRead, SpiBusWrite}; +use embedded_hal_1::spi::{self, Operation, SpiBus}; use crate::shared_bus::SpiDeviceError; use crate::SetConfig; @@ -48,58 +48,6 @@ where type Error = SpiDeviceError; } -impl 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 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 embedded_hal_1::spi::SpiDevice for SpiDevice<'_, M, BUS, CS> where M: RawMutex, @@ -116,6 +64,13 @@ where Operation::Write(buf) => bus.write(buf), Operation::Transfer(read, write) => bus.transfer(read, write), Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), + #[cfg(not(feature = "time"))] + Operation::DelayUs(_) => Err(SpiDeviceError::DelayUsNotSupported), + #[cfg(feature = "time")] + Operation::DelayUs(us) => { + embassy_time::block_for(embassy_time::Duration::from_micros(*us as _)); + Ok(()) + } }); // On failure, it's important to still flush and deassert CS. @@ -199,58 +154,6 @@ where type Error = SpiDeviceError; } -impl 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 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 embedded_hal_1::spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS> where M: RawMutex, @@ -268,6 +171,13 @@ where Operation::Write(buf) => bus.write(buf), Operation::Transfer(read, write) => bus.transfer(read, write), Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), + #[cfg(not(feature = "time"))] + Operation::DelayUs(_) => Err(SpiDeviceError::DelayUsNotSupported), + #[cfg(feature = "time")] + Operation::DelayUs(us) => { + embassy_time::block_for(embassy_time::Duration::from_micros(*us as _)); + Ok(()) + } }); // On failure, it's important to still flush and deassert CS. diff --git a/embassy-embedded-hal/src/shared_bus/mod.rs b/embassy-embedded-hal/src/shared_bus/mod.rs index 617d921e..79a90bd5 100644 --- a/embassy-embedded-hal/src/shared_bus/mod.rs +++ b/embassy-embedded-hal/src/shared_bus/mod.rs @@ -30,11 +30,14 @@ where /// Error returned by SPI device implementations in this crate. #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] pub enum SpiDeviceError { /// An operation on the inner SPI bus failed. Spi(BUS), /// Setting the value of the Chip Select (CS) pin failed. Cs(CS), + /// DelayUs operations are not supported when the `time` Cargo feature is not enabled. + DelayUsNotSupported, } impl spi::Error for SpiDeviceError @@ -46,6 +49,7 @@ where match self { Self::Spi(e) => e.kind(), Self::Cs(_) => spi::ErrorKind::Other, + Self::DelayUsNotSupported => spi::ErrorKind::Other, } } } diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 1e5494ef..590718e3 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -61,8 +61,8 @@ log = { version = "0.4.14", optional = true } rtos-trace = { version = "0.1.2", optional = true } futures-util = { version = "0.3.17", default-features = false } -embassy-macros = { version = "0.2.0", path = "../embassy-macros" } -embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true} +embassy-macros = { version = "0.2.0", path = "../embassy-macros" } +embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true} atomic-polyfill = "1.0.1" critical-section = "1.1" static_cell = "1.1" diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index bd0cff26..f3760f58 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -165,6 +165,9 @@ impl TaskStorage { Poll::Ready(_) => { this.future.drop_in_place(); this.raw.state.fetch_and(!STATE_SPAWNED, Ordering::AcqRel); + + #[cfg(feature = "integrated-timers")] + this.raw.expires_at.set(Instant::MAX); } Poll::Pending => {} } diff --git a/embassy-hal-common/src/peripheral.rs b/embassy-hal-common/src/peripheral.rs index c7133bac..38b4c452 100644 --- a/embassy-hal-common/src/peripheral.rs +++ b/embassy-hal-common/src/peripheral.rs @@ -161,7 +161,7 @@ pub trait Peripheral: Sized { } } -impl<'b, T: Deref> Peripheral for T +impl<'b, T: DerefMut> Peripheral for T where T::Target: Peripheral, { diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index 05b6fa2d..e4524af5 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -20,11 +20,11 @@ defmt = ["dep:defmt", "lorawan-device/defmt"] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -embassy-time = { version = "0.1.0", path = "../embassy-time" } +embassy-time = { version = "0.1.2", path = "../embassy-time" } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } -embedded-hal-async = { version = "=0.2.0-alpha.1" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } +embedded-hal-async = { version = "=0.2.0-alpha.2" } embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common", default-features = false } futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } embedded-hal = { version = "0.2", features = ["unproven"] } @@ -32,3 +32,6 @@ bit_field = { version = "0.10" } lora-phy = { version = "1" } lorawan-device = { version = "0.10.0", default-features = false, features = ["async"] } + +[patch.crates-io] +lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } diff --git a/embassy-net-driver/src/lib.rs b/embassy-net-driver/src/lib.rs index 4149bf4a..09def20c 100644 --- a/embassy-net-driver/src/lib.rs +++ b/embassy-net-driver/src/lib.rs @@ -164,6 +164,9 @@ pub enum Medium { /// /// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode. Ip, + + /// IEEE 802_15_4 medium + Ieee802154, } impl Default for Medium { diff --git a/embassy-net-esp-hosted/Cargo.toml b/embassy-net-esp-hosted/Cargo.toml index a7e18ee0..0053c49a 100644 --- a/embassy-net-esp-hosted/Cargo.toml +++ b/embassy-net-esp-hosted/Cargo.toml @@ -7,14 +7,20 @@ edition = "2021" defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -embassy-time = { version = "0.1.0", path = "../embassy-time" } +embassy-time = { version = "0.1.2", path = "../embassy-time" } embassy-sync = { version = "0.2.0", path = "../embassy-sync"} embassy-futures = { version = "0.1.0", path = "../embassy-futures"} embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"} -embedded-hal = { version = "1.0.0-alpha.10" } -embedded-hal-async = { version = "=0.2.0-alpha.1" } +embedded-hal = { version = "1.0.0-alpha.11" } +embedded-hal-async = { version = "=0.2.0-alpha.2" } noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] } #noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] } heapless = "0.7.16" + +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-esp-hosted-v$VERSION/embassy-net-esp-hosted/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-esp-hosted/src/" +target = "thumbv7em-none-eabi" +features = ["defmt"] \ No newline at end of file diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs index fce82ade..79f8cde7 100644 --- a/embassy-net-esp-hosted/src/control.rs +++ b/embassy-net-esp-hosted/src/control.rs @@ -54,7 +54,9 @@ impl<'a> Control<'a> { })), }; let resp = self.ioctl(req).await; - let proto::CtrlMsgPayload::RespConnectAp(resp) = resp.payload.unwrap() else { panic!("unexpected resp") }; + let proto::CtrlMsgPayload::RespConnectAp(resp) = resp.payload.unwrap() else { + panic!("unexpected resp") + }; debug!("======= {:?}", Debug2Format(&resp)); assert_eq!(resp.resp, 0); self.state_ch.set_link_state(LinkState::Up); @@ -71,7 +73,9 @@ impl<'a> Control<'a> { )), }; let resp = self.ioctl(req).await; - let proto::CtrlMsgPayload::RespGetMacAddress(resp) = resp.payload.unwrap() else { panic!("unexpected resp") }; + let proto::CtrlMsgPayload::RespGetMacAddress(resp) = resp.payload.unwrap() else { + panic!("unexpected resp") + }; assert_eq!(resp.resp, 0); // WHY IS THIS A STRING? WHYYYY @@ -100,7 +104,9 @@ impl<'a> Control<'a> { payload: Some(proto::CtrlMsgPayload::ReqSetWifiMode(proto::CtrlMsgReqSetMode { mode })), }; let resp = self.ioctl(req).await; - let proto::CtrlMsgPayload::RespSetWifiMode(resp) = resp.payload.unwrap() else { panic!("unexpected resp") }; + let proto::CtrlMsgPayload::RespSetWifiMode(resp) = resp.payload.unwrap() else { + panic!("unexpected resp") + }; assert_eq!(resp.resp, 0); } diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs index 44dfbe89..a35adfca 100644 --- a/embassy-net-esp-hosted/src/lib.rs +++ b/embassy-net-esp-hosted/src/lib.rs @@ -311,14 +311,14 @@ where fn handle_event(&self, data: &[u8]) { let Ok(event) = noproto::read::(data) else { warn!("failed to parse event"); - return + return; }; debug!("event: {:?}", &event); let Some(payload) = &event.payload else { warn!("event without payload?"); - return + return; }; match payload { diff --git a/embassy-net-w5500/Cargo.toml b/embassy-net-w5500/Cargo.toml index 37d15c7a..8972b814 100644 --- a/embassy-net-w5500/Cargo.toml +++ b/embassy-net-w5500/Cargo.toml @@ -8,11 +8,11 @@ license = "MIT OR Apache-2.0" edition = "2021" [dependencies] -embedded-hal = { version = "1.0.0-alpha.10" } -embedded-hal-async = { version = "=0.2.0-alpha.1" } -embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"} -embassy-time = { version = "0.1.0" } -embassy-futures = { version = "0.1.0" } +embedded-hal = { version = "1.0.0-alpha.11" } +embedded-hal-async = { version = "=0.2.0-alpha.2" } +embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" } +embassy-time = { version = "0.1.2", path = "../embassy-time" } +embassy-futures = { version = "0.1.0", path = "../embassy-futures" } defmt = { version = "0.3", optional = true } [package.metadata.embassy_docs] diff --git a/embassy-net-w5500/src/spi.rs b/embassy-net-w5500/src/spi.rs index 6cd52c44..07749d6b 100644 --- a/embassy-net-w5500/src/spi.rs +++ b/embassy-net-w5500/src/spi.rs @@ -22,7 +22,11 @@ impl SpiInterface { let address_phase = address.to_be_bytes(); let control_phase = [(block as u8) << 3 | 0b0000_0100]; let data_phase = data; - let operations = &[&address_phase[..], &control_phase, &data_phase]; - self.0.write_transaction(operations).await + let operations = &mut [ + Operation::Write(&address_phase[..]), + Operation::Write(&control_phase), + Operation::Write(&data_phase), + ]; + self.0.transaction(operations).await } } diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index e89039da..9b6a11c1 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -37,6 +37,7 @@ proto-ipv4 = ["smoltcp/proto-ipv4"] proto-ipv6 = ["smoltcp/proto-ipv6"] medium-ethernet = ["smoltcp/medium-ethernet"] medium-ip = ["smoltcp/medium-ip"] +medium-ieee802154 = ["smoltcp/medium-ieee802154"] igmp = ["smoltcp/proto-igmp"] [dependencies] @@ -50,7 +51,7 @@ smoltcp = { version = "0.10.0", default-features = false, features = [ ] } embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } -embassy-time = { version = "0.1.0", path = "../embassy-time" } +embassy-time = { version = "0.1.2", path = "../embassy-time" } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embedded-io = { version = "0.4.0", optional = true } diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs index 4513c86d..d29ab897 100644 --- a/embassy-net/src/device.rs +++ b/embassy-net/src/device.rs @@ -51,6 +51,8 @@ where Medium::Ethernet => phy::Medium::Ethernet, #[cfg(feature = "medium-ip")] Medium::Ip => phy::Medium::Ip, + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => phy::Medium::Ieee802154, #[allow(unreachable_patterns)] _ => panic!( "Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.", diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 840d7a09..81c751a2 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -24,9 +24,11 @@ use embassy_net_driver::{Driver, LinkState, Medium}; use embassy_sync::waitqueue::WakerRegistration; use embassy_time::{Instant, Timer}; use futures::pin_mut; +#[allow(unused_imports)] use heapless::Vec; #[cfg(feature = "igmp")] pub use smoltcp::iface::MulticastError; +#[allow(unused_imports)] use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage}; #[cfg(feature = "dhcpv4")] use smoltcp::socket::dhcpv4::{self, RetryConfig}; @@ -34,7 +36,9 @@ use smoltcp::socket::dhcpv4::{self, RetryConfig}; pub use smoltcp::wire::IpListenEndpoint; #[cfg(feature = "medium-ethernet")] pub use smoltcp::wire::{EthernetAddress, HardwareAddress}; -pub use smoltcp::wire::{IpAddress, IpCidr}; +#[cfg(feature = "medium-ieee802154")] +pub use smoltcp::wire::{HardwareAddress, Ieee802154Address}; +pub use smoltcp::wire::{IpAddress, IpCidr, IpEndpoint}; #[cfg(feature = "proto-ipv4")] pub use smoltcp::wire::{Ipv4Address, Ipv4Cidr}; #[cfg(feature = "proto-ipv6")] @@ -232,7 +236,7 @@ impl Stack { resources: &'static mut StackResources, random_seed: u64, ) -> Self { - #[cfg(feature = "medium-ethernet")] + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] let medium = device.capabilities().medium; let hardware_addr = match medium { @@ -240,6 +244,8 @@ impl Stack { Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress(device.ethernet_address())), #[cfg(feature = "medium-ip")] Medium::Ip => HardwareAddress::Ip, + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => HardwareAddress::Ieee802154(Ieee802154Address::Absent), #[allow(unreachable_patterns)] _ => panic!( "Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.", @@ -262,6 +268,7 @@ impl Stack { let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN; + #[cfg_attr(feature = "medium-ieee802154", allow(unused_mut))] let mut socket = SocketStack { sockets, iface, @@ -269,6 +276,7 @@ impl Stack { next_local_port, }; + #[cfg_attr(feature = "medium-ieee802154", allow(unused_mut))] let mut inner = Inner { device, link_up: false, @@ -287,6 +295,9 @@ impl Stack { dns_waker: WakerRegistration::new(), }; + #[cfg(feature = "medium-ieee802154")] + let _ = config; + #[cfg(feature = "proto-ipv4")] match config.ipv4 { ConfigV4::Static(config) => { @@ -479,30 +490,78 @@ impl Stack { } #[cfg(feature = "igmp")] -impl Stack { +impl Stack { /// Join a multicast group. - pub fn join_multicast_group(&self, addr: T) -> Result + pub async fn join_multicast_group(&self, addr: T) -> Result + where + T: Into, + { + 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(&self, addr: T, cx: &mut Context<'_>) -> Poll> where T: Into, { let addr = addr.into(); self.with_mut(|s, i| { - s.iface - .join_multicast_group(&mut i.device, addr, instant_to_smoltcp(Instant::now())) + let mut smoldev = DriverAdapter { + cx: Some(cx), + inner: &mut i.device, + }; + + match s + .iface + .join_multicast_group(&mut smoldev, addr, instant_to_smoltcp(Instant::now())) + { + Ok(announce_sent) => Poll::Ready(Ok(announce_sent)), + Err(MulticastError::Exhausted) => Poll::Pending, + Err(other) => Poll::Ready(Err(other)), + } }) } /// Leave a multicast group. - pub fn leave_multicast_group(&self, addr: T) -> Result + pub async fn leave_multicast_group(&self, addr: T) -> Result + where + T: Into, + { + 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(&self, addr: T, cx: &mut Context<'_>) -> Poll> where T: Into, { let addr = addr.into(); self.with_mut(|s, i| { - s.iface - .leave_multicast_group(&mut i.device, addr, instant_to_smoltcp(Instant::now())) + let mut smoldev = DriverAdapter { + cx: Some(cx), + inner: &mut i.device, + }; + + match s + .iface + .leave_multicast_group(&mut smoldev, addr, instant_to_smoltcp(Instant::now())) + { + Ok(leave_sent) => Poll::Ready(Ok(leave_sent)), + Err(MulticastError::Exhausted) => Poll::Pending, + Err(other) => Poll::Ready(Err(other)), + } }) } @@ -531,11 +590,14 @@ impl Inner { debug!(" IP address: {}", config.address); s.iface.update_ip_addrs(|addrs| { - if addrs.is_empty() { - addrs.push(IpCidr::Ipv4(config.address)).unwrap(); - } else { - addrs[0] = IpCidr::Ipv4(config.address); + if let Some((index, _)) = addrs + .iter() + .enumerate() + .find(|(_, &addr)| matches!(addr, IpCidr::Ipv4(_))) + { + addrs.remove(index); } + addrs.push(IpCidr::Ipv4(config.address)).unwrap(); }); #[cfg(feature = "medium-ethernet")] @@ -570,11 +632,14 @@ impl Inner { debug!(" IP address: {}", config.address); s.iface.update_ip_addrs(|addrs| { - if addrs.is_empty() { - addrs.push(IpCidr::Ipv6(config.address)).unwrap(); - } else { - addrs[0] = IpCidr::Ipv6(config.address); + if let Some((index, _)) = addrs + .iter() + .enumerate() + .find(|(_, &addr)| matches!(addr, IpCidr::Ipv6(_))) + { + addrs.remove(index); } + addrs.push(IpCidr::Ipv6(config.address)).unwrap(); }); #[cfg(feature = "medium-ethernet")] @@ -642,13 +707,21 @@ impl Inner { socket.set_retry_config(config.retry_config); } - #[allow(unused)] // used only with dhcp - fn unapply_config(&mut self, s: &mut SocketStack) { + #[cfg(feature = "dhcpv4")] + fn unapply_config_v4(&mut self, s: &mut SocketStack) { #[cfg(feature = "medium-ethernet")] let medium = self.device.capabilities().medium; - debug!("Lost IP configuration"); - s.iface.update_ip_addrs(|ip_addrs| ip_addrs.clear()); + s.iface.update_ip_addrs(|ip_addrs| { + #[cfg(feature = "proto-ipv4")] + if let Some((index, _)) = ip_addrs + .iter() + .enumerate() + .find(|(_, &addr)| matches!(addr, IpCidr::Ipv4(_))) + { + ip_addrs.remove(index); + } + }); #[cfg(feature = "medium-ethernet")] if medium == Medium::Ethernet { #[cfg(feature = "proto-ipv4")] @@ -695,7 +768,7 @@ impl Inner { if self.link_up { match socket.poll() { None => {} - Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s), + Some(dhcpv4::Event::Deconfigured) => self.unapply_config_v4(s), Some(dhcpv4::Event::Configured(config)) => { let config = StaticConfigV4 { address: config.address, @@ -707,7 +780,7 @@ impl Inner { } } else if old_link_up { socket.reset(); - self.unapply_config(s); + self.unapply_config_v4(s); } } //if old_link_up || self.link_up { diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 36f8d06f..0d97b6db 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -3,7 +3,7 @@ use core::cell::RefCell; use core::future::poll_fn; use core::mem; -use core::task::Poll; +use core::task::{Context, Poll}; use embassy_net_driver::Driver; use smoltcp::iface::{Interface, SocketHandle}; @@ -102,37 +102,61 @@ impl<'a> UdpSocket<'a> { /// /// Returns the number of bytes received and the remote endpoint. pub async fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, IpEndpoint), Error> { - poll_fn(move |cx| { - self.with_mut(|s, _| match s.recv_slice(buf) { - Ok((n, meta)) => Poll::Ready(Ok((n, meta.endpoint))), - // No data ready - Err(udp::RecvError::Exhausted) => { - s.register_recv_waker(cx.waker()); - Poll::Pending - } - }) + poll_fn(move |cx| self.poll_recv_from(buf, cx)).await + } + + /// Receive a datagram. + /// + /// When no datagram is available, this method will return `Poll::Pending` and + /// register the current task to be notified when a datagram is received. + /// + /// When a datagram is received, this method will return `Poll::Ready` with the + /// number of bytes received and the remote endpoint. + pub fn poll_recv_from(&self, buf: &mut [u8], cx: &mut Context<'_>) -> Poll> { + self.with_mut(|s, _| match s.recv_slice(buf) { + Ok((n, meta)) => Poll::Ready(Ok((n, meta.endpoint))), + // No data ready + Err(udp::RecvError::Exhausted) => { + s.register_recv_waker(cx.waker()); + Poll::Pending + } }) - .await } /// Send a datagram to the specified remote endpoint. + /// + /// This method will wait until the datagram has been sent. + /// + /// When the remote endpoint is not reachable, this method will return `Err(Error::NoRoute)` pub async fn send_to(&self, buf: &[u8], remote_endpoint: T) -> Result<(), Error> where T: Into, { - let remote_endpoint = remote_endpoint.into(); - poll_fn(move |cx| { - self.with_mut(|s, _| match s.send_slice(buf, remote_endpoint) { - // Entire datagram has been sent - Ok(()) => Poll::Ready(Ok(())), - Err(udp::SendError::BufferFull) => { - s.register_send_waker(cx.waker()); - Poll::Pending - } - Err(udp::SendError::Unaddressable) => Poll::Ready(Err(Error::NoRoute)), - }) + let remote_endpoint: IpEndpoint = remote_endpoint.into(); + poll_fn(move |cx| self.poll_send_to(buf, remote_endpoint, cx)).await + } + + /// Send a datagram to the specified remote endpoint. + /// + /// When the datagram has been sent, this method will return `Poll::Ready(Ok())`. + /// + /// When the socket's send buffer is full, this method will return `Poll::Pending` + /// and register the current task to be notified when the buffer has space available. + /// + /// When the remote endpoint is not reachable, this method will return `Poll::Ready(Err(Error::NoRoute))`. + pub fn poll_send_to(&self, buf: &[u8], remote_endpoint: T, cx: &mut Context<'_>) -> Poll> + where + T: Into, + { + 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. diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 3e858f85..57dd22f1 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" src_base = "https://github.com/embassy-rs/embassy/blob/embassy-nrf-v$VERSION/embassy-nrf/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-nrf/src/" -features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "gpiote", "time-driver-rtc1"] +features = ["nightly", "time", "defmt", "unstable-pac", "unstable-traits", "gpiote", "time-driver-rtc1"] flavors = [ { regex_feature = "nrf52.*", target = "thumbv7em-none-eabihf" }, { regex_feature = "nrf53.*", target = "thumbv8m.main-none-eabihf" }, @@ -91,22 +91,22 @@ _dppi = [] _gpio-p1 = [] [dependencies] -embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true } +embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common", features = ["cortex-m", "prio-bits-3"] } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} -embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true} +embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true} embedded-io = { version = "0.4.0", features = ["async"], optional = true } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } cortex-m-rt = ">=0.6.15,<0.8" cortex-m = "0.7.6" -futures = { version = "0.3.17", default-features = false } +futures = { version = "0.3.17", default-features = false } critical-section = "1.1" rand_core = "0.6.3" fixed = "1.10.0" @@ -114,13 +114,13 @@ embedded-storage = "0.3.0" embedded-storage-async = { version = "0.4.0", optional = true } cfg-if = "1.0.0" -nrf52805-pac = { version = "0.12.0", optional = true } -nrf52810-pac = { version = "0.12.0", optional = true } -nrf52811-pac = { version = "0.12.0", optional = true } -nrf52820-pac = { version = "0.12.0", optional = true } -nrf52832-pac = { version = "0.12.0", optional = true } -nrf52833-pac = { version = "0.12.0", optional = true } -nrf52840-pac = { version = "0.12.0", optional = true } +nrf52805-pac = { version = "0.12.0", optional = true } +nrf52810-pac = { version = "0.12.0", optional = true } +nrf52811-pac = { version = "0.12.0", optional = true } +nrf52820-pac = { version = "0.12.0", optional = true } +nrf52832-pac = { version = "0.12.0", optional = true } +nrf52833-pac = { version = "0.12.0", optional = true } +nrf52840-pac = { version = "0.12.0", optional = true } nrf5340-app-pac = { version = "0.12.0", optional = true } nrf5340-net-pac = { version = "0.12.0", optional = true } nrf9160-pac = { version = "0.12.0", optional = true } diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 21d0d956..6550f2ab 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -221,7 +221,7 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> { } /// Returns the IN event, for use with PPI. - pub fn event_in(&self) -> Event { + pub fn event_in(&self) -> Event<'d> { let g = regs(); Event::from_reg(&g.events_in[self.ch.number()]) } @@ -292,21 +292,21 @@ impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> { } /// Returns the OUT task, for use with PPI. - pub fn task_out(&self) -> Task { + pub fn task_out(&self) -> Task<'d> { let g = regs(); Task::from_reg(&g.tasks_out[self.ch.number()]) } /// Returns the CLR task, for use with PPI. #[cfg(not(feature = "nrf51"))] - pub fn task_clr(&self) -> Task { + pub fn task_clr(&self) -> Task<'d> { let g = regs(); Task::from_reg(&g.tasks_clr[self.ch.number()]) } /// Returns the SET task, for use with PPI. #[cfg(not(feature = "nrf51"))] - pub fn task_set(&self) -> Task { + pub fn task_set(&self) -> Task<'d> { let g = regs(); Task::from_reg(&g.tasks_set[self.ch.number()]) } diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index 0e30f700..217884d1 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -1,294 +1,500 @@ -//! Pulse Density Modulation (PDM) mirophone driver. - -#![macro_use] - -use core::marker::PhantomData; -use core::sync::atomic::{compiler_fence, Ordering}; -use core::task::Poll; - -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; -use futures::future::poll_fn; - -use crate::chip::EASY_DMA_SIZE; -use crate::gpio::sealed::Pin; -use crate::gpio::{AnyPin, Pin as GpioPin}; -use crate::interrupt::typelevel::Interrupt; -use crate::{interrupt, Peripheral}; - -/// Interrupt handler. -pub struct InterruptHandler { - _phantom: PhantomData, -} - -impl interrupt::typelevel::Handler for InterruptHandler { - unsafe fn on_interrupt() { - T::regs().intenclr.write(|w| w.end().clear()); - T::state().waker.wake(); - } -} - -/// PDM microphone interface -pub struct Pdm<'d, T: Instance> { - _peri: PeripheralRef<'d, T>, -} - -/// PDM error. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[non_exhaustive] -pub enum Error { - /// Buffer is too long. - BufferTooLong, - /// Buffer is empty - BufferZeroLength, - /// PDM is not running - NotRunning, -} - -static DUMMY_BUFFER: [i16; 1] = [0; 1]; - -impl<'d, T: Instance> Pdm<'d, T> { - /// Create PDM driver - pub fn new( - pdm: impl Peripheral

+ 'd, - _irq: impl interrupt::typelevel::Binding> + 'd, - clk: impl Peripheral

+ 'd, - din: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(pdm, clk, din); - Self::new_inner(pdm, clk.map_into(), din.map_into(), config) - } - - fn new_inner( - pdm: PeripheralRef<'d, T>, - clk: PeripheralRef<'d, AnyPin>, - din: PeripheralRef<'d, AnyPin>, - config: Config, - ) -> Self { - into_ref!(pdm); - - let r = T::regs(); - - // setup gpio pins - din.conf().write(|w| w.input().set_bit()); - r.psel.din.write(|w| unsafe { w.bits(din.psel_bits()) }); - clk.set_low(); - clk.conf().write(|w| w.dir().output()); - r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) }); - - // configure - // use default for - // - gain right - // - gain left - // - clk - // - ratio - r.mode.write(|w| { - w.edge().bit(config.edge == Edge::LeftRising); - w.operation().bit(config.operation_mode == OperationMode::Mono); - w - }); - r.gainl.write(|w| w.gainl().default_gain()); - r.gainr.write(|w| w.gainr().default_gain()); - - // IRQ - T::Interrupt::unpend(); - unsafe { T::Interrupt::enable() }; - - r.enable.write(|w| w.enable().set_bit()); - - Self { _peri: pdm } - } - - /// Start sampling microphon data into a dummy buffer - /// Usefull to start the microphon and keep it active between recording samples - pub async fn start(&mut self) { - let r = T::regs(); - - // start dummy sampling because microphon needs some setup time - r.sample - .ptr - .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); - r.sample - .maxcnt - .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); - - r.tasks_start.write(|w| unsafe { w.bits(1) }); - } - - /// Stop sampling microphon data inta a dummy buffer - pub async fn stop(&mut self) { - let r = T::regs(); - r.tasks_stop.write(|w| unsafe { w.bits(1) }); - r.events_started.reset(); - } - - /// Sample data into the given buffer. - pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> { - if buffer.len() == 0 { - return Err(Error::BufferZeroLength); - } - if buffer.len() > EASY_DMA_SIZE { - return Err(Error::BufferTooLong); - } - - let r = T::regs(); - - if r.events_started.read().bits() == 0 { - return Err(Error::NotRunning); - } - - let drop = OnDrop::new(move || { - r.intenclr.write(|w| w.end().clear()); - r.events_stopped.reset(); - - // reset to dummy buffer - r.sample - .ptr - .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); - r.sample - .maxcnt - .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); - - while r.events_stopped.read().bits() == 0 {} - }); - - // setup user buffer - let ptr = buffer.as_ptr(); - let len = buffer.len(); - r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(ptr as u32) }); - r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(len as _) }); - - // wait till the current sample is finished and the user buffer sample is started - Self::wait_for_sample().await; - - // reset the buffer back to the dummy buffer - r.sample - .ptr - .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); - r.sample - .maxcnt - .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); - - // wait till the user buffer is sampled - Self::wait_for_sample().await; - - drop.defuse(); - - Ok(()) - } - - async fn wait_for_sample() { - let r = T::regs(); - - r.events_end.reset(); - r.intenset.write(|w| w.end().set()); - - compiler_fence(Ordering::SeqCst); - - poll_fn(|cx| { - T::state().waker.register(cx.waker()); - if r.events_end.read().bits() != 0 { - return Poll::Ready(()); - } - Poll::Pending - }) - .await; - - compiler_fence(Ordering::SeqCst); - } -} - -/// PDM microphone driver Config -pub struct Config { - /// Use stero or mono operation - pub operation_mode: OperationMode, - /// On which edge the left channel should be samples - pub edge: Edge, -} - -impl Default for Config { - fn default() -> Self { - Self { - operation_mode: OperationMode::Mono, - edge: Edge::LeftFalling, - } - } -} - -/// PDM operation mode. -#[derive(PartialEq)] -pub enum OperationMode { - /// Mono (1 channel) - Mono, - /// Stereo (2 channels) - Stereo, -} - -/// PDM edge polarity -#[derive(PartialEq)] -pub enum Edge { - /// Left edge is rising - LeftRising, - /// Left edge is falling - LeftFalling, -} - -impl<'d, T: Instance> Drop for Pdm<'d, T> { - fn drop(&mut self) { - let r = T::regs(); - - r.tasks_stop.write(|w| unsafe { w.bits(1) }); - - r.enable.write(|w| w.enable().disabled()); - - r.psel.din.reset(); - r.psel.clk.reset(); - } -} - -pub(crate) mod sealed { - use embassy_sync::waitqueue::AtomicWaker; - - /// Peripheral static state - pub struct State { - pub waker: AtomicWaker, - } - - impl State { - pub const fn new() -> Self { - Self { - waker: AtomicWaker::new(), - } - } - } - - pub trait Instance { - fn regs() -> &'static crate::pac::pdm::RegisterBlock; - fn state() -> &'static State; - } -} - -/// PDM peripheral instance. -pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { - /// Interrupt for this peripheral. - type Interrupt: interrupt::typelevel::Interrupt; -} - -macro_rules! impl_pdm { - ($type:ident, $pac_type:ident, $irq:ident) => { - impl crate::pdm::sealed::Instance for peripherals::$type { - fn regs() -> &'static crate::pac::pdm::RegisterBlock { - unsafe { &*pac::$pac_type::ptr() } - } - fn state() -> &'static crate::pdm::sealed::State { - static STATE: crate::pdm::sealed::State = crate::pdm::sealed::State::new(); - &STATE - } - } - impl crate::pdm::Instance for peripherals::$type { - type Interrupt = crate::interrupt::typelevel::$irq; - } - }; -} +//! Pulse Density Modulation (PDM) mirophone driver. + +#![macro_use] + +use core::marker::PhantomData; +use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::Poll; + +use embassy_hal_common::drop::OnDrop; +use embassy_hal_common::{into_ref, PeripheralRef}; +use fixed::types::I7F1; +use futures::future::poll_fn; + +use crate::chip::EASY_DMA_SIZE; +use crate::gpio::sealed::Pin; +use crate::gpio::{AnyPin, Pin as GpioPin}; +use crate::interrupt::typelevel::Interrupt; +use crate::pac::pdm::mode::{EDGE_A, OPERATION_A}; +pub use crate::pac::pdm::pdmclkctrl::FREQ_A as Frequency; +#[cfg(any( + feature = "nrf52840", + feature = "nrf52833", + feature = "_nrf5340-app", + feature = "_nrf9160", +))] +pub use crate::pac::pdm::ratio::RATIO_A as Ratio; +use crate::{interrupt, Peripheral}; + +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let r = T::regs(); + + if r.events_end.read().bits() != 0 { + r.intenclr.write(|w| w.end().clear()); + } + + if r.events_started.read().bits() != 0 { + r.intenclr.write(|w| w.started().clear()); + } + + if r.events_stopped.read().bits() != 0 { + r.intenclr.write(|w| w.stopped().clear()); + } + + T::state().waker.wake(); + } +} + +/// PDM microphone interface +pub struct Pdm<'d, T: Instance> { + _peri: PeripheralRef<'d, T>, +} + +/// PDM error. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + /// Buffer is too long. + BufferTooLong, + /// Buffer is empty + BufferZeroLength, + /// PDM is not running + NotRunning, + /// PDM is already running + AlreadyRunning, +} + +static DUMMY_BUFFER: [i16; 1] = [0; 1]; + +/// The state of a continuously running sampler. While it reflects +/// the progress of a sampler, it also signals what should be done +/// next. For example, if the sampler has stopped then the Pdm implementation +/// can then tear down its infrastructure. +#[derive(PartialEq)] +pub enum SamplerState { + /// The sampler processed the samples and is ready for more. + Sampled, + /// The sampler is done processing samples. + Stopped, +} + +impl<'d, T: Instance> Pdm<'d, T> { + /// Create PDM driver + pub fn new( + pdm: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + clk: impl Peripheral

+ 'd, + din: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(pdm, clk, din); + Self::new_inner(pdm, clk.map_into(), din.map_into(), config) + } + + fn new_inner( + pdm: PeripheralRef<'d, T>, + clk: PeripheralRef<'d, AnyPin>, + din: PeripheralRef<'d, AnyPin>, + config: Config, + ) -> Self { + into_ref!(pdm); + + let r = T::regs(); + + // setup gpio pins + din.conf().write(|w| w.input().set_bit()); + r.psel.din.write(|w| unsafe { w.bits(din.psel_bits()) }); + clk.set_low(); + clk.conf().write(|w| w.dir().output()); + r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) }); + + // configure + r.pdmclkctrl.write(|w| w.freq().variant(config.frequency)); + #[cfg(any( + feature = "nrf52840", + feature = "nrf52833", + feature = "_nrf5340-app", + feature = "_nrf9160", + ))] + r.ratio.write(|w| w.ratio().variant(config.ratio)); + r.mode.write(|w| { + w.operation().variant(config.operation_mode.into()); + w.edge().variant(config.edge.into()); + w + }); + + Self::_set_gain(r, config.gain_left, config.gain_right); + + // Disable all events interrupts + r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) }); + + // IRQ + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + r.enable.write(|w| w.enable().set_bit()); + + Self { _peri: pdm } + } + + fn _set_gain(r: &crate::pac::pdm::RegisterBlock, gain_left: I7F1, gain_right: I7F1) { + let gain_left = gain_left + .saturating_add(I7F1::from_bits(40)) + .saturating_to_num::() + .clamp(0, 0x50); + let gain_right = gain_right + .saturating_add(I7F1::from_bits(40)) + .saturating_to_num::() + .clamp(0, 0x50); + + r.gainl.write(|w| unsafe { w.gainl().bits(gain_left) }); + r.gainr.write(|w| unsafe { w.gainr().bits(gain_right) }); + } + + /// Adjust the gain of the PDM microphone on the fly + pub fn set_gain(&mut self, gain_left: I7F1, gain_right: I7F1) { + Self::_set_gain(T::regs(), gain_left, gain_right) + } + + /// Start sampling microphon data into a dummy buffer + /// Usefull to start the microphon and keep it active between recording samples + pub async fn start(&mut self) { + let r = T::regs(); + + // start dummy sampling because microphon needs some setup time + r.sample + .ptr + .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); + r.sample + .maxcnt + .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); + + r.tasks_start.write(|w| unsafe { w.bits(1) }); + } + + /// Stop sampling microphon data inta a dummy buffer + pub async fn stop(&mut self) { + let r = T::regs(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + r.events_started.reset(); + } + + /// Sample data into the given buffer. + pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> { + if buffer.len() == 0 { + return Err(Error::BufferZeroLength); + } + if buffer.len() > EASY_DMA_SIZE { + return Err(Error::BufferTooLong); + } + + let r = T::regs(); + + if r.events_started.read().bits() == 0 { + return Err(Error::NotRunning); + } + + let drop = OnDrop::new(move || { + r.intenclr.write(|w| w.end().clear()); + r.events_stopped.reset(); + + // reset to dummy buffer + r.sample + .ptr + .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); + r.sample + .maxcnt + .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); + + while r.events_stopped.read().bits() == 0 {} + }); + + // setup user buffer + let ptr = buffer.as_ptr(); + let len = buffer.len(); + r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(ptr as u32) }); + r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(len as _) }); + + // wait till the current sample is finished and the user buffer sample is started + Self::wait_for_sample().await; + + // reset the buffer back to the dummy buffer + r.sample + .ptr + .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); + r.sample + .maxcnt + .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); + + // wait till the user buffer is sampled + Self::wait_for_sample().await; + + drop.defuse(); + + Ok(()) + } + + async fn wait_for_sample() { + let r = T::regs(); + + r.events_end.reset(); + r.intenset.write(|w| w.end().set()); + + compiler_fence(Ordering::SeqCst); + + poll_fn(|cx| { + T::state().waker.register(cx.waker()); + if r.events_end.read().bits() != 0 { + return Poll::Ready(()); + } + Poll::Pending + }) + .await; + + compiler_fence(Ordering::SeqCst); + } + + /// Continuous sampling with double buffers. + /// + /// A sampler closure is provided that receives the buffer of samples, noting + /// that the size of this buffer can be less than the original buffer's size. + /// A command is return from the closure that indicates whether the sampling + /// should continue or stop. + /// + /// NOTE: The time spent within the callback supplied should not exceed the time + /// taken to acquire the samples into a single buffer. You should measure the + /// time taken by the callback and set the sample buffer size accordingly. + /// Exceeding this time can lead to samples becoming dropped. + pub async fn run_task_sampler( + &mut self, + bufs: &mut [[i16; N]; 2], + mut sampler: S, + ) -> Result<(), Error> + where + S: FnMut(&[i16; N]) -> SamplerState, + { + let r = T::regs(); + + if r.events_started.read().bits() != 0 { + return Err(Error::AlreadyRunning); + } + + r.sample + .ptr + .write(|w| unsafe { w.sampleptr().bits(bufs[0].as_mut_ptr() as u32) }); + r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(N as _) }); + + // Reset and enable the events + r.events_end.reset(); + r.events_started.reset(); + r.events_stopped.reset(); + r.intenset.write(|w| { + w.end().set(); + w.started().set(); + w.stopped().set(); + w + }); + + // Don't reorder the start event before the previous writes. Hopefully self + // wouldn't happen anyway. + compiler_fence(Ordering::SeqCst); + + r.tasks_start.write(|w| unsafe { w.bits(1) }); + + let mut current_buffer = 0; + + let mut done = false; + + let drop = OnDrop::new(|| { + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + // N.B. It would be better if this were async, but Drop only support sync code. + while r.events_stopped.read().bits() != 0 {} + }); + + // Wait for events and complete when the sampler indicates it has had enough. + poll_fn(|cx| { + let r = T::regs(); + + T::state().waker.register(cx.waker()); + + if r.events_end.read().bits() != 0 { + compiler_fence(Ordering::SeqCst); + + r.events_end.reset(); + r.intenset.write(|w| w.end().set()); + + if !done { + // Discard the last buffer after the user requested a stop. + if sampler(&bufs[current_buffer]) == SamplerState::Sampled { + let next_buffer = 1 - current_buffer; + current_buffer = next_buffer; + } else { + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + done = true; + }; + }; + } + + if r.events_started.read().bits() != 0 { + r.events_started.reset(); + r.intenset.write(|w| w.started().set()); + + let next_buffer = 1 - current_buffer; + r.sample + .ptr + .write(|w| unsafe { w.sampleptr().bits(bufs[next_buffer].as_mut_ptr() as u32) }); + } + + if r.events_stopped.read().bits() != 0 { + return Poll::Ready(()); + } + + Poll::Pending + }) + .await; + drop.defuse(); + Ok(()) + } +} + +/// PDM microphone driver Config +pub struct Config { + /// Use stero or mono operation + pub operation_mode: OperationMode, + /// On which edge the left channel should be samples + pub edge: Edge, + /// Clock frequency + pub frequency: Frequency, + /// Clock ratio + #[cfg(any( + feature = "nrf52840", + feature = "nrf52833", + feature = "_nrf5340-app", + feature = "_nrf9160", + ))] + pub ratio: Ratio, + /// Gain left in dB + pub gain_left: I7F1, + /// Gain right in dB + pub gain_right: I7F1, +} + +impl Default for Config { + fn default() -> Self { + Self { + operation_mode: OperationMode::Mono, + edge: Edge::LeftFalling, + frequency: Frequency::DEFAULT, + #[cfg(any( + feature = "nrf52840", + feature = "nrf52833", + feature = "_nrf5340-app", + feature = "_nrf9160", + ))] + ratio: Ratio::RATIO80, + gain_left: I7F1::ZERO, + gain_right: I7F1::ZERO, + } + } +} + +/// PDM operation mode. +#[derive(PartialEq)] +pub enum OperationMode { + /// Mono (1 channel) + Mono, + /// Stereo (2 channels) + Stereo, +} + +impl From 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 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

+ 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; + } + }; +} diff --git a/embassy-nrf/src/ppi/dppi.rs b/embassy-nrf/src/ppi/dppi.rs index 3a1e7f17..40ccb2f0 100644 --- a/embassy-nrf/src/ppi/dppi.rs +++ b/embassy-nrf/src/ppi/dppi.rs @@ -12,14 +12,14 @@ pub(crate) fn regs() -> &'static pac::dppic::RegisterBlock { impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> { /// Configure PPI channel to trigger `task` on `event`. - pub fn new_one_to_one(ch: impl Peripheral

+ 'd, event: Event, task: Task) -> Self { + pub fn new_one_to_one(ch: impl Peripheral

+ 'd, event: Event<'d>, task: Task<'d>) -> Self { Ppi::new_many_to_many(ch, [event], [task]) } } impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> { /// Configure PPI channel to trigger both `task1` and `task2` on `event`. - pub fn new_one_to_two(ch: impl Peripheral

+ 'd, event: Event, task1: Task, task2: Task) -> Self { + pub fn new_one_to_two(ch: impl Peripheral

+ 'd, event: Event<'d>, task1: Task<'d>, task2: Task<'d>) -> Self { Ppi::new_many_to_many(ch, [event], [task1, task2]) } } @@ -30,8 +30,8 @@ impl<'d, C: ConfigurableChannel, const EVENT_COUNT: usize, const TASK_COUNT: usi /// Configure a DPPI channel to trigger all `tasks` when any of the `events` fires. pub fn new_many_to_many( ch: impl Peripheral

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

+ 'd, event: Event, task: Task) -> Self { + pub fn new_one_to_one(ch: impl Peripheral

+ 'd, event: Event<'d>, task: Task<'d>) -> Self { into_ref!(ch); let r = regs(); @@ -49,7 +49,7 @@ impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> { #[cfg(not(feature = "nrf51"))] // Not for nrf51 because of the fork task impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> { /// Configure PPI channel to trigger both `task1` and `task2` on `event`. - pub fn new_one_to_two(ch: impl Peripheral

+ 'd, event: Event, task1: Task, task2: Task) -> Self { + pub fn new_one_to_two(ch: impl Peripheral

+ 'd, event: Event<'d>, task1: Task<'d>, task2: Task<'d>) -> Self { into_ref!(ch); let r = regs(); diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index 363a255d..c8c81fa0 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -181,7 +181,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// Returns reference to `Stopped` event endpoint for PPI. #[inline(always)] - pub fn event_stopped(&self) -> Event { + pub fn event_stopped(&self) -> Event<'d> { let r = T::regs(); Event::from_reg(&r.events_stopped) @@ -189,7 +189,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// Returns reference to `LoopsDone` event endpoint for PPI. #[inline(always)] - pub fn event_loops_done(&self) -> Event { + pub fn event_loops_done(&self) -> Event<'d> { let r = T::regs(); Event::from_reg(&r.events_loopsdone) @@ -197,7 +197,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// Returns reference to `PwmPeriodEnd` event endpoint for PPI. #[inline(always)] - pub fn event_pwm_period_end(&self) -> Event { + pub fn event_pwm_period_end(&self) -> Event<'d> { let r = T::regs(); Event::from_reg(&r.events_pwmperiodend) @@ -205,7 +205,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// Returns reference to `Seq0 End` event endpoint for PPI. #[inline(always)] - pub fn event_seq_end(&self) -> Event { + pub fn event_seq_end(&self) -> Event<'d> { let r = T::regs(); Event::from_reg(&r.events_seqend[0]) @@ -213,7 +213,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// Returns reference to `Seq1 End` event endpoint for PPI. #[inline(always)] - pub fn event_seq1_end(&self) -> Event { + pub fn event_seq1_end(&self) -> Event<'d> { let r = T::regs(); Event::from_reg(&r.events_seqend[1]) @@ -221,7 +221,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// Returns reference to `Seq0 Started` event endpoint for PPI. #[inline(always)] - pub fn event_seq0_started(&self) -> Event { + pub fn event_seq0_started(&self) -> Event<'d> { let r = T::regs(); Event::from_reg(&r.events_seqstarted[0]) @@ -229,7 +229,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// Returns reference to `Seq1 Started` event endpoint for PPI. #[inline(always)] - pub fn event_seq1_started(&self) -> Event { + pub fn event_seq1_started(&self) -> Event<'d> { let r = T::regs(); Event::from_reg(&r.events_seqstarted[1]) @@ -240,7 +240,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// /// Interacting with the sequence while it runs puts it in an unknown state #[inline(always)] - pub unsafe fn task_start_seq0(&self) -> Task { + pub unsafe fn task_start_seq0(&self) -> Task<'d> { let r = T::regs(); Task::from_reg(&r.tasks_seqstart[0]) @@ -251,7 +251,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// /// Interacting with the sequence while it runs puts it in an unknown state #[inline(always)] - pub unsafe fn task_start_seq1(&self) -> Task { + pub unsafe fn task_start_seq1(&self) -> Task<'d> { let r = T::regs(); Task::from_reg(&r.tasks_seqstart[1]) @@ -262,7 +262,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// /// Interacting with the sequence while it runs puts it in an unknown state #[inline(always)] - pub unsafe fn task_next_step(&self) -> Task { + pub unsafe fn task_next_step(&self) -> Task<'d> { let r = T::regs(); Task::from_reg(&r.tasks_nextstep) @@ -273,7 +273,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { /// /// Interacting with the sequence while it runs puts it in an unknown state #[inline(always)] - pub unsafe fn task_stop(&self) -> Task { + pub unsafe fn task_stop(&self) -> Task<'d> { let r = T::regs(); Task::from_reg(&r.tasks_stop) diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index cf3fb999..23292924 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -320,7 +320,9 @@ impl<'d, const N: usize> Saadc<'d, N> { timer.cc(0).write(sample_counter); timer.cc(0).short_compare_clear(); - let mut sample_ppi = Ppi::new_one_to_one(ppi_ch2, timer.cc(0).event_compare(), Task::from_reg(&r.tasks_sample)); + let timer_cc = timer.cc(0); + + let mut sample_ppi = Ppi::new_one_to_one(ppi_ch2, timer_cc.event_compare(), Task::from_reg(&r.tasks_sample)); timer.start(); diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index 66bbd1a8..b7dc332e 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -468,25 +468,19 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::spi::SpiBusFlush for Spim<'d, T> { + impl<'d, T: Instance> embedded_hal_1::spi::SpiBus for Spim<'d, T> { fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } - } - impl<'d, T: Instance> embedded_hal_1::spi::SpiBusRead for Spim<'d, T> { fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { self.blocking_transfer(words, &[]) } - } - impl<'d, T: Instance> embedded_hal_1::spi::SpiBusWrite for Spim<'d, T> { fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { self.blocking_write(words) } - } - impl<'d, T: Instance> embedded_hal_1::spi::SpiBus for Spim<'d, T> { fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { self.blocking_transfer(read, write) } @@ -502,25 +496,19 @@ mod eha { use super::*; - impl<'d, T: Instance> embedded_hal_async::spi::SpiBusFlush for Spim<'d, T> { + impl<'d, T: Instance> embedded_hal_async::spi::SpiBus for Spim<'d, T> { async fn flush(&mut self) -> Result<(), Error> { Ok(()) } - } - impl<'d, T: Instance> embedded_hal_async::spi::SpiBusRead for Spim<'d, T> { async fn read(&mut self, words: &mut [u8]) -> Result<(), Error> { self.read(words).await } - } - impl<'d, T: Instance> embedded_hal_async::spi::SpiBusWrite for Spim<'d, T> { async fn write(&mut self, data: &[u8]) -> Result<(), Error> { self.write(data).await } - } - impl<'d, T: Instance> embedded_hal_async::spi::SpiBus for Spim<'d, T> { async fn transfer(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> { self.transfer(rx, tx).await } diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index dc375785..04748238 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -168,21 +168,21 @@ impl<'d, T: Instance> Timer<'d, T> { /// Returns the START task, for use with PPI. /// /// When triggered, this task starts the timer. - pub fn task_start(&self) -> Task { + pub fn task_start(&self) -> Task<'d> { Task::from_reg(&T::regs().tasks_start) } /// Returns the STOP task, for use with PPI. /// /// When triggered, this task stops the timer. - pub fn task_stop(&self) -> Task { + pub fn task_stop(&self) -> Task<'d> { Task::from_reg(&T::regs().tasks_stop) } /// Returns the CLEAR task, for use with PPI. /// /// When triggered, this task resets the timer's counter to 0. - pub fn task_clear(&self) -> Task { + pub fn task_clear(&self) -> Task<'d> { Task::from_reg(&T::regs().tasks_clear) } @@ -190,7 +190,7 @@ impl<'d, T: Instance> Timer<'d, T> { /// /// When triggered, this task increments the timer's counter by 1. /// Only works in counter mode. - pub fn task_count(&self) -> Task { + pub fn task_count(&self) -> Task<'d> { Task::from_reg(&T::regs().tasks_count) } @@ -258,14 +258,14 @@ impl<'d, T: Instance> Cc<'d, T> { /// Returns this CC register's CAPTURE task, for use with PPI. /// /// When triggered, this task will capture the current value of the timer's counter in this register. - pub fn task_capture(&self) -> Task { + pub fn task_capture(&self) -> Task<'d> { Task::from_reg(&T::regs().tasks_capture) } /// Returns this CC register's COMPARE event, for use with PPI. /// /// This event will fire when the timer's counter reaches the value in this CC register. - pub fn event_compare(&self) -> Event { + pub fn event_compare(&self) -> Event<'d> { Event::from_reg(&T::regs().events_compare[self.n]) } diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 66823771..8f3ed885 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -56,7 +56,7 @@ unstable-traits = ["embedded-hal-1", "embedded-hal-nb"] [dependencies] embassy-sync = { version = "0.2.0", path = "../embassy-sync" } -embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] } +embassy-time = { version = "0.1.2", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common", features = ["cortex-m", "prio-bits-2"] } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } @@ -79,9 +79,9 @@ fixed = "1.23.1" rp-pac = { version = "6" } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} -embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true} -embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true} +embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true} +embedded-hal-nb = { version = "=1.0.0-alpha.3", optional = true} paste = "1.0" pio-proc = {version= "0.2" } diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index 699a0d61..95780c06 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -3,22 +3,17 @@ use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; +use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use embedded_hal_02::adc::{Channel, OneShot}; -use crate::gpio::Pin; +use crate::gpio::sealed::Pin as GpioPin; +use crate::gpio::{self, AnyPin, Pull}; use crate::interrupt::typelevel::Binding; use crate::interrupt::InterruptExt; use crate::peripherals::ADC; use crate::{interrupt, pac, peripherals, Peripheral}; -static WAKER: AtomicWaker = AtomicWaker::new(); -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[non_exhaustive] -pub enum Error { - // No errors for now -} +static WAKER: AtomicWaker = AtomicWaker::new(); #[non_exhaustive] pub struct Config {} @@ -28,11 +23,75 @@ impl Default for Config { Self {} } } -pub struct Adc<'d> { - phantom: PhantomData<&'d ADC>, + +pub struct Pin<'p> { + pin: PeripheralRef<'p, AnyPin>, } -impl<'d> Adc<'d> { +impl<'p> Pin<'p> { + pub fn new(pin: impl Peripheral

+ 'p, 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] fn regs() -> pac::adc::Adc { pac::ADC @@ -45,11 +104,7 @@ impl<'d> Adc<'d> { ret } - pub fn new( - _inner: impl Peripheral

+ 'd, - _irq: impl Binding, - _config: Config, - ) -> Self { + fn setup() { let reset = Self::reset(); crate::reset::reset(reset); crate::reset::unreset_wait(reset); @@ -58,6 +113,43 @@ impl<'d> Adc<'d> { r.cs().write(|w| w.set_en(true)); // Wait for ADC ready while !r.cs().read().ready() {} + } + + fn sample_blocking(channel: u8) -> Result { + 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 { + Self::sample_blocking(pin.channel()) + } + + pub fn blocking_read_temperature(&mut self) -> Result { + 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

+ 'd, + _irq: impl Binding, + _config: Config, + ) -> Self { + Self::setup(); // Setup IRQ interrupt::ADC_IRQ_FIFO.unpend(); @@ -80,76 +172,42 @@ impl<'d> Adc<'d> { .await; } - pub async fn read, ID = u8> + Pin>(&mut self, pin: &mut PIN) -> u16 { + async fn sample_async(channel: u8) -> Result { let r = Self::regs(); - // disable pull-down and pull-up resistors - // pull-down resistors are enabled by default - pin.pad_ctrl().modify(|w| { - w.set_ie(true); - let (pu, pd) = (false, false); - w.set_pue(pu); - w.set_pde(pd); - }); r.cs().modify(|w| { - w.set_ainsel(PIN::channel()); - w.set_start_once(true) + w.set_ainsel(channel); + w.set_start_once(true); + w.set_err(true); }); Self::wait_for_ready().await; - r.result().read().result().into() + match r.cs().read().err() { + true => Err(Error::ConversionFailed), + false => Ok(r.result().read().result().into()), + } } - pub async fn read_temperature(&mut self) -> u16 { + pub async fn read(&mut self, pin: &mut Pin<'_>) -> Result { + Self::sample_async(pin.channel()).await + } + + pub async fn read_temperature(&mut self) -> Result { let r = Self::regs(); r.cs().modify(|w| w.set_ts_en(true)); if !r.cs().read().ready() { Self::wait_for_ready().await; } - r.cs().modify(|w| { - w.set_ainsel(4); - w.set_start_once(true) - }); - Self::wait_for_ready().await; - r.result().read().result().into() - } - - pub fn blocking_read, ID = u8> + Pin>(&mut self, pin: &mut PIN) -> u16 { - let r = Self::regs(); - pin.pad_ctrl().modify(|w| { - w.set_ie(true); - let (pu, pd) = (false, false); - w.set_pue(pu); - w.set_pde(pd); - }); - r.cs().modify(|w| { - w.set_ainsel(PIN::channel()); - w.set_start_once(true) - }); - while !r.cs().read().ready() {} - r.result().read().result().into() - } - - pub fn blocking_read_temperature(&mut self) -> u16 { - let r = Self::regs(); - r.cs().modify(|w| w.set_ts_en(true)); - while !r.cs().read().ready() {} - r.cs().modify(|w| { - w.set_ainsel(4); - w.set_start_once(true) - }); - while !r.cs().read().ready() {} - r.result().read().result().into() + let result = Self::sample_async(4).await; + r.cs().modify(|w| w.set_ts_en(false)); + result } } -macro_rules! impl_pin { - ($pin:ident, $channel:expr) => { - impl Channel> for peripherals::$pin { - type ID = u8; - fn channel() -> u8 { - $channel - } - } - }; +impl<'d> Adc<'d, Blocking> { + pub fn new_blocking(_inner: impl Peripheral

+ 'd, _config: Config) -> Self { + Self::setup(); + + Self { phantom: PhantomData } + } } pub struct InterruptHandler { @@ -158,24 +216,33 @@ pub struct InterruptHandler { impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { - let r = Adc::regs(); + let r = Adc::::regs(); r.inte().write(|w| w.set_fifo(false)); WAKER.wake(); } } +mod sealed { + pub trait AdcPin: crate::gpio::sealed::Pin { + fn channel(&mut self) -> u8; + } +} + +pub trait AdcPin: sealed::AdcPin + gpio::Pin {} + +macro_rules! impl_pin { + ($pin:ident, $channel:expr) => { + impl sealed::AdcPin for peripherals::$pin { + fn channel(&mut self) -> u8 { + $channel + } + } + + impl AdcPin for peripherals::$pin {} + }; +} + impl_pin!(PIN_26, 0); impl_pin!(PIN_27, 1); impl_pin!(PIN_28, 2); impl_pin!(PIN_29, 3); - -impl OneShot, WORD, PIN> for Adc<'static> -where - WORD: From, - PIN: Channel, ID = u8> + Pin, -{ - type Error = (); - fn read(&mut self, pin: &mut PIN) -> nb::Result { - Ok(self.blocking_read(pin).into()) - } -} diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index ddd61d22..acb21dce 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -308,6 +308,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { // - QSPI (we're using it to run this code!) // - PLLs (it may be suicide if that's what's clocking us) // - USB, SYSCFG (breaks usb-to-swd on core1) + // - RTC (else there would be no more time...) let mut peris = reset::ALL_PERIPHERALS; peris.set_io_qspi(false); // peris.set_io_bank0(false); // might be suicide if we're clocked from gpin @@ -317,6 +318,7 @@ pub(crate) unsafe fn init(config: ClockConfig) { // TODO investigate if usb should be unreset here peris.set_usbctrl(false); peris.set_syscfg(false); + peris.set_rtc(false); reset::reset(peris); // Disable resus that may be enabled from previous software diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index f8048a4d..a3d330cd 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -41,7 +41,7 @@ impl From for bool { } /// Represents a pull setting for an input. -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum Pull { None, Up, @@ -566,13 +566,13 @@ impl<'d, T: Pin> Flex<'d, T> { /// Is the output level high? #[inline] pub fn is_set_high(&self) -> bool { - (self.pin.sio_out().value().read() & self.bit()) == 0 + !self.is_set_low() } /// Is the output level low? #[inline] pub fn is_set_low(&self) -> bool { - !self.is_set_high() + (self.pin.sio_out().value().read() & self.bit()) == 0 } /// What level output is set to diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 791c6455..9b85b234 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -716,6 +716,9 @@ mod nightly { async fn transaction(&mut self, address: A, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { let addr: u16 = address.into(); + if operations.len() > 0 { + Self::setup(addr)?; + } let mut iterator = operations.iter_mut(); while let Some(op) = iterator.next() { @@ -723,11 +726,9 @@ mod nightly { match op { Operation::Read(buffer) => { - Self::setup(addr)?; self.read_async_internal(buffer, false, last).await?; } Operation::Write(buffer) => { - Self::setup(addr)?; self.write_async_internal(buffer.into_iter().cloned(), last).await?; } } diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 4fd3cb46..4f205a16 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -252,7 +252,6 @@ pub fn init(config: config::Config) -> Peripherals { #[cfg(feature = "time-driver")] timer::init(); dma::init(); - pio::init(); gpio::init(); } diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 30648e8e..72a2f44e 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -16,12 +16,12 @@ use pio::{SideSet, Wrap}; use crate::dma::{Channel, Transfer, Word}; use crate::gpio::sealed::Pin as SealedPin; use crate::gpio::{self, AnyPin, Drive, Level, Pull, SlewRate}; -use crate::interrupt::InterruptExt; +use crate::interrupt::typelevel::{Binding, Handler, Interrupt}; use crate::pac::dma::vals::TreqSel; use crate::relocate::RelocatedProgram; -use crate::{interrupt, pac, peripherals, pio_instr_util, RegExt}; +use crate::{pac, peripherals, pio_instr_util, RegExt}; -struct Wakers([AtomicWaker; 12]); +pub struct Wakers([AtomicWaker; 12]); impl Wakers { #[inline(always)] @@ -38,10 +38,6 @@ impl Wakers { } } -const NEW_AW: AtomicWaker = AtomicWaker::new(); -const PIO_WAKERS_INIT: Wakers = Wakers([NEW_AW; 12]); -static WAKERS: [Wakers; 2] = [PIO_WAKERS_INIT; 2]; - #[derive(Clone, Copy, PartialEq, Eq, Default, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] @@ -85,42 +81,20 @@ const RXNEMPTY_MASK: u32 = 1 << 0; const TXNFULL_MASK: u32 = 1 << 4; const SMIRQ_MASK: u32 = 1 << 8; -#[cfg(feature = "rt")] -#[interrupt] -fn PIO0_IRQ_0() { - use crate::pac; - let ints = pac::PIO0.irqs(0).ints().read().0; - for bit in 0..12 { - if ints & (1 << bit) != 0 { - WAKERS[0].0[bit].wake(); - } - } - pac::PIO0.irqs(0).inte().write_clear(|m| m.0 = ints); +pub struct InterruptHandler { + _pio: PhantomData, } -#[cfg(feature = "rt")] -#[interrupt] -fn PIO1_IRQ_0() { - use crate::pac; - let ints = pac::PIO1.irqs(0).ints().read().0; - for bit in 0..12 { - if ints & (1 << bit) != 0 { - WAKERS[1].0[bit].wake(); +impl Handler for InterruptHandler { + unsafe fn on_interrupt() { + let ints = PIO::PIO.irqs(0).ints().read().0; + for bit in 0..12 { + if ints & (1 << bit) != 0 { + PIO::wakers().0[bit].wake(); + } } + PIO::PIO.irqs(0).inte().write_clear(|m| m.0 = ints); } - pac::PIO1.irqs(0).inte().write_clear(|m| m.0 = ints); -} - -pub(crate) unsafe fn init() { - interrupt::PIO0_IRQ_0.disable(); - interrupt::PIO0_IRQ_0.set_priority(interrupt::Priority::P3); - pac::PIO0.irqs(0).inte().write(|m| m.0 = 0); - interrupt::PIO0_IRQ_0.enable(); - - interrupt::PIO1_IRQ_0.disable(); - interrupt::PIO1_IRQ_0.set_priority(interrupt::Priority::P3); - pac::PIO1.irqs(0).inte().write(|m| m.0 = 0); - interrupt::PIO1_IRQ_0.enable(); } /// Future that waits for TX-FIFO to become writable @@ -144,7 +118,7 @@ impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoOutFuture<'a, 'd, PI if self.get_mut().sm_tx.try_push(value) { Poll::Ready(()) } else { - WAKERS[PIO::PIO_NO as usize].fifo_out()[SM].register(cx.waker()); + PIO::wakers().fifo_out()[SM].register(cx.waker()); PIO::PIO.irqs(0).inte().write_set(|m| { m.0 = TXNFULL_MASK << SM; }); @@ -181,7 +155,7 @@ impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoInFuture<'a, 'd, PIO if let Some(v) = self.sm_rx.try_pull() { Poll::Ready(v) } else { - WAKERS[PIO::PIO_NO as usize].fifo_in()[SM].register(cx.waker()); + PIO::wakers().fifo_in()[SM].register(cx.waker()); PIO::PIO.irqs(0).inte().write_set(|m| { m.0 = RXNEMPTY_MASK << SM; }); @@ -217,7 +191,7 @@ impl<'a, 'd, PIO: Instance> Future for IrqFuture<'a, 'd, PIO> { return Poll::Ready(()); } - WAKERS[PIO::PIO_NO as usize].irq()[self.irq_no as usize].register(cx.waker()); + PIO::wakers().irq()[self.irq_no as usize].register(cx.waker()); PIO::PIO.irqs(0).inte().write_set(|m| { m.0 = SMIRQ_MASK << self.irq_no; }); @@ -949,9 +923,11 @@ pub struct Pio<'d, PIO: Instance> { } impl<'d, PIO: Instance> Pio<'d, PIO> { - pub fn new(_pio: impl Peripheral

+ 'd) -> Self { + pub fn new(_pio: impl Peripheral

+ 'd, _irq: impl Binding>) -> Self { PIO::state().users.store(5, Ordering::Release); PIO::state().used_pins.store(0, Ordering::Release); + PIO::Interrupt::unpend(); + unsafe { PIO::Interrupt::enable() }; Self { common: Common { instructions_used: 0, @@ -1017,6 +993,15 @@ mod sealed { const PIO_NO: u8; const PIO: &'static crate::pac::pio::Pio; const FUNCSEL: crate::pac::io::vals::Gpio0ctrlFuncsel; + type Interrupt: crate::interrupt::typelevel::Interrupt; + + #[inline] + fn wakers() -> &'static Wakers { + const NEW_AW: AtomicWaker = AtomicWaker::new(); + static WAKERS: Wakers = Wakers([NEW_AW; 12]); + + &WAKERS + } #[inline] fn state() -> &'static State { @@ -1033,18 +1018,19 @@ mod sealed { pub trait Instance: sealed::Instance + Sized + Unpin {} macro_rules! impl_pio { - ($name:ident, $pio:expr, $pac:ident, $funcsel:ident) => { + ($name:ident, $pio:expr, $pac:ident, $funcsel:ident, $irq:ident) => { impl sealed::Instance for peripherals::$name { const PIO_NO: u8 = $pio; const PIO: &'static pac::pio::Pio = &pac::$pac; const FUNCSEL: pac::io::vals::Gpio0ctrlFuncsel = pac::io::vals::Gpio0ctrlFuncsel::$funcsel; + type Interrupt = crate::interrupt::typelevel::$irq; } impl Instance for peripherals::$name {} }; } -impl_pio!(PIO0, 0, PIO0, PIO0_0); -impl_pio!(PIO1, 1, PIO1, PIO1_0); +impl_pio!(PIO0, 0, PIO0, PIO0_0, PIO0_IRQ_0); +impl_pio!(PIO1, 1, PIO1, PIO1_0, PIO1_IRQ_0); pub trait PioPin: sealed::PioPin + gpio::Pin {} diff --git a/embassy-rp/src/rtc/datetime_no_deps.rs b/embassy-rp/src/rtc/datetime_no_deps.rs index 92770e98..ea899c33 100644 --- a/embassy-rp/src/rtc/datetime_no_deps.rs +++ b/embassy-rp/src/rtc/datetime_no_deps.rs @@ -25,6 +25,7 @@ pub enum Error { } /// Structure containing date and time information +#[derive(Clone, Debug)] pub struct DateTime { /// 0..4095 pub year: u16, diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs index b18f12fc..1b33fdf8 100644 --- a/embassy-rp/src/rtc/mod.rs +++ b/embassy-rp/src/rtc/mod.rs @@ -12,26 +12,24 @@ pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; use crate::clocks::clk_rtc_freq; /// A reference to the real time clock of the system -pub struct RealTimeClock<'d, T: Instance> { +pub struct Rtc<'d, T: Instance> { inner: PeripheralRef<'d, T>, } -impl<'d, T: Instance> RealTimeClock<'d, T> { +impl<'d, T: Instance> Rtc<'d, T> { /// Create a new instance of the real time clock, with the given date as an initial value. /// /// # Errors /// /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range. - pub fn new(inner: impl Peripheral

+ 'd, initial_date: DateTime) -> Result { + pub fn new(inner: impl Peripheral

+ 'd) -> Self { into_ref!(inner); // Set the RTC divider inner.regs().clkdiv_m1().write(|w| w.set_clkdiv_m1(clk_rtc_freq() - 1)); - let mut result = Self { inner }; - result.set_leap_year_check(true); // should be on by default, make sure this is the case. - result.set_datetime(initial_date)?; - Ok(result) + let result = Self { inner }; + result } /// Enable or disable the leap year check. The rp2040 chip will always add a Feb 29th on every year that is divisable by 4, but this may be incorrect (e.g. on century years). This function allows you to disable this check. @@ -43,7 +41,37 @@ impl<'d, T: Instance> RealTimeClock<'d, T> { }); } - /// Checks to see if this RealTimeClock is running + /// Set the time from internal format + pub fn restore(&mut self, ymd: rp_pac::rtc::regs::Rtc1, hms: rp_pac::rtc::regs::Rtc0) { + // disable RTC while we configure it + self.inner.regs().ctrl().modify(|w| w.set_rtc_enable(false)); + while self.inner.regs().ctrl().read().rtc_active() { + core::hint::spin_loop(); + } + + self.inner.regs().setup_0().write(|w| { + *w = rp_pac::rtc::regs::Setup0(ymd.0); + }); + self.inner.regs().setup_1().write(|w| { + *w = rp_pac::rtc::regs::Setup1(hms.0); + }); + + // Load the new datetime and re-enable RTC + self.inner.regs().ctrl().write(|w| w.set_load(true)); + self.inner.regs().ctrl().write(|w| w.set_rtc_enable(true)); + while !self.inner.regs().ctrl().read().rtc_active() { + core::hint::spin_loop(); + } + } + + /// Get the time in internal format + pub fn save(&mut self) -> (rp_pac::rtc::regs::Rtc1, rp_pac::rtc::regs::Rtc0) { + let rtc_0: rp_pac::rtc::regs::Rtc0 = self.inner.regs().rtc_0().read(); + let rtc_1 = self.inner.regs().rtc_1().read(); + (rtc_1, rtc_0) + } + + /// Checks to see if this Rtc is running pub fn is_running(&self) -> bool { self.inner.regs().ctrl().read().rtc_active() } @@ -113,8 +141,8 @@ impl<'d, T: Instance> RealTimeClock<'d, T> { /// # fn main() { } /// # #[cfg(not(feature = "chrono"))] /// # fn main() { - /// # use embassy_rp::rtc::{RealTimeClock, DateTimeFilter}; - /// # let mut real_time_clock: RealTimeClock = unsafe { core::mem::zeroed() }; + /// # use embassy_rp::rtc::{Rtc, DateTimeFilter}; + /// # let mut real_time_clock: Rtc = unsafe { core::mem::zeroed() }; /// let now = real_time_clock.now().unwrap(); /// real_time_clock.schedule_alarm( /// DateTimeFilter::default() @@ -150,7 +178,7 @@ impl<'d, T: Instance> RealTimeClock<'d, T> { } } -/// Errors that can occur on methods on [RealTimeClock] +/// Errors that can occur on methods on [Rtc] #[derive(Clone, Debug, PartialEq, Eq)] pub enum RtcError { /// An invalid DateTime was given or stored on the hardware. diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index e817d074..af101cf4 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -545,25 +545,19 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBusFlush for Spi<'d, T, M> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBus for Spi<'d, T, M> { fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } - } - impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBusRead for Spi<'d, T, M> { fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { self.blocking_transfer(words, &[]) } - } - impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBusWrite for Spi<'d, T, M> { fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { self.blocking_write(words) } - } - impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBus for Spi<'d, T, M> { fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { self.blocking_transfer(read, write) } @@ -578,30 +572,24 @@ mod eh1 { mod eha { use super::*; - impl<'d, T: Instance> embedded_hal_async::spi::SpiBusFlush for Spi<'d, T, Async> { + impl<'d, T: Instance> embedded_hal_async::spi::SpiBus for Spi<'d, T, Async> { async fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } - } - impl<'d, T: Instance> embedded_hal_async::spi::SpiBusWrite for Spi<'d, T, Async> { async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { self.write(words).await } - } - impl<'d, T: Instance> embedded_hal_async::spi::SpiBusRead for Spi<'d, T, Async> { async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { self.read(words).await } - } - impl<'d, T: Instance> embedded_hal_async::spi::SpiBus for Spi<'d, T, Async> { - async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> { + async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { self.transfer(read, write).await } - async fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Result<(), Self::Error> { + async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { self.transfer_in_place(words).await } } diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index b3f3bd92..4ab881f6 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -361,6 +361,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { let regs = T::regs(); let siestatus = regs.sie_status().read(); + let intrstatus = regs.intr().read(); if siestatus.resume() { regs.sie_status().write(|w| w.set_resume(true)); @@ -389,7 +390,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { return Poll::Ready(Event::Reset); } - if siestatus.suspended() { + if siestatus.suspended() && intrstatus.dev_suspend() { regs.sie_status().write(|w| w.set_suspended(true)); return Poll::Ready(Event::Suspend); } diff --git a/embassy-rp/src/watchdog.rs b/embassy-rp/src/watchdog.rs index d37795cc..f1e986ec 100644 --- a/embassy-rp/src/watchdog.rs +++ b/embassy-rp/src/watchdog.rs @@ -107,4 +107,36 @@ impl Watchdog { w.set_trigger(true); }) } + + /// Store data in scratch register + pub fn set_scratch(&mut self, index: usize, value: u32) { + let watchdog = pac::WATCHDOG; + match index { + 0 => watchdog.scratch0().write(|w| *w = value), + 1 => watchdog.scratch1().write(|w| *w = value), + 2 => watchdog.scratch2().write(|w| *w = value), + 3 => watchdog.scratch3().write(|w| *w = value), + 4 => watchdog.scratch4().write(|w| *w = value), + 5 => watchdog.scratch5().write(|w| *w = value), + 6 => watchdog.scratch6().write(|w| *w = value), + 7 => watchdog.scratch7().write(|w| *w = value), + _ => panic!("Invalid watchdog scratch index"), + } + } + + /// Read data from scratch register + pub fn get_scratch(&mut self, index: usize) -> u32 { + let watchdog = pac::WATCHDOG; + match index { + 0 => watchdog.scratch0().read(), + 1 => watchdog.scratch1().read(), + 2 => watchdog.scratch2().read(), + 3 => watchdog.scratch3().read(), + 4 => watchdog.scratch4().read(), + 5 => watchdog.scratch5().read(), + 6 => watchdog.scratch6().read(), + 7 => watchdog.scratch7().read(), + _ => panic!("Invalid watchdog scratch index"), + } + } } diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 4b830cab..6cd12220 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -5,18 +5,19 @@ edition = "2021" license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] -src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-wpan-v$VERSION/embassy-stm32-wpan/src" -src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32-wpan/src" +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-wpan-v$VERSION/embassy-stm32-wpan/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32-wpan/src/" target = "thumbv7em-none-eabihf" features = ["stm32wb55rg"] [dependencies] embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32" } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } -embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true } +embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" } embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" } +embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver", optional=true } defmt = { version = "0.3", optional = true } cortex-m = "0.7.6" @@ -25,13 +26,15 @@ aligned = "0.4.1" bit_field = "0.10.2" stm32-device-signature = { version = "0.3.3", features = ["stm32wb5x"] } -stm32wb-hci = { version = "0.1.2", features = ["version-5-0"], optional = true } +stm32wb-hci = { version = "0.1.3", optional = true } +futures = { version = "0.3.17", default-features = false, features = ["async-await"] } +bitflags = { version = "2.3.3", optional = true } [features] -defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt"] +defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "stm32wb-hci?/defmt"] ble = ["dep:stm32wb-hci"] -mac = [] +mac = ["dep:bitflags", "dep:embassy-net-driver" ] stm32wb10cc = [ "embassy-stm32/stm32wb10cc" ] stm32wb15cc = [ "embassy-stm32/stm32wb15cc" ] @@ -48,4 +51,4 @@ stm32wb55rg = [ "embassy-stm32/stm32wb55rg" ] stm32wb55vc = [ "embassy-stm32/stm32wb55vc" ] stm32wb55ve = [ "embassy-stm32/stm32wb55ve" ] stm32wb55vg = [ "embassy-stm32/stm32wb55vg" ] -stm32wb55vy = [ "embassy-stm32/stm32wb55vy" ] \ No newline at end of file +stm32wb55vy = [ "embassy-stm32/stm32wb55vy" ] diff --git a/embassy-stm32-wpan/src/cmd.rs b/embassy-stm32-wpan/src/cmd.rs index 8428b6ff..92835738 100644 --- a/embassy-stm32-wpan/src/cmd.rs +++ b/embassy-stm32-wpan/src/cmd.rs @@ -37,7 +37,7 @@ pub struct CmdSerialStub { } #[derive(Copy, Clone, Default)] -#[repr(C, packed(4))] +#[repr(C, packed)] pub struct CmdPacket { pub header: PacketHeader, pub cmdserial: CmdSerial, diff --git a/embassy-stm32-wpan/src/consts.rs b/embassy-stm32-wpan/src/consts.rs index f234151d..bd70851e 100644 --- a/embassy-stm32-wpan/src/consts.rs +++ b/embassy-stm32-wpan/src/consts.rs @@ -6,6 +6,8 @@ use crate::PacketHeader; #[derive(Debug)] #[repr(C)] pub enum TlPacketType { + MacCmd = 0x00, + BleCmd = 0x01, AclData = 0x02, BleEvt = 0x04, @@ -79,6 +81,7 @@ pub const CFG_TL_BLE_MOST_EVENT_PAYLOAD_SIZE: usize = 255; pub const TL_BLE_EVENT_FRAME_SIZE: usize = TL_EVT_HEADER_SIZE + CFG_TL_BLE_MOST_EVENT_PAYLOAD_SIZE; pub const POOL_SIZE: usize = CFG_TL_BLE_EVT_QUEUE_LENGTH * 4 * divc(TL_PACKET_HEADER_SIZE + TL_BLE_EVENT_FRAME_SIZE, 4); +pub const C_SIZE_CMD_STRING: usize = 256; pub const fn divc(x: usize, y: usize) -> usize { (x + y - 1) / y diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs index 99c61058..6836d7a8 100644 --- a/embassy-stm32-wpan/src/lib.rs +++ b/embassy-stm32-wpan/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] -#![cfg_attr(feature = "ble", feature(async_fn_in_trait))] +#![cfg_attr(any(feature = "ble", feature = "mac"), feature(async_fn_in_trait))] +#![cfg_attr(feature = "mac", feature(type_alias_impl_trait, concat_bytes))] // This must go FIRST so that all the other modules see its macros. pub mod fmt; @@ -26,6 +27,9 @@ pub mod sub; pub mod tables; pub mod unsafe_linked_list; +#[cfg(feature = "mac")] +pub mod mac; + #[cfg(feature = "ble")] pub use crate::sub::ble::hci; @@ -60,9 +64,9 @@ impl<'d> TlMbox<'d> { mem_manager_table: TL_MEM_MANAGER_TABLE.as_ptr(), traces_table: TL_TRACES_TABLE.as_ptr(), mac_802_15_4_table: TL_MAC_802_15_4_TABLE.as_ptr(), - // zigbee_table: TL_ZIGBEE_TABLE.as_ptr(), - // lld_tests_table: TL_LLD_TESTS_TABLE.as_ptr(), - // ble_lld_table: TL_BLE_LLD_TABLE.as_ptr(), + zigbee_table: TL_ZIGBEE_TABLE.as_ptr(), + lld_tests_table: TL_LLD_TESTS_TABLE.as_ptr(), + ble_lld_table: TL_BLE_LLD_TABLE.as_ptr(), }); TL_SYS_TABLE @@ -87,15 +91,15 @@ impl<'d> TlMbox<'d> { TL_MAC_802_15_4_TABLE .as_mut_ptr() .write_volatile(MaybeUninit::zeroed().assume_init()); - // TL_ZIGBEE_TABLE - // .as_mut_ptr() - // .write_volatile(MaybeUninit::zeroed().assume_init()); - // TL_LLD_TESTS_TABLE - // .as_mut_ptr() - // .write_volatile(MaybeUninit::zeroed().assume_init()); - // TL_BLE_LLD_TABLE - // .as_mut_ptr() - // .write_volatile(MaybeUninit::zeroed().assume_init()); + TL_ZIGBEE_TABLE + .as_mut_ptr() + .write_volatile(MaybeUninit::zeroed().assume_init()); + TL_LLD_TESTS_TABLE + .as_mut_ptr() + .write_volatile(MaybeUninit::zeroed().assume_init()); + TL_BLE_LLD_TABLE + .as_mut_ptr() + .write_volatile(MaybeUninit::zeroed().assume_init()); EVT_POOL .as_mut_ptr() @@ -103,18 +107,30 @@ impl<'d> TlMbox<'d> { SYS_SPARE_EVT_BUF .as_mut_ptr() .write_volatile(MaybeUninit::zeroed().assume_init()); - BLE_SPARE_EVT_BUF + CS_BUFFER .as_mut_ptr() .write_volatile(MaybeUninit::zeroed().assume_init()); + #[cfg(feature = "ble")] { + BLE_SPARE_EVT_BUF + .as_mut_ptr() + .write_volatile(MaybeUninit::zeroed().assume_init()); + BLE_CMD_BUFFER .as_mut_ptr() .write_volatile(MaybeUninit::zeroed().assume_init()); HCI_ACL_DATA_BUFFER .as_mut_ptr() .write_volatile(MaybeUninit::zeroed().assume_init()); - CS_BUFFER + } + + #[cfg(feature = "mac")] + { + MAC_802_15_4_CMD_BUFFER + .as_mut_ptr() + .write_volatile(MaybeUninit::zeroed().assume_init()); + MAC_802_15_4_NOTIF_RSP_EVT_BUFFER .as_mut_ptr() .write_volatile(MaybeUninit::zeroed().assume_init()); } diff --git a/embassy-stm32-wpan/src/mac/commands.rs b/embassy-stm32-wpan/src/mac/commands.rs new file mode 100644 index 00000000..8f6dcbbb --- /dev/null +++ b/embassy-stm32-wpan/src/mac/commands.rs @@ -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::()) } + } +} + +/// 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; +} diff --git a/embassy-stm32-wpan/src/mac/consts.rs b/embassy-stm32-wpan/src/mac/consts.rs new file mode 100644 index 00000000..56903d98 --- /dev/null +++ b/embassy-stm32-wpan/src/mac/consts.rs @@ -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; diff --git a/embassy-stm32-wpan/src/mac/control.rs b/embassy-stm32-wpan/src/mac/control.rs new file mode 100644 index 00000000..8a13de81 --- /dev/null +++ b/embassy-stm32-wpan/src/mac/control.rs @@ -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(&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(&self, cmd: &T) -> Result, 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.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; + }); + } +} diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs new file mode 100644 index 00000000..fffbb9ed --- /dev/null +++ b/embassy-stm32-wpan/src/mac/driver.rs @@ -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> { + 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, 1>, +} + +impl<'d> embassy_net_driver::RxToken for RxToken<'d> { + fn consume(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, + tx_buf: &'d Channel, +} + +impl<'d> embassy_net_driver::TxToken for TxToken<'d> { + fn consume(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 + } +} diff --git a/embassy-stm32-wpan/src/mac/event.rs b/embassy-stm32-wpan/src/mac/event.rs new file mode 100644 index 00000000..9ca4f5a2 --- /dev/null +++ b/embassy-stm32-wpan/src/mac/event.rs @@ -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::() { + 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) -> Result { + 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()) }; + } +} diff --git a/embassy-stm32-wpan/src/mac/indications.rs b/embassy-stm32-wpan/src/mac/indications.rs new file mode 100644 index 00000000..c0b86d74 --- /dev/null +++ b/embassy-stm32-wpan/src/mac/indications.rs @@ -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 {} diff --git a/embassy-stm32-wpan/src/mac/macros.rs b/embassy-stm32-wpan/src/mac/macros.rs new file mode 100644 index 00000000..1a988a77 --- /dev/null +++ b/embassy-stm32-wpan/src/mac/macros.rs @@ -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 { + match value { + $($constant => Ok( $name :: $enum ),)* + _ => Err(()) + } + } + } + + impl ::core::convert::From<$name> for $repr { + fn from(value: $name) -> $repr { + match value { + $($name :: $enum => $constant,)* + } + } + } + } +} diff --git a/embassy-stm32-wpan/src/mac/mod.rs b/embassy-stm32-wpan/src/mac/mod.rs new file mode 100644 index 00000000..c847a5cc --- /dev/null +++ b/embassy-stm32-wpan/src/mac/mod.rs @@ -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)) +} diff --git a/embassy-stm32-wpan/src/mac/opcodes.rs b/embassy-stm32-wpan/src/mac/opcodes.rs new file mode 100644 index 00000000..fd701187 --- /dev/null +++ b/embassy-stm32-wpan/src/mac/opcodes.rs @@ -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 for OpcodeM0ToM4 { + type Error = (); + + fn try_from(value: u16) -> Result { + 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(()), + } + } +} diff --git a/embassy-stm32-wpan/src/mac/responses.rs b/embassy-stm32-wpan/src/mac/responses.rs new file mode 100644 index 00000000..544fdaae --- /dev/null +++ b/embassy-stm32-wpan/src/mac/responses.rs @@ -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 {} diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/mac/runner.rs new file mode 100644 index 00000000..1be6df8a --- /dev/null +++ b/embassy-stm32-wpan/src/mac/runner.rs @@ -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 = blocking_mutex::Mutex>>>; + +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>, + pub(crate) read_mutex: Mutex, + pub(crate) write_mutex: Mutex, + pub(crate) rx_channel: Channel, 1>, + pub(crate) tx_channel: Channel, + pub(crate) tx_buf_channel: Channel, +} + +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 {} + } +} diff --git a/embassy-stm32-wpan/src/mac/typedefs.rs b/embassy-stm32-wpan/src/mac/typedefs.rs new file mode 100644 index 00000000..0552b8ea --- /dev/null +++ b/embassy-stm32-wpan/src/mac/typedefs.rs @@ -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 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 { + 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 { + 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]); +} diff --git a/embassy-stm32-wpan/src/sub/mac.rs b/embassy-stm32-wpan/src/sub/mac.rs index fd8af860..b0cf0248 100644 --- a/embassy-stm32-wpan/src/sub/mac.rs +++ b/embassy-stm32-wpan/src/sub/mac.rs @@ -11,9 +11,10 @@ use embassy_sync::waitqueue::AtomicWaker; use crate::cmd::CmdPacket; use crate::consts::TlPacketType; use crate::evt::{EvtBox, EvtPacket}; -use crate::tables::{ - Mac802_15_4Table, MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER, TL_MAC_802_15_4_TABLE, -}; +use crate::mac::commands::MacCommand; +use crate::mac::event::MacEvent; +use crate::mac::typedefs::MacError; +use crate::tables::{MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER}; use crate::{channels, evt}; static MAC_WAKER: AtomicWaker = AtomicWaker::new(); @@ -25,21 +26,13 @@ pub struct Mac { impl Mac { pub(crate) fn new() -> Self { - unsafe { - TL_MAC_802_15_4_TABLE.as_mut_ptr().write_volatile(Mac802_15_4Table { - p_cmdrsp_buffer: MAC_802_15_4_CMD_BUFFER.as_mut_ptr().cast(), - p_notack_buffer: MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr().cast(), - evt_queue: ptr::null_mut(), - }); - } - Self { phantom: PhantomData } } /// `HW_IPCC_MAC_802_15_4_EvtNot` /// /// This function will stall if the previous `EvtBox` has not been dropped - pub async fn read(&self) -> EvtBox { + pub async fn tl_read(&self) -> EvtBox { // Wait for the last event box to be dropped poll_fn(|cx| { MAC_WAKER.register(cx.waker()); @@ -63,9 +56,9 @@ impl Mac { } /// `HW_IPCC_MAC_802_15_4_CmdEvtNot` - pub async fn write_and_get_response(&self, opcode: u16, payload: &[u8]) -> u8 { - self.write(opcode, payload).await; - Ipcc::flush(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL).await; + pub async fn tl_write_and_get_response(&self, opcode: u16, payload: &[u8]) -> u8 { + self.tl_write(opcode, payload).await; + Ipcc::flush(channels::cpu1::IPCC_MAC_802_15_4_CMD_RSP_CHANNEL).await; unsafe { let p_event_packet = MAC_802_15_4_CMD_BUFFER.as_ptr() as *const EvtPacket; @@ -76,22 +69,41 @@ impl Mac { } /// `TL_MAC_802_15_4_SendCmd` - pub async fn write(&self, opcode: u16, payload: &[u8]) { + pub async fn tl_write(&self, opcode: u16, payload: &[u8]) { Ipcc::send(channels::cpu1::IPCC_MAC_802_15_4_CMD_RSP_CHANNEL, || unsafe { CmdPacket::write_into( MAC_802_15_4_CMD_BUFFER.as_mut_ptr(), - TlPacketType::OtCmd, + TlPacketType::MacCmd, opcode, payload, ); }) .await; } + + pub async fn send_command(&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::new(self.tl_read().await) + } } impl evt::MemoryManager for Mac { /// SAFETY: passing a pointer to something other than a managed event packet is UB unsafe fn drop_event_packet(_: *mut EvtPacket) { + trace!("mac drop event"); + // Write the ack CmdPacket::write_into( MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _, @@ -101,7 +113,7 @@ impl evt::MemoryManager for Mac { ); // Clear the rx flag - let _ = poll_once(Ipcc::receive::( + let _ = poll_once(Ipcc::receive::<()>( channels::cpu2::IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL, || None, )); diff --git a/embassy-stm32-wpan/src/sub/mm.rs b/embassy-stm32-wpan/src/sub/mm.rs index 1f2ecac2..da05ad1d 100644 --- a/embassy-stm32-wpan/src/sub/mm.rs +++ b/embassy-stm32-wpan/src/sub/mm.rs @@ -4,20 +4,21 @@ use core::marker::PhantomData; use core::mem::MaybeUninit; use core::task::Poll; +use aligned::{Aligned, A4}; use cortex_m::interrupt; use embassy_stm32::ipcc::Ipcc; use embassy_sync::waitqueue::AtomicWaker; use crate::consts::POOL_SIZE; use crate::evt::EvtPacket; -use crate::tables::{ - MemManagerTable, BLE_SPARE_EVT_BUF, EVT_POOL, FREE_BUF_QUEUE, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE, -}; +#[cfg(feature = "ble")] +use crate::tables::BLE_SPARE_EVT_BUF; +use crate::tables::{MemManagerTable, EVT_POOL, FREE_BUF_QUEUE, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE}; use crate::unsafe_linked_list::LinkedListNode; use crate::{channels, evt}; static MM_WAKER: AtomicWaker = AtomicWaker::new(); -static mut LOCAL_FREE_BUF_QUEUE: MaybeUninit = MaybeUninit::uninit(); +static mut LOCAL_FREE_BUF_QUEUE: Aligned> = Aligned(MaybeUninit::uninit()); pub struct MemoryManager { phantom: PhantomData, @@ -30,7 +31,10 @@ impl MemoryManager { LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()); TL_MEM_MANAGER_TABLE.as_mut_ptr().write_volatile(MemManagerTable { + #[cfg(feature = "ble")] spare_ble_buffer: BLE_SPARE_EVT_BUF.as_ptr().cast(), + #[cfg(not(feature = "ble"))] + spare_ble_buffer: core::ptr::null(), spare_sys_buffer: SYS_SPARE_EVT_BUF.as_ptr().cast(), blepool: EVT_POOL.as_ptr().cast(), blepoolsize: POOL_SIZE as u32, diff --git a/embassy-stm32-wpan/src/sub/sys.rs b/embassy-stm32-wpan/src/sub/sys.rs index af652860..c17fd531 100644 --- a/embassy-stm32-wpan/src/sub/sys.rs +++ b/embassy-stm32-wpan/src/sub/sys.rs @@ -50,7 +50,7 @@ impl Sys { } /// `HW_IPCC_SYS_CmdEvtNot` - pub async fn write_and_get_response(&self, opcode: ShciOpcode, payload: &[u8]) -> SchiCommandStatus { + pub async fn write_and_get_response(&self, opcode: ShciOpcode, payload: &[u8]) -> Result { self.write(opcode, payload).await; Ipcc::flush(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL).await; @@ -59,17 +59,36 @@ impl Sys { let p_command_event = &((*p_event_packet).evt_serial.evt.payload) as *const _ as *const CcEvt; let p_payload = &((*p_command_event).payload) as *const u8; - ptr::read_volatile(p_payload).try_into().unwrap() + ptr::read_volatile(p_payload).try_into() } } #[cfg(feature = "mac")] - pub async fn shci_c2_mac_802_15_4_init(&self) -> SchiCommandStatus { + pub async fn shci_c2_mac_802_15_4_init(&self) -> Result { + use crate::tables::{ + Mac802_15_4Table, TracesTable, MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER, + TL_MAC_802_15_4_TABLE, TL_TRACES_TABLE, TRACES_EVT_QUEUE, + }; + + unsafe { + LinkedListNode::init_head(TRACES_EVT_QUEUE.as_mut_ptr() as *mut _); + + TL_TRACES_TABLE.as_mut_ptr().write_volatile(TracesTable { + traces_queue: TRACES_EVT_QUEUE.as_ptr() as *const _, + }); + + TL_MAC_802_15_4_TABLE.as_mut_ptr().write_volatile(Mac802_15_4Table { + p_cmdrsp_buffer: MAC_802_15_4_CMD_BUFFER.as_mut_ptr().cast(), + p_notack_buffer: MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr().cast(), + evt_queue: core::ptr::null_mut(), + }); + }; + self.write_and_get_response(ShciOpcode::Mac802_15_4Init, &[]).await } #[cfg(feature = "ble")] - pub async fn shci_c2_ble_init(&self, param: ShciBleInitCmdParam) -> SchiCommandStatus { + pub async fn shci_c2_ble_init(&self, param: ShciBleInitCmdParam) -> Result { self.write_and_get_response(ShciOpcode::BleInit, param.payload()).await } diff --git a/embassy-stm32-wpan/src/tables.rs b/embassy-stm32-wpan/src/tables.rs index 1b5dcdf2..f2c25052 100644 --- a/embassy-stm32-wpan/src/tables.rs +++ b/embassy-stm32-wpan/src/tables.rs @@ -4,6 +4,8 @@ use aligned::{Aligned, A4}; use bit_field::BitField; use crate::cmd::{AclDataPacket, CmdPacket}; +#[cfg(feature = "mac")] +use crate::consts::C_SIZE_CMD_STRING; use crate::consts::{POOL_SIZE, TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE}; use crate::unsafe_linked_list::LinkedListNode; @@ -80,7 +82,7 @@ impl WirelessFwInfoTable { } #[derive(Debug, Clone)] -#[repr(C, align(4))] +#[repr(C)] pub struct DeviceInfoTable { pub safe_boot_info_table: SafeBootInfoTable, pub rss_info_table: RssInfoTable, @@ -88,7 +90,7 @@ pub struct DeviceInfoTable { } #[derive(Debug)] -#[repr(C, align(4))] +#[repr(C)] pub struct BleTable { pub pcmd_buffer: *mut CmdPacket, pub pcs_buffer: *const u8, @@ -97,16 +99,15 @@ pub struct BleTable { } #[derive(Debug)] -#[repr(C, align(4))] +#[repr(C)] pub struct ThreadTable { pub nostack_buffer: *const u8, pub clicmdrsp_buffer: *const u8, pub otcmdrsp_buffer: *const u8, } -// TODO: use later #[derive(Debug)] -#[repr(C, align(4))] +#[repr(C)] pub struct LldTestsTable { pub clicmdrsp_buffer: *const u8, pub m0cmd_buffer: *const u8, @@ -114,7 +115,7 @@ pub struct LldTestsTable { // TODO: use later #[derive(Debug)] -#[repr(C, align(4))] +#[repr(C)] pub struct BleLldTable { pub cmdrsp_buffer: *const u8, pub m0cmd_buffer: *const u8, @@ -122,7 +123,7 @@ pub struct BleLldTable { // TODO: use later #[derive(Debug)] -#[repr(C, align(4))] +#[repr(C)] pub struct ZigbeeTable { pub notif_m0_to_m4_buffer: *const u8, pub appli_cmd_m4_to_m0_bufer: *const u8, @@ -130,14 +131,14 @@ pub struct ZigbeeTable { } #[derive(Debug)] -#[repr(C, align(4))] +#[repr(C)] pub struct SysTable { pub pcmd_buffer: *mut CmdPacket, pub sys_queue: *const LinkedListNode, } #[derive(Debug)] -#[repr(C, align(4))] +#[repr(C)] pub struct MemManagerTable { pub spare_ble_buffer: *const u8, pub spare_sys_buffer: *const u8, @@ -152,13 +153,13 @@ pub struct MemManagerTable { } #[derive(Debug)] -#[repr(C, align(4))] +#[repr(C)] pub struct TracesTable { pub traces_queue: *const u8, } #[derive(Debug)] -#[repr(C, align(4))] +#[repr(C)] pub struct Mac802_15_4Table { pub p_cmdrsp_buffer: *const u8, pub p_notack_buffer: *const u8, @@ -176,6 +177,9 @@ pub struct RefTable { pub mem_manager_table: *const MemManagerTable, pub traces_table: *const TracesTable, pub mac_802_15_4_table: *const Mac802_15_4Table, + pub zigbee_table: *const ZigbeeTable, + pub lld_tests_table: *const LldTestsTable, + pub ble_lld_table: *const BleLldTable, } // --------------------- ref table --------------------- @@ -183,57 +187,57 @@ pub struct RefTable { pub static mut TL_REF_TABLE: MaybeUninit = MaybeUninit::uninit(); #[link_section = "MB_MEM1"] -pub static mut TL_DEVICE_INFO_TABLE: MaybeUninit = MaybeUninit::uninit(); +pub static mut TL_DEVICE_INFO_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); #[link_section = "MB_MEM1"] -pub static mut TL_BLE_TABLE: MaybeUninit = MaybeUninit::uninit(); +pub static mut TL_BLE_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); #[link_section = "MB_MEM1"] -pub static mut TL_THREAD_TABLE: MaybeUninit = MaybeUninit::uninit(); - -// #[link_section = "MB_MEM1"] -// pub static mut TL_LLD_TESTS_TABLE: MaybeUninit = MaybeUninit::uninit(); - -// #[link_section = "MB_MEM1"] -// pub static mut TL_BLE_LLD_TABLE: MaybeUninit = MaybeUninit::uninit(); +pub static mut TL_THREAD_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); #[link_section = "MB_MEM1"] -pub static mut TL_SYS_TABLE: MaybeUninit = MaybeUninit::uninit(); +pub static mut TL_LLD_TESTS_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); #[link_section = "MB_MEM1"] -pub static mut TL_MEM_MANAGER_TABLE: MaybeUninit = MaybeUninit::uninit(); +pub static mut TL_BLE_LLD_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); #[link_section = "MB_MEM1"] -pub static mut TL_TRACES_TABLE: MaybeUninit = MaybeUninit::uninit(); +pub static mut TL_SYS_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); #[link_section = "MB_MEM1"] -pub static mut TL_MAC_802_15_4_TABLE: MaybeUninit = MaybeUninit::uninit(); +pub static mut TL_MEM_MANAGER_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -// #[link_section = "MB_MEM1"] -// pub static mut TL_ZIGBEE_TABLE: MaybeUninit = MaybeUninit::uninit(); +#[link_section = "MB_MEM1"] +pub static mut TL_TRACES_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); + +#[link_section = "MB_MEM1"] +pub static mut TL_MAC_802_15_4_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); + +#[link_section = "MB_MEM1"] +pub static mut TL_ZIGBEE_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); // --------------------- tables --------------------- #[link_section = "MB_MEM1"] -pub static mut FREE_BUF_QUEUE: MaybeUninit = MaybeUninit::uninit(); +pub static mut FREE_BUF_QUEUE: Aligned> = Aligned(MaybeUninit::uninit()); #[allow(dead_code)] #[link_section = "MB_MEM1"] -pub static mut TRACES_EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); +pub static mut TRACES_EVT_QUEUE: Aligned> = Aligned(MaybeUninit::uninit()); #[link_section = "MB_MEM2"] -pub static mut CS_BUFFER: MaybeUninit> = - MaybeUninit::uninit(); +pub static mut CS_BUFFER: Aligned> = + Aligned(MaybeUninit::uninit()); #[link_section = "MB_MEM2"] -pub static mut EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); +pub static mut EVT_QUEUE: Aligned> = Aligned(MaybeUninit::uninit()); #[link_section = "MB_MEM2"] -pub static mut SYSTEM_EVT_QUEUE: MaybeUninit = MaybeUninit::uninit(); +pub static mut SYSTEM_EVT_QUEUE: Aligned> = Aligned(MaybeUninit::uninit()); // --------------------- app tables --------------------- #[cfg(feature = "mac")] #[link_section = "MB_MEM2"] -pub static mut MAC_802_15_4_CMD_BUFFER: MaybeUninit = MaybeUninit::uninit(); +pub static mut MAC_802_15_4_CMD_BUFFER: Aligned> = Aligned(MaybeUninit::uninit()); #[cfg(feature = "mac")] #[link_section = "MB_MEM2"] @@ -242,23 +246,31 @@ pub static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit< > = MaybeUninit::uninit(); #[link_section = "MB_MEM2"] -pub static mut EVT_POOL: MaybeUninit> = MaybeUninit::uninit(); +pub static mut EVT_POOL: Aligned> = Aligned(MaybeUninit::uninit()); #[link_section = "MB_MEM2"] -pub static mut SYS_CMD_BUF: MaybeUninit = MaybeUninit::uninit(); +pub static mut SYS_CMD_BUF: Aligned> = Aligned(MaybeUninit::uninit()); #[link_section = "MB_MEM2"] -pub static mut SYS_SPARE_EVT_BUF: MaybeUninit> = - MaybeUninit::uninit(); +pub static mut SYS_SPARE_EVT_BUF: Aligned> = + Aligned(MaybeUninit::uninit()); +#[cfg(feature = "mac")] +#[link_section = "MB_MEM2"] +pub static mut MAC_802_15_4_CNFINDNOT: Aligned> = + Aligned(MaybeUninit::uninit()); + +#[cfg(feature = "ble")] #[link_section = "MB_MEM1"] -pub static mut BLE_CMD_BUFFER: MaybeUninit = MaybeUninit::uninit(); +pub static mut BLE_CMD_BUFFER: Aligned> = Aligned(MaybeUninit::uninit()); +#[cfg(feature = "ble")] #[link_section = "MB_MEM2"] -pub static mut BLE_SPARE_EVT_BUF: MaybeUninit> = - MaybeUninit::uninit(); +pub static mut BLE_SPARE_EVT_BUF: Aligned> = + Aligned(MaybeUninit::uninit()); +#[cfg(feature = "ble")] #[link_section = "MB_MEM2"] // fuck these "magic" numbers from ST ---v---v -pub static mut HCI_ACL_DATA_BUFFER: MaybeUninit> = - MaybeUninit::uninit(); +pub static mut HCI_ACL_DATA_BUFFER: Aligned> = + Aligned(MaybeUninit::uninit()); diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index b3fe9c1f..0fb6fdb5 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -32,7 +32,7 @@ flavors = [ [dependencies] embassy-sync = { version = "0.2.0", path = "../embassy-sync" } -embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true } +embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common", features = ["cortex-m", "prio-bits-4"] } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } @@ -40,9 +40,9 @@ embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} -embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true} -embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true} +embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true} +embedded-hal-nb = { version = "=1.0.0-alpha.3", optional = true} embedded-storage = "0.3.0" embedded-storage-async = { version = "0.4.0", optional = true } @@ -57,7 +57,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = "12" +stm32-metapac = "13" vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -74,7 +74,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { version = "12", default-features = false, features = ["metadata"]} +stm32-metapac = { version = "13", default-features = false, features = ["metadata"]} [features] default = ["rt"] diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index d628bcf0..d2b1cfd0 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -348,9 +348,7 @@ fn main() { g.extend(quote! { impl crate::rcc::sealed::RccPeripheral for peripherals::#pname { fn frequency() -> crate::time::Hertz { - critical_section::with(|_| unsafe { - crate::rcc::get_freqs().#clk - }) + unsafe { crate::rcc::get_freqs().#clk } } fn enable() { critical_section::with(|_| { diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 56ecd63c..94a8538b 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -1,5 +1,6 @@ #![macro_use] +#[cfg(not(adc_f3))] #[cfg_attr(adc_f1, path = "f1.rs")] #[cfg_attr(adc_v1, path = "v1.rs")] #[cfg_attr(adc_v2, path = "v2.rs")] @@ -7,14 +8,16 @@ #[cfg_attr(adc_v4, path = "v4.rs")] mod _version; -#[cfg(not(adc_f1))] +#[cfg(not(any(adc_f1, adc_f3)))] mod resolution; mod sample_time; +#[cfg(not(adc_f3))] #[allow(unused)] pub use _version::*; -#[cfg(not(adc_f1))] +#[cfg(not(any(adc_f1, adc_f3)))] pub use resolution::Resolution; +#[cfg(not(adc_f3))] pub use sample_time::SampleTime; use crate::peripherals; @@ -22,13 +25,14 @@ use crate::peripherals; pub struct Adc<'d, T: Instance> { #[allow(unused)] adc: crate::PeripheralRef<'d, T>, + #[cfg(not(adc_f3))] sample_time: SampleTime, } pub(crate) mod sealed { pub trait Instance { fn regs() -> crate::pac::adc::Adc; - #[cfg(all(not(adc_f1), not(adc_v1)))] + #[cfg(not(any(adc_f1, adc_v1, adc_f3)))] fn common_regs() -> crate::pac::adccommon::AdcCommon; } @@ -56,7 +60,7 @@ foreach_peripheral!( fn regs() -> crate::pac::adc::Adc { crate::pac::$inst } - #[cfg(all(not(adc_f1), not(adc_v1)))] + #[cfg(not(any(adc_f1, adc_v1, adc_f3)))] fn common_regs() -> crate::pac::adccommon::AdcCommon { foreach_peripheral!{ (adccommon, $common_inst:ident) => { diff --git a/embassy-stm32/src/adc/sample_time.rs b/embassy-stm32/src/adc/sample_time.rs index 0faa1e3c..df052556 100644 --- a/embassy-stm32/src/adc/sample_time.rs +++ b/embassy-stm32/src/adc/sample_time.rs @@ -1,3 +1,4 @@ +#[cfg(not(adc_f3))] macro_rules! impl_sample_time { ($default_doc:expr, $default:ident, ($(($doc:expr, $variant:ident, $pac_variant:ident)),*)) => { #[doc = concat!("ADC sample time\n\nThe default setting is ", $default_doc, " ADC clock cycles.")] diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 73861776..5a015346 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -1,3 +1,4 @@ +use core::cell::{RefCell, RefMut}; use core::future::poll_fn; use core::marker::PhantomData; use core::ops::{Deref, DerefMut}; @@ -72,7 +73,7 @@ impl interrupt::typelevel::Handler for SceInterrup } pub struct Can<'d, T: Instance> { - can: bxcan::Can>, + pub can: RefCell>>, } #[derive(Debug)] @@ -147,19 +148,24 @@ impl<'d, T: Instance> Can<'d, T> { tx.set_as_af(tx.af_num(), AFType::OutputPushPull); let can = bxcan::Can::builder(BxcanInstance(peri)).leave_disabled(); - Self { can } + let can_ref_cell = RefCell::new(can); + Self { can: can_ref_cell } } pub fn set_bitrate(&mut self, bitrate: u32) { let bit_timing = Self::calc_bxcan_timings(T::frequency(), bitrate).unwrap(); - self.can.modify_config().set_bit_timing(bit_timing).leave_disabled(); + self.can + .borrow_mut() + .modify_config() + .set_bit_timing(bit_timing) + .leave_disabled(); } /// Queues the message to be sent but exerts backpressure pub async fn write(&mut self, frame: &Frame) -> bxcan::TransmitStatus { poll_fn(|cx| { T::state().tx_waker.register(cx.waker()); - if let Ok(status) = self.can.transmit(frame) { + if let Ok(status) = self.can.borrow_mut().transmit(frame) { return Poll::Ready(status); } @@ -341,6 +347,79 @@ impl<'d, T: Instance> Can<'d, T> { // Pack into BTR register values Some((sjw - 1) << 24 | (bs1 as u32 - 1) << 16 | (bs2 as u32 - 1) << 20 | (prescaler as u32 - 1)) } + + pub fn split<'c>(&'c self) -> (CanTx<'c, 'd, T>, CanRx<'c, 'd, T>) { + (CanTx { can: &self.can }, CanRx { can: &self.can }) + } + + pub fn as_mut(&self) -> RefMut<'_, bxcan::Can>> { + self.can.borrow_mut() + } +} + +pub struct CanTx<'c, 'd, T: Instance> { + can: &'c RefCell>>, +} + +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>>, +} + +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 { + let err = { T::regs().esr().read() }; + if err.boff() { + return Some(BusError::BusOff); + } else if err.epvf() { + return Some(BusError::BusPassive); + } else if err.ewgf() { + return Some(BusError::BusWarning); + } else if let Some(err) = err.lec().into_bus_err() { + return Some(err); + } + None + } } enum RxFifo { @@ -358,7 +437,7 @@ impl<'d, T: Instance> Drop for Can<'d, T> { } impl<'d, T: Instance> Deref for Can<'d, T> { - type Target = bxcan::Can>; + type Target = RefCell>>; fn deref(&self) -> &Self::Target { &self.can diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs new file mode 100644 index 00000000..c31a7fc6 --- /dev/null +++ b/embassy-stm32/src/can/fdcan.rs @@ -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, + } + + 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); diff --git a/embassy-stm32/src/can/mod.rs b/embassy-stm32/src/can/mod.rs index c7e2e620..4ff5aa0d 100644 --- a/embassy-stm32/src/can/mod.rs +++ b/embassy-stm32/src/can/mod.rs @@ -1,5 +1,6 @@ #![macro_use] #[cfg_attr(can_bxcan, path = "bxcan.rs")] +#[cfg_attr(can_fdcan, path = "fdcan.rs")] mod _version; pub use _version::*; diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 1dc13949..3d58914b 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -51,7 +51,10 @@ impl Ch1Trigger { fn tsel(&self) -> dac::vals::Tsel1 { match self { Ch1Trigger::Tim6 => dac::vals::Tsel1::TIM6_TRGO, + #[cfg(not(dac_v3))] Ch1Trigger::Tim3 => dac::vals::Tsel1::TIM3_TRGO, + #[cfg(dac_v3)] + Ch1Trigger::Tim3 => dac::vals::Tsel1::TIM1_TRGO, Ch1Trigger::Tim7 => dac::vals::Tsel1::TIM7_TRGO, Ch1Trigger::Tim15 => dac::vals::Tsel1::TIM15_TRGO, Ch1Trigger::Tim2 => dac::vals::Tsel1::TIM2_TRGO, @@ -264,7 +267,7 @@ impl<'d, T: Instance, Tx> DacCh1<'d, T, Tx> { }); let tx_request = self.dma.request(); - let dma_channel = &self.dma; + let dma_channel = &mut self.dma; let tx_options = crate::dma::TransferOptions { circular, @@ -376,7 +379,7 @@ impl<'d, T: Instance, Tx> DacCh2<'d, T, Tx> { }); let tx_request = self.dma.request(); - let dma_channel = &self.dma; + let dma_channel = &mut self.dma; let tx_options = crate::dma::TransferOptions { circular, diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 8abe541d..58d438af 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -1,10 +1,9 @@ use core::future::Future; use core::marker::PhantomData; use core::pin::Pin; -use core::sync::atomic::{fence, Ordering}; +use core::sync::atomic::{fence, AtomicUsize, Ordering}; use core::task::{Context, Poll, Waker}; -use atomic_polyfill::AtomicUsize; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; diff --git a/embassy-stm32/src/eth/generic_smi.rs b/embassy-stm32/src/eth/generic_smi.rs index 96825604..2ed46ca2 100644 --- a/embassy-stm32/src/eth/generic_smi.rs +++ b/embassy-stm32/src/eth/generic_smi.rs @@ -1,5 +1,11 @@ //! Generic SMI Ethernet PHY +#[cfg(feature = "time")] +use embassy_time::{Duration, Timer}; +use futures::task::Context; +#[cfg(feature = "time")] +use futures::FutureExt; + use super::{StationManagement, PHY}; #[allow(dead_code)] @@ -36,25 +42,47 @@ mod phy_consts { use self::phy_consts::*; /// Generic SMI Ethernet PHY -pub struct GenericSMI; +pub struct GenericSMI { + #[cfg(feature = "time")] + poll_interval: Duration, + #[cfg(not(feature = "time"))] + _private: (), +} + +impl GenericSMI { + pub fn new() -> Self { + Self { + #[cfg(feature = "time")] + poll_interval: Duration::from_millis(500), + #[cfg(not(feature = "time"))] + _private: (), + } + } +} unsafe impl PHY for GenericSMI { /// Reset PHY and wait for it to come out of reset. - fn phy_reset(sm: &mut S) { + fn phy_reset(&mut self, sm: &mut S) { sm.smi_write(PHY_REG_BCR, PHY_REG_BCR_RESET); while sm.smi_read(PHY_REG_BCR) & PHY_REG_BCR_RESET == PHY_REG_BCR_RESET {} } /// PHY initialisation. - fn phy_init(sm: &mut S) { + fn phy_init(&mut self, sm: &mut S) { // Clear WU CSR - Self::smi_write_ext(sm, PHY_REG_WUCSR, 0); + self.smi_write_ext(sm, PHY_REG_WUCSR, 0); // Enable auto-negotiation sm.smi_write(PHY_REG_BCR, PHY_REG_BCR_AN | PHY_REG_BCR_ANRST | PHY_REG_BCR_100M); } - fn poll_link(sm: &mut S) -> bool { + fn poll_link(&mut self, sm: &mut S, cx: &mut Context) -> bool { + #[cfg(not(feature = "time"))] + cx.waker().wake_by_ref(); + + #[cfg(feature = "time")] + let _ = Timer::after(self.poll_interval).poll_unpin(cx); + let bsr = sm.smi_read(PHY_REG_BSR); // No link without autonegotiate @@ -73,8 +101,13 @@ unsafe impl PHY for GenericSMI { /// Public functions for the PHY impl GenericSMI { + #[cfg(feature = "time")] + pub fn set_poll_interval(&mut self, poll_interval: Duration) { + self.poll_interval = poll_interval + } + // Writes a value to an extended PHY register in MMD address space - fn smi_write_ext(sm: &mut S, reg_addr: u16, reg_data: u16) { + fn smi_write_ext(&mut self, sm: &mut S, reg_addr: u16, reg_data: u16) { sm.smi_write(PHY_REG_CTL, 0x0003); // set address sm.smi_write(PHY_REG_ADDAR, reg_addr); sm.smi_write(PHY_REG_CTL, 0x4003); // set data diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 4989e17c..1687cb31 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -81,9 +81,7 @@ impl<'d, T: Instance, P: PHY> embassy_net_driver::Driver for Ethernet<'d, T, P> } fn link_state(&mut self, cx: &mut Context) -> LinkState { - // TODO: wake cx.waker on link state change - cx.waker().wake_by_ref(); - if P::poll_link(self) { + if self.phy.poll_link(&mut self.station_management, cx) { LinkState::Up } else { LinkState::Down @@ -148,11 +146,11 @@ pub unsafe trait StationManagement { /// The methods cannot move S pub unsafe trait PHY { /// Reset PHY and wait for it to come out of reset. - fn phy_reset(sm: &mut S); + fn phy_reset(&mut self, sm: &mut S); /// PHY initialisation. - fn phy_init(sm: &mut S); + fn phy_init(&mut self, sm: &mut S); /// Poll link to see if it is up and FD with 100Mbps - fn poll_link(sm: &mut S) -> bool; + fn poll_link(&mut self, sm: &mut S, cx: &mut Context) -> bool; } pub(crate) mod sealed { diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index b53c2d0f..2a6ea35f 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -3,6 +3,7 @@ mod rx_desc; mod tx_desc; +use core::marker::PhantomData; use core::sync::atomic::{fence, Ordering}; use embassy_hal_common::{into_ref, PeripheralRef}; @@ -48,9 +49,8 @@ pub struct Ethernet<'d, T: Instance, P: PHY> { pub(crate) rx: RDesRing<'d>, pins: [PeripheralRef<'d, AnyPin>; 9], - _phy: P, - clock_range: Cr, - phy_addr: u8, + pub(crate) phy: P, + pub(crate) station_management: EthernetStationManagement, pub(crate) mac_addr: [u8; 6], } @@ -224,9 +224,12 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { let mut this = Self { _peri: peri, pins, - _phy: phy, - clock_range, - phy_addr, + phy: phy, + station_management: EthernetStationManagement { + peri: PhantomData, + clock_range: clock_range, + phy_addr: phy_addr, + }, mac_addr, tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), @@ -256,8 +259,8 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { w.set_tie(true); }); - P::phy_reset(&mut this); - P::phy_init(&mut this); + this.phy.phy_reset(&mut this.station_management); + this.phy.phy_init(&mut this.station_management); interrupt::ETH.unpend(); unsafe { interrupt::ETH.enable() }; @@ -266,7 +269,13 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { } } -unsafe impl<'d, T: Instance, P: PHY> StationManagement for Ethernet<'d, T, P> { +pub struct EthernetStationManagement { + peri: PhantomData, + clock_range: Cr, + phy_addr: u8, +} + +unsafe impl StationManagement for EthernetStationManagement { fn smi_read(&mut self, reg: u8) -> u16 { let mac = ETH.ethernet_mac(); diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index 600e1d3b..bb681c42 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -1,5 +1,6 @@ mod descriptors; +use core::marker::PhantomData; use core::sync::atomic::{fence, Ordering}; use embassy_hal_common::{into_ref, PeripheralRef}; @@ -40,9 +41,8 @@ pub struct Ethernet<'d, T: Instance, P: PHY> { pub(crate) tx: TDesRing<'d>, pub(crate) rx: RDesRing<'d>, pins: [PeripheralRef<'d, AnyPin>; 9], - _phy: P, - clock_range: u8, - phy_addr: u8, + pub(crate) phy: P, + pub(crate) station_management: EthernetStationManagement, pub(crate) mac_addr: [u8; 6], } @@ -201,9 +201,12 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), pins, - _phy: phy, - clock_range, - phy_addr, + phy: phy, + station_management: EthernetStationManagement { + peri: PhantomData, + clock_range: clock_range, + phy_addr: phy_addr, + }, mac_addr, }; @@ -229,8 +232,8 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { w.set_tie(true); }); - P::phy_reset(&mut this); - P::phy_init(&mut this); + this.phy.phy_reset(&mut this.station_management); + this.phy.phy_init(&mut this.station_management); interrupt::ETH.unpend(); unsafe { interrupt::ETH.enable() }; @@ -239,7 +242,13 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { } } -unsafe impl<'d, T: Instance, P: PHY> StationManagement for Ethernet<'d, T, P> { +pub struct EthernetStationManagement { + peri: PhantomData, + clock_range: u8, + phy_addr: u8, +} + +unsafe impl StationManagement for EthernetStationManagement { fn smi_read(&mut self, reg: u8) -> u16 { let mac = ETH.ethernet_mac(); diff --git a/embassy-stm32/src/flash/asynch.rs b/embassy-stm32/src/flash/asynch.rs index 70a5ded6..f175349c 100644 --- a/embassy-stm32/src/flash/asynch.rs +++ b/embassy-stm32/src/flash/asynch.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; +use core::sync::atomic::{fence, Ordering}; -use atomic_polyfill::{fence, Ordering}; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::into_ref; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index c6cdc574..2a374733 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; +use core::sync::atomic::{fence, Ordering}; -use atomic_polyfill::{fence, Ordering}; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; use stm32_metapac::FLASH_BASE; diff --git a/embassy-stm32/src/flash/f0.rs b/embassy-stm32/src/flash/f0.rs index 02bd4cc1..ec8343e7 100644 --- a/embassy-stm32/src/flash/f0.rs +++ b/embassy-stm32/src/flash/f0.rs @@ -1,7 +1,6 @@ use core::convert::TryInto; use core::ptr::write_volatile; - -use atomic_polyfill::{fence, Ordering}; +use core::sync::atomic::{fence, Ordering}; use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; diff --git a/embassy-stm32/src/flash/f3.rs b/embassy-stm32/src/flash/f3.rs index b093a783..40335d64 100644 --- a/embassy-stm32/src/flash/f3.rs +++ b/embassy-stm32/src/flash/f3.rs @@ -1,7 +1,6 @@ use core::convert::TryInto; use core::ptr::write_volatile; - -use atomic_polyfill::{fence, Ordering}; +use core::sync::atomic::{fence, Ordering}; use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 242d9927..4cb39e03 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -1,8 +1,7 @@ use core::convert::TryInto; use core::ptr::write_volatile; -use core::sync::atomic::{fence, Ordering}; +use core::sync::atomic::{fence, AtomicBool, Ordering}; -use atomic_polyfill::AtomicBool; use embassy_sync::waitqueue::AtomicWaker; use pac::flash::regs::Sr; use pac::FLASH_SIZE; diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 9baf059e..bf17b5b1 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -1,7 +1,6 @@ use core::convert::TryInto; use core::ptr::write_volatile; - -use atomic_polyfill::{fence, Ordering}; +use core::sync::atomic::{fence, Ordering}; use super::{FlashRegion, FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index deefd05e..243c8b51 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -1,6 +1,5 @@ use core::ptr::write_volatile; - -use atomic_polyfill::{fence, Ordering}; +use core::sync::atomic::{fence, Ordering}; use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; diff --git a/embassy-stm32/src/fmc.rs b/embassy-stm32/src/fmc.rs index a4f3b968..60d7a00e 100644 --- a/embassy-stm32/src/fmc.rs +++ b/embassy-stm32/src/fmc.rs @@ -86,6 +86,24 @@ macro_rules! fmc_sdram_constructor { } impl<'d, T: Instance> Fmc<'d, T> { + fmc_sdram_constructor!(sdram_a12bits_d16bits_4banks_bank1: ( + bank: stm32_fmc::SdramTargetBank::Bank1, + addr: [ + (a0: A0Pin), (a1: A1Pin), (a2: A2Pin), (a3: A3Pin), (a4: A4Pin), (a5: A5Pin), (a6: A6Pin), (a7: A7Pin), (a8: A8Pin), (a9: A9Pin), (a10: A10Pin), (a11: A11Pin) + ], + ba: [(ba0: BA0Pin), (ba1: BA1Pin)], + d: [ + (d0: D0Pin), (d1: D1Pin), (d2: D2Pin), (d3: D3Pin), (d4: D4Pin), (d5: D5Pin), (d6: D6Pin), (d7: D7Pin), + (d8: D8Pin), (d9: D9Pin), (d10: D10Pin), (d11: D11Pin), (d12: D12Pin), (d13: D13Pin), (d14: D14Pin), (d15: D15Pin) + ], + nbl: [ + (nbl0: NBL0Pin), (nbl1: NBL1Pin) + ], + ctrl: [ + (sdcke: SDCKE0Pin), (sdclk: SDCLKPin), (sdncas: SDNCASPin), (sdne: SDNE0Pin), (sdnras: SDNRASPin), (sdnwe: SDNWEPin) + ] + )); + fmc_sdram_constructor!(sdram_a12bits_d32bits_4banks_bank1: ( bank: stm32_fmc::SdramTargetBank::Bank1, addr: [ diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 1f036d55..208d1527 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -382,13 +382,18 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // I2C start // // ST SAD+W - Self::master_write( + if let Err(err) = Self::master_write( address, write.len().min(255), Stop::Software, last_chunk_idx != 0, &check_timeout, - )?; + ) { + if send_stop { + self.master_stop(); + } + return Err(err); + } for (number, chunk) in write.chunks(255).enumerate() { if number != 0 { @@ -399,18 +404,22 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { // Wait until we are allowed to send data // (START has been ACKed or last byte when // through) - self.wait_txe(&check_timeout)?; + if let Err(err) = self.wait_txe(&check_timeout) { + if send_stop { + self.master_stop(); + } + return Err(err); + } T::regs().txdr().write(|w| w.set_txdata(*byte)); } } // Wait until the write finishes - self.wait_tc(&check_timeout)?; - + let result = self.wait_tc(&check_timeout); if send_stop { self.master_stop(); } - Ok(()) + result } async fn write_dma_internal( @@ -707,13 +716,16 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let first_length = write[0].len(); let last_slice_index = write.len() - 1; - Self::master_write( + if let Err(err) = Self::master_write( address, first_length.min(255), Stop::Software, (first_length > 255) || (last_slice_index != 0), &check_timeout, - )?; + ) { + self.master_stop(); + return Err(err); + } for (idx, slice) in write.iter().enumerate() { let slice_len = slice.len(); @@ -726,27 +738,36 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { let last_chunk_idx = total_chunks.saturating_sub(1); if idx != 0 { - Self::master_continue( + if let Err(err) = Self::master_continue( slice_len.min(255), (idx != last_slice_index) || (slice_len > 255), &check_timeout, - )?; + ) { + self.master_stop(); + return Err(err); + } } for (number, chunk) in slice.chunks(255).enumerate() { if number != 0 { - Self::master_continue( + if let Err(err) = Self::master_continue( chunk.len(), (number != last_chunk_idx) || (idx != last_slice_index), &check_timeout, - )?; + ) { + self.master_stop(); + return Err(err); + } } for byte in chunk { // Wait until we are allowed to send data // (START has been ACKed or last byte when // through) - self.wait_txe(&check_timeout)?; + if let Err(err) = self.wait_txe(&check_timeout) { + self.master_stop(); + return Err(err); + } // Put byte on the wire //self.i2c.txdr.write(|w| w.txdata().bits(*byte)); @@ -755,10 +776,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> { } } // Wait until the write finishes - self.wait_tc(&check_timeout)?; + let result = self.wait_tc(&check_timeout); self.master_stop(); - - Ok(()) + result } pub fn blocking_write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> { diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index 37f840c7..a24cba9f 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs @@ -1,8 +1,7 @@ use core::future::poll_fn; +use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use atomic_polyfill::{compiler_fence, Ordering}; - use self::sealed::Instance; use crate::interrupt; use crate::interrupt::typelevel::Interrupt; diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index e9db934b..31b67608 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs @@ -1,332 +1,332 @@ -#![macro_use] - -pub mod enums; - -use embassy_hal_common::{into_ref, PeripheralRef}; -use enums::*; - -use crate::dma::Transfer; -use crate::gpio::sealed::AFType; -use crate::gpio::AnyPin; -use crate::pac::quadspi::Quadspi as Regs; -use crate::rcc::RccPeripheral; -use crate::{peripherals, Peripheral}; - -pub struct TransferConfig { - /// Instraction width (IMODE) - pub iwidth: QspiWidth, - /// Address width (ADMODE) - pub awidth: QspiWidth, - /// Data width (DMODE) - pub dwidth: QspiWidth, - /// Instruction Id - pub instruction: u8, - /// Flash memory address - pub address: Option, - /// Number of dummy cycles (DCYC) - pub dummy: DummyCycles, - /// Length of data - pub data_len: Option, -} - -impl Default for TransferConfig { - fn default() -> Self { - Self { - iwidth: QspiWidth::NONE, - awidth: QspiWidth::NONE, - dwidth: QspiWidth::NONE, - instruction: 0, - address: None, - dummy: DummyCycles::_0, - data_len: None, - } - } -} - -pub struct Config { - /// Flash memory size representend as 2^[0-32], as reasonable minimum 1KiB(9) was chosen. - /// If you need other value the whose predefined use `Other` variant. - pub memory_size: MemorySize, - /// Address size (8/16/24/32-bit) - pub address_size: AddressSize, - /// Scalar factor for generating CLK [0-255] - pub prescaler: u8, - /// Number of bytes to trigger FIFO threshold flag. - pub fifo_threshold: FIFOThresholdLevel, - /// Minimum number of cycles that chip select must be high between issued commands - pub cs_high_time: ChipSelectHightTime, -} - -impl Default for Config { - fn default() -> Self { - Self { - memory_size: MemorySize::Other(0), - address_size: AddressSize::_24bit, - prescaler: 128, - fifo_threshold: FIFOThresholdLevel::_17Bytes, - cs_high_time: ChipSelectHightTime::_5Cycle, - } - } -} - -#[allow(dead_code)] -pub struct Qspi<'d, T: Instance, Dma> { - _peri: PeripheralRef<'d, T>, - sck: Option>, - d0: Option>, - d1: Option>, - d2: Option>, - d3: Option>, - nss: Option>, - dma: PeripheralRef<'d, Dma>, - config: Config, -} - -impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { - pub fn new( - peri: impl Peripheral

+ 'd, - d0: impl Peripheral

> + 'd, - d1: impl Peripheral

> + 'd, - d2: impl Peripheral

> + 'd, - d3: impl Peripheral

> + 'd, - sck: impl Peripheral

> + 'd, - nss: impl Peripheral

> + 'd, - dma: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(peri, d0, d1, d2, d3, sck, nss); - - sck.set_as_af(sck.af_num(), AFType::OutputPushPull); - sck.set_speed(crate::gpio::Speed::VeryHigh); - nss.set_as_af(nss.af_num(), AFType::OutputPushPull); - nss.set_speed(crate::gpio::Speed::VeryHigh); - d0.set_as_af(d0.af_num(), AFType::OutputPushPull); - d0.set_speed(crate::gpio::Speed::VeryHigh); - d1.set_as_af(d1.af_num(), AFType::OutputPushPull); - d1.set_speed(crate::gpio::Speed::VeryHigh); - d2.set_as_af(d2.af_num(), AFType::OutputPushPull); - d2.set_speed(crate::gpio::Speed::VeryHigh); - d3.set_as_af(d3.af_num(), AFType::OutputPushPull); - d3.set_speed(crate::gpio::Speed::VeryHigh); - - Self::new_inner( - peri, - Some(d0.map_into()), - Some(d1.map_into()), - Some(d2.map_into()), - Some(d3.map_into()), - Some(sck.map_into()), - Some(nss.map_into()), - dma, - config, - ) - } - - fn new_inner( - peri: impl Peripheral

+ 'd, - d0: Option>, - d1: Option>, - d2: Option>, - d3: Option>, - sck: Option>, - nss: Option>, - dma: impl Peripheral

+ 'd, - config: Config, - ) -> Self { - into_ref!(peri, dma); - - T::enable(); - T::REGS.cr().write(|w| w.set_fthres(config.fifo_threshold.into())); - - while T::REGS.sr().read().busy() {} - - T::REGS.cr().write(|w| { - w.set_prescaler(config.prescaler); - w.set_en(true); - }); - T::REGS.dcr().write(|w| { - w.set_fsize(config.memory_size.into()); - w.set_csht(config.cs_high_time.into()); - w.set_ckmode(false); - }); - - Self { - _peri: peri, - sck, - d0, - d1, - d2, - d3, - nss, - dma, - config, - } - } - - pub fn command(&mut self, transaction: TransferConfig) { - T::REGS.cr().modify(|v| v.set_dmaen(false)); - self.setup_transaction(QspiMode::IndirectWrite, &transaction); - - while !T::REGS.sr().read().tcf() {} - T::REGS.fcr().modify(|v| v.set_ctcf(true)); - } - - pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) { - T::REGS.cr().modify(|v| v.set_dmaen(false)); - self.setup_transaction(QspiMode::IndirectWrite, &transaction); - - if let Some(len) = transaction.data_len { - let current_ar = T::REGS.ar().read().address(); - T::REGS.ccr().modify(|v| { - v.set_fmode(QspiMode::IndirectRead.into()); - }); - T::REGS.ar().write(|v| { - v.set_address(current_ar); - }); - - for idx in 0..len { - while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} - buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() }; - } - } - - while !T::REGS.sr().read().tcf() {} - T::REGS.fcr().modify(|v| v.set_ctcf(true)); - } - - pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) { - T::REGS.cr().modify(|v| v.set_dmaen(false)); - self.setup_transaction(QspiMode::IndirectWrite, &transaction); - - if let Some(len) = transaction.data_len { - T::REGS.ccr().modify(|v| { - v.set_fmode(QspiMode::IndirectWrite.into()); - }); - - for idx in 0..len { - while !T::REGS.sr().read().ftf() {} - unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(buf[idx]) }; - } - } - - while !T::REGS.sr().read().tcf() {} - T::REGS.fcr().modify(|v| v.set_ctcf(true)); - } - - pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) - where - Dma: QuadDma, - { - self.setup_transaction(QspiMode::IndirectWrite, &transaction); - - T::REGS.ccr().modify(|v| { - v.set_fmode(QspiMode::IndirectRead.into()); - }); - let current_ar = T::REGS.ar().read().address(); - T::REGS.ar().write(|v| { - v.set_address(current_ar); - }); - - let request = self.dma.request(); - let transfer = unsafe { - Transfer::new_read( - &mut self.dma, - request, - T::REGS.dr().as_ptr() as *mut u8, - buf, - Default::default(), - ) - }; - - T::REGS.cr().modify(|v| v.set_dmaen(true)); - - transfer.blocking_wait(); - } - - pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig) - where - Dma: QuadDma, - { - self.setup_transaction(QspiMode::IndirectWrite, &transaction); - - T::REGS.ccr().modify(|v| { - v.set_fmode(QspiMode::IndirectWrite.into()); - }); - - let request = self.dma.request(); - let transfer = unsafe { - Transfer::new_write( - &mut self.dma, - request, - buf, - T::REGS.dr().as_ptr() as *mut u8, - Default::default(), - ) - }; - - T::REGS.cr().modify(|v| v.set_dmaen(true)); - - transfer.blocking_wait(); - } - - fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig) { - T::REGS.fcr().modify(|v| { - v.set_csmf(true); - v.set_ctcf(true); - v.set_ctef(true); - v.set_ctof(true); - }); - - while T::REGS.sr().read().busy() {} - - if let Some(len) = transaction.data_len { - T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1)); - } - - T::REGS.ccr().write(|v| { - v.set_fmode(fmode.into()); - v.set_imode(transaction.iwidth.into()); - v.set_instruction(transaction.instruction); - v.set_admode(transaction.awidth.into()); - v.set_adsize(self.config.address_size.into()); - v.set_dmode(transaction.dwidth.into()); - v.set_abmode(QspiWidth::NONE.into()); - v.set_dcyc(transaction.dummy.into()); - }); - - if let Some(addr) = transaction.address { - T::REGS.ar().write(|v| { - v.set_address(addr); - }); - } - } -} - -pub(crate) mod sealed { - use super::*; - - pub trait Instance { - const REGS: Regs; - } -} - -pub trait Instance: Peripheral

+ sealed::Instance + RccPeripheral {} - -pin_trait!(SckPin, Instance); -pin_trait!(D0Pin, Instance); -pin_trait!(D1Pin, Instance); -pin_trait!(D2Pin, Instance); -pin_trait!(D3Pin, Instance); -pin_trait!(NSSPin, Instance); - -dma_trait!(QuadDma, Instance); - -foreach_peripheral!( - (quadspi, $inst:ident) => { - impl sealed::Instance for peripherals::$inst { - const REGS: Regs = crate::pac::$inst; - } - - impl Instance for peripherals::$inst {} - }; -); +#![macro_use] + +pub mod enums; + +use embassy_hal_common::{into_ref, PeripheralRef}; +use enums::*; + +use crate::dma::Transfer; +use crate::gpio::sealed::AFType; +use crate::gpio::AnyPin; +use crate::pac::quadspi::Quadspi as Regs; +use crate::rcc::RccPeripheral; +use crate::{peripherals, Peripheral}; + +pub struct TransferConfig { + /// Instraction width (IMODE) + pub iwidth: QspiWidth, + /// Address width (ADMODE) + pub awidth: QspiWidth, + /// Data width (DMODE) + pub dwidth: QspiWidth, + /// Instruction Id + pub instruction: u8, + /// Flash memory address + pub address: Option, + /// Number of dummy cycles (DCYC) + pub dummy: DummyCycles, + /// Length of data + pub data_len: Option, +} + +impl Default for TransferConfig { + fn default() -> Self { + Self { + iwidth: QspiWidth::NONE, + awidth: QspiWidth::NONE, + dwidth: QspiWidth::NONE, + instruction: 0, + address: None, + dummy: DummyCycles::_0, + data_len: None, + } + } +} + +pub struct Config { + /// Flash memory size representend as 2^[0-32], as reasonable minimum 1KiB(9) was chosen. + /// If you need other value the whose predefined use `Other` variant. + pub memory_size: MemorySize, + /// Address size (8/16/24/32-bit) + pub address_size: AddressSize, + /// Scalar factor for generating CLK [0-255] + pub prescaler: u8, + /// Number of bytes to trigger FIFO threshold flag. + pub fifo_threshold: FIFOThresholdLevel, + /// Minimum number of cycles that chip select must be high between issued commands + pub cs_high_time: ChipSelectHightTime, +} + +impl Default for Config { + fn default() -> Self { + Self { + memory_size: MemorySize::Other(0), + address_size: AddressSize::_24bit, + prescaler: 128, + fifo_threshold: FIFOThresholdLevel::_17Bytes, + cs_high_time: ChipSelectHightTime::_5Cycle, + } + } +} + +#[allow(dead_code)] +pub struct Qspi<'d, T: Instance, Dma> { + _peri: PeripheralRef<'d, T>, + sck: Option>, + d0: Option>, + d1: Option>, + d2: Option>, + d3: Option>, + nss: Option>, + dma: PeripheralRef<'d, Dma>, + config: Config, +} + +impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { + pub fn new( + peri: impl Peripheral

+ 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + d2: impl Peripheral

> + 'd, + d3: impl Peripheral

> + 'd, + sck: impl Peripheral

> + 'd, + nss: impl Peripheral

> + 'd, + dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(peri, d0, d1, d2, d3, sck, nss); + + sck.set_as_af(sck.af_num(), AFType::OutputPushPull); + sck.set_speed(crate::gpio::Speed::VeryHigh); + nss.set_as_af(nss.af_num(), AFType::OutputPushPull); + nss.set_speed(crate::gpio::Speed::VeryHigh); + d0.set_as_af(d0.af_num(), AFType::OutputPushPull); + d0.set_speed(crate::gpio::Speed::VeryHigh); + d1.set_as_af(d1.af_num(), AFType::OutputPushPull); + d1.set_speed(crate::gpio::Speed::VeryHigh); + d2.set_as_af(d2.af_num(), AFType::OutputPushPull); + d2.set_speed(crate::gpio::Speed::VeryHigh); + d3.set_as_af(d3.af_num(), AFType::OutputPushPull); + d3.set_speed(crate::gpio::Speed::VeryHigh); + + Self::new_inner( + peri, + Some(d0.map_into()), + Some(d1.map_into()), + Some(d2.map_into()), + Some(d3.map_into()), + Some(sck.map_into()), + Some(nss.map_into()), + dma, + config, + ) + } + + fn new_inner( + peri: impl Peripheral

+ 'd, + d0: Option>, + d1: Option>, + d2: Option>, + d3: Option>, + sck: Option>, + nss: Option>, + dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(peri, dma); + + T::enable(); + T::REGS.cr().write(|w| w.set_fthres(config.fifo_threshold.into())); + + while T::REGS.sr().read().busy() {} + + T::REGS.cr().write(|w| { + w.set_prescaler(config.prescaler); + w.set_en(true); + }); + T::REGS.dcr().write(|w| { + w.set_fsize(config.memory_size.into()); + w.set_csht(config.cs_high_time.into()); + w.set_ckmode(false); + }); + + Self { + _peri: peri, + sck, + d0, + d1, + d2, + d3, + nss, + dma, + config, + } + } + + pub fn command(&mut self, transaction: TransferConfig) { + T::REGS.cr().modify(|v| v.set_dmaen(false)); + self.setup_transaction(QspiMode::IndirectWrite, &transaction); + + while !T::REGS.sr().read().tcf() {} + T::REGS.fcr().modify(|v| v.set_ctcf(true)); + } + + pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) { + T::REGS.cr().modify(|v| v.set_dmaen(false)); + self.setup_transaction(QspiMode::IndirectWrite, &transaction); + + if let Some(len) = transaction.data_len { + let current_ar = T::REGS.ar().read().address(); + T::REGS.ccr().modify(|v| { + v.set_fmode(QspiMode::IndirectRead.into()); + }); + T::REGS.ar().write(|v| { + v.set_address(current_ar); + }); + + for idx in 0..len { + while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} + buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() }; + } + } + + while !T::REGS.sr().read().tcf() {} + T::REGS.fcr().modify(|v| v.set_ctcf(true)); + } + + pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) { + T::REGS.cr().modify(|v| v.set_dmaen(false)); + self.setup_transaction(QspiMode::IndirectWrite, &transaction); + + if let Some(len) = transaction.data_len { + T::REGS.ccr().modify(|v| { + v.set_fmode(QspiMode::IndirectWrite.into()); + }); + + for idx in 0..len { + while !T::REGS.sr().read().ftf() {} + unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(buf[idx]) }; + } + } + + while !T::REGS.sr().read().tcf() {} + T::REGS.fcr().modify(|v| v.set_ctcf(true)); + } + + pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) + where + Dma: QuadDma, + { + self.setup_transaction(QspiMode::IndirectWrite, &transaction); + + T::REGS.ccr().modify(|v| { + v.set_fmode(QspiMode::IndirectRead.into()); + }); + let current_ar = T::REGS.ar().read().address(); + T::REGS.ar().write(|v| { + v.set_address(current_ar); + }); + + let request = self.dma.request(); + let transfer = unsafe { + Transfer::new_read( + &mut self.dma, + request, + T::REGS.dr().as_ptr() as *mut u8, + buf, + Default::default(), + ) + }; + + T::REGS.cr().modify(|v| v.set_dmaen(true)); + + transfer.blocking_wait(); + } + + pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig) + where + Dma: QuadDma, + { + self.setup_transaction(QspiMode::IndirectWrite, &transaction); + + T::REGS.ccr().modify(|v| { + v.set_fmode(QspiMode::IndirectWrite.into()); + }); + + let request = self.dma.request(); + let transfer = unsafe { + Transfer::new_write( + &mut self.dma, + request, + buf, + T::REGS.dr().as_ptr() as *mut u8, + Default::default(), + ) + }; + + T::REGS.cr().modify(|v| v.set_dmaen(true)); + + transfer.blocking_wait(); + } + + fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig) { + T::REGS.fcr().modify(|v| { + v.set_csmf(true); + v.set_ctcf(true); + v.set_ctef(true); + v.set_ctof(true); + }); + + while T::REGS.sr().read().busy() {} + + if let Some(len) = transaction.data_len { + T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1)); + } + + T::REGS.ccr().write(|v| { + v.set_fmode(fmode.into()); + v.set_imode(transaction.iwidth.into()); + v.set_instruction(transaction.instruction); + v.set_admode(transaction.awidth.into()); + v.set_adsize(self.config.address_size.into()); + v.set_dmode(transaction.dwidth.into()); + v.set_abmode(QspiWidth::NONE.into()); + v.set_dcyc(transaction.dummy.into()); + }); + + if let Some(addr) = transaction.address { + T::REGS.ar().write(|v| { + v.set_address(addr); + }); + } + } +} + +pub(crate) mod sealed { + use super::*; + + pub trait Instance { + const REGS: Regs; + } +} + +pub trait Instance: Peripheral

+ sealed::Instance + RccPeripheral {} + +pin_trait!(SckPin, Instance); +pin_trait!(D0Pin, Instance); +pin_trait!(D1Pin, Instance); +pin_trait!(D2Pin, Instance); +pin_trait!(D3Pin, Instance); +pin_trait!(NSSPin, Instance); + +dma_trait!(QuadDma, Instance); + +foreach_peripheral!( + (quadspi, $inst:ident) => { + impl sealed::Instance for peripherals::$inst { + const REGS: Regs = crate::pac::$inst; + } + + impl Instance for peripherals::$inst {} + }; +); diff --git a/embassy-stm32/src/rcc/h5.rs b/embassy-stm32/src/rcc/h5.rs index 4025a4e0..7e2f75ab 100644 --- a/embassy-stm32/src/rcc/h5.rs +++ b/embassy-stm32/src/rcc/h5.rs @@ -473,11 +473,11 @@ fn init_pll(num: usize, config: Option, input: &PllInput) -> PllOutput { w.set_divm(0); }); - return PllOutput{ + return PllOutput { p: None, q: None, r: None, - } + }; }; assert!(1 <= config.prediv && config.prediv <= 63); diff --git a/embassy-stm32/src/rcc/h7.rs b/embassy-stm32/src/rcc/h7.rs index f3a98c79..7e5cd0d1 100644 --- a/embassy-stm32/src/rcc/h7.rs +++ b/embassy-stm32/src/rcc/h7.rs @@ -740,7 +740,7 @@ mod pll { } }; - let vco_ck = output + pll_x_p; + let vco_ck = output * pll_x_p; assert!(pll_x_p < 128); assert!(vco_ck >= VCO_MIN); diff --git a/embassy-stm32/src/rcc/l4.rs b/embassy-stm32/src/rcc/l4.rs index 20cb8c91..8a9b4adb 100644 --- a/embassy-stm32/src/rcc/l4.rs +++ b/embassy-stm32/src/rcc/l4.rs @@ -1,6 +1,7 @@ use core::marker::PhantomData; use embassy_hal_common::into_ref; +use stm32_metapac::rcc::regs::Cfgr; use stm32_metapac::rcc::vals::{Lsedrv, Mcopre, Mcosel}; use crate::gpio::sealed::AFType; @@ -439,6 +440,26 @@ impl<'d, T: McoInstance> Mco<'d, T> { } pub(crate) unsafe fn init(config: Config) { + // Switch to MSI to prevent problems with PLL configuration. + if !RCC.cr().read().msion() { + // Turn on MSI and configure it to 4MHz. + RCC.cr().modify(|w| { + w.set_msirgsel(true); // MSI Range is provided by MSIRANGE[3:0]. + w.set_msirange(MSIRange::default().into()); + w.set_msipllen(false); + w.set_msion(true) + }); + + // Wait until MSI is running + while !RCC.cr().read().msirdy() {} + } + if RCC.cfgr().read().sws() != Sw::MSI { + // Set MSI as a clock source, reset prescalers. + RCC.cfgr().write_value(Cfgr::default()); + // Wait for clock switch status bits to change. + while RCC.cfgr().read().sws() != Sw::MSI {} + } + match config.rtc_mux { RtcClockSource::LSE32 => { // 1. Unlock the backup domain @@ -660,6 +681,8 @@ pub(crate) unsafe fn init(config: Config) { } }; + RCC.apb1enr1().modify(|w| w.set_pwren(true)); + set_freqs(Clocks { sys: Hertz(sys_clk), ahb1: Hertz(ahb_freq), diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index d6816d6a..886fc0b9 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -83,12 +83,12 @@ static mut CLOCK_FREQS: MaybeUninit = MaybeUninit::uninit(); /// Safety: Sets a mutable global. pub(crate) unsafe fn set_freqs(freqs: Clocks) { debug!("rcc: {:?}", freqs); - CLOCK_FREQS.as_mut_ptr().write(freqs); + CLOCK_FREQS = MaybeUninit::new(freqs); } /// Safety: Reads a mutable global. pub(crate) unsafe fn get_freqs() -> &'static Clocks { - &*CLOCK_FREQS.as_ptr() + CLOCK_FREQS.assume_init_ref() } #[cfg(feature = "unstable-pac")] diff --git a/embassy-stm32/src/rcc/wl.rs b/embassy-stm32/src/rcc/wl.rs index 7072db98..6b69bb1c 100644 --- a/embassy-stm32/src/rcc/wl.rs +++ b/embassy-stm32/src/rcc/wl.rs @@ -1,4 +1,5 @@ -use crate::pac::{FLASH, RCC}; +use crate::pac::pwr::vals::Dbp; +use crate::pac::{FLASH, PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -184,6 +185,8 @@ pub struct Config { pub apb1_pre: APBPrescaler, pub apb2_pre: APBPrescaler, pub enable_lsi: bool, + pub enable_rtc_apb: bool, + pub rtc_mux: RtcClockSource, } impl Default for Config { @@ -196,10 +199,25 @@ impl Default for Config { apb1_pre: APBPrescaler::NotDivided, apb2_pre: APBPrescaler::NotDivided, enable_lsi: false, + enable_rtc_apb: false, + rtc_mux: RtcClockSource::LSI32, } } } +pub enum RtcClockSource { + LSE32, + LSI32, +} + +#[repr(u8)] +pub enum Lsedrv { + Low = 0, + MediumLow = 1, + MediumHigh = 2, + High = 3, +} + pub(crate) unsafe fn init(config: Config) { let (sys_clk, sw, vos) = match config.mux { ClockSrc::HSI16 => (HSI_FREQ.0, 0x01, VoltageScale::Range2), @@ -266,6 +284,32 @@ pub(crate) unsafe fn init(config: Config) { while FLASH.acr().read().latency() != ws {} + match config.rtc_mux { + RtcClockSource::LSE32 => { + // 1. Unlock the backup domain + PWR.cr1().modify(|w| w.set_dbp(Dbp::ENABLED)); + + // 2. Setup the LSE + RCC.bdcr().modify(|w| { + // Enable LSE + w.set_lseon(true); + // Max drive strength + // TODO: should probably be settable + w.set_lsedrv(Lsedrv::High as u8); //---// PAM - should not be commented + }); + + // Wait until LSE is running + while !RCC.bdcr().read().lserdy() {} + } + RtcClockSource::LSI32 => { + // Turn on the internal 32 kHz LSI oscillator + RCC.csr().modify(|w| w.set_lsion(true)); + + // Wait until LSI is running + while !RCC.csr().read().lsirdy() {} + } + } + match config.mux { ClockSrc::HSI16 => { // Enable HSI16 @@ -287,11 +331,26 @@ pub(crate) unsafe fn init(config: Config) { w.set_msirgsel(true); w.set_msirange(range.into()); w.set_msion(true); + + if let RtcClockSource::LSE32 = config.rtc_mux { + // If LSE is enabled, enable calibration of MSI + w.set_msipllen(true); + } else { + w.set_msipllen(false); + } }); while !RCC.cr().read().msirdy() {} } } + if config.enable_rtc_apb { + // enable peripheral clock for communication + crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true)); + + // read to allow the pwr clock to enable + crate::pac::PWR.cr1().read(); + } + RCC.extcfgr().modify(|w| { if config.shd_ahb_pre == AHBPrescaler::NotDivided { w.set_shdhpre(0); diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index 7e5c64d9..8ef0ec51 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -172,6 +172,7 @@ impl sealed::Instance for crate::peripherals::RTC { const BACKUP_REGISTER_COUNT: usize = 32; fn read_backup_register(_rtc: &Rtc, register: usize) -> Option { + #[allow(clippy::if_same_then_else)] if register < Self::BACKUP_REGISTER_COUNT { //Some(rtc.bkpr()[register].read().bits()) None // RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index c3224073..d5f63f84 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -852,25 +852,19 @@ mod eh1 { type Error = Error; } - impl<'d, T: Instance, Tx, Rx> embedded_hal_1::spi::SpiBusFlush for Spi<'d, T, Tx, Rx> { + impl<'d, T: Instance, W: Word, Tx, Rx> embedded_hal_1::spi::SpiBus for Spi<'d, T, Tx, Rx> { fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } - } - impl<'d, T: Instance, W: Word, Tx, Rx> embedded_hal_1::spi::SpiBusRead for Spi<'d, T, Tx, Rx> { fn read(&mut self, words: &mut [W]) -> Result<(), Self::Error> { self.blocking_read(words) } - } - impl<'d, T: Instance, W: Word, Tx, Rx> embedded_hal_1::spi::SpiBusWrite for Spi<'d, T, Tx, Rx> { fn write(&mut self, words: &[W]) -> Result<(), Self::Error> { self.blocking_write(words) } - } - impl<'d, T: Instance, W: Word, Tx, Rx> embedded_hal_1::spi::SpiBus for Spi<'d, T, Tx, Rx> { fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Self::Error> { self.blocking_transfer(read, write) } @@ -895,32 +889,25 @@ mod eh1 { #[cfg(all(feature = "unstable-traits", feature = "nightly"))] mod eha { use super::*; - impl<'d, T: Instance, Tx, Rx> embedded_hal_async::spi::SpiBusFlush for Spi<'d, T, Tx, Rx> { + + impl<'d, T: Instance, Tx: TxDma, Rx: RxDma, W: Word> embedded_hal_async::spi::SpiBus for Spi<'d, T, Tx, Rx> { async fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } - } - impl<'d, T: Instance, Tx: TxDma, Rx, W: Word> embedded_hal_async::spi::SpiBusWrite for Spi<'d, T, Tx, Rx> { async fn write(&mut self, words: &[W]) -> Result<(), Self::Error> { self.write(words).await } - } - impl<'d, T: Instance, Tx: TxDma, Rx: RxDma, W: Word> embedded_hal_async::spi::SpiBusRead - for Spi<'d, T, Tx, Rx> - { async fn read(&mut self, words: &mut [W]) -> Result<(), Self::Error> { self.read(words).await } - } - impl<'d, T: Instance, Tx: TxDma, Rx: RxDma, W: Word> embedded_hal_async::spi::SpiBus for Spi<'d, T, Tx, Rx> { - async fn transfer<'a>(&'a mut self, read: &'a mut [W], write: &'a [W]) -> Result<(), Self::Error> { + async fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Self::Error> { self.transfer(read, write).await } - async fn transfer_in_place<'a>(&'a mut self, words: &'a mut [W]) -> Result<(), Self::Error> { + async fn transfer_in_place(&mut self, words: &mut [W]) -> Result<(), Self::Error> { self.transfer_in_place(words).await } } diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index c97efbf0..ea8e525e 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -116,6 +116,10 @@ pub struct Config { /// but will effectively disable noise detection. #[cfg(not(usart_v1))] pub assume_noise_free: bool, + + /// Set this to true to swap the RX and TX pins. + #[cfg(any(usart_v3, usart_v4))] + pub swap_rx_tx: bool, } impl Default for Config { @@ -129,6 +133,8 @@ impl Default for Config { detect_previous_overrun: false, #[cfg(not(usart_v1))] assume_noise_free: false, + #[cfg(any(usart_v3, usart_v4))] + swap_rx_tx: false, } } } @@ -688,8 +694,22 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { let r = T::regs(); - rx.set_as_af(rx.af_num(), AFType::Input); - tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + // Some chips do not have swap_rx_tx bit + cfg_if::cfg_if! { + if #[cfg(any(usart_v3, usart_v4))] { + if config.swap_rx_tx { + let (rx, tx) = (tx, rx); + rx.set_as_af(rx.af_num(), AFType::Input); + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + } else { + rx.set_as_af(rx.af_num(), AFType::Input); + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + } + } else { + rx.set_as_af(rx.af_num(), AFType::Input); + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + } + } configure(r, &config, T::frequency(), T::KIND, true, true); @@ -847,6 +867,9 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: StopBits::STOP1P5 => vals::Stop::STOP1P5, StopBits::STOP2 => vals::Stop::STOP2, }); + + #[cfg(any(usart_v3, usart_v4))] + w.set_swap(config.swap_rx_tx); }); r.cr1().write(|w| { // enable uart diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index 6783db28..fd0e22ad 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -1,8 +1,8 @@ use core::cell::UnsafeCell; use core::marker::PhantomData; +use core::sync::atomic::{AtomicBool, AtomicU16, Ordering}; use core::task::Poll; -use atomic_polyfill::{AtomicBool, AtomicU16, Ordering}; use embassy_hal_common::{into_ref, Peripheral}; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver::{ @@ -648,7 +648,7 @@ impl<'d, T: Instance> Bus<'d, T> { let r = T::regs(); let core_id = r.cid().read().0; - info!("Core id {:08x}", core_id); + trace!("Core id {:08x}", core_id); // Wait for AHB ready. while !r.grstctl().read().ahbidl() {} @@ -1154,14 +1154,22 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointOut for Endpoint<'d, T, Out> { trace!("read start len={}", buf.len()); poll_fn(|cx| { + let r = T::regs(); let index = self.info.addr.index(); let state = T::state(); state.ep_out_wakers[index].register(cx.waker()); + let doepctl = r.doepctl(index).read(); + trace!("read ep={:?}: doepctl {:08x}", self.info.addr, doepctl.0,); + if !doepctl.usbaep() { + trace!("read ep={:?} error disabled", self.info.addr); + return Poll::Ready(Err(EndpointError::Disabled)); + } + let len = state.ep_out_size[index].load(Ordering::Relaxed); if len != EP_OUT_BUFFER_EMPTY { - trace!("read done len={}", len); + trace!("read ep={:?} done len={}", self.info.addr, len); if len as usize > buf.len() { return Poll::Ready(Err(EndpointError::BufferOverflow)); @@ -1214,7 +1222,12 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { let diepctl = r.diepctl(index).read(); let dtxfsts = r.dtxfsts(index).read(); - info!("diepctl {:08x} ftxfsts {:08x}", diepctl.0, dtxfsts.0); + trace!( + "write ep={:?}: diepctl {:08x} ftxfsts {:08x}", + self.info.addr, + diepctl.0, + dtxfsts.0 + ); if !diepctl.usbaep() { trace!("write ep={:?} wait for prev: error disabled", self.info.addr); Poll::Ready(Err(EndpointError::Disabled)) diff --git a/embassy-sync/src/channel.rs b/embassy-sync/src/channel.rs index 77352874..f421af39 100644 --- a/embassy-sync/src/channel.rs +++ b/embassy-sync/src/channel.rs @@ -335,6 +335,12 @@ impl ChannelState { } } + fn poll_ready_to_receive(&mut self, cx: &mut Context<'_>) -> bool { + self.receiver_waker.register(cx.waker()); + + !self.queue.is_empty() + } + fn try_send(&mut self, message: T) -> Result<(), TrySendError> { self.try_send_with_context(message, None) } @@ -353,6 +359,12 @@ impl ChannelState { } } } + + fn poll_ready_to_send(&mut self, cx: &mut Context<'_>) -> bool { + self.senders_waker.register(cx.waker()); + + !self.queue.is_full() + } } /// A bounded channel for communicating between asynchronous tasks @@ -401,6 +413,16 @@ where self.lock(|c| c.try_send_with_context(m, cx)) } + /// Allows a poll_fn to poll until the channel is ready to receive + pub fn poll_ready_to_receive(&self, cx: &mut Context<'_>) -> bool { + self.lock(|c| c.poll_ready_to_receive(cx)) + } + + /// Allows a poll_fn to poll until the channel is ready to send + pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> bool { + self.lock(|c| c.poll_ready_to_send(cx)) + } + /// Get a sender for this channel. pub fn sender(&self) -> Sender<'_, M, T, N> { Sender { channel: self } diff --git a/embassy-time/CHANGELOG.md b/embassy-time/CHANGELOG.md index f4a7860e..26640d93 100644 --- a/embassy-time/CHANGELOG.md +++ b/embassy-time/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.1.2 - 2023-07-05 + +- Update `embedded-hal-async` to `0.2.0-alpha.2`. +- Update `embedded-hal v1` to `1.0.0-alpha.11`. (Note: v0.2 support is kept unchanged). + ## 0.1.1 - 2023-04-13 - Update `embedded-hal-async` to `0.2.0-alpha.1` (uses `async fn` in traits). diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 857da546..0afb1103 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-time" -version = "0.1.1" +version = "0.1.2" edition = "2021" description = "Instant and Duration for embedded no-std systems, with async timer support" repository = "https://github.com/embassy-rs/embassy" @@ -23,7 +23,7 @@ target = "x86_64-unknown-linux-gnu" features = ["nightly", "defmt", "unstable-traits", "std"] [features] -std = ["tick-hz-1_000_000"] +std = ["tick-hz-1_000_000", "critical-section/std"] wasm = ["dep:wasm-bindgen", "dep:js-sys", "dep:wasm-timer", "tick-hz-1_000_000"] # Enable nightly-only features @@ -152,8 +152,8 @@ defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} -embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true} +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true} +embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true} futures-util = { version = "0.3.17", default-features = false } atomic-polyfill = "1.0.1" diff --git a/embassy-time/src/driver.rs b/embassy-time/src/driver.rs index d6436369..5fe7beca 100644 --- a/embassy-time/src/driver.rs +++ b/embassy-time/src/driver.rs @@ -36,7 +36,7 @@ //! ``` //! use embassy_time::driver::{Driver, AlarmHandle}; //! -//! struct MyDriver{}; // not public! +//! struct MyDriver{} // not public! //! embassy_time::time_driver_impl!(static DRIVER: MyDriver = MyDriver{}); //! //! impl Driver for MyDriver { diff --git a/embassy-time/src/driver_std.rs b/embassy-time/src/driver_std.rs index 9f8c57b5..32db47a3 100644 --- a/embassy-time/src/driver_std.rs +++ b/embassy-time/src/driver_std.rs @@ -1,10 +1,10 @@ +use core::sync::atomic::{AtomicU8, Ordering}; use std::cell::{RefCell, UnsafeCell}; use std::mem::MaybeUninit; use std::sync::{Condvar, Mutex, Once}; use std::time::{Duration as StdDuration, Instant as StdInstant}; use std::{mem, ptr, thread}; -use atomic_polyfill::{AtomicU8, Ordering}; use critical_section::Mutex as CsMutex; use crate::driver::{AlarmHandle, Driver}; diff --git a/embassy-time/src/driver_wasm.rs b/embassy-time/src/driver_wasm.rs index 63d04989..0f672dc7 100644 --- a/embassy-time/src/driver_wasm.rs +++ b/embassy-time/src/driver_wasm.rs @@ -1,9 +1,9 @@ +use core::sync::atomic::{AtomicU8, Ordering}; use std::cell::UnsafeCell; use std::mem::MaybeUninit; use std::ptr; use std::sync::{Mutex, Once}; -use atomic_polyfill::{AtomicU8, Ordering}; use wasm_bindgen::prelude::*; use wasm_timer::Instant as StdInstant; diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs index 4795eb2f..77947ab2 100644 --- a/embassy-time/src/queue_generic.rs +++ b/embassy-time/src/queue_generic.rs @@ -16,7 +16,7 @@ const QUEUE_SIZE: usize = 16; #[cfg(feature = "generic-queue-32")] const QUEUE_SIZE: usize = 32; #[cfg(feature = "generic-queue-64")] -const QUEUE_SIZE: usize = 32; +const QUEUE_SIZE: usize = 64; #[cfg(feature = "generic-queue-128")] const QUEUE_SIZE: usize = 128; #[cfg(not(any( diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index b98f73f3..2a0cf781 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } -embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] } +embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly"] } embassy-nrf = { version = "0.1.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", "nightly"] } embassy-boot = { version = "0.1.0", path = "../../../../embassy-boot/boot", features = ["nightly"] } embassy-boot-nrf = { version = "0.1.0", path = "../../../../embassy-boot/nrf", features = ["nightly"] } diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index 007b6839..95b2da95 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] } -embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly"] } +embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly"] } embassy-rp = { version = "0.1.0", path = "../../../../embassy-rp", features = ["time-driver", "unstable-traits", "nightly"] } embassy-boot-rp = { version = "0.1.0", path = "../../../../embassy-boot/rp", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index 5b3faf8f..3b0fc4d9 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f303re", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index b6a6f9cd..323b4ab2 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32f767zi", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index 0a7e19b1..b2abdc89 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync" } embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32h743zi", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index 998df4dc..0b7e72d5 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l072cz", "time-driver-any", "exti", "memory-x"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index 10b58c17..5f3f365c 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l151cb-a", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index 713a6527..44eb5aba 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32l475vg", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index 4c8bbd73..fdad5506 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../../../embassy-time", features = ["nightly", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["unstable-traits", "nightly", "stm32wl55jc-cm4", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.1.0", path = "../../../../embassy-boot/stm32", features = ["nightly"] } embassy-embedded-hal = { version = "0.1.0", path = "../../../../embassy-embedded-hal" } diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index a3acc56b..30b67b7b 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml @@ -18,7 +18,7 @@ log = [ [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync" } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "rtos-trace-interrupt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time" } +embassy-time = { version = "0.1.2", path = "../../embassy-time" } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } diff --git a/examples/nrf52840-rtic/Cargo.toml b/examples/nrf52840-rtic/Cargo.toml index 0f9048b0..ded3b7db 100644 --- a/examples/nrf52840-rtic/Cargo.toml +++ b/examples/nrf52840-rtic/Cargo.toml @@ -9,7 +9,7 @@ rtic = { version = "2", features = ["thumbv7-backend"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "generic-queue"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "generic-queue"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nightly", "unstable-traits", "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "0.3" diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 8c417596..9b41ec5a 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -29,7 +29,7 @@ nightly = [ embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor",], optional = true } @@ -43,6 +43,7 @@ embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-host defmt = "0.3" defmt-rtt = "0.4" +fixed = "1.10.0" static_cell = "1.1" cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" @@ -52,4 +53,9 @@ rand = { version = "0.8.4", default-features = false } embedded-storage = "0.3.0" usbd-hid = "0.6.0" serde = { version = "1.0.136", default-features = false } -embedded-hal-async = { version = "0.2.0-alpha.1", optional = true } +embedded-hal-async = { version = "0.2.0-alpha.2", optional = true } +num-integer = { version = "0.1.45", default-features = false } +microfft = "0.5.0" + +[patch.crates-io] +lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } diff --git a/examples/nrf52840/src/bin/pdm.rs b/examples/nrf52840/src/bin/pdm.rs index 6b41320c..444b9137 100644 --- a/examples/nrf52840/src/bin/pdm.rs +++ b/examples/nrf52840/src/bin/pdm.rs @@ -7,6 +7,8 @@ use embassy_executor::Spawner; use embassy_nrf::pdm::{self, Config, Pdm}; use embassy_nrf::{bind_interrupts, peripherals}; use embassy_time::{Duration, Timer}; +use fixed::types::I7F1; +use num_integer::Roots; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -20,18 +22,36 @@ async fn main(_p: Spawner) { let mut pdm = Pdm::new(p.PDM, Irqs, p.P0_01, p.P0_00, config); loop { - pdm.start().await; + for gain in [I7F1::from_num(-20), I7F1::from_num(0), I7F1::from_num(20)] { + pdm.set_gain(gain, gain); + info!("Gain = {} dB", defmt::Debug2Format(&gain)); + pdm.start().await; - // wait some time till the microphon settled - Timer::after(Duration::from_millis(1000)).await; + // wait some time till the microphon settled + Timer::after(Duration::from_millis(1000)).await; - const SAMPLES: usize = 2048; - let mut buf = [0i16; SAMPLES]; - pdm.sample(&mut buf).await.unwrap(); + const SAMPLES: usize = 2048; + let mut buf = [0i16; SAMPLES]; + pdm.sample(&mut buf).await.unwrap(); - info!("samples: {:?}", &buf); + let mean = (buf.iter().map(|v| i32::from(*v)).sum::() / buf.len() as i32) as i16; + info!( + "{} samples, min {=i16}, max {=i16}, mean {=i16}, AC RMS {=i16}", + buf.len(), + buf.iter().min().unwrap(), + buf.iter().max().unwrap(), + mean, + (buf.iter() + .map(|v| i32::from(*v - mean).pow(2)) + .fold(0i32, |a, b| a.saturating_add(b)) + / buf.len() as i32) + .sqrt() as i16, + ); - pdm.stop().await; - Timer::after(Duration::from_millis(100)).await; + info!("samples: {:?}", &buf); + + pdm.stop().await; + Timer::after(Duration::from_millis(100)).await; + } } } diff --git a/examples/nrf52840/src/bin/pdm_continuous.rs b/examples/nrf52840/src/bin/pdm_continuous.rs new file mode 100644 index 00000000..7d853147 --- /dev/null +++ b/examples/nrf52840/src/bin/pdm_continuous.rs @@ -0,0 +1,81 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use core::cmp::Ordering; + +use defmt::info; +use embassy_executor::Spawner; +use embassy_nrf::pdm::{self, Config, Frequency, OperationMode, Pdm, Ratio, SamplerState}; +use embassy_nrf::{bind_interrupts, peripherals}; +use fixed::types::I7F1; +use microfft::real::rfft_1024; +use num_integer::Roots; +use {defmt_rtt as _, panic_probe as _}; + +// Demonstrates both continuous sampling and scanning multiple channels driven by a PPI linked timer + +bind_interrupts!(struct Irqs { + PDM => pdm::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_p: Spawner) { + let mut p = embassy_nrf::init(Default::default()); + let mut config = Config::default(); + // Pins are correct for the onboard microphone on the Feather nRF52840 Sense. + config.frequency = Frequency::_1280K; // 16 kHz sample rate + config.ratio = Ratio::RATIO80; + config.operation_mode = OperationMode::Mono; + config.gain_left = I7F1::from_bits(5); // 2.5 dB + let mut pdm = Pdm::new(p.PDM, Irqs, &mut p.P0_00, &mut p.P0_01, config); + + let mut bufs = [[0; 1024]; 2]; + + pdm.run_task_sampler(&mut bufs, move |buf| { + // NOTE: It is important that the time spent within this callback + // does not exceed the time taken to acquire the 1500 samples we + // have in this example, which would be 10us + 2us per + // sample * 1500 = 18ms. You need to measure the time taken here + // and set the sample buffer size accordingly. Exceeding this + // time can lead to the peripheral re-writing the other buffer. + let mean = (buf.iter().map(|v| i32::from(*v)).sum::() / buf.len() as i32) as i16; + let (peak_freq_index, peak_mag) = fft_peak_freq(&buf); + let peak_freq = peak_freq_index * 16000 / buf.len(); + info!( + "{} samples, min {=i16}, max {=i16}, mean {=i16}, AC RMS {=i16}, peak {} @ {} Hz", + buf.len(), + buf.iter().min().unwrap(), + buf.iter().max().unwrap(), + mean, + (buf.iter() + .map(|v| i32::from(*v - mean).pow(2)) + .fold(0i32, |a, b| a.saturating_add(b)) + / buf.len() as i32) + .sqrt() as i16, + peak_mag, + peak_freq, + ); + SamplerState::Sampled + }) + .await + .unwrap(); +} + +fn fft_peak_freq(input: &[i16; 1024]) -> (usize, u32) { + let mut f = [0f32; 1024]; + for i in 0..input.len() { + f[i] = (input[i] as f32) / 32768.0; + } + // N.B. rfft_1024 does the FFT in-place so result is actually also a reference to f. + let result = rfft_1024(&mut f); + result[0].im = 0.0; + + result + .iter() + .map(|c| c.norm_sqr()) + .enumerate() + .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(Ordering::Equal)) + .map(|(i, v)| (i, ((v * 32768.0) as u32).sqrt())) + .unwrap() +} diff --git a/examples/nrf52840/src/bin/wifi_esp_hosted.rs b/examples/nrf52840/src/bin/wifi_esp_hosted.rs index 4eb31b10..112e41bc 100644 --- a/examples/nrf52840/src/bin/wifi_esp_hosted.rs +++ b/examples/nrf52840/src/bin/wifi_esp_hosted.rs @@ -10,11 +10,15 @@ use embassy_nrf::gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin, Pull}; use embassy_nrf::rng::Rng; use embassy_nrf::spim::{self, Spim}; use embassy_nrf::{bind_interrupts, peripherals}; +use embassy_time::Delay; use embedded_hal_async::spi::ExclusiveDevice; use embedded_io::asynch::Write; use static_cell::make_static; use {defmt_rtt as _, embassy_net_esp_hosted as hosted, panic_probe as _}; +const WIFI_NETWORK: &str = "EmbassyTest"; +const WIFI_PASSWORD: &str = "V8YxhKt5CdIAJFud"; + bind_interrupts!(struct Irqs { SPIM3 => spim::InterruptHandler; RNG => embassy_nrf::rng::InterruptHandler; @@ -24,7 +28,7 @@ bind_interrupts!(struct Irqs { async fn wifi_task( runner: hosted::Runner< 'static, - ExclusiveDevice, Output<'static, peripherals::P0_31>>, + ExclusiveDevice, Output<'static, peripherals::P0_31>, Delay>, Input<'static, AnyPin>, Output<'static, peripherals::P1_05>, >, @@ -55,7 +59,7 @@ async fn main(spawner: Spawner) { config.frequency = spim::Frequency::M32; config.mode = spim::MODE_2; // !!! let spi = spim::Spim::new(p.SPI3, Irqs, sck, miso, mosi, config); - let spi = ExclusiveDevice::new(spi, cs); + let spi = ExclusiveDevice::new(spi, cs, Delay); let (device, mut control, runner) = embassy_net_esp_hosted::new( make_static!(embassy_net_esp_hosted::State::new()), @@ -69,7 +73,7 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(wifi_task(runner))); control.init().await; - control.join(env!("WIFI_NETWORK"), env!("WIFI_PASSWORD")).await; + control.join(WIFI_NETWORK, WIFI_PASSWORD).await; let config = embassy_net::Config::dhcpv4(Default::default()); // let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index efb66bae..f1d45f33 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -14,7 +14,7 @@ embassy-executor = { version = "0.2.0", path = "../../embassy-executor", feature "defmt", "integrated-timers", ] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = [ +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime", ] } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 48f3a26b..c812cb3e 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal", features = ["defmt"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "critical-section-impl"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] } @@ -40,9 +40,10 @@ display-interface = "0.4.1" byte-slice-cast = { version = "1.2.0", default-features = false } smart-leds = "0.3.0" heapless = "0.7.15" +usbd-hid = "0.6.1" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } -embedded-hal-async = "0.2.0-alpha.1" +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } +embedded-hal-async = "0.2.0-alpha.2" embedded-io = { version = "0.4.0", features = ["async", "defmt"] } embedded-storage = { version = "0.3" } static_cell = { version = "1.1", features = ["nightly"]} @@ -53,3 +54,6 @@ rand = { version = "0.8.5", default-features = false } [profile.release] debug = true + +[patch.crates-io] +lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } diff --git a/examples/rp/src/bin/adc.rs b/examples/rp/src/bin/adc.rs index 7c2ca19f..81a8b834 100644 --- a/examples/rp/src/bin/adc.rs +++ b/examples/rp/src/bin/adc.rs @@ -1,11 +1,15 @@ +//! This example test the ADC (Analog to Digital Conversion) of the RS2040 pin 26, 27 and 28. +//! It also reads the temperature sensor in the chip. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] use defmt::*; use embassy_executor::Spawner; -use embassy_rp::adc::{Adc, Config, InterruptHandler}; +use embassy_rp::adc::{Adc, Config, InterruptHandler, Pin}; use embassy_rp::bind_interrupts; +use embassy_rp::gpio::Pull; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -18,18 +22,18 @@ async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); let mut adc = Adc::new(p.ADC, Irqs, Config::default()); - let mut p26 = p.PIN_26; - let mut p27 = p.PIN_27; - let mut p28 = p.PIN_28; + let mut p26 = Pin::new(p.PIN_26, Pull::None); + let mut p27 = Pin::new(p.PIN_27, Pull::None); + let mut p28 = Pin::new(p.PIN_28, Pull::None); loop { - let level = adc.read(&mut p26).await; + let level = adc.read(&mut p26).await.unwrap(); info!("Pin 26 ADC: {}", level); - let level = adc.read(&mut p27).await; + let level = adc.read(&mut p27).await.unwrap(); info!("Pin 27 ADC: {}", level); - let level = adc.read(&mut p28).await; + let level = adc.read(&mut p28).await.unwrap(); info!("Pin 28 ADC: {}", level); - let temp = adc.read_temperature().await; + let temp = adc.read_temperature().await.unwrap(); info!("Temp: {} degrees", convert_to_celsius(temp)); Timer::after(Duration::from_secs(1)).await; } @@ -37,5 +41,8 @@ async fn main(_spawner: Spawner) { fn convert_to_celsius(raw_temp: u16) -> f32 { // According to chapter 4.9.5. Temperature Sensor in RP2040 datasheet - 27.0 - (raw_temp as f32 * 3.3 / 4096.0 - 0.706) / 0.001721 as f32 + let temp = 27.0 - (raw_temp as f32 * 3.3 / 4096.0 - 0.706) / 0.001721; + let sign = if temp < 0.0 { -1.0 } else { 1.0 }; + let rounded_temp_x10: i16 = ((temp * 10.0) + 0.5 * sign) as i16; + (rounded_temp_x10 as f32) / 10.0 } diff --git a/examples/rp/src/bin/blinky.rs b/examples/rp/src/bin/blinky.rs index 7aa36a19..295b000f 100644 --- a/examples/rp/src/bin/blinky.rs +++ b/examples/rp/src/bin/blinky.rs @@ -1,3 +1,7 @@ +//! This example test the RP Pico on board LED. +//! +//! It does not work with the RP Pico W board. See wifi_blinky.rs. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/button.rs b/examples/rp/src/bin/button.rs index 0d246c09..d7aa8941 100644 --- a/examples/rp/src/bin/button.rs +++ b/examples/rp/src/bin/button.rs @@ -1,3 +1,7 @@ +//! This example uses the RP Pico on board LED to test input pin 28. This is not the button on the board. +//! +//! It does not work with the RP Pico W board. Use wifi_blinky.rs and add input pin. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/ethernet_w5500_multisocket.rs b/examples/rp/src/bin/ethernet_w5500_multisocket.rs index 82568254..e81da177 100644 --- a/examples/rp/src/bin/ethernet_w5500_multisocket.rs +++ b/examples/rp/src/bin/ethernet_w5500_multisocket.rs @@ -15,7 +15,7 @@ use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; -use embassy_time::Duration; +use embassy_time::{Delay, Duration}; use embedded_hal_async::spi::ExclusiveDevice; use embedded_io::asynch::Write; use rand::RngCore; @@ -26,7 +26,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn ethernet_task( runner: Runner< 'static, - ExclusiveDevice, Output<'static, PIN_17>>, + ExclusiveDevice, Output<'static, PIN_17>, Delay>, Input<'static, PIN_21>, Output<'static, PIN_20>, >, @@ -54,8 +54,14 @@ async fn main(spawner: Spawner) { let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; let state = make_static!(State::<8, 8>::new()); - let (device, runner) = - embassy_net_w5500::new(mac_addr, state, ExclusiveDevice::new(spi, cs), w5500_int, w5500_reset).await; + let (device, runner) = embassy_net_w5500::new( + mac_addr, + state, + ExclusiveDevice::new(spi, cs, Delay), + w5500_int, + w5500_reset, + ) + .await; unwrap!(spawner.spawn(ethernet_task(runner))); // Generate random seed diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs index d562defa..9dd7ae97 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs @@ -17,7 +17,7 @@ use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; -use embassy_time::{Duration, Timer}; +use embassy_time::{Delay, Duration, Timer}; use embedded_hal_async::spi::ExclusiveDevice; use embedded_io::asynch::Write; use rand::RngCore; @@ -28,7 +28,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn ethernet_task( runner: Runner< 'static, - ExclusiveDevice, Output<'static, PIN_17>>, + ExclusiveDevice, Output<'static, PIN_17>, Delay>, Input<'static, PIN_21>, Output<'static, PIN_20>, >, @@ -57,8 +57,14 @@ async fn main(spawner: Spawner) { let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; let state = make_static!(State::<8, 8>::new()); - let (device, runner) = - embassy_net_w5500::new(mac_addr, state, ExclusiveDevice::new(spi, cs), w5500_int, w5500_reset).await; + let (device, runner) = embassy_net_w5500::new( + mac_addr, + state, + ExclusiveDevice::new(spi, cs, Delay), + w5500_int, + w5500_reset, + ) + .await; unwrap!(spawner.spawn(ethernet_task(runner))); // Generate random seed diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs index 7f521cdb..db21c2b6 100644 --- a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs +++ b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs @@ -16,7 +16,7 @@ use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; -use embassy_time::Duration; +use embassy_time::{Delay, Duration}; use embedded_hal_async::spi::ExclusiveDevice; use embedded_io::asynch::Write; use rand::RngCore; @@ -26,7 +26,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn ethernet_task( runner: Runner< 'static, - ExclusiveDevice, Output<'static, PIN_17>>, + ExclusiveDevice, Output<'static, PIN_17>, Delay>, Input<'static, PIN_21>, Output<'static, PIN_20>, >, @@ -55,8 +55,14 @@ async fn main(spawner: Spawner) { let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; let state = make_static!(State::<8, 8>::new()); - let (device, runner) = - embassy_net_w5500::new(mac_addr, state, ExclusiveDevice::new(spi, cs), w5500_int, w5500_reset).await; + let (device, runner) = embassy_net_w5500::new( + mac_addr, + state, + ExclusiveDevice::new(spi, cs, Delay), + w5500_int, + w5500_reset, + ) + .await; unwrap!(spawner.spawn(ethernet_task(runner))); // Generate random seed diff --git a/examples/rp/src/bin/ethernet_w5500_udp.rs b/examples/rp/src/bin/ethernet_w5500_udp.rs index ada86ae5..038432b1 100644 --- a/examples/rp/src/bin/ethernet_w5500_udp.rs +++ b/examples/rp/src/bin/ethernet_w5500_udp.rs @@ -16,6 +16,7 @@ use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Level, Output, Pull}; use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0}; use embassy_rp::spi::{Async, Config as SpiConfig, Spi}; +use embassy_time::Delay; use embedded_hal_async::spi::ExclusiveDevice; use rand::RngCore; use static_cell::make_static; @@ -24,7 +25,7 @@ use {defmt_rtt as _, panic_probe as _}; async fn ethernet_task( runner: Runner< 'static, - ExclusiveDevice, Output<'static, PIN_17>>, + ExclusiveDevice, Output<'static, PIN_17>, Delay>, Input<'static, PIN_21>, Output<'static, PIN_20>, >, @@ -52,8 +53,14 @@ async fn main(spawner: Spawner) { let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00]; let state = make_static!(State::<8, 8>::new()); - let (device, runner) = - embassy_net_w5500::new(mac_addr, state, ExclusiveDevice::new(spi, cs), w5500_int, w5500_reset).await; + let (device, runner) = embassy_net_w5500::new( + mac_addr, + state, + ExclusiveDevice::new(spi, cs, Delay), + w5500_int, + w5500_reset, + ) + .await; unwrap!(spawner.spawn(ethernet_task(runner))); // Generate random seed diff --git a/examples/rp/src/bin/flash.rs b/examples/rp/src/bin/flash.rs index 19076150..4c4982ac 100644 --- a/examples/rp/src/bin/flash.rs +++ b/examples/rp/src/bin/flash.rs @@ -1,3 +1,5 @@ +//! This example test the flash connected to the RP2040 chip. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/gpio_async.rs b/examples/rp/src/bin/gpio_async.rs index 52d13a9d..bf58044d 100644 --- a/examples/rp/src/bin/gpio_async.rs +++ b/examples/rp/src/bin/gpio_async.rs @@ -1,3 +1,7 @@ +//! This example shows how async gpio can be used with a RP2040. +//! +//! The LED on the RP Pico W board is connected differently. See wifi_blinky.rs. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] @@ -9,8 +13,6 @@ use embassy_time::{Duration, Timer}; use gpio::{Input, Level, Output, Pull}; use {defmt_rtt as _, panic_probe as _}; -/// This example shows how async gpio can be used with a RP2040. -/// /// It requires an external signal to be manually triggered on PIN 16. For /// example, this could be accomplished using an external power source with a /// button so that it is possible to toggle the signal from low to high. diff --git a/examples/rp/src/bin/gpout.rs b/examples/rp/src/bin/gpout.rs index 64461fc5..0a3b5fa9 100644 --- a/examples/rp/src/bin/gpout.rs +++ b/examples/rp/src/bin/gpout.rs @@ -1,3 +1,7 @@ +//! This example shows how GPOUT (General purpose clock outputs) can toggle a output pin. +//! +//! The LED on the RP Pico W board is connected differently. Add a LED and resistor to another pin. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/i2c_async.rs b/examples/rp/src/bin/i2c_async.rs index cf3cf742..93224bc4 100644 --- a/examples/rp/src/bin/i2c_async.rs +++ b/examples/rp/src/bin/i2c_async.rs @@ -1,3 +1,8 @@ +//! This example shows how to communicate asynchronous using i2c with external chips. +//! +//! Example written for the [`MCP23017 16-Bit I2C I/O Expander with Serial Interface`] chip. +//! (https://www.microchip.com/en-us/product/mcp23017) + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/i2c_blocking.rs b/examples/rp/src/bin/i2c_blocking.rs index 7623e33c..1c8c2039 100644 --- a/examples/rp/src/bin/i2c_blocking.rs +++ b/examples/rp/src/bin/i2c_blocking.rs @@ -1,3 +1,8 @@ +//! This example shows how to communicate using i2c with external chips. +//! +//! Example written for the [`MCP23017 16-Bit I2C I/O Expander with Serial Interface`] chip. +//! (https://www.microchip.com/en-us/product/mcp23017) + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/lora_lorawan.rs b/examples/rp/src/bin/lora_lorawan.rs index a9c84bf9..d631fafa 100644 --- a/examples/rp/src/bin/lora_lorawan.rs +++ b/examples/rp/src/bin/lora_lorawan.rs @@ -1,5 +1,6 @@ //! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio. //! It demonstrates LoRaWAN join functionality. + #![no_std] #![no_main] #![macro_use] diff --git a/examples/rp/src/bin/lora_p2p_receive.rs b/examples/rp/src/bin/lora_p2p_receive.rs index 25041920..396d669d 100644 --- a/examples/rp/src/bin/lora_p2p_receive.rs +++ b/examples/rp/src/bin/lora_p2p_receive.rs @@ -1,5 +1,6 @@ //! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio. //! It demonstrates LORA P2P receive functionality in conjunction with the lora_p2p_send example. + #![no_std] #![no_main] #![macro_use] diff --git a/examples/rp/src/bin/lora_p2p_send.rs b/examples/rp/src/bin/lora_p2p_send.rs index 3a0544b1..a0f70fa5 100644 --- a/examples/rp/src/bin/lora_p2p_send.rs +++ b/examples/rp/src/bin/lora_p2p_send.rs @@ -1,5 +1,6 @@ //! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio. //! It demonstrates LORA P2P send functionality. + #![no_std] #![no_main] #![macro_use] diff --git a/examples/rp/src/bin/lora_p2p_send_multicore.rs b/examples/rp/src/bin/lora_p2p_send_multicore.rs index eef2f7a5..89a62818 100644 --- a/examples/rp/src/bin/lora_p2p_send_multicore.rs +++ b/examples/rp/src/bin/lora_p2p_send_multicore.rs @@ -1,5 +1,6 @@ //! This example runs on the Raspberry Pi Pico with a Waveshare board containing a Semtech Sx1262 radio. //! It demonstrates LORA P2P send functionality using the second core, with data provided by the first core. + #![no_std] #![no_main] #![macro_use] diff --git a/examples/rp/src/bin/multicore.rs b/examples/rp/src/bin/multicore.rs index 57278dd6..893b724b 100644 --- a/examples/rp/src/bin/multicore.rs +++ b/examples/rp/src/bin/multicore.rs @@ -1,3 +1,7 @@ +//! This example shows how to send messages between the two cores in the RP2040 chip. +//! +//! The LED on the RP Pico W board is connected differently. See wifi_blinky.rs. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 79eda1a0..c001d644 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -1,15 +1,22 @@ +//! This example shows powerful PIO module in the RP2040 chip. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] use defmt::info; use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{Common, Config, Irq, Pio, PioPin, ShiftDirection, StateMachine}; +use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine}; use embassy_rp::relocate::RelocatedProgram; use fixed::traits::ToFixed; use fixed_macro::types::U56F8; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 0>, pin: impl PioPin) { // Setup sm0 @@ -49,7 +56,14 @@ fn setup_pio_task_sm1<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, // Setupm sm1 // Read 0b10101 repeatedly until ISR is full - let prg = pio_proc::pio_asm!(".origin 8", "set x, 0x15", ".wrap_target", "in x, 5 [31]", ".wrap",); + let prg = pio_proc::pio_asm!( + // + ".origin 8", + "set x, 0x15", + ".wrap_target", + "in x, 5 [31]", + ".wrap", + ); let relocated = RelocatedProgram::new(&prg.program); let mut cfg = Config::default(); @@ -110,7 +124,7 @@ async fn main(spawner: Spawner) { mut sm1, mut sm2, .. - } = Pio::new(pio); + } = Pio::new(pio, Irqs); setup_pio_task_sm0(&mut common, &mut sm0, p.PIN_0); setup_pio_task_sm1(&mut common, &mut sm1); diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs index 05c0ebb1..9ab72e1f 100644 --- a/examples/rp/src/bin/pio_dma.rs +++ b/examples/rp/src/bin/pio_dma.rs @@ -1,16 +1,23 @@ +//! This example shows powerful PIO module in the RP2040 chip. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] use defmt::info; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_rp::pio::{Config, Pio, ShiftConfig, ShiftDirection}; +use embassy_rp::peripherals::PIO0; +use embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection}; use embassy_rp::relocate::RelocatedProgram; -use embassy_rp::Peripheral; +use embassy_rp::{bind_interrupts, Peripheral}; use fixed::traits::ToFixed; use fixed_macro::types::U56F8; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + fn swap_nibbles(v: u32) -> u32 { let v = (v & 0x0f0f_0f0f) << 4 | (v & 0xf0f0_f0f0) >> 4; let v = (v & 0x00ff_00ff) << 8 | (v & 0xff00_ff00) >> 8; @@ -25,7 +32,7 @@ async fn main(_spawner: Spawner) { mut common, sm0: mut sm, .. - } = Pio::new(pio); + } = Pio::new(pio, Irqs); let prg = pio_proc::pio_asm!( ".origin 0", diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index bfc6c990..8aedd24b 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -1,3 +1,6 @@ +//! This example shows powerful PIO module in the RP2040 chip to communicate with a HD44780 display. +//! See (https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] @@ -7,13 +10,19 @@ use core::fmt::Write; use embassy_executor::Spawner; use embassy_rp::dma::{AnyChannel, Channel}; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{Config, Direction, FifoJoin, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine}; +use embassy_rp::pio::{ + Config, Direction, FifoJoin, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, +}; use embassy_rp::pwm::{self, Pwm}; use embassy_rp::relocate::RelocatedProgram; -use embassy_rp::{into_ref, Peripheral, PeripheralRef}; +use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef}; use embassy_time::{Duration, Instant, Timer}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(pub struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { // this test assumes a 2x16 HD44780 display attached as follow: @@ -37,7 +46,7 @@ async fn main(_spawner: Spawner) { }); let mut hd = HD44780::new( - p.PIO0, p.DMA_CH3, p.PIN_0, p.PIN_1, p.PIN_2, p.PIN_3, p.PIN_4, p.PIN_5, p.PIN_6, + p.PIO0, Irqs, p.DMA_CH3, p.PIN_0, p.PIN_1, p.PIN_2, p.PIN_3, p.PIN_4, p.PIN_5, p.PIN_6, ) .await; @@ -72,6 +81,7 @@ pub struct HD44780<'l> { impl<'l> HD44780<'l> { pub async fn new( pio: impl Peripheral

+ 'l, + irq: Irqs, dma: impl Peripheral

+ 'l, rs: impl PioPin, rw: impl PioPin, @@ -88,7 +98,7 @@ impl<'l> HD44780<'l> { mut irq0, mut sm0, .. - } = Pio::new(pio); + } = Pio::new(pio, irq); // takes command words ( <0:4>) let prg = pio_proc::pio_asm!( diff --git a/examples/rp/src/bin/pio_ws2812.rs b/examples/rp/src/bin/pio_ws2812.rs index 26422421..3de2bd48 100644 --- a/examples/rp/src/bin/pio_ws2812.rs +++ b/examples/rp/src/bin/pio_ws2812.rs @@ -1,3 +1,6 @@ +//! This example shows powerful PIO module in the RP2040 chip to communicate with WS2812 LED modules. +//! See (https://www.sparkfun.com/categories/tags/ws2812) + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] @@ -5,15 +8,22 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::dma::{AnyChannel, Channel}; -use embassy_rp::pio::{Common, Config, FifoJoin, Instance, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine}; +use embassy_rp::peripherals::PIO0; +use embassy_rp::pio::{ + Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, +}; use embassy_rp::relocate::RelocatedProgram; -use embassy_rp::{clocks, into_ref, Peripheral, PeripheralRef}; +use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef}; use embassy_time::{Duration, Timer}; use fixed::types::U24F8; use fixed_macro::fixed; use smart_leds::RGB8; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + pub struct Ws2812<'d, P: Instance, const S: usize, const N: usize> { dma: PeripheralRef<'d, AnyChannel>, sm: StateMachine<'d, P, S>, @@ -123,7 +133,7 @@ async fn main(_spawner: Spawner) { info!("Start"); let p = embassy_rp::init(Default::default()); - let Pio { mut common, sm0, .. } = Pio::new(p.PIO0); + let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); // This is the number of leds in the string. Helpfully, the sparkfun thing plus and adafruit // feather boards for the 2040 both have one built in. diff --git a/examples/rp/src/bin/pwm.rs b/examples/rp/src/bin/pwm.rs index 2b3d5d97..9d919287 100644 --- a/examples/rp/src/bin/pwm.rs +++ b/examples/rp/src/bin/pwm.rs @@ -1,3 +1,7 @@ +//! This example shows how to use PWM (Pulse Width Modulation) in the RP2040 chip. +//! +//! The LED on the RP Pico W board is connected differently. Add a LED and resistor to another pin. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/rtc.rs b/examples/rp/src/bin/rtc.rs new file mode 100644 index 00000000..15aa8243 --- /dev/null +++ b/examples/rp/src/bin/rtc.rs @@ -0,0 +1,46 @@ +//! This example shows how to use RTC (Real Time Clock) in the RP2040 chip. + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::rtc::{DateTime, DayOfWeek, Rtc}; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + info!("Wait for 20s"); + + let mut rtc = Rtc::new(p.RTC); + + if !rtc.is_running() { + info!("Start RTC"); + let now = DateTime { + year: 2000, + month: 1, + day: 1, + day_of_week: DayOfWeek::Saturday, + hour: 0, + minute: 0, + second: 0, + }; + rtc.set_datetime(now).unwrap(); + } + + Timer::after(Duration::from_millis(20000)).await; + + if let Ok(dt) = rtc.now() { + info!( + "Now: {}-{:02}-{:02} {}:{:02}:{:02}", + dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, + ); + } + + info!("Reboot."); + Timer::after(Duration::from_millis(200)).await; + cortex_m::peripheral::SCB::sys_reset(); +} diff --git a/examples/rp/src/bin/spi.rs b/examples/rp/src/bin/spi.rs index a830a17a..602348f7 100644 --- a/examples/rp/src/bin/spi.rs +++ b/examples/rp/src/bin/spi.rs @@ -1,3 +1,7 @@ +//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip. +//! +//! Example for resistive touch sensor in Waveshare Pico-ResTouch + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/spi_async.rs b/examples/rp/src/bin/spi_async.rs index 671a9caa..328074e8 100644 --- a/examples/rp/src/bin/spi_async.rs +++ b/examples/rp/src/bin/spi_async.rs @@ -1,3 +1,6 @@ +//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip. +//! No specific hardware is specified in this example. If you connect pin 11 and 12 you should get the same data back. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/spi_display.rs b/examples/rp/src/bin/spi_display.rs index 85a19ce0..26c258e1 100644 --- a/examples/rp/src/bin/spi_display.rs +++ b/examples/rp/src/bin/spi_display.rs @@ -1,3 +1,8 @@ +//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip. +//! +//! Example written for a display using the ST7789 chip. Possibly the Waveshare Pico-ResTouch +//! (https://www.waveshare.com/wiki/Pico-ResTouch-LCD-2.8) + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] @@ -175,7 +180,7 @@ mod touch { mod my_display_interface { use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand}; use embedded_hal_1::digital::OutputPin; - use embedded_hal_1::spi::SpiDeviceWrite; + use embedded_hal_1::spi::SpiDevice; /// SPI display interface. /// @@ -187,7 +192,7 @@ mod my_display_interface { impl SPIDeviceInterface where - SPI: SpiDeviceWrite, + SPI: SpiDevice, DC: OutputPin, { /// Create new SPI interface for communciation with a display driver @@ -198,7 +203,7 @@ mod my_display_interface { impl WriteOnlyDataCommand for SPIDeviceInterface where - SPI: SpiDeviceWrite, + SPI: SpiDevice, DC: OutputPin, { fn send_commands(&mut self, cmds: DataFormat<'_>) -> Result<(), DisplayError> { @@ -218,7 +223,7 @@ mod my_display_interface { } } - fn send_u8(spi: &mut T, words: DataFormat<'_>) -> Result<(), T::Error> { + fn send_u8(spi: &mut T, words: DataFormat<'_>) -> Result<(), T::Error> { match words { DataFormat::U8(slice) => spi.write(slice), DataFormat::U16(slice) => { diff --git a/examples/rp/src/bin/uart.rs b/examples/rp/src/bin/uart.rs index 05177a6b..451c3c39 100644 --- a/examples/rp/src/bin/uart.rs +++ b/examples/rp/src/bin/uart.rs @@ -1,3 +1,9 @@ +//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip. +//! +//! No specific hardware is specified in this example. Only output on pin 0 is tested. +//! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used +//! with its UART port. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/uart_buffered_split.rs b/examples/rp/src/bin/uart_buffered_split.rs index 9df99bd5..73520171 100644 --- a/examples/rp/src/bin/uart_buffered_split.rs +++ b/examples/rp/src/bin/uart_buffered_split.rs @@ -1,3 +1,9 @@ +//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip. +//! +//! No specific hardware is specified in this example. If you connect pin 0 and 1 you should get the same data back. +//! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used +//! with its UART port. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/uart_unidir.rs b/examples/rp/src/bin/uart_unidir.rs index c0943a1b..c1515a91 100644 --- a/examples/rp/src/bin/uart_unidir.rs +++ b/examples/rp/src/bin/uart_unidir.rs @@ -1,5 +1,9 @@ -//! test TX-only and RX-only UARTs. You need to connect GPIO0 to GPIO5 for +//! This example shows how to use UART (Universal asynchronous receiver-transmitter) in the RP2040 chip. +//! +//! Test TX-only and RX-only on two different UARTs. You need to connect GPIO0 to GPIO5 for //! this to work +//! The Raspberry Pi Debug Probe (https://www.raspberrypi.com/products/debug-probe/) could be used +//! with its UART port. #![no_std] #![no_main] diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 91d1ec8e..0a08f667 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -1,3 +1,7 @@ +//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip. +//! +//! This is a CDC-NCM class implementation, aka Ethernet over USB. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/usb_hid_keyboard.rs b/examples/rp/src/bin/usb_hid_keyboard.rs new file mode 100644 index 00000000..99af1f02 --- /dev/null +++ b/examples/rp/src/bin/usb_hid_keyboard.rs @@ -0,0 +1,188 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use core::sync::atomic::{AtomicBool, Ordering}; + +use defmt::*; +use embassy_executor::Spawner; +use embassy_futures::join::join; +use embassy_rp::bind_interrupts; +use embassy_rp::gpio::{Input, Pull}; +use embassy_rp::peripherals::USB; +use embassy_rp::usb::{Driver, InterruptHandler}; +use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; +use embassy_usb::control::OutResponse; +use embassy_usb::{Builder, Config, Handler}; +use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + USBCTRL_IRQ => InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + // Create the driver, from the HAL. + let driver = Driver::new(p.USB, Irqs); + + // Create embassy-usb Config + let mut config = Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("HID keyboard example"); + config.serial_number = Some("12345678"); + config.max_power = 100; + config.max_packet_size_0 = 64; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut device_descriptor = [0; 256]; + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + // You can also add a Microsoft OS descriptor. + // let mut msos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + let request_handler = MyRequestHandler {}; + let mut device_handler = MyDeviceHandler::new(); + + let mut state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + // &mut msos_descriptor, + &mut control_buf, + ); + + builder.handler(&mut device_handler); + + // Create classes on the builder. + let config = embassy_usb::class::hid::Config { + report_descriptor: KeyboardReport::desc(), + request_handler: Some(&request_handler), + poll_ms: 60, + max_packet_size: 64, + }; + let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Set up the signal pin that will be used to trigger the keyboard. + let mut signal_pin = Input::new(p.PIN_16, Pull::None); + + let (reader, mut writer) = hid.split(); + + // Do stuff with the class! + let in_fut = async { + loop { + info!("Waiting for HIGH on pin 16"); + signal_pin.wait_for_high().await; + info!("HIGH DETECTED"); + // Create a report with the A key pressed. (no shift modifier) + let report = KeyboardReport { + keycodes: [4, 0, 0, 0, 0, 0], + leds: 0, + modifier: 0, + reserved: 0, + }; + // Send the report. + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + }; + signal_pin.wait_for_low().await; + info!("LOW DETECTED"); + let report = KeyboardReport { + keycodes: [0, 0, 0, 0, 0, 0], + leds: 0, + modifier: 0, + reserved: 0, + }; + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + }; + } + }; + + let out_fut = async { + reader.run(false, &request_handler).await; + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, join(in_fut, out_fut)).await; +} + +struct MyRequestHandler {} + +impl RequestHandler for MyRequestHandler { + fn get_report(&self, id: ReportId, _buf: &mut [u8]) -> Option { + info!("Get report for {:?}", id); + None + } + + fn set_report(&self, id: ReportId, data: &[u8]) -> OutResponse { + info!("Set report for {:?}: {=[u8]}", id, data); + OutResponse::Accepted + } + + fn set_idle_ms(&self, id: Option, dur: u32) { + info!("Set idle rate for {:?} to {:?}", id, dur); + } + + fn get_idle_ms(&self, id: Option) -> Option { + info!("Get idle rate for {:?}", id); + None + } +} + +struct MyDeviceHandler { + configured: AtomicBool, +} + +impl MyDeviceHandler { + fn new() -> Self { + MyDeviceHandler { + configured: AtomicBool::new(false), + } + } +} + +impl Handler for MyDeviceHandler { + fn enabled(&mut self, enabled: bool) { + self.configured.store(false, Ordering::Relaxed); + if enabled { + info!("Device enabled"); + } else { + info!("Device disabled"); + } + } + + fn reset(&mut self) { + self.configured.store(false, Ordering::Relaxed); + info!("Bus reset, the Vbus current limit is 100mA"); + } + + fn addressed(&mut self, addr: u8) { + self.configured.store(false, Ordering::Relaxed); + info!("USB address set to: {}", addr); + } + + fn configured(&mut self, configured: bool) { + self.configured.store(configured, Ordering::Relaxed); + if configured { + info!("Device configured, it may now draw up to the configured current limit from Vbus.") + } else { + info!("Device is no longer configured, the Vbus current limit is 100mA."); + } + } +} diff --git a/examples/rp/src/bin/usb_logger.rs b/examples/rp/src/bin/usb_logger.rs index 7c90d0ca..9c5e6897 100644 --- a/examples/rp/src/bin/usb_logger.rs +++ b/examples/rp/src/bin/usb_logger.rs @@ -1,3 +1,7 @@ +//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip. +//! +//! This creates the possibility to send log::info/warn/error/debug! to USB serial port. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs index ca728536..164e2052 100644 --- a/examples/rp/src/bin/usb_serial.rs +++ b/examples/rp/src/bin/usb_serial.rs @@ -1,3 +1,7 @@ +//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip. +//! +//! This creates a USB serial port that echos. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/watchdog.rs b/examples/rp/src/bin/watchdog.rs index ece5cfe3..fe5eaf92 100644 --- a/examples/rp/src/bin/watchdog.rs +++ b/examples/rp/src/bin/watchdog.rs @@ -1,3 +1,7 @@ +//! This example shows how to use Watchdog in the RP2040 chip. +//! +//! It does not work with the RP Pico W board. See wifi_blinky.rs or connect external LED and resistor. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/examples/rp/src/bin/wifi_ap_tcp_server.rs b/examples/rp/src/bin/wifi_ap_tcp_server.rs index 310e84d9..e3e39344 100644 --- a/examples/rp/src/bin/wifi_ap_tcp_server.rs +++ b/examples/rp/src/bin/wifi_ap_tcp_server.rs @@ -1,3 +1,6 @@ +//! This example uses the RP Pico W board Wifi chip (cyw43). +//! Creates an Access point Wifi network and creates a TCP endpoint on port 1234. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] @@ -11,14 +14,19 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Config, Stack, StackResources}; +use embassy_rp::bind_interrupts; use embassy_rp::gpio::{Level, Output}; use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; -use embassy_rp::pio::Pio; +use embassy_rp::pio::{InterruptHandler, Pio}; use embassy_time::Duration; use embedded_io::asynch::Write; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + #[embassy_executor::task] async fn wifi_task( runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, @@ -49,7 +57,7 @@ async fn main(spawner: Spawner) { let pwr = Output::new(p.PIN_23, Level::Low); let cs = Output::new(p.PIN_25, Level::High); - let mut pio = Pio::new(p.PIO0); + let mut pio = Pio::new(p.PIO0, Irqs); let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); let state = make_static!(cyw43::State::new()); diff --git a/examples/rp/src/bin/wifi_blinky.rs b/examples/rp/src/bin/wifi_blinky.rs index bbcb1b5e..33d43788 100644 --- a/examples/rp/src/bin/wifi_blinky.rs +++ b/examples/rp/src/bin/wifi_blinky.rs @@ -1,3 +1,7 @@ +//! This example test the RP Pico W on board LED. +//! +//! It does not work with the RP Pico board. See blinky.rs. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] @@ -5,13 +9,18 @@ use cyw43_pio::PioSpi; use defmt::*; use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; use embassy_rp::gpio::{Level, Output}; use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; -use embassy_rp::pio::Pio; +use embassy_rp::pio::{InterruptHandler, Pio}; use embassy_time::{Duration, Timer}; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + #[embassy_executor::task] async fn wifi_task( runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, @@ -34,7 +43,7 @@ async fn main(spawner: Spawner) { let pwr = Output::new(p.PIN_23, Level::Low); let cs = Output::new(p.PIN_25, Level::High); - let mut pio = Pio::new(p.PIO0); + let mut pio = Pio::new(p.PIO0, Irqs); let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); let state = make_static!(cyw43::State::new()); diff --git a/examples/rp/src/bin/wifi_scan.rs b/examples/rp/src/bin/wifi_scan.rs index 391e1228..743fab61 100644 --- a/examples/rp/src/bin/wifi_scan.rs +++ b/examples/rp/src/bin/wifi_scan.rs @@ -1,3 +1,6 @@ +//! This example uses the RP Pico W board Wifi chip (cyw43). +//! Scans Wifi for ssid names. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] @@ -10,12 +13,17 @@ use cyw43_pio::PioSpi; use defmt::*; use embassy_executor::Spawner; use embassy_net::Stack; +use embassy_rp::bind_interrupts; use embassy_rp::gpio::{Level, Output}; use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; -use embassy_rp::pio::Pio; +use embassy_rp::pio::{InterruptHandler, Pio}; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + #[embassy_executor::task] async fn wifi_task( runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, @@ -46,7 +54,7 @@ async fn main(spawner: Spawner) { let pwr = Output::new(p.PIN_23, Level::Low); let cs = Output::new(p.PIN_25, Level::High); - let mut pio = Pio::new(p.PIO0); + let mut pio = Pio::new(p.PIO0, Irqs); let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); let state = make_static!(cyw43::State::new()); diff --git a/examples/rp/src/bin/wifi_tcp_server.rs b/examples/rp/src/bin/wifi_tcp_server.rs index e9d1079a..0223a363 100644 --- a/examples/rp/src/bin/wifi_tcp_server.rs +++ b/examples/rp/src/bin/wifi_tcp_server.rs @@ -1,3 +1,6 @@ +//! This example uses the RP Pico W board Wifi chip (cyw43). +//! Connects to specified Wifi network and creates a TCP endpoint on port 1234. + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] @@ -11,14 +14,22 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Config, Stack, StackResources}; +use embassy_rp::bind_interrupts; use embassy_rp::gpio::{Level, Output}; use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; -use embassy_rp::pio::Pio; +use embassy_rp::pio::{InterruptHandler, Pio}; use embassy_time::Duration; use embedded_io::asynch::Write; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + +const WIFI_NETWORK: &str = "EmbassyTest"; +const WIFI_PASSWORD: &str = "V8YxhKt5CdIAJFud"; + #[embassy_executor::task] async fn wifi_task( runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, @@ -49,7 +60,7 @@ async fn main(spawner: Spawner) { let pwr = Output::new(p.PIN_23, Level::Low); let cs = Output::new(p.PIN_25, Level::High); - let mut pio = Pio::new(p.PIO0); + let mut pio = Pio::new(p.PIO0, Irqs); let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); let state = make_static!(cyw43::State::new()); @@ -82,8 +93,8 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(net_task(stack))); loop { - //control.join_open(env!("WIFI_NETWORK")).await; - match control.join_wpa2(env!("WIFI_NETWORK"), env!("WIFI_PASSWORD")).await { + //control.join_open(WIFI_NETWORK).await; + match control.join_wpa2(WIFI_NETWORK, WIFI_PASSWORD).await { Ok(_) => break, Err(err) => { info!("join failed with status={}", err.status); diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 878ad8c5..92933ab5 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["log"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-std", "executor-thread", "log", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "std", "nightly"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["log", "std", "nightly"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dns", "dhcpv4", "unstable-traits", "proto-ipv6"] } embassy-net-driver = { version = "0.1.0", path = "../../embassy-net-driver" } embedded-io = { version = "0.4.0", features = ["async", "std", "futures"] } diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml index 43f43252..26837abe 100644 --- a/examples/stm32c0/Cargo.toml +++ b/examples/stm32c0/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] } defmt = "0.3" diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index 8d2248ed..b7b5eaa9 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -14,6 +14,6 @@ defmt-rtt = "0.4" panic-probe = "0.3" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "memory-x", "stm32f091rc", "time-driver-any", "exti", "unstable-pac"] } static_cell = { version = "1.1", features = ["nightly"]} diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index d34fd439..29cad5b6 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any", "unstable-traits" ] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index 5e3e0d0f..652210c7 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] } defmt = "0.3" diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 29ab2009..489d0ff4 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 7ecb64fc..c1c82136 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers", "arch-cortex-m", "executor-thread", "executor-interrupt"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "embedded-sdmmc", "chrono"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } diff --git a/examples/stm32f4/src/bin/can.rs b/examples/stm32f4/src/bin/can.rs index da895505..08bed88d 100644 --- a/examples/stm32f4/src/bin/can.rs +++ b/examples/stm32f4/src/bin/can.rs @@ -2,8 +2,8 @@ #![no_main] #![feature(type_alias_impl_trait)] -use cortex_m_rt::entry; use defmt::*; +use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::can::bxcan::filter::Mask32; use embassy_stm32::can::bxcan::{Fifo, Frame, StandardId}; @@ -19,8 +19,8 @@ bind_interrupts!(struct Irqs { CAN1_TX => TxInterruptHandler; }); -#[entry] -fn main() -> ! { +#[embassy_executor::main] +async fn main(_spawner: Spawner) { info!("Hello World!"); let mut p = embassy_stm32::init(Default::default()); @@ -34,9 +34,12 @@ fn main() -> ! { let mut can = Can::new(p.CAN1, p.PA11, p.PA12, Irqs); - can.modify_filters().enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); + can.as_mut() + .modify_filters() + .enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); - can.modify_config() + can.as_mut() + .modify_config() .set_bit_timing(0x001c0003) // http://www.bittiming.can-wiki.info/ .set_loopback(true) // Receive own frames .set_silent(true) @@ -45,9 +48,8 @@ fn main() -> ! { let mut i: u8 = 0; loop { let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), [i]); - unwrap!(nb::block!(can.transmit(&tx_frame))); - while !can.is_transmitter_idle() {} - let rx_frame = unwrap!(nb::block!(can.receive())); + can.write(&tx_frame).await; + let (_, rx_frame) = can.read().await.unwrap(); info!("loopback frame {=u8}", unwrap!(rx_frame.data())[0]); i += 1; } diff --git a/examples/stm32f4/src/bin/eth.rs b/examples/stm32f4/src/bin/eth.rs new file mode 100644 index 00000000..d0b16439 --- /dev/null +++ b/examples/stm32f4/src/bin/eth.rs @@ -0,0 +1,111 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_net::tcp::TcpSocket; +use embassy_net::{Ipv4Address, Stack, StackResources}; +use embassy_stm32::eth::generic_smi::GenericSMI; +use embassy_stm32::eth::{Ethernet, PacketQueue}; +use embassy_stm32::peripherals::ETH; +use embassy_stm32::rng::Rng; +use embassy_stm32::time::mhz; +use embassy_stm32::{bind_interrupts, eth, Config}; +use embassy_time::{Duration, Timer}; +use embedded_io::asynch::Write; +use static_cell::make_static; +use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + ETH => eth::InterruptHandler; +}); + +type Device = Ethernet<'static, ETH, GenericSMI>; + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack) -> ! { + stack.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) -> ! { + let mut config = Config::default(); + config.rcc.sys_ck = Some(mhz(200)); + let p = embassy_stm32::init(config); + + info!("Hello World!"); + + // Generate random seed. + let mut rng = Rng::new(p.RNG); + let mut seed = [0; 8]; + let _ = rng.async_fill_bytes(&mut seed).await; + let seed = u64::from_le_bytes(seed); + + let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; + + let device = Ethernet::new( + make_static!(PacketQueue::<16, 16>::new()), + p.ETH, + Irqs, + p.PA1, + p.PA2, + p.PC1, + p.PA7, + p.PC4, + p.PC5, + p.PG13, + p.PB13, + p.PG11, + GenericSMI::new(), + mac_addr, + 0, + ); + + let config = embassy_net::Config::dhcpv4(Default::default()); + //let config = embassy_net::Config::ipv4_static(embassy_net::StaticConfigV4 { + // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), + // dns_servers: Vec::new(), + // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), + //}); + + // Init network stack + let stack = &*make_static!(Stack::new( + device, + config, + make_static!(StackResources::<2>::new()), + seed + )); + + // Launch network task + unwrap!(spawner.spawn(net_task(&stack))); + + info!("Network task initialized"); + + // Then we can use it! + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + + loop { + let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); + + socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); + + let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000); + info!("connecting..."); + let r = socket.connect(remote_endpoint).await; + if let Err(e) = r { + info!("connect error: {:?}", e); + continue; + } + info!("connected!"); + let buf = [0; 1024]; + loop { + let r = socket.write_all(&buf).await; + if let Err(e) = r { + info!("write error: {:?}", e); + continue; + } + Timer::after(Duration::from_secs(1)).await; + } + } +} diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index 657251c5..84d7b79c 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } embedded-io = { version = "0.4.0", features = ["async"] } diff --git a/examples/stm32f7/src/bin/can.rs b/examples/stm32f7/src/bin/can.rs new file mode 100644 index 00000000..1b5b377e --- /dev/null +++ b/examples/stm32f7/src/bin/can.rs @@ -0,0 +1,66 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::bind_interrupts; +use embassy_stm32::can::bxcan::filter::Mask32; +use embassy_stm32::can::bxcan::{Fifo, Frame, StandardId}; +use embassy_stm32::can::{ + Can, CanTx, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler, +}; +use embassy_stm32::gpio::{Input, Pull}; +use embassy_stm32::peripherals::CAN3; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + CAN3_RX0 => Rx0InterruptHandler; + CAN3_RX1 => Rx1InterruptHandler; + CAN3_SCE => SceInterruptHandler; + CAN3_TX => TxInterruptHandler; +}); + +#[embassy_executor::task] +pub async fn send_can_message(tx: &'static mut CanTx<'static, 'static, CAN3>) { + loop { + let frame = Frame::new_data(unwrap!(StandardId::new(0 as _)), [0]); + tx.write(&frame).await; + embassy_time::Timer::after(embassy_time::Duration::from_secs(1)).await; + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + info!("Hello World!"); + + let mut p = embassy_stm32::init(Default::default()); + + // The next two lines are a workaround for testing without transceiver. + // To synchronise to the bus the RX input needs to see a high level. + // Use `mem::forget()` to release the borrow on the pin but keep the + // pull-up resistor enabled. + let rx_pin = Input::new(&mut p.PA15, Pull::Up); + core::mem::forget(rx_pin); + + let can: &'static mut Can<'static, CAN3> = static_cell::make_static!(Can::new(p.CAN3, p.PA8, p.PA15, Irqs)); + can.as_mut() + .modify_filters() + .enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); + + can.as_mut() + .modify_config() + .set_bit_timing(0x001c0001) // http://www.bittiming.can-wiki.info/ + .set_loopback(true) + .enable(); + + let (tx, mut rx) = can.split(); + + let tx: &'static mut CanTx<'static, 'static, CAN3> = static_cell::make_static!(tx); + spawner.spawn(send_can_message(tx)).unwrap(); + + loop { + let frame = rx.read().await.unwrap(); + println!("Received: {:?}", frame); + } +} diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index fde6a757..c6b2ba45 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -57,7 +57,7 @@ async fn main(spawner: Spawner) -> ! { p.PG13, p.PB13, p.PG11, - GenericSMI, + GenericSMI::new(), mac_addr, 0, ); diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index c5245757..c88282d9 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g071rb", "memory-x", "unstable-pac", "exti"] } defmt = "0.3" diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index fbfbc640..18bd03c3 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index ebe51134..227bc28b 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h563zi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] } embedded-io = { version = "0.4.0", features = ["async"] } @@ -19,8 +19,8 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } -embedded-hal-async = { version = "=0.2.0-alpha.1" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } +embedded-hal-async = { version = "=0.2.0-alpha.2" } embedded-nal-async = "0.4.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs index 78c8282a..0bff85ed 100644 --- a/examples/stm32h5/src/bin/eth.rs +++ b/examples/stm32h5/src/bin/eth.rs @@ -76,7 +76,7 @@ async fn main(spawner: Spawner) -> ! { p.PG13, p.PB15, p.PG11, - GenericSMI, + GenericSMI::new(), mac_addr, 0, ); diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 62ef5e9e..768702fa 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits", "proto-ipv6"] } embedded-io = { version = "0.4.0", features = ["async"] } @@ -19,8 +19,8 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } -embedded-hal-async = { version = "=0.2.0-alpha.1" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } +embedded-hal-async = { version = "=0.2.0-alpha.2" } embedded-nal-async = "0.4.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index 12d37f7a..cfafcaed 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -58,7 +58,7 @@ async fn main(spawner: Spawner) -> ! { p.PG13, p.PB13, p.PG11, - GenericSMI, + GenericSMI::new(), mac_addr, 0, ); diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index 6078fc3f..4ed73757 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs @@ -59,7 +59,7 @@ async fn main(spawner: Spawner) -> ! { p.PG13, p.PB13, p.PG11, - GenericSMI, + GenericSMI::new(), mac_addr, 0, ); diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 2ead714e..747cec7b 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -12,7 +12,7 @@ nightly = ["embassy-stm32/nightly", "embassy-time/nightly", "embassy-time/unstab [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"], optional = true } lora-phy = { version = "1", optional = true } @@ -32,3 +32,6 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa heapless = { version = "0.7.5", default-features = false } embedded-hal = "0.2.6" static_cell = "1.1" + +[patch.crates-io] +lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index 93d48abe..dcca1cc3 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] } defmt = "0.3" diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 3bb473ef..c5555851 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -7,9 +7,9 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits", "chrono"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } defmt = "0.3" @@ -18,10 +18,11 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } -embedded-hal-async = { version = "=0.2.0-alpha.1" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } +embedded-hal-async = { version = "=0.2.0-alpha.2" } panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } +chrono = { version = "^0.4", default-features = false } micromath = "2.0.0" diff --git a/examples/stm32l4/src/bin/rtc.rs b/examples/stm32l4/src/bin/rtc.rs new file mode 100644 index 00000000..d72d5ddb --- /dev/null +++ b/examples/stm32l4/src/bin/rtc.rs @@ -0,0 +1,49 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use chrono::{NaiveDate, NaiveDateTime}; +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::rcc::{self, ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv}; +use embassy_stm32::rtc::{Rtc, RtcConfig}; +use embassy_stm32::time::Hertz; +use embassy_stm32::Config; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = { + let mut config = Config::default(); + config.rcc.mux = ClockSrc::PLL( + PLLSource::HSE(Hertz::mhz(8)), + PLLClkDiv::Div2, + PLLSrcDiv::Div1, + PLLMul::Mul20, + None, + ); + config.rcc.rtc_mux = rcc::RtcClockSource::LSE32; + embassy_stm32::init(config) + }; + info!("Hello World!"); + + let now = NaiveDate::from_ymd_opt(2020, 5, 15) + .unwrap() + .and_hms_opt(10, 30, 15) + .unwrap(); + + let mut rtc = Rtc::new( + p.RTC, + RtcConfig::default().clock_config(embassy_stm32::rtc::RtcClockSource::LSE), + ); + info!("Got RTC! {:?}", now.timestamp()); + + rtc.set_datetime(now.into()).expect("datetime not set"); + + // In reality the delay would be much longer + Timer::after(Duration::from_millis(20000)).await; + + let then: NaiveDateTime = rtc.now().unwrap().into(); + info!("Got RTC! {:?}", then.timestamp()); +} diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 6035c291..54911482 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index e2318c3d..835e3294 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32wb/.cargo/config.toml b/examples/stm32wb/.cargo/config.toml index 8b6d6d75..51c499ee 100644 --- a/examples/stm32wb/.cargo/config.toml +++ b/examples/stm32wb/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] # replace STM32WB55CCUx with your chip as listed in `probe-rs chip list` -# runner = "probe-rs run --chip STM32WB55RGVx --speed 1000 --connect-under-reset" +# runner = "probe-run --chip STM32WB55RGVx --speed 1000 --connect-under-reset" runner = "teleprobe local run --chip STM32WB55RG --elf" [build] diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index fbb2d918..7c0b83e6 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -7,9 +7,10 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti"] } embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "udp", "medium-ieee802154", "nightly"], optional=true } defmt = "0.3" defmt-rtt = "0.4" @@ -20,11 +21,11 @@ embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } - +static_cell = { version = "1.1", features = ["nightly"]} [features] -default = ["ble"] -mac = ["embassy-stm32-wpan/mac"] +default = ["ble", "mac"] +mac = ["embassy-stm32-wpan/mac", "dep:embassy-net"] ble = ["embassy-stm32-wpan/ble"] [[bin]] @@ -35,9 +36,18 @@ required-features = ["ble"] name = "tl_mbox_mac" required-features = ["mac"] +[[bin]] +name = "mac_ffd" +required-features = ["mac"] + +[[bin]] +name = "mac_ffd_net" +required-features = ["mac"] + [[bin]] name = "eddystone_beacon" required-features = ["ble"] -[patch.crates-io] -stm32wb-hci = { git = "https://github.com/OueslatiGhaith/stm32wb-hci", rev = "9f663be"} \ No newline at end of file +[[bin]] +name = "gatt_server" +required-features = ["ble"] \ No newline at end of file diff --git a/examples/stm32wb/src/bin/eddystone_beacon.rs b/examples/stm32wb/src/bin/eddystone_beacon.rs index b99f8cb2..451bd7d2 100644 --- a/examples/stm32wb/src/bin/eddystone_beacon.rs +++ b/examples/stm32wb/src/bin/eddystone_beacon.rs @@ -63,7 +63,7 @@ async fn main(_spawner: Spawner) { let sys_event = mbox.sys_subsystem.read().await; info!("sys event: {}", sys_event.payload()); - mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; + let _ = mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; info!("resetting BLE..."); mbox.ble_subsystem.reset().await; diff --git a/examples/stm32wb/src/bin/gatt_server.rs b/examples/stm32wb/src/bin/gatt_server.rs new file mode 100644 index 00000000..0f6419d4 --- /dev/null +++ b/examples/stm32wb/src/bin/gatt_server.rs @@ -0,0 +1,397 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use core::time::Duration; + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::bind_interrupts; +use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32_wpan::hci::event::command::{CommandComplete, ReturnParameters}; +use embassy_stm32_wpan::hci::host::uart::{Packet, UartHci}; +use embassy_stm32_wpan::hci::host::{AdvertisingFilterPolicy, EncryptionKey, HostHci, OwnAddressType}; +use embassy_stm32_wpan::hci::types::AdvertisingType; +use embassy_stm32_wpan::hci::vendor::stm32wb::command::gap::{ + AddressType, AuthenticationRequirements, DiscoverableParameters, GapCommands, IoCapability, LocalName, Pin, Role, + SecureConnectionSupport, +}; +use embassy_stm32_wpan::hci::vendor::stm32wb::command::gatt::{ + AddCharacteristicParameters, AddServiceParameters, CharacteristicEvent, CharacteristicPermission, + CharacteristicProperty, EncryptionKeySize, GattCommands, ServiceType, UpdateCharacteristicValueParameters, Uuid, + WriteResponseParameters, +}; +use embassy_stm32_wpan::hci::vendor::stm32wb::command::hal::{ConfigData, HalCommands, PowerLevel}; +use embassy_stm32_wpan::hci::vendor::stm32wb::event::{self, AttributeHandle, Stm32Wb5xEvent}; +use embassy_stm32_wpan::hci::{BdAddr, Event}; +use embassy_stm32_wpan::lhci::LhciC1DeviceInformationCcrp; +use embassy_stm32_wpan::sub::ble::Ble; +use embassy_stm32_wpan::TlMbox; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs{ + IPCC_C1_RX => ReceiveInterruptHandler; + IPCC_C1_TX => TransmitInterruptHandler; +}); + +const BLE_GAP_DEVICE_NAME_LENGTH: u8 = 7; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + /* + How to make this work: + + - Obtain a NUCLEO-STM32WB55 from your preferred supplier. + - Download and Install STM32CubeProgrammer. + - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from + gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x + - Open STM32CubeProgrammer + - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. + - Once complete, click connect to connect to the device. + - On the left hand pane, click the RSS signal icon to open "Firmware Upgrade Services". + - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file + - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the + stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. + - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Select "Start Wireless Stack". + - Disconnect from the device. + - In the examples folder for stm32wb, modify the memory.x file to match your target device. + - Run this example. + + Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. + */ + + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let config = Config::default(); + let mut mbox = TlMbox::init(p.IPCC, Irqs, config); + + let sys_event = mbox.sys_subsystem.read().await; + info!("sys event: {}", sys_event.payload()); + + let _ = mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; + + info!("resetting BLE..."); + mbox.ble_subsystem.reset().await; + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + info!("config public address..."); + mbox.ble_subsystem + .write_config_data(&ConfigData::public_address(get_bd_addr()).build()) + .await; + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + info!("config random address..."); + mbox.ble_subsystem + .write_config_data(&ConfigData::random_address(get_random_addr()).build()) + .await; + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + info!("config identity root..."); + mbox.ble_subsystem + .write_config_data(&ConfigData::identity_root(&get_irk()).build()) + .await; + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + info!("config encryption root..."); + mbox.ble_subsystem + .write_config_data(&ConfigData::encryption_root(&get_erk()).build()) + .await; + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + info!("config tx power level..."); + mbox.ble_subsystem.set_tx_power_level(PowerLevel::ZerodBm).await; + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + info!("GATT init..."); + mbox.ble_subsystem.init_gatt().await; + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + info!("GAP init..."); + mbox.ble_subsystem + .init_gap(Role::PERIPHERAL, false, BLE_GAP_DEVICE_NAME_LENGTH) + .await; + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + info!("set IO capabilities..."); + mbox.ble_subsystem.set_io_capability(IoCapability::DisplayConfirm).await; + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + info!("set authentication requirements..."); + mbox.ble_subsystem + .set_authentication_requirement(&AuthenticationRequirements { + bonding_required: false, + keypress_notification_support: false, + mitm_protection_required: false, + encryption_key_size_range: (8, 16), + fixed_pin: Pin::Requested, + identity_address_type: AddressType::Public, + secure_connection_support: SecureConnectionSupport::Optional, + }) + .await + .unwrap(); + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + info!("set scan response data..."); + mbox.ble_subsystem.le_set_scan_response_data(b"TXTX").await.unwrap(); + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + info!("set scan response data..."); + mbox.ble_subsystem.le_set_scan_response_data(b"TXTX").await.unwrap(); + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + defmt::info!("initializing services and characteristics..."); + let mut ble_context = init_gatt_services(&mut mbox.ble_subsystem).await.unwrap(); + defmt::info!("{}", ble_context); + + let discovery_params = DiscoverableParameters { + advertising_type: AdvertisingType::ConnectableUndirected, + advertising_interval: Some((Duration::from_millis(100), Duration::from_millis(100))), + address_type: OwnAddressType::Public, + filter_policy: AdvertisingFilterPolicy::AllowConnectionAndScan, + local_name: Some(LocalName::Complete(b"TXTX")), + advertising_data: &[], + conn_interval: (None, None), + }; + + info!("set discoverable..."); + mbox.ble_subsystem.set_discoverable(&discovery_params).await.unwrap(); + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + loop { + let response = mbox.ble_subsystem.read().await; + defmt::debug!("{}", response); + + if let Ok(Packet::Event(event)) = response { + match event { + Event::LeConnectionComplete(_) => { + defmt::info!("connected"); + } + Event::DisconnectionComplete(_) => { + defmt::info!("disconnected"); + ble_context.is_subscribed = false; + mbox.ble_subsystem.set_discoverable(&discovery_params).await.unwrap(); + } + Event::Vendor(vendor_event) => match vendor_event { + Stm32Wb5xEvent::AttReadPermitRequest(read_req) => { + defmt::info!("read request received {}, allowing", read_req); + mbox.ble_subsystem.allow_read(read_req.conn_handle).await + } + Stm32Wb5xEvent::AttWritePermitRequest(write_req) => { + defmt::info!("write request received {}, allowing", write_req); + mbox.ble_subsystem + .write_response(&WriteResponseParameters { + conn_handle: write_req.conn_handle, + attribute_handle: write_req.attribute_handle, + status: Ok(()), + value: write_req.value(), + }) + .await + .unwrap() + } + Stm32Wb5xEvent::GattAttributeModified(attribute) => { + defmt::info!("{}", ble_context); + if attribute.attr_handle.0 == ble_context.chars.notify.0 + 2 { + if attribute.data()[0] == 0x01 { + defmt::info!("subscribed"); + ble_context.is_subscribed = true; + } else { + defmt::info!("unsubscribed"); + ble_context.is_subscribed = false; + } + } + } + _ => {} + }, + _ => {} + } + } + } +} + +fn get_bd_addr() -> BdAddr { + let mut bytes = [0u8; 6]; + + let lhci_info = LhciC1DeviceInformationCcrp::new(); + bytes[0] = (lhci_info.uid64 & 0xff) as u8; + bytes[1] = ((lhci_info.uid64 >> 8) & 0xff) as u8; + bytes[2] = ((lhci_info.uid64 >> 16) & 0xff) as u8; + bytes[3] = lhci_info.device_type_id; + bytes[4] = (lhci_info.st_company_id & 0xff) as u8; + bytes[5] = (lhci_info.st_company_id >> 8 & 0xff) as u8; + + BdAddr(bytes) +} + +fn get_random_addr() -> BdAddr { + let mut bytes = [0u8; 6]; + + let lhci_info = LhciC1DeviceInformationCcrp::new(); + bytes[0] = (lhci_info.uid64 & 0xff) as u8; + bytes[1] = ((lhci_info.uid64 >> 8) & 0xff) as u8; + bytes[2] = ((lhci_info.uid64 >> 16) & 0xff) as u8; + bytes[3] = 0; + bytes[4] = 0x6E; + bytes[5] = 0xED; + + BdAddr(bytes) +} + +const BLE_CFG_IRK: [u8; 16] = [ + 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, +]; +const BLE_CFG_ERK: [u8; 16] = [ + 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21, +]; + +fn get_irk() -> EncryptionKey { + EncryptionKey(BLE_CFG_IRK) +} + +fn get_erk() -> EncryptionKey { + EncryptionKey(BLE_CFG_ERK) +} + +#[derive(defmt::Format)] +pub struct BleContext { + pub service_handle: AttributeHandle, + pub chars: CharHandles, + pub is_subscribed: bool, +} + +#[derive(defmt::Format)] +pub struct CharHandles { + pub read: AttributeHandle, + pub write: AttributeHandle, + pub notify: AttributeHandle, +} + +pub async fn init_gatt_services(ble_subsystem: &mut Ble) -> Result { + let service_handle = gatt_add_service(ble_subsystem, Uuid::Uuid16(0x500)).await?; + + let read = gatt_add_char( + ble_subsystem, + service_handle, + Uuid::Uuid16(0x501), + CharacteristicProperty::READ, + Some(b"Hello from embassy!"), + ) + .await?; + + let write = gatt_add_char( + ble_subsystem, + service_handle, + Uuid::Uuid16(0x502), + CharacteristicProperty::WRITE_WITHOUT_RESPONSE | CharacteristicProperty::WRITE | CharacteristicProperty::READ, + None, + ) + .await?; + + let notify = gatt_add_char( + ble_subsystem, + service_handle, + Uuid::Uuid16(0x503), + CharacteristicProperty::NOTIFY | CharacteristicProperty::READ, + None, + ) + .await?; + + Ok(BleContext { + service_handle, + is_subscribed: false, + chars: CharHandles { read, write, notify }, + }) +} + +async fn gatt_add_service(ble_subsystem: &mut Ble, uuid: Uuid) -> Result { + ble_subsystem + .add_service(&AddServiceParameters { + uuid, + service_type: ServiceType::Primary, + max_attribute_records: 8, + }) + .await; + let response = ble_subsystem.read().await; + defmt::debug!("{}", response); + + if let Ok(Packet::Event(Event::CommandComplete(CommandComplete { + return_params: + ReturnParameters::Vendor(event::command::ReturnParameters::GattAddService(event::command::GattService { + service_handle, + .. + })), + .. + }))) = response + { + Ok(service_handle) + } else { + Err(()) + } +} + +async fn gatt_add_char( + ble_subsystem: &mut Ble, + service_handle: AttributeHandle, + characteristic_uuid: Uuid, + characteristic_properties: CharacteristicProperty, + default_value: Option<&[u8]>, +) -> Result { + ble_subsystem + .add_characteristic(&AddCharacteristicParameters { + service_handle, + characteristic_uuid, + characteristic_properties, + characteristic_value_len: 32, + security_permissions: CharacteristicPermission::empty(), + gatt_event_mask: CharacteristicEvent::all(), + encryption_key_size: EncryptionKeySize::with_value(7).unwrap(), + is_variable: true, + }) + .await; + let response = ble_subsystem.read().await; + defmt::debug!("{}", response); + + if let Ok(Packet::Event(Event::CommandComplete(CommandComplete { + return_params: + ReturnParameters::Vendor(event::command::ReturnParameters::GattAddCharacteristic( + event::command::GattCharacteristic { + characteristic_handle, .. + }, + )), + .. + }))) = response + { + if let Some(value) = default_value { + ble_subsystem + .update_characteristic_value(&UpdateCharacteristicValueParameters { + service_handle, + characteristic_handle, + offset: 0, + value, + }) + .await + .unwrap(); + + let response = ble_subsystem.read().await; + defmt::debug!("{}", response); + } + Ok(characteristic_handle) + } else { + Err(()) + } +} diff --git a/examples/stm32wb/src/bin/mac_ffd.rs b/examples/stm32wb/src/bin/mac_ffd.rs new file mode 100644 index 00000000..1379ac6b --- /dev/null +++ b/examples/stm32wb/src/bin/mac_ffd.rs @@ -0,0 +1,185 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::bind_interrupts; +use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32_wpan::mac::commands::{AssociateResponse, ResetRequest, SetRequest, StartRequest}; +use embassy_stm32_wpan::mac::event::MacEvent; +use embassy_stm32_wpan::mac::typedefs::{MacChannel, MacStatus, PanId, PibId, SecurityLevel}; +use embassy_stm32_wpan::sub::mm; +use embassy_stm32_wpan::TlMbox; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs{ + IPCC_C1_RX => ReceiveInterruptHandler; + IPCC_C1_TX => TransmitInterruptHandler; +}); + +#[embassy_executor::task] +async fn run_mm_queue(memory_manager: mm::MemoryManager) { + memory_manager.run_queue().await; +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + /* + How to make this work: + + - Obtain a NUCLEO-STM32WB55 from your preferred supplier. + - Download and Install STM32CubeProgrammer. + - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from + gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x + - Open STM32CubeProgrammer + - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. + - Once complete, click connect to connect to the device. + - On the left hand pane, click the RSS signal icon to open "Firmware Upgrade Services". + - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file + - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the + stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. + - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Select "Start Wireless Stack". + - Disconnect from the device. + - In the examples folder for stm32wb, modify the memory.x file to match your target device. + - Run this example. + + Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. + */ + + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let config = Config::default(); + let mbox = TlMbox::init(p.IPCC, Irqs, config); + + spawner.spawn(run_mm_queue(mbox.mm_subsystem)).unwrap(); + + let sys_event = mbox.sys_subsystem.read().await; + info!("sys event: {}", sys_event.payload()); + + core::mem::drop(sys_event); + + let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; + info!("initialized mac: {}", result); + + info!("resetting"); + mbox.mac_subsystem + .send_command(&ResetRequest { + set_default_pib: true, + ..Default::default() + }) + .await + .unwrap(); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + + info!("setting extended address"); + let extended_address: u64 = 0xACDE480000000001; + mbox.mac_subsystem + .send_command(&SetRequest { + pib_attribute_ptr: &extended_address as *const _ as *const u8, + pib_attribute: PibId::ExtendedAddress, + }) + .await + .unwrap(); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + + info!("setting short address"); + let short_address: u16 = 0x1122; + mbox.mac_subsystem + .send_command(&SetRequest { + pib_attribute_ptr: &short_address as *const _ as *const u8, + pib_attribute: PibId::ShortAddress, + }) + .await + .unwrap(); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + + info!("setting association permit"); + let association_permit: bool = true; + mbox.mac_subsystem + .send_command(&SetRequest { + pib_attribute_ptr: &association_permit as *const _ as *const u8, + pib_attribute: PibId::AssociationPermit, + }) + .await + .unwrap(); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + + info!("setting TX power"); + let transmit_power: i8 = 2; + mbox.mac_subsystem + .send_command(&SetRequest { + pib_attribute_ptr: &transmit_power as *const _ as *const u8, + pib_attribute: PibId::TransmitPower, + }) + .await + .unwrap(); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + + info!("starting FFD device"); + mbox.mac_subsystem + .send_command(&StartRequest { + pan_id: PanId([0x1A, 0xAA]), + channel_number: MacChannel::Channel16, + beacon_order: 0x0F, + superframe_order: 0x0F, + pan_coordinator: true, + battery_life_extension: false, + ..Default::default() + }) + .await + .unwrap(); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + + info!("setting RX on when idle"); + let rx_on_while_idle: bool = true; + mbox.mac_subsystem + .send_command(&SetRequest { + pib_attribute_ptr: &rx_on_while_idle as *const _ as *const u8, + pib_attribute: PibId::RxOnWhenIdle, + }) + .await + .unwrap(); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + + loop { + let evt = mbox.mac_subsystem.read().await; + if let Ok(evt) = evt { + defmt::info!("parsed mac event"); + defmt::info!("{:#x}", evt); + + match evt { + MacEvent::MlmeAssociateInd(association) => mbox + .mac_subsystem + .send_command(&AssociateResponse { + device_address: association.device_address, + assoc_short_address: [0x33, 0x44], + status: MacStatus::Success, + security_level: SecurityLevel::Unsecure, + ..Default::default() + }) + .await + .unwrap(), + MacEvent::McpsDataInd(data_ind) => { + let payload = data_ind.payload(); + let ref_payload = b"Hello from embassy!"; + info!("{}", payload); + + if payload == ref_payload { + info!("success"); + } else { + info!("ref payload: {}", ref_payload); + } + } + _ => { + defmt::info!("other mac event"); + } + } + } else { + defmt::info!("failed to parse mac event"); + } + } +} diff --git a/examples/stm32wb/src/bin/mac_ffd_net.rs b/examples/stm32wb/src/bin/mac_ffd_net.rs new file mode 100644 index 00000000..bbcd0a70 --- /dev/null +++ b/examples/stm32wb/src/bin/mac_ffd_net.rs @@ -0,0 +1,170 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::bind_interrupts; +use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32_wpan::mac::commands::{ResetRequest, SetRequest, StartRequest}; +use embassy_stm32_wpan::mac::typedefs::{MacChannel, PanId, PibId}; +use embassy_stm32_wpan::mac::{self, Runner}; +use embassy_stm32_wpan::sub::mm; +use embassy_stm32_wpan::TlMbox; +use static_cell::make_static; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs{ + IPCC_C1_RX => ReceiveInterruptHandler; + IPCC_C1_TX => TransmitInterruptHandler; +}); + +#[embassy_executor::task] +async fn run_mm_queue(memory_manager: mm::MemoryManager) { + memory_manager.run_queue().await; +} + +#[embassy_executor::task] +async fn run_mac(runner: &'static Runner<'static>) { + runner.run().await; +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + /* + How to make this work: + + - Obtain a NUCLEO-STM32WB55 from your preferred supplier. + - Download and Install STM32CubeProgrammer. + - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from + gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x + - Open STM32CubeProgrammer + - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. + - Once complete, click connect to connect to the device. + - On the left hand pane, click the RSS signal icon to open "Firmware Upgrade Services". + - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file + - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the + stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. + - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Select "Start Wireless Stack". + - Disconnect from the device. + - In the examples folder for stm32wb, modify the memory.x file to match your target device. + - Run this example. + + Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. + */ + + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let config = Config::default(); + let mbox = TlMbox::init(p.IPCC, Irqs, config); + + spawner.spawn(run_mm_queue(mbox.mm_subsystem)).unwrap(); + + let sys_event = mbox.sys_subsystem.read().await; + info!("sys event: {}", sys_event.payload()); + + core::mem::drop(sys_event); + + let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; + info!("initialized mac: {}", result); + + info!("resetting"); + mbox.mac_subsystem + .send_command(&ResetRequest { + set_default_pib: true, + ..Default::default() + }) + .await + .unwrap(); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + + info!("setting extended address"); + let extended_address: u64 = 0xACDE480000000001; + mbox.mac_subsystem + .send_command(&SetRequest { + pib_attribute_ptr: &extended_address as *const _ as *const u8, + pib_attribute: PibId::ExtendedAddress, + }) + .await + .unwrap(); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + + info!("setting short address"); + let short_address: u16 = 0x1122; + mbox.mac_subsystem + .send_command(&SetRequest { + pib_attribute_ptr: &short_address as *const _ as *const u8, + pib_attribute: PibId::ShortAddress, + }) + .await + .unwrap(); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + + info!("setting association permit"); + let association_permit: bool = true; + mbox.mac_subsystem + .send_command(&SetRequest { + pib_attribute_ptr: &association_permit as *const _ as *const u8, + pib_attribute: PibId::AssociationPermit, + }) + .await + .unwrap(); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + + info!("setting TX power"); + let transmit_power: i8 = 2; + mbox.mac_subsystem + .send_command(&SetRequest { + pib_attribute_ptr: &transmit_power as *const _ as *const u8, + pib_attribute: PibId::TransmitPower, + }) + .await + .unwrap(); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + + info!("starting FFD device"); + mbox.mac_subsystem + .send_command(&StartRequest { + pan_id: PanId([0x1A, 0xAA]), + channel_number: MacChannel::Channel16, + beacon_order: 0x0F, + superframe_order: 0x0F, + pan_coordinator: true, + battery_life_extension: false, + ..Default::default() + }) + .await + .unwrap(); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + + info!("setting RX on when idle"); + let rx_on_while_idle: bool = true; + mbox.mac_subsystem + .send_command(&SetRequest { + pib_attribute_ptr: &rx_on_while_idle as *const _ as *const u8, + pib_attribute: PibId::RxOnWhenIdle, + }) + .await + .unwrap(); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + + let tx_queue = [ + make_static!([0u8; 127]), + make_static!([0u8; 127]), + make_static!([0u8; 127]), + make_static!([0u8; 127]), + make_static!([0u8; 127]), + ]; + + let runner = make_static!(Runner::new(mbox.mac_subsystem, tx_queue)); + + spawner.spawn(run_mac(runner)).unwrap(); + + let (driver, control) = mac::new(runner).await; + + let _ = driver; + let _ = control; +} diff --git a/examples/stm32wb/src/bin/mac_rfd.rs b/examples/stm32wb/src/bin/mac_rfd.rs new file mode 100644 index 00000000..4d8b6601 --- /dev/null +++ b/examples/stm32wb/src/bin/mac_rfd.rs @@ -0,0 +1,182 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::bind_interrupts; +use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32_wpan::mac::commands::{AssociateRequest, DataRequest, GetRequest, ResetRequest, SetRequest}; +use embassy_stm32_wpan::mac::event::MacEvent; +use embassy_stm32_wpan::mac::typedefs::{ + AddressMode, Capabilities, KeyIdMode, MacAddress, MacChannel, PanId, PibId, SecurityLevel, +}; +use embassy_stm32_wpan::sub::mm; +use embassy_stm32_wpan::TlMbox; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs{ + IPCC_C1_RX => ReceiveInterruptHandler; + IPCC_C1_TX => TransmitInterruptHandler; +}); + +#[embassy_executor::task] +async fn run_mm_queue(memory_manager: mm::MemoryManager) { + memory_manager.run_queue().await; +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + /* + How to make this work: + + - Obtain a NUCLEO-STM32WB55 from your preferred supplier. + - Download and Install STM32CubeProgrammer. + - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from + gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x + - Open STM32CubeProgrammer + - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. + - Once complete, click connect to connect to the device. + - On the left hand pane, click the RSS signal icon to open "Firmware Upgrade Services". + - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file + - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the + stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. + - Select that file, the memory address, "verify download", and then "Firmware Upgrade". + - Select "Start Wireless Stack". + - Disconnect from the device. + - In the examples folder for stm32wb, modify the memory.x file to match your target device. + - Run this example. + + Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. + */ + + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let config = Config::default(); + let mbox = TlMbox::init(p.IPCC, Irqs, config); + + spawner.spawn(run_mm_queue(mbox.mm_subsystem)).unwrap(); + + let sys_event = mbox.sys_subsystem.read().await; + info!("sys event: {}", sys_event.payload()); + + core::mem::drop(sys_event); + + let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; + info!("initialized mac: {}", result); + + info!("resetting"); + mbox.mac_subsystem + .send_command(&ResetRequest { + set_default_pib: true, + ..Default::default() + }) + .await + .unwrap(); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + + info!("setting extended address"); + let extended_address: u64 = 0xACDE480000000002; + mbox.mac_subsystem + .send_command(&SetRequest { + pib_attribute_ptr: &extended_address as *const _ as *const u8, + pib_attribute: PibId::ExtendedAddress, + }) + .await + .unwrap(); + defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + + info!("getting extended address"); + mbox.mac_subsystem + .send_command(&GetRequest { + pib_attribute: PibId::ExtendedAddress, + ..Default::default() + }) + .await + .unwrap(); + + { + let evt = mbox.mac_subsystem.read().await.unwrap(); + info!("{:#x}", evt); + + if let MacEvent::MlmeGetCnf(evt) = evt { + if evt.pib_attribute_value_len == 8 { + let value = unsafe { core::ptr::read_unaligned(evt.pib_attribute_value_ptr as *const u64) }; + + info!("value {:#x}", value) + } + } + } + + info!("assocation request"); + let a = AssociateRequest { + channel_number: MacChannel::Channel16, + channel_page: 0, + coord_addr_mode: AddressMode::Short, + coord_address: MacAddress { short: [34, 17] }, + capability_information: Capabilities::ALLOCATE_ADDRESS, + coord_pan_id: PanId([0x1A, 0xAA]), + security_level: SecurityLevel::Unsecure, + key_id_mode: KeyIdMode::Implicite, + key_source: [0; 8], + key_index: 152, + }; + info!("{}", a); + mbox.mac_subsystem.send_command(&a).await.unwrap(); + let short_addr = { + let evt = mbox.mac_subsystem.read().await.unwrap(); + info!("{:#x}", evt); + + if let MacEvent::MlmeAssociateCnf(conf) = evt { + conf.assoc_short_address + } else { + defmt::panic!() + } + }; + + info!("setting short address"); + mbox.mac_subsystem + .send_command(&SetRequest { + pib_attribute_ptr: &short_addr as *const _ as *const u8, + pib_attribute: PibId::ShortAddress, + }) + .await + .unwrap(); + { + let evt = mbox.mac_subsystem.read().await.unwrap(); + info!("{:#x}", evt); + } + + info!("sending data"); + let data = b"Hello from embassy!"; + mbox.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: 0x02, + ack_tx: 0x00, + gts_tx: false, + security_level: SecurityLevel::Unsecure, + ..Default::default() + } + .set_buffer(data), + ) + .await + .unwrap(); + { + let evt = mbox.mac_subsystem.read().await.unwrap(); + info!("{:#x}", evt); + } + + loop { + match mbox.mac_subsystem.read().await { + Ok(evt) => info!("{:#x}", evt), + _ => continue, + }; + } +} diff --git a/examples/stm32wb/src/bin/tl_mbox_ble.rs b/examples/stm32wb/src/bin/tl_mbox_ble.rs index a511e89a..90349422 100644 --- a/examples/stm32wb/src/bin/tl_mbox_ble.rs +++ b/examples/stm32wb/src/bin/tl_mbox_ble.rs @@ -49,7 +49,7 @@ async fn main(_spawner: Spawner) { let sys_event = mbox.sys_subsystem.read().await; info!("sys event: {}", sys_event.payload()); - mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; + let _ = mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; info!("starting ble..."); mbox.ble_subsystem.tl_write(0x0c, &[]).await; diff --git a/examples/stm32wb/src/bin/tl_mbox_mac.rs b/examples/stm32wb/src/bin/tl_mbox_mac.rs index f67be468..5931c392 100644 --- a/examples/stm32wb/src/bin/tl_mbox_mac.rs +++ b/examples/stm32wb/src/bin/tl_mbox_mac.rs @@ -6,6 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32_wpan::sub::mm; use embassy_stm32_wpan::TlMbox; use {defmt_rtt as _, panic_probe as _}; @@ -14,8 +15,13 @@ bind_interrupts!(struct Irqs{ IPCC_C1_TX => TransmitInterruptHandler; }); +#[embassy_executor::task] +async fn run_mm_queue(memory_manager: mm::MemoryManager) { + memory_manager.run_queue().await; +} + #[embassy_executor::main] -async fn main(_spawner: Spawner) { +async fn main(spawner: Spawner) { /* How to make this work: @@ -46,9 +52,13 @@ async fn main(_spawner: Spawner) { let config = Config::default(); let mbox = TlMbox::init(p.IPCC, Irqs, config); + spawner.spawn(run_mm_queue(mbox.mm_subsystem)).unwrap(); + let sys_event = mbox.sys_subsystem.read().await; info!("sys event: {}", sys_event.payload()); + core::mem::drop(sys_event); + let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; info!("initialized mac: {}", result); diff --git a/examples/stm32wl/.cargo/config.toml b/examples/stm32wl/.cargo/config.toml index 4f8094ff..ee416fcb 100644 --- a/examples/stm32wl/.cargo/config.toml +++ b/examples/stm32wl/.cargo/config.toml @@ -3,7 +3,7 @@ runner = "probe-rs run --chip STM32WLE5JCIx" [build] -target = "thumbv7em-none-eabihf" +target = "thumbv7em-none-eabi" [env] DEFMT_LOG = "trace" diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 260f9afa..e2c66f45 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -7,9 +7,9 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti"] } -embassy-embedded-hal = {version = "0.1.0", path = "../../embassy-embedded-hal" } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti", "chrono"] } +embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["stm32wl", "time", "defmt"] } lora-phy = { version = "1" } lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"] } @@ -25,3 +25,7 @@ embedded-storage = "0.3.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } +chrono = { version = "^0.4", default-features = false } + +[patch.crates-io] +lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } diff --git a/examples/stm32wl/src/bin/rtc.rs b/examples/stm32wl/src/bin/rtc.rs new file mode 100644 index 00000000..e1182549 --- /dev/null +++ b/examples/stm32wl/src/bin/rtc.rs @@ -0,0 +1,43 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use chrono::{NaiveDate, NaiveDateTime}; +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::rcc::{self, ClockSrc}; +use embassy_stm32::rtc::{Rtc, RtcConfig}; +use embassy_stm32::Config; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = { + let mut config = Config::default(); + config.rcc.mux = ClockSrc::HSE32; + config.rcc.rtc_mux = rcc::RtcClockSource::LSE32; + config.rcc.enable_rtc_apb = true; + embassy_stm32::init(config) + }; + info!("Hello World!"); + + let now = NaiveDate::from_ymd_opt(2020, 5, 15) + .unwrap() + .and_hms_opt(10, 30, 15) + .unwrap(); + + let mut rtc = Rtc::new( + p.RTC, + RtcConfig::default().clock_config(embassy_stm32::rtc::RtcClockSource::LSE), + ); + info!("Got RTC! {:?}", now.timestamp()); + + rtc.set_datetime(now.into()).expect("datetime not set"); + + // In reality the delay would be much longer + Timer::after(Duration::from_millis(20000)).await; + + let then: NaiveDateTime = rtc.now().unwrap().into(); + info!("Got RTC! {:?}", then.timestamp()); +} diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml index 437e443a..3679e385 100644 --- a/examples/wasm/Cargo.toml +++ b/examples/wasm/Cargo.toml @@ -10,7 +10,7 @@ crate-type = ["cdylib"] [dependencies] embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["log"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "wasm", "nightly"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["log", "wasm", "nightly"] } wasm-logger = "0.2.0" wasm-bindgen = "0.2" diff --git a/rust-toolchain.toml b/rust-toolchain.toml index fd454db2..179ed1d6 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,7 +1,7 @@ # Before upgrading check that everything is available on all tier1 targets here: # https://rust-lang.github.io/rustup-components-history [toolchain] -channel = "nightly-2023-05-18" +channel = "nightly-2023-06-28" components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] targets = [ "thumbv7em-none-eabi", @@ -11,4 +11,4 @@ targets = [ "thumbv8m.main-none-eabihf", "riscv32imac-unknown-none-elf", "wasm32-unknown-unknown", -] +] \ No newline at end of file diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index 4f9ecc47..7ce51aa5 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -10,12 +10,12 @@ teleprobe-meta = "1" embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt", "nightly"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "nightly", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "nightly", "defmt-timestamp-uptime"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "nightly", "unstable-traits", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nightly", "unstable-traits", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } embedded-io = { version = "0.4.0", features = ["async"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } -embedded-hal-async = { version = "0.2.0-alpha.1" } +embedded-hal-async = { version = "0.2.0-alpha.2" } static_cell = { version = "1.1", features = [ "nightly" ] } defmt = "0.3" diff --git a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs index 277b985c..398ab9d2 100644 --- a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs +++ b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs @@ -14,7 +14,7 @@ use embassy_nrf::gpio::{AnyPin, Input, Level, Output, OutputDrive, Pin, Pull}; use embassy_nrf::rng::Rng; use embassy_nrf::spim::{self, Spim}; use embassy_nrf::{bind_interrupts, peripherals}; -use embassy_time::{with_timeout, Duration, Timer}; +use embassy_time::{with_timeout, Delay, Duration, Timer}; use embedded_hal_async::spi::ExclusiveDevice; use static_cell::make_static; use {defmt_rtt as _, embassy_net_esp_hosted as hosted, panic_probe as _}; @@ -30,7 +30,7 @@ bind_interrupts!(struct Irqs { async fn wifi_task( runner: hosted::Runner< 'static, - ExclusiveDevice, Output<'static, peripherals::P0_31>>, + ExclusiveDevice, Output<'static, peripherals::P0_31>, Delay>, Input<'static, AnyPin>, Output<'static, peripherals::P1_05>, >, @@ -63,7 +63,7 @@ async fn main(spawner: Spawner) { config.frequency = spim::Frequency::M32; config.mode = spim::MODE_2; // !!! let spi = spim::Spim::new(p.SPI3, Irqs, sck, miso, mosi, config); - let spi = ExclusiveDevice::new(spi, cs); + let spi = ExclusiveDevice::new(spi, cs, Delay); let (device, mut control, runner) = embassy_net_esp_hosted::new( make_static!(embassy_net_esp_hosted::State::new()), diff --git a/tests/riscv32/Cargo.toml b/tests/riscv32/Cargo.toml index 81bfdfab..61f886c0 100644 --- a/tests/riscv32/Cargo.toml +++ b/tests/riscv32/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" critical-section = { version = "1.1.1", features = ["restore-state-bool"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync" } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["arch-riscv32", "nightly", "executor-thread"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time" } +embassy-time = { version = "0.1.2", path = "../../embassy-time" } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } riscv-rt = "0.11" diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 180d0ebb..f2c90278 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -9,7 +9,7 @@ teleprobe-meta = "1.1" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"] } @@ -22,13 +22,15 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6" } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } -embedded-hal-async = { version = "=0.2.0-alpha.1" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } +embedded-hal-async = { version = "=0.2.0-alpha.2" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } embedded-io = { version = "0.4.0", features = ["async"] } embedded-storage = { version = "0.3" } static_cell = { version = "1.1", features = ["nightly"]} +pio = "0.2" +pio-proc = "0.2" [profile.dev] debug = 2 diff --git a/tests/rp/src/bin/adc.rs b/tests/rp/src/bin/adc.rs new file mode 100644 index 00000000..e659844a --- /dev/null +++ b/tests/rp/src/bin/adc.rs @@ -0,0 +1,86 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::adc::{Adc, Config, InterruptHandler, Pin}; +use embassy_rp::bind_interrupts; +use embassy_rp::gpio::Pull; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + ADC_IRQ_FIFO => InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut p = embassy_rp::init(Default::default()); + let mut adc = Adc::new(p.ADC, Irqs, Config::default()); + + { + { + let mut p = Pin::new(&mut p.PIN_26, Pull::Down); + defmt::assert!(adc.blocking_read(&mut p).unwrap() < 0b01_0000_0000); + defmt::assert!(adc.read(&mut p).await.unwrap() < 0b01_0000_0000); + } + { + let mut p = Pin::new(&mut p.PIN_26, Pull::Up); + defmt::assert!(adc.blocking_read(&mut p).unwrap() > 0b11_0000_0000); + defmt::assert!(adc.read(&mut p).await.unwrap() > 0b11_0000_0000); + } + } + // not bothering with async reads from now on + { + { + let mut p = Pin::new(&mut p.PIN_27, Pull::Down); + defmt::assert!(adc.blocking_read(&mut p).unwrap() < 0b01_0000_0000); + } + { + let mut p = Pin::new(&mut p.PIN_27, Pull::Up); + defmt::assert!(adc.blocking_read(&mut p).unwrap() > 0b11_0000_0000); + } + } + { + { + let mut p = Pin::new(&mut p.PIN_28, Pull::Down); + defmt::assert!(adc.blocking_read(&mut p).unwrap() < 0b01_0000_0000); + } + { + let mut p = Pin::new(&mut p.PIN_28, Pull::Up); + defmt::assert!(adc.blocking_read(&mut p).unwrap() > 0b11_0000_0000); + } + } + { + // gp29 is connected to vsys through a 200k/100k divider, + // adding pulls should change the value + let low = { + let mut p = Pin::new(&mut p.PIN_29, Pull::Down); + adc.blocking_read(&mut p).unwrap() + }; + let none = { + let mut p = Pin::new(&mut p.PIN_29, Pull::None); + adc.blocking_read(&mut p).unwrap() + }; + let up = { + let mut p = Pin::new(&mut p.PIN_29, Pull::Up); + adc.blocking_read(&mut p).unwrap() + }; + defmt::assert!(low < none); + defmt::assert!(none < up); + } + + let temp = convert_to_celsius(adc.read_temperature().await.unwrap()); + defmt::assert!(temp > 0.0); + defmt::assert!(temp < 60.0); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} + +fn convert_to_celsius(raw_temp: u16) -> f32 { + // According to chapter 4.9.5. Temperature Sensor in RP2040 datasheet + 27.0 - (raw_temp as f32 * 3.3 / 4096.0 - 0.706) / 0.001721 as f32 +} diff --git a/tests/rp/src/bin/cyw43-perf.rs b/tests/rp/src/bin/cyw43-perf.rs index 1ecaab26..bc127e2e 100644 --- a/tests/rp/src/bin/cyw43-perf.rs +++ b/tests/rp/src/bin/cyw43-perf.rs @@ -12,12 +12,16 @@ use embassy_net::tcp::TcpSocket; use embassy_net::{Config, Ipv4Address, Stack, StackResources}; use embassy_rp::gpio::{Level, Output}; use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; -use embassy_rp::pio::Pio; -use embassy_rp::rom_data; +use embassy_rp::pio::{InterruptHandler, Pio}; +use embassy_rp::{bind_interrupts, rom_data}; use embassy_time::{with_timeout, Duration, Timer}; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + teleprobe_meta::timeout!(120); #[embassy_executor::task] @@ -51,7 +55,7 @@ async fn main(spawner: Spawner) { let pwr = Output::new(p.PIN_23, Level::Low); let cs = Output::new(p.PIN_25, Level::High); - let mut pio = Pio::new(p.PIO0); + let mut pio = Pio::new(p.PIO0, Irqs); let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); let state = make_static!(cyw43::State::new()); diff --git a/tests/rp/src/bin/gpio.rs b/tests/rp/src/bin/gpio.rs index 51112d31..946b7dc8 100644 --- a/tests/rp/src/bin/gpio.rs +++ b/tests/rp/src/bin/gpio.rs @@ -21,14 +21,46 @@ async fn main(_spawner: Spawner) { let b = Input::new(&mut b, Pull::None); { - let _a = Output::new(&mut a, Level::Low); + let a = Output::new(&mut a, Level::Low); delay(); assert!(b.is_low()); + assert!(!b.is_high()); + assert!(a.is_set_low()); + assert!(!a.is_set_high()); } { - let _a = Output::new(&mut a, Level::High); + let mut a = Output::new(&mut a, Level::High); + delay(); + assert!(!b.is_low()); + assert!(b.is_high()); + assert!(!a.is_set_low()); + assert!(a.is_set_high()); + + // Test is_set_low / is_set_high + a.set_low(); + delay(); + assert!(b.is_low()); + assert!(a.is_set_low()); + assert!(!a.is_set_high()); + + a.set_high(); delay(); assert!(b.is_high()); + assert!(!a.is_set_low()); + assert!(a.is_set_high()); + + // Test toggle + a.toggle(); + delay(); + assert!(b.is_low()); + assert!(a.is_set_low()); + assert!(!a.is_set_high()); + + a.toggle(); + delay(); + assert!(b.is_high()); + assert!(!a.is_set_low()); + assert!(a.is_set_high()); } } diff --git a/tests/rp/src/bin/pio_irq.rs b/tests/rp/src/bin/pio_irq.rs new file mode 100644 index 00000000..45004424 --- /dev/null +++ b/tests/rp/src/bin/pio_irq.rs @@ -0,0 +1,55 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; + +use defmt::info; +use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; +use embassy_rp::peripherals::PIO0; +use embassy_rp::pio::{Config, InterruptHandler, Pio}; +use embassy_rp::relocate::RelocatedProgram; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let pio = p.PIO0; + let Pio { + mut common, + sm0: mut sm, + irq_flags, + .. + } = Pio::new(pio, Irqs); + + let prg = pio_proc::pio_asm!( + "irq set 0", + "irq wait 0", + "irq set 1", + // pause execution here + "irq wait 1", + ); + + let relocated = RelocatedProgram::new(&prg.program); + let mut cfg = Config::default(); + cfg.use_program(&common.load_program(&relocated), &[]); + sm.set_config(&cfg); + sm.set_enable(true); + + // not using the wait futures on purpose because they clear the irq bits, + // and we want to see in which order they are set. + while !irq_flags.check(0) {} + cortex_m::asm::nop(); + assert!(!irq_flags.check(1)); + irq_flags.clear(0); + cortex_m::asm::nop(); + assert!(irq_flags.check(1)); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index c2422f7b..3007cd1e 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -12,14 +12,16 @@ stm32g071rb = ["embassy-stm32/stm32g071rb", "not-gpdma"] # Nucleo stm32c031c6 = ["embassy-stm32/stm32c031c6", "not-gpdma"] # Nucleo stm32g491re = ["embassy-stm32/stm32g491re", "not-gpdma"] # Nucleo stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "not-gpdma"] # Nucleo -stm32wb55rg = ["embassy-stm32/stm32wb55rg", "not-gpdma", "ble" ] # Nucleo +stm32wb55rg = ["embassy-stm32/stm32wb55rg", "not-gpdma", "ble", "mac" ] # Nucleo stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board sdmmc = [] chrono = ["embassy-stm32/chrono", "dep:chrono"] can = [] -ble = ["dep:embassy-stm32-wpan"] +ble = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/ble"] +mac = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/mac"] +embassy-stm32-wpan = [] not-gpdma = [] [dependencies] @@ -27,7 +29,7 @@ teleprobe-meta = "1" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } -embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768", "defmt-timestamp-uptime"] } +embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "tick-hz-32_768", "defmt-timestamp-uptime"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "memory-x", "time-driver-any"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", optional = true, features = ["defmt", "stm32wb55rg", "ble"] } @@ -38,24 +40,16 @@ defmt-rtt = "0.4" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } -embedded-hal-async = { version = "=0.2.0-alpha.1" } +embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } +embedded-hal-async = { version = "=0.2.0-alpha.2" } panic-probe = { version = "0.3.0", features = ["print-defmt"] } rand_core = { version = "0.6", default-features = false } rand_chacha = { version = "0.3", default-features = false } chrono = { version = "^0.4", default-features = false, optional = true} -[patch.crates-io] -stm32wb-hci = { git = "https://github.com/OueslatiGhaith/stm32wb-hci", rev = "9f663be"} - # BEGIN TESTS # Generated by gen_test.py. DO NOT EDIT. -[[bin]] -name = "tl_mbox" -path = "src/bin/tl_mbox.rs" -required-features = [ "ble",] - [[bin]] name = "can" path = "src/bin/can.rs" @@ -106,6 +100,16 @@ name = "usart_rx_ringbuffered" path = "src/bin/usart_rx_ringbuffered.rs" required-features = [ "not-gpdma",] +[[bin]] +name = "wpan_ble" +path = "src/bin/wpan_ble.rs" +required-features = [ "ble",] + +[[bin]] +name = "wpan_mac" +path = "src/bin/wpan_mac.rs" +required-features = [ "mac",] + # END TESTS [profile.dev] diff --git a/tests/stm32/src/bin/can.rs b/tests/stm32/src/bin/can.rs index 33d63d54..8bdd3c24 100644 --- a/tests/stm32/src/bin/can.rs +++ b/tests/stm32/src/bin/can.rs @@ -43,10 +43,13 @@ async fn main(_spawner: Spawner) { info!("Configuring can..."); - can.modify_filters().enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); + can.as_mut() + .modify_filters() + .enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); can.set_bitrate(1_000_000); - can.modify_config() + can.as_mut() + .modify_config() .set_loopback(true) // Receive own frames .set_silent(true) // .set_bit_timing(0x001c0003) diff --git a/tests/stm32/src/bin/gpio.rs b/tests/stm32/src/bin/gpio.rs index 67f44317..aad17443 100644 --- a/tests/stm32/src/bin/gpio.rs +++ b/tests/stm32/src/bin/gpio.rs @@ -40,14 +40,46 @@ async fn main(_spawner: Spawner) { let b = Input::new(&mut b, Pull::None); { - let _a = Output::new(&mut a, Level::Low, Speed::Low); + let a = Output::new(&mut a, Level::Low, Speed::Low); delay(); assert!(b.is_low()); + assert!(!b.is_high()); + assert!(a.is_set_low()); + assert!(!a.is_set_high()); } { - let _a = Output::new(&mut a, Level::High, Speed::Low); + let mut a = Output::new(&mut a, Level::High, Speed::Low); + delay(); + assert!(!b.is_low()); + assert!(b.is_high()); + assert!(!a.is_set_low()); + assert!(a.is_set_high()); + + // Test is_set_low / is_set_high + a.set_low(); + delay(); + assert!(b.is_low()); + assert!(a.is_set_low()); + assert!(!a.is_set_high()); + + a.set_high(); delay(); assert!(b.is_high()); + assert!(!a.is_set_low()); + assert!(a.is_set_high()); + + // Test toggle + a.toggle(); + delay(); + assert!(b.is_low()); + assert!(a.is_set_low()); + assert!(!a.is_set_high()); + + a.toggle(); + delay(); + assert!(b.is_high()); + assert!(!a.is_set_low()); + assert!(a.is_set_high()); } } diff --git a/tests/stm32/src/bin/rtc.rs b/tests/stm32/src/bin/rtc.rs index 582df575..194b153d 100644 --- a/tests/stm32/src/bin/rtc.rs +++ b/tests/stm32/src/bin/rtc.rs @@ -1,3 +1,5 @@ +// required-features: chrono + #![no_std] #![no_main] #![feature(type_alias_impl_trait)] diff --git a/tests/stm32/src/bin/tl_mbox.rs b/tests/stm32/src/bin/wpan_ble.rs similarity index 99% rename from tests/stm32/src/bin/tl_mbox.rs rename to tests/stm32/src/bin/wpan_ble.rs index af383270..3ad8aca4 100644 --- a/tests/stm32/src/bin/tl_mbox.rs +++ b/tests/stm32/src/bin/wpan_ble.rs @@ -64,7 +64,7 @@ async fn main(spawner: Spawner) { version_major, version_minor, subversion, sram2a_size, sram2b_size ); - mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; + let _ = mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; info!("resetting BLE..."); mbox.ble_subsystem.reset().await; diff --git a/tests/stm32/src/bin/wpan_mac.rs b/tests/stm32/src/bin/wpan_mac.rs new file mode 100644 index 00000000..b04a19ee --- /dev/null +++ b/tests/stm32/src/bin/wpan_mac.rs @@ -0,0 +1,124 @@ +// required-features: mac + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; + +use common::*; +use embassy_executor::Spawner; +use embassy_stm32::bind_interrupts; +use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32_wpan::mac::commands::{AssociateRequest, GetRequest, ResetRequest, SetRequest}; +use embassy_stm32_wpan::mac::event::MacEvent; +use embassy_stm32_wpan::mac::typedefs::{ + AddressMode, Capabilities, KeyIdMode, MacAddress, MacChannel, PanId, PibId, SecurityLevel, +}; +use embassy_stm32_wpan::sub::mm; +use embassy_stm32_wpan::TlMbox; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs{ + IPCC_C1_RX => ReceiveInterruptHandler; + IPCC_C1_TX => TransmitInterruptHandler; +}); + +#[embassy_executor::task] +async fn run_mm_queue(memory_manager: mm::MemoryManager) { + memory_manager.run_queue().await; +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_stm32::init(config()); + info!("Hello World!"); + + let config = Config::default(); + let mbox = TlMbox::init(p.IPCC, Irqs, config); + + spawner.spawn(run_mm_queue(mbox.mm_subsystem)).unwrap(); + + let sys_event = mbox.sys_subsystem.read().await; + info!("sys event: {}", sys_event.payload()); + + core::mem::drop(sys_event); + + let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; + info!("initialized mac: {}", result); + + info!("resetting"); + mbox.mac_subsystem + .send_command(&ResetRequest { + set_default_pib: true, + ..Default::default() + }) + .await + .unwrap(); + { + let evt = mbox.mac_subsystem.read().await.unwrap(); + info!("{:#x}", evt); + } + + info!("setting extended address"); + let extended_address: u64 = 0xACDE480000000002; + mbox.mac_subsystem + .send_command(&SetRequest { + pib_attribute_ptr: &extended_address as *const _ as *const u8, + pib_attribute: PibId::ExtendedAddress, + }) + .await + .unwrap(); + { + let evt = mbox.mac_subsystem.read().await.unwrap(); + info!("{:#x}", evt); + } + + info!("getting extended address"); + mbox.mac_subsystem + .send_command(&GetRequest { + pib_attribute: PibId::ExtendedAddress, + ..Default::default() + }) + .await + .unwrap(); + + { + let evt = mbox.mac_subsystem.read().await.unwrap(); + info!("{:#x}", evt); + + if let MacEvent::MlmeGetCnf(evt) = evt { + if evt.pib_attribute_value_len == 8 { + let value = unsafe { core::ptr::read_unaligned(evt.pib_attribute_value_ptr as *const u64) }; + + info!("value {:#x}", value) + } + } + } + + info!("assocation request"); + let a = AssociateRequest { + channel_number: MacChannel::Channel16, + channel_page: 0, + coord_addr_mode: AddressMode::Short, + coord_address: MacAddress { short: [34, 17] }, + capability_information: Capabilities::ALLOCATE_ADDRESS, + coord_pan_id: PanId([0x1A, 0xAA]), + security_level: SecurityLevel::Unsecure, + key_id_mode: KeyIdMode::Implicite, + key_source: [0; 8], + key_index: 152, + }; + info!("{}", a); + mbox.mac_subsystem.send_command(&a).await.unwrap(); + let short_addr = if let MacEvent::MlmeAssociateCnf(conf) = mbox.mac_subsystem.read().await.unwrap() { + conf.assoc_short_address + } else { + defmt::panic!() + }; + + info!("{}", short_addr); + + info!("Test OK"); + cortex_m::asm::bkpt(); +}