Merge branch 'master' of https://github.com/embassy-rs/embassy into embassy-rp/flash

This commit is contained in:
Mathias 2022-10-24 12:14:26 +02:00
commit 8d809c96ec
132 changed files with 4270 additions and 586 deletions

86
.github/workflows/doc.yml vendored Normal file
View File

@ -0,0 +1,86 @@
name: Docs
on:
push:
branches: [master]
env:
BUILDER_THREADS: '1'
jobs:
doc:
runs-on: ubuntu-latest
# Since stm32 crates take SO LONG to build, we split them
# into a separate job. This way it doesn't slow down updating
# the rest.
strategy:
matrix:
crates:
- stm32
- rest
# This will ensure at most one doc build job is running at a time
# (for stm32 and non-stm32 independently).
# If another job is already running, the new job will wait.
# If another job is already waiting, it'll be canceled.
# This means some commits will be skipped, but that's fine because
# we only care that the latest gets built.
concurrency: doc-${{ matrix.crates }}
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Install Rust targets
run: |
rustup target add x86_64-unknown-linux-gnu
rustup target add wasm32-unknown-unknown
rustup target add thumbv6m-none-eabi
rustup target add thumbv7m-none-eabi
rustup target add thumbv7em-none-eabi
rustup target add thumbv7em-none-eabihf
rustup target add thumbv8m.base-none-eabi
rustup target add thumbv8m.main-none-eabi
rustup target add thumbv8m.main-none-eabihf
- name: Install docserver
run: |
wget -q -O /usr/local/bin/builder "https://github.com/embassy-rs/docserver/releases/download/v0.3/builder"
chmod +x /usr/local/bin/builder
- name: build-stm32
if: ${{ matrix.crates=='stm32' }}
run: |
mkdir crates
builder ./embassy-stm32 crates/embassy-stm32/git.zup
builder ./stm32-metapac crates/stm32-metapac/git.zup
- name: build-rest
if: ${{ matrix.crates=='rest' }}
run: |
mkdir crates
builder ./embassy-boot/boot crates/embassy-boot/git.zup
builder ./embassy-boot/nrf crates/embassy-boot-nrf/git.zup
builder ./embassy-boot/stm32 crates/embassy-boot-stm32/git.zup
builder ./embassy-cortex-m crates/embassy-cortex-m/git.zup
builder ./embassy-embedded-hal crates/embassy-embedded-hal/git.zup
builder ./embassy-executor crates/embassy-executor/git.zup
builder ./embassy-futures crates/embassy-futures/git.zup
builder ./embassy-lora crates/embassy-lora/git.zup
builder ./embassy-net crates/embassy-net/git.zup
builder ./embassy-nrf crates/embassy-nrf/git.zup
builder ./embassy-rp crates/embassy-rp/git.zup
builder ./embassy-sync crates/embassy-sync/git.zup
builder ./embassy-time crates/embassy-time/git.zup
builder ./embassy-usb crates/embassy-usb/git.zup
builder ./embassy-usb-driver crates/embassy-usb-driver/git.zup
- name: upload
run: |
mkdir -p ~/.kube
echo "${{secrets.KUBECONFIG}}" > ~/.kube/config
POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name})
kubectl cp crates $POD:/data

View File

@ -11,7 +11,7 @@ env:
jobs:
all:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
needs: [build-nightly, build-stable, test]
steps:
- name: Done

View File

@ -20,10 +20,13 @@
//"embassy-executor/Cargo.toml",
//"embassy-sync/Cargo.toml",
"examples/nrf/Cargo.toml",
// "examples/nrf-rtos-trace/Cargo.toml",
// "examples/rp/Cargo.toml",
// "examples/std/Cargo.toml",
// "examples/stm32f0/Cargo.toml",
// "examples/stm32f1/Cargo.toml",
// "examples/stm32f2/Cargo.toml",
// "examples/stm32f3/Cargo.toml",
// "examples/stm32f4/Cargo.toml",
// "examples/stm32f7/Cargo.toml",
// "examples/stm32g0/Cargo.toml",
@ -32,8 +35,11 @@
// "examples/stm32l0/Cargo.toml",
// "examples/stm32l1/Cargo.toml",
// "examples/stm32l4/Cargo.toml",
// "examples/stm32l5/Cargo.toml",
// "examples/stm32u5/Cargo.toml",
// "examples/stm32wb/Cargo.toml",
// "examples/stm32wb55/Cargo.toml",
// "examples/stm32wl/Cargo.toml",
// "examples/stm32wl55/Cargo.toml",
// "examples/wasm/Cargo.toml",
],

View File

@ -31,7 +31,7 @@ The <a href="https://docs.embassy.dev/embassy-net/">embassy-net</a> network stac
The <a href="https://github.com/embassy-rs/nrf-softdevice">nrf-softdevice</a> crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers.
- **LoRa** -
<a href="https://docs.embassy.dev/embassy-lora/">embassy-lora</a> supports LoRa networking on STM32WL wireless microcontrollers and Semtech SX127x transceivers.
<a href="https://docs.embassy.dev/embassy-lora/">embassy-lora</a> supports LoRa networking on STM32WL wireless microcontrollers and Semtech SX126x and SX127x transceivers.
- **USB** -
<a href="https://docs.embassy.dev/embassy-usb/">embassy-usb</a> implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own.

View File

@ -3,16 +3,16 @@ authors = ["Dario Nieuwenhuis <dirbaio@dirbaio.net>"]
edition = "2018"
name = "embassy-basic-example"
version = "0.1.0"
license = "MIT OR Apache-2.0"
[dependencies]
embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly"] }
embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers"] }
embassy-time = { version = "0.1.0", path = "../../../../../embassy-time", features = ["defmt", "nightly"] }
embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] }
defmt = "0.3"
defmt-rtt = "0.3"
cortex-m = "0.7.3"
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7.0"
embedded-hal = "0.2.6"
panic-probe = { version = "0.3", features = ["print-defmt"] }

View File

@ -0,0 +1,35 @@
//! This build script copies the `memory.x` file from the crate root into
//! a directory where the linker can always find it at build time.
//! For many projects this is optional, as the linker always searches the
//! project root directory -- wherever `Cargo.toml` is. However, if you
//! are using a workspace or have a more complicated build setup, this
//! build script becomes required. Additionally, by requesting that
//! Cargo re-run the build script whenever `memory.x` is changed,
//! updating `memory.x` ensures a rebuild of the application with the
//! new memory settings.
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
fn main() {
// Put `memory.x` in our output directory and ensure it's
// on the linker search path.
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out.join("memory.x"))
.unwrap()
.write_all(include_bytes!("memory.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.display());
// By default, Cargo will re-run a build script whenever
// any file in the project changes. By specifying `memory.x`
// here, we ensure the build script is only re-run when
// `memory.x` is changed.
println!("cargo:rerun-if-changed=memory.x");
println!("cargo:rustc-link-arg-bins=--nmagic");
println!("cargo:rustc-link-arg-bins=-Tlink.x");
println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
}

View File

@ -0,0 +1,7 @@
MEMORY
{
/* NOTE 1 K = 1 KiBi = 1024 bytes */
/* These values correspond to the NRF52840 with Softdevices S140 7.0.1 */
FLASH : ORIGIN = 0x00000000, LENGTH = 1024K
RAM : ORIGIN = 0x20000000, LENGTH = 256K
}

View File

@ -2,6 +2,7 @@
name = "blinky-async"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[dependencies]
cortex-m = "0.7"

View File

@ -2,6 +2,7 @@
name = "blinky-hal"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[dependencies]
cortex-m = "0.7"

View File

@ -2,6 +2,7 @@
name = "blinky-irq"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[dependencies]
cortex-m = "0.7"

View File

@ -2,6 +2,7 @@
name = "blinky-pac"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[dependencies]
cortex-m = "0.7"

View File

@ -25,10 +25,19 @@ image::bootloader_flash.png[Bootloader flash layout]
The bootloader divides the storage into 4 main partitions, configurable when creating the bootloader
instance or via linker scripts:
* BOOTLOADER - Where the bootloader is placed. The bootloader itself consumes about 8kB of flash.
* ACTIVE - Where the main application is placed. The bootloader will attempt to load the application at the start of this partition. This partition is only written to by the bootloader.
* DFU - Where the application-to-be-swapped is placed. This partition is written to by the application.
* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a flag is set to instruct the bootloader that the partitions should be swapped.
* BOOTLOADER - Where the bootloader is placed. The bootloader itself consumes about 8kB of flash, but if you need to debug it and have space available, increasing this to 24kB will allow you to run the bootloader with probe-rs.
* ACTIVE - Where the main application is placed. The bootloader will attempt to load the application at the start of this partition. This partition is only written to by the bootloader. The size required for this partition depends on the size of your application.
* DFU - Where the application-to-be-swapped is placed. This partition is written to by the application. This partition must be at least 1 page bigger than the ACTIVE partition, since the swap algorithm uses the extra space to ensure power safe copy of data:
+
Partition Size~dfu~= Partition Size~active~+ Page Size~active~
+
All values are specified in bytes.
* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a magic field is written to instruct the bootloader that the partitions should be swapped. This partition must be able to store a magic field as well as the partition swap progress. The partition size given by:
+
Partition Size~state~ = Write Size~state~ + (2 × Partition Size~active~ / Page Size~active~)
+
All values are specified in bytes.
The partitions for ACTIVE (+BOOTLOADER), DFU and BOOTLOADER_STATE may be placed in separate flash. The page size used by the bootloader is determined by the lowest common multiple of the ACTIVE and DFU page sizes.
The BOOTLOADER_STATE partition must be big enough to store one word per page in the ACTIVE and DFU partitions combined.

View File

@ -3,6 +3,7 @@ edition = "2021"
name = "embassy-boot"
version = "0.1.0"
description = "Bootloader using Embassy"
license = "MIT OR Apache-2.0"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-v$VERSION/embassy-boot/boot/src/"

View File

@ -3,6 +3,7 @@ edition = "2021"
name = "embassy-boot-nrf"
version = "0.1.0"
description = "Bootloader lib for nRF chips"
license = "MIT OR Apache-2.0"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-nrf-v$VERSION/embassy-boot/nrf/src/"

View File

@ -3,6 +3,7 @@ edition = "2021"
name = "embassy-boot-stm32"
version = "0.1.0"
description = "Bootloader lib for STM32 chips"
license = "MIT OR Apache-2.0"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-nrf-v$VERSION/embassy-boot/stm32/src/"

View File

@ -2,6 +2,7 @@
name = "embassy-cortex-m"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-cortex-m-v$VERSION/embassy-cortex-m/src/"

View File

@ -2,13 +2,14 @@
name = "embassy-embedded-hal"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-embedded-hal-v$VERSION/embassy-embedded-hal/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-embedded-hal/src/"
features = ["nightly", "std"]
target = "thumbv7em-none-eabi"
target = "x86_64-unknown-linux-gnu"
[features]
std = []
@ -18,8 +19,8 @@ nightly = ["embedded-hal-async", "embedded-storage-async"]
[dependencies]
embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" }
embedded-hal-async = { version = "0.1.0-alpha.1", optional = true }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" }
embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true }
embedded-storage = "0.3.0"
embedded-storage-async = { version = "0.3.0", optional = true }
nb = "1.0.0"

View File

@ -29,7 +29,7 @@ use core::future::Future;
use embassy_sync::blocking_mutex::raw::RawMutex;
use embassy_sync::mutex::Mutex;
use embedded_hal_1::digital::blocking::OutputPin;
use embedded_hal_1::digital::OutputPin;
use embedded_hal_1::spi::ErrorType;
use embedded_hal_async::spi;
@ -57,7 +57,7 @@ where
type Error = SpiDeviceError<BUS::Error, CS::Error>;
}
impl<M, BUS, CS> spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
unsafe impl<M, BUS, CS> spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
where
M: RawMutex + 'static,
BUS: spi::SpiBusFlush + 'static,
@ -122,7 +122,7 @@ where
type Error = SpiDeviceError<BUS::Error, CS::Error>;
}
impl<M, BUS, CS> spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
unsafe impl<M, BUS, CS> spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
where
M: RawMutex + 'static,
BUS: spi::SpiBusFlush + SetConfig + 'static,

View File

@ -20,8 +20,7 @@ use core::cell::RefCell;
use embassy_sync::blocking_mutex::raw::RawMutex;
use embassy_sync::blocking_mutex::Mutex;
use embedded_hal_1::i2c::blocking::{I2c, Operation};
use embedded_hal_1::i2c::ErrorType;
use embedded_hal_1::i2c::{ErrorType, I2c, Operation};
use crate::shared_bus::I2cDeviceError;
use crate::SetConfig;

View File

@ -22,9 +22,9 @@ use core::cell::RefCell;
use embassy_sync::blocking_mutex::raw::RawMutex;
use embassy_sync::blocking_mutex::Mutex;
use embedded_hal_1::digital::blocking::OutputPin;
use embedded_hal_1::digital::OutputPin;
use embedded_hal_1::spi;
use embedded_hal_1::spi::blocking::SpiBusFlush;
use embedded_hal_1::spi::SpiBusFlush;
use crate::shared_bus::SpiDeviceError;
use crate::SetConfig;
@ -50,7 +50,7 @@ where
type Error = SpiDeviceError<BUS::Error, CS::Error>;
}
impl<BUS, M, CS> embedded_hal_1::spi::blocking::SpiDevice for SpiDevice<'_, M, BUS, CS>
impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
where
M: RawMutex,
BUS: SpiBusFlush,
@ -146,7 +146,7 @@ where
type Error = SpiDeviceError<BUS::Error, CS::Error>;
}
impl<BUS, M, CS> embedded_hal_1::spi::blocking::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
where
M: RawMutex,
BUS: SpiBusFlush + SetConfig,

View File

@ -2,12 +2,13 @@
name = "embassy-executor"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/"
features = ["nightly", "defmt", "unstable-traits"]
features = ["nightly", "defmt"]
flavors = [
{ name = "std", target = "x86_64-unknown-linux-gnu", features = ["std"] },
{ name = "wasm", target = "wasm32-unknown-unknown", features = ["wasm"] },

View File

@ -2,6 +2,7 @@
name = "embassy-hal-common"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[features]

View File

@ -2,12 +2,14 @@
name = "embassy-lora"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/embassy-lora/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/"
features = ["time", "defmt"]
flavors = [
{ name = "sx126x", target = "thumbv7em-none-eabihf", features = ["sx126x"] },
{ name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] },
{ name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] },
]
@ -15,6 +17,7 @@ flavors = [
[lib]
[features]
sx126x = []
sx127x = []
stm32wl = ["embassy-stm32", "embassy-stm32/subghz"]
time = []
@ -28,8 +31,8 @@ log = { version = "0.4.14", optional = true }
embassy-time = { version = "0.1.0", path = "../embassy-time" }
embassy-sync = { version = "0.1.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.8" }
embedded-hal-async = { version = "0.1.0-alpha.1" }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" }
embedded-hal-async = { version = "=0.1.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"] }

View File

@ -7,6 +7,8 @@ pub(crate) mod fmt;
#[cfg(feature = "stm32wl")]
pub mod stm32wl;
#[cfg(feature = "sx126x")]
pub mod sx126x;
#[cfg(feature = "sx127x")]
pub mod sx127x;

View File

@ -0,0 +1,153 @@
use core::future::Future;
use defmt::Format;
use embedded_hal::digital::v2::OutputPin;
use embedded_hal_async::digital::Wait;
use embedded_hal_async::spi::*;
use lorawan_device::async_device::radio::{PhyRxTx, RfConfig, RxQuality, TxConfig};
use lorawan_device::async_device::Timings;
mod sx126x_lora;
use sx126x_lora::LoRa;
use self::sx126x_lora::mod_params::RadioError;
/// Semtech Sx126x LoRa peripheral
pub struct Sx126xRadio<SPI, CTRL, WAIT, BUS>
where
SPI: SpiBus<u8, Error = BUS> + 'static,
CTRL: OutputPin + 'static,
WAIT: Wait + 'static,
BUS: Error + Format + 'static,
{
pub lora: LoRa<SPI, CTRL, WAIT>,
}
impl<SPI, CTRL, WAIT, BUS> Sx126xRadio<SPI, CTRL, WAIT, BUS>
where
SPI: SpiBus<u8, Error = BUS> + 'static,
CTRL: OutputPin + 'static,
WAIT: Wait + 'static,
BUS: Error + Format + 'static,
{
pub async fn new(
spi: SPI,
cs: CTRL,
reset: CTRL,
antenna_rx: CTRL,
antenna_tx: CTRL,
dio1: WAIT,
busy: WAIT,
enable_public_network: bool,
) -> Result<Self, RadioError<BUS>> {
let mut lora = LoRa::new(spi, cs, reset, antenna_rx, antenna_tx, dio1, busy);
lora.init().await?;
lora.set_lora_modem(enable_public_network).await?;
Ok(Self { lora })
}
}
impl<SPI, CTRL, WAIT, BUS> Timings for Sx126xRadio<SPI, CTRL, WAIT, BUS>
where
SPI: SpiBus<u8, Error = BUS> + 'static,
CTRL: OutputPin + 'static,
WAIT: Wait + 'static,
BUS: Error + Format + 'static,
{
fn get_rx_window_offset_ms(&self) -> i32 {
-500
}
fn get_rx_window_duration_ms(&self) -> u32 {
800
}
}
impl<SPI, CTRL, WAIT, BUS> PhyRxTx for Sx126xRadio<SPI, CTRL, WAIT, BUS>
where
SPI: SpiBus<u8, Error = BUS> + 'static,
CTRL: OutputPin + 'static,
WAIT: Wait + 'static,
BUS: Error + Format + 'static,
{
type PhyError = RadioError<BUS>;
type TxFuture<'m> = impl Future<Output = Result<u32, Self::PhyError>> + 'm
where
SPI: 'm,
CTRL: 'm,
WAIT: 'm,
BUS: 'm;
fn tx<'m>(&'m mut self, config: TxConfig, buffer: &'m [u8]) -> Self::TxFuture<'m> {
trace!("TX START");
async move {
self.lora
.set_tx_config(
config.pw,
config.rf.spreading_factor.into(),
config.rf.bandwidth.into(),
config.rf.coding_rate.into(),
4,
false,
true,
false,
0,
false,
)
.await?;
self.lora.set_max_payload_length(buffer.len() as u8).await?;
self.lora.set_channel(config.rf.frequency).await?;
self.lora.send(buffer, 0xffffff).await?;
self.lora.process_irq(None, None, None).await?;
trace!("TX DONE");
return Ok(0);
}
}
type RxFuture<'m> = impl Future<Output = Result<(usize, RxQuality), Self::PhyError>> + 'm
where
SPI: 'm,
CTRL: 'm,
WAIT: 'm,
BUS: 'm;
fn rx<'m>(&'m mut self, config: RfConfig, receiving_buffer: &'m mut [u8]) -> Self::RxFuture<'m> {
trace!("RX START");
async move {
self.lora
.set_rx_config(
config.spreading_factor.into(),
config.bandwidth.into(),
config.coding_rate.into(),
4,
4,
false,
0u8,
true,
false,
0,
false,
true,
)
.await?;
self.lora.set_max_payload_length(receiving_buffer.len() as u8).await?;
self.lora.set_channel(config.frequency).await?;
self.lora.rx(90 * 1000).await?;
let mut received_len = 0u8;
self.lora
.process_irq(Some(receiving_buffer), Some(&mut received_len), None)
.await?;
trace!("RX DONE");
let packet_status = self.lora.get_latest_packet_status();
let mut rssi = 0i16;
let mut snr = 0i8;
if packet_status.is_some() {
rssi = packet_status.unwrap().rssi as i16;
snr = packet_status.unwrap().snr;
}
Ok((received_len as usize, RxQuality::new(rssi, snr)))
}
}
}

View File

