Merge branch 'main' of https://github.com/embassy-rs/embassy into hrtim
This commit is contained in:
commit
d42dff45de
41
.gitattributes
vendored
Normal file
41
.gitattributes
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
* text=auto
|
||||
|
||||
*.adoc text
|
||||
*.html text
|
||||
*.in text
|
||||
*.json text
|
||||
*.md text
|
||||
*.proto text
|
||||
*.py text
|
||||
*.rs text
|
||||
*.service text
|
||||
*.sh text
|
||||
*.toml text
|
||||
*.txt text
|
||||
*.x text
|
||||
*.yml text
|
||||
|
||||
*.raw binary
|
||||
*.bin binary
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.jpeg binary
|
||||
*.gif binary
|
||||
*.ico binary
|
||||
*.mov binary
|
||||
*.mp4 binary
|
||||
*.mp3 binary
|
||||
*.flv binary
|
||||
*.fla binary
|
||||
*.swf binary
|
||||
*.gz binary
|
||||
*.zip binary
|
||||
*.7z binary
|
||||
*.ttf binary
|
||||
*.eot binary
|
||||
*.woff binary
|
||||
*.pyc binary
|
||||
*.pdf binary
|
||||
*.ez binary
|
||||
*.bz2 binary
|
||||
*.swp binary
|
17
.github/ci/crlf.sh
vendored
Executable file
17
.github/ci/crlf.sh
vendored
Executable file
@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
## on push branch~=gh-readonly-queue/main/.*
|
||||
## on pull_request
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
FILES_WITH_CRLF=$(find ! -path "./.git/*" -not -type d | xargs file -N | (grep " CRLF " || true))
|
||||
|
||||
if [ -z "$FILES_WITH_CRLF" ]; then
|
||||
echo -e "No files with CRLF endings found."
|
||||
exit 0
|
||||
else
|
||||
NR_FILES=$(echo "$FILES_WITH_CRLF" | wc -l)
|
||||
echo -e "ERROR: Found ${NR_FILES} files with CRLF endings."
|
||||
echo "$FILES_WITH_CRLF"
|
||||
exit "$NR_FILES"
|
||||
fi
|
52
.github/ci/doc.sh
vendored
52
.github/ci/doc.sh
vendored
@ -6,7 +6,7 @@ set -euo pipefail
|
||||
export RUSTUP_HOME=/ci/cache/rustup
|
||||
export CARGO_HOME=/ci/cache/cargo
|
||||
export CARGO_TARGET_DIR=/ci/cache/target
|
||||
export BUILDER_THREADS=6
|
||||
export BUILDER_THREADS=4
|
||||
export BUILDER_COMPRESS=true
|
||||
|
||||
# force rustup to download the toolchain before starting building.
|
||||
@ -15,30 +15,32 @@ export BUILDER_COMPRESS=true
|
||||
# which makes rustup very sad
|
||||
rustc --version > /dev/null
|
||||
|
||||
docserver-builder -i ./embassy-stm32 -o crates/embassy-stm32/git.zup
|
||||
docserver-builder -i ./embassy-boot/boot -o crates/embassy-boot/git.zup
|
||||
docserver-builder -i ./embassy-boot/nrf -o crates/embassy-boot-nrf/git.zup
|
||||
docserver-builder -i ./embassy-boot/rp -o crates/embassy-boot-rp/git.zup
|
||||
docserver-builder -i ./embassy-boot/stm32 -o crates/embassy-boot-stm32/git.zup
|
||||
docserver-builder -i ./embassy-embedded-hal -o crates/embassy-embedded-hal/git.zup
|
||||
docserver-builder -i ./embassy-executor -o crates/embassy-executor/git.zup
|
||||
docserver-builder -i ./embassy-futures -o crates/embassy-futures/git.zup
|
||||
docserver-builder -i ./embassy-lora -o crates/embassy-lora/git.zup
|
||||
docserver-builder -i ./embassy-net -o crates/embassy-net/git.zup
|
||||
docserver-builder -i ./embassy-net-driver -o crates/embassy-net-driver/git.zup
|
||||
docserver-builder -i ./embassy-net-driver-channel -o crates/embassy-net-driver-channel/git.zup
|
||||
docserver-builder -i ./embassy-nrf -o crates/embassy-nrf/git.zup
|
||||
docserver-builder -i ./embassy-rp -o crates/embassy-rp/git.zup
|
||||
docserver-builder -i ./embassy-sync -o crates/embassy-sync/git.zup
|
||||
docserver-builder -i ./embassy-time -o crates/embassy-time/git.zup
|
||||
docserver-builder -i ./embassy-usb -o crates/embassy-usb/git.zup
|
||||
docserver-builder -i ./embassy-usb-driver -o crates/embassy-usb-driver/git.zup
|
||||
docserver-builder -i ./embassy-usb-logger -o crates/embassy-usb-logger/git.zup
|
||||
docserver-builder -i ./cyw43 -o crates/cyw43/git.zup
|
||||
docserver-builder -i ./cyw43-pio -o crates/cyw43-pio/git.zup
|
||||
docserver-builder -i ./embassy-net-w5500 -o crates/embassy-net-w5500/git.zup
|
||||
docserver-builder -i ./embassy-stm32-wpan -o crates/embassy-stm32-wpan/git.zup
|
||||
docserver-builder -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup
|
||||
docserver-builder -i ./embassy-boot/boot -o webroot/crates/embassy-boot/git.zup
|
||||
docserver-builder -i ./embassy-boot/nrf -o webroot/crates/embassy-boot-nrf/git.zup
|
||||
docserver-builder -i ./embassy-boot/rp -o webroot/crates/embassy-boot-rp/git.zup
|
||||
docserver-builder -i ./embassy-boot/stm32 -o webroot/crates/embassy-boot-stm32/git.zup
|
||||
docserver-builder -i ./embassy-embedded-hal -o webroot/crates/embassy-embedded-hal/git.zup
|
||||
docserver-builder -i ./embassy-executor -o webroot/crates/embassy-executor/git.zup
|
||||
docserver-builder -i ./embassy-futures -o webroot/crates/embassy-futures/git.zup
|
||||
docserver-builder -i ./embassy-lora -o webroot/crates/embassy-lora/git.zup
|
||||
docserver-builder -i ./embassy-net -o webroot/crates/embassy-net/git.zup
|
||||
docserver-builder -i ./embassy-net-driver -o webroot/crates/embassy-net-driver/git.zup
|
||||
docserver-builder -i ./embassy-net-driver-channel -o webroot/crates/embassy-net-driver-channel/git.zup
|
||||
docserver-builder -i ./embassy-nrf -o webroot/crates/embassy-nrf/git.zup
|
||||
docserver-builder -i ./embassy-rp -o webroot/crates/embassy-rp/git.zup
|
||||
docserver-builder -i ./embassy-sync -o webroot/crates/embassy-sync/git.zup
|
||||
docserver-builder -i ./embassy-time -o webroot/crates/embassy-time/git.zup
|
||||
docserver-builder -i ./embassy-usb -o webroot/crates/embassy-usb/git.zup
|
||||
docserver-builder -i ./embassy-usb-driver -o webroot/crates/embassy-usb-driver/git.zup
|
||||
docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/git.zup
|
||||
docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup
|
||||
docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup
|
||||
docserver-builder -i ./embassy-net-w5500 -o webroot/crates/embassy-net-w5500/git.zup
|
||||
docserver-builder -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup
|
||||
docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static
|
||||
|
||||
export KUBECONFIG=/ci/secrets/kubeconfig.yml
|
||||
POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name})
|
||||
kubectl cp crates $POD:/data
|
||||
kubectl cp webroot/crates $POD:/data
|
||||
kubectl cp webroot/static $POD:/data
|
4
ci.sh
4
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=""
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
```
|
||||
|
@ -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() {
|
||||
|
@ -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"
|
||||
|
@ -74,7 +74,21 @@ where
|
||||
E: embedded_hal_1::spi::Error + 'static,
|
||||
T: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>,
|
||||
{
|
||||
async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> {
|
||||
async fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> {
|
||||
self.wrapped.write(data)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.wrapped.transfer(data)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
|
||||
// Ensure we write the expected bytes
|
||||
for i in 0..core::cmp::min(read.len(), write.len()) {
|
||||
read[i] = write[i].clone();
|
||||
@ -83,38 +97,7 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn transfer_in_place<'a>(&'a mut self, _: &'a mut [u8]) -> Result<(), Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> embedded_hal_async::spi::SpiBusFlush for BlockingAsync<T>
|
||||
where
|
||||
E: embedded_hal_1::spi::Error + 'static,
|
||||
T: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>,
|
||||
{
|
||||
async fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> embedded_hal_async::spi::SpiBusWrite<u8> for BlockingAsync<T>
|
||||
where
|
||||
E: embedded_hal_1::spi::Error + 'static,
|
||||
T: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>,
|
||||
{
|
||||
async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> {
|
||||
self.wrapped.write(data)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> embedded_hal_async::spi::SpiBusRead<u8> for BlockingAsync<T>
|
||||
where
|
||||
E: embedded_hal_1::spi::Error + 'static,
|
||||
T: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>,
|
||||
{
|
||||
async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
|
||||
async fn transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.wrapped.transfer(data)?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -69,54 +69,39 @@ where
|
||||
type Error = T::Error;
|
||||
}
|
||||
|
||||
impl<T> embedded_hal_async::spi::SpiBus<u8> for YieldingAsync<T>
|
||||
impl<T, Word: 'static + Copy> embedded_hal_async::spi::SpiBus<Word> for YieldingAsync<T>
|
||||
where
|
||||
T: embedded_hal_async::spi::SpiBus,
|
||||
{
|
||||
async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> {
|
||||
self.wrapped.transfer(read, write).await?;
|
||||
yield_now().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Result<(), Self::Error> {
|
||||
self.wrapped.transfer_in_place(words).await?;
|
||||
yield_now().await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> embedded_hal_async::spi::SpiBusFlush for YieldingAsync<T>
|
||||
where
|
||||
T: embedded_hal_async::spi::SpiBusFlush,
|
||||
T: embedded_hal_async::spi::SpiBus<Word>,
|
||||
{
|
||||
async fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
self.wrapped.flush().await?;
|
||||
yield_now().await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> embedded_hal_async::spi::SpiBusWrite<u8> for YieldingAsync<T>
|
||||
where
|
||||
T: embedded_hal_async::spi::SpiBusWrite<u8>,
|
||||
{
|
||||
async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> {
|
||||
async fn write(&mut self, data: &[Word]) -> Result<(), Self::Error> {
|
||||
self.wrapped.write(data).await?;
|
||||
yield_now().await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> embedded_hal_async::spi::SpiBusRead<u8> for YieldingAsync<T>
|
||||
where
|
||||
T: embedded_hal_async::spi::SpiBusRead<u8>,
|
||||
{
|
||||
async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
|
||||
async fn read(&mut self, data: &mut [Word]) -> Result<(), Self::Error> {
|
||||
self.wrapped.read(data).await?;
|
||||
yield_now().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> {
|
||||
self.wrapped.transfer(read, write).await?;
|
||||
yield_now().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
|
||||
self.wrapped.transfer_in_place(words).await?;
|
||||
yield_now().await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -56,62 +56,6 @@ where
|
||||
type Error = SpiDeviceError<BUS::Error, CS::Error>;
|
||||
}
|
||||
|
||||
impl<M, BUS, CS> spi::SpiDeviceRead for SpiDevice<'_, M, BUS, CS>
|
||||
where
|
||||
M: RawMutex,
|
||||
BUS: spi::SpiBusRead,
|
||||
CS: OutputPin,
|
||||
{
|
||||
async fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> {
|
||||
let mut bus = self.bus.lock().await;
|
||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
||||
|
||||
let op_res: Result<(), BUS::Error> = try {
|
||||
for buf in operations {
|
||||
bus.read(buf).await?;
|
||||
}
|
||||
};
|
||||
|
||||
// On failure, it's important to still flush and deassert CS.
|
||||
let flush_res = bus.flush().await;
|
||||
let cs_res = self.cs.set_high();
|
||||
|
||||
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
|
||||
flush_res.map_err(SpiDeviceError::Spi)?;
|
||||
cs_res.map_err(SpiDeviceError::Cs)?;
|
||||
|
||||
Ok(op_res)
|
||||
}
|
||||
}
|
||||
|
||||
impl<M, BUS, CS> spi::SpiDeviceWrite for SpiDevice<'_, M, BUS, CS>
|
||||
where
|
||||
M: RawMutex,
|
||||
BUS: spi::SpiBusWrite,
|
||||
CS: OutputPin,
|
||||
{
|
||||
async fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> {
|
||||
let mut bus = self.bus.lock().await;
|
||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
||||
|
||||
let op_res: Result<(), BUS::Error> = try {
|
||||
for buf in operations {
|
||||
bus.write(buf).await?;
|
||||
}
|
||||
};
|
||||
|
||||
// On failure, it's important to still flush and deassert CS.
|
||||
let flush_res = bus.flush().await;
|
||||
let cs_res = self.cs.set_high();
|
||||
|
||||
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
|
||||
flush_res.map_err(SpiDeviceError::Spi)?;
|
||||
cs_res.map_err(SpiDeviceError::Cs)?;
|
||||
|
||||
Ok(op_res)
|
||||
}
|
||||
}
|
||||
|
||||
impl<M, BUS, CS> spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
|
||||
where
|
||||
M: RawMutex,
|
||||
@ -129,6 +73,12 @@ where
|
||||
Operation::Write(buf) => bus.write(buf).await?,
|
||||
Operation::Transfer(read, write) => bus.transfer(read, write).await?,
|
||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?,
|
||||
#[cfg(not(feature = "time"))]
|
||||
Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported),
|
||||
#[cfg(feature = "time")]
|
||||
Operation::DelayUs(us) => {
|
||||
embassy_time::Timer::after(embassy_time::Duration::from_micros(*us as _)).await
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -172,64 +122,6 @@ where
|
||||
type Error = SpiDeviceError<BUS::Error, CS::Error>;
|
||||
}
|
||||
|
||||
impl<M, BUS, CS> spi::SpiDeviceWrite for SpiDeviceWithConfig<'_, M, BUS, CS>
|
||||
where
|
||||
M: RawMutex,
|
||||
BUS: spi::SpiBusWrite + SetConfig,
|
||||
CS: OutputPin,
|
||||
{
|
||||
async fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> {
|
||||
let mut bus = self.bus.lock().await;
|
||||
bus.set_config(&self.config);
|
||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
||||
|
||||
let op_res: Result<(), BUS::Error> = try {
|
||||
for buf in operations {
|
||||
bus.write(buf).await?;
|
||||
}
|
||||
};
|
||||
|
||||
// On failure, it's important to still flush and deassert CS.
|
||||
let flush_res = bus.flush().await;
|
||||
let cs_res = self.cs.set_high();
|
||||
|
||||
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
|
||||
flush_res.map_err(SpiDeviceError::Spi)?;
|
||||
cs_res.map_err(SpiDeviceError::Cs)?;
|
||||
|
||||
Ok(op_res)
|
||||
}
|
||||
}
|
||||
|
||||
impl<M, BUS, CS> spi::SpiDeviceRead for SpiDeviceWithConfig<'_, M, BUS, CS>
|
||||
where
|
||||
M: RawMutex,
|
||||
BUS: spi::SpiBusRead + SetConfig,
|
||||
CS: OutputPin,
|
||||
{
|
||||
async fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> {
|
||||
let mut bus = self.bus.lock().await;
|
||||
bus.set_config(&self.config);
|
||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
||||
|
||||
let op_res: Result<(), BUS::Error> = try {
|
||||
for buf in operations {
|
||||
bus.read(buf).await?;
|
||||
}
|
||||
};
|
||||
|
||||
// On failure, it's important to still flush and deassert CS.
|
||||
let flush_res = bus.flush().await;
|
||||
let cs_res = self.cs.set_high();
|
||||
|
||||
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
|
||||
flush_res.map_err(SpiDeviceError::Spi)?;
|
||||
cs_res.map_err(SpiDeviceError::Cs)?;
|
||||
|
||||
Ok(op_res)
|
||||
}
|
||||
}
|
||||
|
||||
impl<M, BUS, CS> spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
|
||||
where
|
||||
M: RawMutex,
|
||||
@ -248,6 +140,12 @@ where
|
||||
Operation::Write(buf) => bus.write(buf).await?,
|
||||
Operation::Transfer(read, write) => bus.transfer(read, write).await?,
|
||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?,
|
||||
#[cfg(not(feature = "time"))]
|
||||
Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported),
|
||||
#[cfg(feature = "time")]
|
||||
Operation::DelayUs(us) => {
|
||||
embassy_time::Timer::after(embassy_time::Duration::from_micros(*us as _)).await
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -22,7 +22,7 @@ use core::cell::RefCell;
|
||||
use embassy_sync::blocking_mutex::raw::RawMutex;
|
||||
use embassy_sync::blocking_mutex::Mutex;
|
||||
use embedded_hal_1::digital::OutputPin;
|
||||
use embedded_hal_1::spi::{self, Operation, SpiBus, SpiBusRead, SpiBusWrite};
|
||||
use embedded_hal_1::spi::{self, Operation, SpiBus};
|
||||
|
||||
use crate::shared_bus::SpiDeviceError;
|
||||
use crate::SetConfig;
|
||||
@ -48,58 +48,6 @@ where
|
||||
type Error = SpiDeviceError<BUS::Error, CS::Error>;
|
||||
}
|
||||
|
||||
impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceRead for SpiDevice<'_, M, BUS, CS>
|
||||
where
|
||||
M: RawMutex,
|
||||
BUS: SpiBusRead,
|
||||
CS: OutputPin,
|
||||
{
|
||||
fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> {
|
||||
self.bus.lock(|bus| {
|
||||
let mut bus = bus.borrow_mut();
|
||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
||||
|
||||
let op_res = operations.iter_mut().try_for_each(|buf| bus.read(buf));
|
||||
|
||||
// On failure, it's important to still flush and deassert CS.
|
||||
let flush_res = bus.flush();
|
||||
let cs_res = self.cs.set_high();
|
||||
|
||||
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
|
||||
flush_res.map_err(SpiDeviceError::Spi)?;
|
||||
cs_res.map_err(SpiDeviceError::Cs)?;
|
||||
|
||||
Ok(op_res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceWrite for SpiDevice<'_, M, BUS, CS>
|
||||
where
|
||||
M: RawMutex,
|
||||
BUS: SpiBusWrite,
|
||||
CS: OutputPin,
|
||||
{
|
||||
fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> {
|
||||
self.bus.lock(|bus| {
|
||||
let mut bus = bus.borrow_mut();
|
||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
||||
|
||||
let op_res = operations.iter().try_for_each(|buf| bus.write(buf));
|
||||
|
||||
// On failure, it's important to still flush and deassert CS.
|
||||
let flush_res = bus.flush();
|
||||
let cs_res = self.cs.set_high();
|
||||
|
||||
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
|
||||
flush_res.map_err(SpiDeviceError::Spi)?;
|
||||
cs_res.map_err(SpiDeviceError::Cs)?;
|
||||
|
||||
Ok(op_res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
|
||||
where
|
||||
M: RawMutex,
|
||||
@ -116,6 +64,13 @@ where
|
||||
Operation::Write(buf) => bus.write(buf),
|
||||
Operation::Transfer(read, write) => bus.transfer(read, write),
|
||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
|
||||
#[cfg(not(feature = "time"))]
|
||||
Operation::DelayUs(_) => Err(SpiDeviceError::DelayUsNotSupported),
|
||||
#[cfg(feature = "time")]
|
||||
Operation::DelayUs(us) => {
|
||||
embassy_time::block_for(embassy_time::Duration::from_micros(*us as _));
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
// On failure, it's important to still flush and deassert CS.
|
||||
@ -199,58 +154,6 @@ where
|
||||
type Error = SpiDeviceError<BUS::Error, CS::Error>;
|
||||
}
|
||||
|
||||
impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceRead for SpiDeviceWithConfig<'_, M, BUS, CS>
|
||||
where
|
||||
M: RawMutex,
|
||||
BUS: SpiBusRead + SetConfig,
|
||||
CS: OutputPin,
|
||||
{
|
||||
fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> {
|
||||
self.bus.lock(|bus| {
|
||||
let mut bus = bus.borrow_mut();
|
||||
bus.set_config(&self.config);
|
||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
||||
|
||||
let op_res = operations.iter_mut().try_for_each(|buf| bus.read(buf));
|
||||
|
||||
// On failure, it's important to still flush and deassert CS.
|
||||
let flush_res = bus.flush();
|
||||
let cs_res = self.cs.set_high();
|
||||
|
||||
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
|
||||
flush_res.map_err(SpiDeviceError::Spi)?;
|
||||
cs_res.map_err(SpiDeviceError::Cs)?;
|
||||
Ok(op_res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceWrite for SpiDeviceWithConfig<'_, M, BUS, CS>
|
||||
where
|
||||
M: RawMutex,
|
||||
BUS: SpiBusWrite + SetConfig,
|
||||
CS: OutputPin,
|
||||
{
|
||||
fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> {
|
||||
self.bus.lock(|bus| {
|
||||
let mut bus = bus.borrow_mut();
|
||||
bus.set_config(&self.config);
|
||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
||||
|
||||
let op_res = operations.iter().try_for_each(|buf| bus.write(buf));
|
||||
|
||||
// On failure, it's important to still flush and deassert CS.
|
||||
let flush_res = bus.flush();
|
||||
let cs_res = self.cs.set_high();
|
||||
|
||||
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
|
||||
flush_res.map_err(SpiDeviceError::Spi)?;
|
||||
cs_res.map_err(SpiDeviceError::Cs)?;
|
||||
Ok(op_res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
|
||||
where
|
||||
M: RawMutex,
|
||||
@ -268,6 +171,13 @@ where
|
||||
Operation::Write(buf) => bus.write(buf),
|
||||
Operation::Transfer(read, write) => bus.transfer(read, write),
|
||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
|
||||
#[cfg(not(feature = "time"))]
|
||||
Operation::DelayUs(_) => Err(SpiDeviceError::DelayUsNotSupported),
|
||||
#[cfg(feature = "time")]
|
||||
Operation::DelayUs(us) => {
|
||||
embassy_time::block_for(embassy_time::Duration::from_micros(*us as _));
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
// On failure, it's important to still flush and deassert CS.
|
||||
|
@ -30,11 +30,14 @@ where
|
||||
/// Error returned by SPI device implementations in this crate.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[non_exhaustive]
|
||||
pub enum SpiDeviceError<BUS, CS> {
|
||||
/// An operation on the inner SPI bus failed.
|
||||
Spi(BUS),
|
||||
/// Setting the value of the Chip Select (CS) pin failed.
|
||||
Cs(CS),
|
||||
/// DelayUs operations are not supported when the `time` Cargo feature is not enabled.
|
||||
DelayUsNotSupported,
|
||||
}
|
||||
|
||||
impl<BUS, CS> spi::Error for SpiDeviceError<BUS, CS>
|
||||
@ -46,6 +49,7 @@ where
|
||||
match self {
|
||||
Self::Spi(e) => e.kind(),
|
||||
Self::Cs(_) => spi::ErrorKind::Other,
|
||||
Self::DelayUsNotSupported => spi::ErrorKind::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -165,6 +165,9 @@ impl<F: Future + 'static> TaskStorage<F> {
|
||||
Poll::Ready(_) => {
|
||||
this.future.drop_in_place();
|
||||
this.raw.state.fetch_and(!STATE_SPAWNED, Ordering::AcqRel);
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
this.raw.expires_at.set(Instant::MAX);
|
||||
}
|
||||
Poll::Pending => {}
|
||||
}
|
||||
|
@ -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,
|
||||
{
|
||||
|
@ -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" }
|
||||
|
@ -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 {
|
||||
|
@ -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"]
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -311,14 +311,14 @@ where
|
||||
fn handle_event(&self, data: &[u8]) {
|
||||
let Ok(event) = noproto::read::<CtrlMsg>(data) else {
|
||||
warn!("failed to parse event");
|
||||
return
|
||||
return;
|
||||
};
|
||||
|
||||
debug!("event: {:?}", &event);
|
||||
|
||||
let Some(payload) = &event.payload else {
|
||||
warn!("event without payload?");
|
||||
return
|
||||
return;
|
||||
};
|
||||
|
||||
match payload {
|
||||
|
@ -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]
|
||||
|
@ -22,7 +22,11 @@ impl<SPI: SpiDevice> SpiInterface<SPI> {
|
||||
let address_phase = address.to_be_bytes();
|
||||
let control_phase = [(block as u8) << 3 | 0b0000_0100];
|
||||
let data_phase = data;
|
||||
let operations = &[&address_phase[..], &control_phase, &data_phase];
|
||||
self.0.write_transaction(operations).await
|
||||
let operations = &mut [
|
||||
Operation::Write(&address_phase[..]),
|
||||
Operation::Write(&control_phase),
|
||||
Operation::Write(&data_phase),
|
||||
];
|
||||
self.0.transaction(operations).await
|
||||
}
|
||||
}
|
||||
|
@ -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 }
|
||||
|
||||
|
@ -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.",
|
||||
|
@ -24,9 +24,11 @@ use embassy_net_driver::{Driver, LinkState, Medium};
|
||||
use embassy_sync::waitqueue::WakerRegistration;
|
||||
use embassy_time::{Instant, Timer};
|
||||
use futures::pin_mut;
|
||||
#[allow(unused_imports)]
|
||||
use heapless::Vec;
|
||||
#[cfg(feature = "igmp")]
|
||||
pub use smoltcp::iface::MulticastError;
|
||||
#[allow(unused_imports)]
|
||||
use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage};
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
use smoltcp::socket::dhcpv4::{self, RetryConfig};
|
||||
@ -34,7 +36,9 @@ use smoltcp::socket::dhcpv4::{self, RetryConfig};
|
||||
pub use smoltcp::wire::IpListenEndpoint;
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
pub use smoltcp::wire::{EthernetAddress, HardwareAddress};
|
||||
pub use smoltcp::wire::{IpAddress, IpCidr};
|
||||
#[cfg(feature = "medium-ieee802154")]
|
||||
pub use smoltcp::wire::{HardwareAddress, Ieee802154Address};
|
||||
pub use smoltcp::wire::{IpAddress, IpCidr, IpEndpoint};
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
pub use smoltcp::wire::{Ipv4Address, Ipv4Cidr};
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
@ -232,7 +236,7 @@ impl<D: Driver + 'static> Stack<D> {
|
||||
resources: &'static mut StackResources<SOCK>,
|
||||
random_seed: u64,
|
||||
) -> Self {
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
|
||||
let medium = device.capabilities().medium;
|
||||
|
||||
let hardware_addr = match medium {
|
||||
@ -240,6 +244,8 @@ impl<D: Driver + 'static> Stack<D> {
|
||||
Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress(device.ethernet_address())),
|
||||
#[cfg(feature = "medium-ip")]
|
||||
Medium::Ip => HardwareAddress::Ip,
|
||||
#[cfg(feature = "medium-ieee802154")]
|
||||
Medium::Ieee802154 => HardwareAddress::Ieee802154(Ieee802154Address::Absent),
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => panic!(
|
||||
"Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.",
|
||||
@ -262,6 +268,7 @@ impl<D: Driver + 'static> Stack<D> {
|
||||
|
||||
let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN;
|
||||
|
||||
#[cfg_attr(feature = "medium-ieee802154", allow(unused_mut))]
|
||||
let mut socket = SocketStack {
|
||||
sockets,
|
||||
iface,
|
||||
@ -269,6 +276,7 @@ impl<D: Driver + 'static> Stack<D> {
|
||||
next_local_port,
|
||||
};
|
||||
|
||||
#[cfg_attr(feature = "medium-ieee802154", allow(unused_mut))]
|
||||
let mut inner = Inner {
|
||||
device,
|
||||
link_up: false,
|
||||
@ -287,6 +295,9 @@ impl<D: Driver + 'static> Stack<D> {
|
||||
dns_waker: WakerRegistration::new(),
|
||||
};
|
||||
|
||||
#[cfg(feature = "medium-ieee802154")]
|
||||
let _ = config;
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
match config.ipv4 {
|
||||
ConfigV4::Static(config) => {
|
||||
@ -479,30 +490,78 @@ impl<D: Driver + 'static> Stack<D> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "igmp")]
|
||||
impl<D: Driver + smoltcp::phy::Device + 'static> Stack<D> {
|
||||
impl<D: Driver + 'static> Stack<D> {
|
||||
/// Join a multicast group.
|
||||
pub fn join_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError>
|
||||
pub async fn join_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError>
|
||||
where
|
||||
T: Into<IpAddress>,
|
||||
{
|
||||
let addr = addr.into();
|
||||
|
||||
poll_fn(move |cx| self.poll_join_multicast_group(addr, cx)).await
|
||||
}
|
||||
|
||||
/// Join a multicast group.
|
||||
///
|
||||
/// When the send queue is full, this method will return `Poll::Pending`
|
||||
/// and register the current task to be notified when the queue has space available.
|
||||
pub fn poll_join_multicast_group<T>(&self, addr: T, cx: &mut Context<'_>) -> Poll<Result<bool, MulticastError>>
|
||||
where
|
||||
T: Into<IpAddress>,
|
||||
{
|
||||
let addr = addr.into();
|
||||
|
||||
self.with_mut(|s, i| {
|
||||
s.iface
|
||||
.join_multicast_group(&mut i.device, addr, instant_to_smoltcp(Instant::now()))
|
||||
let mut smoldev = DriverAdapter {
|
||||
cx: Some(cx),
|
||||
inner: &mut i.device,
|
||||
};
|
||||
|
||||
match s
|
||||
.iface
|
||||
.join_multicast_group(&mut smoldev, addr, instant_to_smoltcp(Instant::now()))
|
||||
{
|
||||
Ok(announce_sent) => Poll::Ready(Ok(announce_sent)),
|
||||
Err(MulticastError::Exhausted) => Poll::Pending,
|
||||
Err(other) => Poll::Ready(Err(other)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Leave a multicast group.
|
||||
pub fn leave_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError>
|
||||
pub async fn leave_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError>
|
||||
where
|
||||
T: Into<IpAddress>,
|
||||
{
|
||||
let addr = addr.into();
|
||||
|
||||
poll_fn(move |cx| self.poll_leave_multicast_group(addr, cx)).await
|
||||
}
|
||||
|
||||
/// Leave a multicast group.
|
||||
///
|
||||
/// When the send queue is full, this method will return `Poll::Pending`
|
||||
/// and register the current task to be notified when the queue has space available.
|
||||
pub fn poll_leave_multicast_group<T>(&self, addr: T, cx: &mut Context<'_>) -> Poll<Result<bool, MulticastError>>
|
||||
where
|
||||
T: Into<IpAddress>,
|
||||
{
|
||||
let addr = addr.into();
|
||||
|
||||
self.with_mut(|s, i| {
|
||||
s.iface
|
||||
.leave_multicast_group(&mut i.device, addr, instant_to_smoltcp(Instant::now()))
|
||||
let mut smoldev = DriverAdapter {
|
||||
cx: Some(cx),
|
||||
inner: &mut i.device,
|
||||
};
|
||||
|
||||
match s
|
||||
.iface
|
||||
.leave_multicast_group(&mut smoldev, addr, instant_to_smoltcp(Instant::now()))
|
||||
{
|
||||
Ok(leave_sent) => Poll::Ready(Ok(leave_sent)),
|
||||
Err(MulticastError::Exhausted) => Poll::Pending,
|
||||
Err(other) => Poll::Ready(Err(other)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -531,11 +590,14 @@ impl<D: Driver + 'static> Inner<D> {
|
||||
|
||||
debug!(" IP address: {}", config.address);
|
||||
s.iface.update_ip_addrs(|addrs| {
|
||||
if addrs.is_empty() {
|
||||
addrs.push(IpCidr::Ipv4(config.address)).unwrap();
|
||||
} else {
|
||||
addrs[0] = IpCidr::Ipv4(config.address);
|
||||
if let Some((index, _)) = addrs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, &addr)| matches!(addr, IpCidr::Ipv4(_)))
|
||||
{
|
||||
addrs.remove(index);
|
||||
}
|
||||
addrs.push(IpCidr::Ipv4(config.address)).unwrap();
|
||||
});
|
||||
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
@ -570,11 +632,14 @@ impl<D: Driver + 'static> Inner<D> {
|
||||
|
||||
debug!(" IP address: {}", config.address);
|
||||
s.iface.update_ip_addrs(|addrs| {
|
||||
if addrs.is_empty() {
|
||||
addrs.push(IpCidr::Ipv6(config.address)).unwrap();
|
||||
} else {
|
||||
addrs[0] = IpCidr::Ipv6(config.address);
|
||||
if let Some((index, _)) = addrs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, &addr)| matches!(addr, IpCidr::Ipv6(_)))
|
||||
{
|
||||
addrs.remove(index);
|
||||
}
|
||||
addrs.push(IpCidr::Ipv6(config.address)).unwrap();
|
||||
});
|
||||
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
@ -642,13 +707,21 @@ impl<D: Driver + 'static> Inner<D> {
|
||||
socket.set_retry_config(config.retry_config);
|
||||
}
|
||||
|
||||
#[allow(unused)] // used only with dhcp
|
||||
fn unapply_config(&mut self, s: &mut SocketStack) {
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
fn unapply_config_v4(&mut self, s: &mut SocketStack) {
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
let medium = self.device.capabilities().medium;
|
||||
|
||||
debug!("Lost IP configuration");
|
||||
s.iface.update_ip_addrs(|ip_addrs| ip_addrs.clear());
|
||||
s.iface.update_ip_addrs(|ip_addrs| {
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
if let Some((index, _)) = ip_addrs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, &addr)| matches!(addr, IpCidr::Ipv4(_)))
|
||||
{
|
||||
ip_addrs.remove(index);
|
||||
}
|
||||
});
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
if medium == Medium::Ethernet {
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
@ -695,7 +768,7 @@ impl<D: Driver + 'static> Inner<D> {
|
||||
if self.link_up {
|
||||
match socket.poll() {
|
||||
None => {}
|
||||
Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s),
|
||||
Some(dhcpv4::Event::Deconfigured) => self.unapply_config_v4(s),
|
||||
Some(dhcpv4::Event::Configured(config)) => {
|
||||
let config = StaticConfigV4 {
|
||||
address: config.address,
|
||||
@ -707,7 +780,7 @@ impl<D: Driver + 'static> Inner<D> {
|
||||
}
|
||||
} else if old_link_up {
|
||||
socket.reset();
|
||||
self.unapply_config(s);
|
||||
self.unapply_config_v4(s);
|
||||
}
|
||||
}
|
||||
//if old_link_up || self.link_up {
|
||||
|
@ -3,7 +3,7 @@
|
||||
use core::cell::RefCell;
|
||||
use core::future::poll_fn;
|
||||
use core::mem;
|
||||
use core::task::Poll;
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
use embassy_net_driver::Driver;
|
||||
use smoltcp::iface::{Interface, SocketHandle};
|
||||
@ -102,37 +102,61 @@ impl<'a> UdpSocket<'a> {
|
||||
///
|
||||
/// Returns the number of bytes received and the remote endpoint.
|
||||
pub async fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, IpEndpoint), Error> {
|
||||
poll_fn(move |cx| {
|
||||
self.with_mut(|s, _| match s.recv_slice(buf) {
|
||||
Ok((n, meta)) => Poll::Ready(Ok((n, meta.endpoint))),
|
||||
// No data ready
|
||||
Err(udp::RecvError::Exhausted) => {
|
||||
s.register_recv_waker(cx.waker());
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
poll_fn(move |cx| self.poll_recv_from(buf, cx)).await
|
||||
}
|
||||
|
||||
/// Receive a datagram.
|
||||
///
|
||||
/// When no datagram is available, this method will return `Poll::Pending` and
|
||||
/// register the current task to be notified when a datagram is received.
|
||||
///
|
||||
/// When a datagram is received, this method will return `Poll::Ready` with the
|
||||
/// number of bytes received and the remote endpoint.
|
||||
pub fn poll_recv_from(&self, buf: &mut [u8], cx: &mut Context<'_>) -> Poll<Result<(usize, IpEndpoint), Error>> {
|
||||
self.with_mut(|s, _| match s.recv_slice(buf) {
|
||||
Ok((n, meta)) => Poll::Ready(Ok((n, meta.endpoint))),
|
||||
// No data ready
|
||||
Err(udp::RecvError::Exhausted) => {
|
||||
s.register_recv_waker(cx.waker());
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// Send a datagram to the specified remote endpoint.
|
||||
///
|
||||
/// This method will wait until the datagram has been sent.
|
||||
///
|
||||
/// When the remote endpoint is not reachable, this method will return `Err(Error::NoRoute)`
|
||||
pub async fn send_to<T>(&self, buf: &[u8], remote_endpoint: T) -> Result<(), Error>
|
||||
where
|
||||
T: Into<IpEndpoint>,
|
||||
{
|
||||
let remote_endpoint = remote_endpoint.into();
|
||||
poll_fn(move |cx| {
|
||||
self.with_mut(|s, _| match s.send_slice(buf, remote_endpoint) {
|
||||
// Entire datagram has been sent
|
||||
Ok(()) => Poll::Ready(Ok(())),
|
||||
Err(udp::SendError::BufferFull) => {
|
||||
s.register_send_waker(cx.waker());
|
||||
Poll::Pending
|
||||
}
|
||||
Err(udp::SendError::Unaddressable) => Poll::Ready(Err(Error::NoRoute)),
|
||||
})
|
||||
let remote_endpoint: IpEndpoint = remote_endpoint.into();
|
||||
poll_fn(move |cx| self.poll_send_to(buf, remote_endpoint, cx)).await
|
||||
}
|
||||
|
||||
/// Send a datagram to the specified remote endpoint.
|
||||
///
|
||||
/// When the datagram has been sent, this method will return `Poll::Ready(Ok())`.
|
||||
///
|
||||
/// When the socket's send buffer is full, this method will return `Poll::Pending`
|
||||
/// and register the current task to be notified when the buffer has space available.
|
||||
///
|
||||
/// When the remote endpoint is not reachable, this method will return `Poll::Ready(Err(Error::NoRoute))`.
|
||||
pub fn poll_send_to<T>(&self, buf: &[u8], remote_endpoint: T, cx: &mut Context<'_>) -> Poll<Result<(), Error>>
|
||||
where
|
||||
T: Into<IpEndpoint>,
|
||||
{
|
||||
self.with_mut(|s, _| match s.send_slice(buf, remote_endpoint) {
|
||||
// Entire datagram has been sent
|
||||
Ok(()) => Poll::Ready(Ok(())),
|
||||
Err(udp::SendError::BufferFull) => {
|
||||
s.register_send_waker(cx.waker());
|
||||
Poll::Pending
|
||||
}
|
||||
Err(udp::SendError::Unaddressable) => Poll::Ready(Err(Error::NoRoute)),
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// Returns the local endpoint of the socket.
|
||||
|
@ -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 }
|
||||
|
@ -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()])
|
||||
}
|
||||
|
@ -1,294 +1,500 @@
|
||||
//! Pulse Density Modulation (PDM) mirophone driver.
|
||||
|
||||
#![macro_use]
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_common::drop::OnDrop;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use futures::future::poll_fn;
|
||||
|
||||
use crate::chip::EASY_DMA_SIZE;
|
||||
use crate::gpio::sealed::Pin;
|
||||
use crate::gpio::{AnyPin, Pin as GpioPin};
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::{interrupt, Peripheral};
|
||||
|
||||
/// Interrupt handler.
|
||||
pub struct InterruptHandler<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
|
||||
unsafe fn on_interrupt() {
|
||||
T::regs().intenclr.write(|w| w.end().clear());
|
||||
T::state().waker.wake();
|
||||
}
|
||||
}
|
||||
|
||||
/// PDM microphone interface
|
||||
pub struct Pdm<'d, T: Instance> {
|
||||
_peri: PeripheralRef<'d, T>,
|
||||
}
|
||||
|
||||
/// PDM error.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
/// Buffer is too long.
|
||||
BufferTooLong,
|
||||
/// Buffer is empty
|
||||
BufferZeroLength,
|
||||
/// PDM is not running
|
||||
NotRunning,
|
||||
}
|
||||
|
||||
static DUMMY_BUFFER: [i16; 1] = [0; 1];
|
||||
|
||||
impl<'d, T: Instance> Pdm<'d, T> {
|
||||
/// Create PDM driver
|
||||
pub fn new(
|
||||
pdm: impl Peripheral<P = T> + 'd,
|
||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
||||
clk: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
din: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(pdm, clk, din);
|
||||
Self::new_inner(pdm, clk.map_into(), din.map_into(), config)
|
||||
}
|
||||
|
||||
fn new_inner(
|
||||
pdm: PeripheralRef<'d, T>,
|
||||
clk: PeripheralRef<'d, AnyPin>,
|
||||
din: PeripheralRef<'d, AnyPin>,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(pdm);
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
// setup gpio pins
|
||||
din.conf().write(|w| w.input().set_bit());
|
||||
r.psel.din.write(|w| unsafe { w.bits(din.psel_bits()) });
|
||||
clk.set_low();
|
||||
clk.conf().write(|w| w.dir().output());
|
||||
r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) });
|
||||
|
||||
// configure
|
||||
// use default for
|
||||
// - gain right
|
||||
// - gain left
|
||||
// - clk
|
||||
// - ratio
|
||||
r.mode.write(|w| {
|
||||
w.edge().bit(config.edge == Edge::LeftRising);
|
||||
w.operation().bit(config.operation_mode == OperationMode::Mono);
|
||||
w
|
||||
});
|
||||
r.gainl.write(|w| w.gainl().default_gain());
|
||||
r.gainr.write(|w| w.gainr().default_gain());
|
||||
|
||||
// IRQ
|
||||
T::Interrupt::unpend();
|
||||
unsafe { T::Interrupt::enable() };
|
||||
|
||||
r.enable.write(|w| w.enable().set_bit());
|
||||
|
||||
Self { _peri: pdm }
|
||||
}
|
||||
|
||||
/// Start sampling microphon data into a dummy buffer
|
||||
/// Usefull to start the microphon and keep it active between recording samples
|
||||
pub async fn start(&mut self) {
|
||||
let r = T::regs();
|
||||
|
||||
// start dummy sampling because microphon needs some setup time
|
||||
r.sample
|
||||
.ptr
|
||||
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
|
||||
r.sample
|
||||
.maxcnt
|
||||
.write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
|
||||
|
||||
r.tasks_start.write(|w| unsafe { w.bits(1) });
|
||||
}
|
||||
|
||||
/// Stop sampling microphon data inta a dummy buffer
|
||||
pub async fn stop(&mut self) {
|
||||
let r = T::regs();
|
||||
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||
r.events_started.reset();
|
||||
}
|
||||
|
||||
/// Sample data into the given buffer.
|
||||
pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> {
|
||||
if buffer.len() == 0 {
|
||||
return Err(Error::BufferZeroLength);
|
||||
}
|
||||
if buffer.len() > EASY_DMA_SIZE {
|
||||
return Err(Error::BufferTooLong);
|
||||
}
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
if r.events_started.read().bits() == 0 {
|
||||
return Err(Error::NotRunning);
|
||||
}
|
||||
|
||||
let drop = OnDrop::new(move || {
|
||||
r.intenclr.write(|w| w.end().clear());
|
||||
r.events_stopped.reset();
|
||||
|
||||
// reset to dummy buffer
|
||||
r.sample
|
||||
.ptr
|
||||
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
|
||||
r.sample
|
||||
.maxcnt
|
||||
.write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
|
||||
|
||||
while r.events_stopped.read().bits() == 0 {}
|
||||
});
|
||||
|
||||
// setup user buffer
|
||||
let ptr = buffer.as_ptr();
|
||||
let len = buffer.len();
|
||||
r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(ptr as u32) });
|
||||
r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(len as _) });
|
||||
|
||||
// wait till the current sample is finished and the user buffer sample is started
|
||||
Self::wait_for_sample().await;
|
||||
|
||||
// reset the buffer back to the dummy buffer
|
||||
r.sample
|
||||
.ptr
|
||||
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
|
||||
r.sample
|
||||
.maxcnt
|
||||
.write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
|
||||
|
||||
// wait till the user buffer is sampled
|
||||
Self::wait_for_sample().await;
|
||||
|
||||
drop.defuse();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_sample() {
|
||||
let r = T::regs();
|
||||
|
||||
r.events_end.reset();
|
||||
r.intenset.write(|w| w.end().set());
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
poll_fn(|cx| {
|
||||
T::state().waker.register(cx.waker());
|
||||
if r.events_end.read().bits() != 0 {
|
||||
return Poll::Ready(());
|
||||
}
|
||||
Poll::Pending
|
||||
})
|
||||
.await;
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
/// PDM microphone driver Config
|
||||
pub struct Config {
|
||||
/// Use stero or mono operation
|
||||
pub operation_mode: OperationMode,
|
||||
/// On which edge the left channel should be samples
|
||||
pub edge: Edge,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
operation_mode: OperationMode::Mono,
|
||||
edge: Edge::LeftFalling,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// PDM operation mode.
|
||||
#[derive(PartialEq)]
|
||||
pub enum OperationMode {
|
||||
/// Mono (1 channel)
|
||||
Mono,
|
||||
/// Stereo (2 channels)
|
||||
Stereo,
|
||||
}
|
||||
|
||||
/// PDM edge polarity
|
||||
#[derive(PartialEq)]
|
||||
pub enum Edge {
|
||||
/// Left edge is rising
|
||||
LeftRising,
|
||||
/// Left edge is falling
|
||||
LeftFalling,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Drop for Pdm<'d, T> {
|
||||
fn drop(&mut self) {
|
||||
let r = T::regs();
|
||||
|
||||
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||
|
||||
r.enable.write(|w| w.enable().disabled());
|
||||
|
||||
r.psel.din.reset();
|
||||
r.psel.clk.reset();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
/// Peripheral static state
|
||||
pub struct State {
|
||||
pub waker: AtomicWaker,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
waker: AtomicWaker::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Instance {
|
||||
fn regs() -> &'static crate::pac::pdm::RegisterBlock;
|
||||
fn state() -> &'static State;
|
||||
}
|
||||
}
|
||||
|
||||
/// PDM peripheral instance.
|
||||
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
|
||||
/// Interrupt for this peripheral.
|
||||
type Interrupt: interrupt::typelevel::Interrupt;
|
||||
}
|
||||
|
||||
macro_rules! impl_pdm {
|
||||
($type:ident, $pac_type:ident, $irq:ident) => {
|
||||
impl crate::pdm::sealed::Instance for peripherals::$type {
|
||||
fn regs() -> &'static crate::pac::pdm::RegisterBlock {
|
||||
unsafe { &*pac::$pac_type::ptr() }
|
||||
}
|
||||
fn state() -> &'static crate::pdm::sealed::State {
|
||||
static STATE: crate::pdm::sealed::State = crate::pdm::sealed::State::new();
|
||||
&STATE
|
||||
}
|
||||
}
|
||||
impl crate::pdm::Instance for peripherals::$type {
|
||||
type Interrupt = crate::interrupt::typelevel::$irq;
|
||||
}
|
||||
};
|
||||
}
|
||||
//! Pulse Density Modulation (PDM) mirophone driver.
|
||||
|
||||
#![macro_use]
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_common::drop::OnDrop;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use fixed::types::I7F1;
|
||||
use futures::future::poll_fn;
|
||||
|
||||
use crate::chip::EASY_DMA_SIZE;
|
||||
use crate::gpio::sealed::Pin;
|
||||
use crate::gpio::{AnyPin, Pin as GpioPin};
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::pac::pdm::mode::{EDGE_A, OPERATION_A};
|
||||
pub use crate::pac::pdm::pdmclkctrl::FREQ_A as Frequency;
|
||||
#[cfg(any(
|
||||
feature = "nrf52840",
|
||||
feature = "nrf52833",
|
||||
feature = "_nrf5340-app",
|
||||
feature = "_nrf9160",
|
||||
))]
|
||||
pub use crate::pac::pdm::ratio::RATIO_A as Ratio;
|
||||
use crate::{interrupt, Peripheral};
|
||||
|
||||
/// Interrupt handler.
|
||||
pub struct InterruptHandler<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
|
||||
unsafe fn on_interrupt() {
|
||||
let r = T::regs();
|
||||
|
||||
if r.events_end.read().bits() != 0 {
|
||||
r.intenclr.write(|w| w.end().clear());
|
||||
}
|
||||
|
||||
if r.events_started.read().bits() != 0 {
|
||||
r.intenclr.write(|w| w.started().clear());
|
||||
}
|
||||
|
||||
if r.events_stopped.read().bits() != 0 {
|
||||
r.intenclr.write(|w| w.stopped().clear());
|
||||
}
|
||||
|
||||
T::state().waker.wake();
|
||||
}
|
||||
}
|
||||
|
||||
/// PDM microphone interface
|
||||
pub struct Pdm<'d, T: Instance> {
|
||||
_peri: PeripheralRef<'d, T>,
|
||||
}
|
||||
|
||||
/// PDM error.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
/// Buffer is too long.
|
||||
BufferTooLong,
|
||||
/// Buffer is empty
|
||||
BufferZeroLength,
|
||||
/// PDM is not running
|
||||
NotRunning,
|
||||
/// PDM is already running
|
||||
AlreadyRunning,
|
||||
}
|
||||
|
||||
static DUMMY_BUFFER: [i16; 1] = [0; 1];
|
||||
|
||||
/// The state of a continuously running sampler. While it reflects
|
||||
/// the progress of a sampler, it also signals what should be done
|
||||
/// next. For example, if the sampler has stopped then the Pdm implementation
|
||||
/// can then tear down its infrastructure.
|
||||
#[derive(PartialEq)]
|
||||
pub enum SamplerState {
|
||||
/// The sampler processed the samples and is ready for more.
|
||||
Sampled,
|
||||
/// The sampler is done processing samples.
|
||||
Stopped,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Pdm<'d, T> {
|
||||
/// Create PDM driver
|
||||
pub fn new(
|
||||
pdm: impl Peripheral<P = T> + 'd,
|
||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
|
||||
clk: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
din: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(pdm, clk, din);
|
||||
Self::new_inner(pdm, clk.map_into(), din.map_into(), config)
|
||||
}
|
||||
|
||||
fn new_inner(
|
||||
pdm: PeripheralRef<'d, T>,
|
||||
clk: PeripheralRef<'d, AnyPin>,
|
||||
din: PeripheralRef<'d, AnyPin>,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(pdm);
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
// setup gpio pins
|
||||
din.conf().write(|w| w.input().set_bit());
|
||||
r.psel.din.write(|w| unsafe { w.bits(din.psel_bits()) });
|
||||
clk.set_low();
|
||||
clk.conf().write(|w| w.dir().output());
|
||||
r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) });
|
||||
|
||||
// configure
|
||||
r.pdmclkctrl.write(|w| w.freq().variant(config.frequency));
|
||||
#[cfg(any(
|
||||
feature = "nrf52840",
|
||||
feature = "nrf52833",
|
||||
feature = "_nrf5340-app",
|
||||
feature = "_nrf9160",
|
||||
))]
|
||||
r.ratio.write(|w| w.ratio().variant(config.ratio));
|
||||
r.mode.write(|w| {
|
||||
w.operation().variant(config.operation_mode.into());
|
||||
w.edge().variant(config.edge.into());
|
||||
w
|
||||
});
|
||||
|
||||
Self::_set_gain(r, config.gain_left, config.gain_right);
|
||||
|
||||
// Disable all events interrupts
|
||||
r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) });
|
||||
|
||||
// IRQ
|
||||
T::Interrupt::unpend();
|
||||
unsafe { T::Interrupt::enable() };
|
||||
|
||||
r.enable.write(|w| w.enable().set_bit());
|
||||
|
||||
Self { _peri: pdm }
|
||||
}
|
||||
|
||||
fn _set_gain(r: &crate::pac::pdm::RegisterBlock, gain_left: I7F1, gain_right: I7F1) {
|
||||
let gain_left = gain_left
|
||||
.saturating_add(I7F1::from_bits(40))
|
||||
.saturating_to_num::<u8>()
|
||||
.clamp(0, 0x50);
|
||||
let gain_right = gain_right
|
||||
.saturating_add(I7F1::from_bits(40))
|
||||
.saturating_to_num::<u8>()
|
||||
.clamp(0, 0x50);
|
||||
|
||||
r.gainl.write(|w| unsafe { w.gainl().bits(gain_left) });
|
||||
r.gainr.write(|w| unsafe { w.gainr().bits(gain_right) });
|
||||
}
|
||||
|
||||
/// Adjust the gain of the PDM microphone on the fly
|
||||
pub fn set_gain(&mut self, gain_left: I7F1, gain_right: I7F1) {
|
||||
Self::_set_gain(T::regs(), gain_left, gain_right)
|
||||
}
|
||||
|
||||
/// Start sampling microphon data into a dummy buffer
|
||||
/// Usefull to start the microphon and keep it active between recording samples
|
||||
pub async fn start(&mut self) {
|
||||
let r = T::regs();
|
||||
|
||||
// start dummy sampling because microphon needs some setup time
|
||||
r.sample
|
||||
.ptr
|
||||
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
|
||||
r.sample
|
||||
.maxcnt
|
||||
.write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
|
||||
|
||||
r.tasks_start.write(|w| unsafe { w.bits(1) });
|
||||
}
|
||||
|
||||
/// Stop sampling microphon data inta a dummy buffer
|
||||
pub async fn stop(&mut self) {
|
||||
let r = T::regs();
|
||||
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||
r.events_started.reset();
|
||||
}
|
||||
|
||||
/// Sample data into the given buffer.
|
||||
pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> {
|
||||
if buffer.len() == 0 {
|
||||
return Err(Error::BufferZeroLength);
|
||||
}
|
||||
if buffer.len() > EASY_DMA_SIZE {
|
||||
return Err(Error::BufferTooLong);
|
||||
}
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
if r.events_started.read().bits() == 0 {
|
||||
return Err(Error::NotRunning);
|
||||
}
|
||||
|
||||
let drop = OnDrop::new(move || {
|
||||
r.intenclr.write(|w| w.end().clear());
|
||||
r.events_stopped.reset();
|
||||
|
||||
// reset to dummy buffer
|
||||
r.sample
|
||||
.ptr
|
||||
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
|
||||
r.sample
|
||||
.maxcnt
|
||||
.write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
|
||||
|
||||
while r.events_stopped.read().bits() == 0 {}
|
||||
});
|
||||
|
||||
// setup user buffer
|
||||
let ptr = buffer.as_ptr();
|
||||
let len = buffer.len();
|
||||
r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(ptr as u32) });
|
||||
r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(len as _) });
|
||||
|
||||
// wait till the current sample is finished and the user buffer sample is started
|
||||
Self::wait_for_sample().await;
|
||||
|
||||
// reset the buffer back to the dummy buffer
|
||||
r.sample
|
||||
.ptr
|
||||
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
|
||||
r.sample
|
||||
.maxcnt
|
||||
.write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
|
||||
|
||||
// wait till the user buffer is sampled
|
||||
Self::wait_for_sample().await;
|
||||
|
||||
drop.defuse();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_sample() {
|
||||
let r = T::regs();
|
||||
|
||||
r.events_end.reset();
|
||||
r.intenset.write(|w| w.end().set());
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
poll_fn(|cx| {
|
||||
T::state().waker.register(cx.waker());
|
||||
if r.events_end.read().bits() != 0 {
|
||||
return Poll::Ready(());
|
||||
}
|
||||
Poll::Pending
|
||||
})
|
||||
.await;
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
}
|
||||
|
||||
/// Continuous sampling with double buffers.
|
||||
///
|
||||
/// A sampler closure is provided that receives the buffer of samples, noting
|
||||
/// that the size of this buffer can be less than the original buffer's size.
|
||||
/// A command is return from the closure that indicates whether the sampling
|
||||
/// should continue or stop.
|
||||
///
|
||||
/// NOTE: The time spent within the callback supplied should not exceed the time
|
||||
/// taken to acquire the samples into a single buffer. You should measure the
|
||||
/// time taken by the callback and set the sample buffer size accordingly.
|
||||
/// Exceeding this time can lead to samples becoming dropped.
|
||||
pub async fn run_task_sampler<S, const N: usize>(
|
||||
&mut self,
|
||||
bufs: &mut [[i16; N]; 2],
|
||||
mut sampler: S,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
S: FnMut(&[i16; N]) -> SamplerState,
|
||||
{
|
||||
let r = T::regs();
|
||||
|
||||
if r.events_started.read().bits() != 0 {
|
||||
return Err(Error::AlreadyRunning);
|
||||
}
|
||||
|
||||
r.sample
|
||||
.ptr
|
||||
.write(|w| unsafe { w.sampleptr().bits(bufs[0].as_mut_ptr() as u32) });
|
||||
r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(N as _) });
|
||||
|
||||
// Reset and enable the events
|
||||
r.events_end.reset();
|
||||
r.events_started.reset();
|
||||
r.events_stopped.reset();
|
||||
r.intenset.write(|w| {
|
||||
w.end().set();
|
||||
w.started().set();
|
||||
w.stopped().set();
|
||||
w
|
||||
});
|
||||
|
||||
// Don't reorder the start event before the previous writes. Hopefully self
|
||||
// wouldn't happen anyway.
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
r.tasks_start.write(|w| unsafe { w.bits(1) });
|
||||
|
||||
let mut current_buffer = 0;
|
||||
|
||||
let mut done = false;
|
||||
|
||||
let drop = OnDrop::new(|| {
|
||||
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||
// N.B. It would be better if this were async, but Drop only support sync code.
|
||||
while r.events_stopped.read().bits() != 0 {}
|
||||
});
|
||||
|
||||
// Wait for events and complete when the sampler indicates it has had enough.
|
||||
poll_fn(|cx| {
|
||||
let r = T::regs();
|
||||
|
||||
T::state().waker.register(cx.waker());
|
||||
|
||||
if r.events_end.read().bits() != 0 {
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
r.events_end.reset();
|
||||
r.intenset.write(|w| w.end().set());
|
||||
|
||||
if !done {
|
||||
// Discard the last buffer after the user requested a stop.
|
||||
if sampler(&bufs[current_buffer]) == SamplerState::Sampled {
|
||||
let next_buffer = 1 - current_buffer;
|
||||
current_buffer = next_buffer;
|
||||
} else {
|
||||
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||
done = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
if r.events_started.read().bits() != 0 {
|
||||
r.events_started.reset();
|
||||
r.intenset.write(|w| w.started().set());
|
||||
|
||||
let next_buffer = 1 - current_buffer;
|
||||
r.sample
|
||||
.ptr
|
||||
.write(|w| unsafe { w.sampleptr().bits(bufs[next_buffer].as_mut_ptr() as u32) });
|
||||
}
|
||||
|
||||
if r.events_stopped.read().bits() != 0 {
|
||||
return Poll::Ready(());
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
})
|
||||
.await;
|
||||
drop.defuse();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// PDM microphone driver Config
|
||||
pub struct Config {
|
||||
/// Use stero or mono operation
|
||||
pub operation_mode: OperationMode,
|
||||
/// On which edge the left channel should be samples
|
||||
pub edge: Edge,
|
||||
/// Clock frequency
|
||||
pub frequency: Frequency,
|
||||
/// Clock ratio
|
||||
#[cfg(any(
|
||||
feature = "nrf52840",
|
||||
feature = "nrf52833",
|
||||
feature = "_nrf5340-app",
|
||||
feature = "_nrf9160",
|
||||
))]
|
||||
pub ratio: Ratio,
|
||||
/// Gain left in dB
|
||||
pub gain_left: I7F1,
|
||||
/// Gain right in dB
|
||||
pub gain_right: I7F1,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
operation_mode: OperationMode::Mono,
|
||||
edge: Edge::LeftFalling,
|
||||
frequency: Frequency::DEFAULT,
|
||||
#[cfg(any(
|
||||
feature = "nrf52840",
|
||||
feature = "nrf52833",
|
||||
feature = "_nrf5340-app",
|
||||
feature = "_nrf9160",
|
||||
))]
|
||||
ratio: Ratio::RATIO80,
|
||||
gain_left: I7F1::ZERO,
|
||||
gain_right: I7F1::ZERO,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// PDM operation mode.
|
||||
#[derive(PartialEq)]
|
||||
pub enum OperationMode {
|
||||
/// Mono (1 channel)
|
||||
Mono,
|
||||
/// Stereo (2 channels)
|
||||
Stereo,
|
||||
}
|
||||
|
||||
impl From<OperationMode> for OPERATION_A {
|
||||
fn from(mode: OperationMode) -> Self {
|
||||
match mode {
|
||||
OperationMode::Mono => OPERATION_A::MONO,
|
||||
OperationMode::Stereo => OPERATION_A::STEREO,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// PDM edge polarity
|
||||
#[derive(PartialEq)]
|
||||
pub enum Edge {
|
||||
/// Left edge is rising
|
||||
LeftRising,
|
||||
/// Left edge is falling
|
||||
LeftFalling,
|
||||
}
|
||||
|
||||
impl From<Edge> for EDGE_A {
|
||||
fn from(edge: Edge) -> Self {
|
||||
match edge {
|
||||
Edge::LeftRising => EDGE_A::LEFT_RISING,
|
||||
Edge::LeftFalling => EDGE_A::LEFT_FALLING,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Drop for Pdm<'d, T> {
|
||||
fn drop(&mut self) {
|
||||
let r = T::regs();
|
||||
|
||||
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||
|
||||
r.enable.write(|w| w.enable().disabled());
|
||||
|
||||
r.psel.din.reset();
|
||||
r.psel.clk.reset();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
/// Peripheral static state
|
||||
pub struct State {
|
||||
pub waker: AtomicWaker,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
waker: AtomicWaker::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Instance {
|
||||
fn regs() -> &'static crate::pac::pdm::RegisterBlock;
|
||||
fn state() -> &'static State;
|
||||
}
|
||||
}
|
||||
|
||||
/// PDM peripheral instance.
|
||||
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
|
||||
/// Interrupt for this peripheral.
|
||||
type Interrupt: interrupt::typelevel::Interrupt;
|
||||
}
|
||||
|
||||
macro_rules! impl_pdm {
|
||||
($type:ident, $pac_type:ident, $irq:ident) => {
|
||||
impl crate::pdm::sealed::Instance for peripherals::$type {
|
||||
fn regs() -> &'static crate::pac::pdm::RegisterBlock {
|
||||
unsafe { &*pac::$pac_type::ptr() }
|
||||
}
|
||||
fn state() -> &'static crate::pdm::sealed::State {
|
||||
static STATE: crate::pdm::sealed::State = crate::pdm::sealed::State::new();
|
||||
&STATE
|
||||
}
|
||||
}
|
||||
impl crate::pdm::Instance for peripherals::$type {
|
||||
type Interrupt = crate::interrupt::typelevel::$irq;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -12,14 +12,14 @@ pub(crate) fn regs() -> &'static pac::dppic::RegisterBlock {
|
||||
|
||||
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
|
||||
/// Configure PPI channel to trigger `task` on `event`.
|
||||
pub fn new_one_to_one(ch: impl Peripheral<P = C> + 'd, event: Event, task: Task) -> Self {
|
||||
pub fn new_one_to_one(ch: impl Peripheral<P = C> + 'd, event: Event<'d>, task: Task<'d>) -> Self {
|
||||
Ppi::new_many_to_many(ch, [event], [task])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {
|
||||
/// Configure PPI channel to trigger both `task1` and `task2` on `event`.
|
||||
pub fn new_one_to_two(ch: impl Peripheral<P = C> + 'd, event: Event, task1: Task, task2: Task) -> Self {
|
||||
pub fn new_one_to_two(ch: impl Peripheral<P = C> + 'd, event: Event<'d>, task1: Task<'d>, task2: Task<'d>) -> Self {
|
||||
Ppi::new_many_to_many(ch, [event], [task1, task2])
|
||||
}
|
||||
}
|
||||
@ -30,8 +30,8 @@ impl<'d, C: ConfigurableChannel, const EVENT_COUNT: usize, const TASK_COUNT: usi
|
||||
/// Configure a DPPI channel to trigger all `tasks` when any of the `events` fires.
|
||||
pub fn new_many_to_many(
|
||||
ch: impl Peripheral<P = C> + 'd,
|
||||
events: [Event; EVENT_COUNT],
|
||||
tasks: [Task; TASK_COUNT],
|
||||
events: [Event<'d>; EVENT_COUNT],
|
||||
tasks: [Task<'d>; TASK_COUNT],
|
||||
) -> Self {
|
||||
into_ref!(ch);
|
||||
|
||||
|
@ -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::<u32>();
|
||||
/// When a task is subscribed to a PPI channel, it will run when the channel is triggered by
|
||||
/// a published event.
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub struct Task(NonNull<u32>);
|
||||
pub struct Task<'d>(NonNull<u32>, PhantomData<&'d ()>);
|
||||
|
||||
impl Task {
|
||||
impl<'d> Task<'d> {
|
||||
/// Create a new `Task` from a task register pointer
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `ptr` must be a pointer to a valid `TASKS_*` register from an nRF peripheral.
|
||||
pub unsafe fn new_unchecked(ptr: NonNull<u32>) -> Self {
|
||||
Self(ptr)
|
||||
Self(ptr, PhantomData)
|
||||
}
|
||||
|
||||
/// Triggers this task.
|
||||
@ -143,7 +144,10 @@ impl Task {
|
||||
}
|
||||
|
||||
pub(crate) fn from_reg<T>(reg: &T) -> Self {
|
||||
Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
|
||||
Self(
|
||||
unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) },
|
||||
PhantomData,
|
||||
)
|
||||
}
|
||||
|
||||
/// Address of subscription register for this task.
|
||||
@ -156,26 +160,29 @@ impl Task {
|
||||
/// # Safety
|
||||
///
|
||||
/// NonNull is not send, but this event is only allowed to point at registers and those exist in any context on the same core.
|
||||
unsafe impl Send for Task {}
|
||||
unsafe impl Send for Task<'_> {}
|
||||
|
||||
/// Represents an event that a peripheral can publish.
|
||||
///
|
||||
/// An event can be set to publish on a PPI channel when the event happens.
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub struct Event(NonNull<u32>);
|
||||
pub struct Event<'d>(NonNull<u32>, PhantomData<&'d ()>);
|
||||
|
||||
impl Event {
|
||||
impl<'d> Event<'d> {
|
||||
/// Create a new `Event` from an event register pointer
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `ptr` must be a pointer to a valid `EVENTS_*` register from an nRF peripheral.
|
||||
pub unsafe fn new_unchecked(ptr: NonNull<u32>) -> Self {
|
||||
Self(ptr)
|
||||
Self(ptr, PhantomData)
|
||||
}
|
||||
|
||||
pub(crate) fn from_reg<T>(reg: &T) -> Self {
|
||||
Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
|
||||
pub(crate) fn from_reg<T>(reg: &'d T) -> Self {
|
||||
Self(
|
||||
unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) },
|
||||
PhantomData,
|
||||
)
|
||||
}
|
||||
|
||||
/// Describes whether this Event is currently in a triggered state.
|
||||
@ -198,7 +205,7 @@ impl Event {
|
||||
/// # Safety
|
||||
///
|
||||
/// NonNull is not send, but this event is only allowed to point at registers and those exist in any context on the same core.
|
||||
unsafe impl Send for Event {}
|
||||
unsafe impl Send for Event<'_> {}
|
||||
|
||||
// ======================
|
||||
// traits
|
||||
|
@ -3,12 +3,12 @@ use embassy_hal_common::into_ref;
|
||||
use super::{Channel, ConfigurableChannel, Event, Ppi, StaticChannel, Task};
|
||||
use crate::{pac, Peripheral};
|
||||
|
||||
impl Task {
|
||||
impl<'d> Task<'d> {
|
||||
fn reg_val(&self) -> u32 {
|
||||
self.0.as_ptr() as _
|
||||
}
|
||||
}
|
||||
impl Event {
|
||||
impl<'d> Event<'d> {
|
||||
fn reg_val(&self) -> u32 {
|
||||
self.0.as_ptr() as _
|
||||
}
|
||||
@ -34,7 +34,7 @@ impl<'d, C: StaticChannel> Ppi<'d, C, 0, 1> {
|
||||
|
||||
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
|
||||
/// Configure PPI channel to trigger `task` on `event`.
|
||||
pub fn new_one_to_one(ch: impl Peripheral<P = C> + 'd, event: Event, task: Task) -> Self {
|
||||
pub fn new_one_to_one(ch: impl Peripheral<P = C> + 'd, event: Event<'d>, task: Task<'d>) -> Self {
|
||||
into_ref!(ch);
|
||||
|
||||
let r = regs();
|
||||
@ -49,7 +49,7 @@ impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
|
||||
#[cfg(not(feature = "nrf51"))] // Not for nrf51 because of the fork task
|
||||
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {
|
||||
/// Configure PPI channel to trigger both `task1` and `task2` on `event`.
|
||||
pub fn new_one_to_two(ch: impl Peripheral<P = C> + 'd, event: Event, task1: Task, task2: Task) -> Self {
|
||||
pub fn new_one_to_two(ch: impl Peripheral<P = C> + 'd, event: Event<'d>, task1: Task<'d>, task2: Task<'d>) -> Self {
|
||||
into_ref!(ch);
|
||||
|
||||
let r = regs();
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
||||
|
@ -468,25 +468,19 @@ mod eh1 {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_1::spi::SpiBusFlush for Spim<'d, T> {
|
||||
impl<'d, T: Instance> embedded_hal_1::spi::SpiBus<u8> for Spim<'d, T> {
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_1::spi::SpiBusRead<u8> for Spim<'d, T> {
|
||||
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_transfer(words, &[])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_1::spi::SpiBusWrite<u8> for Spim<'d, T> {
|
||||
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(words)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_1::spi::SpiBus<u8> for Spim<'d, T> {
|
||||
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_transfer(read, write)
|
||||
}
|
||||
@ -502,25 +496,19 @@ mod eha {
|
||||
|
||||
use super::*;
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusFlush for Spim<'d, T> {
|
||||
impl<'d, T: Instance> embedded_hal_async::spi::SpiBus<u8> for Spim<'d, T> {
|
||||
async fn flush(&mut self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusRead<u8> for Spim<'d, T> {
|
||||
async fn read(&mut self, words: &mut [u8]) -> Result<(), Error> {
|
||||
self.read(words).await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusWrite<u8> for Spim<'d, T> {
|
||||
async fn write(&mut self, data: &[u8]) -> Result<(), Error> {
|
||||
self.write(data).await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::spi::SpiBus<u8> for Spim<'d, T> {
|
||||
async fn transfer(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> {
|
||||
self.transfer(rx, tx).await
|
||||
}
|
||||
|
@ -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])
|
||||
}
|
||||
|
||||
|
@ -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" }
|
||||
|
@ -3,22 +3,17 @@ use core::marker::PhantomData;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use embedded_hal_02::adc::{Channel, OneShot};
|
||||
|
||||
use crate::gpio::Pin;
|
||||
use crate::gpio::sealed::Pin as GpioPin;
|
||||
use crate::gpio::{self, AnyPin, Pull};
|
||||
use crate::interrupt::typelevel::Binding;
|
||||
use crate::interrupt::InterruptExt;
|
||||
use crate::peripherals::ADC;
|
||||
use crate::{interrupt, pac, peripherals, Peripheral};
|
||||
static WAKER: AtomicWaker = AtomicWaker::new();
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
// No errors for now
|
||||
}
|
||||
static WAKER: AtomicWaker = AtomicWaker::new();
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct Config {}
|
||||
@ -28,11 +23,75 @@ impl Default for Config {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
pub struct Adc<'d> {
|
||||
phantom: PhantomData<&'d ADC>,
|
||||
|
||||
pub struct Pin<'p> {
|
||||
pin: PeripheralRef<'p, AnyPin>,
|
||||
}
|
||||
|
||||
impl<'d> Adc<'d> {
|
||||
impl<'p> Pin<'p> {
|
||||
pub fn new(pin: impl Peripheral<P = impl AdcPin + 'p> + 'p, pull: Pull) -> Self {
|
||||
into_ref!(pin);
|
||||
pin.pad_ctrl().modify(|w| {
|
||||
// manual says:
|
||||
//
|
||||
// > When using an ADC input shared with a GPIO pin, the 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<P = ADC> + 'd,
|
||||
_irq: impl Binding<interrupt::typelevel::ADC_IRQ_FIFO, InterruptHandler>,
|
||||
_config: Config,
|
||||
) -> Self {
|
||||
fn setup() {
|
||||
let reset = Self::reset();
|
||||
crate::reset::reset(reset);
|
||||
crate::reset::unreset_wait(reset);
|
||||
@ -58,6 +113,43 @@ impl<'d> Adc<'d> {
|
||||
r.cs().write(|w| w.set_en(true));
|
||||
// Wait for ADC ready
|
||||
while !r.cs().read().ready() {}
|
||||
}
|
||||
|
||||
fn sample_blocking(channel: u8) -> Result<u16, Error> {
|
||||
let r = Self::regs();
|
||||
r.cs().modify(|w| {
|
||||
w.set_ainsel(channel);
|
||||
w.set_start_once(true);
|
||||
w.set_err(true);
|
||||
});
|
||||
while !r.cs().read().ready() {}
|
||||
match r.cs().read().err() {
|
||||
true => Err(Error::ConversionFailed),
|
||||
false => Ok(r.result().read().result().into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn blocking_read(&mut self, pin: &mut Pin) -> Result<u16, Error> {
|
||||
Self::sample_blocking(pin.channel())
|
||||
}
|
||||
|
||||
pub fn blocking_read_temperature(&mut self) -> Result<u16, Error> {
|
||||
let r = Self::regs();
|
||||
r.cs().modify(|w| w.set_ts_en(true));
|
||||
while !r.cs().read().ready() {}
|
||||
let result = Self::sample_blocking(4);
|
||||
r.cs().modify(|w| w.set_ts_en(false));
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> Adc<'d, Async> {
|
||||
pub fn new(
|
||||
_inner: impl Peripheral<P = ADC> + 'd,
|
||||
_irq: impl Binding<interrupt::typelevel::ADC_IRQ_FIFO, InterruptHandler>,
|
||||
_config: Config,
|
||||
) -> Self {
|
||||
Self::setup();
|
||||
|
||||
// Setup IRQ
|
||||
interrupt::ADC_IRQ_FIFO.unpend();
|
||||
@ -80,76 +172,42 @@ impl<'d> Adc<'d> {
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn read<PIN: Channel<Adc<'d>, ID = u8> + Pin>(&mut self, pin: &mut PIN) -> u16 {
|
||||
async fn sample_async(channel: u8) -> Result<u16, Error> {
|
||||
let r = Self::regs();
|
||||
// disable pull-down and pull-up resistors
|
||||
// pull-down resistors are enabled by default
|
||||
pin.pad_ctrl().modify(|w| {
|
||||
w.set_ie(true);
|
||||
let (pu, pd) = (false, false);
|
||||
w.set_pue(pu);
|
||||
w.set_pde(pd);
|
||||
});
|
||||
r.cs().modify(|w| {
|
||||
w.set_ainsel(PIN::channel());
|
||||
w.set_start_once(true)
|
||||
w.set_ainsel(channel);
|
||||
w.set_start_once(true);
|
||||
w.set_err(true);
|
||||
});
|
||||
Self::wait_for_ready().await;
|
||||
r.result().read().result().into()
|
||||
match r.cs().read().err() {
|
||||
true => Err(Error::ConversionFailed),
|
||||
false => Ok(r.result().read().result().into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn read_temperature(&mut self) -> u16 {
|
||||
pub async fn read(&mut self, pin: &mut Pin<'_>) -> Result<u16, Error> {
|
||||
Self::sample_async(pin.channel()).await
|
||||
}
|
||||
|
||||
pub async fn read_temperature(&mut self) -> Result<u16, Error> {
|
||||
let r = Self::regs();
|
||||
r.cs().modify(|w| w.set_ts_en(true));
|
||||
if !r.cs().read().ready() {
|
||||
Self::wait_for_ready().await;
|
||||
}
|
||||
r.cs().modify(|w| {
|
||||
w.set_ainsel(4);
|
||||
w.set_start_once(true)
|
||||
});
|
||||
Self::wait_for_ready().await;
|
||||
r.result().read().result().into()
|
||||
}
|
||||
|
||||
pub fn blocking_read<PIN: Channel<Adc<'d>, ID = u8> + Pin>(&mut self, pin: &mut PIN) -> u16 {
|
||||
let r = Self::regs();
|
||||
pin.pad_ctrl().modify(|w| {
|
||||
w.set_ie(true);
|
||||
let (pu, pd) = (false, false);
|
||||
w.set_pue(pu);
|
||||
w.set_pde(pd);
|
||||
});
|
||||
r.cs().modify(|w| {
|
||||
w.set_ainsel(PIN::channel());
|
||||
w.set_start_once(true)
|
||||
});
|
||||
while !r.cs().read().ready() {}
|
||||
r.result().read().result().into()
|
||||
}
|
||||
|
||||
pub fn blocking_read_temperature(&mut self) -> u16 {
|
||||
let r = Self::regs();
|
||||
r.cs().modify(|w| w.set_ts_en(true));
|
||||
while !r.cs().read().ready() {}
|
||||
r.cs().modify(|w| {
|
||||
w.set_ainsel(4);
|
||||
w.set_start_once(true)
|
||||
});
|
||||
while !r.cs().read().ready() {}
|
||||
r.result().read().result().into()
|
||||
let result = Self::sample_async(4).await;
|
||||
r.cs().modify(|w| w.set_ts_en(false));
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_pin {
|
||||
($pin:ident, $channel:expr) => {
|
||||
impl Channel<Adc<'static>> for peripherals::$pin {
|
||||
type ID = u8;
|
||||
fn channel() -> u8 {
|
||||
$channel
|
||||
}
|
||||
}
|
||||
};
|
||||
impl<'d> Adc<'d, Blocking> {
|
||||
pub fn new_blocking(_inner: impl Peripheral<P = ADC> + 'd, _config: Config) -> Self {
|
||||
Self::setup();
|
||||
|
||||
Self { phantom: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InterruptHandler {
|
||||
@ -158,24 +216,33 @@ pub struct InterruptHandler {
|
||||
|
||||
impl interrupt::typelevel::Handler<interrupt::typelevel::ADC_IRQ_FIFO> for InterruptHandler {
|
||||
unsafe fn on_interrupt() {
|
||||
let r = Adc::regs();
|
||||
let r = Adc::<Async>::regs();
|
||||
r.inte().write(|w| w.set_fifo(false));
|
||||
WAKER.wake();
|
||||
}
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
pub trait AdcPin: crate::gpio::sealed::Pin {
|
||||
fn channel(&mut self) -> u8;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AdcPin: sealed::AdcPin + gpio::Pin {}
|
||||
|
||||
macro_rules! impl_pin {
|
||||
($pin:ident, $channel:expr) => {
|
||||
impl sealed::AdcPin for peripherals::$pin {
|
||||
fn channel(&mut self) -> u8 {
|
||||
$channel
|
||||
}
|
||||
}
|
||||
|
||||
impl AdcPin for peripherals::$pin {}
|
||||
};
|
||||
}
|
||||
|
||||
impl_pin!(PIN_26, 0);
|
||||
impl_pin!(PIN_27, 1);
|
||||
impl_pin!(PIN_28, 2);
|
||||
impl_pin!(PIN_29, 3);
|
||||
|
||||
impl<WORD, PIN> OneShot<Adc<'static>, WORD, PIN> for Adc<'static>
|
||||
where
|
||||
WORD: From<u16>,
|
||||
PIN: Channel<Adc<'static>, ID = u8> + Pin,
|
||||
{
|
||||
type Error = ();
|
||||
fn read(&mut self, pin: &mut PIN) -> nb::Result<WORD, Self::Error> {
|
||||
Ok(self.blocking_read(pin).into())
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -41,7 +41,7 @@ impl From<Level> for bool {
|
||||
}
|
||||
|
||||
/// Represents a pull setting for an input.
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum Pull {
|
||||
None,
|
||||
Up,
|
||||
@ -566,13 +566,13 @@ impl<'d, T: Pin> Flex<'d, T> {
|
||||
/// Is the output level high?
|
||||
#[inline]
|
||||
pub fn is_set_high(&self) -> bool {
|
||||
(self.pin.sio_out().value().read() & self.bit()) == 0
|
||||
!self.is_set_low()
|
||||
}
|
||||
|
||||
/// Is the output level low?
|
||||
#[inline]
|
||||
pub fn is_set_low(&self) -> bool {
|
||||
!self.is_set_high()
|
||||
(self.pin.sio_out().value().read() & self.bit()) == 0
|
||||
}
|
||||
|
||||
/// What level output is set to
|
||||
|
@ -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?;
|
||||
}
|
||||
}
|
||||
|
@ -252,7 +252,6 @@ pub fn init(config: config::Config) -> Peripherals {
|
||||
#[cfg(feature = "time-driver")]
|
||||
timer::init();
|
||||
dma::init();
|
||||
pio::init();
|
||||
gpio::init();
|
||||
}
|
||||
|
||||
|
@ -16,12 +16,12 @@ use pio::{SideSet, Wrap};
|
||||
use crate::dma::{Channel, Transfer, Word};
|
||||
use crate::gpio::sealed::Pin as SealedPin;
|
||||
use crate::gpio::{self, AnyPin, Drive, Level, Pull, SlewRate};
|
||||
use crate::interrupt::InterruptExt;
|
||||
use crate::interrupt::typelevel::{Binding, Handler, Interrupt};
|
||||
use crate::pac::dma::vals::TreqSel;
|
||||
use crate::relocate::RelocatedProgram;
|
||||
use crate::{interrupt, pac, peripherals, pio_instr_util, RegExt};
|
||||
use crate::{pac, peripherals, pio_instr_util, RegExt};
|
||||
|
||||
struct Wakers([AtomicWaker; 12]);
|
||||
pub struct Wakers([AtomicWaker; 12]);
|
||||
|
||||
impl Wakers {
|
||||
#[inline(always)]
|
||||
@ -38,10 +38,6 @@ impl Wakers {
|
||||
}
|
||||
}
|
||||
|
||||
const NEW_AW: AtomicWaker = AtomicWaker::new();
|
||||
const PIO_WAKERS_INIT: Wakers = Wakers([NEW_AW; 12]);
|
||||
static WAKERS: [Wakers; 2] = [PIO_WAKERS_INIT; 2];
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[repr(u8)]
|
||||
@ -85,42 +81,20 @@ const RXNEMPTY_MASK: u32 = 1 << 0;
|
||||
const TXNFULL_MASK: u32 = 1 << 4;
|
||||
const SMIRQ_MASK: u32 = 1 << 8;
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
#[interrupt]
|
||||
fn PIO0_IRQ_0() {
|
||||
use crate::pac;
|
||||
let ints = pac::PIO0.irqs(0).ints().read().0;
|
||||
for bit in 0..12 {
|
||||
if ints & (1 << bit) != 0 {
|
||||
WAKERS[0].0[bit].wake();
|
||||
}
|
||||
}
|
||||
pac::PIO0.irqs(0).inte().write_clear(|m| m.0 = ints);
|
||||
pub struct InterruptHandler<PIO: Instance> {
|
||||
_pio: PhantomData<PIO>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "rt")]
|
||||
#[interrupt]
|
||||
fn PIO1_IRQ_0() {
|
||||
use crate::pac;
|
||||
let ints = pac::PIO1.irqs(0).ints().read().0;
|
||||
for bit in 0..12 {
|
||||
if ints & (1 << bit) != 0 {
|
||||
WAKERS[1].0[bit].wake();
|
||||
impl<PIO: Instance> Handler<PIO::Interrupt> for InterruptHandler<PIO> {
|
||||
unsafe fn on_interrupt() {
|
||||
let ints = PIO::PIO.irqs(0).ints().read().0;
|
||||
for bit in 0..12 {
|
||||
if ints & (1 << bit) != 0 {
|
||||
PIO::wakers().0[bit].wake();
|
||||
}
|
||||
}
|
||||
PIO::PIO.irqs(0).inte().write_clear(|m| m.0 = ints);
|
||||
}
|
||||
pac::PIO1.irqs(0).inte().write_clear(|m| m.0 = ints);
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init() {
|
||||
interrupt::PIO0_IRQ_0.disable();
|
||||
interrupt::PIO0_IRQ_0.set_priority(interrupt::Priority::P3);
|
||||
pac::PIO0.irqs(0).inte().write(|m| m.0 = 0);
|
||||
interrupt::PIO0_IRQ_0.enable();
|
||||
|
||||
interrupt::PIO1_IRQ_0.disable();
|
||||
interrupt::PIO1_IRQ_0.set_priority(interrupt::Priority::P3);
|
||||
pac::PIO1.irqs(0).inte().write(|m| m.0 = 0);
|
||||
interrupt::PIO1_IRQ_0.enable();
|
||||
}
|
||||
|
||||
/// Future that waits for TX-FIFO to become writable
|
||||
@ -144,7 +118,7 @@ impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoOutFuture<'a, 'd, PI
|
||||
if self.get_mut().sm_tx.try_push(value) {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
WAKERS[PIO::PIO_NO as usize].fifo_out()[SM].register(cx.waker());
|
||||
PIO::wakers().fifo_out()[SM].register(cx.waker());
|
||||
PIO::PIO.irqs(0).inte().write_set(|m| {
|
||||
m.0 = TXNFULL_MASK << SM;
|
||||
});
|
||||
@ -181,7 +155,7 @@ impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoInFuture<'a, 'd, PIO
|
||||
if let Some(v) = self.sm_rx.try_pull() {
|
||||
Poll::Ready(v)
|
||||
} else {
|
||||
WAKERS[PIO::PIO_NO as usize].fifo_in()[SM].register(cx.waker());
|
||||
PIO::wakers().fifo_in()[SM].register(cx.waker());
|
||||
PIO::PIO.irqs(0).inte().write_set(|m| {
|
||||
m.0 = RXNEMPTY_MASK << SM;
|
||||
});
|
||||
@ -217,7 +191,7 @@ impl<'a, 'd, PIO: Instance> Future for IrqFuture<'a, 'd, PIO> {
|
||||
return Poll::Ready(());
|
||||
}
|
||||
|
||||
WAKERS[PIO::PIO_NO as usize].irq()[self.irq_no as usize].register(cx.waker());
|
||||
PIO::wakers().irq()[self.irq_no as usize].register(cx.waker());
|
||||
PIO::PIO.irqs(0).inte().write_set(|m| {
|
||||
m.0 = SMIRQ_MASK << self.irq_no;
|
||||
});
|
||||
@ -949,9 +923,11 @@ pub struct Pio<'d, PIO: Instance> {
|
||||
}
|
||||
|
||||
impl<'d, PIO: Instance> Pio<'d, PIO> {
|
||||
pub fn new(_pio: impl Peripheral<P = PIO> + 'd) -> Self {
|
||||
pub fn new(_pio: impl Peripheral<P = PIO> + 'd, _irq: impl Binding<PIO::Interrupt, InterruptHandler<PIO>>) -> Self {
|
||||
PIO::state().users.store(5, Ordering::Release);
|
||||
PIO::state().used_pins.store(0, Ordering::Release);
|
||||
PIO::Interrupt::unpend();
|
||||
unsafe { PIO::Interrupt::enable() };
|
||||
Self {
|
||||
common: Common {
|
||||
instructions_used: 0,
|
||||
@ -1017,6 +993,15 @@ mod sealed {
|
||||
const PIO_NO: u8;
|
||||
const PIO: &'static crate::pac::pio::Pio;
|
||||
const FUNCSEL: crate::pac::io::vals::Gpio0ctrlFuncsel;
|
||||
type Interrupt: crate::interrupt::typelevel::Interrupt;
|
||||
|
||||
#[inline]
|
||||
fn wakers() -> &'static Wakers {
|
||||
const NEW_AW: AtomicWaker = AtomicWaker::new();
|
||||
static WAKERS: Wakers = Wakers([NEW_AW; 12]);
|
||||
|
||||
&WAKERS
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn state() -> &'static State {
|
||||
@ -1033,18 +1018,19 @@ mod sealed {
|
||||
pub trait Instance: sealed::Instance + Sized + Unpin {}
|
||||
|
||||
macro_rules! impl_pio {
|
||||
($name:ident, $pio:expr, $pac:ident, $funcsel:ident) => {
|
||||
($name:ident, $pio:expr, $pac:ident, $funcsel:ident, $irq:ident) => {
|
||||
impl sealed::Instance for peripherals::$name {
|
||||
const PIO_NO: u8 = $pio;
|
||||
const PIO: &'static pac::pio::Pio = &pac::$pac;
|
||||
const FUNCSEL: pac::io::vals::Gpio0ctrlFuncsel = pac::io::vals::Gpio0ctrlFuncsel::$funcsel;
|
||||
type Interrupt = crate::interrupt::typelevel::$irq;
|
||||
}
|
||||
impl Instance for peripherals::$name {}
|
||||
};
|
||||
}
|
||||
|
||||
impl_pio!(PIO0, 0, PIO0, PIO0_0);
|
||||
impl_pio!(PIO1, 1, PIO1, PIO1_0);
|
||||
impl_pio!(PIO0, 0, PIO0, PIO0_0, PIO0_IRQ_0);
|
||||
impl_pio!(PIO1, 1, PIO1, PIO1_0, PIO1_IRQ_0);
|
||||
|
||||
pub trait PioPin: sealed::PioPin + gpio::Pin {}
|
||||
|
||||
|
@ -25,6 +25,7 @@ pub enum Error {
|
||||
}
|
||||
|
||||
/// Structure containing date and time information
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DateTime {
|
||||
/// 0..4095
|
||||
pub year: u16,
|
||||
|
@ -12,26 +12,24 @@ pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError};
|
||||
use crate::clocks::clk_rtc_freq;
|
||||
|
||||
/// A reference to the real time clock of the system
|
||||
pub struct RealTimeClock<'d, T: Instance> {
|
||||
pub struct Rtc<'d, T: Instance> {
|
||||
inner: PeripheralRef<'d, T>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> RealTimeClock<'d, T> {
|
||||
impl<'d, T: Instance> Rtc<'d, T> {
|
||||
/// Create a new instance of the real time clock, with the given date as an initial value.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.
|
||||
pub fn new(inner: impl Peripheral<P = T> + 'd, initial_date: DateTime) -> Result<Self, RtcError> {
|
||||
pub fn new(inner: impl Peripheral<P = T> + 'd) -> Self {
|
||||
into_ref!(inner);
|
||||
|
||||
// Set the RTC divider
|
||||
inner.regs().clkdiv_m1().write(|w| w.set_clkdiv_m1(clk_rtc_freq() - 1));
|
||||
|
||||
let mut result = Self { inner };
|
||||
result.set_leap_year_check(true); // should be on by default, make sure this is the case.
|
||||
result.set_datetime(initial_date)?;
|
||||
Ok(result)
|
||||
let result = Self { inner };
|
||||
result
|
||||
}
|
||||
|
||||
/// Enable or disable the leap year check. The rp2040 chip will always add a Feb 29th on every year that is divisable by 4, but this may be incorrect (e.g. on century years). This function allows you to disable this check.
|
||||
@ -43,7 +41,37 @@ impl<'d, T: Instance> RealTimeClock<'d, T> {
|
||||
});
|
||||
}
|
||||
|
||||
/// Checks to see if this RealTimeClock is running
|
||||
/// Set the time from internal format
|
||||
pub fn restore(&mut self, ymd: rp_pac::rtc::regs::Rtc1, hms: rp_pac::rtc::regs::Rtc0) {
|
||||
// disable RTC while we configure it
|
||||
self.inner.regs().ctrl().modify(|w| w.set_rtc_enable(false));
|
||||
while self.inner.regs().ctrl().read().rtc_active() {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
self.inner.regs().setup_0().write(|w| {
|
||||
*w = rp_pac::rtc::regs::Setup0(ymd.0);
|
||||
});
|
||||
self.inner.regs().setup_1().write(|w| {
|
||||
*w = rp_pac::rtc::regs::Setup1(hms.0);
|
||||
});
|
||||
|
||||
// Load the new datetime and re-enable RTC
|
||||
self.inner.regs().ctrl().write(|w| w.set_load(true));
|
||||
self.inner.regs().ctrl().write(|w| w.set_rtc_enable(true));
|
||||
while !self.inner.regs().ctrl().read().rtc_active() {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the time in internal format
|
||||
pub fn save(&mut self) -> (rp_pac::rtc::regs::Rtc1, rp_pac::rtc::regs::Rtc0) {
|
||||
let rtc_0: rp_pac::rtc::regs::Rtc0 = self.inner.regs().rtc_0().read();
|
||||
let rtc_1 = self.inner.regs().rtc_1().read();
|
||||
(rtc_1, rtc_0)
|
||||
}
|
||||
|
||||
/// Checks to see if this Rtc is running
|
||||
pub fn is_running(&self) -> bool {
|
||||
self.inner.regs().ctrl().read().rtc_active()
|
||||
}
|
||||
@ -113,8 +141,8 @@ impl<'d, T: Instance> RealTimeClock<'d, T> {
|
||||
/// # fn main() { }
|
||||
/// # #[cfg(not(feature = "chrono"))]
|
||||
/// # fn main() {
|
||||
/// # use embassy_rp::rtc::{RealTimeClock, DateTimeFilter};
|
||||
/// # let mut real_time_clock: RealTimeClock<embassy_rp::peripherals::RTC> = unsafe { core::mem::zeroed() };
|
||||
/// # use embassy_rp::rtc::{Rtc, DateTimeFilter};
|
||||
/// # let mut real_time_clock: Rtc<embassy_rp::peripherals::RTC> = unsafe { core::mem::zeroed() };
|
||||
/// let now = real_time_clock.now().unwrap();
|
||||
/// real_time_clock.schedule_alarm(
|
||||
/// DateTimeFilter::default()
|
||||
@ -150,7 +178,7 @@ impl<'d, T: Instance> RealTimeClock<'d, T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Errors that can occur on methods on [RealTimeClock]
|
||||
/// Errors that can occur on methods on [Rtc]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum RtcError {
|
||||
/// An invalid DateTime was given or stored on the hardware.
|
||||
|
@ -545,25 +545,19 @@ mod eh1 {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBusFlush for Spi<'d, T, M> {
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBus<u8> for Spi<'d, T, M> {
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBusRead<u8> for Spi<'d, T, M> {
|
||||
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_transfer(words, &[])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBusWrite<u8> for Spi<'d, T, M> {
|
||||
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(words)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBus<u8> for Spi<'d, T, M> {
|
||||
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_transfer(read, write)
|
||||
}
|
||||
@ -578,30 +572,24 @@ mod eh1 {
|
||||
mod eha {
|
||||
use super::*;
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusFlush for Spi<'d, T, Async> {
|
||||
impl<'d, T: Instance> embedded_hal_async::spi::SpiBus<u8> for Spi<'d, T, Async> {
|
||||
async fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusWrite<u8> for Spi<'d, T, Async> {
|
||||
async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
|
||||
self.write(words).await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusRead<u8> for Spi<'d, T, Async> {
|
||||
async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.read(words).await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::spi::SpiBus<u8> for Spi<'d, T, Async> {
|
||||
async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> {
|
||||
async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
|
||||
self.transfer(read, write).await
|
||||
}
|
||||
|
||||
async fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Result<(), Self::Error> {
|
||||
async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.transfer_in_place(words).await
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,18 +5,19 @@ edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-wpan-v$VERSION/embassy-stm32-wpan/src"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32-wpan/src"
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-wpan-v$VERSION/embassy-stm32-wpan/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32-wpan/src/"
|
||||
target = "thumbv7em-none-eabihf"
|
||||
features = ["stm32wb55rg"]
|
||||
|
||||
[dependencies]
|
||||
embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32" }
|
||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
||||
embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true }
|
||||
embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true }
|
||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||
embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" }
|
||||
embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" }
|
||||
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver", optional=true }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
cortex-m = "0.7.6"
|
||||
@ -25,13 +26,15 @@ aligned = "0.4.1"
|
||||
|
||||
bit_field = "0.10.2"
|
||||
stm32-device-signature = { version = "0.3.3", features = ["stm32wb5x"] }
|
||||
stm32wb-hci = { version = "0.1.2", features = ["version-5-0"], optional = true }
|
||||
stm32wb-hci = { version = "0.1.3", optional = true }
|
||||
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
|
||||
bitflags = { version = "2.3.3", optional = true }
|
||||
|
||||
[features]
|
||||
defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt"]
|
||||
defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "stm32wb-hci?/defmt"]
|
||||
|
||||
ble = ["dep:stm32wb-hci"]
|
||||
mac = []
|
||||
mac = ["dep:bitflags", "dep:embassy-net-driver" ]
|
||||
|
||||
stm32wb10cc = [ "embassy-stm32/stm32wb10cc" ]
|
||||
stm32wb15cc = [ "embassy-stm32/stm32wb15cc" ]
|
||||
@ -48,4 +51,4 @@ stm32wb55rg = [ "embassy-stm32/stm32wb55rg" ]
|
||||
stm32wb55vc = [ "embassy-stm32/stm32wb55vc" ]
|
||||
stm32wb55ve = [ "embassy-stm32/stm32wb55ve" ]
|
||||
stm32wb55vg = [ "embassy-stm32/stm32wb55vg" ]
|
||||
stm32wb55vy = [ "embassy-stm32/stm32wb55vy" ]
|
||||
stm32wb55vy = [ "embassy-stm32/stm32wb55vy" ]
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
|
476
embassy-stm32-wpan/src/mac/commands.rs
Normal file
476
embassy-stm32-wpan/src/mac/commands.rs
Normal file
@ -0,0 +1,476 @@
|
||||
use core::{mem, slice};
|
||||
|
||||
use super::opcodes::OpcodeM4ToM0;
|
||||
use super::typedefs::{
|
||||
AddressMode, Capabilities, DisassociationReason, GtsCharacteristics, KeyIdMode, MacAddress, MacChannel, MacStatus,
|
||||
PanId, PibId, ScanType, SecurityLevel,
|
||||
};
|
||||
|
||||
pub trait MacCommand: Sized {
|
||||
const OPCODE: OpcodeM4ToM0;
|
||||
|
||||
fn payload<'a>(&'a self) -> &'a [u8] {
|
||||
unsafe { slice::from_raw_parts(self as *const _ as *const u8, mem::size_of::<Self>()) }
|
||||
}
|
||||
}
|
||||
|
||||
/// MLME ASSOCIATE Request used to request an association
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct AssociateRequest {
|
||||
/// the logical channel on which to attempt association
|
||||
pub channel_number: MacChannel,
|
||||
/// the channel page on which to attempt association
|
||||
pub channel_page: u8,
|
||||
/// coordinator addressing mode
|
||||
pub coord_addr_mode: AddressMode,
|
||||
/// operational capabilities of the associating device
|
||||
pub capability_information: Capabilities,
|
||||
/// the identifier of the PAN with which to associate
|
||||
pub coord_pan_id: PanId,
|
||||
/// the security level to be used
|
||||
pub security_level: SecurityLevel,
|
||||
/// the mode used to identify the key to be used
|
||||
pub key_id_mode: KeyIdMode,
|
||||
/// the originator of the key to be used
|
||||
pub key_source: [u8; 8],
|
||||
/// Coordinator address
|
||||
pub coord_address: MacAddress,
|
||||
/// the index of the key to be used
|
||||
pub key_index: u8,
|
||||
}
|
||||
|
||||
impl MacCommand for AssociateRequest {
|
||||
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeAssociateReq;
|
||||
}
|
||||
|
||||
/// MLME DISASSOCIATE Request sed to request a disassociation
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct DisassociateRequest {
|
||||
/// device addressing mode used
|
||||
pub device_addr_mode: AddressMode,
|
||||
/// the identifier of the PAN of the device
|
||||
pub device_pan_id: PanId,
|
||||
/// the reason for the disassociation
|
||||
pub disassociation_reason: DisassociationReason,
|
||||
/// device address
|
||||
pub device_address: MacAddress,
|
||||
/// `true` if the disassociation notification command is to be sent indirectly
|
||||
pub tx_indirect: bool,
|
||||
/// the security level to be used
|
||||
pub security_level: SecurityLevel,
|
||||
/// the mode to be used to indetify the key to be used
|
||||
pub key_id_mode: KeyIdMode,
|
||||
/// the index of the key to be used
|
||||
pub key_index: u8,
|
||||
/// the originator of the key to be used
|
||||
pub key_source: [u8; 8],
|
||||
}
|
||||
|
||||
impl MacCommand for DisassociateRequest {
|
||||
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeDisassociateReq;
|
||||
}
|
||||
|
||||
/// MLME GET Request used to request a PIB value
|
||||
#[repr(C)]
|
||||
#[derive(Default)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct GetRequest {
|
||||
/// the name of the PIB attribute to read
|
||||
pub pib_attribute: PibId,
|
||||
|
||||
/// byte stuffing to keep 32 bit alignment
|
||||
pub a_stuffing: [u8; 3],
|
||||
}
|
||||
|
||||
impl MacCommand for GetRequest {
|
||||
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeGetReq;
|
||||
}
|
||||
|
||||
/// MLME GTS Request used to request and maintain GTSs
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct GtsRequest {
|
||||
/// the characteristics of the GTS
|
||||
pub characteristics: GtsCharacteristics,
|
||||
/// the security level to be used
|
||||
pub security_level: SecurityLevel,
|
||||
/// the mode used to identify the key to be used
|
||||
pub key_id_mode: KeyIdMode,
|
||||
/// the index of the key to be used
|
||||
pub key_index: u8,
|
||||
/// the originator of the key to be used
|
||||
pub key_source: [u8; 8],
|
||||
}
|
||||
|
||||
impl MacCommand for GtsRequest {
|
||||
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeGetReq;
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct ResetRequest {
|
||||
/// MAC PIB attributes are set to their default values or not during reset
|
||||
pub set_default_pib: bool,
|
||||
/// byte stuffing to keep 32 bit alignment
|
||||
pub a_stuffing: [u8; 3],
|
||||
}
|
||||
|
||||
impl MacCommand for ResetRequest {
|
||||
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeResetReq;
|
||||
}
|
||||
|
||||
/// MLME RX ENABLE Request used to request that the receiver is either enabled
|
||||
/// for a finite period of time or disabled
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct RxEnableRequest {
|
||||
/// the request operation can be deferred or not
|
||||
pub defer_permit: bool,
|
||||
/// configure the transceiver to RX with ranging for a value of
|
||||
/// RANGING_ON or to not enable ranging for RANGING_OFF
|
||||
pub ranging_rx_control: u8,
|
||||
/// byte stuffing to keep 32 bit alignment
|
||||
pub a_stuffing: [u8; 2],
|
||||
/// number of symbols measured before the receiver is to be enabled or disabled
|
||||
pub rx_on_time: [u8; 4],
|
||||
/// number of symbols for which the receiver is to be enabled
|
||||
pub rx_on_duration: [u8; 4],
|
||||
}
|
||||
|
||||
impl MacCommand for RxEnableRequest {
|
||||
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeRxEnableReq;
|
||||
}
|
||||
|
||||
/// MLME SCAN Request used to initiate a channel scan over a given list of channels
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct ScanRequest {
|
||||
/// the type of scan to be performed
|
||||
pub scan_type: ScanType,
|
||||
/// the time spent on scanning each channel
|
||||
pub scan_duration: u8,
|
||||
/// channel page on which to perform the scan
|
||||
pub channel_page: u8,
|
||||
/// security level to be used
|
||||
pub security_level: SecurityLevel,
|
||||
/// indicate which channels are to be scanned
|
||||
pub scan_channels: [u8; 4],
|
||||
/// originator the key to be used
|
||||
pub key_source: [u8; 8],
|
||||
/// mode used to identify the key to be used
|
||||
pub key_id_mode: KeyIdMode,
|
||||
/// index of the key to be used
|
||||
pub key_index: u8,
|
||||
/// byte stuffing to keep 32 bit alignment
|
||||
pub a_stuffing: [u8; 2],
|
||||
}
|
||||
|
||||
impl MacCommand for ScanRequest {
|
||||
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeScanReq;
|
||||
}
|
||||
|
||||
/// MLME SET Request used to attempt to write the given value to the indicated PIB attribute
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct SetRequest {
|
||||
/// the pointer to the value of the PIB attribute to set
|
||||
pub pib_attribute_ptr: *const u8,
|
||||
/// the name of the PIB attribute to set
|
||||
pub pib_attribute: PibId,
|
||||
}
|
||||
|
||||
impl MacCommand for SetRequest {
|
||||
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSetReq;
|
||||
}
|
||||
|
||||
/// MLME START Request used by the FFDs to intiate a new PAN or to begin using a new superframe
|
||||
/// configuration
|
||||
#[repr(C)]
|
||||
#[derive(Default)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct StartRequest {
|
||||
/// PAN indentifier to used by the device
|
||||
pub pan_id: PanId,
|
||||
/// logical channel on which to begin
|
||||
pub channel_number: MacChannel,
|
||||
/// channel page on which to begin
|
||||
pub channel_page: u8,
|
||||
/// time at which to begin transmitting beacons
|
||||
pub start_time: [u8; 4],
|
||||
/// indicated how often the beacon is to be transmitted
|
||||
pub beacon_order: u8,
|
||||
/// length of the active portion of the superframe
|
||||
pub superframe_order: u8,
|
||||
/// indicated wheter the device is a PAN coordinator or not
|
||||
pub pan_coordinator: bool,
|
||||
/// indicates if the receiver of the beaconing device is disabled or not
|
||||
pub battery_life_extension: bool,
|
||||
/// indicated if the coordinator realignment command is to be trasmitted
|
||||
pub coord_realignment: u8,
|
||||
/// indicated if the coordinator realignment command is to be trasmitted
|
||||
pub coord_realign_security_level: SecurityLevel,
|
||||
/// index of the key to be used
|
||||
pub coord_realign_key_id_index: u8,
|
||||
/// originator of the key to be used
|
||||
pub coord_realign_key_source: [u8; 8],
|
||||
/// security level to be used for beacon frames
|
||||
pub beacon_security_level: SecurityLevel,
|
||||
/// mode used to identify the key to be used
|
||||
pub beacon_key_id_mode: KeyIdMode,
|
||||
/// index of the key to be used
|
||||
pub beacon_key_index: u8,
|
||||
/// originator of the key to be used
|
||||
pub beacon_key_source: [u8; 8],
|
||||
}
|
||||
|
||||
impl MacCommand for StartRequest {
|
||||
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeStartReq;
|
||||
}
|
||||
|
||||
/// MLME SYNC Request used to synchronize with the coordinator by acquiring and, if
|
||||
/// specified, tracking its beacons
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct SyncRequest {
|
||||
/// the channel number on which to attempt coordinator synchronization
|
||||
pub channel_number: MacChannel,
|
||||
/// the channel page on which to attempt coordinator synchronization
|
||||
pub channel_page: u8,
|
||||
/// `true` if the MLME is to synchronize with the next beacon and attempts
|
||||
/// to track all future beacons.
|
||||
///
|
||||
/// `false` if the MLME is to synchronize with only the next beacon
|
||||
pub track_beacon: bool,
|
||||
/// byte stuffing to keep 32 bit alignment
|
||||
pub a_stuffing: [u8; 1],
|
||||
}
|
||||
|
||||
impl MacCommand for SyncRequest {
|
||||
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSyncReq;
|
||||
}
|
||||
|
||||
/// MLME POLL Request propmts the device to request data from the coordinator
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct PollRequest {
|
||||
/// addressing mode of the coordinator
|
||||
pub coord_addr_mode: AddressMode,
|
||||
/// security level to be used
|
||||
pub security_level: SecurityLevel,
|
||||
/// mode used to identify the key to be used
|
||||
pub key_id_mode: KeyIdMode,
|
||||
/// index of the key to be used
|
||||
pub key_index: u8,
|
||||
/// coordinator address
|
||||
pub coord_address: MacAddress,
|
||||
/// originator of the key to be used
|
||||
pub key_source: [u8; 8],
|
||||
/// PAN identifier of the coordinator
|
||||
pub coord_pan_id: PanId,
|
||||
/// byte stuffing to keep 32 bit alignment
|
||||
pub a_stuffing: [u8; 2],
|
||||
}
|
||||
|
||||
impl MacCommand for PollRequest {
|
||||
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmePollReq;
|
||||
}
|
||||
|
||||
/// MLME DPS Request allows the next higher layer to request that the PHY utilize a
|
||||
/// given pair of preamble codes for a single use pending expiration of the DPSIndexDuration
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct DpsRequest {
|
||||
/// the index value for the transmitter
|
||||
tx_dps_index: u8,
|
||||
/// the index value of the receiver
|
||||
rx_dps_index: u8,
|
||||
/// the number of symbols for which the transmitter and receiver will utilize the
|
||||
/// respective DPS indices
|
||||
dps_index_duration: u8,
|
||||
/// byte stuffing to keep 32 bit alignment
|
||||
pub a_stuffing: [u8; 1],
|
||||
}
|
||||
|
||||
impl MacCommand for DpsRequest {
|
||||
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeDpsReq;
|
||||
}
|
||||
|
||||
/// MLME SOUNDING request primitive which is used by the next higher layer to request that
|
||||
/// the PHY respond with channel sounding information
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct SoundingRequest {
|
||||
/// byte stuffing to keep 32 bit alignment
|
||||
pub a_stuffing: [u8; 4],
|
||||
}
|
||||
|
||||
impl MacCommand for SoundingRequest {
|
||||
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeSoundingReq;
|
||||
}
|
||||
|
||||
/// MLME CALIBRATE request primitive which used to obtain the results of a ranging
|
||||
/// calibration request from an RDEV
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct CalibrateRequest {
|
||||
/// byte stuffing to keep 32 bit alignment
|
||||
pub a_stuffing: [u8; 4],
|
||||
}
|
||||
|
||||
impl MacCommand for CalibrateRequest {
|
||||
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeCalibrateReq;
|
||||
}
|
||||
|
||||
/// MCPS DATA Request used for MAC data related requests from the application
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct DataRequest {
|
||||
/// the handle assocated with the MSDU to be transmitted
|
||||
pub msdu_ptr: *const u8,
|
||||
/// source addressing mode used
|
||||
pub src_addr_mode: AddressMode,
|
||||
/// destination addressing mode used
|
||||
pub dst_addr_mode: AddressMode,
|
||||
/// destination PAN Id
|
||||
pub dst_pan_id: PanId,
|
||||
/// destination address
|
||||
pub dst_address: MacAddress,
|
||||
/// the number of octets contained in the MSDU
|
||||
pub msdu_length: u8,
|
||||
/// the handle assocated with the MSDU to be transmitted
|
||||
pub msdu_handle: u8,
|
||||
/// the ACK transmittion options for the MSDU
|
||||
pub ack_tx: u8,
|
||||
/// `true` if a GTS is to be used for transmission
|
||||
///
|
||||
/// `false` indicates that the CAP will be used
|
||||
pub gts_tx: bool,
|
||||
/// the pending bit transmission options for the MSDU
|
||||
pub indirect_tx: u8,
|
||||
/// the security level to be used
|
||||
pub security_level: SecurityLevel,
|
||||
/// the mode used to indentify the key to be used
|
||||
pub key_id_mode: KeyIdMode,
|
||||
/// the index of the key to be used
|
||||
pub key_index: u8,
|
||||
/// the originator of the key to be used
|
||||
pub key_source: [u8; 8],
|
||||
/// 2011 - the pulse repitition value
|
||||
pub uwbprf: u8,
|
||||
/// 2011 - the ranging configuration
|
||||
pub ranging: u8,
|
||||
/// 2011 - the preamble symbol repititions
|
||||
pub uwb_preamble_symbol_repetitions: u8,
|
||||
/// 2011 - indicates the data rate
|
||||
pub datrate: u8,
|
||||
}
|
||||
|
||||
impl DataRequest {
|
||||
pub fn set_buffer<'a>(&'a mut self, buf: &'a [u8]) -> &mut Self {
|
||||
self.msdu_ptr = buf as *const _ as *const u8;
|
||||
self.msdu_length = buf.len() as u8;
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DataRequest {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
msdu_ptr: 0 as *const u8,
|
||||
src_addr_mode: AddressMode::NoAddress,
|
||||
dst_addr_mode: AddressMode::NoAddress,
|
||||
dst_pan_id: PanId([0, 0]),
|
||||
dst_address: MacAddress { short: [0, 0] },
|
||||
msdu_length: 0,
|
||||
msdu_handle: 0,
|
||||
ack_tx: 0,
|
||||
gts_tx: false,
|
||||
indirect_tx: 0,
|
||||
security_level: SecurityLevel::Unsecure,
|
||||
key_id_mode: KeyIdMode::Implicite,
|
||||
key_index: 0,
|
||||
key_source: [0u8; 8],
|
||||
uwbprf: 0,
|
||||
ranging: 0,
|
||||
uwb_preamble_symbol_repetitions: 0,
|
||||
datrate: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MacCommand for DataRequest {
|
||||
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::McpsDataReq;
|
||||
}
|
||||
|
||||
/// for MCPS PURGE Request used to purge an MSDU from the transaction queue
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct PurgeRequest {
|
||||
/// the handle associated with the MSDU to be purged from the transaction
|
||||
/// queue
|
||||
pub msdu_handle: u8,
|
||||
/// byte stuffing to keep 32 bit alignment
|
||||
pub a_stuffing: [u8; 3],
|
||||
}
|
||||
|
||||
impl MacCommand for PurgeRequest {
|
||||
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::McpsPurgeReq;
|
||||
}
|
||||
|
||||
/// MLME ASSOCIATE Response used to initiate a response to an MLME-ASSOCIATE.indication
|
||||
#[repr(C)]
|
||||
#[derive(Default)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct AssociateResponse {
|
||||
/// extended address of the device requesting association
|
||||
pub device_address: [u8; 8],
|
||||
/// 16-bitshort device address allocated by the coordinator on successful
|
||||
/// association
|
||||
pub assoc_short_address: [u8; 2],
|
||||
/// status of the association attempt
|
||||
pub status: MacStatus,
|
||||
/// security level to be used
|
||||
pub security_level: SecurityLevel,
|
||||
/// the originator of the key to be used
|
||||
pub key_source: [u8; 8],
|
||||
/// the mode used to identify the key to be used
|
||||
pub key_id_mode: KeyIdMode,
|
||||
/// the index of the key to be used
|
||||
pub key_index: u8,
|
||||
/// byte stuffing to keep 32 bit alignment
|
||||
pub a_stuffing: [u8; 2],
|
||||
}
|
||||
|
||||
impl MacCommand for AssociateResponse {
|
||||
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeAssociateRes;
|
||||
}
|
||||
|
||||
/// MLME ORPHAN Response used to respond to the MLME ORPHAN Indication
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct OrphanResponse {
|
||||
/// extended address of the orphaned device
|
||||
pub orphan_address: [u8; 8],
|
||||
/// short address allocated to the orphaned device
|
||||
pub short_address: [u8; 2],
|
||||
/// if the orphaned device is associated with coordinator or not
|
||||
pub associated_member: bool,
|
||||
/// security level to be used
|
||||
pub security_level: SecurityLevel,
|
||||
/// the originator of the key to be used
|
||||
pub key_source: [u8; 8],
|
||||
/// the mode used to identify the key to be used
|
||||
pub key_id_mode: KeyIdMode,
|
||||
/// the index of the key to be used
|
||||
pub key_index: u8,
|
||||
/// byte stuffing to keep 32 bit alignment
|
||||
pub a_stuffing: [u8; 2],
|
||||
}
|
||||
|
||||
impl MacCommand for OrphanResponse {
|
||||
const OPCODE: OpcodeM4ToM0 = OpcodeM4ToM0::MlmeOrphanRes;
|
||||
}
|
4
embassy-stm32-wpan/src/mac/consts.rs
Normal file
4
embassy-stm32-wpan/src/mac/consts.rs
Normal file
@ -0,0 +1,4 @@
|
||||
pub const MAX_PAN_DESC_SUPPORTED: usize = 6;
|
||||
pub const MAX_SOUNDING_LIST_SUPPORTED: usize = 6;
|
||||
pub const MAX_PENDING_ADDRESS: usize = 7;
|
||||
pub const MAX_ED_SCAN_RESULTS_SUPPORTED: usize = 16;
|
95
embassy-stm32-wpan/src/mac/control.rs
Normal file
95
embassy-stm32-wpan/src/mac/control.rs
Normal file
@ -0,0 +1,95 @@
|
||||
use core::future::Future;
|
||||
use core::task;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::mutex::MutexGuard;
|
||||
use embassy_sync::signal::Signal;
|
||||
use futures::FutureExt;
|
||||
|
||||
use super::commands::MacCommand;
|
||||
use super::event::MacEvent;
|
||||
use super::typedefs::MacError;
|
||||
use crate::mac::runner::Runner;
|
||||
|
||||
pub struct Control<'a> {
|
||||
runner: &'a Runner<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Control<'a> {
|
||||
pub(crate) fn new(runner: &'a Runner<'a>) -> Self {
|
||||
Self { runner: runner }
|
||||
}
|
||||
|
||||
pub async fn send_command<T>(&self, cmd: &T) -> Result<(), MacError>
|
||||
where
|
||||
T: MacCommand,
|
||||
{
|
||||
let _wm = self.runner.write_mutex.lock().await;
|
||||
|
||||
self.runner.mac_subsystem.send_command(cmd).await
|
||||
}
|
||||
|
||||
pub async fn send_command_and_get_response<T>(&self, cmd: &T) -> Result<EventToken<'a>, MacError>
|
||||
where
|
||||
T: MacCommand,
|
||||
{
|
||||
let rm = self.runner.read_mutex.lock().await;
|
||||
let _wm = self.runner.write_mutex.lock().await;
|
||||
let token = EventToken::new(self.runner, rm);
|
||||
|
||||
self.runner.mac_subsystem.send_command(cmd).await?;
|
||||
|
||||
Ok(token)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventToken<'a> {
|
||||
runner: &'a Runner<'a>,
|
||||
_mutex_guard: MutexGuard<'a, CriticalSectionRawMutex, ()>,
|
||||
}
|
||||
|
||||
impl<'a> EventToken<'a> {
|
||||
pub(crate) fn new(runner: &'a Runner<'a>, mutex_guard: MutexGuard<'a, CriticalSectionRawMutex, ()>) -> Self {
|
||||
// Enable event receiving
|
||||
runner.rx_event_channel.lock(|s| {
|
||||
*s.borrow_mut() = Some(Signal::new());
|
||||
});
|
||||
|
||||
Self {
|
||||
runner: runner,
|
||||
_mutex_guard: mutex_guard,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Future for EventToken<'a> {
|
||||
type Output = MacEvent<'a>;
|
||||
|
||||
fn poll(self: core::pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
|
||||
self.get_mut().runner.rx_event_channel.lock(|s| {
|
||||
let signal = s.borrow_mut();
|
||||
let signal = match &*signal {
|
||||
Some(s) => s,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let result = match signal.wait().poll_unpin(cx) {
|
||||
Poll::Ready(mac_event) => Poll::Ready(mac_event),
|
||||
Poll::Pending => Poll::Pending,
|
||||
};
|
||||
|
||||
result
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for EventToken<'a> {
|
||||
fn drop(&mut self) {
|
||||
// Disable event receiving
|
||||
// This will also drop the contained event, if it exists, and will free up receiving the next event
|
||||
self.runner.rx_event_channel.lock(|s| {
|
||||
*s.borrow_mut() = None;
|
||||
});
|
||||
}
|
||||
}
|
122
embassy-stm32-wpan/src/mac/driver.rs
Normal file
122
embassy-stm32-wpan/src/mac/driver.rs
Normal file
@ -0,0 +1,122 @@
|
||||
#![allow(incomplete_features)]
|
||||
#![deny(unused_must_use)]
|
||||
|
||||
use core::task::Context;
|
||||
|
||||
use embassy_net_driver::{Capabilities, LinkState, Medium};
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::channel::Channel;
|
||||
|
||||
use crate::mac::event::MacEvent;
|
||||
use crate::mac::runner::Runner;
|
||||
use crate::mac::MTU;
|
||||
|
||||
pub struct Driver<'d> {
|
||||
runner: &'d Runner<'d>,
|
||||
}
|
||||
|
||||
impl<'d> Driver<'d> {
|
||||
pub(crate) fn new(runner: &'d Runner<'d>) -> Self {
|
||||
Self { runner: runner }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> embassy_net_driver::Driver for Driver<'d> {
|
||||
// type RxToken<'a> = RxToken<'a, 'd> where Self: 'a;
|
||||
// type TxToken<'a> = TxToken<'a, 'd> where Self: 'a;
|
||||
type RxToken<'a> = RxToken<'d> where Self: 'a;
|
||||
type TxToken<'a> = TxToken<'d> where Self: 'a;
|
||||
|
||||
fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
|
||||
if self.runner.rx_channel.poll_ready_to_receive(cx) && self.runner.tx_buf_channel.poll_ready_to_receive(cx) {
|
||||
Some((
|
||||
RxToken {
|
||||
rx: &self.runner.rx_channel,
|
||||
},
|
||||
TxToken {
|
||||
tx: &self.runner.tx_channel,
|
||||
tx_buf: &self.runner.tx_buf_channel,
|
||||
},
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> {
|
||||
if self.runner.tx_buf_channel.poll_ready_to_receive(cx) {
|
||||
Some(TxToken {
|
||||
tx: &self.runner.tx_channel,
|
||||
tx_buf: &self.runner.tx_buf_channel,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn capabilities(&self) -> Capabilities {
|
||||
let mut caps = Capabilities::default();
|
||||
caps.max_transmission_unit = MTU;
|
||||
// caps.max_burst_size = Some(self.tx.len());
|
||||
|
||||
caps.medium = Medium::Ieee802154;
|
||||
caps
|
||||
}
|
||||
|
||||
fn link_state(&mut self, _cx: &mut Context) -> LinkState {
|
||||
// if self.phy.poll_link(&mut self.station_management, cx) {
|
||||
// LinkState::Up
|
||||
// } else {
|
||||
// LinkState::Down
|
||||
// }
|
||||
|
||||
LinkState::Down
|
||||
}
|
||||
|
||||
fn ethernet_address(&self) -> [u8; 6] {
|
||||
// self.mac_addr
|
||||
|
||||
[0; 6]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RxToken<'d> {
|
||||
rx: &'d Channel<CriticalSectionRawMutex, MacEvent<'d>, 1>,
|
||||
}
|
||||
|
||||
impl<'d> embassy_net_driver::RxToken for RxToken<'d> {
|
||||
fn consume<R, F>(self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> R,
|
||||
{
|
||||
// Only valid data events should be put into the queue
|
||||
|
||||
let data_event = match self.rx.try_recv().unwrap() {
|
||||
MacEvent::McpsDataInd(data_event) => data_event,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
f(&mut data_event.payload())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TxToken<'d> {
|
||||
tx: &'d Channel<CriticalSectionRawMutex, (&'d mut [u8; MTU], usize), 5>,
|
||||
tx_buf: &'d Channel<CriticalSectionRawMutex, &'d mut [u8; MTU], 5>,
|
||||
}
|
||||
|
||||
impl<'d> embassy_net_driver::TxToken for TxToken<'d> {
|
||||
fn consume<R, F>(self, len: usize, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> R,
|
||||
{
|
||||
// Only valid tx buffers should be put into the queue
|
||||
let buf = self.tx_buf.try_recv().unwrap();
|
||||
let r = f(&mut buf[..len]);
|
||||
|
||||
// The tx channel should always be of equal capacity to the tx_buf channel
|
||||
self.tx.try_send((buf, len)).unwrap();
|
||||
|
||||
r
|
||||
}
|
||||
}
|
153
embassy-stm32-wpan/src/mac/event.rs
Normal file
153
embassy-stm32-wpan/src/mac/event.rs
Normal file
@ -0,0 +1,153 @@
|
||||
use core::{mem, ptr};
|
||||
|
||||
use super::indications::{
|
||||
AssociateIndication, BeaconNotifyIndication, CommStatusIndication, DataIndication, DisassociateIndication,
|
||||
DpsIndication, GtsIndication, OrphanIndication, PollIndication, SyncLossIndication,
|
||||
};
|
||||
use super::responses::{
|
||||
AssociateConfirm, CalibrateConfirm, DataConfirm, DisassociateConfirm, DpsConfirm, GetConfirm, GtsConfirm,
|
||||
PollConfirm, PurgeConfirm, ResetConfirm, RxEnableConfirm, ScanConfirm, SetConfirm, SoundingConfirm, StartConfirm,
|
||||
};
|
||||
use crate::evt::{EvtBox, MemoryManager};
|
||||
use crate::mac::opcodes::OpcodeM0ToM4;
|
||||
use crate::sub::mac::{self, Mac};
|
||||
|
||||
pub(crate) trait ParseableMacEvent: Sized {
|
||||
fn from_buffer<'a>(buf: &'a [u8]) -> Result<&'a Self, ()> {
|
||||
if buf.len() < mem::size_of::<Self>() {
|
||||
Err(())
|
||||
} else {
|
||||
Ok(unsafe { &*(buf as *const _ as *const Self) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[derive(Debug)]
|
||||
pub enum MacEvent<'a> {
|
||||
MlmeAssociateCnf(&'a AssociateConfirm),
|
||||
MlmeDisassociateCnf(&'a DisassociateConfirm),
|
||||
MlmeGetCnf(&'a GetConfirm),
|
||||
MlmeGtsCnf(&'a GtsConfirm),
|
||||
MlmeResetCnf(&'a ResetConfirm),
|
||||
MlmeRxEnableCnf(&'a RxEnableConfirm),
|
||||
MlmeScanCnf(&'a ScanConfirm),
|
||||
MlmeSetCnf(&'a SetConfirm),
|
||||
MlmeStartCnf(&'a StartConfirm),
|
||||
MlmePollCnf(&'a PollConfirm),
|
||||
MlmeDpsCnf(&'a DpsConfirm),
|
||||
MlmeSoundingCnf(&'a SoundingConfirm),
|
||||
MlmeCalibrateCnf(&'a CalibrateConfirm),
|
||||
McpsDataCnf(&'a DataConfirm),
|
||||
McpsPurgeCnf(&'a PurgeConfirm),
|
||||
MlmeAssociateInd(&'a AssociateIndication),
|
||||
MlmeDisassociateInd(&'a DisassociateIndication),
|
||||
MlmeBeaconNotifyInd(&'a BeaconNotifyIndication),
|
||||
MlmeCommStatusInd(&'a CommStatusIndication),
|
||||
MlmeGtsInd(&'a GtsIndication),
|
||||
MlmeOrphanInd(&'a OrphanIndication),
|
||||
MlmeSyncLossInd(&'a SyncLossIndication),
|
||||
MlmeDpsInd(&'a DpsIndication),
|
||||
McpsDataInd(&'a DataIndication),
|
||||
MlmePollInd(&'a PollIndication),
|
||||
}
|
||||
|
||||
impl<'a> MacEvent<'a> {
|
||||
pub(crate) fn new(event_box: EvtBox<Mac>) -> Result<Self, ()> {
|
||||
let payload = event_box.payload();
|
||||
let opcode = u16::from_le_bytes(payload[0..2].try_into().unwrap());
|
||||
|
||||
let opcode = OpcodeM0ToM4::try_from(opcode)?;
|
||||
let buf = &payload[2..];
|
||||
|
||||
// To avoid re-parsing the opcode, we store the result of the parse
|
||||
// this requires use of unsafe because rust cannot assume that a reference will become
|
||||
// invalid when the underlying result is moved. However, because we refer to a "heap"
|
||||
// allocation, the underlying reference will not move until the struct is dropped.
|
||||
|
||||
let mac_event = match opcode {
|
||||
OpcodeM0ToM4::MlmeAssociateCnf => {
|
||||
MacEvent::MlmeAssociateCnf(unsafe { &*(AssociateConfirm::from_buffer(buf)? as *const _) })
|
||||
}
|
||||
OpcodeM0ToM4::MlmeDisassociateCnf => {
|
||||
MacEvent::MlmeDisassociateCnf(unsafe { &*(DisassociateConfirm::from_buffer(buf)? as *const _) })
|
||||
}
|
||||
OpcodeM0ToM4::MlmeGetCnf => MacEvent::MlmeGetCnf(unsafe { &*(GetConfirm::from_buffer(buf)? as *const _) }),
|
||||
OpcodeM0ToM4::MlmeGtsCnf => MacEvent::MlmeGtsCnf(unsafe { &*(GtsConfirm::from_buffer(buf)? as *const _) }),
|
||||
OpcodeM0ToM4::MlmeResetCnf => {
|
||||
MacEvent::MlmeResetCnf(unsafe { &*(ResetConfirm::from_buffer(buf)? as *const _) })
|
||||
}
|
||||
OpcodeM0ToM4::MlmeRxEnableCnf => {
|
||||
MacEvent::MlmeRxEnableCnf(unsafe { &*(RxEnableConfirm::from_buffer(buf)? as *const _) })
|
||||
}
|
||||
OpcodeM0ToM4::MlmeScanCnf => {
|
||||
MacEvent::MlmeScanCnf(unsafe { &*(ScanConfirm::from_buffer(buf)? as *const _) })
|
||||
}
|
||||
OpcodeM0ToM4::MlmeSetCnf => MacEvent::MlmeSetCnf(unsafe { &*(SetConfirm::from_buffer(buf)? as *const _) }),
|
||||
OpcodeM0ToM4::MlmeStartCnf => {
|
||||
MacEvent::MlmeStartCnf(unsafe { &*(StartConfirm::from_buffer(buf)? as *const _) })
|
||||
}
|
||||
OpcodeM0ToM4::MlmePollCnf => {
|
||||
MacEvent::MlmePollCnf(unsafe { &*(PollConfirm::from_buffer(buf)? as *const _) })
|
||||
}
|
||||
OpcodeM0ToM4::MlmeDpsCnf => MacEvent::MlmeDpsCnf(unsafe { &*(DpsConfirm::from_buffer(buf)? as *const _) }),
|
||||
OpcodeM0ToM4::MlmeSoundingCnf => {
|
||||
MacEvent::MlmeSoundingCnf(unsafe { &*(SoundingConfirm::from_buffer(buf)? as *const _) })
|
||||
}
|
||||
OpcodeM0ToM4::MlmeCalibrateCnf => {
|
||||
MacEvent::MlmeCalibrateCnf(unsafe { &*(CalibrateConfirm::from_buffer(buf)? as *const _) })
|
||||
}
|
||||
OpcodeM0ToM4::McpsDataCnf => {
|
||||
MacEvent::McpsDataCnf(unsafe { &*(DataConfirm::from_buffer(buf)? as *const _) })
|
||||
}
|
||||
OpcodeM0ToM4::McpsPurgeCnf => {
|
||||
MacEvent::McpsPurgeCnf(unsafe { &*(PurgeConfirm::from_buffer(buf)? as *const _) })
|
||||
}
|
||||
OpcodeM0ToM4::MlmeAssociateInd => {
|
||||
MacEvent::MlmeAssociateInd(unsafe { &*(AssociateIndication::from_buffer(buf)? as *const _) })
|
||||
}
|
||||
OpcodeM0ToM4::MlmeDisassociateInd => {
|
||||
MacEvent::MlmeDisassociateInd(unsafe { &*(DisassociateIndication::from_buffer(buf)? as *const _) })
|
||||
}
|
||||
OpcodeM0ToM4::MlmeBeaconNotifyInd => {
|
||||
MacEvent::MlmeBeaconNotifyInd(unsafe { &*(BeaconNotifyIndication::from_buffer(buf)? as *const _) })
|
||||
}
|
||||
OpcodeM0ToM4::MlmeCommStatusInd => {
|
||||
MacEvent::MlmeCommStatusInd(unsafe { &*(CommStatusIndication::from_buffer(buf)? as *const _) })
|
||||
}
|
||||
OpcodeM0ToM4::MlmeGtsInd => {
|
||||
MacEvent::MlmeGtsInd(unsafe { &*(GtsIndication::from_buffer(buf)? as *const _) })
|
||||
}
|
||||
OpcodeM0ToM4::MlmeOrphanInd => {
|
||||
MacEvent::MlmeOrphanInd(unsafe { &*(OrphanIndication::from_buffer(buf)? as *const _) })
|
||||
}
|
||||
OpcodeM0ToM4::MlmeSyncLossInd => {
|
||||
MacEvent::MlmeSyncLossInd(unsafe { &*(SyncLossIndication::from_buffer(buf)? as *const _) })
|
||||
}
|
||||
OpcodeM0ToM4::MlmeDpsInd => {
|
||||
MacEvent::MlmeDpsInd(unsafe { &*(DpsIndication::from_buffer(buf)? as *const _) })
|
||||
}
|
||||
OpcodeM0ToM4::McpsDataInd => {
|
||||
MacEvent::McpsDataInd(unsafe { &*(DataIndication::from_buffer(buf)? as *const _) })
|
||||
}
|
||||
OpcodeM0ToM4::MlmePollInd => {
|
||||
MacEvent::MlmePollInd(unsafe { &*(PollIndication::from_buffer(buf)? as *const _) })
|
||||
}
|
||||
};
|
||||
|
||||
// Forget the event box so that drop isn't called
|
||||
// We want to handle the lifetime ourselves
|
||||
|
||||
mem::forget(event_box);
|
||||
|
||||
Ok(mac_event)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> Send for MacEvent<'a> {}
|
||||
|
||||
impl<'a> Drop for MacEvent<'a> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { mac::Mac::drop_event_packet(ptr::null_mut()) };
|
||||
}
|
||||
}
|
265
embassy-stm32-wpan/src/mac/indications.rs
Normal file
265
embassy-stm32-wpan/src/mac/indications.rs
Normal file
@ -0,0 +1,265 @@
|
||||
use core::slice;
|
||||
|
||||
use super::consts::MAX_PENDING_ADDRESS;
|
||||
use super::event::ParseableMacEvent;
|
||||
use super::typedefs::{
|
||||
AddressMode, Capabilities, DisassociationReason, KeyIdMode, MacAddress, MacChannel, MacStatus, PanDescriptor,
|
||||
PanId, SecurityLevel,
|
||||
};
|
||||
|
||||
/// MLME ASSOCIATE Indication which will be used by the MAC
|
||||
/// to indicate the reception of an association request command
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct AssociateIndication {
|
||||
/// Extended address of the device requesting association
|
||||
pub device_address: [u8; 8],
|
||||
/// Operational capabilities of the device requesting association
|
||||
pub capability_information: Capabilities,
|
||||
/// Security level purportedly used by the received MAC command frame
|
||||
pub security_level: SecurityLevel,
|
||||
/// The mode used to identify the key used by the originator of frame
|
||||
pub key_id_mode: KeyIdMode,
|
||||
/// Index of the key used by the originator of the received frame
|
||||
pub key_index: u8,
|
||||
/// The originator of the key used by the originator of the received frame
|
||||
pub key_source: [u8; 8],
|
||||
}
|
||||
|
||||
impl ParseableMacEvent for AssociateIndication {}
|
||||
|
||||
/// MLME DISASSOCIATE indication which will be used to send
|
||||
/// disassociation indication to the application.
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct DisassociateIndication {
|
||||
/// Extended address of the device requesting association
|
||||
pub device_address: [u8; 8],
|
||||
/// The reason for the disassociation
|
||||
pub disassociation_reason: DisassociationReason,
|
||||
/// The security level to be used
|
||||
pub security_level: SecurityLevel,
|
||||
/// The mode used to identify the key to be used
|
||||
pub key_id_mode: KeyIdMode,
|
||||
/// The index of the key to be used
|
||||
pub key_index: u8,
|
||||
/// The originator of the key to be used
|
||||
pub key_source: [u8; 8],
|
||||
}
|
||||
|
||||
impl ParseableMacEvent for DisassociateIndication {}
|
||||
|
||||
/// MLME BEACON NOTIIFY Indication which is used to send parameters contained
|
||||
/// within a beacon frame received by the MAC to the application
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct BeaconNotifyIndication {
|
||||
/// he set of octets comprising the beacon payload to be transferred
|
||||
/// from the MAC sublayer entity to the next higher layer
|
||||
pub sdu_ptr: *const u8,
|
||||
/// The PAN Descriptor for the received beacon
|
||||
pub pan_descriptor: PanDescriptor,
|
||||
/// The list of addresses of the devices
|
||||
pub addr_list: [MacAddress; MAX_PENDING_ADDRESS],
|
||||
/// Beacon Sequence Number
|
||||
pub bsn: u8,
|
||||
/// The beacon pending address specification
|
||||
pub pend_addr_spec: u8,
|
||||
/// Number of octets contained in the beacon payload of the beacon frame
|
||||
pub sdu_length: u8,
|
||||
}
|
||||
|
||||
impl ParseableMacEvent for BeaconNotifyIndication {}
|
||||
|
||||
/// MLME COMM STATUS Indication which is used by the MAC to indicate a communications status
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct CommStatusIndication {
|
||||
/// The 16-bit PAN identifier of the device from which the frame
|
||||
/// was received or to which the frame was being sent
|
||||
pub pan_id: PanId,
|
||||
/// Source addressing mode
|
||||
pub src_addr_mode: AddressMode,
|
||||
/// Destination addressing mode
|
||||
pub dst_addr_mode: AddressMode,
|
||||
/// Source address
|
||||
pub src_address: MacAddress,
|
||||
/// Destination address
|
||||
pub dst_address: MacAddress,
|
||||
/// The communications status
|
||||
pub status: MacStatus,
|
||||
/// Security level to be used
|
||||
pub security_level: SecurityLevel,
|
||||
/// Mode used to identify the key to be used
|
||||
pub key_id_mode: KeyIdMode,
|
||||
/// Index of the key to be used
|
||||
pub key_index: u8,
|
||||
/// Originator of the key to be used
|
||||
pub key_source: [u8; 8],
|
||||
}
|
||||
|
||||
impl ParseableMacEvent for CommStatusIndication {}
|
||||
|
||||
/// MLME GTS Indication indicates that a GTS has been allocated or that a
|
||||
/// previously allocated GTS has been deallocated
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct GtsIndication {
|
||||
/// The short address of the device that has been allocated or deallocated a GTS
|
||||
pub device_address: [u8; 2],
|
||||
/// The characteristics of the GTS
|
||||
pub gts_characteristics: u8,
|
||||
/// Security level to be used
|
||||
pub security_level: SecurityLevel,
|
||||
/// Mode used to identify the key to be used
|
||||
pub key_id_mode: KeyIdMode,
|
||||
/// Index of the key to be used
|
||||
pub key_index: u8,
|
||||
/// byte stuffing to keep 32 bit alignment
|
||||
a_stuffing: [u8; 2],
|
||||
/// Originator of the key to be used
|
||||
pub key_source: [u8; 8],
|
||||
}
|
||||
|
||||
impl ParseableMacEvent for GtsIndication {}
|
||||
|
||||
/// MLME ORPHAN Indication which is used by the coordinator to notify the
|
||||
/// application of the presence of an orphaned device
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct OrphanIndication {
|
||||
/// Extended address of the orphaned device
|
||||
pub orphan_address: [u8; 8],
|
||||
/// Originator of the key used by the originator of the received frame
|
||||
pub key_source: [u8; 8],
|
||||
/// Security level purportedly used by the received MAC command frame
|
||||
pub security_level: SecurityLevel,
|
||||
/// Mode used to identify the key used by originator of received frame
|
||||
pub key_id_mode: KeyIdMode,
|
||||
/// Index of the key used by the originator of the received frame
|
||||
pub key_index: u8,
|
||||
/// byte stuffing to keep 32 bit alignment
|
||||
a_stuffing: [u8; 1],
|
||||
}
|
||||
|
||||
impl ParseableMacEvent for OrphanIndication {}
|
||||
|
||||
/// MLME SYNC LOSS Indication which is used by the MAC to indicate the loss
|
||||
/// of synchronization with the coordinator
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct SyncLossIndication {
|
||||
/// The PAN identifier with which the device lost synchronization or to which it was realigned
|
||||
pub pan_id: PanId,
|
||||
/// The reason that synchronization was lost
|
||||
pub loss_reason: u8,
|
||||
/// The logical channel on which the device lost synchronization or to whi
|
||||
pub channel_number: MacChannel,
|
||||
/// The channel page on which the device lost synchronization or to which
|
||||
pub channel_page: u8,
|
||||
/// The security level used by the received MAC frame
|
||||
pub security_level: SecurityLevel,
|
||||
/// Mode used to identify the key used by originator of received frame
|
||||
pub key_id_mode: KeyIdMode,
|
||||
/// Index of the key used by the originator of the received frame
|
||||
pub key_index: u8,
|
||||
/// Originator of the key used by the originator of the received frame
|
||||
pub key_source: [u8; 8],
|
||||
}
|
||||
|
||||
impl ParseableMacEvent for SyncLossIndication {}
|
||||
|
||||
/// MLME DPS Indication which indicates the expiration of the DPSIndexDuration
|
||||
/// and the resetting of the DPS values in the PHY
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct DpsIndication {
|
||||
/// byte stuffing to keep 32 bit alignment
|
||||
a_stuffing: [u8; 4],
|
||||
}
|
||||
|
||||
impl ParseableMacEvent for DpsIndication {}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct DataIndication {
|
||||
/// Pointer to the set of octets forming the MSDU being indicated
|
||||
pub msdu_ptr: *const u8,
|
||||
/// Source addressing mode used
|
||||
pub src_addr_mode: AddressMode,
|
||||
/// Source PAN ID
|
||||
pub src_pan_id: PanId,
|
||||
/// Source address
|
||||
pub src_address: MacAddress,
|
||||
/// Destination addressing mode used
|
||||
pub dst_addr_mode: AddressMode,
|
||||
/// Destination PAN ID
|
||||
pub dst_pan_id: PanId,
|
||||
/// Destination address
|
||||
pub dst_address: MacAddress,
|
||||
/// The number of octets contained in the MSDU being indicated
|
||||
pub msdu_length: u8,
|
||||
/// QI value measured during reception of the MPDU
|
||||
pub mpdu_link_quality: u8,
|
||||
/// The data sequence number of the received data frame
|
||||
pub dsn: u8,
|
||||
/// The time, in symbols, at which the data were received
|
||||
pub time_stamp: [u8; 4],
|
||||
/// The security level purportedly used by the received data frame
|
||||
security_level: SecurityLevel,
|
||||
/// Mode used to identify the key used by originator of received frame
|
||||
key_id_mode: KeyIdMode,
|
||||
/// The originator of the key
|
||||
pub key_source: [u8; 8],
|
||||
/// The index of the key
|
||||
pub key_index: u8,
|
||||
/// he pulse repetition value of the received PPDU
|
||||
pub uwbprf: u8,
|
||||
/// The preamble symbol repetitions of the UWB PHY frame
|
||||
pub uwn_preamble_symbol_repetitions: u8,
|
||||
/// Indicates the data rate
|
||||
pub datrate: u8,
|
||||
/// time units corresponding to an RMARKER at the antenna at the end of a ranging exchange,
|
||||
pub ranging_received: u8,
|
||||
pub ranging_counter_start: u32,
|
||||
pub ranging_counter_stop: u32,
|
||||
/// ime units in a message exchange over which the tracking offset was measured
|
||||
pub ranging_tracking_interval: u32,
|
||||
/// time units slipped or advanced by the radio tracking system
|
||||
pub ranging_offset: u32,
|
||||
/// The FoM characterizing the ranging measurement
|
||||
pub ranging_fom: u8,
|
||||
/// The Received Signal Strength Indicator measured
|
||||
pub rssi: u8,
|
||||
}
|
||||
|
||||
impl ParseableMacEvent for DataIndication {}
|
||||
|
||||
impl DataIndication {
|
||||
pub fn payload<'a>(&'a self) -> &'a mut [u8] {
|
||||
unsafe { slice::from_raw_parts_mut(self.msdu_ptr as *mut _, self.msdu_length as usize) }
|
||||
}
|
||||
}
|
||||
|
||||
/// MLME POLL Indication which will be used for indicating the Data Request
|
||||
/// reception to upper layer as defined in Zigbee r22 - D.8.2
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct PollIndication {
|
||||
/// addressing mode used
|
||||
pub addr_mode: AddressMode,
|
||||
/// Poll requester address
|
||||
pub request_address: MacAddress,
|
||||
}
|
||||
|
||||
impl ParseableMacEvent for PollIndication {}
|
32
embassy-stm32-wpan/src/mac/macros.rs
Normal file
32
embassy-stm32-wpan/src/mac/macros.rs
Normal file
@ -0,0 +1,32 @@
|
||||
#[macro_export]
|
||||
macro_rules! numeric_enum {
|
||||
(#[repr($repr:ident)]
|
||||
$(#$attrs:tt)* $vis:vis enum $name:ident {
|
||||
$($(#$enum_attrs:tt)* $enum:ident = $constant:expr),* $(,)?
|
||||
} ) => {
|
||||
#[repr($repr)]
|
||||
$(#$attrs)*
|
||||
$vis enum $name {
|
||||
$($(#$enum_attrs)* $enum = $constant),*
|
||||
}
|
||||
|
||||
impl ::core::convert::TryFrom<$repr> for $name {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: $repr) -> ::core::result::Result<Self, ()> {
|
||||
match value {
|
||||
$($constant => Ok( $name :: $enum ),)*
|
||||
_ => Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ::core::convert::From<$name> for $repr {
|
||||
fn from(value: $name) -> $repr {
|
||||
match value {
|
||||
$($name :: $enum => $constant,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
21
embassy-stm32-wpan/src/mac/mod.rs
Normal file
21
embassy-stm32-wpan/src/mac/mod.rs
Normal file
@ -0,0 +1,21 @@
|
||||
pub mod commands;
|
||||
mod consts;
|
||||
pub mod control;
|
||||
mod driver;
|
||||
pub mod event;
|
||||
pub mod indications;
|
||||
mod macros;
|
||||
mod opcodes;
|
||||
pub mod responses;
|
||||
pub mod runner;
|
||||
pub mod typedefs;
|
||||
|
||||
pub use crate::mac::control::Control;
|
||||
use crate::mac::driver::Driver;
|
||||
pub use crate::mac::runner::Runner;
|
||||
|
||||
const MTU: usize = 127;
|
||||
|
||||
pub async fn new<'a>(runner: &'a Runner<'a>) -> (Control<'a>, Driver<'a>) {
|
||||
(Control::new(runner), Driver::new(runner))
|
||||
}
|
92
embassy-stm32-wpan/src/mac/opcodes.rs
Normal file
92
embassy-stm32-wpan/src/mac/opcodes.rs
Normal file
@ -0,0 +1,92 @@
|
||||
const ST_VENDOR_OGF: u16 = 0x3F;
|
||||
const MAC_802_15_4_CMD_OPCODE_OFFSET: u16 = 0x280;
|
||||
|
||||
const fn opcode(ocf: u16) -> isize {
|
||||
((ST_VENDOR_OGF << 9) | (MAC_802_15_4_CMD_OPCODE_OFFSET + ocf)) as isize
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum OpcodeM4ToM0 {
|
||||
MlmeAssociateReq = opcode(0x00),
|
||||
MlmeAssociateRes = opcode(0x01),
|
||||
MlmeDisassociateReq = opcode(0x02),
|
||||
MlmeGetReq = opcode(0x03),
|
||||
MlmeGtsReq = opcode(0x04),
|
||||
MlmeOrphanRes = opcode(0x05),
|
||||
MlmeResetReq = opcode(0x06),
|
||||
MlmeRxEnableReq = opcode(0x07),
|
||||
MlmeScanReq = opcode(0x08),
|
||||
MlmeSetReq = opcode(0x09),
|
||||
MlmeStartReq = opcode(0x0A),
|
||||
MlmeSyncReq = opcode(0x0B),
|
||||
MlmePollReq = opcode(0x0C),
|
||||
MlmeDpsReq = opcode(0x0D),
|
||||
MlmeSoundingReq = opcode(0x0E),
|
||||
MlmeCalibrateReq = opcode(0x0F),
|
||||
McpsDataReq = opcode(0x10),
|
||||
McpsPurgeReq = opcode(0x11),
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum OpcodeM0ToM4 {
|
||||
MlmeAssociateCnf = 0x00,
|
||||
MlmeDisassociateCnf,
|
||||
MlmeGetCnf,
|
||||
MlmeGtsCnf,
|
||||
MlmeResetCnf,
|
||||
MlmeRxEnableCnf,
|
||||
MlmeScanCnf,
|
||||
MlmeSetCnf,
|
||||
MlmeStartCnf,
|
||||
MlmePollCnf,
|
||||
MlmeDpsCnf,
|
||||
MlmeSoundingCnf,
|
||||
MlmeCalibrateCnf,
|
||||
McpsDataCnf,
|
||||
McpsPurgeCnf,
|
||||
MlmeAssociateInd,
|
||||
MlmeDisassociateInd,
|
||||
MlmeBeaconNotifyInd,
|
||||
MlmeCommStatusInd,
|
||||
MlmeGtsInd,
|
||||
MlmeOrphanInd,
|
||||
MlmeSyncLossInd,
|
||||
MlmeDpsInd,
|
||||
McpsDataInd,
|
||||
MlmePollInd,
|
||||
}
|
||||
|
||||
impl TryFrom<u16> for OpcodeM0ToM4 {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: u16) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0 => Ok(Self::MlmeAssociateCnf),
|
||||
1 => Ok(Self::MlmeDisassociateCnf),
|
||||
2 => Ok(Self::MlmeGetCnf),
|
||||
3 => Ok(Self::MlmeGtsCnf),
|
||||
4 => Ok(Self::MlmeResetCnf),
|
||||
5 => Ok(Self::MlmeRxEnableCnf),
|
||||
6 => Ok(Self::MlmeScanCnf),
|
||||
7 => Ok(Self::MlmeSetCnf),
|
||||
8 => Ok(Self::MlmeStartCnf),
|
||||
9 => Ok(Self::MlmePollCnf),
|
||||
10 => Ok(Self::MlmeDpsCnf),
|
||||
11 => Ok(Self::MlmeSoundingCnf),
|
||||
12 => Ok(Self::MlmeCalibrateCnf),
|
||||
13 => Ok(Self::McpsDataCnf),
|
||||
14 => Ok(Self::McpsPurgeCnf),
|
||||
15 => Ok(Self::MlmeAssociateInd),
|
||||
16 => Ok(Self::MlmeDisassociateInd),
|
||||
17 => Ok(Self::MlmeBeaconNotifyInd),
|
||||
18 => Ok(Self::MlmeCommStatusInd),
|
||||
19 => Ok(Self::MlmeGtsInd),
|
||||
20 => Ok(Self::MlmeOrphanInd),
|
||||
21 => Ok(Self::MlmeSyncLossInd),
|
||||
22 => Ok(Self::MlmeDpsInd),
|
||||
23 => Ok(Self::McpsDataInd),
|
||||
24 => Ok(Self::MlmePollInd),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
273
embassy-stm32-wpan/src/mac/responses.rs
Normal file
273
embassy-stm32-wpan/src/mac/responses.rs
Normal file
@ -0,0 +1,273 @@
|
||||
use super::consts::{MAX_ED_SCAN_RESULTS_SUPPORTED, MAX_PAN_DESC_SUPPORTED, MAX_SOUNDING_LIST_SUPPORTED};
|
||||
use super::event::ParseableMacEvent;
|
||||
use super::typedefs::{
|
||||
AddressMode, AssociationStatus, KeyIdMode, MacAddress, MacStatus, PanDescriptor, PanId, PibId, ScanType,
|
||||
SecurityLevel,
|
||||
};
|
||||
|
||||
/// MLME ASSOCIATE Confirm used to inform of the initiating device whether
|
||||
/// its request to associate was successful or unsuccessful
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct AssociateConfirm {
|
||||
/// short address allocated by the coordinator on successful association
|
||||
pub assoc_short_address: [u8; 2],
|
||||
/// status of the association request
|
||||
pub status: AssociationStatus,
|
||||
/// security level to be used
|
||||
pub security_level: SecurityLevel,
|
||||
/// the originator of the key to be used
|
||||
pub key_source: [u8; 8],
|
||||
/// the mode used to identify the key to be used
|
||||
pub key_id_mode: KeyIdMode,
|
||||
/// the index of the key to be used
|
||||
pub key_index: u8,
|
||||
/// byte stuffing to keep 32 bit alignment
|
||||
a_stuffing: [u8; 2],
|
||||
}
|
||||
|
||||
impl ParseableMacEvent for AssociateConfirm {}
|
||||
|
||||
/// MLME DISASSOCIATE Confirm used to send disassociation Confirmation to the application.
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct DisassociateConfirm {
|
||||
/// status of the disassociation attempt
|
||||
pub status: MacStatus,
|
||||
/// device addressing mode used
|
||||
pub device_addr_mode: AddressMode,
|
||||
/// the identifier of the PAN of the device
|
||||
pub device_pan_id: PanId,
|
||||
/// device address
|
||||
pub device_address: MacAddress,
|
||||
}
|
||||
|
||||
impl ParseableMacEvent for DisassociateConfirm {}
|
||||
|
||||
/// MLME GET Confirm which requests information about a given PIB attribute
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct GetConfirm {
|
||||
/// The pointer to the value of the PIB attribute attempted to read
|
||||
pub pib_attribute_value_ptr: *const u8,
|
||||
/// Status of the GET attempt
|
||||
pub status: MacStatus,
|
||||
/// The name of the PIB attribute attempted to read
|
||||
pub pib_attribute: PibId,
|
||||
/// The lenght of the PIB attribute Value return
|
||||
pub pib_attribute_value_len: u8,
|
||||
/// byte stuffing to keep 32 bit alignment
|
||||
a_stuffing: [u8; 1],
|
||||
}
|
||||
|
||||
impl ParseableMacEvent for GetConfirm {}
|
||||
|
||||
/// MLME GTS Confirm which eports the results of a request to allocate a new GTS
|
||||
/// or to deallocate an existing GTS
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct GtsConfirm {
|
||||
/// The characteristics of the GTS
|
||||
pub gts_characteristics: u8,
|
||||
/// The status of the GTS reques
|
||||
pub status: MacStatus,
|
||||
/// byte stuffing to keep 32 bit alignment
|
||||
a_stuffing: [u8; 2],
|
||||
}
|
||||
|
||||
impl ParseableMacEvent for GtsConfirm {}
|
||||
|
||||
/// MLME RESET Confirm which is used to report the results of the reset operation
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct ResetConfirm {
|
||||
/// The result of the reset operation
|
||||
pub status: MacStatus,
|
||||
/// byte stuffing to keep 32 bit alignment
|
||||
a_stuffing: [u8; 3],
|
||||
}
|
||||
|
||||
impl ParseableMacEvent for ResetConfirm {}
|
||||
|
||||
/// MLME RX ENABLE Confirm which is used to report the results of the attempt
|
||||
/// to enable or disable the receiver
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct RxEnableConfirm {
|
||||
/// Result of the request to enable or disable the receiver
|
||||
pub status: MacStatus,
|
||||
/// byte stuffing to keep 32 bit alignment
|
||||
a_stuffing: [u8; 3],
|
||||
}
|
||||
|
||||
impl ParseableMacEvent for RxEnableConfirm {}
|
||||
|
||||
/// MLME SCAN Confirm which is used to report the result of the channel scan request
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct ScanConfirm {
|
||||
/// Status of the scan request
|
||||
pub status: MacStatus,
|
||||
/// The type of scan performed
|
||||
pub scan_type: ScanType,
|
||||
/// Channel page on which the scan was performed
|
||||
pub channel_page: u8,
|
||||
/// Channels given in the request which were not scanned
|
||||
pub unscanned_channels: [u8; 4],
|
||||
/// Number of elements returned in the appropriate result lists
|
||||
pub result_list_size: u8,
|
||||
/// List of energy measurements
|
||||
pub energy_detect_list: [u8; MAX_ED_SCAN_RESULTS_SUPPORTED],
|
||||
/// List of PAN descriptors
|
||||
pub pan_descriptor_list: [PanDescriptor; MAX_PAN_DESC_SUPPORTED],
|
||||
/// Categorization of energy detected in channel
|
||||
pub detected_category: u8,
|
||||
/// For UWB PHYs, the list of energy measurements taken
|
||||
pub uwb_energy_detect_list: [u8; MAX_ED_SCAN_RESULTS_SUPPORTED],
|
||||
}
|
||||
|
||||
impl ParseableMacEvent for ScanConfirm {}
|
||||
|
||||
/// MLME SET Confirm which reports the result of an attempt to write a value to a PIB attribute
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct SetConfirm {
|
||||
/// The result of the set operation
|
||||
pub status: MacStatus,
|
||||
/// The name of the PIB attribute that was written
|
||||
pub pin_attribute: PibId,
|
||||
/// byte stuffing to keep 32 bit alignment
|
||||
a_stuffing: [u8; 2],
|
||||
}
|
||||
|
||||
impl ParseableMacEvent for SetConfirm {}
|
||||
|
||||
/// MLME START Confirm which is used to report the results of the attempt to
|
||||
/// start using a new superframe configuration
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct StartConfirm {
|
||||
/// Result of the attempt to start using an updated superframe configuration
|
||||
pub status: MacStatus,
|
||||
/// byte stuffing to keep 32 bit alignment
|
||||
a_stuffing: [u8; 3],
|
||||
}
|
||||
|
||||
impl ParseableMacEvent for StartConfirm {}
|
||||
|
||||
/// MLME POLL Confirm which is used to report the result of a request to poll the coordinator for data
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct PollConfirm {
|
||||
/// The status of the data request
|
||||
pub status: MacStatus,
|
||||
/// byte stuffing to keep 32 bit alignment
|
||||
a_stuffing: [u8; 3],
|
||||
}
|
||||
|
||||
impl ParseableMacEvent for PollConfirm {}
|
||||
|
||||
/// MLME DPS Confirm which reports the results of the attempt to enable or disable the DPS
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct DpsConfirm {
|
||||
/// The status of the DPS request
|
||||
pub status: MacStatus,
|
||||
/// byte stuffing to keep 32 bit alignment
|
||||
a_stuffing: [u8; 3],
|
||||
}
|
||||
|
||||
impl ParseableMacEvent for DpsConfirm {}
|
||||
|
||||
/// MLME SOUNDING Confirm which reports the result of a request to the PHY to provide
|
||||
/// channel sounding information
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct SoundingConfirm {
|
||||
/// Results of the sounding measurement
|
||||
pub sounding_list: [u8; MAX_SOUNDING_LIST_SUPPORTED],
|
||||
|
||||
status: u8,
|
||||
}
|
||||
|
||||
impl ParseableMacEvent for SoundingConfirm {}
|
||||
|
||||
/// MLME CALIBRATE Confirm which reports the result of a request to the PHY
|
||||
/// to provide internal propagation path information
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct CalibrateConfirm {
|
||||
/// The status of the attempt to return sounding data
|
||||
pub status: MacStatus,
|
||||
/// byte stuffing to keep 32 bit alignment
|
||||
a_stuffing: [u8; 3],
|
||||
/// A count of the propagation time from the ranging counter
|
||||
/// to the transmit antenna
|
||||
pub cal_tx_rmaker_offset: u32,
|
||||
/// A count of the propagation time from the receive antenna
|
||||
/// to the ranging counter
|
||||
pub cal_rx_rmaker_offset: u32,
|
||||
}
|
||||
|
||||
impl ParseableMacEvent for CalibrateConfirm {}
|
||||
|
||||
/// MCPS DATA Confirm which will be used for reporting the results of
|
||||
/// MAC data related requests from the application
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct DataConfirm {
|
||||
/// The handle associated with the MSDU being confirmed
|
||||
pub msdu_handle: u8,
|
||||
/// The time, in symbols, at which the data were transmitted
|
||||
pub time_stamp: [u8; 4],
|
||||
/// ranging status
|
||||
pub ranging_received: u8,
|
||||
/// The status of the last MSDU transmission
|
||||
pub status: MacStatus,
|
||||
/// time units corresponding to an RMARKER at the antenna at
|
||||
/// the beginning of a ranging exchange
|
||||
pub ranging_counter_start: u32,
|
||||
/// time units corresponding to an RMARKER at the antenna
|
||||
/// at the end of a ranging exchange
|
||||
pub ranging_counter_stop: u32,
|
||||
/// time units in a message exchange over which the tracking offset was measured
|
||||
pub ranging_tracking_interval: u32,
|
||||
/// time units slipped or advanced by the radio tracking system
|
||||
pub ranging_offset: u32,
|
||||
/// The FoM characterizing the ranging measurement
|
||||
pub ranging_fom: u8,
|
||||
/// byte stuffing to keep 32 bit alignment
|
||||
a_stuffing: [u8; 3],
|
||||
}
|
||||
|
||||
impl ParseableMacEvent for DataConfirm {}
|
||||
|
||||
/// MCPS PURGE Confirm which will be used by the MAC to notify the application of
|
||||
/// the status of its request to purge an MSDU from the transaction queue
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct PurgeConfirm {
|
||||
/// Handle associated with the MSDU requested to be purged from the transaction queue
|
||||
pub msdu_handle: u8,
|
||||
/// The status of the request
|
||||
pub status: MacStatus,
|
||||
/// byte stuffing to keep 32 bit alignment
|
||||
a_stuffing: [u8; 2],
|
||||
}
|
||||
|
||||
impl ParseableMacEvent for PurgeConfirm {}
|
109
embassy-stm32-wpan/src/mac/runner.rs
Normal file
109
embassy-stm32-wpan/src/mac/runner.rs
Normal file
@ -0,0 +1,109 @@
|
||||
use core::cell::RefCell;
|
||||
|
||||
use embassy_futures::join;
|
||||
use embassy_sync::blocking_mutex;
|
||||
use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex};
|
||||
use embassy_sync::channel::Channel;
|
||||
use embassy_sync::mutex::Mutex;
|
||||
use embassy_sync::signal::Signal;
|
||||
|
||||
use crate::mac::commands::DataRequest;
|
||||
use crate::mac::event::MacEvent;
|
||||
use crate::mac::typedefs::{AddressMode, MacAddress, PanId, SecurityLevel};
|
||||
use crate::mac::MTU;
|
||||
use crate::sub::mac::Mac;
|
||||
|
||||
type ZeroCopyPubSub<M, T> = blocking_mutex::Mutex<M, RefCell<Option<Signal<NoopRawMutex, T>>>>;
|
||||
|
||||
pub struct Runner<'a> {
|
||||
pub(crate) mac_subsystem: Mac,
|
||||
// rx event backpressure is already provided through the MacEvent drop mechanism
|
||||
// therefore, we don't need to worry about overwriting events
|
||||
pub(crate) rx_event_channel: ZeroCopyPubSub<CriticalSectionRawMutex, MacEvent<'a>>,
|
||||
pub(crate) read_mutex: Mutex<CriticalSectionRawMutex, ()>,
|
||||
pub(crate) write_mutex: Mutex<CriticalSectionRawMutex, ()>,
|
||||
pub(crate) rx_channel: Channel<CriticalSectionRawMutex, MacEvent<'a>, 1>,
|
||||
pub(crate) tx_channel: Channel<CriticalSectionRawMutex, (&'a mut [u8; MTU], usize), 5>,
|
||||
pub(crate) tx_buf_channel: Channel<CriticalSectionRawMutex, &'a mut [u8; MTU], 5>,
|
||||
}
|
||||
|
||||
impl<'a> Runner<'a> {
|
||||
pub fn new(mac: Mac, tx_buf_queue: [&'a mut [u8; MTU]; 5]) -> Self {
|
||||
let this = Self {
|
||||
mac_subsystem: mac,
|
||||
rx_event_channel: blocking_mutex::Mutex::new(RefCell::new(None)),
|
||||
read_mutex: Mutex::new(()),
|
||||
write_mutex: Mutex::new(()),
|
||||
rx_channel: Channel::new(),
|
||||
tx_channel: Channel::new(),
|
||||
tx_buf_channel: Channel::new(),
|
||||
};
|
||||
|
||||
for buf in tx_buf_queue {
|
||||
this.tx_buf_channel.try_send(buf).unwrap();
|
||||
}
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
pub async fn run(&'a self) -> ! {
|
||||
join::join(
|
||||
async {
|
||||
loop {
|
||||
if let Ok(mac_event) = self.mac_subsystem.read().await {
|
||||
match mac_event {
|
||||
MacEvent::McpsDataInd(_) => {
|
||||
self.rx_channel.send(mac_event).await;
|
||||
}
|
||||
_ => {
|
||||
self.rx_event_channel.lock(|s| {
|
||||
match &*s.borrow() {
|
||||
Some(signal) => {
|
||||
signal.signal(mac_event);
|
||||
}
|
||||
None => {}
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
async {
|
||||
let mut msdu_handle = 0x02;
|
||||
|
||||
loop {
|
||||
let (buf, len) = self.tx_channel.recv().await;
|
||||
let _wm = self.write_mutex.lock().await;
|
||||
|
||||
// The mutex should be dropped on the next loop iteration
|
||||
self.mac_subsystem
|
||||
.send_command(
|
||||
DataRequest {
|
||||
src_addr_mode: AddressMode::Short,
|
||||
dst_addr_mode: AddressMode::Short,
|
||||
dst_pan_id: PanId([0x1A, 0xAA]),
|
||||
dst_address: MacAddress::BROADCAST,
|
||||
msdu_handle: msdu_handle,
|
||||
ack_tx: 0x00,
|
||||
gts_tx: false,
|
||||
security_level: SecurityLevel::Unsecure,
|
||||
..Default::default()
|
||||
}
|
||||
.set_buffer(&buf[..len]),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
msdu_handle = msdu_handle.wrapping_add(1);
|
||||
|
||||
// The tx channel should always be of equal capacity to the tx_buf channel
|
||||
self.tx_buf_channel.try_send(buf).unwrap();
|
||||
}
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
loop {}
|
||||
}
|
||||
}
|
381
embassy-stm32-wpan/src/mac/typedefs.rs
Normal file
381
embassy-stm32-wpan/src/mac/typedefs.rs
Normal file
@ -0,0 +1,381 @@
|
||||
use core::fmt::Debug;
|
||||
|
||||
use crate::numeric_enum;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum MacError {
|
||||
Error = 0x01,
|
||||
NotImplemented = 0x02,
|
||||
NotSupported = 0x03,
|
||||
HardwareNotSupported = 0x04,
|
||||
Undefined = 0x05,
|
||||
}
|
||||
|
||||
impl From<u8> for MacError {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
0x01 => Self::Error,
|
||||
0x02 => Self::NotImplemented,
|
||||
0x03 => Self::NotSupported,
|
||||
0x04 => Self::HardwareNotSupported,
|
||||
0x05 => Self::Undefined,
|
||||
_ => Self::Undefined,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
numeric_enum! {
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Default)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum MacStatus {
|
||||
#[default]
|
||||
Success = 0x00,
|
||||
Failure = 0xFF
|
||||
}
|
||||
}
|
||||
|
||||
numeric_enum! {
|
||||
#[repr(u8)]
|
||||
/// this enum contains all the MAC PIB Ids
|
||||
#[derive(Default, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum PibId {
|
||||
// PHY
|
||||
#[default]
|
||||
CurrentChannel = 0x00,
|
||||
ChannelsSupported = 0x01,
|
||||
TransmitPower = 0x02,
|
||||
CCAMode = 0x03,
|
||||
CurrentPage = 0x04,
|
||||
MaxFrameDuration = 0x05,
|
||||
SHRDuration = 0x06,
|
||||
SymbolsPerOctet = 0x07,
|
||||
|
||||
// MAC
|
||||
AckWaitDuration = 0x40,
|
||||
AssociationPermit = 0x41,
|
||||
AutoRequest = 0x42,
|
||||
BeaconPayload = 0x45,
|
||||
BeaconPayloadLength = 0x46,
|
||||
BeaconOrder = 0x47,
|
||||
Bsn = 0x49,
|
||||
CoordExtendedAdddress = 0x4A,
|
||||
CoordShortAddress = 0x4B,
|
||||
Dsn = 0x4C,
|
||||
MaxFrameTotalWaitTime = 0x58,
|
||||
MaxFrameRetries = 0x59,
|
||||
PanId = 0x50,
|
||||
ResponseWaitTime = 0x5A,
|
||||
RxOnWhenIdle = 0x52,
|
||||
SecurityEnabled = 0x5D,
|
||||
ShortAddress = 0x53,
|
||||
SuperframeOrder = 0x54,
|
||||
TimestampSupported = 0x5C,
|
||||
TransactionPersistenceTime = 0x55,
|
||||
MaxBe = 0x57,
|
||||
LifsPeriod = 0x5E,
|
||||
SifsPeriod = 0x5F,
|
||||
MaxCsmaBackoffs = 0x4E,
|
||||
MinBe = 0x4F,
|
||||
PanCoordinator = 0x10,
|
||||
AssocPanCoordinator = 0x11,
|
||||
ExtendedAddress = 0x6F,
|
||||
AclEntryDescriptor = 0x70,
|
||||
AclEntryDescriptorSize = 0x71,
|
||||
DefaultSecurity = 0x72,
|
||||
DefaultSecurityMaterialLength = 0x73,
|
||||
DefaultSecurityMaterial = 0x74,
|
||||
DefaultSecuritySuite = 0x75,
|
||||
SecurityMode = 0x76,
|
||||
CurrentAclEntries = 0x80,
|
||||
DefaultSecurityExtendedAddress = 0x81,
|
||||
AssociatedPanCoordinator = 0x56,
|
||||
PromiscuousMode = 0x51,
|
||||
}
|
||||
}
|
||||
|
||||
numeric_enum! {
|
||||
#[repr(u8)]
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum AddressMode {
|
||||
#[default]
|
||||
NoAddress = 0x00,
|
||||
Reserved = 0x01,
|
||||
Short = 0x02,
|
||||
Extended = 0x03,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub union MacAddress {
|
||||
pub short: [u8; 2],
|
||||
pub extended: [u8; 8],
|
||||
}
|
||||
|
||||
impl Debug for MacAddress {
|
||||
fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
unsafe {
|
||||
write!(
|
||||
fmt,
|
||||
"MacAddress {{ short: {:?}, extended: {:?} }}",
|
||||
self.short, self.extended
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
impl defmt::Format for MacAddress {
|
||||
fn format(&self, fmt: defmt::Formatter) {
|
||||
unsafe {
|
||||
defmt::write!(
|
||||
fmt,
|
||||
"MacAddress {{ short: {}, extended: {} }}",
|
||||
self.short,
|
||||
self.extended
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MacAddress {
|
||||
fn default() -> Self {
|
||||
Self { short: [0, 0] }
|
||||
}
|
||||
}
|
||||
|
||||
impl MacAddress {
|
||||
pub const BROADCAST: Self = Self { short: [0xFF, 0xFF] };
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for MacAddress {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
|
||||
const SIZE: usize = 8;
|
||||
if buf.len() < SIZE {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
extended: [buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct GtsCharacteristics {
|
||||
pub fields: u8,
|
||||
}
|
||||
|
||||
/// MAC PAN Descriptor which contains the network details of the device from
|
||||
/// which the beacon is received
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct PanDescriptor {
|
||||
/// PAN identifier of the coordinator
|
||||
pub coord_pan_id: PanId,
|
||||
/// Coordinator addressing mode
|
||||
pub coord_addr_mode: AddressMode,
|
||||
/// The current logical channel occupied by the network
|
||||
pub logical_channel: MacChannel,
|
||||
/// Coordinator address
|
||||
pub coord_addr: MacAddress,
|
||||
/// The current channel page occupied by the network
|
||||
pub channel_page: u8,
|
||||
/// PAN coordinator is accepting GTS requests or not
|
||||
pub gts_permit: bool,
|
||||
/// Superframe specification as specified in the received beacon frame
|
||||
pub superframe_spec: [u8; 2],
|
||||
/// The time at which the beacon frame was received, in symbols
|
||||
pub time_stamp: [u8; 4],
|
||||
/// The LQI at which the network beacon was received
|
||||
pub link_quality: u8,
|
||||
/// Security level purportedly used by the received beacon frame
|
||||
pub security_level: u8,
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for PanDescriptor {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
|
||||
const SIZE: usize = 22;
|
||||
if buf.len() < SIZE {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let coord_addr_mode = AddressMode::try_from(buf[2])?;
|
||||
let coord_addr = match coord_addr_mode {
|
||||
AddressMode::NoAddress => MacAddress { short: [0, 0] },
|
||||
AddressMode::Reserved => MacAddress { short: [0, 0] },
|
||||
AddressMode::Short => MacAddress {
|
||||
short: [buf[4], buf[5]],
|
||||
},
|
||||
AddressMode::Extended => MacAddress {
|
||||
extended: [buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]],
|
||||
},
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
coord_pan_id: PanId([buf[0], buf[1]]),
|
||||
coord_addr_mode,
|
||||
logical_channel: MacChannel::try_from(buf[3])?,
|
||||
coord_addr,
|
||||
channel_page: buf[12],
|
||||
gts_permit: buf[13] != 0,
|
||||
superframe_spec: [buf[14], buf[15]],
|
||||
time_stamp: [buf[16], buf[17], buf[18], buf[19]],
|
||||
link_quality: buf[20],
|
||||
security_level: buf[21],
|
||||
// 2 byte stuffing
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
numeric_enum! {
|
||||
#[repr(u8)]
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Building wireless applications with STM32WB series MCUs - Application note 13.10.3
|
||||
pub enum MacChannel {
|
||||
Channel11 = 0x0B,
|
||||
Channel12 = 0x0C,
|
||||
Channel13 = 0x0D,
|
||||
Channel14 = 0x0E,
|
||||
Channel15 = 0x0F,
|
||||
#[default]
|
||||
Channel16 = 0x10,
|
||||
Channel17 = 0x11,
|
||||
Channel18 = 0x12,
|
||||
Channel19 = 0x13,
|
||||
Channel20 = 0x14,
|
||||
Channel21 = 0x15,
|
||||
Channel22 = 0x16,
|
||||
Channel23 = 0x17,
|
||||
Channel24 = 0x18,
|
||||
Channel25 = 0x19,
|
||||
Channel26 = 0x1A,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
bitflags::bitflags! {
|
||||
pub struct Capabilities: u8 {
|
||||
/// 1 if the device is capabaleof becoming a PAN coordinator
|
||||
const IS_COORDINATOR_CAPABLE = 0b00000001;
|
||||
/// 1 if the device is an FFD, 0 if it is an RFD
|
||||
const IS_FFD = 0b00000010;
|
||||
/// 1 if the device is receiving power from mains, 0 if it is battery-powered
|
||||
const IS_MAINS_POWERED = 0b00000100;
|
||||
/// 1 if the device does not disable its receiver to conserver power during idle periods
|
||||
const RECEIVER_ON_WHEN_IDLE = 0b00001000;
|
||||
// 0b00010000 reserved
|
||||
// 0b00100000 reserved
|
||||
/// 1 if the device is capable of sending and receiving secured MAC frames
|
||||
const IS_SECURE = 0b01000000;
|
||||
/// 1 if the device wishes the coordinator to allocate a short address as a result of the association
|
||||
const ALLOCATE_ADDRESS = 0b10000000;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
defmt::bitflags! {
|
||||
pub struct Capabilities: u8 {
|
||||
/// 1 if the device is capabaleof becoming a PAN coordinator
|
||||
const IS_COORDINATOR_CAPABLE = 0b00000001;
|
||||
/// 1 if the device is an FFD, 0 if it is an RFD
|
||||
const IS_FFD = 0b00000010;
|
||||
/// 1 if the device is receiving power from mains, 0 if it is battery-powered
|
||||
const IS_MAINS_POWERED = 0b00000100;
|
||||
/// 1 if the device does not disable its receiver to conserver power during idle periods
|
||||
const RECEIVER_ON_WHEN_IDLE = 0b00001000;
|
||||
// 0b00010000 reserved
|
||||
// 0b00100000 reserved
|
||||
/// 1 if the device is capable of sending and receiving secured MAC frames
|
||||
const IS_SECURE = 0b01000000;
|
||||
/// 1 if the device wishes the coordinator to allocate a short address as a result of the association
|
||||
const ALLOCATE_ADDRESS = 0b10000000;
|
||||
}
|
||||
}
|
||||
|
||||
numeric_enum! {
|
||||
#[repr(u8)]
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum KeyIdMode {
|
||||
#[default]
|
||||
/// the key is determined implicitly from the originator and recipient(s) of the frame
|
||||
Implicite = 0x00,
|
||||
/// the key is determined explicitly using a 1 bytes key source and a 1 byte key index
|
||||
Explicite1Byte = 0x01,
|
||||
/// the key is determined explicitly using a 4 bytes key source and a 1 byte key index
|
||||
Explicite4Byte = 0x02,
|
||||
/// the key is determined explicitly using a 8 bytes key source and a 1 byte key index
|
||||
Explicite8Byte = 0x03,
|
||||
}
|
||||
}
|
||||
|
||||
numeric_enum! {
|
||||
#[repr(u8)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum AssociationStatus {
|
||||
/// Association successful
|
||||
Success = 0x00,
|
||||
/// PAN at capacity
|
||||
PanAtCapacity = 0x01,
|
||||
/// PAN access denied
|
||||
PanAccessDenied = 0x02
|
||||
}
|
||||
}
|
||||
|
||||
numeric_enum! {
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum DisassociationReason {
|
||||
/// The coordinator wishes the device to leave the PAN.
|
||||
CoordRequested = 0x01,
|
||||
/// The device wishes to leave the PAN.
|
||||
DeviceRequested = 0x02,
|
||||
}
|
||||
}
|
||||
|
||||
numeric_enum! {
|
||||
#[repr(u8)]
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum SecurityLevel {
|
||||
/// MAC Unsecured Mode Security
|
||||
#[default]
|
||||
Unsecure = 0x00,
|
||||
/// MAC ACL Mode Security
|
||||
AclMode = 0x01,
|
||||
/// MAC Secured Mode Security
|
||||
Secured = 0x02,
|
||||
}
|
||||
}
|
||||
|
||||
numeric_enum! {
|
||||
#[repr(u8)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum ScanType {
|
||||
EdScan = 0x00,
|
||||
Active = 0x01,
|
||||
Passive = 0x02,
|
||||
Orphan = 0x03
|
||||
}
|
||||
}
|
||||
|
||||
/// newtype for Pan Id
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct PanId(pub [u8; 2]);
|
||||
|
||||
impl PanId {
|
||||
pub const BROADCAST: Self = Self([0xFF, 0xFF]);
|
||||
}
|
@ -11,9 +11,10 @@ use embassy_sync::waitqueue::AtomicWaker;
|
||||
use crate::cmd::CmdPacket;
|
||||
use crate::consts::TlPacketType;
|
||||
use crate::evt::{EvtBox, EvtPacket};
|
||||
use crate::tables::{
|
||||
Mac802_15_4Table, MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER, TL_MAC_802_15_4_TABLE,
|
||||
};
|
||||
use crate::mac::commands::MacCommand;
|
||||
use crate::mac::event::MacEvent;
|
||||
use crate::mac::typedefs::MacError;
|
||||
use crate::tables::{MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER};
|
||||
use crate::{channels, evt};
|
||||
|
||||
static MAC_WAKER: AtomicWaker = AtomicWaker::new();
|
||||
@ -25,21 +26,13 @@ pub struct Mac {
|
||||
|
||||
impl Mac {
|
||||
pub(crate) fn new() -> Self {
|
||||
unsafe {
|
||||
TL_MAC_802_15_4_TABLE.as_mut_ptr().write_volatile(Mac802_15_4Table {
|
||||
p_cmdrsp_buffer: MAC_802_15_4_CMD_BUFFER.as_mut_ptr().cast(),
|
||||
p_notack_buffer: MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr().cast(),
|
||||
evt_queue: ptr::null_mut(),
|
||||
});
|
||||
}
|
||||
|
||||
Self { phantom: PhantomData }
|
||||
}
|
||||
|
||||
/// `HW_IPCC_MAC_802_15_4_EvtNot`
|
||||
///
|
||||
/// This function will stall if the previous `EvtBox` has not been dropped
|
||||
pub async fn read(&self) -> EvtBox<Self> {
|
||||
pub async fn tl_read(&self) -> EvtBox<Self> {
|
||||
// Wait for the last event box to be dropped
|
||||
poll_fn(|cx| {
|
||||
MAC_WAKER.register(cx.waker());
|
||||
@ -63,9 +56,9 @@ impl Mac {
|
||||
}
|
||||
|
||||
/// `HW_IPCC_MAC_802_15_4_CmdEvtNot`
|
||||
pub async fn write_and_get_response(&self, opcode: u16, payload: &[u8]) -> u8 {
|
||||
self.write(opcode, payload).await;
|
||||
Ipcc::flush(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL).await;
|
||||
pub async fn tl_write_and_get_response(&self, opcode: u16, payload: &[u8]) -> u8 {
|
||||
self.tl_write(opcode, payload).await;
|
||||
Ipcc::flush(channels::cpu1::IPCC_MAC_802_15_4_CMD_RSP_CHANNEL).await;
|
||||
|
||||
unsafe {
|
||||
let p_event_packet = MAC_802_15_4_CMD_BUFFER.as_ptr() as *const EvtPacket;
|
||||
@ -76,22 +69,41 @@ impl Mac {
|
||||
}
|
||||
|
||||
/// `TL_MAC_802_15_4_SendCmd`
|
||||
pub async fn write(&self, opcode: u16, payload: &[u8]) {
|
||||
pub async fn tl_write(&self, opcode: u16, payload: &[u8]) {
|
||||
Ipcc::send(channels::cpu1::IPCC_MAC_802_15_4_CMD_RSP_CHANNEL, || unsafe {
|
||||
CmdPacket::write_into(
|
||||
MAC_802_15_4_CMD_BUFFER.as_mut_ptr(),
|
||||
TlPacketType::OtCmd,
|
||||
TlPacketType::MacCmd,
|
||||
opcode,
|
||||
payload,
|
||||
);
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn send_command<T>(&self, cmd: &T) -> Result<(), MacError>
|
||||
where
|
||||
T: MacCommand,
|
||||
{
|
||||
let response = self.tl_write_and_get_response(T::OPCODE as u16, cmd.payload()).await;
|
||||
|
||||
if response == 0x00 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(MacError::from(response))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn read(&self) -> Result<MacEvent<'_>, ()> {
|
||||
MacEvent::new(self.tl_read().await)
|
||||
}
|
||||
}
|
||||
|
||||
impl evt::MemoryManager for Mac {
|
||||
/// SAFETY: passing a pointer to something other than a managed event packet is UB
|
||||
unsafe fn drop_event_packet(_: *mut EvtPacket) {
|
||||
trace!("mac drop event");
|
||||
|
||||
// Write the ack
|
||||
CmdPacket::write_into(
|
||||
MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _,
|
||||
@ -101,7 +113,7 @@ impl evt::MemoryManager for Mac {
|
||||
);
|
||||
|
||||
// Clear the rx flag
|
||||
let _ = poll_once(Ipcc::receive::<bool>(
|
||||
let _ = poll_once(Ipcc::receive::<()>(
|
||||
channels::cpu2::IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL,
|
||||
|| None,
|
||||
));
|
||||
|
@ -4,20 +4,21 @@ use core::marker::PhantomData;
|
||||
use core::mem::MaybeUninit;
|
||||
use core::task::Poll;
|
||||
|
||||
use aligned::{Aligned, A4};
|
||||
use cortex_m::interrupt;
|
||||
use embassy_stm32::ipcc::Ipcc;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
use crate::consts::POOL_SIZE;
|
||||
use crate::evt::EvtPacket;
|
||||
use crate::tables::{
|
||||
MemManagerTable, BLE_SPARE_EVT_BUF, EVT_POOL, FREE_BUF_QUEUE, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE,
|
||||
};
|
||||
#[cfg(feature = "ble")]
|
||||
use crate::tables::BLE_SPARE_EVT_BUF;
|
||||
use crate::tables::{MemManagerTable, EVT_POOL, FREE_BUF_QUEUE, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE};
|
||||
use crate::unsafe_linked_list::LinkedListNode;
|
||||
use crate::{channels, evt};
|
||||
|
||||
static MM_WAKER: AtomicWaker = AtomicWaker::new();
|
||||
static mut LOCAL_FREE_BUF_QUEUE: MaybeUninit<LinkedListNode> = MaybeUninit::uninit();
|
||||
static mut LOCAL_FREE_BUF_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit());
|
||||
|
||||
pub struct MemoryManager {
|
||||
phantom: PhantomData<MemoryManager>,
|
||||
@ -30,7 +31,10 @@ impl MemoryManager {
|
||||
LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr());
|
||||
|
||||
TL_MEM_MANAGER_TABLE.as_mut_ptr().write_volatile(MemManagerTable {
|
||||
#[cfg(feature = "ble")]
|
||||
spare_ble_buffer: BLE_SPARE_EVT_BUF.as_ptr().cast(),
|
||||
#[cfg(not(feature = "ble"))]
|
||||
spare_ble_buffer: core::ptr::null(),
|
||||
spare_sys_buffer: SYS_SPARE_EVT_BUF.as_ptr().cast(),
|
||||
blepool: EVT_POOL.as_ptr().cast(),
|
||||
blepoolsize: POOL_SIZE as u32,
|
||||
|
@ -50,7 +50,7 @@ impl Sys {
|
||||
}
|
||||
|
||||
/// `HW_IPCC_SYS_CmdEvtNot`
|
||||
pub async fn write_and_get_response(&self, opcode: ShciOpcode, payload: &[u8]) -> SchiCommandStatus {
|
||||
pub async fn write_and_get_response(&self, opcode: ShciOpcode, payload: &[u8]) -> Result<SchiCommandStatus, ()> {
|
||||
self.write(opcode, payload).await;
|
||||
Ipcc::flush(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL).await;
|
||||
|
||||
@ -59,17 +59,36 @@ impl Sys {
|
||||
let p_command_event = &((*p_event_packet).evt_serial.evt.payload) as *const _ as *const CcEvt;
|
||||
let p_payload = &((*p_command_event).payload) as *const u8;
|
||||
|
||||
ptr::read_volatile(p_payload).try_into().unwrap()
|
||||
ptr::read_volatile(p_payload).try_into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mac")]
|
||||
pub async fn shci_c2_mac_802_15_4_init(&self) -> SchiCommandStatus {
|
||||
pub async fn shci_c2_mac_802_15_4_init(&self) -> Result<SchiCommandStatus, ()> {
|
||||
use crate::tables::{
|
||||
Mac802_15_4Table, TracesTable, MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER,
|
||||
TL_MAC_802_15_4_TABLE, TL_TRACES_TABLE, TRACES_EVT_QUEUE,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
LinkedListNode::init_head(TRACES_EVT_QUEUE.as_mut_ptr() as *mut _);
|
||||
|
||||
TL_TRACES_TABLE.as_mut_ptr().write_volatile(TracesTable {
|
||||
traces_queue: TRACES_EVT_QUEUE.as_ptr() as *const _,
|
||||
});
|
||||
|
||||
TL_MAC_802_15_4_TABLE.as_mut_ptr().write_volatile(Mac802_15_4Table {
|
||||
p_cmdrsp_buffer: MAC_802_15_4_CMD_BUFFER.as_mut_ptr().cast(),
|
||||
p_notack_buffer: MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr().cast(),
|
||||
evt_queue: core::ptr::null_mut(),
|
||||
});
|
||||
};
|
||||
|
||||
self.write_and_get_response(ShciOpcode::Mac802_15_4Init, &[]).await
|
||||
}
|
||||
|
||||
#[cfg(feature = "ble")]
|
||||
pub async fn shci_c2_ble_init(&self, param: ShciBleInitCmdParam) -> SchiCommandStatus {
|
||||
pub async fn shci_c2_ble_init(&self, param: ShciBleInitCmdParam) -> Result<SchiCommandStatus, ()> {
|
||||
self.write_and_get_response(ShciOpcode::BleInit, param.payload()).await
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,8 @@ use aligned::{Aligned, A4};
|
||||
use bit_field::BitField;
|
||||
|
||||
use crate::cmd::{AclDataPacket, CmdPacket};
|
||||
#[cfg(feature = "mac")]
|
||||
use crate::consts::C_SIZE_CMD_STRING;
|
||||
use crate::consts::{POOL_SIZE, TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE};
|
||||
use crate::unsafe_linked_list::LinkedListNode;
|
||||
|
||||
@ -80,7 +82,7 @@ impl WirelessFwInfoTable {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[repr(C, align(4))]
|
||||
#[repr(C)]
|
||||
pub struct DeviceInfoTable {
|
||||
pub safe_boot_info_table: SafeBootInfoTable,
|
||||
pub rss_info_table: RssInfoTable,
|
||||
@ -88,7 +90,7 @@ pub struct DeviceInfoTable {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C, align(4))]
|
||||
#[repr(C)]
|
||||
pub struct BleTable {
|
||||
pub pcmd_buffer: *mut CmdPacket,
|
||||
pub pcs_buffer: *const u8,
|
||||
@ -97,16 +99,15 @@ pub struct BleTable {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C, align(4))]
|
||||
#[repr(C)]
|
||||
pub struct ThreadTable {
|
||||
pub nostack_buffer: *const u8,
|
||||
pub clicmdrsp_buffer: *const u8,
|
||||
pub otcmdrsp_buffer: *const u8,
|
||||
}
|
||||
|
||||
// TODO: use later
|
||||
#[derive(Debug)]
|
||||
#[repr(C, align(4))]
|
||||
#[repr(C)]
|
||||
pub struct LldTestsTable {
|
||||
pub clicmdrsp_buffer: *const u8,
|
||||
pub m0cmd_buffer: *const u8,
|
||||
@ -114,7 +115,7 @@ pub struct LldTestsTable {
|
||||
|
||||
// TODO: use later
|
||||
#[derive(Debug)]
|
||||
#[repr(C, align(4))]
|
||||
#[repr(C)]
|
||||
pub struct BleLldTable {
|
||||
pub cmdrsp_buffer: *const u8,
|
||||
pub m0cmd_buffer: *const u8,
|
||||
@ -122,7 +123,7 @@ pub struct BleLldTable {
|
||||
|
||||
// TODO: use later
|
||||
#[derive(Debug)]
|
||||
#[repr(C, align(4))]
|
||||
#[repr(C)]
|
||||
pub struct ZigbeeTable {
|
||||
pub notif_m0_to_m4_buffer: *const u8,
|
||||
pub appli_cmd_m4_to_m0_bufer: *const u8,
|
||||
@ -130,14 +131,14 @@ pub struct ZigbeeTable {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C, align(4))]
|
||||
#[repr(C)]
|
||||
pub struct SysTable {
|
||||
pub pcmd_buffer: *mut CmdPacket,
|
||||
pub sys_queue: *const LinkedListNode,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C, align(4))]
|
||||
#[repr(C)]
|
||||
pub struct MemManagerTable {
|
||||
pub spare_ble_buffer: *const u8,
|
||||
pub spare_sys_buffer: *const u8,
|
||||
@ -152,13 +153,13 @@ pub struct MemManagerTable {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C, align(4))]
|
||||
#[repr(C)]
|
||||
pub struct TracesTable {
|
||||
pub traces_queue: *const u8,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C, align(4))]
|
||||
#[repr(C)]
|
||||
pub struct Mac802_15_4Table {
|
||||
pub p_cmdrsp_buffer: *const u8,
|
||||
pub p_notack_buffer: *const u8,
|
||||
@ -176,6 +177,9 @@ pub struct RefTable {
|
||||
pub mem_manager_table: *const MemManagerTable,
|
||||
pub traces_table: *const TracesTable,
|
||||
pub mac_802_15_4_table: *const Mac802_15_4Table,
|
||||
pub zigbee_table: *const ZigbeeTable,
|
||||
pub lld_tests_table: *const LldTestsTable,
|
||||
pub ble_lld_table: *const BleLldTable,
|
||||
}
|
||||
|
||||
// --------------------- ref table ---------------------
|
||||
@ -183,57 +187,57 @@ pub struct RefTable {
|
||||
pub static mut TL_REF_TABLE: MaybeUninit<RefTable> = MaybeUninit::uninit();
|
||||
|
||||
#[link_section = "MB_MEM1"]
|
||||
pub static mut TL_DEVICE_INFO_TABLE: MaybeUninit<DeviceInfoTable> = MaybeUninit::uninit();
|
||||
pub static mut TL_DEVICE_INFO_TABLE: Aligned<A4, MaybeUninit<DeviceInfoTable>> = Aligned(MaybeUninit::uninit());
|
||||
|
||||
#[link_section = "MB_MEM1"]
|
||||
pub static mut TL_BLE_TABLE: MaybeUninit<BleTable> = MaybeUninit::uninit();
|
||||
pub static mut TL_BLE_TABLE: Aligned<A4, MaybeUninit<BleTable>> = Aligned(MaybeUninit::uninit());
|
||||
|
||||
#[link_section = "MB_MEM1"]
|
||||
pub static mut TL_THREAD_TABLE: MaybeUninit<ThreadTable> = MaybeUninit::uninit();
|
||||
|
||||
// #[link_section = "MB_MEM1"]
|
||||
// pub static mut TL_LLD_TESTS_TABLE: MaybeUninit<LldTestTable> = MaybeUninit::uninit();
|
||||
|
||||
// #[link_section = "MB_MEM1"]
|
||||
// pub static mut TL_BLE_LLD_TABLE: MaybeUninit<BleLldTable> = MaybeUninit::uninit();
|
||||
pub static mut TL_THREAD_TABLE: Aligned<A4, MaybeUninit<ThreadTable>> = Aligned(MaybeUninit::uninit());
|
||||
|
||||
#[link_section = "MB_MEM1"]
|
||||
pub static mut TL_SYS_TABLE: MaybeUninit<SysTable> = MaybeUninit::uninit();
|
||||
pub static mut TL_LLD_TESTS_TABLE: Aligned<A4, MaybeUninit<LldTestsTable>> = Aligned(MaybeUninit::uninit());
|
||||
|
||||
#[link_section = "MB_MEM1"]
|
||||
pub static mut TL_MEM_MANAGER_TABLE: MaybeUninit<MemManagerTable> = MaybeUninit::uninit();
|
||||
pub static mut TL_BLE_LLD_TABLE: Aligned<A4, MaybeUninit<BleLldTable>> = Aligned(MaybeUninit::uninit());
|
||||
|
||||
#[link_section = "MB_MEM1"]
|
||||
pub static mut TL_TRACES_TABLE: MaybeUninit<TracesTable> = MaybeUninit::uninit();
|
||||
pub static mut TL_SYS_TABLE: Aligned<A4, MaybeUninit<SysTable>> = Aligned(MaybeUninit::uninit());
|
||||
|
||||
#[link_section = "MB_MEM1"]
|
||||
pub static mut TL_MAC_802_15_4_TABLE: MaybeUninit<Mac802_15_4Table> = MaybeUninit::uninit();
|
||||
pub static mut TL_MEM_MANAGER_TABLE: Aligned<A4, MaybeUninit<MemManagerTable>> = Aligned(MaybeUninit::uninit());
|
||||
|
||||
// #[link_section = "MB_MEM1"]
|
||||
// pub static mut TL_ZIGBEE_TABLE: MaybeUninit<ZigbeeTable> = MaybeUninit::uninit();
|
||||
#[link_section = "MB_MEM1"]
|
||||
pub static mut TL_TRACES_TABLE: Aligned<A4, MaybeUninit<TracesTable>> = Aligned(MaybeUninit::uninit());
|
||||
|
||||
#[link_section = "MB_MEM1"]
|
||||
pub static mut TL_MAC_802_15_4_TABLE: Aligned<A4, MaybeUninit<Mac802_15_4Table>> = Aligned(MaybeUninit::uninit());
|
||||
|
||||
#[link_section = "MB_MEM1"]
|
||||
pub static mut TL_ZIGBEE_TABLE: Aligned<A4, MaybeUninit<ZigbeeTable>> = Aligned(MaybeUninit::uninit());
|
||||
|
||||
// --------------------- tables ---------------------
|
||||
#[link_section = "MB_MEM1"]
|
||||
pub static mut FREE_BUF_QUEUE: MaybeUninit<LinkedListNode> = MaybeUninit::uninit();
|
||||
pub static mut FREE_BUF_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit());
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[link_section = "MB_MEM1"]
|
||||
pub static mut TRACES_EVT_QUEUE: MaybeUninit<LinkedListNode> = MaybeUninit::uninit();
|
||||
pub static mut TRACES_EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit());
|
||||
|
||||
#[link_section = "MB_MEM2"]
|
||||
pub static mut CS_BUFFER: MaybeUninit<Aligned<A4, [u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE]>> =
|
||||
MaybeUninit::uninit();
|
||||
pub static mut CS_BUFFER: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE]>> =
|
||||
Aligned(MaybeUninit::uninit());
|
||||
|
||||
#[link_section = "MB_MEM2"]
|
||||
pub static mut EVT_QUEUE: MaybeUninit<LinkedListNode> = MaybeUninit::uninit();
|
||||
pub static mut EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit());
|
||||
|
||||
#[link_section = "MB_MEM2"]
|
||||
pub static mut SYSTEM_EVT_QUEUE: MaybeUninit<LinkedListNode> = MaybeUninit::uninit();
|
||||
pub static mut SYSTEM_EVT_QUEUE: Aligned<A4, MaybeUninit<LinkedListNode>> = Aligned(MaybeUninit::uninit());
|
||||
|
||||
// --------------------- app tables ---------------------
|
||||
#[cfg(feature = "mac")]
|
||||
#[link_section = "MB_MEM2"]
|
||||
pub static mut MAC_802_15_4_CMD_BUFFER: MaybeUninit<CmdPacket> = MaybeUninit::uninit();
|
||||
pub static mut MAC_802_15_4_CMD_BUFFER: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::uninit());
|
||||
|
||||
#[cfg(feature = "mac")]
|
||||
#[link_section = "MB_MEM2"]
|
||||
@ -242,23 +246,31 @@ pub static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit<
|
||||
> = MaybeUninit::uninit();
|
||||
|
||||
#[link_section = "MB_MEM2"]
|
||||
pub static mut EVT_POOL: MaybeUninit<Aligned<A4, [u8; POOL_SIZE]>> = MaybeUninit::uninit();
|
||||
pub static mut EVT_POOL: Aligned<A4, MaybeUninit<[u8; POOL_SIZE]>> = Aligned(MaybeUninit::uninit());
|
||||
|
||||
#[link_section = "MB_MEM2"]
|
||||
pub static mut SYS_CMD_BUF: MaybeUninit<CmdPacket> = MaybeUninit::uninit();
|
||||
pub static mut SYS_CMD_BUF: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::uninit());
|
||||
|
||||
#[link_section = "MB_MEM2"]
|
||||
pub static mut SYS_SPARE_EVT_BUF: MaybeUninit<Aligned<A4, [u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> =
|
||||
MaybeUninit::uninit();
|
||||
pub static mut SYS_SPARE_EVT_BUF: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> =
|
||||
Aligned(MaybeUninit::uninit());
|
||||
|
||||
#[cfg(feature = "mac")]
|
||||
#[link_section = "MB_MEM2"]
|
||||
pub static mut MAC_802_15_4_CNFINDNOT: Aligned<A4, MaybeUninit<[u8; C_SIZE_CMD_STRING]>> =
|
||||
Aligned(MaybeUninit::uninit());
|
||||
|
||||
#[cfg(feature = "ble")]
|
||||
#[link_section = "MB_MEM1"]
|
||||
pub static mut BLE_CMD_BUFFER: MaybeUninit<CmdPacket> = MaybeUninit::uninit();
|
||||
pub static mut BLE_CMD_BUFFER: Aligned<A4, MaybeUninit<CmdPacket>> = Aligned(MaybeUninit::uninit());
|
||||
|
||||
#[cfg(feature = "ble")]
|
||||
#[link_section = "MB_MEM2"]
|
||||
pub static mut BLE_SPARE_EVT_BUF: MaybeUninit<Aligned<A4, [u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> =
|
||||
MaybeUninit::uninit();
|
||||
pub static mut BLE_SPARE_EVT_BUF: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + TL_EVT_HEADER_SIZE + 255]>> =
|
||||
Aligned(MaybeUninit::uninit());
|
||||
|
||||
#[cfg(feature = "ble")]
|
||||
#[link_section = "MB_MEM2"]
|
||||
// fuck these "magic" numbers from ST ---v---v
|
||||
pub static mut HCI_ACL_DATA_BUFFER: MaybeUninit<Aligned<A4, [u8; TL_PACKET_HEADER_SIZE + 5 + 251]>> =
|
||||
MaybeUninit::uninit();
|
||||
pub static mut HCI_ACL_DATA_BUFFER: Aligned<A4, MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]>> =
|
||||
Aligned(MaybeUninit::uninit());
|
||||
|
@ -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"]
|
||||
|
@ -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(|_| {
|
||||
|
@ -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) => {
|
||||
|
@ -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.")]
|
||||
|
@ -1,3 +1,4 @@
|
||||
use core::cell::{RefCell, RefMut};
|
||||
use core::future::poll_fn;
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
@ -72,7 +73,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::SCEInterrupt> for SceInterrup
|
||||
}
|
||||
|
||||
pub struct Can<'d, T: Instance> {
|
||||
can: bxcan::Can<BxcanInstance<'d, T>>,
|
||||
pub can: RefCell<bxcan::Can<BxcanInstance<'d, T>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -147,19 +148,24 @@ impl<'d, T: Instance> Can<'d, T> {
|
||||
tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
|
||||
|
||||
let can = bxcan::Can::builder(BxcanInstance(peri)).leave_disabled();
|
||||
Self { can }
|
||||
let can_ref_cell = RefCell::new(can);
|
||||
Self { can: can_ref_cell }
|
||||
}
|
||||
|
||||
pub fn set_bitrate(&mut self, bitrate: u32) {
|
||||
let bit_timing = Self::calc_bxcan_timings(T::frequency(), bitrate).unwrap();
|
||||
self.can.modify_config().set_bit_timing(bit_timing).leave_disabled();
|
||||
self.can
|
||||
.borrow_mut()
|
||||
.modify_config()
|
||||
.set_bit_timing(bit_timing)
|
||||
.leave_disabled();
|
||||
}
|
||||
|
||||
/// Queues the message to be sent but exerts backpressure
|
||||
pub async fn write(&mut self, frame: &Frame) -> bxcan::TransmitStatus {
|
||||
poll_fn(|cx| {
|
||||
T::state().tx_waker.register(cx.waker());
|
||||
if let Ok(status) = self.can.transmit(frame) {
|
||||
if let Ok(status) = self.can.borrow_mut().transmit(frame) {
|
||||
return Poll::Ready(status);
|
||||
}
|
||||
|
||||
@ -341,6 +347,79 @@ impl<'d, T: Instance> Can<'d, T> {
|
||||
// Pack into BTR register values
|
||||
Some((sjw - 1) << 24 | (bs1 as u32 - 1) << 16 | (bs2 as u32 - 1) << 20 | (prescaler as u32 - 1))
|
||||
}
|
||||
|
||||
pub fn split<'c>(&'c self) -> (CanTx<'c, 'd, T>, CanRx<'c, 'd, T>) {
|
||||
(CanTx { can: &self.can }, CanRx { can: &self.can })
|
||||
}
|
||||
|
||||
pub fn as_mut(&self) -> RefMut<'_, bxcan::Can<BxcanInstance<'d, T>>> {
|
||||
self.can.borrow_mut()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CanTx<'c, 'd, T: Instance> {
|
||||
can: &'c RefCell<bxcan::Can<BxcanInstance<'d, T>>>,
|
||||
}
|
||||
|
||||
impl<'c, 'd, T: Instance> CanTx<'c, 'd, T> {
|
||||
pub async fn write(&mut self, frame: &Frame) -> bxcan::TransmitStatus {
|
||||
poll_fn(|cx| {
|
||||
T::state().tx_waker.register(cx.waker());
|
||||
if let Ok(status) = self.can.borrow_mut().transmit(frame) {
|
||||
return Poll::Ready(status);
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn flush(&self, mb: bxcan::Mailbox) {
|
||||
poll_fn(|cx| {
|
||||
T::state().tx_waker.register(cx.waker());
|
||||
if T::regs().tsr().read().tme(mb.index()) {
|
||||
return Poll::Ready(());
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct CanRx<'c, 'd, T: Instance> {
|
||||
can: &'c RefCell<bxcan::Can<BxcanInstance<'d, T>>>,
|
||||
}
|
||||
|
||||
impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> {
|
||||
pub async fn read(&mut self) -> Result<(u16, bxcan::Frame), BusError> {
|
||||
poll_fn(|cx| {
|
||||
T::state().err_waker.register(cx.waker());
|
||||
if let Poll::Ready((time, frame)) = T::state().rx_queue.recv().poll_unpin(cx) {
|
||||
return Poll::Ready(Ok((time, frame)));
|
||||
} else if let Some(err) = self.curr_error() {
|
||||
return Poll::Ready(Err(err));
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
fn curr_error(&self) -> Option<BusError> {
|
||||
let err = { T::regs().esr().read() };
|
||||
if err.boff() {
|
||||
return Some(BusError::BusOff);
|
||||
} else if err.epvf() {
|
||||
return Some(BusError::BusPassive);
|
||||
} else if err.ewgf() {
|
||||
return Some(BusError::BusWarning);
|
||||
} else if let Some(err) = err.lec().into_bus_err() {
|
||||
return Some(err);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
enum RxFifo {
|
||||
@ -358,7 +437,7 @@ impl<'d, T: Instance> Drop for Can<'d, T> {
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Deref for Can<'d, T> {
|
||||
type Target = bxcan::Can<BxcanInstance<'d, T>>;
|
||||
type Target = RefCell<bxcan::Can<BxcanInstance<'d, T>>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.can
|
||||
|
66
embassy-stm32/src/can/fdcan.rs
Normal file
66
embassy-stm32/src/can/fdcan.rs
Normal file
@ -0,0 +1,66 @@
|
||||
pub use bxcan;
|
||||
use embassy_hal_common::PeripheralRef;
|
||||
|
||||
use crate::peripherals;
|
||||
|
||||
pub(crate) mod sealed {
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::channel::Channel;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
pub struct State {
|
||||
pub tx_waker: AtomicWaker,
|
||||
pub err_waker: AtomicWaker,
|
||||
pub rx_queue: Channel<CriticalSectionRawMutex, (u16, bxcan::Frame), 32>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
tx_waker: AtomicWaker::new(),
|
||||
err_waker: AtomicWaker::new(),
|
||||
rx_queue: Channel::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Instance {
|
||||
const REGISTERS: *mut bxcan::RegisterBlock;
|
||||
|
||||
fn regs() -> &'static crate::pac::can::Fdcan;
|
||||
fn state() -> &'static State;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait InterruptableInstance {}
|
||||
pub trait Instance: sealed::Instance + InterruptableInstance + 'static {}
|
||||
|
||||
pub struct BxcanInstance<'a, T>(PeripheralRef<'a, T>);
|
||||
|
||||
unsafe impl<'d, T: Instance> bxcan::Instance for BxcanInstance<'d, T> {
|
||||
const REGISTERS: *mut bxcan::RegisterBlock = T::REGISTERS;
|
||||
}
|
||||
|
||||
foreach_peripheral!(
|
||||
(can, $inst:ident) => {
|
||||
impl sealed::Instance for peripherals::$inst {
|
||||
const REGISTERS: *mut bxcan::RegisterBlock = crate::pac::$inst.as_ptr() as *mut _;
|
||||
|
||||
fn regs() -> &'static crate::pac::can::Fdcan {
|
||||
&crate::pac::$inst
|
||||
}
|
||||
|
||||
fn state() -> &'static sealed::State {
|
||||
static STATE: sealed::State = sealed::State::new();
|
||||
&STATE
|
||||
}
|
||||
}
|
||||
|
||||
impl Instance for peripherals::$inst {}
|
||||
|
||||
impl InterruptableInstance for peripherals::$inst {}
|
||||
};
|
||||
);
|
||||
|
||||
pin_trait!(RxPin, Instance);
|
||||
pin_trait!(TxPin, Instance);
|
@ -1,5 +1,6 @@
|
||||
#![macro_use]
|
||||
|
||||
#[cfg_attr(can_bxcan, path = "bxcan.rs")]
|
||||
#[cfg_attr(can_fdcan, path = "fdcan.rs")]
|
||||
mod _version;
|
||||
pub use _version::*;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
|
@ -1,5 +1,11 @@
|
||||
//! Generic SMI Ethernet PHY
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
use embassy_time::{Duration, Timer};
|
||||
use futures::task::Context;
|
||||
#[cfg(feature = "time")]
|
||||
use futures::FutureExt;
|
||||
|
||||
use super::{StationManagement, PHY};
|
||||
|
||||
#[allow(dead_code)]
|
||||
@ -36,25 +42,47 @@ mod phy_consts {
|
||||
use self::phy_consts::*;
|
||||
|
||||
/// Generic SMI Ethernet PHY
|
||||
pub struct GenericSMI;
|
||||
pub struct GenericSMI {
|
||||
#[cfg(feature = "time")]
|
||||
poll_interval: Duration,
|
||||
#[cfg(not(feature = "time"))]
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl GenericSMI {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
#[cfg(feature = "time")]
|
||||
poll_interval: Duration::from_millis(500),
|
||||
#[cfg(not(feature = "time"))]
|
||||
_private: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl PHY for GenericSMI {
|
||||
/// Reset PHY and wait for it to come out of reset.
|
||||
fn phy_reset<S: StationManagement>(sm: &mut S) {
|
||||
fn phy_reset<S: StationManagement>(&mut self, sm: &mut S) {
|
||||
sm.smi_write(PHY_REG_BCR, PHY_REG_BCR_RESET);
|
||||
while sm.smi_read(PHY_REG_BCR) & PHY_REG_BCR_RESET == PHY_REG_BCR_RESET {}
|
||||
}
|
||||
|
||||
/// PHY initialisation.
|
||||
fn phy_init<S: StationManagement>(sm: &mut S) {
|
||||
fn phy_init<S: StationManagement>(&mut self, sm: &mut S) {
|
||||
// Clear WU CSR
|
||||
Self::smi_write_ext(sm, PHY_REG_WUCSR, 0);
|
||||
self.smi_write_ext(sm, PHY_REG_WUCSR, 0);
|
||||
|
||||
// Enable auto-negotiation
|
||||
sm.smi_write(PHY_REG_BCR, PHY_REG_BCR_AN | PHY_REG_BCR_ANRST | PHY_REG_BCR_100M);
|
||||
}
|
||||
|
||||
fn poll_link<S: StationManagement>(sm: &mut S) -> bool {
|
||||
fn poll_link<S: StationManagement>(&mut self, sm: &mut S, cx: &mut Context) -> bool {
|
||||
#[cfg(not(feature = "time"))]
|
||||
cx.waker().wake_by_ref();
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
let _ = Timer::after(self.poll_interval).poll_unpin(cx);
|
||||
|
||||
let bsr = sm.smi_read(PHY_REG_BSR);
|
||||
|
||||
// No link without autonegotiate
|
||||
@ -73,8 +101,13 @@ unsafe impl PHY for GenericSMI {
|
||||
|
||||
/// Public functions for the PHY
|
||||
impl GenericSMI {
|
||||
#[cfg(feature = "time")]
|
||||
pub fn set_poll_interval(&mut self, poll_interval: Duration) {
|
||||
self.poll_interval = poll_interval
|
||||
}
|
||||
|
||||
// Writes a value to an extended PHY register in MMD address space
|
||||
fn smi_write_ext<S: StationManagement>(sm: &mut S, reg_addr: u16, reg_data: u16) {
|
||||
fn smi_write_ext<S: StationManagement>(&mut self, sm: &mut S, reg_addr: u16, reg_data: u16) {
|
||||
sm.smi_write(PHY_REG_CTL, 0x0003); // set address
|
||||
sm.smi_write(PHY_REG_ADDAR, reg_addr);
|
||||
sm.smi_write(PHY_REG_CTL, 0x4003); // set data
|
||||
|
@ -81,9 +81,7 @@ impl<'d, T: Instance, P: PHY> embassy_net_driver::Driver for Ethernet<'d, T, P>
|
||||
}
|
||||
|
||||
fn link_state(&mut self, cx: &mut Context) -> LinkState {
|
||||
// TODO: wake cx.waker on link state change
|
||||
cx.waker().wake_by_ref();
|
||||
if P::poll_link(self) {
|
||||
if self.phy.poll_link(&mut self.station_management, cx) {
|
||||
LinkState::Up
|
||||
} else {
|
||||
LinkState::Down
|
||||
@ -148,11 +146,11 @@ pub unsafe trait StationManagement {
|
||||
/// The methods cannot move S
|
||||
pub unsafe trait PHY {
|
||||
/// Reset PHY and wait for it to come out of reset.
|
||||
fn phy_reset<S: StationManagement>(sm: &mut S);
|
||||
fn phy_reset<S: StationManagement>(&mut self, sm: &mut S);
|
||||
/// PHY initialisation.
|
||||
fn phy_init<S: StationManagement>(sm: &mut S);
|
||||
fn phy_init<S: StationManagement>(&mut self, sm: &mut S);
|
||||
/// Poll link to see if it is up and FD with 100Mbps
|
||||
fn poll_link<S: StationManagement>(sm: &mut S) -> bool;
|
||||
fn poll_link<S: StationManagement>(&mut self, sm: &mut S, cx: &mut Context) -> bool;
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
|
@ -3,6 +3,7 @@
|
||||
mod rx_desc;
|
||||
mod tx_desc;
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
@ -48,9 +49,8 @@ pub struct Ethernet<'d, T: Instance, P: PHY> {
|
||||
pub(crate) rx: RDesRing<'d>,
|
||||
|
||||
pins: [PeripheralRef<'d, AnyPin>; 9],
|
||||
_phy: P,
|
||||
clock_range: Cr,
|
||||
phy_addr: u8,
|
||||
pub(crate) phy: P,
|
||||
pub(crate) station_management: EthernetStationManagement<T>,
|
||||
pub(crate) mac_addr: [u8; 6],
|
||||
}
|
||||
|
||||
@ -224,9 +224,12 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
||||
let mut this = Self {
|
||||
_peri: peri,
|
||||
pins,
|
||||
_phy: phy,
|
||||
clock_range,
|
||||
phy_addr,
|
||||
phy: phy,
|
||||
station_management: EthernetStationManagement {
|
||||
peri: PhantomData,
|
||||
clock_range: clock_range,
|
||||
phy_addr: phy_addr,
|
||||
},
|
||||
mac_addr,
|
||||
tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf),
|
||||
rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf),
|
||||
@ -256,8 +259,8 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
||||
w.set_tie(true);
|
||||
});
|
||||
|
||||
P::phy_reset(&mut this);
|
||||
P::phy_init(&mut this);
|
||||
this.phy.phy_reset(&mut this.station_management);
|
||||
this.phy.phy_init(&mut this.station_management);
|
||||
|
||||
interrupt::ETH.unpend();
|
||||
unsafe { interrupt::ETH.enable() };
|
||||
@ -266,7 +269,13 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'d, T: Instance, P: PHY> StationManagement for Ethernet<'d, T, P> {
|
||||
pub struct EthernetStationManagement<T: Instance> {
|
||||
peri: PhantomData<T>,
|
||||
clock_range: Cr,
|
||||
phy_addr: u8,
|
||||
}
|
||||
|
||||
unsafe impl<T: Instance> StationManagement for EthernetStationManagement<T> {
|
||||
fn smi_read(&mut self, reg: u8) -> u16 {
|
||||
let mac = ETH.ethernet_mac();
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
mod descriptors;
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use core::sync::atomic::{fence, Ordering};
|
||||
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
@ -40,9 +41,8 @@ pub struct Ethernet<'d, T: Instance, P: PHY> {
|
||||
pub(crate) tx: TDesRing<'d>,
|
||||
pub(crate) rx: RDesRing<'d>,
|
||||
pins: [PeripheralRef<'d, AnyPin>; 9],
|
||||
_phy: P,
|
||||
clock_range: u8,
|
||||
phy_addr: u8,
|
||||
pub(crate) phy: P,
|
||||
pub(crate) station_management: EthernetStationManagement<T>,
|
||||
pub(crate) mac_addr: [u8; 6],
|
||||
}
|
||||
|
||||
@ -201,9 +201,12 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
||||
tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf),
|
||||
rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf),
|
||||
pins,
|
||||
_phy: phy,
|
||||
clock_range,
|
||||
phy_addr,
|
||||
phy: phy,
|
||||
station_management: EthernetStationManagement {
|
||||
peri: PhantomData,
|
||||
clock_range: clock_range,
|
||||
phy_addr: phy_addr,
|
||||
},
|
||||
mac_addr,
|
||||
};
|
||||
|
||||
@ -229,8 +232,8 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
||||
w.set_tie(true);
|
||||
});
|
||||
|
||||
P::phy_reset(&mut this);
|
||||
P::phy_init(&mut this);
|
||||
this.phy.phy_reset(&mut this.station_management);
|
||||
this.phy.phy_init(&mut this.station_management);
|
||||
|
||||
interrupt::ETH.unpend();
|
||||
unsafe { interrupt::ETH.enable() };
|
||||
@ -239,7 +242,13 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'d, T: Instance, P: PHY> StationManagement for Ethernet<'d, T, P> {
|
||||
pub struct EthernetStationManagement<T: Instance> {
|
||||
peri: PhantomData<T>,
|
||||
clock_range: u8,
|
||||
phy_addr: u8,
|
||||
}
|
||||
|
||||
unsafe impl<T: Instance> StationManagement for EthernetStationManagement<T> {
|
||||
fn smi_read(&mut self, reg: u8) -> u16 {
|
||||
let mac = ETH.ethernet_mac();
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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: [
|
||||
|
@ -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> {
|
||||
|
@ -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;
|
||||
|
@ -1,332 +1,332 @@
|
||||
#![macro_use]
|
||||
|
||||
pub mod enums;
|
||||
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use enums::*;
|
||||
|
||||
use crate::dma::Transfer;
|
||||
use crate::gpio::sealed::AFType;
|
||||
use crate::gpio::AnyPin;
|
||||
use crate::pac::quadspi::Quadspi as Regs;
|
||||
use crate::rcc::RccPeripheral;
|
||||
use crate::{peripherals, Peripheral};
|
||||
|
||||
pub struct TransferConfig {
|
||||
/// Instraction width (IMODE)
|
||||
pub iwidth: QspiWidth,
|
||||
/// Address width (ADMODE)
|
||||
pub awidth: QspiWidth,
|
||||
/// Data width (DMODE)
|
||||
pub dwidth: QspiWidth,
|
||||
/// Instruction Id
|
||||
pub instruction: u8,
|
||||
/// Flash memory address
|
||||
pub address: Option<u32>,
|
||||
/// Number of dummy cycles (DCYC)
|
||||
pub dummy: DummyCycles,
|
||||
/// Length of data
|
||||
pub data_len: Option<usize>,
|
||||
}
|
||||
|
||||
impl Default for TransferConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
iwidth: QspiWidth::NONE,
|
||||
awidth: QspiWidth::NONE,
|
||||
dwidth: QspiWidth::NONE,
|
||||
instruction: 0,
|
||||
address: None,
|
||||
dummy: DummyCycles::_0,
|
||||
data_len: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Config {
|
||||
/// Flash memory size representend as 2^[0-32], as reasonable minimum 1KiB(9) was chosen.
|
||||
/// If you need other value the whose predefined use `Other` variant.
|
||||
pub memory_size: MemorySize,
|
||||
/// Address size (8/16/24/32-bit)
|
||||
pub address_size: AddressSize,
|
||||
/// Scalar factor for generating CLK [0-255]
|
||||
pub prescaler: u8,
|
||||
/// Number of bytes to trigger FIFO threshold flag.
|
||||
pub fifo_threshold: FIFOThresholdLevel,
|
||||
/// Minimum number of cycles that chip select must be high between issued commands
|
||||
pub cs_high_time: ChipSelectHightTime,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
memory_size: MemorySize::Other(0),
|
||||
address_size: AddressSize::_24bit,
|
||||
prescaler: 128,
|
||||
fifo_threshold: FIFOThresholdLevel::_17Bytes,
|
||||
cs_high_time: ChipSelectHightTime::_5Cycle,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct Qspi<'d, T: Instance, Dma> {
|
||||
_peri: PeripheralRef<'d, T>,
|
||||
sck: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d0: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d1: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d2: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d3: Option<PeripheralRef<'d, AnyPin>>,
|
||||
nss: Option<PeripheralRef<'d, AnyPin>>,
|
||||
dma: PeripheralRef<'d, Dma>,
|
||||
config: Config,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
|
||||
pub fn new(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
|
||||
d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
|
||||
d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
|
||||
dma: impl Peripheral<P = Dma> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(peri, d0, d1, d2, d3, sck, nss);
|
||||
|
||||
sck.set_as_af(sck.af_num(), AFType::OutputPushPull);
|
||||
sck.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
nss.set_as_af(nss.af_num(), AFType::OutputPushPull);
|
||||
nss.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
d0.set_as_af(d0.af_num(), AFType::OutputPushPull);
|
||||
d0.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
d1.set_as_af(d1.af_num(), AFType::OutputPushPull);
|
||||
d1.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
d2.set_as_af(d2.af_num(), AFType::OutputPushPull);
|
||||
d2.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
d3.set_as_af(d3.af_num(), AFType::OutputPushPull);
|
||||
d3.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
|
||||
Self::new_inner(
|
||||
peri,
|
||||
Some(d0.map_into()),
|
||||
Some(d1.map_into()),
|
||||
Some(d2.map_into()),
|
||||
Some(d3.map_into()),
|
||||
Some(sck.map_into()),
|
||||
Some(nss.map_into()),
|
||||
dma,
|
||||
config,
|
||||
)
|
||||
}
|
||||
|
||||
fn new_inner(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
d0: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d1: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d2: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d3: Option<PeripheralRef<'d, AnyPin>>,
|
||||
sck: Option<PeripheralRef<'d, AnyPin>>,
|
||||
nss: Option<PeripheralRef<'d, AnyPin>>,
|
||||
dma: impl Peripheral<P = Dma> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(peri, dma);
|
||||
|
||||
T::enable();
|
||||
T::REGS.cr().write(|w| w.set_fthres(config.fifo_threshold.into()));
|
||||
|
||||
while T::REGS.sr().read().busy() {}
|
||||
|
||||
T::REGS.cr().write(|w| {
|
||||
w.set_prescaler(config.prescaler);
|
||||
w.set_en(true);
|
||||
});
|
||||
T::REGS.dcr().write(|w| {
|
||||
w.set_fsize(config.memory_size.into());
|
||||
w.set_csht(config.cs_high_time.into());
|
||||
w.set_ckmode(false);
|
||||
});
|
||||
|
||||
Self {
|
||||
_peri: peri,
|
||||
sck,
|
||||
d0,
|
||||
d1,
|
||||
d2,
|
||||
d3,
|
||||
nss,
|
||||
dma,
|
||||
config,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn command(&mut self, transaction: TransferConfig) {
|
||||
T::REGS.cr().modify(|v| v.set_dmaen(false));
|
||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||
|
||||
while !T::REGS.sr().read().tcf() {}
|
||||
T::REGS.fcr().modify(|v| v.set_ctcf(true));
|
||||
}
|
||||
|
||||
pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) {
|
||||
T::REGS.cr().modify(|v| v.set_dmaen(false));
|
||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||
|
||||
if let Some(len) = transaction.data_len {
|
||||
let current_ar = T::REGS.ar().read().address();
|
||||
T::REGS.ccr().modify(|v| {
|
||||
v.set_fmode(QspiMode::IndirectRead.into());
|
||||
});
|
||||
T::REGS.ar().write(|v| {
|
||||
v.set_address(current_ar);
|
||||
});
|
||||
|
||||
for idx in 0..len {
|
||||
while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {}
|
||||
buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() };
|
||||
}
|
||||
}
|
||||
|
||||
while !T::REGS.sr().read().tcf() {}
|
||||
T::REGS.fcr().modify(|v| v.set_ctcf(true));
|
||||
}
|
||||
|
||||
pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) {
|
||||
T::REGS.cr().modify(|v| v.set_dmaen(false));
|
||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||
|
||||
if let Some(len) = transaction.data_len {
|
||||
T::REGS.ccr().modify(|v| {
|
||||
v.set_fmode(QspiMode::IndirectWrite.into());
|
||||
});
|
||||
|
||||
for idx in 0..len {
|
||||
while !T::REGS.sr().read().ftf() {}
|
||||
unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(buf[idx]) };
|
||||
}
|
||||
}
|
||||
|
||||
while !T::REGS.sr().read().tcf() {}
|
||||
T::REGS.fcr().modify(|v| v.set_ctcf(true));
|
||||
}
|
||||
|
||||
pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig)
|
||||
where
|
||||
Dma: QuadDma<T>,
|
||||
{
|
||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||
|
||||
T::REGS.ccr().modify(|v| {
|
||||
v.set_fmode(QspiMode::IndirectRead.into());
|
||||
});
|
||||
let current_ar = T::REGS.ar().read().address();
|
||||
T::REGS.ar().write(|v| {
|
||||
v.set_address(current_ar);
|
||||
});
|
||||
|
||||
let request = self.dma.request();
|
||||
let transfer = unsafe {
|
||||
Transfer::new_read(
|
||||
&mut self.dma,
|
||||
request,
|
||||
T::REGS.dr().as_ptr() as *mut u8,
|
||||
buf,
|
||||
Default::default(),
|
||||
)
|
||||
};
|
||||
|
||||
T::REGS.cr().modify(|v| v.set_dmaen(true));
|
||||
|
||||
transfer.blocking_wait();
|
||||
}
|
||||
|
||||
pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig)
|
||||
where
|
||||
Dma: QuadDma<T>,
|
||||
{
|
||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||
|
||||
T::REGS.ccr().modify(|v| {
|
||||
v.set_fmode(QspiMode::IndirectWrite.into());
|
||||
});
|
||||
|
||||
let request = self.dma.request();
|
||||
let transfer = unsafe {
|
||||
Transfer::new_write(
|
||||
&mut self.dma,
|
||||
request,
|
||||
buf,
|
||||
T::REGS.dr().as_ptr() as *mut u8,
|
||||
Default::default(),
|
||||
)
|
||||
};
|
||||
|
||||
T::REGS.cr().modify(|v| v.set_dmaen(true));
|
||||
|
||||
transfer.blocking_wait();
|
||||
}
|
||||
|
||||
fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig) {
|
||||
T::REGS.fcr().modify(|v| {
|
||||
v.set_csmf(true);
|
||||
v.set_ctcf(true);
|
||||
v.set_ctef(true);
|
||||
v.set_ctof(true);
|
||||
});
|
||||
|
||||
while T::REGS.sr().read().busy() {}
|
||||
|
||||
if let Some(len) = transaction.data_len {
|
||||
T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1));
|
||||
}
|
||||
|
||||
T::REGS.ccr().write(|v| {
|
||||
v.set_fmode(fmode.into());
|
||||
v.set_imode(transaction.iwidth.into());
|
||||
v.set_instruction(transaction.instruction);
|
||||
v.set_admode(transaction.awidth.into());
|
||||
v.set_adsize(self.config.address_size.into());
|
||||
v.set_dmode(transaction.dwidth.into());
|
||||
v.set_abmode(QspiWidth::NONE.into());
|
||||
v.set_dcyc(transaction.dummy.into());
|
||||
});
|
||||
|
||||
if let Some(addr) = transaction.address {
|
||||
T::REGS.ar().write(|v| {
|
||||
v.set_address(addr);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
use super::*;
|
||||
|
||||
pub trait Instance {
|
||||
const REGS: Regs;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {}
|
||||
|
||||
pin_trait!(SckPin, Instance);
|
||||
pin_trait!(D0Pin, Instance);
|
||||
pin_trait!(D1Pin, Instance);
|
||||
pin_trait!(D2Pin, Instance);
|
||||
pin_trait!(D3Pin, Instance);
|
||||
pin_trait!(NSSPin, Instance);
|
||||
|
||||
dma_trait!(QuadDma, Instance);
|
||||
|
||||
foreach_peripheral!(
|
||||
(quadspi, $inst:ident) => {
|
||||
impl sealed::Instance for peripherals::$inst {
|
||||
const REGS: Regs = crate::pac::$inst;
|
||||
}
|
||||
|
||||
impl Instance for peripherals::$inst {}
|
||||
};
|
||||
);
|
||||
#![macro_use]
|
||||
|
||||
pub mod enums;
|
||||
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use enums::*;
|
||||
|
||||
use crate::dma::Transfer;
|
||||
use crate::gpio::sealed::AFType;
|
||||
use crate::gpio::AnyPin;
|
||||
use crate::pac::quadspi::Quadspi as Regs;
|
||||
use crate::rcc::RccPeripheral;
|
||||
use crate::{peripherals, Peripheral};
|
||||
|
||||
pub struct TransferConfig {
|
||||
/// Instraction width (IMODE)
|
||||
pub iwidth: QspiWidth,
|
||||
/// Address width (ADMODE)
|
||||
pub awidth: QspiWidth,
|
||||
/// Data width (DMODE)
|
||||
pub dwidth: QspiWidth,
|
||||
/// Instruction Id
|
||||
pub instruction: u8,
|
||||
/// Flash memory address
|
||||
pub address: Option<u32>,
|
||||
/// Number of dummy cycles (DCYC)
|
||||
pub dummy: DummyCycles,
|
||||
/// Length of data
|
||||
pub data_len: Option<usize>,
|
||||
}
|
||||
|
||||
impl Default for TransferConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
iwidth: QspiWidth::NONE,
|
||||
awidth: QspiWidth::NONE,
|
||||
dwidth: QspiWidth::NONE,
|
||||
instruction: 0,
|
||||
address: None,
|
||||
dummy: DummyCycles::_0,
|
||||
data_len: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Config {
|
||||
/// Flash memory size representend as 2^[0-32], as reasonable minimum 1KiB(9) was chosen.
|
||||
/// If you need other value the whose predefined use `Other` variant.
|
||||
pub memory_size: MemorySize,
|
||||
/// Address size (8/16/24/32-bit)
|
||||
pub address_size: AddressSize,
|
||||
/// Scalar factor for generating CLK [0-255]
|
||||
pub prescaler: u8,
|
||||
/// Number of bytes to trigger FIFO threshold flag.
|
||||
pub fifo_threshold: FIFOThresholdLevel,
|
||||
/// Minimum number of cycles that chip select must be high between issued commands
|
||||
pub cs_high_time: ChipSelectHightTime,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
memory_size: MemorySize::Other(0),
|
||||
address_size: AddressSize::_24bit,
|
||||
prescaler: 128,
|
||||
fifo_threshold: FIFOThresholdLevel::_17Bytes,
|
||||
cs_high_time: ChipSelectHightTime::_5Cycle,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct Qspi<'d, T: Instance, Dma> {
|
||||
_peri: PeripheralRef<'d, T>,
|
||||
sck: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d0: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d1: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d2: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d3: Option<PeripheralRef<'d, AnyPin>>,
|
||||
nss: Option<PeripheralRef<'d, AnyPin>>,
|
||||
dma: PeripheralRef<'d, Dma>,
|
||||
config: Config,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
|
||||
pub fn new(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
|
||||
d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
|
||||
d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
|
||||
d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
|
||||
sck: impl Peripheral<P = impl SckPin<T>> + 'd,
|
||||
nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
|
||||
dma: impl Peripheral<P = Dma> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(peri, d0, d1, d2, d3, sck, nss);
|
||||
|
||||
sck.set_as_af(sck.af_num(), AFType::OutputPushPull);
|
||||
sck.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
nss.set_as_af(nss.af_num(), AFType::OutputPushPull);
|
||||
nss.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
d0.set_as_af(d0.af_num(), AFType::OutputPushPull);
|
||||
d0.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
d1.set_as_af(d1.af_num(), AFType::OutputPushPull);
|
||||
d1.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
d2.set_as_af(d2.af_num(), AFType::OutputPushPull);
|
||||
d2.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
d3.set_as_af(d3.af_num(), AFType::OutputPushPull);
|
||||
d3.set_speed(crate::gpio::Speed::VeryHigh);
|
||||
|
||||
Self::new_inner(
|
||||
peri,
|
||||
Some(d0.map_into()),
|
||||
Some(d1.map_into()),
|
||||
Some(d2.map_into()),
|
||||
Some(d3.map_into()),
|
||||
Some(sck.map_into()),
|
||||
Some(nss.map_into()),
|
||||
dma,
|
||||
config,
|
||||
)
|
||||
}
|
||||
|
||||
fn new_inner(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
d0: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d1: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d2: Option<PeripheralRef<'d, AnyPin>>,
|
||||
d3: Option<PeripheralRef<'d, AnyPin>>,
|
||||
sck: Option<PeripheralRef<'d, AnyPin>>,
|
||||
nss: Option<PeripheralRef<'d, AnyPin>>,
|
||||
dma: impl Peripheral<P = Dma> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(peri, dma);
|
||||
|
||||
T::enable();
|
||||
T::REGS.cr().write(|w| w.set_fthres(config.fifo_threshold.into()));
|
||||
|
||||
while T::REGS.sr().read().busy() {}
|
||||
|
||||
T::REGS.cr().write(|w| {
|
||||
w.set_prescaler(config.prescaler);
|
||||
w.set_en(true);
|
||||
});
|
||||
T::REGS.dcr().write(|w| {
|
||||
w.set_fsize(config.memory_size.into());
|
||||
w.set_csht(config.cs_high_time.into());
|
||||
w.set_ckmode(false);
|
||||
});
|
||||
|
||||
Self {
|
||||
_peri: peri,
|
||||
sck,
|
||||
d0,
|
||||
d1,
|
||||
d2,
|
||||
d3,
|
||||
nss,
|
||||
dma,
|
||||
config,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn command(&mut self, transaction: TransferConfig) {
|
||||
T::REGS.cr().modify(|v| v.set_dmaen(false));
|
||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||
|
||||
while !T::REGS.sr().read().tcf() {}
|
||||
T::REGS.fcr().modify(|v| v.set_ctcf(true));
|
||||
}
|
||||
|
||||
pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) {
|
||||
T::REGS.cr().modify(|v| v.set_dmaen(false));
|
||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||
|
||||
if let Some(len) = transaction.data_len {
|
||||
let current_ar = T::REGS.ar().read().address();
|
||||
T::REGS.ccr().modify(|v| {
|
||||
v.set_fmode(QspiMode::IndirectRead.into());
|
||||
});
|
||||
T::REGS.ar().write(|v| {
|
||||
v.set_address(current_ar);
|
||||
});
|
||||
|
||||
for idx in 0..len {
|
||||
while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {}
|
||||
buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() };
|
||||
}
|
||||
}
|
||||
|
||||
while !T::REGS.sr().read().tcf() {}
|
||||
T::REGS.fcr().modify(|v| v.set_ctcf(true));
|
||||
}
|
||||
|
||||
pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) {
|
||||
T::REGS.cr().modify(|v| v.set_dmaen(false));
|
||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||
|
||||
if let Some(len) = transaction.data_len {
|
||||
T::REGS.ccr().modify(|v| {
|
||||
v.set_fmode(QspiMode::IndirectWrite.into());
|
||||
});
|
||||
|
||||
for idx in 0..len {
|
||||
while !T::REGS.sr().read().ftf() {}
|
||||
unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(buf[idx]) };
|
||||
}
|
||||
}
|
||||
|
||||
while !T::REGS.sr().read().tcf() {}
|
||||
T::REGS.fcr().modify(|v| v.set_ctcf(true));
|
||||
}
|
||||
|
||||
pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig)
|
||||
where
|
||||
Dma: QuadDma<T>,
|
||||
{
|
||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||
|
||||
T::REGS.ccr().modify(|v| {
|
||||
v.set_fmode(QspiMode::IndirectRead.into());
|
||||
});
|
||||
let current_ar = T::REGS.ar().read().address();
|
||||
T::REGS.ar().write(|v| {
|
||||
v.set_address(current_ar);
|
||||
});
|
||||
|
||||
let request = self.dma.request();
|
||||
let transfer = unsafe {
|
||||
Transfer::new_read(
|
||||
&mut self.dma,
|
||||
request,
|
||||
T::REGS.dr().as_ptr() as *mut u8,
|
||||
buf,
|
||||
Default::default(),
|
||||
)
|
||||
};
|
||||
|
||||
T::REGS.cr().modify(|v| v.set_dmaen(true));
|
||||
|
||||
transfer.blocking_wait();
|
||||
}
|
||||
|
||||
pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig)
|
||||
where
|
||||
Dma: QuadDma<T>,
|
||||
{
|
||||
self.setup_transaction(QspiMode::IndirectWrite, &transaction);
|
||||
|
||||
T::REGS.ccr().modify(|v| {
|
||||
v.set_fmode(QspiMode::IndirectWrite.into());
|
||||
});
|
||||
|
||||
let request = self.dma.request();
|
||||
let transfer = unsafe {
|
||||
Transfer::new_write(
|
||||
&mut self.dma,
|
||||
request,
|
||||
buf,
|
||||
T::REGS.dr().as_ptr() as *mut u8,
|
||||
Default::default(),
|
||||
)
|
||||
};
|
||||
|
||||
T::REGS.cr().modify(|v| v.set_dmaen(true));
|
||||
|
||||
transfer.blocking_wait();
|
||||
}
|
||||
|
||||
fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig) {
|
||||
T::REGS.fcr().modify(|v| {
|
||||
v.set_csmf(true);
|
||||
v.set_ctcf(true);
|
||||
v.set_ctef(true);
|
||||
v.set_ctof(true);
|
||||
});
|
||||
|
||||
while T::REGS.sr().read().busy() {}
|
||||
|
||||
if let Some(len) = transaction.data_len {
|
||||
T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1));
|
||||
}
|
||||
|
||||
T::REGS.ccr().write(|v| {
|
||||
v.set_fmode(fmode.into());
|
||||
v.set_imode(transaction.iwidth.into());
|
||||
v.set_instruction(transaction.instruction);
|
||||
v.set_admode(transaction.awidth.into());
|
||||
v.set_adsize(self.config.address_size.into());
|
||||
v.set_dmode(transaction.dwidth.into());
|
||||
v.set_abmode(QspiWidth::NONE.into());
|
||||
v.set_dcyc(transaction.dummy.into());
|
||||
});
|
||||
|
||||
if let Some(addr) = transaction.address {
|
||||
T::REGS.ar().write(|v| {
|
||||
v.set_address(addr);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
use super::*;
|
||||
|
||||
pub trait Instance {
|
||||
const REGS: Regs;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Instance: Peripheral<P = Self> + sealed::Instance + RccPeripheral {}
|
||||
|
||||
pin_trait!(SckPin, Instance);
|
||||
pin_trait!(D0Pin, Instance);
|
||||
pin_trait!(D1Pin, Instance);
|
||||
pin_trait!(D2Pin, Instance);
|
||||
pin_trait!(D3Pin, Instance);
|
||||
pin_trait!(NSSPin, Instance);
|
||||
|
||||
dma_trait!(QuadDma, Instance);
|
||||
|
||||
foreach_peripheral!(
|
||||
(quadspi, $inst:ident) => {
|
||||
impl sealed::Instance for peripherals::$inst {
|
||||
const REGS: Regs = crate::pac::$inst;
|
||||
}
|
||||
|
||||
impl Instance for peripherals::$inst {}
|
||||
};
|
||||
);
|
||||
|
@ -473,11 +473,11 @@ fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
|
||||
w.set_divm(0);
|
||||
});
|
||||
|
||||
return PllOutput{
|
||||
return PllOutput {
|
||||
p: None,
|
||||
q: None,
|
||||
r: None,
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
assert!(1 <= config.prediv && config.prediv <= 63);
|
||||
|
@ -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);
|
||||
|
@ -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),
|
||||
|
@ -83,12 +83,12 @@ static mut CLOCK_FREQS: MaybeUninit<Clocks> = MaybeUninit::uninit();
|
||||
/// Safety: Sets a mutable global.
|
||||
pub(crate) unsafe fn set_freqs(freqs: Clocks) {
|
||||
debug!("rcc: {:?}", freqs);
|
||||
CLOCK_FREQS.as_mut_ptr().write(freqs);
|
||||
CLOCK_FREQS = MaybeUninit::new(freqs);
|
||||
}
|
||||
|
||||
/// Safety: Reads a mutable global.
|
||||
pub(crate) unsafe fn get_freqs() -> &'static Clocks {
|
||||
&*CLOCK_FREQS.as_ptr()
|
||||
CLOCK_FREQS.assume_init_ref()
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-pac")]
|
||||
|
@ -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);
|
||||
|
@ -172,6 +172,7 @@ impl sealed::Instance for crate::peripherals::RTC {
|
||||
const BACKUP_REGISTER_COUNT: usize = 32;
|
||||
|
||||
fn read_backup_register(_rtc: &Rtc, register: usize) -> Option<u32> {
|
||||
#[allow(clippy::if_same_then_else)]
|
||||
if register < Self::BACKUP_REGISTER_COUNT {
|
||||
//Some(rtc.bkpr()[register].read().bits())
|
||||
None // RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC
|
||||
|
@ -852,25 +852,19 @@ mod eh1 {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, Tx, Rx> embedded_hal_1::spi::SpiBusFlush for Spi<'d, T, Tx, Rx> {
|
||||
impl<'d, T: Instance, W: Word, Tx, Rx> embedded_hal_1::spi::SpiBus<W> for Spi<'d, T, Tx, Rx> {
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, W: Word, Tx, Rx> embedded_hal_1::spi::SpiBusRead<W> for Spi<'d, T, Tx, Rx> {
|
||||
fn read(&mut self, words: &mut [W]) -> Result<(), Self::Error> {
|
||||
self.blocking_read(words)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, W: Word, Tx, Rx> embedded_hal_1::spi::SpiBusWrite<W> for Spi<'d, T, Tx, Rx> {
|
||||
fn write(&mut self, words: &[W]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(words)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, W: Word, Tx, Rx> embedded_hal_1::spi::SpiBus<W> for Spi<'d, T, Tx, Rx> {
|
||||
fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Self::Error> {
|
||||
self.blocking_transfer(read, write)
|
||||
}
|
||||
@ -895,32 +889,25 @@ mod eh1 {
|
||||
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
|
||||
mod eha {
|
||||
use super::*;
|
||||
impl<'d, T: Instance, Tx, Rx> embedded_hal_async::spi::SpiBusFlush for Spi<'d, T, Tx, Rx> {
|
||||
|
||||
impl<'d, T: Instance, Tx: TxDma<T>, Rx: RxDma<T>, W: Word> embedded_hal_async::spi::SpiBus<W> for Spi<'d, T, Tx, Rx> {
|
||||
async fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, Tx: TxDma<T>, Rx, W: Word> embedded_hal_async::spi::SpiBusWrite<W> for Spi<'d, T, Tx, Rx> {
|
||||
async fn write(&mut self, words: &[W]) -> Result<(), Self::Error> {
|
||||
self.write(words).await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, Tx: TxDma<T>, Rx: RxDma<T>, W: Word> embedded_hal_async::spi::SpiBusRead<W>
|
||||
for Spi<'d, T, Tx, Rx>
|
||||
{
|
||||
async fn read(&mut self, words: &mut [W]) -> Result<(), Self::Error> {
|
||||
self.read(words).await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, Tx: TxDma<T>, Rx: RxDma<T>, W: Word> embedded_hal_async::spi::SpiBus<W> for Spi<'d, T, Tx, Rx> {
|
||||
async fn transfer<'a>(&'a mut self, read: &'a mut [W], write: &'a [W]) -> Result<(), Self::Error> {
|
||||
async fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Self::Error> {
|
||||
self.transfer(read, write).await
|
||||
}
|
||||
|
||||
async fn transfer_in_place<'a>(&'a mut self, words: &'a mut [W]) -> Result<(), Self::Error> {
|
||||
async fn transfer_in_place(&mut self, words: &mut [W]) -> Result<(), Self::Error> {
|
||||
self.transfer_in_place(words).await
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user