@ -0,0 +1,256 @@
use embassy_time::{Duration, Timer};
use embedded_hal::digital::v2::OutputPin;
use embedded_hal_async::digital::Wait;
use embedded_hal_async::spi::SpiBus;
use super::mod_params::RadioError::*;
use super::mod_params::*;
use super::LoRa;
// Defines the time required for the TCXO to wakeup [ms].
const BRD_TCXO_WAKEUP_TIME: u32 = 10;
// Provides board-specific functionality for Semtech SX126x-based boards.
impl<SPI, CTRL, WAIT, BUS> LoRa<SPI, CTRL, WAIT>
where
SPI: SpiBus<u8, Error = BUS>,
CTRL: OutputPin,
WAIT: Wait,
{
// De-initialize the radio I/Os pins interface. Useful when going into MCU low power modes.
pub(super) async fn brd_io_deinit(&mut self) -> Result<(), RadioError<BUS>> {
Ok(()) // no operation currently
}
// Initialize the TCXO power pin
pub(super) async fn brd_io_tcxo_init(&mut self) -> Result<(), RadioError<BUS>> {
let timeout = self.brd_get_board_tcxo_wakeup_time() << 6;
self.sub_set_dio3_as_tcxo_ctrl(TcxoCtrlVoltage::Ctrl1V7, timeout)
.await?;
Ok(())
}
// Initialize RF switch control pins
pub(super) async fn brd_io_rf_switch_init(&mut self) -> Result<(), RadioError<BUS>> {
self.sub_set_dio2_as_rf_switch_ctrl(true).await?;
Ok(())
}
// Initialize the radio debug pins
pub(super) async fn brd_io_dbg_init(&mut self) -> Result<(), RadioError<BUS>> {
Ok(()) // no operation currently
}
// Hardware reset of the radio
pub(super) async fn brd_reset(&mut self) -> Result<(), RadioError<BUS>> {
Timer::after(Duration::from_millis(10)).await;
self.reset.set_low().map_err(|_| Reset)?;
Timer::after(Duration::from_millis(20)).await;
self.reset.set_high().map_err(|_| Reset)?;
Timer::after(Duration::from_millis(10)).await;
Ok(())
}
// Wait while the busy pin is high
pub(super) async fn brd_wait_on_busy(&mut self) -> Result<(), RadioError<BUS>> {
self.busy.wait_for_low().await.map_err(|_| Busy)?;
Ok(())
}
// Wake up the radio
pub(super) async fn brd_wakeup(&mut self) -> Result<(), RadioError<BUS>> {
self.cs.set_low().map_err(|_| CS)?;
self.spi.write(&[OpCode::GetStatus.value()]).await.map_err(SPI)?;
self.spi.write(&[0x00]).await.map_err(SPI)?;
self.cs.set_high().map_err(|_| CS)?;
self.brd_wait_on_busy().await?;
self.brd_set_operating_mode(RadioMode::StandbyRC);
Ok(())
}
// Send a command that writes data to the radio
pub(super) async fn brd_write_command(&mut self, op_code: OpCode, buffer: &[u8]) -> Result<(), RadioError<BUS>> {
self.sub_check_device_ready().await?;
self.cs.set_low().map_err(|_| CS)?;
self.spi.write(&[op_code.value()]).await.map_err(SPI)?;
self.spi.write(buffer).await.map_err(SPI)?;
self.cs.set_high().map_err(|_| CS)?;
if op_code != OpCode::SetSleep {
self.brd_wait_on_busy().await?;
}
Ok(())
}
// Send a command that reads data from the radio, filling the provided buffer and returning a status
pub(super) async fn brd_read_command(&mut self, op_code: OpCode, buffer: &mut [u8]) -> Result<u8, RadioError<BUS>> {
let mut status = [0u8];
let mut input = [0u8];
self.sub_check_device_ready().await?;
self.cs.set_low().map_err(|_| CS)?;
self.spi.write(&[op_code.value()]).await.map_err(SPI)?;
self.spi.transfer(&mut status, &[0x00]).await.map_err(SPI)?;
for i in 0..buffer.len() {
self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?;
buffer[i] = input[0];
}
self.cs.set_high().map_err(|_| CS)?;
self.brd_wait_on_busy().await?;
Ok(status[0])
}
// Write one or more bytes of data to the radio memory
pub(super) async fn brd_write_registers(
&mut self,
start_register: Register,
buffer: &[u8],
) -> Result<(), RadioError<BUS>> {
self.sub_check_device_ready().await?;
self.cs.set_low().map_err(|_| CS)?;
self.spi.write(&[OpCode::WriteRegister.value()]).await.map_err(SPI)?;
self.spi
.write(&[
((start_register.addr() & 0xFF00) >> 8) as u8,
(start_register.addr() & 0x00FF) as u8,
])
.await
.map_err(SPI)?;
self.spi.write(buffer).await.map_err(SPI)?;
self.cs.set_high().map_err(|_| CS)?;
self.brd_wait_on_busy().await?;
Ok(())
}
// Read one or more bytes of data from the radio memory
pub(super) async fn brd_read_registers(
&mut self,
start_register: Register,
buffer: &mut [u8],
) -> Result<(), RadioError<BUS>> {
let mut input = [0u8];
self.sub_check_device_ready().await?;
self.cs.set_low().map_err(|_| CS)?;
self.spi.write(&[OpCode::ReadRegister.value()]).await.map_err(SPI)?;
self.spi
.write(&[
((start_register.addr() & 0xFF00) >> 8) as u8,
(start_register.addr() & 0x00FF) as u8,
0x00u8,
])
.await
.map_err(SPI)?;
for i in 0..buffer.len() {
self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?;
buffer[i] = input[0];
}
self.cs.set_high().map_err(|_| CS)?;
self.brd_wait_on_busy().await?;
Ok(())
}
// Write data to the buffer holding the payload in the radio
pub(super) async fn brd_write_buffer(&mut self, offset: u8, buffer: &[u8]) -> Result<(), RadioError<BUS>> {
self.sub_check_device_ready().await?;
self.cs.set_low().map_err(|_| CS)?;
self.spi.write(&[OpCode::WriteBuffer.value()]).await.map_err(SPI)?;
self.spi.write(&[offset]).await.map_err(SPI)?;
self.spi.write(buffer).await.map_err(SPI)?;
self.cs.set_high().map_err(|_| CS)?;
self.brd_wait_on_busy().await?;
Ok(())
}
// Read data from the buffer holding the payload in the radio
pub(super) async fn brd_read_buffer(&mut self, offset: u8, buffer: &mut [u8]) -> Result<(), RadioError<BUS>> {
let mut input = [0u8];
self.sub_check_device_ready().await?;
self.cs.set_low().map_err(|_| CS)?;
self.spi.write(&[OpCode::ReadBuffer.value()]).await.map_err(SPI)?;
self.spi.write(&[offset]).await.map_err(SPI)?;
self.spi.write(&[0x00]).await.map_err(SPI)?;
for i in 0..buffer.len() {
self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?;
buffer[i] = input[0];
}
self.cs.set_high().map_err(|_| CS)?;
self.brd_wait_on_busy().await?;
Ok(())
}
// Set the radio output power
pub(super) async fn brd_set_rf_tx_power(&mut self, power: i8) -> Result<(), RadioError<BUS>> {
self.sub_set_tx_params(power, RampTime::Ramp40Us).await?;
Ok(())
}
// Get the radio type
pub(super) fn brd_get_radio_type(&mut self) -> RadioType {
RadioType::SX1262
}
// Quiesce the antenna(s).
pub(super) fn brd_ant_sleep(&mut self) -> Result<(), RadioError<BUS>> {
self.antenna_tx.set_low().map_err(|_| AntTx)?;
self.antenna_rx.set_low().map_err(|_| AntRx)?;
Ok(())
}
// Prepare the antenna(s) for a receive operation
pub(super) fn brd_ant_set_rx(&mut self) -> Result<(), RadioError<BUS>> {
self.antenna_tx.set_low().map_err(|_| AntTx)?;
self.antenna_rx.set_high().map_err(|_| AntRx)?;
Ok(())
}
// Prepare the antenna(s) for a send operation
pub(super) fn brd_ant_set_tx(&mut self) -> Result<(), RadioError<BUS>> {
self.antenna_rx.set_low().map_err(|_| AntRx)?;
self.antenna_tx.set_high().map_err(|_| AntTx)?;
Ok(())
}
// Check if the given RF frequency is supported by the hardware
pub(super) async fn brd_check_rf_frequency(&mut self, _frequency: u32) -> Result<bool, RadioError<BUS>> {
Ok(true)
}
// Get the duration required for the TCXO to wakeup [ms].
pub(super) fn brd_get_board_tcxo_wakeup_time(&mut self) -> u32 {
BRD_TCXO_WAKEUP_TIME
}
/* Get current state of the DIO1 pin - not currently needed if waiting on DIO1 instead of using an IRQ process
pub(super) async fn brd_get_dio1_pin_state(
&mut self,
) -> Result<u32, RadioError<BUS>> {
Ok(0)
}
*/
// Get the current radio operatiing mode
pub(super) fn brd_get_operating_mode(&mut self) -> RadioMode {
self.operating_mode
}
// Set/Update the current radio operating mode This function is only required to reflect the current radio operating mode when processing interrupts.
pub(super) fn brd_set_operating_mode(&mut self, mode: RadioMode) {
self.operating_mode = mode;
}
}

View File

@ -0,0 +1,732 @@
#![allow(dead_code)]
use embassy_time::{Duration, Timer};
use embedded_hal::digital::v2::OutputPin;
use embedded_hal_async::digital::Wait;
use embedded_hal_async::spi::SpiBus;
mod board_specific;
pub mod mod_params;
mod subroutine;
use mod_params::RadioError::*;
use mod_params::*;
// Syncwords for public and private networks
const LORA_MAC_PUBLIC_SYNCWORD: u16 = 0x3444;
const LORA_MAC_PRIVATE_SYNCWORD: u16 = 0x1424;
// Maximum number of registers that can be added to the retention list
const MAX_NUMBER_REGS_IN_RETENTION: u8 = 4;
// Possible LoRa bandwidths
const LORA_BANDWIDTHS: [Bandwidth; 3] = [Bandwidth::_125KHz, Bandwidth::_250KHz, Bandwidth::_500KHz];
// Radio complete wakeup time with margin for temperature compensation [ms]
const RADIO_WAKEUP_TIME: u32 = 3;
/// Provides high-level access to Semtech SX126x-based boards
pub struct LoRa<SPI, CTRL, WAIT> {
spi: SPI,
cs: CTRL,
reset: CTRL,
antenna_rx: CTRL,
antenna_tx: CTRL,
dio1: WAIT,
busy: WAIT,
operating_mode: RadioMode,
rx_continuous: bool,
max_payload_length: u8,
modulation_params: Option<ModulationParams>,
packet_type: PacketType,
packet_params: Option<PacketParams>,
packet_status: Option<PacketStatus>,
image_calibrated: bool,
frequency_error: u32,
}
impl<SPI, CTRL, WAIT, BUS> LoRa<SPI, CTRL, WAIT>
where
SPI: SpiBus<u8, Error = BUS>,
CTRL: OutputPin,
WAIT: Wait,
{
/// Builds and returns a new instance of the radio. Only one instance of the radio should exist at a time ()
pub fn new(spi: SPI, cs: CTRL, reset: CTRL, antenna_rx: CTRL, antenna_tx: CTRL, dio1: WAIT, busy: WAIT) -> Self {
Self {
spi,
cs,
reset,
antenna_rx,
antenna_tx,
dio1,
busy,
operating_mode: RadioMode::Sleep,
rx_continuous: false,
max_payload_length: 0xFFu8,
modulation_params: None,
packet_type: PacketType::LoRa,
packet_params: None,
packet_status: None,
image_calibrated: false,
frequency_error: 0u32, // where is volatile FrequencyError modified ???
}
}
/// Initialize the radio
pub async fn init(&mut self) -> Result<(), RadioError<BUS>> {
self.sub_init().await?;
self.sub_set_standby(StandbyMode::RC).await?;
self.sub_set_regulator_mode(RegulatorMode::UseDCDC).await?;
self.sub_set_buffer_base_address(0x00u8, 0x00u8).await?;
self.sub_set_tx_params(0i8, RampTime::Ramp200Us).await?;
self.sub_set_dio_irq_params(
IrqMask::All.value(),
IrqMask::All.value(),
IrqMask::None.value(),
IrqMask::None.value(),
)
.await?;
self.add_register_to_retention_list(Register::RxGain.addr()).await?;
self.add_register_to_retention_list(Register::TxModulation.addr())
.await?;
Ok(())
}
/// Return current radio state
pub fn get_status(&mut self) -> RadioState {
match self.brd_get_operating_mode() {
RadioMode::Transmit => RadioState::TxRunning,
RadioMode::Receive => RadioState::RxRunning,
RadioMode::ChannelActivityDetection => RadioState::ChannelActivityDetecting,
_ => RadioState::Idle,
}
}
/// Configure the radio for LoRa (FSK support should be provided in a separate driver, if desired)
pub async fn set_lora_modem(&mut self, enable_public_network: bool) -> Result<(), RadioError<BUS>> {
self.sub_set_packet_type(PacketType::LoRa).await?;
if enable_public_network {
self.brd_write_registers(
Register::LoRaSyncword,
&[
((LORA_MAC_PUBLIC_SYNCWORD >> 8) & 0xFF) as u8,
(LORA_MAC_PUBLIC_SYNCWORD & 0xFF) as u8,
],
)
.await?;
} else {
self.brd_write_registers(
Register::LoRaSyncword,
&[
((LORA_MAC_PRIVATE_SYNCWORD >> 8) & 0xFF) as u8,
(LORA_MAC_PRIVATE_SYNCWORD & 0xFF) as u8,
],
)
.await?;
}
Ok(())
}
/// Sets the channel frequency
pub async fn set_channel(&mut self, frequency: u32) -> Result<(), RadioError<BUS>> {
self.sub_set_rf_frequency(frequency).await?;
Ok(())
}
/* Checks if the channel is free for the given time. This is currently not implemented until a substitute
for switching to the FSK modem is found.
pub async fn is_channel_free(&mut self, frequency: u32, rxBandwidth: u32, rssiThresh: i16, maxCarrierSenseTime: u32) -> bool;
*/
/// Generate a 32 bit random value based on the RSSI readings, after disabling all interrupts. Ensure set_lora_modem() is called befrorehand.
/// After calling this function either set_rx_config() or set_tx_config() must be called.
pub async fn get_random_value(&mut self) -> Result<u32, RadioError<BUS>> {
self.sub_set_dio_irq_params(
IrqMask::None.value(),
IrqMask::None.value(),
IrqMask::None.value(),
IrqMask::None.value(),
)
.await?;
let result = self.sub_get_random().await?;
Ok(result)
}
/// Set the reception parameters for the LoRa modem (only). Ensure set_lora_modem() is called befrorehand.
/// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol]
/// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved]
/// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
/// preamble_length length in symbols (the hardware adds 4 more symbols)
/// symb_timeout RxSingle timeout value in symbols
/// fixed_len fixed length packets [0: variable, 1: fixed]
/// payload_len payload length when fixed length is used
/// crc_on [0: OFF, 1: ON]
/// freq_hop_on intra-packet frequency hopping [0: OFF, 1: ON]
/// hop_period number of symbols between each hop
/// iq_inverted invert IQ signals [0: not inverted, 1: inverted]
/// rx_continuous reception mode [false: single mode, true: continuous mode]
pub async fn set_rx_config(
&mut self,
spreading_factor: SpreadingFactor,
bandwidth: Bandwidth,
coding_rate: CodingRate,
preamble_length: u16,
symb_timeout: u16,
fixed_len: bool,
payload_len: u8,
crc_on: bool,
_freq_hop_on: bool,
_hop_period: u8,
iq_inverted: bool,
rx_continuous: bool,
) -> Result<(), RadioError<BUS>> {
let mut symb_timeout_final = symb_timeout;
self.rx_continuous = rx_continuous;
if self.rx_continuous {
symb_timeout_final = 0;
}
if fixed_len {
self.max_payload_length = payload_len;
} else {
self.max_payload_length = 0xFFu8;
}
self.sub_set_stop_rx_timer_on_preamble_detect(false).await?;
let mut low_data_rate_optimize = 0x00u8;
if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12))
&& (bandwidth == Bandwidth::_125KHz))
|| ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz))
{
low_data_rate_optimize = 0x01u8;
}
let modulation_params = ModulationParams {
spreading_factor: spreading_factor,
bandwidth: bandwidth,
coding_rate: coding_rate,
low_data_rate_optimize: low_data_rate_optimize,
};
let mut preamble_length_final = preamble_length;
if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6))
&& (preamble_length < 12)
{
preamble_length_final = 12;
}
let packet_params = PacketParams {
preamble_length: preamble_length_final,
implicit_header: fixed_len,
payload_length: self.max_payload_length,
crc_on: crc_on,
iq_inverted: iq_inverted,
};
self.modulation_params = Some(modulation_params);
self.packet_params = Some(packet_params);
self.standby().await?;
self.sub_set_modulation_params().await?;
self.sub_set_packet_params().await?;
self.sub_set_lora_symb_num_timeout(symb_timeout_final).await?;
// Optimize the Inverted IQ Operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4)
let mut iq_polarity = [0x00u8];
self.brd_read_registers(Register::IQPolarity, &mut iq_polarity).await?;
if iq_inverted {
self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] & (!(1 << 2))])
.await?;
} else {
self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] | (1 << 2)])
.await?;
}
Ok(())
}
/// Set the transmission parameters for the LoRa modem (only).
/// power output power [dBm]
/// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol]
/// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved]
/// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
/// preamble_length length in symbols (the hardware adds 4 more symbols)
/// fixed_len fixed length packets [0: variable, 1: fixed]
/// crc_on [0: OFF, 1: ON]
/// freq_hop_on intra-packet frequency hopping [0: OFF, 1: ON]
/// hop_period number of symbols between each hop
/// iq_inverted invert IQ signals [0: not inverted, 1: inverted]
pub async fn set_tx_config(
&mut self,
power: i8,
spreading_factor: SpreadingFactor,
bandwidth: Bandwidth,
coding_rate: CodingRate,
preamble_length: u16,
fixed_len: bool,
crc_on: bool,
_freq_hop_on: bool,
_hop_period: u8,
iq_inverted: bool,
) -> Result<(), RadioError<BUS>> {
let mut low_data_rate_optimize = 0x00u8;
if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12))
&& (bandwidth == Bandwidth::_125KHz))
|| ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz))
{
low_data_rate_optimize = 0x01u8;
}
let modulation_params = ModulationParams {
spreading_factor: spreading_factor,
bandwidth: bandwidth,
coding_rate: coding_rate,
low_data_rate_optimize: low_data_rate_optimize,
};
let mut preamble_length_final = preamble_length;
if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6))
&& (preamble_length < 12)
{
preamble_length_final = 12;
}
let packet_params = PacketParams {
preamble_length: preamble_length_final,
implicit_header: fixed_len,
payload_length: self.max_payload_length,
crc_on: crc_on,
iq_inverted: iq_inverted,
};
self.modulation_params = Some(modulation_params);
self.packet_params = Some(packet_params);
self.standby().await?;
self.sub_set_modulation_params().await?;
self.sub_set_packet_params().await?;
// Handle modulation quality with the 500 kHz LoRa bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1)
let mut tx_modulation = [0x00u8];
self.brd_read_registers(Register::TxModulation, &mut tx_modulation)
.await?;
if bandwidth == Bandwidth::_500KHz {
self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] & (!(1 << 2))])
.await?;
} else {
self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] | (1 << 2)])
.await?;
}
self.brd_set_rf_tx_power(power).await?;
Ok(())
}
/// Check if the given RF frequency is supported by the hardware [true: supported, false: unsupported]
pub async fn check_rf_frequency(&mut self, frequency: u32) -> Result<bool, RadioError<BUS>> {
Ok(self.brd_check_rf_frequency(frequency).await?)
}
/// Computes the packet time on air in ms for the given payload for a LoRa modem (can only be called once set_rx_config or set_tx_config have been called)
/// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol]
/// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved]
/// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
/// preamble_length length in symbols (the hardware adds 4 more symbols)
/// fixed_len fixed length packets [0: variable, 1: fixed]
/// payload_len sets payload length when fixed length is used
/// crc_on [0: OFF, 1: ON]
pub fn get_time_on_air(
&mut self,
spreading_factor: SpreadingFactor,
bandwidth: Bandwidth,
coding_rate: CodingRate,
preamble_length: u16,
fixed_len: bool,
payload_len: u8,
crc_on: bool,
) -> Result<u32, RadioError<BUS>> {
let numerator = 1000
* Self::get_lora_time_on_air_numerator(
spreading_factor,
bandwidth,
coding_rate,
preamble_length,
fixed_len,
payload_len,
crc_on,
);
let denominator = bandwidth.value_in_hz();
if denominator == 0 {
Err(RadioError::InvalidBandwidth)
} else {
Ok((numerator + denominator - 1) / denominator)
}
}
/// Send the buffer of the given size. Prepares the packet to be sent and sets the radio in transmission [timeout in ms]
pub async fn send(&mut self, buffer: &[u8], timeout: u32) -> Result<(), RadioError<BUS>> {
if self.packet_params.is_some() {
self.sub_set_dio_irq_params(
IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(),
IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(),
IrqMask::None.value(),
IrqMask::None.value(),
)
.await?;
let mut packet_params = self.packet_params.as_mut().unwrap();
packet_params.payload_length = buffer.len() as u8;
self.sub_set_packet_params().await?;
self.sub_send_payload(buffer, timeout).await?;
Ok(())
} else {
Err(RadioError::PacketParamsMissing)
}
}
/// Set the radio in sleep mode
pub async fn sleep(&mut self) -> Result<(), RadioError<BUS>> {
self.sub_set_sleep(SleepParams {
wakeup_rtc: false,
reset: false,
warm_start: true,
})
.await?;
Timer::after(Duration::from_millis(2)).await;
Ok(())
}
/// Set the radio in standby mode
pub async fn standby(&mut self) -> Result<(), RadioError<BUS>> {
self.sub_set_standby(StandbyMode::RC).await?;
Ok(())
}
/// Set the radio in reception mode for the given duration [0: continuous, others: timeout (ms)]
pub async fn rx(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
self.sub_set_dio_irq_params(
IrqMask::All.value(),
IrqMask::All.value(),
IrqMask::None.value(),
IrqMask::None.value(),
)
.await?;
if self.rx_continuous {
self.sub_set_rx(0xFFFFFF).await?;
} else {
self.sub_set_rx(timeout << 6).await?;
}
Ok(())
}
/// Start a Channel Activity Detection
pub async fn start_cad(&mut self) -> Result<(), RadioError<BUS>> {
self.sub_set_dio_irq_params(
IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(),
IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(),
IrqMask::None.value(),
IrqMask::None.value(),
)
.await?;
self.sub_set_cad().await?;
Ok(())
}
/// Sets the radio in continuous wave transmission mode
/// frequency channel RF frequency
/// power output power [dBm]
/// timeout transmission mode timeout [s]
pub async fn set_tx_continuous_wave(
&mut self,
frequency: u32,
power: i8,
_timeout: u16,
) -> Result<(), RadioError<BUS>> {
self.sub_set_rf_frequency(frequency).await?;
self.brd_set_rf_tx_power(power).await?;
self.sub_set_tx_continuous_wave().await?;
Ok(())
}
/// Read the current RSSI value for the LoRa modem (only) [dBm]
pub async fn get_rssi(&mut self) -> Result<i16, RadioError<BUS>> {
let value = self.sub_get_rssi_inst().await?;
Ok(value as i16)
}
/// Write one or more radio registers with a buffer of a given size, starting at the first register address
pub async fn write_registers_from_buffer(
&mut self,
start_register: Register,
buffer: &[u8],
) -> Result<(), RadioError<BUS>> {
self.brd_write_registers(start_register, buffer).await?;
Ok(())
}
/// Read one or more radio registers into a buffer of a given size, starting at the first register address
pub async fn read_registers_into_buffer(
&mut self,
start_register: Register,
buffer: &mut [u8],
) -> Result<(), RadioError<BUS>> {
self.brd_read_registers(start_register, buffer).await?;
Ok(())
}
/// Set the maximum payload length (in bytes) for a LoRa modem (only).
pub async fn set_max_payload_length(&mut self, max: u8) -> Result<(), RadioError<BUS>> {
if self.packet_params.is_some() {
let packet_params = self.packet_params.as_mut().unwrap();
self.max_payload_length = max;
packet_params.payload_length = max;
self.sub_set_packet_params().await?;
Ok(())
} else {
Err(RadioError::PacketParamsMissing)
}
}
/// Get the time required for the board plus radio to get out of sleep [ms]
pub fn get_wakeup_time(&mut self) -> u32 {
self.brd_get_board_tcxo_wakeup_time() + RADIO_WAKEUP_TIME
}
/// Process the radio irq
pub async fn process_irq(
&mut self,
receiving_buffer: Option<&mut [u8]>,
received_len: Option<&mut u8>,
cad_activity_detected: Option<&mut bool>,
) -> Result<(), RadioError<BUS>> {
loop {
trace!("process_irq loop entered");
let de = self.sub_get_device_errors().await?;
trace!("device_errors: rc_64khz_calibration = {}, rc_13mhz_calibration = {}, pll_calibration = {}, adc_calibration = {}, image_calibration = {}, xosc_start = {}, pll_lock = {}, pa_ramp = {}",
de.rc_64khz_calibration, de.rc_13mhz_calibration, de.pll_calibration, de.adc_calibration, de.image_calibration, de.xosc_start, de.pll_lock, de.pa_ramp);
let st = self.sub_get_status().await?;
trace!(
"radio status: cmd_status: {:x}, chip_mode: {:x}",
st.cmd_status,
st.chip_mode
);
self.dio1.wait_for_high().await.map_err(|_| DIO1)?;
let operating_mode = self.brd_get_operating_mode();
let irq_flags = self.sub_get_irq_status().await?;
self.sub_clear_irq_status(irq_flags).await?;
trace!("process_irq DIO1 satisfied: irq_flags = {:x}", irq_flags);
// check for errors and unexpected interrupt masks (based on operation mode)
if (irq_flags & IrqMask::HeaderError.value()) == IrqMask::HeaderError.value() {
if !self.rx_continuous {
self.brd_set_operating_mode(RadioMode::StandbyRC);
}
return Err(RadioError::HeaderError);
} else if (irq_flags & IrqMask::CRCError.value()) == IrqMask::CRCError.value() {
if operating_mode == RadioMode::Receive {
if !self.rx_continuous {
self.brd_set_operating_mode(RadioMode::StandbyRC);
}
return Err(RadioError::CRCErrorOnReceive);
} else {
return Err(RadioError::CRCErrorUnexpected);
}
} else if (irq_flags & IrqMask::RxTxTimeout.value()) == IrqMask::RxTxTimeout.value() {
if operating_mode == RadioMode::Transmit {
self.brd_set_operating_mode(RadioMode::StandbyRC);
return Err(RadioError::TransmitTimeout);
} else if operating_mode == RadioMode::Receive {
self.brd_set_operating_mode(RadioMode::StandbyRC);
return Err(RadioError::ReceiveTimeout);
} else {
return Err(RadioError::TimeoutUnexpected);
}
} else if ((irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value())
&& (operating_mode != RadioMode::Transmit)
{
return Err(RadioError::TransmitDoneUnexpected);
} else if ((irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value())
&& (operating_mode != RadioMode::Receive)
{
return Err(RadioError::ReceiveDoneUnexpected);
} else if (((irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value())
|| ((irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value()))
&& (operating_mode != RadioMode::ChannelActivityDetection)
{
return Err(RadioError::CADUnexpected);
}
if (irq_flags & IrqMask::HeaderValid.value()) == IrqMask::HeaderValid.value() {
trace!("HeaderValid");
} else if (irq_flags & IrqMask::PreambleDetected.value()) == IrqMask::PreambleDetected.value() {
trace!("PreambleDetected");
} else if (irq_flags & IrqMask::SyncwordValid.value()) == IrqMask::SyncwordValid.value() {
trace!("SyncwordValid");
}
// handle completions
if (irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value() {
self.brd_set_operating_mode(RadioMode::StandbyRC);
return Ok(());
} else if (irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value() {
if !self.rx_continuous {
self.brd_set_operating_mode(RadioMode::StandbyRC);
// implicit header mode timeout behavior (see DS_SX1261-2_V1.2 datasheet chapter 15.3)
self.brd_write_registers(Register::RTCCtrl, &[0x00]).await?;
let mut evt_clr = [0x00u8];
self.brd_read_registers(Register::EvtClr, &mut evt_clr).await?;
evt_clr[0] |= 1 << 1;
self.brd_write_registers(Register::EvtClr, &evt_clr).await?;
}
if receiving_buffer.is_some() && received_len.is_some() {
*(received_len.unwrap()) = self.sub_get_payload(receiving_buffer.unwrap()).await?;
}
self.packet_status = self.sub_get_packet_status().await?.into();
return Ok(());
} else if (irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value() {
if cad_activity_detected.is_some() {
*(cad_activity_detected.unwrap()) =
(irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value();
}
self.brd_set_operating_mode(RadioMode::StandbyRC);
return Ok(());
}
// if DIO1 was driven high for reasons other than an error or operation completion (currently, PreambleDetected, SyncwordValid, and HeaderValid
// are in that category), loop to wait again
}
}
// SX126x-specific functions
/// Set the radio in reception mode with Max LNA gain for the given time (SX126x radios only) [0: continuous, others timeout in ms]
pub async fn set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
self.sub_set_dio_irq_params(
IrqMask::All.value(),
IrqMask::All.value(),
IrqMask::None.value(),
IrqMask::None.value(),
)
.await?;
if self.rx_continuous {
self.sub_set_rx_boosted(0xFFFFFF).await?; // Rx continuous
} else {
self.sub_set_rx_boosted(timeout << 6).await?;
}
Ok(())
}
/// Set the Rx duty cycle management parameters (SX126x radios only)
/// rx_time structure describing reception timeout value
/// sleep_time structure describing sleep timeout value
pub async fn set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError<BUS>> {
self.sub_set_rx_duty_cycle(rx_time, sleep_time).await?;
Ok(())
}
pub fn get_latest_packet_status(&mut self) -> Option<PacketStatus> {
self.packet_status
}
// Utilities
async fn add_register_to_retention_list(&mut self, register_address: u16) -> Result<(), RadioError<BUS>> {
let mut buffer = [0x00u8; (1 + (2 * MAX_NUMBER_REGS_IN_RETENTION)) as usize];
// Read the address and registers already added to the list
self.brd_read_registers(Register::RetentionList, &mut buffer).await?;
let number_of_registers = buffer[0];
for i in 0..number_of_registers {
if register_address
== ((buffer[(1 + (2 * i)) as usize] as u16) << 8) | (buffer[(2 + (2 * i)) as usize] as u16)
{
return Ok(()); // register already in list
}
}
if number_of_registers < MAX_NUMBER_REGS_IN_RETENTION {
buffer[0] += 1; // increment number of registers
buffer[(1 + (2 * number_of_registers)) as usize] = ((register_address >> 8) & 0xFF) as u8;
buffer[(2 + (2 * number_of_registers)) as usize] = (register_address & 0xFF) as u8;
self.brd_write_registers(Register::RetentionList, &buffer).await?;
Ok(())
} else {
Err(RadioError::RetentionListExceeded)
}
}
fn get_lora_time_on_air_numerator(
spreading_factor: SpreadingFactor,
bandwidth: Bandwidth,
coding_rate: CodingRate,
preamble_length: u16,
fixed_len: bool,
payload_len: u8,
crc_on: bool,
) -> u32 {
let cell_denominator;
let cr_denominator = (coding_rate.value() as i32) + 4;
// Ensure that the preamble length is at least 12 symbols when using SF5 or SF6
let mut preamble_length_final = preamble_length;
if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6))
&& (preamble_length < 12)
{
preamble_length_final = 12;
}
let mut low_data_rate_optimize = false;
if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12))
&& (bandwidth == Bandwidth::_125KHz))
|| ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz))
{
low_data_rate_optimize = true;
}
let mut cell_numerator = ((payload_len as i32) << 3) + (if crc_on { 16 } else { 0 })
- (4 * spreading_factor.value() as i32)
+ (if fixed_len { 0 } else { 20 });
if spreading_factor.value() <= 6 {
cell_denominator = 4 * (spreading_factor.value() as i32);
} else {
cell_numerator += 8;
if low_data_rate_optimize {
cell_denominator = 4 * ((spreading_factor.value() as i32) - 2);
} else {
cell_denominator = 4 * (spreading_factor.value() as i32);
}
}
if cell_numerator < 0 {
cell_numerator = 0;
}
let mut intermediate: i32 = (((cell_numerator + cell_denominator - 1) / cell_denominator) * cr_denominator)
+ (preamble_length_final as i32)
+ 12;
if spreading_factor.value() <= 6 {
intermediate = intermediate + 2;
}
(((4 * intermediate) + 1) * (1 << (spreading_factor.value() - 2))) as u32
}
}

View File

@ -0,0 +1,469 @@
use core::fmt::Debug;
use lorawan_device::async_device::radio as device;
#[allow(clippy::upper_case_acronyms)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum RadioError<BUS> {
SPI(BUS),
CS,
Reset,
AntRx,
AntTx,
Busy,
DIO1,
PayloadSizeMismatch(usize, usize),
RetentionListExceeded,
InvalidBandwidth,
ModulationParamsMissing,
PacketParamsMissing,
HeaderError,
CRCErrorUnexpected,
CRCErrorOnReceive,
TransmitTimeout,
ReceiveTimeout,
TimeoutUnexpected,
TransmitDoneUnexpected,
ReceiveDoneUnexpected,
CADUnexpected,
}
pub struct RadioSystemError {
pub rc_64khz_calibration: bool,
pub rc_13mhz_calibration: bool,
pub pll_calibration: bool,
pub adc_calibration: bool,
pub image_calibration: bool,
pub xosc_start: bool,
pub pll_lock: bool,
pub pa_ramp: bool,
}
#[derive(Clone, Copy, PartialEq)]
pub enum PacketType {
GFSK = 0x00,
LoRa = 0x01,
None = 0x0F,
}
impl PacketType {
pub const fn value(self) -> u8 {
self as u8
}
pub fn to_enum(value: u8) -> Self {
if value == 0x00 {
PacketType::GFSK
} else if value == 0x01 {
PacketType::LoRa
} else {
PacketType::None
}
}
}
#[derive(Clone, Copy)]
pub struct PacketStatus {
pub rssi: i8,
pub snr: i8,
pub signal_rssi: i8,
pub freq_error: u32,
}
#[derive(Clone, Copy, PartialEq)]
pub enum RadioType {
SX1261,
SX1262,
}
#[derive(Clone, Copy, PartialEq)]
pub enum RadioMode {
Sleep = 0x00, // sleep mode
StandbyRC = 0x01, // standby mode with RC oscillator
StandbyXOSC = 0x02, // standby mode with XOSC oscillator
FrequencySynthesis = 0x03, // frequency synthesis mode
Transmit = 0x04, // transmit mode
Receive = 0x05, // receive mode
ReceiveDutyCycle = 0x06, // receive duty cycle mode
ChannelActivityDetection = 0x07, // channel activity detection mode
}
impl RadioMode {
/// Returns the value of the mode.
pub const fn value(self) -> u8 {
self as u8
}
pub fn to_enum(value: u8) -> Self {
if value == 0x00 {
RadioMode::Sleep
} else if value == 0x01 {
RadioMode::StandbyRC
} else if value == 0x02 {
RadioMode::StandbyXOSC
} else if value == 0x03 {
RadioMode::FrequencySynthesis
} else if value == 0x04 {
RadioMode::Transmit
} else if value == 0x05 {
RadioMode::Receive
} else if value == 0x06 {
RadioMode::ReceiveDutyCycle
} else if value == 0x07 {
RadioMode::ChannelActivityDetection
} else {
RadioMode::Sleep
}
}
}
pub enum RadioState {
Idle = 0x00,
RxRunning = 0x01,
TxRunning = 0x02,
ChannelActivityDetecting = 0x03,
}
impl RadioState {
/// Returns the value of the state.
pub fn value(self) -> u8 {
self as u8
}
}
pub struct RadioStatus {
pub cmd_status: u8,
pub chip_mode: u8,
}
impl RadioStatus {
pub fn value(self) -> u8 {
(self.chip_mode << 4) | (self.cmd_status << 1)
}
}
#[derive(Clone, Copy)]
pub enum IrqMask {
None = 0x0000,
TxDone = 0x0001,
RxDone = 0x0002,
PreambleDetected = 0x0004,
SyncwordValid = 0x0008,
HeaderValid = 0x0010,
HeaderError = 0x0020,
CRCError = 0x0040,
CADDone = 0x0080,
CADActivityDetected = 0x0100,
RxTxTimeout = 0x0200,
All = 0xFFFF,
}
impl IrqMask {
pub fn value(self) -> u16 {
self as u16
}
}
#[derive(Clone, Copy)]
pub enum Register {
PacketParams = 0x0704, // packet configuration
PayloadLength = 0x0702, // payload size
SynchTimeout = 0x0706, // recalculated number of symbols
Syncword = 0x06C0, // Syncword values
LoRaSyncword = 0x0740, // LoRa Syncword value
GeneratedRandomNumber = 0x0819, //32-bit generated random number
AnaLNA = 0x08E2, // disable the LNA
AnaMixer = 0x08E5, // disable the mixer
RxGain = 0x08AC, // RX gain (0x94: power saving, 0x96: rx boosted)
XTATrim = 0x0911, // device internal trimming capacitor
OCP = 0x08E7, // over current protection max value
RetentionList = 0x029F, // retention list
IQPolarity = 0x0736, // optimize the inverted IQ operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4)
TxModulation = 0x0889, // modulation quality with 500 kHz LoRa Bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1)
TxClampCfg = 0x08D8, // better resistance to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2)
RTCCtrl = 0x0902, // RTC control
EvtClr = 0x0944, // event clear
}
impl Register {
pub fn addr(self) -> u16 {
self as u16
}
}
#[derive(Clone, Copy, PartialEq)]
pub enum OpCode {
GetStatus = 0xC0,
WriteRegister = 0x0D,
ReadRegister = 0x1D,
WriteBuffer = 0x0E,
ReadBuffer = 0x1E,
SetSleep = 0x84,
SetStandby = 0x80,
SetFS = 0xC1,
SetTx = 0x83,
SetRx = 0x82,
SetRxDutyCycle = 0x94,
SetCAD = 0xC5,
SetTxContinuousWave = 0xD1,
SetTxContinuousPremable = 0xD2,
SetPacketType = 0x8A,
GetPacketType = 0x11,
SetRFFrequency = 0x86,
SetTxParams = 0x8E,
SetPAConfig = 0x95,
SetCADParams = 0x88,
SetBufferBaseAddress = 0x8F,
SetModulationParams = 0x8B,
SetPacketParams = 0x8C,
GetRxBufferStatus = 0x13,
GetPacketStatus = 0x14,
GetRSSIInst = 0x15,
GetStats = 0x10,
ResetStats = 0x00,
CfgDIOIrq = 0x08,
GetIrqStatus = 0x12,
ClrIrqStatus = 0x02,
Calibrate = 0x89,
CalibrateImage = 0x98,
SetRegulatorMode = 0x96,
GetErrors = 0x17,
ClrErrors = 0x07,
SetTCXOMode = 0x97,
SetTxFallbackMode = 0x93,
SetRFSwitchMode = 0x9D,
SetStopRxTimerOnPreamble = 0x9F,
SetLoRaSymbTimeout = 0xA0,
}
impl OpCode {
pub fn value(self) -> u8 {
self as u8
}
}
pub struct SleepParams {
pub wakeup_rtc: bool, // get out of sleep mode if wakeup signal received from RTC
pub reset: bool,
pub warm_start: bool,
}
impl SleepParams {
pub fn value(self) -> u8 {
((self.warm_start as u8) << 2) | ((self.reset as u8) << 1) | (self.wakeup_rtc as u8)
}
}
#[derive(Clone, Copy, PartialEq)]
pub enum StandbyMode {
RC = 0x00,
XOSC = 0x01,
}
impl StandbyMode {
pub fn value(self) -> u8 {
self as u8
}
}
#[derive(Clone, Copy)]
pub enum RegulatorMode {
UseLDO = 0x00,
UseDCDC = 0x01,
}
impl RegulatorMode {
pub fn value(self) -> u8 {
self as u8
}
}
#[derive(Clone, Copy)]
pub struct CalibrationParams {
pub rc64k_enable: bool, // calibrate RC64K clock
pub rc13m_enable: bool, // calibrate RC13M clock
pub pll_enable: bool, // calibrate PLL
pub adc_pulse_enable: bool, // calibrate ADC Pulse
pub adc_bulkn_enable: bool, // calibrate ADC bulkN
pub adc_bulkp_enable: bool, // calibrate ADC bulkP
pub img_enable: bool,
}
impl CalibrationParams {
pub fn value(self) -> u8 {
((self.img_enable as u8) << 6)
| ((self.adc_bulkp_enable as u8) << 5)
| ((self.adc_bulkn_enable as u8) << 4)
| ((self.adc_pulse_enable as u8) << 3)
| ((self.pll_enable as u8) << 2)
| ((self.rc13m_enable as u8) << 1)
| ((self.rc64k_enable as u8) << 0)
}
}
#[derive(Clone, Copy)]
pub enum TcxoCtrlVoltage {
Ctrl1V6 = 0x00,
Ctrl1V7 = 0x01,
Ctrl1V8 = 0x02,
Ctrl2V2 = 0x03,
Ctrl2V4 = 0x04,
Ctrl2V7 = 0x05,
Ctrl3V0 = 0x06,
Ctrl3V3 = 0x07,
}
impl TcxoCtrlVoltage {
pub fn value(self) -> u8 {
self as u8
}
}
#[derive(Clone, Copy)]
pub enum RampTime {
Ramp10Us = 0x00,
Ramp20Us = 0x01,
Ramp40Us = 0x02,
Ramp80Us = 0x03,
Ramp200Us = 0x04,
Ramp800Us = 0x05,
Ramp1700Us = 0x06,
Ramp3400Us = 0x07,
}
impl RampTime {
pub fn value(self) -> u8 {
self as u8
}
}
#[derive(Clone, Copy, PartialEq)]
pub enum SpreadingFactor {
_5 = 0x05,
_6 = 0x06,
_7 = 0x07,
_8 = 0x08,
_9 = 0x09,
_10 = 0x0A,
_11 = 0x0B,
_12 = 0x0C,
}
impl SpreadingFactor {
pub fn value(self) -> u8 {
self as u8
}
}
impl From<device::SpreadingFactor> for SpreadingFactor {
fn from(sf: device::SpreadingFactor) -> Self {
match sf {
device::SpreadingFactor::_7 => SpreadingFactor::_7,
device::SpreadingFactor::_8 => SpreadingFactor::_8,
device::SpreadingFactor::_9 => SpreadingFactor::_9,
device::SpreadingFactor::_10 => SpreadingFactor::_10,
device::SpreadingFactor::_11 => SpreadingFactor::_11,
device::SpreadingFactor::_12 => SpreadingFactor::_12,
}
}
}
#[derive(Clone, Copy, PartialEq)]
pub enum Bandwidth {
_500KHz = 0x06,
_250KHz = 0x05,
_125KHz = 0x04,
}
impl Bandwidth {
pub fn value(self) -> u8 {
self as u8
}
pub fn value_in_hz(self) -> u32 {
match self {
Bandwidth::_125KHz => 125000u32,
Bandwidth::_250KHz => 250000u32,
Bandwidth::_500KHz => 500000u32,
}
}
}
impl From<device::Bandwidth> for Bandwidth {
fn from(bw: device::Bandwidth) -> Self {
match bw {
device::Bandwidth::_500KHz => Bandwidth::_500KHz,
device::Bandwidth::_250KHz => Bandwidth::_250KHz,
device::Bandwidth::_125KHz => Bandwidth::_125KHz,
}
}
}
#[derive(Clone, Copy)]
pub enum CodingRate {
_4_5 = 0x01,
_4_6 = 0x02,
_4_7 = 0x03,
_4_8 = 0x04,
}
impl CodingRate {
pub fn value(self) -> u8 {
self as u8
}
}
impl From<device::CodingRate> for CodingRate {
fn from(cr: device::CodingRate) -> Self {
match cr {
device::CodingRate::_4_5 => CodingRate::_4_5,
device::CodingRate::_4_6 => CodingRate::_4_6,
device::CodingRate::_4_7 => CodingRate::_4_7,
device::CodingRate::_4_8 => CodingRate::_4_8,
}
}
}
#[derive(Clone, Copy)]
pub struct ModulationParams {
pub spreading_factor: SpreadingFactor,
pub bandwidth: Bandwidth,
pub coding_rate: CodingRate,
pub low_data_rate_optimize: u8,
}
#[derive(Clone, Copy)]
pub struct PacketParams {
pub preamble_length: u16, // number of LoRa symbols in the preamble
pub implicit_header: bool, // if the header is explicit, it will be transmitted in the LoRa packet, but is not transmitted if the header is implicit (known fixed length)
pub payload_length: u8,
pub crc_on: bool,
pub iq_inverted: bool,
}
#[derive(Clone, Copy)]
pub enum CADSymbols {
_1 = 0x00,
_2 = 0x01,
_4 = 0x02,
_8 = 0x03,
_16 = 0x04,
}
impl CADSymbols {
pub fn value(self) -> u8 {
self as u8
}
}
#[derive(Clone, Copy)]
pub enum CADExitMode {
CADOnly = 0x00,
CADRx = 0x01,
CADLBT = 0x10,
}
impl CADExitMode {
pub fn value(self) -> u8 {
self as u8
}
}

View File

@ -0,0 +1,674 @@
use embedded_hal::digital::v2::OutputPin;
use embedded_hal_async::digital::Wait;
use embedded_hal_async::spi::SpiBus;
use super::mod_params::*;
use super::LoRa;
// Internal frequency of the radio
const SX126X_XTAL_FREQ: u32 = 32000000;
// Scaling factor used to perform fixed-point operations
const SX126X_PLL_STEP_SHIFT_AMOUNT: u32 = 14;
// PLL step - scaled with SX126X_PLL_STEP_SHIFT_AMOUNT
const SX126X_PLL_STEP_SCALED: u32 = SX126X_XTAL_FREQ >> (25 - SX126X_PLL_STEP_SHIFT_AMOUNT);
// Maximum value for parameter symbNum
const SX126X_MAX_LORA_SYMB_NUM_TIMEOUT: u8 = 248;
// Provides board-specific functionality for Semtech SX126x-based boards
impl<SPI, CTRL, WAIT, BUS> LoRa<SPI, CTRL, WAIT>
where
SPI: SpiBus<u8, Error = BUS>,
CTRL: OutputPin,
WAIT: Wait,
{
// Initialize the radio driver
pub(super) async fn sub_init(&mut self) -> Result<(), RadioError<BUS>> {
self.brd_reset().await?;
self.brd_wakeup().await?;
self.sub_set_standby(StandbyMode::RC).await?;
self.brd_io_tcxo_init().await?;
self.brd_io_rf_switch_init().await?;
self.image_calibrated = false;
Ok(())
}
// Wakeup the radio if it is in Sleep mode and check that Busy is low
pub(super) async fn sub_check_device_ready(&mut self) -> Result<(), RadioError<BUS>> {
let operating_mode = self.brd_get_operating_mode();
if operating_mode == RadioMode::Sleep || operating_mode == RadioMode::ReceiveDutyCycle {
self.brd_wakeup().await?;
}
self.brd_wait_on_busy().await?;
Ok(())
}
// Save the payload to be sent in the radio buffer
pub(super) async fn sub_set_payload(&mut self, payload: &[u8]) -> Result<(), RadioError<BUS>> {
self.brd_write_buffer(0x00, payload).await?;
Ok(())
}
// Read the payload received.
pub(super) async fn sub_get_payload(&mut self, buffer: &mut [u8]) -> Result<u8, RadioError<BUS>> {
let (size, offset) = self.sub_get_rx_buffer_status().await?;
if (size as usize) > buffer.len() {
Err(RadioError::PayloadSizeMismatch(size as usize, buffer.len()))
} else {
self.brd_read_buffer(offset, buffer).await?;
Ok(size)
}
}
// Send a payload
pub(super) async fn sub_send_payload(&mut self, payload: &[u8], timeout: u32) -> Result<(), RadioError<BUS>> {
self.sub_set_payload(payload).await?;
self.sub_set_tx(timeout).await?;
Ok(())
}
// Get a 32-bit random value generated by the radio. A valid packet type must have been configured before using this command.
//
// The radio must be in reception mode before executing this function. This code can potentially result in interrupt generation. It is the responsibility of
// the calling code to disable radio interrupts before calling this function, and re-enable them afterwards if necessary, or be certain that any interrupts
// generated during this process will not cause undesired side-effects in the software.
//
// The random numbers produced by the generator do not have a uniform or Gaussian distribution. If uniformity is needed, perform appropriate software post-processing.
pub(super) async fn sub_get_random(&mut self) -> Result<u32, RadioError<BUS>> {
let mut reg_ana_lna_buffer_original = [0x00u8];
let mut reg_ana_mixer_buffer_original = [0x00u8];
let mut reg_ana_lna_buffer = [0x00u8];
let mut reg_ana_mixer_buffer = [0x00u8];
let mut number_buffer = [0x00u8, 0x00u8, 0x00u8, 0x00u8];
self.brd_read_registers(Register::AnaLNA, &mut reg_ana_lna_buffer_original)
.await?;
reg_ana_lna_buffer[0] = reg_ana_lna_buffer_original[0] & (!(1 << 0));
self.brd_write_registers(Register::AnaLNA, &reg_ana_lna_buffer).await?;
self.brd_read_registers(Register::AnaMixer, &mut reg_ana_mixer_buffer_original)
.await?;
reg_ana_mixer_buffer[0] = reg_ana_mixer_buffer_original[0] & (!(1 << 7));
self.brd_write_registers(Register::AnaMixer, &reg_ana_mixer_buffer)
.await?;
// Set radio in continuous reception
self.sub_set_rx(0xFFFFFFu32).await?;
self.brd_read_registers(Register::GeneratedRandomNumber, &mut number_buffer)
.await?;
self.sub_set_standby(StandbyMode::RC).await?;
self.brd_write_registers(Register::AnaLNA, &reg_ana_lna_buffer_original)
.await?;
self.brd_write_registers(Register::AnaMixer, &reg_ana_mixer_buffer_original)
.await?;
Ok(Self::convert_u8_buffer_to_u32(&number_buffer))
}
// Set the radio in sleep mode
pub(super) async fn sub_set_sleep(&mut self, sleep_config: SleepParams) -> Result<(), RadioError<BUS>> {
self.brd_ant_sleep()?;
if !sleep_config.warm_start {
self.image_calibrated = false;
}
self.brd_write_command(OpCode::SetSleep, &[sleep_config.value()])
.await?;
self.brd_set_operating_mode(RadioMode::Sleep);
Ok(())
}
// Set the radio in configuration mode
pub(super) async fn sub_set_standby(&mut self, mode: StandbyMode) -> Result<(), RadioError<BUS>> {
self.brd_write_command(OpCode::SetStandby, &[mode.value()]).await?;
if mode == StandbyMode::RC {
self.brd_set_operating_mode(RadioMode::StandbyRC);
} else {
self.brd_set_operating_mode(RadioMode::StandbyXOSC);
}
self.brd_ant_sleep()?;
Ok(())
}
// Set the radio in FS mode
pub(super) async fn sub_set_fs(&mut self) -> Result<(), RadioError<BUS>> {
// antenna settings ???
self.brd_write_command(OpCode::SetFS, &[]).await?;
self.brd_set_operating_mode(RadioMode::FrequencySynthesis);
Ok(())
}
// Set the radio in transmission mode with timeout specified
pub(super) async fn sub_set_tx(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
let buffer = [
Self::timeout_1(timeout),
Self::timeout_2(timeout),
Self::timeout_3(timeout),
];
self.brd_ant_set_tx()?;
self.brd_set_operating_mode(RadioMode::Transmit);
self.brd_write_command(OpCode::SetTx, &buffer).await?;
Ok(())
}
// Set the radio in reception mode with timeout specified
pub(super) async fn sub_set_rx(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
let buffer = [
Self::timeout_1(timeout),
Self::timeout_2(timeout),
Self::timeout_3(timeout),
];
self.brd_ant_set_rx()?;
self.brd_set_operating_mode(RadioMode::Receive);
self.brd_write_registers(Register::RxGain, &[0x94u8]).await?;
self.brd_write_command(OpCode::SetRx, &buffer).await?;
Ok(())
}
// Set the radio in reception mode with Boosted LNA gain and timeout specified
pub(super) async fn sub_set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
let buffer = [
Self::timeout_1(timeout),
Self::timeout_2(timeout),
Self::timeout_3(timeout),
];
self.brd_ant_set_rx()?;
self.brd_set_operating_mode(RadioMode::Receive);
// set max LNA gain, increase current by ~2mA for around ~3dB in sensitivity
self.brd_write_registers(Register::RxGain, &[0x96u8]).await?;
self.brd_write_command(OpCode::SetRx, &buffer).await?;
Ok(())
}
// Set the Rx duty cycle management parameters
pub(super) async fn sub_set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError<BUS>> {
let buffer = [
((rx_time >> 16) & 0xFF) as u8,
((rx_time >> 8) & 0xFF) as u8,
(rx_time & 0xFF) as u8,
((sleep_time >> 16) & 0xFF) as u8,
((sleep_time >> 8) & 0xFF) as u8,
(sleep_time & 0xFF) as u8,
];
// antenna settings ???
self.brd_write_command(OpCode::SetRxDutyCycle, &buffer).await?;
self.brd_set_operating_mode(RadioMode::ReceiveDutyCycle);
Ok(())
}
// Set the radio in CAD mode
pub(super) async fn sub_set_cad(&mut self) -> Result<(), RadioError<BUS>> {
self.brd_ant_set_rx()?;
self.brd_write_command(OpCode::SetCAD, &[]).await?;
self.brd_set_operating_mode(RadioMode::ChannelActivityDetection);
Ok(())
}
// Set the radio in continuous wave transmission mode
pub(super) async fn sub_set_tx_continuous_wave(&mut self) -> Result<(), RadioError<BUS>> {
self.brd_ant_set_tx()?;
self.brd_write_command(OpCode::SetTxContinuousWave, &[]).await?;
self.brd_set_operating_mode(RadioMode::Transmit);
Ok(())
}
// Set the radio in continuous preamble transmission mode
pub(super) async fn sub_set_tx_infinite_preamble(&mut self) -> Result<(), RadioError<BUS>> {
self.brd_ant_set_tx()?;
self.brd_write_command(OpCode::SetTxContinuousPremable, &[]).await?;
self.brd_set_operating_mode(RadioMode::Transmit);
Ok(())
}
// Decide which interrupt will stop the internal radio rx timer.
// false timer stop after header/syncword detection
// true timer stop after preamble detection
pub(super) async fn sub_set_stop_rx_timer_on_preamble_detect(
&mut self,
enable: bool,
) -> Result<(), RadioError<BUS>> {
self.brd_write_command(OpCode::SetStopRxTimerOnPreamble, &[enable as u8])
.await?;
Ok(())
}
// Set the number of symbols the radio will wait to validate a reception
pub(super) async fn sub_set_lora_symb_num_timeout(&mut self, symb_num: u16) -> Result<(), RadioError<BUS>> {
let mut exp = 0u8;
let mut reg;
let mut mant = ((core::cmp::min(symb_num, SX126X_MAX_LORA_SYMB_NUM_TIMEOUT as u16) as u8) + 1) >> 1;
while mant > 31 {
mant = (mant + 3) >> 2;
exp += 1;
}
reg = mant << ((2 * exp) + 1);
self.brd_write_command(OpCode::SetLoRaSymbTimeout, &[reg]).await?;
if symb_num != 0 {
reg = exp + (mant << 3);
self.brd_write_registers(Register::SynchTimeout, &[reg]).await?;
}
Ok(())
}
// Set the power regulators operating mode (LDO or DC_DC). Using only LDO implies that the Rx or Tx current is doubled
pub(super) async fn sub_set_regulator_mode(&mut self, mode: RegulatorMode) -> Result<(), RadioError<BUS>> {
self.brd_write_command(OpCode::SetRegulatorMode, &[mode.value()])
.await?;
Ok(())
}
// Calibrate the given radio block
pub(super) async fn sub_calibrate(&mut self, calibrate_params: CalibrationParams) -> Result<(), RadioError<BUS>> {
self.brd_write_command(OpCode::Calibrate, &[calibrate_params.value()])
.await?;
Ok(())
}
// Calibrate the image rejection based on the given frequency
pub(super) async fn sub_calibrate_image(&mut self, freq: u32) -> Result<(), RadioError<BUS>> {
let mut cal_freq = [0x00u8, 0x00u8];
if freq > 900000000 {
cal_freq[0] = 0xE1;
cal_freq[1] = 0xE9;
} else if freq > 850000000 {
cal_freq[0] = 0xD7;
cal_freq[1] = 0xDB;
} else if freq > 770000000 {
cal_freq[0] = 0xC1;
cal_freq[1] = 0xC5;
} else if freq > 460000000 {
cal_freq[0] = 0x75;
cal_freq[1] = 0x81;
} else if freq > 425000000 {
cal_freq[0] = 0x6B;
cal_freq[1] = 0x6F;
}
self.brd_write_command(OpCode::CalibrateImage, &cal_freq).await?;
Ok(())
}
// Activate the extention of the timeout when a long preamble is used
pub(super) async fn sub_set_long_preamble(&mut self, _enable: u8) -> Result<(), RadioError<BUS>> {
Ok(()) // no operation currently
}
// Set the transmission parameters
// hp_max 0 for sx1261, 7 for sx1262
// device_sel 1 for sx1261, 0 for sx1262
// pa_lut 0 for 14dBm LUT, 1 for 22dBm LUT
pub(super) async fn sub_set_pa_config(
&mut self,
pa_duty_cycle: u8,
hp_max: u8,
device_sel: u8,
pa_lut: u8,
) -> Result<(), RadioError<BUS>> {
self.brd_write_command(OpCode::SetPAConfig, &[pa_duty_cycle, hp_max, device_sel, pa_lut])
.await?;
Ok(())
}
// Define into which mode the chip goes after a TX / RX done
pub(super) async fn sub_set_rx_tx_fallback_mode(&mut self, fallback_mode: u8) -> Result<(), RadioError<BUS>> {
self.brd_write_command(OpCode::SetTxFallbackMode, &[fallback_mode])
.await?;
Ok(())
}
// Set the IRQ mask and DIO masks
pub(super) async fn sub_set_dio_irq_params(
&mut self,
irq_mask: u16,
dio1_mask: u16,
dio2_mask: u16,
dio3_mask: u16,
) -> Result<(), RadioError<BUS>> {
let mut buffer = [0x00u8; 8];
buffer[0] = ((irq_mask >> 8) & 0x00FF) as u8;
buffer[1] = (irq_mask & 0x00FF) as u8;
buffer[2] = ((dio1_mask >> 8) & 0x00FF) as u8;
buffer[3] = (dio1_mask & 0x00FF) as u8;
buffer[4] = ((dio2_mask >> 8) & 0x00FF) as u8;
buffer[5] = (dio2_mask & 0x00FF) as u8;
buffer[6] = ((dio3_mask >> 8) & 0x00FF) as u8;
buffer[7] = (dio3_mask & 0x00FF) as u8;
self.brd_write_command(OpCode::CfgDIOIrq, &buffer).await?;
Ok(())
}
// Return the current IRQ status
pub(super) async fn sub_get_irq_status(&mut self) -> Result<u16, RadioError<BUS>> {
let mut irq_status = [0x00u8, 0x00u8];
self.brd_read_command(OpCode::GetIrqStatus, &mut irq_status).await?;
Ok(((irq_status[0] as u16) << 8) | (irq_status[1] as u16))
}
// Indicate if DIO2 is used to control an RF Switch
pub(super) async fn sub_set_dio2_as_rf_switch_ctrl(&mut self, enable: bool) -> Result<(), RadioError<BUS>> {
self.brd_write_command(OpCode::SetRFSwitchMode, &[enable as u8]).await?;
Ok(())
}
// Indicate if the radio main clock is supplied from a TCXO
// tcxo_voltage voltage used to control the TCXO on/off from DIO3
// timeout duration given to the TCXO to go to 32MHz
pub(super) async fn sub_set_dio3_as_tcxo_ctrl(
&mut self,
tcxo_voltage: TcxoCtrlVoltage,
timeout: u32,
) -> Result<(), RadioError<BUS>> {
let buffer = [
tcxo_voltage.value() & 0x07,
Self::timeout_1(timeout),
Self::timeout_2(timeout),
Self::timeout_3(timeout),
];
self.brd_write_command(OpCode::SetTCXOMode, &buffer).await?;
Ok(())
}
// Set the RF frequency (Hz)
pub(super) async fn sub_set_rf_frequency(&mut self, frequency: u32) -> Result<(), RadioError<BUS>> {
let mut buffer = [0x00u8; 4];
if !self.image_calibrated {
self.sub_calibrate_image(frequency).await?;
self.image_calibrated = true;
}
let freq_in_pll_steps = Self::convert_freq_in_hz_to_pll_step(frequency);
buffer[0] = ((freq_in_pll_steps >> 24) & 0xFF) as u8;
buffer[1] = ((freq_in_pll_steps >> 16) & 0xFF) as u8;
buffer[2] = ((freq_in_pll_steps >> 8) & 0xFF) as u8;
buffer[3] = (freq_in_pll_steps & 0xFF) as u8;
self.brd_write_command(OpCode::SetRFFrequency, &buffer).await?;
Ok(())
}
// Set the radio for the given protocol (LoRa or GFSK). This method has to be called before setting RF frequency, modulation paramaters, and packet paramaters.
pub(super) async fn sub_set_packet_type(&mut self, packet_type: PacketType) -> Result<(), RadioError<BUS>> {
self.packet_type = packet_type;
self.brd_write_command(OpCode::SetPacketType, &[packet_type.value()])
.await?;
Ok(())
}
// Get the current radio protocol (LoRa or GFSK)
pub(super) fn sub_get_packet_type(&mut self) -> PacketType {
self.packet_type
}
// Set the transmission parameters
// power RF output power [-18..13] dBm
// ramp_time transmission ramp up time
pub(super) async fn sub_set_tx_params(
&mut self,
mut power: i8,
ramp_time: RampTime,
) -> Result<(), RadioError<BUS>> {
if self.brd_get_radio_type() == RadioType::SX1261 {
if power == 15 {
self.sub_set_pa_config(0x06, 0x00, 0x01, 0x01).await?;
} else {
self.sub_set_pa_config(0x04, 0x00, 0x01, 0x01).await?;
}
if power >= 14 {
power = 14;
} else if power < -17 {
power = -17;
}
} else {
// Provide better resistance of the SX1262 Tx to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2)
let mut tx_clamp_cfg = [0x00u8];
self.brd_read_registers(Register::TxClampCfg, &mut tx_clamp_cfg).await?;
tx_clamp_cfg[0] = tx_clamp_cfg[0] | (0x0F << 1);
self.brd_write_registers(Register::TxClampCfg, &tx_clamp_cfg).await?;
self.sub_set_pa_config(0x04, 0x07, 0x00, 0x01).await?;
if power > 22 {
power = 22;
} else if power < -9 {
power = -9;
}
}
// power conversion of negative number from i8 to u8 ???
self.brd_write_command(OpCode::SetTxParams, &[power as u8, ramp_time.value()])
.await?;
Ok(())
}
// Set the modulation parameters
pub(super) async fn sub_set_modulation_params(&mut self) -> Result<(), RadioError<BUS>> {
if self.modulation_params.is_some() {
let mut buffer = [0x00u8; 4];
// Since this driver only supports LoRa, ensure the packet type is set accordingly
self.sub_set_packet_type(PacketType::LoRa).await?;
let modulation_params = self.modulation_params.unwrap();
buffer[0] = modulation_params.spreading_factor.value();
buffer[1] = modulation_params.bandwidth.value();
buffer[2] = modulation_params.coding_rate.value();
buffer[3] = modulation_params.low_data_rate_optimize;
self.brd_write_command(OpCode::SetModulationParams, &buffer).await?;
Ok(())
} else {
Err(RadioError::ModulationParamsMissing)
}
}
// Set the packet parameters
pub(super) async fn sub_set_packet_params(&mut self) -> Result<(), RadioError<BUS>> {
if self.packet_params.is_some() {
let mut buffer = [0x00u8; 6];
// Since this driver only supports LoRa, ensure the packet type is set accordingly
self.sub_set_packet_type(PacketType::LoRa).await?;
let packet_params = self.packet_params.unwrap();
buffer[0] = ((packet_params.preamble_length >> 8) & 0xFF) as u8;
buffer[1] = (packet_params.preamble_length & 0xFF) as u8;
buffer[2] = packet_params.implicit_header as u8;
buffer[3] = packet_params.payload_length;
buffer[4] = packet_params.crc_on as u8;
buffer[5] = packet_params.iq_inverted as u8;
self.brd_write_command(OpCode::SetPacketParams, &buffer).await?;
Ok(())
} else {
Err(RadioError::PacketParamsMissing)
}
}
// Set the channel activity detection (CAD) parameters
// symbols number of symbols to use for CAD operations
// det_peak limit for detection of SNR peak used in the CAD
// det_min minimum symbol recognition for CAD
// exit_mode operation to be done at the end of CAD action
// timeout timeout value to abort the CAD activity
pub(super) async fn sub_set_cad_params(
&mut self,
symbols: CADSymbols,
det_peak: u8,
det_min: u8,
exit_mode: CADExitMode,
timeout: u32,
) -> Result<(), RadioError<BUS>> {
let mut buffer = [0x00u8; 7];
buffer[0] = symbols.value();
buffer[1] = det_peak;
buffer[2] = det_min;
buffer[3] = exit_mode.value();
buffer[4] = Self::timeout_1(timeout);
buffer[5] = Self::timeout_2(timeout);
buffer[6] = Self::timeout_3(timeout);
self.brd_write_command(OpCode::SetCADParams, &buffer).await?;
self.brd_set_operating_mode(RadioMode::ChannelActivityDetection);
Ok(())
}
// Set the data buffer base address for transmission and reception
pub(super) async fn sub_set_buffer_base_address(
&mut self,
tx_base_address: u8,
rx_base_address: u8,
) -> Result<(), RadioError<BUS>> {
self.brd_write_command(OpCode::SetBufferBaseAddress, &[tx_base_address, rx_base_address])
.await?;
Ok(())
}
// Get the current radio status
pub(super) async fn sub_get_status(&mut self) -> Result<RadioStatus, RadioError<BUS>> {
let status = self.brd_read_command(OpCode::GetStatus, &mut []).await?;
Ok(RadioStatus {
cmd_status: (status & (0x07 << 1)) >> 1,
chip_mode: (status & (0x07 << 4)) >> 4,
})
}
// Get the instantaneous RSSI value for the last packet received
pub(super) async fn sub_get_rssi_inst(&mut self) -> Result<i8, RadioError<BUS>> {
let mut buffer = [0x00u8];
self.brd_read_command(OpCode::GetRSSIInst, &mut buffer).await?;
let rssi: i8 = ((-(buffer[0] as i32)) >> 1) as i8; // check this ???
Ok(rssi)
}
// Get the last received packet buffer status
pub(super) async fn sub_get_rx_buffer_status(&mut self) -> Result<(u8, u8), RadioError<BUS>> {
if self.packet_params.is_some() {
let mut status = [0x00u8; 2];
let mut payload_length_buffer = [0x00u8];
self.brd_read_command(OpCode::GetRxBufferStatus, &mut status).await?;
if (self.sub_get_packet_type() == PacketType::LoRa) && self.packet_params.unwrap().implicit_header {
self.brd_read_registers(Register::PayloadLength, &mut payload_length_buffer)
.await?;
} else {
payload_length_buffer[0] = status[0];
}
let payload_length = payload_length_buffer[0];
let offset = status[1];
Ok((payload_length, offset))
} else {
Err(RadioError::PacketParamsMissing)
}
}
// Get the last received packet payload status
pub(super) async fn sub_get_packet_status(&mut self) -> Result<PacketStatus, RadioError<BUS>> {
let mut status = [0x00u8; 3];
self.brd_read_command(OpCode::GetPacketStatus, &mut status).await?;
// check this ???
let rssi = ((-(status[0] as i32)) >> 1) as i8;
let snr = ((status[1] as i8) + 2) >> 2;
let signal_rssi = ((-(status[2] as i32)) >> 1) as i8;
let freq_error = self.frequency_error;
Ok(PacketStatus {
rssi,
snr,
signal_rssi,
freq_error,
})
}
// Get the possible system errors
pub(super) async fn sub_get_device_errors(&mut self) -> Result<RadioSystemError, RadioError<BUS>> {
let mut errors = [0x00u8; 2];
self.brd_read_command(OpCode::GetErrors, &mut errors).await?;
Ok(RadioSystemError {
rc_64khz_calibration: (errors[1] & (1 << 0)) != 0,
rc_13mhz_calibration: (errors[1] & (1 << 1)) != 0,
pll_calibration: (errors[1] & (1 << 2)) != 0,
adc_calibration: (errors[1] & (1 << 3)) != 0,
image_calibration: (errors[1] & (1 << 4)) != 0,
xosc_start: (errors[1] & (1 << 5)) != 0,
pll_lock: (errors[1] & (1 << 6)) != 0,
pa_ramp: (errors[0] & (1 << 0)) != 0,
})
}
// Clear all the errors in the device
pub(super) async fn sub_clear_device_errors(&mut self) -> Result<(), RadioError<BUS>> {
self.brd_write_command(OpCode::ClrErrors, &[0x00u8, 0x00u8]).await?;
Ok(())
}
// Clear the IRQs
pub(super) async fn sub_clear_irq_status(&mut self, irq: u16) -> Result<(), RadioError<BUS>> {
let mut buffer = [0x00u8, 0x00u8];
buffer[0] = ((irq >> 8) & 0xFF) as u8;
buffer[1] = (irq & 0xFF) as u8;
self.brd_write_command(OpCode::ClrIrqStatus, &buffer).await?;
Ok(())
}
// Utility functions
fn timeout_1(timeout: u32) -> u8 {
((timeout >> 16) & 0xFF) as u8
}
fn timeout_2(timeout: u32) -> u8 {
((timeout >> 8) & 0xFF) as u8
}
fn timeout_3(timeout: u32) -> u8 {
(timeout & 0xFF) as u8
}
// check this ???
fn convert_u8_buffer_to_u32(buffer: &[u8; 4]) -> u32 {
let b0 = buffer[0] as u32;
let b1 = buffer[1] as u32;
let b2 = buffer[2] as u32;
let b3 = buffer[3] as u32;
(b0 << 24) | (b1 << 16) | (b2 << 8) | b3
}
fn convert_freq_in_hz_to_pll_step(freq_in_hz: u32) -> u32 {
// Get integer and fractional parts of the frequency computed with a PLL step scaled value
let steps_int = freq_in_hz / SX126X_PLL_STEP_SCALED;
let steps_frac = freq_in_hz - (steps_int * SX126X_PLL_STEP_SCALED);
(steps_int << SX126X_PLL_STEP_SHIFT_AMOUNT)
+ (((steps_frac << SX126X_PLL_STEP_SHIFT_AMOUNT) + (SX126X_PLL_STEP_SCALED >> 1)) / SX126X_PLL_STEP_SCALED)
}
}

View File

@ -2,6 +2,7 @@
name = "embassy-macros"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[dependencies]
syn = { version = "1.0.76", features = ["full", "extra-traits"] }

View File

@ -2,6 +2,7 @@
name = "embassy-net"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[package.metadata.embassy_docs]

View File

@ -2,6 +2,7 @@
name = "embassy-nrf"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-nrf-v$VERSION/embassy-nrf/src/"
@ -73,8 +74,8 @@ 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.8", optional = true}
embedded-hal-async = { version = "0.1.0-alpha.1", optional = true}
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true}
embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true}
embedded-io = { version = "0.3.0", features = ["async"], optional = true }
defmt = { version = "0.3", optional = true }

View File

@ -128,6 +128,9 @@ embassy_hal_common::peripherals! {
// QDEC
QDEC,
// PDM
PDM,
}
impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);

View File

@ -128,6 +128,9 @@ embassy_hal_common::peripherals! {
// QDEC
QDEC,
// PDM
PDM,
}
impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);

View File

@ -158,6 +158,9 @@ embassy_hal_common::peripherals! {
// QDEC
QDEC,
// PDM
PDM,
}
#[cfg(feature = "nightly")]

View File

@ -161,6 +161,9 @@ embassy_hal_common::peripherals! {
// TEMP
TEMP,
// PDM
PDM,
}
#[cfg(feature = "nightly")]

View File

@ -260,6 +260,9 @@ embassy_hal_common::peripherals! {
P0_29,
P0_30,
P0_31,
// PDM
PDM,
}
impl_uarte!(UARTETWISPI0, UARTE0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0);

View File

@ -574,7 +574,7 @@ mod eh1 {
type Error = Infallible;
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::InputPin for Input<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Input<'d, T> {
fn is_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_high())
}
@ -588,7 +588,7 @@ mod eh1 {
type Error = Infallible;
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for Output<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Output<'d, T> {
fn set_high(&mut self) -> Result<(), Self::Error> {
Ok(self.set_high())
}
@ -598,7 +598,7 @@ mod eh1 {
}
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for Output<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Output<'d, T> {
fn is_set_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_set_high())
}
@ -615,7 +615,7 @@ mod eh1 {
/// Implement [`InputPin`] for [`Flex`];
///
/// If the pin is not in input mode the result is unspecified.
impl<'d, T: Pin> embedded_hal_1::digital::blocking::InputPin for Flex<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Flex<'d, T> {
fn is_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_high())
}
@ -625,7 +625,7 @@ mod eh1 {
}
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for Flex<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Flex<'d, T> {
fn set_high(&mut self) -> Result<(), Self::Error> {
Ok(self.set_high())
}
@ -635,7 +635,7 @@ mod eh1 {
}
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for Flex<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Flex<'d, T> {
fn is_set_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_set_high())
}

View File

@ -457,7 +457,7 @@ mod eh1 {
type Error = Infallible;
}
impl<'d, C: Channel, T: GpioPin> embedded_hal_1::digital::blocking::InputPin for InputChannel<'d, C, T> {
impl<'d, C: Channel, T: GpioPin> embedded_hal_1::digital::InputPin for InputChannel<'d, C, T> {
fn is_high(&self) -> Result<bool, Self::Error> {
Ok(self.pin.is_high())
}

View File

@ -76,6 +76,14 @@ pub mod gpio;
pub mod gpiote;
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
pub mod nvmc;
#[cfg(any(
feature = "nrf52810",
feature = "nrf52811",
feature = "nrf52833",
feature = "nrf52840",
feature = "_nrf9160"
))]
pub mod pdm;
pub mod ppi;
#[cfg(not(any(feature = "nrf52805", feature = "nrf52820", feature = "_nrf5340-net")))]
pub mod pwm;

242
embassy-nrf/src/pdm.rs Normal file
View File

@ -0,0 +1,242 @@
//! PDM mirophone interface
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 embassy_sync::waitqueue::AtomicWaker;
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::{self, InterruptExt};
use crate::peripherals::PDM;
use crate::{pac, Peripheral};
/// PDM microphone interface
pub struct Pdm<'d> {
irq: PeripheralRef<'d, interrupt::PDM>,
phantom: PhantomData<&'d PDM>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
BufferTooLong,
BufferZeroLength,
NotRunning,
}
static WAKER: AtomicWaker = AtomicWaker::new();
static DUMMY_BUFFER: [i16; 1] = [0; 1];
impl<'d> Pdm<'d> {
/// Create PDM driver
pub fn new(
pdm: impl Peripheral<P = PDM> + 'd,
irq: impl Peripheral<P = interrupt::PDM> + 'd,
clk: impl Peripheral<P = impl GpioPin> + 'd,
din: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(clk, din);
Self::new_inner(pdm, irq, clk.map_into(), din.map_into(), config)
}
fn new_inner(
_pdm: impl Peripheral<P = PDM> + 'd,
irq: impl Peripheral<P = interrupt::PDM> + 'd,
clk: PeripheralRef<'d, AnyPin>,
din: PeripheralRef<'d, AnyPin>,
config: Config,
) -> Self {
into_ref!(irq);
let r = Self::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
irq.disable();
irq.set_handler(|_| {
let r = Self::regs();
r.intenclr.write(|w| w.end().clear());
WAKER.wake();
});
irq.enable();
r.enable.write(|w| w.enable().set_bit());
Self {
phantom: PhantomData,
irq,
}
}
/// 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 = Self::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| w.tasks_start().set_bit());
}
/// Stop sampling microphon data inta a dummy buffer
pub async fn stop(&mut self) {
let r = Self::regs();
r.tasks_stop.write(|w| w.tasks_stop().set_bit());
r.events_started.reset();
}
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 = Self::regs();
if r.events_started.read().events_started().bit_is_clear() {
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 = Self::regs();
r.events_end.reset();
r.intenset.write(|w| w.end().set());
compiler_fence(Ordering::SeqCst);
poll_fn(|cx| {
WAKER.register(cx.waker());
if r.events_end.read().events_end().bit_is_set() {
return Poll::Ready(());
}
Poll::Pending
})
.await;
compiler_fence(Ordering::SeqCst);
}
fn regs() -> &'static pac::pdm::RegisterBlock {
unsafe { &*pac::PDM::ptr() }
}
}
/// 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,
}
}
}
#[derive(PartialEq)]
pub enum OperationMode {
Mono,
Stereo,
}
#[derive(PartialEq)]
pub enum Edge {
LeftRising,
LeftFalling,
}
impl<'d> Drop for Pdm<'d> {
fn drop(&mut self) {
let r = Self::regs();
r.tasks_stop.write(|w| w.tasks_stop().set_bit());
self.irq.disable();
r.enable.write(|w| w.enable().disabled());
r.psel.din.reset();
r.psel.clk.reset();
}
}

View File

@ -446,25 +446,25 @@ mod eh1 {
type Error = Error;
}
impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusFlush for Spim<'d, T> {
impl<'d, T: Instance> embedded_hal_1::spi::SpiBusFlush for Spim<'d, T> {
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusRead<u8> for Spim<'d, T> {
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::blocking::SpiBusWrite<u8> for Spim<'d, T> {
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::blocking::SpiBus<u8> for Spim<'d, T> {
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)
}

View File

@ -793,7 +793,7 @@ mod eh1 {
type Error = Error;
}
impl<'d, T: Instance> embedded_hal_1::i2c::blocking::I2c for Twim<'d, T> {
impl<'d, T: Instance> embedded_hal_1::i2c::I2c for Twim<'d, T> {
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_read(address, buffer)
}
@ -823,14 +823,14 @@ mod eh1 {
fn transaction<'a>(
&mut self,
_address: u8,
_operations: &mut [embedded_hal_1::i2c::blocking::Operation<'a>],
_operations: &mut [embedded_hal_1::i2c::Operation<'a>],
) -> Result<(), Self::Error> {
todo!();
}
fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error>
where
O: IntoIterator<Item = embedded_hal_1::i2c::blocking::Operation<'a>>,
O: IntoIterator<Item = embedded_hal_1::i2c::Operation<'a>>,
{
todo!();
}

View File

@ -173,6 +173,61 @@ impl<'d, T: Instance> Uarte<'d, T> {
(self.tx, self.rx)
}
/// Split the Uarte into a transmitter and receiver that will
/// return on idle, which is determined as the time it takes
/// for two bytes to be received.
pub fn split_with_idle<U: TimerInstance>(
self,
timer: impl Peripheral<P = U> + 'd,
ppi_ch1: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
ppi_ch2: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
) -> (UarteTx<'d, T>, UarteRxWithIdle<'d, T, U>) {
let mut timer = Timer::new(timer);
into_ref!(ppi_ch1, ppi_ch2);
let r = T::regs();
// BAUDRATE register values are `baudrate * 2^32 / 16000000`
// source: https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values
//
// We want to stop RX if line is idle for 2 bytes worth of time
// That is 20 bits (each byte is 1 start bit + 8 data bits + 1 stop bit)
// This gives us the amount of 16M ticks for 20 bits.
let baudrate = r.baudrate.read().baudrate().variant().unwrap();
let timeout = 0x8000_0000 / (baudrate as u32 / 40);
timer.set_frequency(Frequency::F16MHz);
timer.cc(0).write(timeout);
timer.cc(0).short_compare_clear();
timer.cc(0).short_compare_stop();
let mut ppi_ch1 = Ppi::new_one_to_two(
ppi_ch1.map_into(),
Event::from_reg(&r.events_rxdrdy),
timer.task_clear(),
timer.task_start(),
);
ppi_ch1.enable();
let mut ppi_ch2 = Ppi::new_one_to_one(
ppi_ch2.map_into(),
timer.cc(0).event_compare(),
Task::from_reg(&r.tasks_stoprx),
);
ppi_ch2.enable();
(
self.tx,
UarteRxWithIdle {
rx: self.rx,
timer,
ppi_ch1: ppi_ch1,
_ppi_ch2: ppi_ch2,
},
)
}
/// Return the endtx event for use with PPI
pub fn event_endtx(&self) -> Event {
let r = T::regs();
@ -597,6 +652,117 @@ impl<'a, T: Instance> Drop for UarteRx<'a, T> {
}
}
pub struct UarteRxWithIdle<'d, T: Instance, U: TimerInstance> {
rx: UarteRx<'d, T>,
timer: Timer<'d, U>,
ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 2>,
_ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 1>,
}
impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> {
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
self.ppi_ch1.disable();
self.rx.read(buffer).await
}
pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
self.ppi_ch1.disable();
self.rx.blocking_read(buffer)
}
pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
if buffer.len() == 0 {
return Err(Error::BufferZeroLength);
}
if buffer.len() > EASY_DMA_SIZE {
return Err(Error::BufferTooLong);
}
let ptr = buffer.as_ptr();
let len = buffer.len();
let r = T::regs();
let s = T::state();
self.ppi_ch1.enable();
let drop = OnDrop::new(|| {
self.timer.stop();
r.intenclr.write(|w| w.endrx().clear());
r.events_rxto.reset();
r.tasks_stoprx.write(|w| unsafe { w.bits(1) });
while r.events_endrx.read().bits() == 0 {}
});
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
r.events_endrx.reset();
r.intenset.write(|w| w.endrx().set());
compiler_fence(Ordering::SeqCst);
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
poll_fn(|cx| {
s.endrx_waker.register(cx.waker());
if r.events_endrx.read().bits() != 0 {
return Poll::Ready(());
}
Poll::Pending
})
.await;
compiler_fence(Ordering::SeqCst);
let n = r.rxd.amount.read().amount().bits() as usize;
self.timer.stop();
r.events_rxstarted.reset();
drop.defuse();
Ok(n)
}
pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
if buffer.len() == 0 {
return Err(Error::BufferZeroLength);
}
if buffer.len() > EASY_DMA_SIZE {
return Err(Error::BufferTooLong);
}
let ptr = buffer.as_ptr();
let len = buffer.len();
let r = T::regs();
self.ppi_ch1.enable();
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
r.events_endrx.reset();
r.intenclr.write(|w| w.endrx().clear());
compiler_fence(Ordering::SeqCst);
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
while r.events_endrx.read().bits() == 0 {}
compiler_fence(Ordering::SeqCst);
let n = r.rxd.amount.read().amount().bits() as usize;
self.timer.stop();
r.events_rxstarted.reset();
Ok(n)
}
}
#[cfg(not(any(feature = "_nrf9160", feature = "nrf5340")))]
pub(crate) fn apply_workaround_for_enable_anomaly(_r: &crate::pac::uarte0::RegisterBlock) {
// Do nothing
@ -665,270 +831,6 @@ pub(crate) fn drop_tx_rx(r: &pac::uarte0::RegisterBlock, s: &sealed::State) {
}
}
/// Interface to an UARTE peripheral that uses an additional timer and two PPI channels,
/// allowing it to implement the ReadUntilIdle trait.
pub struct UarteWithIdle<'d, U: Instance, T: TimerInstance> {
tx: UarteTx<'d, U>,
rx: UarteRxWithIdle<'d, U, T>,
}
impl<'d, U: Instance, T: TimerInstance> UarteWithIdle<'d, U, T> {
/// Create a new UARTE without hardware flow control
pub fn new(
uarte: impl Peripheral<P = U> + 'd,
timer: impl Peripheral<P = T> + 'd,
ppi_ch1: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
ppi_ch2: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
irq: impl Peripheral<P = U::Interrupt> + 'd,
rxd: impl Peripheral<P = impl GpioPin> + 'd,
txd: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(rxd, txd);
Self::new_inner(
uarte,
timer,
ppi_ch1,
ppi_ch2,
irq,
rxd.map_into(),
txd.map_into(),
None,
None,
config,
)
}
/// Create a new UARTE with hardware flow control (RTS/CTS)
pub fn new_with_rtscts(
uarte: impl Peripheral<P = U> + 'd,
timer: impl Peripheral<P = T> + 'd,
ppi_ch1: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
ppi_ch2: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
irq: impl Peripheral<P = U::Interrupt> + 'd,
rxd: impl Peripheral<P = impl GpioPin> + 'd,
txd: impl Peripheral<P = impl GpioPin> + 'd,
cts: impl Peripheral<P = impl GpioPin> + 'd,
rts: impl Peripheral<P = impl GpioPin> + 'd,
config: Config,
) -> Self {
into_ref!(rxd, txd, cts, rts);
Self::new_inner(
uarte,
timer,
ppi_ch1,
ppi_ch2,
irq,
rxd.map_into(),
txd.map_into(),
Some(cts.map_into()),
Some(rts.map_into()),
config,
)
}
fn new_inner(
uarte: impl Peripheral<P = U> + 'd,
timer: impl Peripheral<P = T> + 'd,
ppi_ch1: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
ppi_ch2: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
irq: impl Peripheral<P = U::Interrupt> + 'd,
rxd: PeripheralRef<'d, AnyPin>,
txd: PeripheralRef<'d, AnyPin>,
cts: Option<PeripheralRef<'d, AnyPin>>,
rts: Option<PeripheralRef<'d, AnyPin>>,
config: Config,
) -> Self {
let baudrate = config.baudrate;
let (tx, rx) = Uarte::new_inner(uarte, irq, rxd, txd, cts, rts, config).split();
let mut timer = Timer::new(timer);
into_ref!(ppi_ch1, ppi_ch2);
let r = U::regs();
// BAUDRATE register values are `baudrate * 2^32 / 16000000`
// source: https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values
//
// We want to stop RX if line is idle for 2 bytes worth of time
// That is 20 bits (each byte is 1 start bit + 8 data bits + 1 stop bit)
// This gives us the amount of 16M ticks for 20 bits.
let timeout = 0x8000_0000 / (baudrate as u32 / 40);
timer.set_frequency(Frequency::F16MHz);
timer.cc(0).write(timeout);
timer.cc(0).short_compare_clear();
timer.cc(0).short_compare_stop();
let mut ppi_ch1 = Ppi::new_one_to_two(
ppi_ch1.map_into(),
Event::from_reg(&r.events_rxdrdy),
timer.task_clear(),
timer.task_start(),
);
ppi_ch1.enable();
let mut ppi_ch2 = Ppi::new_one_to_one(
ppi_ch2.map_into(),
timer.cc(0).event_compare(),
Task::from_reg(&r.tasks_stoprx),
);
ppi_ch2.enable();
Self {
tx,
rx: UarteRxWithIdle {
rx,
timer,
ppi_ch1: ppi_ch1,
_ppi_ch2: ppi_ch2,
},
}
}
/// Split the Uarte into a transmitter and receiver, which is
/// particuarly useful when having two tasks correlating to
/// transmitting and receiving.
pub fn split(self) -> (UarteTx<'d, U>, UarteRxWithIdle<'d, U, T>) {
(self.tx, self.rx)
}
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
self.rx.read(buffer).await
}
pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
self.tx.write(buffer).await
}
pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
self.rx.blocking_read(buffer)
}
pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {
self.tx.blocking_write(buffer)
}
pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
self.rx.read_until_idle(buffer).await
}
pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
self.rx.blocking_read_until_idle(buffer)
}
}
pub struct UarteRxWithIdle<'d, U: Instance, T: TimerInstance> {
rx: UarteRx<'d, U>,
timer: Timer<'d, T>,
ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 2>,
_ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 1>,
}
impl<'d, U: Instance, T: TimerInstance> UarteRxWithIdle<'d, U, T> {
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
self.ppi_ch1.disable();
self.rx.read(buffer).await
}
pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
self.ppi_ch1.disable();
self.rx.blocking_read(buffer)
}
pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
if buffer.len() == 0 {
return Err(Error::BufferZeroLength);
}
if buffer.len() > EASY_DMA_SIZE {
return Err(Error::BufferTooLong);
}
let ptr = buffer.as_ptr();
let len = buffer.len();
let r = U::regs();
let s = U::state();
self.ppi_ch1.enable();
let drop = OnDrop::new(|| {
self.timer.stop();
r.intenclr.write(|w| w.endrx().clear());
r.events_rxto.reset();
r.tasks_stoprx.write(|w| unsafe { w.bits(1) });
while r.events_endrx.read().bits() == 0 {}
});
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
r.events_endrx.reset();
r.intenset.write(|w| w.endrx().set());
compiler_fence(Ordering::SeqCst);
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
poll_fn(|cx| {
s.endrx_waker.register(cx.waker());
if r.events_endrx.read().bits() != 0 {
return Poll::Ready(());
}
Poll::Pending
})
.await;
compiler_fence(Ordering::SeqCst);
let n = r.rxd.amount.read().amount().bits() as usize;
self.timer.stop();
r.events_rxstarted.reset();
drop.defuse();
Ok(n)
}
pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
if buffer.len() == 0 {
return Err(Error::BufferZeroLength);
}
if buffer.len() > EASY_DMA_SIZE {
return Err(Error::BufferTooLong);
}
let ptr = buffer.as_ptr();
let len = buffer.len();
let r = U::regs();
self.ppi_ch1.enable();
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
r.events_endrx.reset();
r.intenclr.write(|w| w.endrx().clear());
compiler_fence(Ordering::SeqCst);
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
while r.events_endrx.read().bits() == 0 {}
compiler_fence(Ordering::SeqCst);
let n = r.rxd.amount.read().amount().bits() as usize;
self.timer.stop();
r.events_rxstarted.reset();
Ok(n)
}
}
pub(crate) mod sealed {
use core::sync::atomic::AtomicU8;
@ -1006,18 +908,6 @@ mod eh02 {
Ok(())
}
}
impl<'d, U: Instance, T: TimerInstance> embedded_hal_02::blocking::serial::Write<u8> for UarteWithIdle<'d, U, T> {
type Error = Error;
fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(buffer)
}
fn bflush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
}
#[cfg(feature = "unstable-traits")]
@ -1040,7 +930,7 @@ mod eh1 {
type Error = Error;
}
impl<'d, T: Instance> embedded_hal_1::serial::blocking::Write for Uarte<'d, T> {
impl<'d, T: Instance> embedded_hal_1::serial::Write for Uarte<'d, T> {
fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(buffer)
}
@ -1054,7 +944,7 @@ mod eh1 {
type Error = Error;
}
impl<'d, T: Instance> embedded_hal_1::serial::blocking::Write for UarteTx<'d, T> {
impl<'d, T: Instance> embedded_hal_1::serial::Write for UarteTx<'d, T> {
fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(buffer)
}
@ -1067,10 +957,6 @@ mod eh1 {
impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for UarteRx<'d, T> {
type Error = Error;
}
impl<'d, U: Instance, T: TimerInstance> embedded_hal_1::serial::ErrorType for UarteWithIdle<'d, U, T> {
type Error = Error;
}
}
#[cfg(all(
@ -1126,26 +1012,4 @@ mod eha {
self.read(buffer)
}
}
impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Read for UarteWithIdle<'d, U, T> {
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
self.read(buffer)
}
}
impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Write for UarteWithIdle<'d, U, T> {
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> {
self.write(buffer)
}
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
async move { Ok(()) }
}
}
}

View File

@ -2,6 +2,7 @@
name = "embassy-rp"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-rp-v$VERSION/embassy-rp/src/"
@ -31,7 +32,7 @@ nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "
# Implement embedded-hal 1.0 alpha traits.
# Implement embedded-hal-async traits if `nightly` is set as well.
unstable-traits = ["embedded-hal-1"]
unstable-traits = ["embedded-hal-1", "embedded-hal-nb"]
[dependencies]
embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
@ -59,5 +60,6 @@ rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c90
#rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true}
embedded-hal-async = { version = "0.1.0-alpha.1", optional = true}
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true}
embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true}
embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true}

View File

@ -599,12 +599,12 @@ pub(crate) mod sealed {
fn pin_bank(&self) -> u8;
#[inline]
fn pin(&self) -> u8 {
fn _pin(&self) -> u8 {
self.pin_bank() & 0x1f
}
#[inline]
fn bank(&self) -> Bank {
fn _bank(&self) -> Bank {
if self.pin_bank() & 0x20 == 0 {
Bank::Bank0
} else {
@ -613,35 +613,35 @@ pub(crate) mod sealed {
}
fn io(&self) -> pac::io::Gpio {
let block = match self.bank() {
let block = match self._bank() {
Bank::Bank0 => crate::pac::IO_BANK0,
Bank::Qspi => crate::pac::IO_QSPI,
};
block.gpio(self.pin() as _)
block.gpio(self._pin() as _)
}
fn pad_ctrl(&self) -> Reg<pac::pads::regs::GpioCtrl, RW> {
let block = match self.bank() {
let block = match self._bank() {
Bank::Bank0 => crate::pac::PADS_BANK0,
Bank::Qspi => crate::pac::PADS_QSPI,
};
block.gpio(self.pin() as _)
block.gpio(self._pin() as _)
}
fn sio_out(&self) -> pac::sio::Gpio {
SIO.gpio_out(self.bank() as _)
SIO.gpio_out(self._bank() as _)
}
fn sio_oe(&self) -> pac::sio::Gpio {
SIO.gpio_oe(self.bank() as _)
SIO.gpio_oe(self._bank() as _)
}
fn sio_in(&self) -> Reg<u32, RW> {
SIO.gpio_in(self.bank() as _)
SIO.gpio_in(self._bank() as _)
}
fn int_proc(&self) -> pac::io::Int {
let io_block = match self.bank() {
let io_block = match self._bank() {
Bank::Bank0 => crate::pac::IO_BANK0,
Bank::Qspi => crate::pac::IO_QSPI,
};
@ -658,6 +658,18 @@ pub trait Pin: Peripheral<P = Self> + Into<AnyPin> + sealed::Pin + Sized + 'stat
pin_bank: self.pin_bank(),
}
}
/// Returns the pin number within a bank
#[inline]
fn pin(&self) -> u8 {
self._pin()
}
/// Returns the bank of this pin
#[inline]
fn bank(&self) -> Bank {
self._bank()
}
}
pub struct AnyPin {
@ -867,7 +879,7 @@ mod eh1 {
type Error = Infallible;
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::InputPin for Input<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Input<'d, T> {
fn is_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_high())
}
@ -881,7 +893,7 @@ mod eh1 {
type Error = Infallible;
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for Output<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Output<'d, T> {
fn set_high(&mut self) -> Result<(), Self::Error> {
Ok(self.set_high())
}
@ -891,7 +903,7 @@ mod eh1 {
}
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for Output<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Output<'d, T> {
fn is_set_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_set_high())
}
@ -901,7 +913,7 @@ mod eh1 {
}
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::ToggleableOutputPin for Output<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::ToggleableOutputPin for Output<'d, T> {
fn toggle(&mut self) -> Result<(), Self::Error> {
Ok(self.toggle())
}
@ -911,7 +923,7 @@ mod eh1 {
type Error = Infallible;
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for OutputOpenDrain<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for OutputOpenDrain<'d, T> {
fn set_high(&mut self) -> Result<(), Self::Error> {
Ok(self.set_high())
}
@ -921,7 +933,7 @@ mod eh1 {
}
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for OutputOpenDrain<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for OutputOpenDrain<'d, T> {
fn is_set_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_set_high())
}
@ -931,7 +943,7 @@ mod eh1 {
}
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::ToggleableOutputPin for OutputOpenDrain<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::ToggleableOutputPin for OutputOpenDrain<'d, T> {
fn toggle(&mut self) -> Result<(), Self::Error> {
Ok(self.toggle())
}
@ -941,7 +953,7 @@ mod eh1 {
type Error = Infallible;
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::InputPin for Flex<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Flex<'d, T> {
fn is_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_high())
}
@ -951,7 +963,7 @@ mod eh1 {
}
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for Flex<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Flex<'d, T> {
fn set_high(&mut self) -> Result<(), Self::Error> {
Ok(self.set_high())
}
@ -961,7 +973,7 @@ mod eh1 {
}
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for Flex<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Flex<'d, T> {
fn is_set_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_set_high())
}
@ -971,7 +983,7 @@ mod eh1 {
}
}
impl<'d, T: Pin> embedded_hal_1::digital::blocking::ToggleableOutputPin for Flex<'d, T> {
impl<'d, T: Pin> embedded_hal_1::digital::ToggleableOutputPin for Flex<'d, T> {
fn toggle(&mut self) -> Result<(), Self::Error> {
Ok(self.toggle())
}

View File

@ -1,9 +1,12 @@
use core::future;
use core::marker::PhantomData;
use core::task::Poll;
use embassy_cortex_m::interrupt::InterruptExt;
use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use pac::i2c;
use crate::dma::AnyChannel;
use crate::gpio::sealed::Pin;
use crate::gpio::AnyPin;
use crate::{pac, peripherals, Peripheral};
@ -52,31 +55,276 @@ impl Default for Config {
const FIFO_SIZE: u8 = 16;
pub struct I2c<'d, T: Instance, M: Mode> {
_tx_dma: Option<PeripheralRef<'d, AnyChannel>>,
_rx_dma: Option<PeripheralRef<'d, AnyChannel>>,
_dma_buf: [u16; 256],
phantom: PhantomData<(&'d mut T, M)>,
}
impl<'d, T: Instance> I2c<'d, T, Blocking> {
pub fn new_blocking(
_peri: impl Peripheral<P = T> + 'd,
peri: impl Peripheral<P = T> + 'd,
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
sda: impl Peripheral<P = impl SdaPin<T>> + 'd,
config: Config,
) -> Self {
into_ref!(scl, sda);
Self::new_inner(_peri, scl.map_into(), sda.map_into(), None, None, config)
Self::new_inner(peri, scl.map_into(), sda.map_into(), config)
}
}
impl<'d, T: Instance, M: Mode> I2c<'d, T, M> {
impl<'d, T: Instance> I2c<'d, T, Async> {
pub fn new_async(
peri: impl Peripheral<P = T> + 'd,
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
sda: impl Peripheral<P = impl SdaPin<T>> + 'd,
irq: impl Peripheral<P = T::Interrupt> + 'd,
config: Config,
) -> Self {
into_ref!(scl, sda, irq);
let i2c = Self::new_inner(peri, scl.map_into(), sda.map_into(), config);
irq.set_handler(Self::on_interrupt);
unsafe {
let i2c = T::regs();
// mask everything initially
i2c.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0));
}
irq.unpend();
debug_assert!(!irq.is_pending());
irq.enable();
i2c
}
/// Calls `f` to check if we are ready or not.
/// If not, `g` is called once the waker is set (to eg enable the required interrupts).
async fn wait_on<F, U, G>(&mut self, mut f: F, mut g: G) -> U
where
F: FnMut(&mut Self) -> Poll<U>,
G: FnMut(&mut Self),
{
future::poll_fn(|cx| {
let r = f(self);
if r.is_pending() {
T::waker().register(cx.waker());
g(self);
}
r
})
.await
}
// Mask interrupts and wake any task waiting for this interrupt
unsafe fn on_interrupt(_: *mut ()) {
let i2c = T::regs();
i2c.ic_intr_mask().write_value(pac::i2c::regs::IcIntrMask::default());
T::waker().wake();
}
async fn read_async_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> {
if buffer.is_empty() {
return Err(Error::InvalidReadBufferLength);
}
let p = T::regs();
let mut remaining = buffer.len();
let mut remaining_queue = buffer.len();
let mut abort_reason = Ok(());
while remaining > 0 {
// Waggle SCK - basically the same as write
let tx_fifo_space = Self::tx_fifo_capacity();
let mut batch = 0;
debug_assert!(remaining_queue > 0);
for _ in 0..remaining_queue.min(tx_fifo_space as usize) {
remaining_queue -= 1;
let last = remaining_queue == 0;
batch += 1;
unsafe {
p.ic_data_cmd().write(|w| {
w.set_restart(restart && remaining_queue == buffer.len() - 1);
w.set_stop(last && send_stop);
w.set_cmd(true);
});
}
}
// We've either run out of txfifo or just plain finished setting up
// the clocks for the message - either way we need to wait for rx
// data.
debug_assert!(batch > 0);
let res = self
.wait_on(
|me| {
let rxfifo = Self::rx_fifo_len();
if let Err(abort_reason) = me.read_and_clear_abort_reason() {
Poll::Ready(Err(abort_reason))
} else if rxfifo >= batch {
Poll::Ready(Ok(rxfifo))
} else {
Poll::Pending
}
},
|_me| unsafe {
// Set the read threshold to the number of bytes we're
// expecting so we don't get spurious interrupts.
p.ic_rx_tl().write(|w| w.set_rx_tl(batch - 1));
p.ic_intr_mask().modify(|w| {
w.set_m_rx_full(true);
w.set_m_tx_abrt(true);
});
},
)
.await;
match res {
Err(reason) => {
abort_reason = Err(reason);
break;
}
Ok(rxfifo) => {
// Fetch things from rx fifo. We're assuming we're the only
// rxfifo reader, so nothing else can take things from it.
let rxbytes = (rxfifo as usize).min(remaining);
let received = buffer.len() - remaining;
for b in &mut buffer[received..received + rxbytes] {
*b = unsafe { p.ic_data_cmd().read().dat() };
}
remaining -= rxbytes;
}
};
}
self.wait_stop_det(abort_reason, send_stop).await
}
async fn write_async_internal(
&mut self,
bytes: impl IntoIterator<Item = u8>,
send_stop: bool,
) -> Result<(), Error> {
let p = T::regs();
let mut bytes = bytes.into_iter().peekable();
let res = 'xmit: loop {
let tx_fifo_space = Self::tx_fifo_capacity();
for _ in 0..tx_fifo_space {
if let Some(byte) = bytes.next() {
let last = bytes.peek().is_none();
unsafe {
p.ic_data_cmd().write(|w| {
w.set_stop(last && send_stop);
w.set_cmd(false);
w.set_dat(byte);
});
}
} else {
break 'xmit Ok(());
}
}
let res = self
.wait_on(
|me| {
if let abort_reason @ Err(_) = me.read_and_clear_abort_reason() {
Poll::Ready(abort_reason)
} else if !Self::tx_fifo_full() {
// resume if there's any space free in the tx fifo
Poll::Ready(Ok(()))
} else {
Poll::Pending
}
},
|_me| unsafe {
// Set tx "free" threshold a little high so that we get
// woken before the fifo completely drains to minimize
// transfer stalls.
p.ic_tx_tl().write(|w| w.set_tx_tl(1));
p.ic_intr_mask().modify(|w| {
w.set_m_tx_empty(true);
w.set_m_tx_abrt(true);
})
},
)
.await;
if res.is_err() {
break res;
}
};
self.wait_stop_det(res, send_stop).await
}
/// Helper to wait for a stop bit, for both tx and rx. If we had an abort,
/// then we'll get a hardware-generated stop, otherwise wait for a stop if
/// we're expecting it.
///
/// Also handles an abort which arises while processing the tx fifo.
async fn wait_stop_det(&mut self, had_abort: Result<(), Error>, do_stop: bool) -> Result<(), Error> {
if had_abort.is_err() || do_stop {
let p = T::regs();
let had_abort2 = self
.wait_on(
|me| unsafe {
// We could see an abort while processing fifo backlog,
// so handle it here.
let abort = me.read_and_clear_abort_reason();
if had_abort.is_ok() && abort.is_err() {
Poll::Ready(abort)
} else if p.ic_raw_intr_stat().read().stop_det() {
Poll::Ready(Ok(()))
} else {
Poll::Pending
}
},
|_me| unsafe {
p.ic_intr_mask().modify(|w| {
w.set_m_stop_det(true);
w.set_m_tx_abrt(true);
});
},
)
.await;
unsafe {
p.ic_clr_stop_det().read();
}
had_abort.and(had_abort2)
} else {
had_abort
}
}
pub async fn read_async(&mut self, addr: u16, buffer: &mut [u8]) -> Result<(), Error> {
Self::setup(addr)?;
self.read_async_internal(buffer, false, true).await
}
pub async fn write_async(&mut self, addr: u16, bytes: impl IntoIterator<Item = u8>) -> Result<(), Error> {
Self::setup(addr)?;
self.write_async_internal(bytes, true).await
}
}
impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
fn new_inner(
_peri: impl Peripheral<P = T> + 'd,
scl: PeripheralRef<'d, AnyPin>,
sda: PeripheralRef<'d, AnyPin>,
_tx_dma: Option<PeripheralRef<'d, AnyChannel>>,
_rx_dma: Option<PeripheralRef<'d, AnyChannel>>,
config: Config,
) -> Self {
into_ref!(_peri);
@ -87,6 +335,10 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> {
let p = T::regs();
unsafe {
let reset = T::reset();
crate::reset::reset(reset);
crate::reset::unreset_wait(reset);
p.ic_enable().write(|w| w.set_enable(false));
// Select controller mode & speed
@ -172,12 +424,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> {
p.ic_enable().write(|w| w.set_enable(true));
}
Self {
_tx_dma,
_rx_dma,
_dma_buf: [0; 256],
phantom: PhantomData,
}
Self { phantom: PhantomData }
}
fn setup(addr: u16) -> Result<(), Error> {
@ -198,6 +445,23 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> {
Ok(())
}
#[inline]
fn tx_fifo_full() -> bool {
Self::tx_fifo_capacity() == 0
}
#[inline]
fn tx_fifo_capacity() -> u8 {
let p = T::regs();
unsafe { FIFO_SIZE - p.ic_txflr().read().txflr() }
}
#[inline]
fn rx_fifo_len() -> u8 {
let p = T::regs();
unsafe { p.ic_rxflr().read().rxflr() }
}
fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> {
let p = T::regs();
unsafe {
@ -240,7 +504,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> {
// NOTE(unsafe) We have &mut self
unsafe {
// wait until there is space in the FIFO to write the next byte
while p.ic_txflr().read().txflr() == FIFO_SIZE {}
while Self::tx_fifo_full() {}
p.ic_data_cmd().write(|w| {
w.set_restart(restart && first);
@ -249,7 +513,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> {
w.set_cmd(true);
});
while p.ic_rxflr().read().rxflr() == 0 {
while Self::rx_fifo_len() == 0 {
self.read_and_clear_abort_reason()?;
}
@ -379,7 +643,7 @@ mod eh1 {
type Error = Error;
}
impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::blocking::I2c for I2c<'d, T, M> {
impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> {
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_read(address, buffer)
}
@ -421,16 +685,14 @@ mod eh1 {
fn transaction<'a>(
&mut self,
address: u8,
operations: &mut [embedded_hal_1::i2c::blocking::Operation<'a>],
operations: &mut [embedded_hal_1::i2c::Operation<'a>],
) -> Result<(), Self::Error> {
Self::setup(address.into())?;
for i in 0..operations.len() {
let last = i == operations.len() - 1;
match &mut operations[i] {
embedded_hal_1::i2c::blocking::Operation::Read(buf) => {
self.read_blocking_internal(buf, false, last)?
}
embedded_hal_1::i2c::blocking::Operation::Write(buf) => self.write_blocking_internal(buf, last)?,
embedded_hal_1::i2c::Operation::Read(buf) => self.read_blocking_internal(buf, false, last)?,
embedded_hal_1::i2c::Operation::Write(buf) => self.write_blocking_internal(buf, last)?,
}
}
Ok(())
@ -438,23 +700,106 @@ mod eh1 {
fn transaction_iter<'a, O>(&mut self, address: u8, operations: O) -> Result<(), Self::Error>
where
O: IntoIterator<Item = embedded_hal_1::i2c::blocking::Operation<'a>>,
O: IntoIterator<Item = embedded_hal_1::i2c::Operation<'a>>,
{
Self::setup(address.into())?;
let mut peekable = operations.into_iter().peekable();
while let Some(operation) = peekable.next() {
let last = peekable.peek().is_none();
match operation {
embedded_hal_1::i2c::blocking::Operation::Read(buf) => {
self.read_blocking_internal(buf, false, last)?
}
embedded_hal_1::i2c::blocking::Operation::Write(buf) => self.write_blocking_internal(buf, last)?,
embedded_hal_1::i2c::Operation::Read(buf) => self.read_blocking_internal(buf, false, last)?,
embedded_hal_1::i2c::Operation::Write(buf) => self.write_blocking_internal(buf, last)?,
}
}
Ok(())
}
}
}
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
mod nightly {
use core::future::Future;
use embedded_hal_1::i2c::Operation;
use embedded_hal_async::i2c::AddressMode;
use super::*;
impl<'d, A, T> embedded_hal_async::i2c::I2c<A> for I2c<'d, T, Async>
where
A: AddressMode + Into<u16> + 'static,
T: Instance + 'd,
{
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
where Self: 'a;
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
where Self: 'a;
type WriteReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
where Self: 'a;
type TransactionFuture<'a, 'b> = impl Future<Output = Result<(), Error>> + 'a
where Self: 'a, 'b: 'a;
fn read<'a>(&'a mut self, address: A, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
let addr: u16 = address.into();
async move {
Self::setup(addr)?;
self.read_async_internal(buffer, false, true).await
}
}
fn write<'a>(&'a mut self, address: A, write: &'a [u8]) -> Self::WriteFuture<'a> {
let addr: u16 = address.into();
async move {
Self::setup(addr)?;
self.write_async_internal(write.iter().copied(), true).await
}
}
fn write_read<'a>(
&'a mut self,
address: A,
bytes: &'a [u8],
buffer: &'a mut [u8],
) -> Self::WriteReadFuture<'a> {
let addr: u16 = address.into();
async move {
Self::setup(addr)?;
self.write_async_internal(bytes.iter().cloned(), false).await?;
self.read_async_internal(buffer, false, true).await
}
}
fn transaction<'a, 'b>(
&'a mut self,
address: A,
operations: &'a mut [Operation<'b>],
) -> Self::TransactionFuture<'a, 'b> {
let addr: u16 = address.into();
async move {
let mut iterator = operations.iter_mut();
while let Some(op) = iterator.next() {
let last = iterator.len() == 0;
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?;
}
}
}
Ok(())
}
}
}
}
fn i2c_reserved_addr(addr: u16) -> bool {
(addr & 0x78) == 0 || (addr & 0x78) == 0x78
@ -462,6 +807,7 @@ fn i2c_reserved_addr(addr: u16) -> bool {
mod sealed {
use embassy_cortex_m::interrupt::Interrupt;
use embassy_sync::waitqueue::AtomicWaker;
pub trait Instance {
const TX_DREQ: u8;
@ -470,6 +816,8 @@ mod sealed {
type Interrupt: Interrupt;
fn regs() -> crate::pac::i2c::I2c;
fn reset() -> crate::pac::resets::regs::Peripherals;
fn waker() -> &'static AtomicWaker;
}
pub trait Mode {}
@ -496,23 +844,38 @@ impl_mode!(Async);
pub trait Instance: sealed::Instance {}
macro_rules! impl_instance {
($type:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => {
($type:ident, $irq:ident, $reset:ident, $tx_dreq:expr, $rx_dreq:expr) => {
impl sealed::Instance for peripherals::$type {
const TX_DREQ: u8 = $tx_dreq;
const RX_DREQ: u8 = $rx_dreq;
type Interrupt = crate::interrupt::$irq;
#[inline]
fn regs() -> pac::i2c::I2c {
pac::$type
}
#[inline]
fn reset() -> pac::resets::regs::Peripherals {
let mut ret = pac::resets::regs::Peripherals::default();
ret.$reset(true);
ret
}
#[inline]
fn waker() -> &'static AtomicWaker {
static WAKER: AtomicWaker = AtomicWaker::new();
&WAKER
}
}
impl Instance for peripherals::$type {}
};
}
impl_instance!(I2C0, I2C0_IRQ, 32, 33);
impl_instance!(I2C1, I2C1_IRQ, 34, 35);
impl_instance!(I2C0, I2C0_IRQ, set_i2c0, 32, 33);
impl_instance!(I2C1, I2C1_IRQ, set_i2c1, 34, 35);
pub trait SdaPin<T: Instance>: sealed::SdaPin<T> + crate::gpio::Pin {}
pub trait SclPin<T: Instance>: sealed::SclPin<T> + crate::gpio::Pin {}

View File

@ -145,6 +145,8 @@ impl<'d, T: Instance> RealTimeClock<'d, T> {
filter.write_setup_1(w);
});
self.inner.regs().inte().modify(|w| w.set_rtc(true));
// Set the enable bit and check if it is set
self.inner.regs().irq_setup_0().modify(|w| w.set_match_ena(true));
while !self.inner.regs().irq_setup_0().read().match_active() {

View File

@ -523,25 +523,25 @@ mod eh1 {
type Error = Error;
}
impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::blocking::SpiBusFlush for Spi<'d, T, M> {
impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBusFlush for Spi<'d, T, M> {
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::blocking::SpiBusRead<u8> for Spi<'d, T, M> {
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::blocking::SpiBusWrite<u8> for Spi<'d, T, M> {
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::blocking::SpiBus<u8> for Spi<'d, T, M> {
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)
}

View File

@ -486,7 +486,7 @@ mod eh1 {
type Error = Error;
}
impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Read for UartRx<'d, T, M> {
impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Read for UartRx<'d, T, M> {
fn read(&mut self) -> nb::Result<u8, Self::Error> {
let r = T::regs();
unsafe {
@ -509,7 +509,7 @@ mod eh1 {
}
}
impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::blocking::Write for UartTx<'d, T, M> {
impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::Write for UartTx<'d, T, M> {
fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(buffer)
}
@ -519,7 +519,7 @@ mod eh1 {
}
}
impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Write for UartTx<'d, T, M> {
impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, T, M> {
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
self.blocking_write(&[char]).map_err(nb::Error::Other)
}
@ -529,13 +529,13 @@ mod eh1 {
}
}
impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Read for Uart<'d, T, M> {
impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Read for Uart<'d, T, M> {
fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
embedded_hal_02::serial::Read::read(&mut self.rx)
}
}
impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::blocking::Write for Uart<'d, T, M> {
impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::Write for Uart<'d, T, M> {
fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(buffer)
}
@ -545,7 +545,7 @@ mod eh1 {
}
}
impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::nb::Write for Uart<'d, T, M> {
impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for Uart<'d, T, M> {
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
self.blocking_write(&[char]).map_err(nb::Error::Other)
}

View File

@ -522,7 +522,7 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> {
trace!("wait_enabled IN WAITING");
let index = self.info.addr.index();
poll_fn(|cx| {
EP_OUT_WAKERS[index].register(cx.waker());
EP_IN_WAKERS[index].register(cx.waker());
let val = unsafe { T::dpram().ep_in_control(self.info.addr.index() - 1).read() };
if val.enable() {
Poll::Ready(())
@ -811,8 +811,8 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
async move {
trace!("control: accept");
let bufcontrol = T::dpram().ep_in_buffer_control(0);
unsafe {
let bufcontrol = T::dpram().ep_in_buffer_control(0);
bufcontrol.write(|w| {
w.set_length(0, 0);
w.set_pid(0, true);
@ -826,6 +826,18 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
w.set_available(0, true);
});
}
// wait for completion before returning, needed so
// set_address() doesn't happen early.
poll_fn(|cx| {
EP_IN_WAKERS[0].register(cx.waker());
if unsafe { bufcontrol.read().available(0) } {
Poll::Pending
} else {
Poll::Ready(())
}
})
.await;
}
}

View File

@ -2,6 +2,7 @@
name = "embassy-stm32"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/"
@ -42,8 +43,9 @@ embassy-net = { version = "0.1.0", path = "../embassy-net", optional = true }
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true}
embedded-hal-async = { version = "0.1.0-alpha.1", optional = true}
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true}
embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true}
embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true}
embedded-storage = "0.3.0"
embedded-storage-async = { version = "0.3.0", optional = true }
@ -73,7 +75,7 @@ quote = "1.0.15"
stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]}
[features]
defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt"]
defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt"]
sdmmc-rs = ["embedded-sdmmc"]
net = ["embassy-net" ]
memory-x = ["stm32-metapac/memory-x"]
@ -102,7 +104,7 @@ unstable-pac = []
# Implement embedded-hal 1.0 alpha traits.
# Implement embedded-hal-async traits if `nightly` is set as well.
unstable-traits = ["embedded-hal-1"]
unstable-traits = ["embedded-hal-1", "dep:embedded-hal-nb"]
# BEGIN GENERATED FEATURES
# Generated by stm32-gen-features. DO NOT EDIT.

View File

@ -28,15 +28,20 @@ pub(crate) mod sealed {
pub trait AdcPin<T: Instance> {
fn channel(&self) -> u8;
}
pub trait InternalChannel<T> {
fn channel(&self) -> u8;
}
}
#[cfg(not(adc_f1))]
#[cfg(not(any(adc_f1, adc_v2)))]
pub trait Instance: sealed::Instance + 'static {}
#[cfg(adc_f1)]
#[cfg(any(adc_f1, adc_v2))]
pub trait Instance: sealed::Instance + crate::rcc::RccPeripheral + 'static {}
#[cfg(all(not(adc_f1), not(adc_v1)))]
pub trait Common: sealed::Common + 'static {}
pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {}
pub trait InternalChannel<T>: sealed::InternalChannel<T> {}
#[cfg(not(stm32h7))]
foreach_peripheral!(

View File

@ -3,7 +3,9 @@ use core::marker::PhantomData;
use embassy_hal_common::into_ref;
use embedded_hal_02::blocking::delay::DelayUs;
use super::InternalChannel;
use crate::adc::{AdcPin, Instance};
use crate::peripherals::ADC1;
use crate::time::Hertz;
use crate::Peripheral;
@ -12,20 +14,8 @@ pub const VREF_DEFAULT_MV: u32 = 3300;
/// VREF voltage used for factory calibration of VREFINTCAL register.
pub const VREF_CALIB_MV: u32 = 3300;
#[cfg(not(any(rcc_f4, rcc_f7)))]
fn enable() {
todo!()
}
#[cfg(any(rcc_f4, rcc_f7))]
fn enable() {
critical_section::with(|_| unsafe {
// TODO do not enable all adc clocks if not needed
crate::pac::RCC.apb2enr().modify(|w| w.set_adc1en(true));
crate::pac::RCC.apb2enr().modify(|w| w.set_adc2en(true));
crate::pac::RCC.apb2enr().modify(|w| w.set_adc3en(true));
});
}
/// ADC turn-on time
pub const ADC_POWERUP_TIME_US: u32 = 3;
pub enum Resolution {
TwelveBit,
@ -61,24 +51,53 @@ impl Resolution {
}
pub struct VrefInt;
impl<T: Instance> AdcPin<T> for VrefInt {}
impl<T: Instance> super::sealed::AdcPin<T> for VrefInt {
impl InternalChannel<ADC1> for VrefInt {}
impl super::sealed::InternalChannel<ADC1> for VrefInt {
fn channel(&self) -> u8 {
17
}
}
impl VrefInt {
/// Time needed for internal voltage reference to stabilize
pub fn start_time_us() -> u32 {
10
}
}
pub struct Temperature;
impl<T: Instance> AdcPin<T> for Temperature {}
impl<T: Instance> super::sealed::AdcPin<T> for Temperature {
impl InternalChannel<ADC1> for Temperature {}
impl super::sealed::InternalChannel<ADC1> for Temperature {
fn channel(&self) -> u8 {
16
cfg_if::cfg_if! {
if #[cfg(any(stm32f40, stm32f41))] {
16
} else {
18
}
}
}
}
impl Temperature {
/// Converts temperature sensor reading in millivolts to degrees celcius
pub fn to_celcius(sample_mv: u16) -> f32 {
// From 6.3.22 Temperature sensor characteristics
const V25: i32 = 760; // mV
const AVG_SLOPE: f32 = 2.5; // mV/C
(sample_mv as i32 - V25) as f32 / AVG_SLOPE + 25.0
}
/// Time needed for temperature sensor readings to stabilize
pub fn start_time_us() -> u32 {
10
}
}
pub struct Vbat;
impl<T: Instance> AdcPin<T> for Vbat {}
impl<T: Instance> super::sealed::AdcPin<T> for Vbat {
impl InternalChannel<ADC1> for Vbat {}
impl super::sealed::InternalChannel<ADC1> for Vbat {
fn channel(&self) -> u8 {
18
}
@ -164,21 +183,19 @@ where
{
pub fn new(_peri: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
into_ref!(_peri);
enable();
T::enable();
T::reset();
let presc = unsafe { Prescaler::from_pclk2(crate::rcc::get_freqs().apb2) };
let presc = Prescaler::from_pclk2(T::frequency());
unsafe {
T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre()));
}
unsafe {
// disable before config is set
T::regs().cr2().modify(|reg| {
reg.set_adon(crate::pac::adc::vals::Adon::DISABLED);
reg.set_adon(crate::pac::adc::vals::Adon::ENABLED);
});
}
delay.delay_us(20); // TODO?
delay.delay_us(ADC_POWERUP_TIME_US);
Self {
sample_time: Default::default(),
@ -208,6 +225,45 @@ where
((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16
}
/// Enables internal voltage reference and returns [VrefInt], which can be used in
/// [Adc::read_internal()] to perform conversion.
pub fn enable_vrefint(&self) -> VrefInt {
unsafe {
T::common_regs().ccr().modify(|reg| {
reg.set_tsvrefe(crate::pac::adccommon::vals::Tsvrefe::ENABLED);
});
}
VrefInt {}
}
/// Enables internal temperature sensor and returns [Temperature], which can be used in
/// [Adc::read_internal()] to perform conversion.
///
/// On STM32F42 and STM32F43 this can not be used together with [Vbat]. If both are enabled,
/// temperature sensor will return vbat value.
pub fn enable_temperature(&self) -> Temperature {
unsafe {
T::common_regs().ccr().modify(|reg| {
reg.set_tsvrefe(crate::pac::adccommon::vals::Tsvrefe::ENABLED);
});
}
Temperature {}
}
/// Enables vbat input and returns [Vbat], which can be used in
/// [Adc::read_internal()] to perform conversion.
pub fn enable_vbat(&self) -> Vbat {
unsafe {
T::common_regs().ccr().modify(|reg| {
reg.set_vbate(crate::pac::adccommon::vals::Vbate::ENABLED);
});
}
Vbat {}
}
/// Perform a single conversion.
fn convert(&mut self) -> u16 {
unsafe {
@ -238,44 +294,31 @@ where
P: crate::gpio::sealed::Pin,
{
unsafe {
// dissable ADC
T::regs().cr2().modify(|reg| {
reg.set_swstart(false);
});
T::regs().cr2().modify(|reg| {
reg.set_adon(crate::pac::adc::vals::Adon::DISABLED);
});
pin.set_as_analog();
// Configure ADC
T::regs().cr1().modify(|reg| reg.set_res(self.resolution.res()));
// Select channel
T::regs().sqr3().write(|reg| reg.set_sq(0, pin.channel()));
// Configure channel
Self::set_channel_sample_time(pin.channel(), self.sample_time);
// enable adc
T::regs().cr2().modify(|reg| {
reg.set_adon(crate::pac::adc::vals::Adon::ENABLED);
});
let val = self.convert();
// dissable ADC
T::regs().cr2().modify(|reg| {
reg.set_swstart(false);
});
T::regs().cr2().modify(|reg| {
reg.set_adon(crate::pac::adc::vals::Adon::DISABLED);
});
val
self.read_channel(pin.channel())
}
}
pub fn read_internal(&mut self, channel: &mut impl InternalChannel<T>) -> u16 {
unsafe { self.read_channel(channel.channel()) }
}
unsafe fn read_channel(&mut self, channel: u8) -> u16 {
// Configure ADC
T::regs().cr1().modify(|reg| reg.set_res(self.resolution.res()));
// Select channel
T::regs().sqr3().write(|reg| reg.set_sq(0, channel));
// Configure channel
Self::set_channel_sample_time(channel, self.sample_time);
let val = self.convert();
val
}
unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
if ch <= 9 {
T::regs()
@ -288,3 +331,9 @@ where
}
}
}
impl<'d, T: Instance> Drop for Adc<'d, T> {
fn drop(&mut self) {
T::disable();
}
}

View File

@ -5,7 +5,7 @@ use embedded_hal_02::blocking::delay::DelayUs;
use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel};
use pac::adccommon::vals::Presc;
use super::{AdcPin, Instance};
use super::{AdcPin, Instance, InternalChannel};
use crate::time::Hertz;
use crate::{pac, Peripheral};
@ -50,18 +50,10 @@ impl Resolution {
}
}
pub trait InternalChannel<T>: sealed::InternalChannel<T> {}
mod sealed {
pub trait InternalChannel<T> {
fn channel(&self) -> u8;
}
}
// NOTE: Vrefint/Temperature/Vbat are only available on ADC3 on H7, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs
pub struct VrefInt;
impl<T: Instance> InternalChannel<T> for VrefInt {}
impl<T: Instance> sealed::InternalChannel<T> for VrefInt {
impl<T: Instance> super::sealed::InternalChannel<T> for VrefInt {
fn channel(&self) -> u8 {
19
}
@ -69,7 +61,7 @@ impl<T: Instance> sealed::InternalChannel<T> for VrefInt {
pub struct Temperature;
impl<T: Instance> InternalChannel<T> for Temperature {}
impl<T: Instance> sealed::InternalChannel<T> for Temperature {
impl<T: Instance> super::sealed::InternalChannel<T> for Temperature {
fn channel(&self) -> u8 {
18
}
@ -77,7 +69,7 @@ impl<T: Instance> sealed::InternalChannel<T> for Temperature {
pub struct Vbat;
impl<T: Instance> InternalChannel<T> for Vbat {}
impl<T: Instance> sealed::InternalChannel<T> for Vbat {
impl<T: Instance> super::sealed::InternalChannel<T> for Vbat {
fn channel(&self) -> u8 {
// TODO this should be 14 for H7a/b/35
17

View File

@ -155,7 +155,7 @@ mod eh1 {
type Error = Infallible;
}
impl<'d, T: GpioPin> embedded_hal_1::digital::blocking::InputPin for ExtiInput<'d, T> {
impl<'d, T: GpioPin> embedded_hal_1::digital::InputPin for ExtiInput<'d, T> {
fn is_high(&self) -> Result<bool, Self::Error> {
Ok(self.is_high())
}

View File

@ -39,6 +39,10 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error
w.set_psize(2); // 32 bits at once
});
cortex_m::asm::isb();
cortex_m::asm::dsb();
atomic_polyfill::fence(atomic_polyfill::Ordering::SeqCst);
let ret = {
let mut ret: Result<(), Error> = Ok(());
let mut offset = offset;
@ -64,6 +68,10 @@ pub(crate) unsafe fn blocking_write(offset: u32, buf: &[u8]) -> Result<(), Error
bank.cr().write(|w| w.set_pg(false));
cortex_m::asm::isb();
cortex_m::asm::dsb();
atomic_polyfill::fence(atomic_polyfill::Ordering::SeqCst);
ret
}

View File

@ -23,17 +23,6 @@ impl<'d> Flash<'d> {
Self { _inner: p }
}
pub fn unlock(p: impl Peripheral<P = FLASH> + 'd) -> Self {
let flash = Self::new(p);
unsafe { family::unlock() };
flash
}
pub fn lock(&mut self) {
unsafe { family::lock() };
}
pub fn blocking_read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
let offset = FLASH_BASE as u32 + offset;
if offset as usize >= FLASH_END || offset as usize + bytes.len() > FLASH_END {
@ -57,7 +46,12 @@ impl<'d> Flash<'d> {
self.clear_all_err();
unsafe { family::blocking_write(offset, buf) }
unsafe {
family::unlock();
let res = family::blocking_write(offset, buf);
family::lock();
res
}
}
pub fn blocking_erase(&mut self, from: u32, to: u32) -> Result<(), Error> {
@ -72,7 +66,12 @@ impl<'d> Flash<'d> {
self.clear_all_err();
unsafe { family::blocking_erase(from, to) }
unsafe {
family::unlock();
let res = family::blocking_erase(from, to);
family::lock();
res
}
}
fn clear_all_err(&mut self) {
@ -82,7 +81,7 @@ impl<'d> Flash<'d> {
impl Drop for Flash<'_> {
fn drop(&mut self) {
self.lock();
unsafe { family::lock() };
}
}

View File

@ -848,8 +848,7 @@ mod eh02 {
#[cfg(feature = "unstable-traits")]
mod eh1 {
use embedded_hal_1::digital::blocking::{InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin};
use embedded_hal_1::digital::ErrorType;
use embedded_hal_1::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin};
use super::*;

View File

@ -334,7 +334,7 @@ mod eh1 {
type Error = Error;
}
impl<'d, T: Instance> embedded_hal_1::i2c::blocking::I2c for I2c<'d, T> {
impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T> {
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_read(address, buffer)
}
@ -364,14 +364,14 @@ mod eh1 {
fn transaction<'a>(
&mut self,
_address: u8,
_operations: &mut [embedded_hal_1::i2c::blocking::Operation<'a>],
_operations: &mut [embedded_hal_1::i2c::Operation<'a>],
) -> Result<(), Self::Error> {
todo!();
}
fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error>
where
O: IntoIterator<Item = embedded_hal_1::i2c::blocking::Operation<'a>>,
O: IntoIterator<Item = embedded_hal_1::i2c::Operation<'a>>,
{
todo!();
}

View File

@ -883,7 +883,7 @@ mod eh1 {
type Error = Error;
}
impl<'d, T: Instance> embedded_hal_1::i2c::blocking::I2c for I2c<'d, T, NoDma, NoDma> {
impl<'d, T: Instance> embedded_hal_1::i2c::I2c for I2c<'d, T, NoDma, NoDma> {
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
self.blocking_read(address, buffer)
}
@ -913,14 +913,14 @@ mod eh1 {
fn transaction<'a>(
&mut self,
_address: u8,
_operations: &mut [embedded_hal_1::i2c::blocking::Operation<'a>],
_operations: &mut [embedded_hal_1::i2c::Operation<'a>],
) -> Result<(), Self::Error> {
todo!();
}
fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error>
where
O: IntoIterator<Item = embedded_hal_1::i2c::blocking::Operation<'a>>,
O: IntoIterator<Item = embedded_hal_1::i2c::Operation<'a>>,
{
todo!();
}

View File

@ -843,25 +843,25 @@ mod eh1 {
type Error = Error;
}
impl<'d, T: Instance, Tx, Rx> embedded_hal_1::spi::blocking::SpiBusFlush for Spi<'d, T, Tx, Rx> {
impl<'d, T: Instance, Tx, Rx> embedded_hal_1::spi::SpiBusFlush for Spi<'d, T, Tx, Rx> {
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl<'d, T: Instance, W: Word> embedded_hal_1::spi::blocking::SpiBusRead<W> for Spi<'d, T, NoDma, NoDma> {
impl<'d, T: Instance, W: Word> embedded_hal_1::spi::SpiBusRead<W> for Spi<'d, T, NoDma, NoDma> {
fn read(&mut self, words: &mut [W]) -> Result<(), Self::Error> {
self.blocking_read(words)
}
}
impl<'d, T: Instance, W: Word> embedded_hal_1::spi::blocking::SpiBusWrite<W> for Spi<'d, T, NoDma, NoDma> {
impl<'d, T: Instance, W: Word> embedded_hal_1::spi::SpiBusWrite<W> for Spi<'d, T, NoDma, NoDma> {
fn write(&mut self, words: &[W]) -> Result<(), Self::Error> {
self.blocking_write(words)
}
}
impl<'d, T: Instance, W: Word> embedded_hal_1::spi::blocking::SpiBus<W> for Spi<'d, T, NoDma, NoDma> {
impl<'d, T: Instance, W: Word> embedded_hal_1::spi::SpiBus<W> for Spi<'d, T, NoDma, NoDma> {
fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Self::Error> {
self.blocking_transfer(read, write)
}

View File

@ -160,6 +160,30 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
Ok(())
}
pub fn nb_read(&mut self) -> Result<u8, nb::Error<Error>> {
let r = T::regs();
unsafe {
let sr = sr(r).read();
if sr.pe() {
rdr(r).read_volatile();
Err(nb::Error::Other(Error::Parity))
} else if sr.fe() {
rdr(r).read_volatile();
Err(nb::Error::Other(Error::Framing))
} else if sr.ne() {
rdr(r).read_volatile();
Err(nb::Error::Other(Error::Noise))
} else if sr.ore() {
rdr(r).read_volatile();
Err(nb::Error::Other(Error::Overrun))
} else if sr.rxne() {
Ok(rdr(r).read_volatile())
} else {
Err(nb::Error::WouldBlock)
}
}
}
pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
unsafe {
let r = T::regs();
@ -263,6 +287,10 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
self.rx.read(buffer).await
}
pub fn nb_read(&mut self) -> Result<u8, nb::Error<Error>> {
self.rx.nb_read()
}
pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
self.rx.blocking_read(buffer)
}
@ -281,27 +309,7 @@ mod eh02 {
impl<'d, T: BasicInstance, RxDma> embedded_hal_02::serial::Read<u8> for UartRx<'d, T, RxDma> {
type Error = Error;
fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
let r = T::regs();
unsafe {
let sr = sr(r).read();
if sr.pe() {
rdr(r).read_volatile();
Err(nb::Error::Other(Error::Parity))
} else if sr.fe() {
rdr(r).read_volatile();
Err(nb::Error::Other(Error::Framing))
} else if sr.ne() {
rdr(r).read_volatile();
Err(nb::Error::Other(Error::Noise))
} else if sr.ore() {
rdr(r).read_volatile();
Err(nb::Error::Other(Error::Overrun))
} else if sr.rxne() {
Ok(rdr(r).read_volatile())
} else {
Err(nb::Error::WouldBlock)
}
}
self.nb_read()
}
}
@ -318,7 +326,7 @@ mod eh02 {
impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_02::serial::Read<u8> for Uart<'d, T, TxDma, RxDma> {
type Error = Error;
fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
embedded_hal_02::serial::Read::read(&mut self.rx)
self.nb_read()
}
}
@ -359,6 +367,58 @@ mod eh1 {
impl<'d, T: BasicInstance, RxDma> embedded_hal_1::serial::ErrorType for UartRx<'d, T, RxDma> {
type Error = Error;
}
impl<'d, T: BasicInstance, RxDma> embedded_hal_nb::serial::Read for UartRx<'d, T, RxDma> {
fn read(&mut self) -> nb::Result<u8, Self::Error> {
self.nb_read()
}
}
impl<'d, T: BasicInstance, TxDma> embedded_hal_1::serial::Write for UartTx<'d, T, TxDma> {
fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(buffer)
}
fn flush(&mut self) -> Result<(), Self::Error> {
self.blocking_flush()
}
}
impl<'d, T: BasicInstance, TxDma> embedded_hal_nb::serial::Write for UartTx<'d, T, TxDma> {
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
self.blocking_write(&[char]).map_err(nb::Error::Other)
}
fn flush(&mut self) -> nb::Result<(), Self::Error> {
self.blocking_flush().map_err(nb::Error::Other)
}
}
impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_nb::serial::Read for Uart<'d, T, TxDma, RxDma> {
fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
self.nb_read()
}
}
impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_1::serial::Write for Uart<'d, T, TxDma, RxDma> {
fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
self.blocking_write(buffer)
}
fn flush(&mut self) -> Result<(), Self::Error> {
self.blocking_flush()
}
}
impl<'d, T: BasicInstance, TxDma, RxDma> embedded_hal_nb::serial::Write for Uart<'d, T, TxDma, RxDma> {
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
self.blocking_write(&[char]).map_err(nb::Error::Other)
}
fn flush(&mut self) -> nb::Result<(), Self::Error> {
self.blocking_flush().map_err(nb::Error::Other)
}
}
}
#[cfg(all(

View File

@ -2,6 +2,16 @@
name = "embassy-sync"
version = "0.1.0"
edition = "2021"
description = "no-std, no-alloc synchronization primitives with async support"
repository = "https://github.com/embassy-rs/embassy"
readme = "README.md"
license = "MIT OR Apache-2.0"
categories = [
"embedded",
"no-std",
"concurrency",
"asynchronous",
]
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-sync-v$VERSION/embassy-sync/src/"

View File

@ -1,12 +1,32 @@
# embassy-sync
Synchronization primitives and data structures with an async API:
An [Embassy](https://embassy.dev) project.
Synchronization primitives and data structures with async support:
- [`Channel`](channel::Channel) - A Multiple Producer Multiple Consumer (MPMC) channel. Each message is only received by a single consumer.
- [`PubSubChannel`](pubsub::PubSubChannel) - A broadcast channel (publish-subscribe) channel. Each message is received by all consumers.
- [`Signal`](signal::Signal) - Signalling latest value to a single consumer.
- [`Mutex`](mutex::Mutex) - A Mutex for synchronizing state between asynchronous tasks.
- [`Mutex`](mutex::Mutex) - Mutex for synchronizing state between asynchronous tasks.
- [`Pipe`](pipe::Pipe) - Byte stream implementing `embedded_io` traits.
- [`WakerRegistration`](waitqueue::WakerRegistration) - Utility to register and wake a `Waker`.
- [`AtomicWaker`](waitqueue::AtomicWaker) - A variant of `WakerRegistration` accessible using a non-mut API.
- [`MultiWakerRegistration`](waitqueue::MultiWakerRegistration) - Utility registering and waking multiple `Waker`'s.
## Interoperability
Futures from this crate can run on any executor.
## Minimum supported Rust version (MSRV)
Embassy is guaranteed to compile on the latest stable Rust version at the time of release. It might compile with older versions but that may change in any new patch release.
## License
This work is licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
<http://www.apache.org/licenses/LICENSE-2.0>)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
at your option.

View File

@ -192,6 +192,10 @@ impl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usi
})
}
fn available(&self, next_message_id: u64) -> u64 {
self.inner.lock(|s| s.borrow().next_message_id - next_message_id)
}
fn publish_with_context(&self, message: T, cx: Option<&mut Context<'_>>) -> Result<(), T> {
self.inner.lock(|s| {
let mut s = s.borrow_mut();
@ -217,6 +221,13 @@ impl<M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: usi
})
}
fn space(&self) -> usize {
self.inner.lock(|s| {
let s = s.borrow();
s.queue.capacity() - s.queue.len()
})
}
fn unregister_subscriber(&self, subscriber_next_message_id: u64) {
self.inner.lock(|s| {
let mut s = s.borrow_mut();
@ -388,6 +399,10 @@ pub trait PubSubBehavior<T> {
/// If the message is not yet present and a context is given, then its waker is registered in the subsriber wakers.
fn get_message_with_context(&self, next_message_id: &mut u64, cx: Option<&mut Context<'_>>) -> Poll<WaitResult<T>>;
/// Get the amount of messages that are between the given the next_message_id and the most recent message.
/// This is not necessarily the amount of messages a subscriber can still received as it may have lagged.
fn available(&self, next_message_id: u64) -> u64;
/// Try to publish a message to the queue.
///
/// If the queue is full and a context is given, then its waker is registered in the publisher wakers.
@ -396,6 +411,9 @@ pub trait PubSubBehavior<T> {
/// Publish a message immediately
fn publish_immediate(&self, message: T);
/// The amount of messages that can still be published without having to wait or without having to lag the subscribers
fn space(&self) -> usize;
/// Let the channel know that a subscriber has dropped
fn unregister_subscriber(&self, subscriber_next_message_id: u64);
@ -539,4 +557,59 @@ mod tests {
drop(sub0);
}
#[futures_test::test]
async fn correct_available() {
let channel = PubSubChannel::<NoopRawMutex, u32, 4, 4, 4>::new();
let sub0 = channel.subscriber().unwrap();
let mut sub1 = channel.subscriber().unwrap();
let pub0 = channel.publisher().unwrap();
assert_eq!(sub0.available(), 0);
assert_eq!(sub1.available(), 0);
pub0.publish(42).await;
assert_eq!(sub0.available(), 1);
assert_eq!(sub1.available(), 1);
sub1.next_message().await;
assert_eq!(sub1.available(), 0);
pub0.publish(42).await;
assert_eq!(sub0.available(), 2);
assert_eq!(sub1.available(), 1);
}
#[futures_test::test]
async fn correct_space() {
let channel = PubSubChannel::<NoopRawMutex, u32, 4, 4, 4>::new();
let mut sub0 = channel.subscriber().unwrap();
let mut sub1 = channel.subscriber().unwrap();
let pub0 = channel.publisher().unwrap();
assert_eq!(pub0.space(), 4);
pub0.publish(42).await;
assert_eq!(pub0.space(), 3);
pub0.publish(42).await;
assert_eq!(pub0.space(), 2);
sub0.next_message().await;
sub0.next_message().await;
assert_eq!(pub0.space(), 2);
sub1.next_message().await;
assert_eq!(pub0.space(), 3);
sub1.next_message().await;
assert_eq!(pub0.space(), 4);
}
}

View File

@ -42,6 +42,14 @@ impl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> Pub<'a, PSB, T> {
pub fn try_publish(&self, message: T) -> Result<(), T> {
self.channel.publish_with_context(message, None)
}
/// The amount of messages that can still be published without having to wait or without having to lag the subscribers
///
/// *Note: In the time between checking this and a publish action, other publishers may have had time to publish something.
/// So checking doesn't give any guarantees.*
pub fn space(&self) -> usize {
self.channel.space()
}
}
impl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> Drop for Pub<'a, PSB, T> {
@ -115,6 +123,14 @@ impl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> ImmediatePub<'a, PSB, T> {
pub fn try_publish(&self, message: T) -> Result<(), T> {
self.channel.publish_with_context(message, None)
}
/// The amount of messages that can still be published without having to wait or without having to lag the subscribers
///
/// *Note: In the time between checking this and a publish action, other publishers may have had time to publish something.
/// So checking doesn't give any guarantees.*
pub fn space(&self) -> usize {
self.channel.space()
}
}
/// An immediate publisher that holds a dynamic reference to the channel
@ -158,6 +174,7 @@ impl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS:
}
/// Future for the publisher wait action
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct PublisherWaitFuture<'s, 'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> {
/// The message we need to publish
message: Option<T>,

View File

@ -64,6 +64,11 @@ impl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> Sub<'a, PSB, T> {
}
}
}
/// The amount of messages this subscriber hasn't received yet
pub fn available(&self) -> u64 {
self.channel.available(self.next_message_id)
}
}
impl<'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> Drop for Sub<'a, PSB, T> {
@ -135,6 +140,7 @@ impl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS:
}
/// Future for the subscriber wait action
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct SubscriberWaitFuture<'s, 'a, PSB: PubSubBehavior<T> + ?Sized, T: Clone> {
subscriber: &'s mut Sub<'a, PSB, T>,
}

View File

@ -2,6 +2,7 @@
name = "embassy-time"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[package.metadata.embassy_docs]
@ -105,8 +106,8 @@ defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" }
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true}
embedded-hal-async = { version = "0.1.0-alpha.1", optional = true}
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true}
embedded-hal-async = { version = "=0.1.0-alpha.2", optional = true}
futures-util = { version = "0.3.17", default-features = false }
embassy-macros = { version = "0.1.0", path = "../embassy-macros"}
@ -117,4 +118,4 @@ cfg-if = "1.0.0"
# WASM dependencies
wasm-bindgen = { version = "0.2.81", optional = true }
js-sys = { version = "0.3", optional = true }
wasm-timer = { version = "0.2.5", optional = true }
wasm-timer = { version = "0.2.5", optional = true }

View File

@ -18,7 +18,7 @@ pub struct Delay;
mod eh1 {
use super::*;
impl embedded_hal_1::delay::blocking::DelayUs for Delay {
impl embedded_hal_1::delay::DelayUs for Delay {
type Error = core::convert::Infallible;
fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> {

View File

@ -2,6 +2,7 @@
name = "embassy-usb-driver"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -13,4 +14,4 @@ target = "thumbv7em-none-eabi"
[dependencies]
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
log = { version = "0.4.14", optional = true }

View File

@ -54,12 +54,16 @@ impl From<EndpointAddress> for u8 {
}
impl EndpointAddress {
const INBITS: u8 = Direction::In as u8;
const INBITS: u8 = 0x80;
/// Constructs a new EndpointAddress with the given index and direction.
#[inline]
pub fn from_parts(index: usize, dir: Direction) -> Self {
EndpointAddress(index as u8 | dir as u8)
let dir_u8 = match dir {
Direction::Out => 0x00,
Direction::In => Self::INBITS,
};
EndpointAddress(index as u8 | dir_u8)
}
/// Gets the direction part of the address.

View File

@ -2,11 +2,12 @@
name = "embassy-usb"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-v$VERSION/embassy-usb/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb/src/"
features = ["defmt"]
features = ["defmt", "usbd-hid"]
target = "thumbv7em-none-eabi"
[features]

View File

@ -2,6 +2,7 @@
edition = "2021"
name = "embassy-boot-nrf-examples"
version = "0.1.0"
license = "MIT OR Apache-2.0"
[dependencies]
embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" }

View File

@ -2,6 +2,7 @@
edition = "2021"
name = "embassy-boot-stm32f3-examples"
version = "0.1.0"
license = "MIT OR Apache-2.0"
[dependencies]
embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }

View File

@ -17,7 +17,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin");
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
let flash = Flash::unlock(p.FLASH);
let flash = Flash::new(p.FLASH);
let mut flash = BlockingAsync::new(flash);
let button = Input::new(p.PC13, Pull::Up);

View File

@ -2,6 +2,7 @@
edition = "2021"
name = "embassy-boot-stm32f7-examples"
version = "0.1.0"
license = "MIT OR Apache-2.0"
[dependencies]
embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }

View File

@ -16,7 +16,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin");
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
let mut flash = Flash::unlock(p.FLASH);
let mut flash = Flash::new(p.FLASH);
let button = Input::new(p.PC13, Pull::Down);
let mut button = ExtiInput::new(button, p.EXTI13);

View File

@ -2,6 +2,7 @@
edition = "2021"
name = "embassy-boot-stm32h7-examples"
version = "0.1.0"
license = "MIT OR Apache-2.0"
[dependencies]
embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync" }

View File

@ -16,7 +16,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin");
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
let mut flash = Flash::unlock(p.FLASH);
let mut flash = Flash::new(p.FLASH);
let button = Input::new(p.PC13, Pull::Down);
let mut button = ExtiInput::new(button, p.EXTI13);

View File

@ -2,6 +2,7 @@
edition = "2021"
name = "embassy-boot-stm32l0-examples"
version = "0.1.0"
license = "MIT OR Apache-2.0"
[dependencies]
embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }

View File

@ -18,7 +18,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin");
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
let flash = Flash::unlock(p.FLASH);
let flash = Flash::new(p.FLASH);
let mut flash = BlockingAsync::new(flash);
let button = Input::new(p.PB2, Pull::Up);

View File

@ -2,6 +2,7 @@
edition = "2021"
name = "embassy-boot-stm32l1-examples"
version = "0.1.0"
license = "MIT OR Apache-2.0"
[dependencies]
embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }

View File

@ -18,7 +18,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin");
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
let flash = Flash::unlock(p.FLASH);
let flash = Flash::new(p.FLASH);
let mut flash = BlockingAsync::new(flash);
let button = Input::new(p.PB2, Pull::Up);

View File

@ -2,6 +2,7 @@
edition = "2021"
name = "embassy-boot-stm32l4-examples"
version = "0.1.0"
license = "MIT OR Apache-2.0"
[dependencies]
embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }

View File

@ -17,7 +17,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin");
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
let flash = Flash::unlock(p.FLASH);
let flash = Flash::new(p.FLASH);
let mut flash = BlockingAsync::new(flash);
let button = Input::new(p.PC13, Pull::Up);

View File

@ -2,6 +2,7 @@
edition = "2021"
name = "embassy-boot-stm32wl-examples"
version = "0.1.0"
license = "MIT OR Apache-2.0"
[dependencies]
embassy-sync = { version = "0.1.0", path = "../../../../embassy-sync", features = ["defmt"] }

View File

@ -17,7 +17,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin");
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
let flash = Flash::unlock(p.FLASH);
let flash = Flash::new(p.FLASH);
let mut flash = BlockingAsync::new(flash);
let button = Input::new(p.PA0, Pull::Up);

View File

@ -3,6 +3,7 @@ edition = "2021"
name = "nrf-bootloader-example"
version = "0.1.0"
description = "Bootloader for nRF chips"
license = "MIT OR Apache-2.0"
[dependencies]
defmt = { version = "0.3", optional = true }

View File

@ -3,6 +3,7 @@ edition = "2021"
name = "stm32-bootloader-example"
version = "0.1.0"
description = "Example bootloader for STM32 chips"
license = "MIT OR Apache-2.0"
[dependencies]
defmt = { version = "0.3", optional = true }

View File

@ -20,7 +20,7 @@ fn main() -> ! {
*/
let mut bl: BootLoader<ERASE_SIZE, WRITE_SIZE> = BootLoader::default();
let flash = Flash::unlock(p.FLASH);
let flash = Flash::new(p.FLASH);
let mut flash = BootFlash::<_, ERASE_SIZE, ERASE_VALUE>::new(flash);
let start = bl.prepare(&mut SingleFlashConfig::new(&mut flash));
core::mem::drop(flash);

View File

@ -2,6 +2,7 @@
edition = "2021"
name = "embassy-nrf-rtos-trace-examples"
version = "0.1.0"
license = "MIT OR Apache-2.0"
[features]
default = ["log", "nightly"]

View File

@ -2,10 +2,12 @@
edition = "2021"
name = "embassy-nrf-examples"
version = "0.1.0"
license = "MIT OR Apache-2.0"
[features]
default = ["nightly"]
nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net"]
nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net",
"embassy-lora", "lorawan-device", "lorawan"]
[dependencies]
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
@ -16,6 +18,10 @@ embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defm
embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"], optional = true }
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true }
embedded-io = "0.3.0"
embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true }
lorawan-device = { version = "0.8.0", default-features = false, features = ["async"], optional = true }
lorawan = { version = "0.7.1", default-features = false, features = ["default-crypto"], optional = true }
defmt = "0.3"
defmt-rtt = "0.3"

View File

@ -0,0 +1,78 @@
//! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio.
//! Other nrf/sx126x combinations may work with appropriate pin modifications.
//! It demonstates LORA P2P functionality in conjunction with example lora_p2p_sense.rs.
#![no_std]
#![no_main]
#![macro_use]
#![allow(dead_code)]
#![feature(type_alias_impl_trait)]
use defmt::*;
use embassy_executor::Spawner;
use embassy_lora::sx126x::*;
use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull};
use embassy_nrf::{interrupt, spim};
use embassy_time::{Duration, Timer};
use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor};
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_nrf::init(Default::default());
let mut spi_config = spim::Config::default();
spi_config.frequency = spim::Frequency::M16;
let mut radio = {
let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config);
let cs = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard);
let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard);
let dio1 = Input::new(p.P1_15.degrade(), Pull::Down);
let busy = Input::new(p.P1_14.degrade(), Pull::Down);
let antenna_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard);
let antenna_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard);
match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await {
Ok(r) => r,
Err(err) => {
info!("Sx126xRadio error = {}", err);
return;
}
}
};
let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard);
let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard);
start_indicator.set_high();
Timer::after(Duration::from_secs(5)).await;
start_indicator.set_low();
loop {
let rf_config = RfConfig {
frequency: 903900000, // channel in Hz
bandwidth: Bandwidth::_250KHz,
spreading_factor: SpreadingFactor::_10,
coding_rate: CodingRate::_4_8,
};
let mut buffer = [00u8; 100];
// P2P receive
match radio.rx(rf_config, &mut buffer).await {
Ok((buffer_len, rx_quality)) => info!(
"RX received = {:?} with length = {} rssi = {} snr = {}",
&buffer[0..buffer_len],
buffer_len,
rx_quality.rssi(),
rx_quality.snr()
),
Err(err) => info!("RX error = {}", err),
}
debug_indicator.set_high();
Timer::after(Duration::from_secs(2)).await;
debug_indicator.set_low();
}
}

View File

@ -0,0 +1,125 @@
//! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio.
//! Other nrf/sx126x combinations may work with appropriate pin modifications.
//! It demonstates LORA P2P functionality in conjunction with example lora_p2p_report.rs.
#![no_std]
#![no_main]
#![macro_use]
#![feature(type_alias_impl_trait)]
#![feature(alloc_error_handler)]
#![allow(incomplete_features)]
use defmt::*;
use embassy_executor::Spawner;
use embassy_lora::sx126x::*;
use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull};
use embassy_nrf::{interrupt, spim};
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::pubsub::{PubSubChannel, Publisher};
use embassy_time::{Duration, Timer};
use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor, TxConfig};
use {defmt_rtt as _, panic_probe as _, panic_probe as _};
// Message bus: queue of 2, 1 subscriber (Lora P2P), 2 publishers (temperature, motion detection)
static MESSAGE_BUS: PubSubChannel<CriticalSectionRawMutex, Message, 2, 1, 2> = PubSubChannel::new();
#[derive(Clone, defmt::Format)]
enum Message {
Temperature(i32),
MotionDetected,
}
#[embassy_executor::task]
async fn temperature_task(publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>) {
// Publish a fake temperature every 43 seconds, minimizing LORA traffic.
loop {
Timer::after(Duration::from_secs(43)).await;
publisher.publish(Message::Temperature(9)).await;
}
}
#[embassy_executor::task]
async fn motion_detection_task(publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>) {
// Publish a fake motion detection every 79 seconds, minimizing LORA traffic.
loop {
Timer::after(Duration::from_secs(79)).await;
publisher.publish(Message::MotionDetected).await;
}
}
#[embassy_executor::main]
async fn main(spawner: Spawner) {
let p = embassy_nrf::init(Default::default());
// set up to funnel temperature and motion detection events to the Lora Tx task
let mut lora_tx_subscriber = unwrap!(MESSAGE_BUS.subscriber());
let temperature_publisher = unwrap!(MESSAGE_BUS.publisher());
let motion_detection_publisher = unwrap!(MESSAGE_BUS.publisher());
let mut spi_config = spim::Config::default();
spi_config.frequency = spim::Frequency::M16;
let mut radio = {
let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1);
let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config);
let cs = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard);
let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard);
let dio1 = Input::new(p.P1_15.degrade(), Pull::Down);
let busy = Input::new(p.P1_14.degrade(), Pull::Down);
let antenna_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard);
let antenna_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard);
match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await {
Ok(r) => r,
Err(err) => {
info!("Sx126xRadio error = {}", err);
return;
}
}
};
let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard);
start_indicator.set_high();
Timer::after(Duration::from_secs(5)).await;
start_indicator.set_low();
match radio.lora.sleep().await {
Ok(()) => info!("Sleep successful"),
Err(err) => info!("Sleep unsuccessful = {}", err),
}
unwrap!(spawner.spawn(temperature_task(temperature_publisher)));
unwrap!(spawner.spawn(motion_detection_task(motion_detection_publisher)));
loop {
let message = lora_tx_subscriber.next_message_pure().await;
let tx_config = TxConfig {
// 11 byte maximum payload for Bandwidth 125 and SF 10
pw: 10, // up to 20
rf: RfConfig {
frequency: 903900000, // channel in Hz, not MHz
bandwidth: Bandwidth::_250KHz,
spreading_factor: SpreadingFactor::_10,
coding_rate: CodingRate::_4_8,
},
};
let mut buffer = [0x00u8];
match message {
Message::Temperature(temperature) => buffer[0] = temperature as u8,
Message::MotionDetected => buffer[0] = 0x01u8,
};
// unencrypted
match radio.tx(tx_config, &buffer).await {
Ok(ret_val) => info!("TX ret_val = {}", ret_val),
Err(err) => info!("TX error = {}", err),
}
match radio.lora.sleep().await {
Ok(()) => info!("Sleep successful"),
Err(err) => info!("Sleep unsuccessful = {}", err),
}
}
}

View File

@ -0,0 +1,33 @@
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
use defmt::info;
use embassy_executor::Spawner;
use embassy_nrf::interrupt;
use embassy_nrf::pdm::{Config, Pdm};
use embassy_time::{Duration, Timer};
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main]
async fn main(_p: Spawner) {
let p = embassy_nrf::init(Default::default());
let config = Config::default();
let mut pdm = Pdm::new(p.PDM, interrupt::take!(PDM), p.P0_01, p.P0_00, config);
loop {
pdm.start().await;
// wait some time till the microphon settled
Timer::after(Duration::from_millis(1000)).await;
const SAMPLES: usize = 2048;
let mut buf = [0i16; SAMPLES];
pdm.sample(&mut buf).await.unwrap();
info!("samples: {:?}", &buf);
pdm.stop().await;
Timer::after(Duration::from_millis(100)).await;
}
}

View File

@ -15,7 +15,8 @@ async fn main(_spawner: Spawner) {
config.baudrate = uarte::Baudrate::BAUD115200;
let irq = interrupt::take!(UARTE0_UART0);
let mut uart = uarte::UarteWithIdle::new(p.UARTE0, p.TIMER0, p.PPI_CH0, p.PPI_CH1, irq, p.P0_08, p.P0_06, config);
let uart = uarte::Uarte::new(p.UARTE0, irq, p.P0_08, p.P0_06, config);
let (mut tx, mut rx) = uart.split_with_idle(p.TIMER0, p.PPI_CH0, p.PPI_CH1);
info!("uarte initialized!");
@ -23,12 +24,12 @@ async fn main(_spawner: Spawner) {
let mut buf = [0; 8];
buf.copy_from_slice(b"Hello!\r\n");
unwrap!(uart.write(&buf).await);
unwrap!(tx.write(&buf).await);
info!("wrote hello in uart!");
loop {
info!("reading...");
let n = unwrap!(uart.read_until_idle(&mut buf).await);
let n = unwrap!(rx.read_until_idle(&mut buf).await);
info!("got {} bytes", n);
}
}

View File

@ -2,6 +2,7 @@
edition = "2021"
name = "embassy-rp-examples"
version = "0.1.0"
license = "MIT OR Apache-2.0"
[dependencies]
@ -26,7 +27,10 @@ st7789 = "0.6.1"
display-interface = "0.4.1"
byte-slice-cast = { version = "1.2.0", default-features = false }
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" }
embedded-hal-async = { version = "0.1.0-alpha.1" }
embedded-io = { version = "0.3.0", features = ["async", "defmt"] }
static_cell = "1.0.0"
[profile.release]
debug = true

View File

@ -0,0 +1,102 @@
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
use defmt::*;
use embassy_executor::Spawner;
use embassy_rp::i2c::{self, Config};
use embassy_rp::interrupt;
use embassy_time::{Duration, Timer};
use embedded_hal_async::i2c::I2c;
use {defmt_rtt as _, panic_probe as _};
#[allow(dead_code)]
mod mcp23017 {
pub const ADDR: u8 = 0x20; // default addr
macro_rules! mcpregs {
($($name:ident : $val:expr),* $(,)?) => {
$(
pub const $name: u8 = $val;
)*
pub fn regname(reg: u8) -> &'static str {
match reg {
$(
$val => stringify!($name),
)*
_ => panic!("bad reg"),
}
}
}
}
// These are correct for IOCON.BANK=0
mcpregs! {
IODIRA: 0x00,
IPOLA: 0x02,
GPINTENA: 0x04,
DEFVALA: 0x06,
INTCONA: 0x08,
IOCONA: 0x0A,
GPPUA: 0x0C,
INTFA: 0x0E,
INTCAPA: 0x10,
GPIOA: 0x12,
OLATA: 0x14,
IODIRB: 0x01,
IPOLB: 0x03,
GPINTENB: 0x05,
DEFVALB: 0x07,
INTCONB: 0x09,
IOCONB: 0x0B,
GPPUB: 0x0D,
INTFB: 0x0F,
INTCAPB: 0x11,
GPIOB: 0x13,
OLATB: 0x15,
}
}
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_rp::init(Default::default());
let sda = p.PIN_14;
let scl = p.PIN_15;
let irq = interrupt::take!(I2C1_IRQ);
info!("set up i2c ");
let mut i2c = i2c::I2c::new_async(p.I2C1, scl, sda, irq, Config::default());
use mcp23017::*;
info!("init mcp23017 config for IxpandO");
// init - a outputs, b inputs
i2c.write(ADDR, &[IODIRA, 0x00]).await.unwrap();
i2c.write(ADDR, &[IODIRB, 0xff]).await.unwrap();
i2c.write(ADDR, &[GPPUB, 0xff]).await.unwrap(); // pullups
let mut val = 1;
loop {
let mut portb = [0];
i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).await.unwrap();
info!("portb = {:02x}", portb[0]);
i2c.write(mcp23017::ADDR, &[GPIOA, val | portb[0]]).await.unwrap();
val = val.rotate_left(1);
// get a register dump
info!("getting register dump");
let mut regs = [0; 22];
i2c.write_read(ADDR, &[0], &mut regs).await.unwrap();
// always get the regdump but only display it if portb'0 is set
if portb[0] & 1 != 0 {
for (idx, reg) in regs.into_iter().enumerate() {
info!("{} => {:02x}", regname(idx as u8), reg);
}
}
Timer::after(Duration::from_millis(100)).await;
}
}

View File

@ -0,0 +1,70 @@
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
use defmt::*;
use embassy_executor::Spawner;
use embassy_rp::i2c::{self, Config};
use embassy_time::{Duration, Timer};
use embedded_hal_1::i2c::I2c;
use {defmt_rtt as _, panic_probe as _};
#[allow(dead_code)]
mod mcp23017 {
pub const ADDR: u8 = 0x20; // default addr
pub const IODIRA: u8 = 0x00;
pub const IPOLA: u8 = 0x02;
pub const GPINTENA: u8 = 0x04;
pub const DEFVALA: u8 = 0x06;
pub const INTCONA: u8 = 0x08;
pub const IOCONA: u8 = 0x0A;
pub const GPPUA: u8 = 0x0C;
pub const INTFA: u8 = 0x0E;
pub const INTCAPA: u8 = 0x10;
pub const GPIOA: u8 = 0x12;
pub const OLATA: u8 = 0x14;
pub const IODIRB: u8 = 0x01;
pub const IPOLB: u8 = 0x03;
pub const GPINTENB: u8 = 0x05;
pub const DEFVALB: u8 = 0x07;
pub const INTCONB: u8 = 0x09;
pub const IOCONB: u8 = 0x0B;
pub const GPPUB: u8 = 0x0D;
pub const INTFB: u8 = 0x0F;
pub const INTCAPB: u8 = 0x11;
pub const GPIOB: u8 = 0x13;
pub const OLATB: u8 = 0x15;
}
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_rp::init(Default::default());
let sda = p.PIN_14;
let scl = p.PIN_15;
info!("set up i2c ");
let mut i2c = i2c::I2c::new_blocking(p.I2C1, scl, sda, Config::default());
use mcp23017::*;
info!("init mcp23017 config for IxpandO");
// init - a outputs, b inputs
i2c.write(ADDR, &[IODIRA, 0x00]).unwrap();
i2c.write(ADDR, &[IODIRB, 0xff]).unwrap();
i2c.write(ADDR, &[GPPUB, 0xff]).unwrap(); // pullups
let mut val = 0xaa;
loop {
let mut portb = [0];
i2c.write(mcp23017::ADDR, &[GPIOA, val]).unwrap();
i2c.write_read(mcp23017::ADDR, &[GPIOB], &mut portb).unwrap();
info!("portb = {:02x}", portb[0]);
val = !val;
Timer::after(Duration::from_secs(1)).await;
}
}

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