Merge branch 'main' of https://github.com/embassy-rs/embassy into embassy-stm32/rcc-rtc-l4

This commit is contained in:
Mathias 2023-07-01 12:17:12 +02:00
commit 60b2f075dc
357 changed files with 14097 additions and 8360 deletions

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

@ -9,12 +9,17 @@ export CARGO_TARGET_DIR=/ci/cache/target
export BUILDER_THREADS=6 export BUILDER_THREADS=6
export BUILDER_COMPRESS=true export BUILDER_COMPRESS=true
# force rustup to download the toolchain before starting building.
# Otherwise, the docs builder is running multiple instances of cargo rustdoc concurrently.
# They all see the toolchain is not installed and try to install it in parallel
# which makes rustup very sad
rustc --version > /dev/null
docserver-builder -i ./embassy-stm32 -o crates/embassy-stm32/git.zup docserver-builder -i ./embassy-stm32 -o crates/embassy-stm32/git.zup
docserver-builder -i ./embassy-boot/boot -o crates/embassy-boot/git.zup docserver-builder -i ./embassy-boot/boot -o crates/embassy-boot/git.zup
docserver-builder -i ./embassy-boot/nrf -o crates/embassy-boot-nrf/git.zup docserver-builder -i ./embassy-boot/nrf -o crates/embassy-boot-nrf/git.zup
docserver-builder -i ./embassy-boot/rp -o crates/embassy-boot-rp/git.zup docserver-builder -i ./embassy-boot/rp -o crates/embassy-boot-rp/git.zup
docserver-builder -i ./embassy-boot/stm32 -o crates/embassy-boot-stm32/git.zup docserver-builder -i ./embassy-boot/stm32 -o crates/embassy-boot-stm32/git.zup
docserver-builder -i ./embassy-cortex-m -o crates/embassy-cortex-m/git.zup
docserver-builder -i ./embassy-embedded-hal -o crates/embassy-embedded-hal/git.zup docserver-builder -i ./embassy-embedded-hal -o crates/embassy-embedded-hal/git.zup
docserver-builder -i ./embassy-executor -o crates/embassy-executor/git.zup docserver-builder -i ./embassy-executor -o crates/embassy-executor/git.zup
docserver-builder -i ./embassy-futures -o crates/embassy-futures/git.zup docserver-builder -i ./embassy-futures -o crates/embassy-futures/git.zup
@ -32,6 +37,7 @@ docserver-builder -i ./embassy-usb-logger -o crates/embassy-usb-logger/git.zup
docserver-builder -i ./cyw43 -o crates/cyw43/git.zup docserver-builder -i ./cyw43 -o crates/cyw43/git.zup
docserver-builder -i ./cyw43-pio -o crates/cyw43-pio/git.zup docserver-builder -i ./cyw43-pio -o crates/cyw43-pio/git.zup
docserver-builder -i ./embassy-net-w5500 -o crates/embassy-net-w5500/git.zup docserver-builder -i ./embassy-net-w5500 -o crates/embassy-net-w5500/git.zup
docserver-builder -i ./embassy-stm32-wpan -o crates/embassy-stm32-wpan/git.zup
export KUBECONFIG=/ci/secrets/kubeconfig.yml export KUBECONFIG=/ci/secrets/kubeconfig.yml
POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name}) POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name})

View File

@ -6,16 +6,16 @@
"rust-analyzer.check.allTargets": false, "rust-analyzer.check.allTargets": false,
"rust-analyzer.check.noDefaultFeatures": true, "rust-analyzer.check.noDefaultFeatures": true,
"rust-analyzer.cargo.noDefaultFeatures": true, "rust-analyzer.cargo.noDefaultFeatures": true,
"rust-analyzer.cargo.target": "thumbv7em-none-eabi", "rust-analyzer.cargo.target": "thumbv7m-none-eabi",
//"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", //"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf",
"rust-analyzer.cargo.features": [ "rust-analyzer.cargo.features": [
"nightly", ///"nightly",
], ],
"rust-analyzer.linkedProjects": [ "rust-analyzer.linkedProjects": [
// Declare for the target you wish to develop // Declare for the target you wish to develop
// "embassy-executor/Cargo.toml", // "embassy-executor/Cargo.toml",
// "embassy-sync/Cargo.toml", // "embassy-sync/Cargo.toml",
"examples/nrf52840/Cargo.toml", "examples/stm32wl/Cargo.toml",
// "examples/nrf5340/Cargo.toml", // "examples/nrf5340/Cargo.toml",
// "examples/nrf-rtos-trace/Cargo.toml", // "examples/nrf-rtos-trace/Cargo.toml",
// "examples/rp/Cargo.toml", // "examples/rp/Cargo.toml",

View File

@ -99,10 +99,10 @@ Examples are found in the `examples/` folder seperated by the chip manufacturer
### Running examples ### Running examples
- Install `probe-rs-cli` with defmt support. - Install `probe-rs`.
```bash ```bash
cargo install probe-rs-cli cargo install probe-rs --features cli
``` ```
- Change directory to the sample's base directory. For example: - Change directory to the sample's base directory. For example:

33
ci.sh
View File

@ -3,7 +3,7 @@
set -euo pipefail set -euo pipefail
export RUSTFLAGS=-Dwarnings export RUSTFLAGS=-Dwarnings
export DEFMT_LOG=trace export DEFMT_LOG=trace,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info
# needed by wifi examples # needed by wifi examples
export WIFI_NETWORK=x export WIFI_NETWORK=x
@ -25,11 +25,19 @@ cargo batch \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
--- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
--- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits,defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \ --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits,defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits,nightly \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits,nightly \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,unstable-traits \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,nightly \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,unstable-traits,nightly \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet,unstable-traits \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet,nightly \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet,unstable-traits,nightly \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52805,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52805,gpiote,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52810,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52810,gpiote,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52811,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52811,gpiote,time-driver-rtc1 \
@ -104,6 +112,7 @@ cargo batch \
--- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-irq/Cargo.toml --target thumbv7em-none-eabi \ --- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-irq/Cargo.toml --target thumbv7em-none-eabi \
--- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml --target thumbv7em-none-eabi \ --- build --release --manifest-path docs/modules/ROOT/examples/layer-by-layer/blinky-async/Cargo.toml --target thumbv7em-none-eabi \
--- build --release --manifest-path examples/nrf52840/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/nrf52840 \ --- build --release --manifest-path examples/nrf52840/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/nrf52840 \
--- build --release --manifest-path examples/nrf52840-rtic/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/nrf52840-rtic \
--- build --release --manifest-path examples/nrf5340/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf5340 \ --- build --release --manifest-path examples/nrf5340/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf5340 \
--- build --release --manifest-path examples/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/rp \ --- build --release --manifest-path examples/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/rp \
--- build --release --manifest-path examples/stm32f0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32f0 \ --- build --release --manifest-path examples/stm32f0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32f0 \
@ -124,16 +133,16 @@ cargo batch \
--- build --release --manifest-path examples/stm32u5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32u5 \ --- build --release --manifest-path examples/stm32u5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32u5 \
--- build --release --manifest-path examples/stm32wb/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wb \ --- build --release --manifest-path examples/stm32wb/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wb \
--- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wl \ --- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wl \
--- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 --out-dir out/examples/boot/nrf --bin b \ --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840,skip-include --out-dir out/examples/boot/nrf \
--- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns --out-dir out/examples/boot/nrf --bin b \ --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns,skip-include --out-dir out/examples/boot/nrf \
--- build --release --manifest-path examples/boot/application/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/boot/rp --bin b \ --- build --release --manifest-path examples/boot/application/rp/Cargo.toml --target thumbv6m-none-eabi --features skip-include --out-dir out/examples/boot/rp \
--- build --release --manifest-path examples/boot/application/stm32f3/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/boot/stm32f3 --bin b \ --- build --release --manifest-path examples/boot/application/stm32f3/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32f3 \
--- build --release --manifest-path examples/boot/application/stm32f7/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/boot/stm32f7 --bin b \ --- build --release --manifest-path examples/boot/application/stm32f7/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32f7 \
--- build --release --manifest-path examples/boot/application/stm32h7/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/boot/stm32h7 --bin b \ --- build --release --manifest-path examples/boot/application/stm32h7/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32h7 \
--- build --release --manifest-path examples/boot/application/stm32l0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/boot/stm32l0 --bin b \ --- build --release --manifest-path examples/boot/application/stm32l0/Cargo.toml --target thumbv6m-none-eabi --features skip-include --out-dir out/examples/boot/stm32l0 \
--- build --release --manifest-path examples/boot/application/stm32l1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/boot/stm32l1 --bin b \ --- build --release --manifest-path examples/boot/application/stm32l1/Cargo.toml --target thumbv7m-none-eabi --features skip-include --out-dir out/examples/boot/stm32l1 \
--- build --release --manifest-path examples/boot/application/stm32l4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/boot/stm32l4 --bin b \ --- build --release --manifest-path examples/boot/application/stm32l4/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32l4 \
--- build --release --manifest-path examples/boot/application/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/boot/stm32wl --bin b \ --- build --release --manifest-path examples/boot/application/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --features skip-include --out-dir out/examples/boot/stm32wl \
--- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \ --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \
--- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \ --- build --release --manifest-path examples/boot/bootloader/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns \
--- build --release --manifest-path examples/boot/bootloader/rp/Cargo.toml --target thumbv6m-none-eabi \ --- build --release --manifest-path examples/boot/bootloader/rp/Cargo.toml --target thumbv6m-none-eabi \

View File

@ -14,9 +14,11 @@ cargo batch \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features log \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features log \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,unstable-traits \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time-driver-rtc1 \

View File

@ -1,6 +1,6 @@
# cyw43 # cyw43
WIP driver for the CYW43439 wifi chip, used in the Raspberry Pi Pico W. Implementation based on [Infineon/wifi-host-driver](https://github.com/Infineon/wifi-host-driver). Rust driver for the CYW43439 wifi chip, used in the Raspberry Pi Pico W. Implementation based on [Infineon/wifi-host-driver](https://github.com/Infineon/wifi-host-driver).
## Current status ## Current status
@ -19,18 +19,18 @@ Working:
TODO: TODO:
- Setting a custom MAC address. - Setting a custom MAC address.
- Bus sleep (unclear what the benefit is. Is it needed for IRQs? or is it just power consumption optimization?) - Bus sleep (for power consumption optimization)
## Running the examples ## Running the examples
- `cargo install probe-rs-cli` - `cargo install probe-rs --features cli`
- `cd examples/rpi-pico-w` - `cd examples/rp`
### Example 1: Scan the wifi stations ### Example 1: Scan the wifi stations
- `cargo run --release --bin wifi_scan` - `cargo run --release --bin wifi_scan`
### Example 2: Create an access point (IP and credentials in the code) ### Example 2: Create an access point (IP and credentials in the code)
- `cargo run --release --bin tcp_server_ap` - `cargo run --release --bin wifi_ap_tcp_server`
### Example 3: Connect to an existing network and create a server ### Example 3: Connect to an existing network and create a server
- `WIFI_NETWORK=MyWifiNetwork WIFI_PASSWORD=MyWifiPassword cargo run --release` - `WIFI_NETWORK=MyWifiNetwork WIFI_PASSWORD=MyWifiPassword cargo run --release --bin wifi_tcp_server`
After a few seconds, you should see that DHCP picks up an IP address like this After a few seconds, you should see that DHCP picks up an IP address like this
``` ```

View File

@ -381,10 +381,7 @@ impl<'a> Control<'a> {
} }
let ioctl = CancelOnDrop(self.ioctl_state); let ioctl = CancelOnDrop(self.ioctl_state);
let resp_len = ioctl.0.do_ioctl(kind, cmd, iface, buf).await;
ioctl.0.do_ioctl(kind, cmd, iface, buf).await;
let resp_len = ioctl.0.wait_complete().await;
ioctl.defuse(); ioctl.defuse();
resp_len resp_len

View File

@ -197,9 +197,6 @@ macro_rules! unwrap {
} }
} }
#[cfg(feature = "defmt-timestamp-uptime")]
defmt::timestamp! {"{=u64:us}", crate::time::Instant::now().as_micros() }
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct NoneError; pub struct NoneError;

View File

@ -20,13 +20,13 @@ fn main() -> ! {
let led = Output::new(p.PB14, Level::Low, Speed::Low); let led = Output::new(p.PB14, Level::Low, Speed::Low);
let mut button = Input::new(p.PC13, Pull::Up); let mut button = Input::new(p.PC13, Pull::Up);
cortex_m::interrupt::free(|cs| unsafe { cortex_m::interrupt::free(|cs| {
enable_interrupt(&mut button); enable_interrupt(&mut button);
LED.borrow(cs).borrow_mut().replace(led); LED.borrow(cs).borrow_mut().replace(led);
BUTTON.borrow(cs).borrow_mut().replace(button); BUTTON.borrow(cs).borrow_mut().replace(button);
NVIC::unmask(pac::Interrupt::EXTI15_10); unsafe { NVIC::unmask(pac::Interrupt::EXTI15_10) };
}); });
loop { loop {
@ -64,25 +64,21 @@ const PORT: u8 = 2;
const PIN: usize = 13; const PIN: usize = 13;
fn check_interrupt<P: Pin>(_pin: &mut Input<'static, P>) -> bool { fn check_interrupt<P: Pin>(_pin: &mut Input<'static, P>) -> bool {
let exti = pac::EXTI; let exti = pac::EXTI;
unsafe { let pin = PIN;
let pin = PIN; let lines = exti.pr(0).read();
let lines = exti.pr(0).read(); lines.line(pin)
lines.line(pin)
}
} }
fn clear_interrupt<P: Pin>(_pin: &mut Input<'static, P>) { fn clear_interrupt<P: Pin>(_pin: &mut Input<'static, P>) {
let exti = pac::EXTI; let exti = pac::EXTI;
unsafe { let pin = PIN;
let pin = PIN; let mut lines = exti.pr(0).read();
let mut lines = exti.pr(0).read(); lines.set_line(pin, true);
lines.set_line(pin, true); exti.pr(0).write_value(lines);
exti.pr(0).write_value(lines);
}
} }
fn enable_interrupt<P: Pin>(_pin: &mut Input<'static, P>) { fn enable_interrupt<P: Pin>(_pin: &mut Input<'static, P>) {
cortex_m::interrupt::free(|_| unsafe { cortex_m::interrupt::free(|_| {
let rcc = pac::RCC; let rcc = pac::RCC;
rcc.apb2enr().modify(|w| w.set_syscfgen(true)); rcc.apb2enr().modify(|w| w.set_syscfgen(true));

View File

@ -56,6 +56,16 @@ impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> {
} }
} }
// Make sure we are running a booted firmware to avoid reverting to a bad state.
async fn verify_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
if self.get_state(aligned).await? == State::Boot {
Ok(())
} else {
Err(FirmwareUpdaterError::BadState)
}
}
/// Obtain the current state. /// Obtain the current state.
/// ///
/// This is useful to check if the bootloader has just done a swap, in order /// This is useful to check if the bootloader has just done a swap, in order
@ -98,6 +108,8 @@ impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> {
assert_eq!(_aligned.len(), STATE::WRITE_SIZE); assert_eq!(_aligned.len(), STATE::WRITE_SIZE);
assert!(_update_len <= self.dfu.capacity() as u32); assert!(_update_len <= self.dfu.capacity() as u32);
self.verify_booted(_aligned).await?;
#[cfg(feature = "ed25519-dalek")] #[cfg(feature = "ed25519-dalek")]
{ {
use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier}; use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier};
@ -217,8 +229,16 @@ impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> {
/// # Safety /// # Safety
/// ///
/// Failing to meet alignment and size requirements may result in a panic. /// Failing to meet alignment and size requirements may result in a panic.
pub async fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { pub async fn write_firmware(
&mut self,
aligned: &mut [u8],
offset: usize,
data: &[u8],
) -> Result<(), FirmwareUpdaterError> {
assert!(data.len() >= DFU::ERASE_SIZE); assert!(data.len() >= DFU::ERASE_SIZE);
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
self.verify_booted(aligned).await?;
self.dfu.erase(offset as u32, (offset + data.len()) as u32).await?; self.dfu.erase(offset as u32, (offset + data.len()) as u32).await?;
@ -232,7 +252,14 @@ impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> {
/// ///
/// Using this instead of `write_firmware` allows for an optimized API in /// Using this instead of `write_firmware` allows for an optimized API in
/// exchange for added complexity. /// exchange for added complexity.
pub async fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> { ///
/// # Safety
///
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
pub async fn prepare_update(&mut self, aligned: &mut [u8]) -> Result<&mut DFU, FirmwareUpdaterError> {
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
self.verify_booted(aligned).await?;
self.dfu.erase(0, self.dfu.capacity() as u32).await?; self.dfu.erase(0, self.dfu.capacity() as u32).await?;
Ok(&mut self.dfu) Ok(&mut self.dfu)
@ -255,13 +282,14 @@ mod tests {
let flash = Mutex::<NoopRawMutex, _>::new(MemFlash::<131072, 4096, 8>::default()); let flash = Mutex::<NoopRawMutex, _>::new(MemFlash::<131072, 4096, 8>::default());
let state = Partition::new(&flash, 0, 4096); let state = Partition::new(&flash, 0, 4096);
let dfu = Partition::new(&flash, 65536, 65536); let dfu = Partition::new(&flash, 65536, 65536);
let mut aligned = [0; 8];
let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66]; let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
let mut to_write = [0; 4096]; let mut to_write = [0; 4096];
to_write[..7].copy_from_slice(update.as_slice()); to_write[..7].copy_from_slice(update.as_slice());
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }); let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state });
block_on(updater.write_firmware(0, to_write.as_slice())).unwrap(); block_on(updater.write_firmware(&mut aligned, 0, to_write.as_slice())).unwrap();
let mut chunk_buf = [0; 2]; let mut chunk_buf = [0; 2];
let mut hash = [0; 20]; let mut hash = [0; 20];
block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap(); block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();

View File

@ -58,6 +58,16 @@ impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> {
} }
} }
// Make sure we are running a booted firmware to avoid reverting to a bad state.
fn verify_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
if self.get_state(aligned)? == State::Boot {
Ok(())
} else {
Err(FirmwareUpdaterError::BadState)
}
}
/// Obtain the current state. /// Obtain the current state.
/// ///
/// This is useful to check if the bootloader has just done a swap, in order /// This is useful to check if the bootloader has just done a swap, in order
@ -100,6 +110,8 @@ impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> {
assert_eq!(_aligned.len(), STATE::WRITE_SIZE); assert_eq!(_aligned.len(), STATE::WRITE_SIZE);
assert!(_update_len <= self.dfu.capacity() as u32); assert!(_update_len <= self.dfu.capacity() as u32);
self.verify_booted(_aligned)?;
#[cfg(feature = "ed25519-dalek")] #[cfg(feature = "ed25519-dalek")]
{ {
use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier}; use ed25519_dalek::{PublicKey, Signature, SignatureError, Verifier};
@ -219,8 +231,15 @@ impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> {
/// # Safety /// # Safety
/// ///
/// Failing to meet alignment and size requirements may result in a panic. /// Failing to meet alignment and size requirements may result in a panic.
pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> { pub fn write_firmware(
&mut self,
aligned: &mut [u8],
offset: usize,
data: &[u8],
) -> Result<(), FirmwareUpdaterError> {
assert!(data.len() >= DFU::ERASE_SIZE); assert!(data.len() >= DFU::ERASE_SIZE);
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
self.verify_booted(aligned)?;
self.dfu.erase(offset as u32, (offset + data.len()) as u32)?; self.dfu.erase(offset as u32, (offset + data.len()) as u32)?;
@ -234,7 +253,13 @@ impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> {
/// ///
/// Using this instead of `write_firmware` allows for an optimized API in /// Using this instead of `write_firmware` allows for an optimized API in
/// exchange for added complexity. /// exchange for added complexity.
pub fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> { ///
/// # Safety
///
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
pub fn prepare_update(&mut self, aligned: &mut [u8]) -> Result<&mut DFU, FirmwareUpdaterError> {
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
self.verify_booted(aligned)?;
self.dfu.erase(0, self.dfu.capacity() as u32)?; self.dfu.erase(0, self.dfu.capacity() as u32)?;
Ok(&mut self.dfu) Ok(&mut self.dfu)
@ -264,7 +289,8 @@ mod tests {
to_write[..7].copy_from_slice(update.as_slice()); to_write[..7].copy_from_slice(update.as_slice());
let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }); let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state });
updater.write_firmware(0, to_write.as_slice()).unwrap(); let mut aligned = [0; 8];
updater.write_firmware(&mut aligned, 0, to_write.as_slice()).unwrap();
let mut chunk_buf = [0; 2]; let mut chunk_buf = [0; 2];
let mut hash = [0; 20]; let mut hash = [0; 20];
updater updater

View File

@ -26,6 +26,8 @@ pub enum FirmwareUpdaterError {
Flash(NorFlashErrorKind), Flash(NorFlashErrorKind),
/// Signature errors. /// Signature errors.
Signature(signature::Error), Signature(signature::Error),
/// Bad state.
BadState,
} }
#[cfg(feature = "defmt")] #[cfg(feature = "defmt")]
@ -34,6 +36,7 @@ impl defmt::Format for FirmwareUpdaterError {
match self { match self {
FirmwareUpdaterError::Flash(_) => defmt::write!(fmt, "FirmwareUpdaterError::Flash(_)"), FirmwareUpdaterError::Flash(_) => defmt::write!(fmt, "FirmwareUpdaterError::Flash(_)"),
FirmwareUpdaterError::Signature(_) => defmt::write!(fmt, "FirmwareUpdaterError::Signature(_)"), FirmwareUpdaterError::Signature(_) => defmt::write!(fmt, "FirmwareUpdaterError::Signature(_)"),
FirmwareUpdaterError::BadState => defmt::write!(fmt, "FirmwareUpdaterError::BadState"),
} }
} }
} }

View File

@ -51,6 +51,8 @@ impl<const N: usize> AsMut<[u8]> for AlignedBuffer<N> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#![allow(unused_imports)]
use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; use embedded_storage::nor_flash::{NorFlash, ReadNorFlash};
#[cfg(feature = "nightly")] #[cfg(feature = "nightly")]
use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash;
@ -120,9 +122,13 @@ mod tests {
dfu: flash.dfu(), dfu: flash.dfu(),
state: flash.state(), state: flash.state(),
}); });
block_on(updater.write_firmware(0, &UPDATE)).unwrap(); block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap();
block_on(updater.mark_updated(&mut aligned)).unwrap(); block_on(updater.mark_updated(&mut aligned)).unwrap();
// Writing after marking updated is not allowed until marked as booted.
let res: Result<(), FirmwareUpdaterError> = block_on(updater.write_firmware(&mut aligned, 0, &UPDATE));
assert!(matches!(res, Err::<(), _>(FirmwareUpdaterError::BadState)));
let flash = flash.into_blocking(); let flash = flash.into_blocking();
let mut bootloader = BootLoader::new(BootLoaderConfig { let mut bootloader = BootLoader::new(BootLoaderConfig {
active: flash.active(), active: flash.active(),
@ -188,7 +194,7 @@ mod tests {
dfu: flash.dfu(), dfu: flash.dfu(),
state: flash.state(), state: flash.state(),
}); });
block_on(updater.write_firmware(0, &UPDATE)).unwrap(); block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap();
block_on(updater.mark_updated(&mut aligned)).unwrap(); block_on(updater.mark_updated(&mut aligned)).unwrap();
let flash = flash.into_blocking(); let flash = flash.into_blocking();
@ -230,7 +236,7 @@ mod tests {
dfu: flash.dfu(), dfu: flash.dfu(),
state: flash.state(), state: flash.state(),
}); });
block_on(updater.write_firmware(0, &UPDATE)).unwrap(); block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap();
block_on(updater.mark_updated(&mut aligned)).unwrap(); block_on(updater.mark_updated(&mut aligned)).unwrap();
let flash = flash.into_blocking(); let flash = flash.into_blocking();

View File

@ -6,7 +6,7 @@ An adaptation of `embassy-boot` for nRF.
## Features ## Features
* Load applications with our without the softdevice. * Load applications with or without the softdevice.
* Configure bootloader partitions based on linker script. * Configure bootloader partitions based on linker script.
* Using watchdog timer to detect application failure. * Using watchdog timer to detect application failure.

View File

@ -1,45 +0,0 @@
[package]
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/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-cortex-m/src/"
features = ["prio-bits-3"]
flavors = [
{ name = "thumbv6m-none-eabi", target = "thumbv6m-none-eabi", features = [] },
{ name = "thumbv7m-none-eabi", target = "thumbv7m-none-eabi", features = [] },
{ name = "thumbv7em-none-eabi", target = "thumbv7em-none-eabi", features = [] },
{ name = "thumbv7em-none-eabihf", target = "thumbv7em-none-eabihf", features = [] },
{ name = "thumbv8m.main-none-eabihf", target = "thumbv8m.main-none-eabihf", features = [] },
]
[features]
default = []
# Define the number of NVIC priority bits.
prio-bits-0 = []
prio-bits-1 = []
prio-bits-2 = []
prio-bits-3 = []
prio-bits-4 = []
prio-bits-5 = []
prio-bits-6 = []
prio-bits-7 = []
prio-bits-8 = []
[dependencies]
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
embassy-executor = { version = "0.2.0", path = "../embassy-executor"}
embassy-macros = { version = "0.2.0", path = "../embassy-macros"}
embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common"}
atomic-polyfill = "1.0.1"
critical-section = "1.1"
cfg-if = "1.0.0"
cortex-m = "0.7.6"

View File

@ -1,9 +0,0 @@
//! Embassy executor and interrupt handling specific to cortex-m devices.
#![no_std]
#![warn(missing_docs)]
// This mod MUST go first, so that the others see its macros.
pub(crate) mod fmt;
pub use embassy_executor as executor;
pub mod interrupt;

View File

@ -205,5 +205,20 @@ mod interrupt {
executor.spawner().make_send() executor.spawner().make_send()
} }
/// Get a SendSpawner for this executor
///
/// This returns a [`SendSpawner`] you can use to spawn tasks on this
/// executor.
///
/// This MUST only be called on an executor that has already been spawned.
/// The function will panic otherwise.
pub fn spawner(&'static self) -> crate::SendSpawner {
if !self.started.load(Ordering::Acquire) {
panic!("InterruptExecutor::spawner() called on uninitialized executor.");
}
let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
executor.spawner().make_send()
}
} }
} }

View File

@ -31,3 +31,15 @@ pub fn block_on<F: Future>(mut fut: F) -> F::Output {
} }
} }
} }
/// Poll a future once.
pub fn poll_once<F: Future>(mut fut: F) -> Poll<F::Output> {
// safety: we don't move the future after this line.
let mut fut = unsafe { Pin::new_unchecked(&mut fut) };
let raw_waker = RawWaker::new(ptr::null(), &VTABLE);
let waker = unsafe { Waker::from_raw(raw_waker) };
let mut cx = Context::from_waker(&waker);
fut.as_mut().poll(&mut cx)
}

View File

@ -195,9 +195,6 @@ macro_rules! unwrap {
} }
} }
#[cfg(feature = "defmt-timestamp-uptime")]
defmt::timestamp! {"{=u64:us}", crate::time::Instant::now().as_micros() }
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct NoneError; pub struct NoneError;

View File

@ -6,8 +6,24 @@ license = "MIT OR Apache-2.0"
[features] [features]
# Define the number of NVIC priority bits.
prio-bits-0 = []
prio-bits-1 = []
prio-bits-2 = []
prio-bits-3 = []
prio-bits-4 = []
prio-bits-5 = []
prio-bits-6 = []
prio-bits-7 = []
prio-bits-8 = []
cortex-m = ["dep:cortex-m", "dep:critical-section"]
[dependencies] [dependencies]
defmt = { version = "0.3", optional = true } defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true } log = { version = "0.4.14", optional = true }
num-traits = { version = "0.2.14", default-features = false } num-traits = { version = "0.2.14", default-features = false }
cortex-m = { version = "0.7.6", optional = true }
critical-section = { version = "1", optional = true }

View File

@ -2,120 +2,208 @@
use core::mem; use core::mem;
use core::sync::atomic::{compiler_fence, Ordering}; use core::sync::atomic::{compiler_fence, Ordering};
use cortex_m::interrupt::InterruptNumber;
use cortex_m::peripheral::NVIC; use cortex_m::peripheral::NVIC;
/// Do not use. Used for macros and HALs only. Not covered by semver guarantees. /// Generate a standard `mod interrupt` for a HAL.
#[doc(hidden)] #[macro_export]
pub mod _export { macro_rules! interrupt_mod {
pub use atomic_polyfill as atomic; ($($irqs:ident),* $(,)?) => {
pub use embassy_macros::{cortex_m_interrupt as interrupt, cortex_m_interrupt_declare as declare}; #[cfg(feature = "rt")]
} pub use cortex_m_rt::interrupt;
/// Interrupt handler trait. /// Interrupt definitions.
/// pub mod interrupt {
/// Drivers that need to handle interrupts implement this trait. pub use $crate::interrupt::{InterruptExt, Priority};
/// The user must ensure `on_interrupt()` is called every time the interrupt fires. pub use crate::pac::Interrupt::*;
/// Drivers must use use [`Binding`] to assert at compile time that the user has done so. pub use crate::pac::Interrupt;
pub trait Handler<I: Interrupt> {
/// Interrupt handler function.
///
/// Must be called every time the `I` interrupt fires, synchronously from
/// the interrupt handler context.
///
/// # Safety
///
/// This function must ONLY be called from the interrupt handler for `I`.
unsafe fn on_interrupt();
}
/// Compile-time assertion that an interrupt has been bound to a handler. /// Type-level interrupt infrastructure.
/// ///
/// For the vast majority of cases, you should use the `bind_interrupts!` /// This module contains one *type* per interrupt. This is used for checking at compile time that
/// macro instead of writing `unsafe impl`s of this trait. /// the interrupts are correctly bound to HAL drivers.
/// ///
/// # Safety /// As an end user, you shouldn't need to use this module directly. Use the [`crate::bind_interrupts!`] macro
/// /// to bind interrupts, and the [`crate::interrupt`] module to manually register interrupt handlers and manipulate
/// By implementing this trait, you are asserting that you have arranged for `H::on_interrupt()` /// interrupts directly (pending/unpending, enabling/disabling, setting the priority, etc...)
/// to be called every time the `I` interrupt fires. pub mod typelevel {
/// use super::InterruptExt;
/// This allows drivers to check bindings at compile-time.
pub unsafe trait Binding<I: Interrupt, H: Handler<I>> {}
#[derive(Clone, Copy)] mod sealed {
pub(crate) struct NrWrap(pub(crate) u16); pub trait Interrupt {}
unsafe impl cortex_m::interrupt::InterruptNumber for NrWrap { }
fn number(self) -> u16 {
self.0 /// Type-level interrupt.
} ///
/// This trait is implemented for all typelevel interrupt types in this module.
pub trait Interrupt: sealed::Interrupt {
/// Interrupt enum variant.
///
/// This allows going from typelevel interrupts (one type per interrupt) to
/// non-typelevel interrupts (a single `Interrupt` enum type, with one variant per interrupt).
const IRQ: super::Interrupt;
/// Enable the interrupt.
#[inline]
unsafe fn enable() {
Self::IRQ.enable()
}
/// Disable the interrupt.
#[inline]
fn disable() {
Self::IRQ.disable()
}
/// Check if interrupt is enabled.
#[inline]
fn is_enabled() -> bool {
Self::IRQ.is_enabled()
}
/// Check if interrupt is pending.
#[inline]
fn is_pending() -> bool {
Self::IRQ.is_pending()
}
/// Set interrupt pending.
#[inline]
fn pend() {
Self::IRQ.pend()
}
/// Unset interrupt pending.
#[inline]
fn unpend() {
Self::IRQ.unpend()
}
/// Get the priority of the interrupt.
#[inline]
fn get_priority() -> crate::interrupt::Priority {
Self::IRQ.get_priority()
}
/// Set the interrupt priority.
#[inline]
fn set_priority(prio: crate::interrupt::Priority) {
Self::IRQ.set_priority(prio)
}
}
$(
#[allow(non_camel_case_types)]
#[doc=stringify!($irqs)]
#[doc=" typelevel interrupt."]
pub enum $irqs {}
impl sealed::Interrupt for $irqs{}
impl Interrupt for $irqs {
const IRQ: super::Interrupt = super::Interrupt::$irqs;
}
)*
/// Interrupt handler trait.
///
/// Drivers that need to handle interrupts implement this trait.
/// The user must ensure `on_interrupt()` is called every time the interrupt fires.
/// Drivers must use use [`Binding`] to assert at compile time that the user has done so.
pub trait Handler<I: Interrupt> {
/// Interrupt handler function.
///
/// Must be called every time the `I` interrupt fires, synchronously from
/// the interrupt handler context.
///
/// # Safety
///
/// This function must ONLY be called from the interrupt handler for `I`.
unsafe fn on_interrupt();
}
/// Compile-time assertion that an interrupt has been bound to a handler.
///
/// For the vast majority of cases, you should use the `bind_interrupts!`
/// macro instead of writing `unsafe impl`s of this trait.
///
/// # Safety
///
/// By implementing this trait, you are asserting that you have arranged for `H::on_interrupt()`
/// to be called every time the `I` interrupt fires.
///
/// This allows drivers to check bindings at compile-time.
pub unsafe trait Binding<I: Interrupt, H: Handler<I>> {}
}
}
};
} }
/// Represents an interrupt type that can be configured by embassy to handle /// Represents an interrupt type that can be configured by embassy to handle
/// interrupts. /// interrupts.
pub unsafe trait Interrupt { pub unsafe trait InterruptExt: InterruptNumber + Copy {
/// Return the NVIC interrupt number for this interrupt.
fn number() -> u16;
/// Enable the interrupt. /// Enable the interrupt.
#[inline] #[inline]
unsafe fn enable() { unsafe fn enable(self) {
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
NVIC::unmask(NrWrap(Self::number())) NVIC::unmask(self)
} }
/// Disable the interrupt. /// Disable the interrupt.
#[inline] #[inline]
fn disable() { fn disable(self) {
NVIC::mask(NrWrap(Self::number())); NVIC::mask(self);
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
} }
/// Check if interrupt is being handled. /// Check if interrupt is being handled.
#[inline] #[inline]
#[cfg(not(armv6m))] #[cfg(not(armv6m))]
fn is_active() -> bool { fn is_active(self) -> bool {
NVIC::is_active(NrWrap(Self::number())) NVIC::is_active(self)
} }
/// Check if interrupt is enabled. /// Check if interrupt is enabled.
#[inline] #[inline]
fn is_enabled() -> bool { fn is_enabled(self) -> bool {
NVIC::is_enabled(NrWrap(Self::number())) NVIC::is_enabled(self)
} }
/// Check if interrupt is pending. /// Check if interrupt is pending.
#[inline] #[inline]
fn is_pending() -> bool { fn is_pending(self) -> bool {
NVIC::is_pending(NrWrap(Self::number())) NVIC::is_pending(self)
} }
/// Set interrupt pending. /// Set interrupt pending.
#[inline] #[inline]
fn pend() { fn pend(self) {
NVIC::pend(NrWrap(Self::number())) NVIC::pend(self)
} }
/// Unset interrupt pending. /// Unset interrupt pending.
#[inline] #[inline]
fn unpend() { fn unpend(self) {
NVIC::unpend(NrWrap(Self::number())) NVIC::unpend(self)
} }
/// Get the priority of the interrupt. /// Get the priority of the interrupt.
#[inline] #[inline]
fn get_priority() -> Priority { fn get_priority(self) -> Priority {
Priority::from(NVIC::get_priority(NrWrap(Self::number()))) Priority::from(NVIC::get_priority(self))
} }
/// Set the interrupt priority. /// Set the interrupt priority.
#[inline] #[inline]
fn set_priority(prio: Priority) { fn set_priority(self, prio: Priority) {
critical_section::with(|_| unsafe { critical_section::with(|_| unsafe {
let mut nvic: cortex_m::peripheral::NVIC = mem::transmute(()); let mut nvic: cortex_m::peripheral::NVIC = mem::transmute(());
nvic.set_priority(NrWrap(Self::number()), prio.into()) nvic.set_priority(self, prio.into())
}) })
} }
} }
unsafe impl<T: InterruptNumber + Copy> InterruptExt for T {}
impl From<u8> for Priority { impl From<u8> for Priority {
fn from(priority: u8) -> Self { fn from(priority: u8) -> Self {
unsafe { mem::transmute(priority & PRIO_MASK) } unsafe { mem::transmute(priority & PRIO_MASK) }

View File

@ -11,3 +11,6 @@ mod peripheral;
pub mod ratio; pub mod ratio;
pub mod ring_buffer; pub mod ring_buffer;
pub use peripheral::{Peripheral, PeripheralRef}; pub use peripheral::{Peripheral, PeripheralRef};
#[cfg(feature = "cortex-m")]
pub mod interrupt;

View File

@ -1,7 +1,7 @@
#[cfg(feature = "stm32wl")] #[cfg(feature = "stm32wl")]
use embassy_stm32::interrupt; use embassy_stm32::interrupt;
#[cfg(feature = "stm32wl")] #[cfg(feature = "stm32wl")]
use embassy_stm32::interrupt::*; use embassy_stm32::interrupt::InterruptExt;
#[cfg(feature = "stm32wl")] #[cfg(feature = "stm32wl")]
use embassy_stm32::pac; use embassy_stm32::pac;
#[cfg(feature = "stm32wl")] #[cfg(feature = "stm32wl")]
@ -20,9 +20,9 @@ use lora_phy::mod_traits::InterfaceVariant;
pub struct InterruptHandler {} pub struct InterruptHandler {}
#[cfg(feature = "stm32wl")] #[cfg(feature = "stm32wl")]
impl interrupt::Handler<interrupt::SUBGHZ_RADIO> for InterruptHandler { impl interrupt::typelevel::Handler<interrupt::typelevel::SUBGHZ_RADIO> for InterruptHandler {
unsafe fn on_interrupt() { unsafe fn on_interrupt() {
interrupt::SUBGHZ_RADIO::disable(); interrupt::SUBGHZ_RADIO.disable();
IRQ_SIGNAL.signal(()); IRQ_SIGNAL.signal(());
} }
} }
@ -45,11 +45,11 @@ where
{ {
/// Create an InterfaceVariant instance for an stm32wl/sx1262 combination /// Create an InterfaceVariant instance for an stm32wl/sx1262 combination
pub fn new( pub fn new(
_irq: impl interrupt::Binding<interrupt::SUBGHZ_RADIO, InterruptHandler>, _irq: impl interrupt::typelevel::Binding<interrupt::typelevel::SUBGHZ_RADIO, InterruptHandler>,
rf_switch_rx: Option<CTRL>, rf_switch_rx: Option<CTRL>,
rf_switch_tx: Option<CTRL>, rf_switch_tx: Option<CTRL>,
) -> Result<Self, RadioError> { ) -> Result<Self, RadioError> {
interrupt::SUBGHZ_RADIO::disable(); interrupt::SUBGHZ_RADIO.disable();
Ok(Self { Ok(Self {
board_type: BoardType::Stm32wlSx1262, // updated when associated with a specific LoRa board board_type: BoardType::Stm32wlSx1262, // updated when associated with a specific LoRa board
rf_switch_rx, rf_switch_rx,
@ -68,34 +68,28 @@ where
} }
async fn set_nss_low(&mut self) -> Result<(), RadioError> { async fn set_nss_low(&mut self) -> Result<(), RadioError> {
let pwr = pac::PWR; let pwr = pac::PWR;
unsafe { pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::LOW));
pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::LOW));
}
Ok(()) Ok(())
} }
async fn set_nss_high(&mut self) -> Result<(), RadioError> { async fn set_nss_high(&mut self) -> Result<(), RadioError> {
let pwr = pac::PWR; let pwr = pac::PWR;
unsafe { pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::HIGH));
pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::HIGH));
}
Ok(()) Ok(())
} }
async fn reset(&mut self, _delay: &mut impl DelayUs) -> Result<(), RadioError> { async fn reset(&mut self, _delay: &mut impl DelayUs) -> Result<(), RadioError> {
let rcc = pac::RCC; let rcc = pac::RCC;
unsafe { rcc.csr().modify(|w| w.set_rfrst(true));
rcc.csr().modify(|w| w.set_rfrst(true)); rcc.csr().modify(|w| w.set_rfrst(false));
rcc.csr().modify(|w| w.set_rfrst(false));
}
Ok(()) Ok(())
} }
async fn wait_on_busy(&mut self) -> Result<(), RadioError> { async fn wait_on_busy(&mut self) -> Result<(), RadioError> {
let pwr = pac::PWR; let pwr = pac::PWR;
while unsafe { pwr.sr2().read().rfbusys() == pac::pwr::vals::Rfbusys::BUSY } {} while pwr.sr2().read().rfbusys() == pac::pwr::vals::Rfbusys::BUSY {}
Ok(()) Ok(())
} }
async fn await_irq(&mut self) -> Result<(), RadioError> { async fn await_irq(&mut self) -> Result<(), RadioError> {
unsafe { interrupt::SUBGHZ_RADIO::enable() }; unsafe { interrupt::SUBGHZ_RADIO.enable() };
IRQ_SIGNAL.wait().await; IRQ_SIGNAL.wait().await;
Ok(()) Ok(())
} }

View File

@ -12,9 +12,9 @@ categories = [
] ]
[dependencies] [dependencies]
syn = { version = "1.0.76", features = ["full", "extra-traits"] } syn = { version = "2.0.15", features = ["full", "extra-traits"] }
quote = "1.0.9" quote = "1.0.9"
darling = "0.13.0" darling = "0.20.1"
proc-macro2 = "1.0.29" proc-macro2 = "1.0.29"
[lib] [lib]

View File

@ -1,11 +1,28 @@
#![doc = include_str!("../README.md")] #![doc = include_str!("../README.md")]
extern crate proc_macro; extern crate proc_macro;
use darling::ast::NestedMeta;
use proc_macro::TokenStream; use proc_macro::TokenStream;
mod macros; mod macros;
mod util; mod util;
use macros::*; use macros::*;
use syn::parse::{Parse, ParseBuffer};
use syn::punctuated::Punctuated;
use syn::Token;
struct Args {
meta: Vec<NestedMeta>,
}
impl Parse for Args {
fn parse(input: &ParseBuffer) -> syn::Result<Self> {
let meta = Punctuated::<NestedMeta, Token![,]>::parse_terminated(input)?;
Ok(Args {
meta: meta.into_iter().collect(),
})
}
}
/// Declares an async task that can be run by `embassy-executor`. The optional `pool_size` parameter can be used to specify how /// Declares an async task that can be run by `embassy-executor`. The optional `pool_size` parameter can be used to specify how
/// many concurrent tasks can be spawned (default is 1) for the function. /// many concurrent tasks can be spawned (default is 1) for the function.
@ -39,10 +56,10 @@ use macros::*;
/// ``` /// ```
#[proc_macro_attribute] #[proc_macro_attribute]
pub fn task(args: TokenStream, item: TokenStream) -> TokenStream { pub fn task(args: TokenStream, item: TokenStream) -> TokenStream {
let args = syn::parse_macro_input!(args as syn::AttributeArgs); let args = syn::parse_macro_input!(args as Args);
let f = syn::parse_macro_input!(item as syn::ItemFn); let f = syn::parse_macro_input!(item as syn::ItemFn);
task::run(args, f).unwrap_or_else(|x| x).into() task::run(&args.meta, f).unwrap_or_else(|x| x).into()
} }
/// Creates a new `executor` instance and declares an application entry point for Cortex-M spawning the corresponding function body as an async task. /// Creates a new `executor` instance and declares an application entry point for Cortex-M spawning the corresponding function body as an async task.
@ -65,9 +82,9 @@ pub fn task(args: TokenStream, item: TokenStream) -> TokenStream {
/// ``` /// ```
#[proc_macro_attribute] #[proc_macro_attribute]
pub fn main_cortex_m(args: TokenStream, item: TokenStream) -> TokenStream { pub fn main_cortex_m(args: TokenStream, item: TokenStream) -> TokenStream {
let args = syn::parse_macro_input!(args as syn::AttributeArgs); let args = syn::parse_macro_input!(args as Args);
let f = syn::parse_macro_input!(item as syn::ItemFn); let f = syn::parse_macro_input!(item as syn::ItemFn);
main::run(args, f, main::cortex_m()).unwrap_or_else(|x| x).into() main::run(&args.meta, f, main::cortex_m()).unwrap_or_else(|x| x).into()
} }
/// Creates a new `executor` instance and declares an application entry point for RISC-V spawning the corresponding function body as an async task. /// Creates a new `executor` instance and declares an application entry point for RISC-V spawning the corresponding function body as an async task.
@ -100,9 +117,9 @@ pub fn main_cortex_m(args: TokenStream, item: TokenStream) -> TokenStream {
/// ``` /// ```
#[proc_macro_attribute] #[proc_macro_attribute]
pub fn main_riscv(args: TokenStream, item: TokenStream) -> TokenStream { pub fn main_riscv(args: TokenStream, item: TokenStream) -> TokenStream {
let args = syn::parse_macro_input!(args as syn::AttributeArgs); let args = syn::parse_macro_input!(args as Args);
let f = syn::parse_macro_input!(item as syn::ItemFn); let f = syn::parse_macro_input!(item as syn::ItemFn);
main::run(args.clone(), f, main::riscv(args)) main::run(&args.meta, f, main::riscv(&args.meta))
.unwrap_or_else(|x| x) .unwrap_or_else(|x| x)
.into() .into()
} }
@ -127,9 +144,9 @@ pub fn main_riscv(args: TokenStream, item: TokenStream) -> TokenStream {
/// ``` /// ```
#[proc_macro_attribute] #[proc_macro_attribute]
pub fn main_std(args: TokenStream, item: TokenStream) -> TokenStream { pub fn main_std(args: TokenStream, item: TokenStream) -> TokenStream {
let args = syn::parse_macro_input!(args as syn::AttributeArgs); let args = syn::parse_macro_input!(args as Args);
let f = syn::parse_macro_input!(item as syn::ItemFn); let f = syn::parse_macro_input!(item as syn::ItemFn);
main::run(args, f, main::std()).unwrap_or_else(|x| x).into() main::run(&args.meta, f, main::std()).unwrap_or_else(|x| x).into()
} }
/// Creates a new `executor` instance and declares an application entry point for WASM spawning the corresponding function body as an async task. /// Creates a new `executor` instance and declares an application entry point for WASM spawning the corresponding function body as an async task.
@ -152,20 +169,7 @@ pub fn main_std(args: TokenStream, item: TokenStream) -> TokenStream {
/// ``` /// ```
#[proc_macro_attribute] #[proc_macro_attribute]
pub fn main_wasm(args: TokenStream, item: TokenStream) -> TokenStream { pub fn main_wasm(args: TokenStream, item: TokenStream) -> TokenStream {
let args = syn::parse_macro_input!(args as syn::AttributeArgs); let args = syn::parse_macro_input!(args as Args);
let f = syn::parse_macro_input!(item as syn::ItemFn); let f = syn::parse_macro_input!(item as syn::ItemFn);
main::run(args, f, main::wasm()).unwrap_or_else(|x| x).into() main::run(&args.meta, f, main::wasm()).unwrap_or_else(|x| x).into()
}
#[proc_macro_attribute]
pub fn cortex_m_interrupt(args: TokenStream, item: TokenStream) -> TokenStream {
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
let f = syn::parse_macro_input!(item as syn::ItemFn);
cortex_m_interrupt::run(args, f).unwrap_or_else(|x| x).into()
}
#[proc_macro]
pub fn cortex_m_interrupt_declare(item: TokenStream) -> TokenStream {
let name = syn::parse_macro_input!(item as syn::Ident);
cortex_m_interrupt_declare::run(name).unwrap_or_else(|x| x).into()
} }

View File

@ -1,66 +0,0 @@
use std::iter;
use darling::FromMeta;
use proc_macro2::TokenStream;
use quote::quote;
use syn::{ReturnType, Type, Visibility};
use crate::util::ctxt::Ctxt;
#[derive(Debug, FromMeta)]
struct Args {}
pub fn run(args: syn::AttributeArgs, mut f: syn::ItemFn) -> Result<TokenStream, TokenStream> {
let _args = Args::from_list(&args).map_err(|e| e.write_errors())?;
let ident = f.sig.ident.clone();
let ident_s = ident.to_string();
// XXX should we blacklist other attributes?
let valid_signature = f.sig.constness.is_none()
&& f.vis == Visibility::Inherited
&& f.sig.abi.is_none()
&& f.sig.inputs.is_empty()
&& f.sig.generics.params.is_empty()
&& f.sig.generics.where_clause.is_none()
&& f.sig.variadic.is_none()
&& match f.sig.output {
ReturnType::Default => true,
ReturnType::Type(_, ref ty) => match **ty {
Type::Tuple(ref tuple) => tuple.elems.is_empty(),
Type::Never(..) => true,
_ => false,
},
};
let ctxt = Ctxt::new();
if !valid_signature {
ctxt.error_spanned_by(
&f.sig,
"`#[interrupt]` handlers must have signature `[unsafe] fn() [-> !]`",
);
}
ctxt.check()?;
f.block.stmts = iter::once(
syn::parse2(quote! {{
// Check that this interrupt actually exists
let __irq_exists_check: interrupt::#ident;
}})
.unwrap(),
)
.chain(f.block.stmts)
.collect();
let result = quote!(
#[doc(hidden)]
#[export_name = #ident_s]
#[allow(non_snake_case)]
#f
);
Ok(result)
}

View File

@ -1,21 +0,0 @@
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
pub fn run(name: syn::Ident) -> Result<TokenStream, TokenStream> {
let name = format_ident!("{}", name);
let doc = format!("{} interrupt.", name);
let result = quote! {
#[doc = #doc]
#[allow(non_camel_case_types)]
pub enum #name{}
unsafe impl ::embassy_cortex_m::interrupt::Interrupt for #name {
fn number() -> u16 {
use cortex_m::interrupt::InterruptNumber;
let irq = InterruptEnum::#name;
irq.number() as u16
}
}
};
Ok(result)
}

View File

@ -1,3 +1,4 @@
use darling::export::NestedMeta;
use darling::FromMeta; use darling::FromMeta;
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::quote; use quote::quote;
@ -11,8 +12,8 @@ struct Args {
entry: Option<String>, entry: Option<String>,
} }
pub fn riscv(args: syn::AttributeArgs) -> TokenStream { pub fn riscv(args: &[NestedMeta]) -> TokenStream {
let maybe_entry = match Args::from_list(&args) { let maybe_entry = match Args::from_list(args) {
Ok(args) => args.entry, Ok(args) => args.entry,
Err(e) => return e.write_errors(), Err(e) => return e.write_errors(),
}; };
@ -77,9 +78,9 @@ pub fn std() -> TokenStream {
} }
} }
pub fn run(args: syn::AttributeArgs, f: syn::ItemFn, main: TokenStream) -> Result<TokenStream, TokenStream> { pub fn run(args: &[NestedMeta], f: syn::ItemFn, main: TokenStream) -> Result<TokenStream, TokenStream> {
#[allow(unused_variables)] #[allow(unused_variables)]
let args = Args::from_list(&args).map_err(|e| e.write_errors())?; let args = Args::from_list(args).map_err(|e| e.write_errors())?;
let fargs = f.sig.inputs.clone(); let fargs = f.sig.inputs.clone();

View File

@ -1,4 +1,2 @@
pub mod cortex_m_interrupt;
pub mod cortex_m_interrupt_declare;
pub mod main; pub mod main;
pub mod task; pub mod task;

View File

@ -1,20 +1,24 @@
use darling::export::NestedMeta;
use darling::FromMeta; use darling::FromMeta;
use proc_macro2::TokenStream; use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote}; use quote::{format_ident, quote};
use syn::{parse_quote, ItemFn, ReturnType, Type}; use syn::{parse_quote, Expr, ExprLit, ItemFn, Lit, LitInt, ReturnType, Type};
use crate::util::ctxt::Ctxt; use crate::util::ctxt::Ctxt;
#[derive(Debug, FromMeta)] #[derive(Debug, FromMeta)]
struct Args { struct Args {
#[darling(default)] #[darling(default)]
pool_size: Option<usize>, pool_size: Option<syn::Expr>,
} }
pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result<TokenStream, TokenStream> { pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result<TokenStream, TokenStream> {
let args = Args::from_list(&args).map_err(|e| e.write_errors())?; let args = Args::from_list(args).map_err(|e| e.write_errors())?;
let pool_size: usize = args.pool_size.unwrap_or(1); let pool_size = args.pool_size.unwrap_or(Expr::Lit(ExprLit {
attrs: vec![],
lit: Lit::Int(LitInt::new("1", Span::call_site())),
}));
let ctxt = Ctxt::new(); let ctxt = Ctxt::new();
@ -45,10 +49,6 @@ pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result<TokenStream, Toke
}, },
} }
if pool_size < 1 {
ctxt.error_spanned_by(&f.sig, "pool_size must be 1 or greater");
}
let mut arg_names = Vec::new(); let mut arg_names = Vec::new();
let mut fargs = f.sig.inputs.clone(); let mut fargs = f.sig.inputs.clone();
@ -82,7 +82,8 @@ pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result<TokenStream, Toke
let mut task_outer: ItemFn = parse_quote! { let mut task_outer: ItemFn = parse_quote! {
#visibility fn #task_ident(#fargs) -> ::embassy_executor::SpawnToken<impl Sized> { #visibility fn #task_ident(#fargs) -> ::embassy_executor::SpawnToken<impl Sized> {
type Fut = impl ::core::future::Future + 'static; type Fut = impl ::core::future::Future + 'static;
static POOL: ::embassy_executor::raw::TaskPool<Fut, #pool_size> = ::embassy_executor::raw::TaskPool::new(); const POOL_SIZE: usize = #pool_size;
static POOL: ::embassy_executor::raw::TaskPool<Fut, POOL_SIZE> = ::embassy_executor::raw::TaskPool::new();
unsafe { POOL._spawn_async_fn(move || #task_inner_ident(#(#arg_names,)*)) } unsafe { POOL._spawn_async_fn(move || #task_inner_ident(#(#arg_names,)*)) }
} }
}; };

View File

@ -2,6 +2,14 @@
name = "embassy-net-driver-channel" name = "embassy-net-driver-channel"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
license = "MIT OR Apache-2.0"
description = "High-level channel-based driver for the `embassy-net` async TCP/IP network stack."
repository = "https://github.com/embassy-rs/embassy"
categories = [
"embedded",
"no-std",
"asynchronous",
]
[package.metadata.embassy_docs] [package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-channel-v$VERSION/embassy-net-driver-channel/src/" src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-channel-v$VERSION/embassy-net-driver-channel/src/"
@ -9,6 +17,9 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-d
features = ["defmt"] features = ["defmt"]
target = "thumbv7em-none-eabi" target = "thumbv7em-none-eabi"
[package.metadata.docs.rs]
features = ["defmt"]
[dependencies] [dependencies]
defmt = { version = "0.3", optional = true } defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true } log = { version = "0.4.14", optional = true }

View File

@ -0,0 +1,96 @@
# embassy-net-driver-channel
This crate provides a toolkit for implementing [`embassy-net`](https://crates.io/crates/embassy-net) drivers in a
higher level way than implementing the [`embassy-net-driver`](https://crates.io/crates/embassy-net-driver) trait directly.
The `embassy-net-driver` trait is polling-based. To implement it, you must write the packet receive/transmit state machines by
hand, and hook up the `Waker`s provided by `embassy-net` to the right interrupt handlers so that `embassy-net`
knows when to poll your driver again to make more progress.
With `embassy-net-driver-channel`
## A note about deadlocks
When implementing a driver using this crate, it might be tempting to write it in the most straightforward way:
```rust,ignore
loop {
// Wait for either..
match select(
// ... the chip signaling an interrupt, indicating a packet is available to receive, or
irq_pin.wait_for_low(),
// ... a TX buffer becoming available, i.e. embassy-net wants to send a packet
tx_chan.tx_buf(),
).await {
Either::First(_) => {
// a packet is ready to be received!
let buf = rx_chan.rx_buf().await; // allocate a rx buf from the packet queue
let n = receive_packet_over_spi(buf).await;
rx_chan.rx_done(n);
}
Either::Second(buf) => {
// a packet is ready to be sent!
send_packet_over_spi(buf).await;
tx_chan.tx_done();
}
}
}
```
However, this code has a latent deadlock bug. The symptom is it can hang at `rx_chan.rx_buf().await` under load.
The reason is that, under load, both the TX and RX queues can get full at the same time. When this happens, the `embassy-net` task stalls trying to send because the TX queue is full, therefore it stops processing packets in the RX queue. Your driver task also stalls because the RX queue is full, therefore it stops processing packets in the TX queue.
The fix is to make sure to always service the TX queue while you're waiting for space to become available in the TX queue. For example, select on either "tx_chan.tx_buf() available" or "INT is low AND rx_chan.rx_buf() available":
```rust,ignore
loop {
// Wait for either..
match select(
async {
// ... the chip signaling an interrupt, indicating a packet is available to receive
irq_pin.wait_for_low().await;
// *AND* the buffer is ready...
rx_chan.rx_buf().await
},
// ... or a TX buffer becoming available, i.e. embassy-net wants to send a packet
tx_chan.tx_buf(),
).await {
Either::First(buf) => {
// a packet is ready to be received!
let n = receive_packet_over_spi(buf).await;
rx_chan.rx_done(n);
}
Either::Second(buf) => {
// a packet is ready to be sent!
send_packet_over_spi(buf).await;
tx_chan.tx_done();
}
}
}
```
## Examples
These `embassy-net` drivers are implemented using this crate. You can look at them for inspiration.
- [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W
- [`embassy-usb`](https://github.com/embassy-rs/embassy/tree/main/embassy-usb) for Ethernet-over-USB (CDC NCM) support.
- [`embassy-net-w5500`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-w5500) for Wiznet W5500 SPI Ethernet MAC+PHY chip.
- [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU.
## Interoperability
This crate can run on any executor.
## 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

@ -1,4 +1,5 @@
#![no_std] #![no_std]
#![doc = include_str!("../README.md")]
// must go first! // must go first!
mod fmt; mod fmt;

View File

@ -3,7 +3,13 @@ name = "embassy-net-driver"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
description = "Driver trait for the `embassy-net` async TCP/IP network stack."
repository = "https://github.com/embassy-rs/embassy"
categories = [
"embedded",
"no-std",
"asynchronous",
]
[package.metadata.embassy_docs] [package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-v$VERSION/embassy-net-driver/src/" src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-v$VERSION/embassy-net-driver/src/"
@ -11,5 +17,8 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-d
features = ["defmt"] features = ["defmt"]
target = "thumbv7em-none-eabi" target = "thumbv7em-none-eabi"
[package.metadata.docs.rs]
features = ["defmt"]
[dependencies] [dependencies]
defmt = { version = "0.3", optional = true } defmt = { version = "0.3", optional = true }

View File

@ -1,5 +1,21 @@
# embassy-net-driver # embassy-net-driver
This crate contains the driver trait necessary for adding [`embassy-net`](https://crates.io/crates/embassy-net) support
for a new hardware platform.
If you want to *use* `embassy-net` with already made drivers, you should depend on the main `embassy-net` crate, not on this crate.
If you are writing a driver, you should depend only on this crate, not on the main `embassy-net` crate.
This will allow your driver to continue working for newer `embassy-net` major versions, without needing an update,
if the driver trait has not had breaking changes.
See also [`embassy-net-driver-channel`](https://crates.io/crates/embassy-net-driver-channel), which provides a higer-level API
to construct a driver that processes packets in its own background task and communicates with the `embassy-net` task via
packet queues for RX and TX.
## Interoperability
This crate can run on any executor.
## License ## License

View File

@ -0,0 +1,20 @@
[package]
name = "embassy-net-esp-hosted"
version = "0.1.0"
edition = "2021"
[dependencies]
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
embassy-time = { version = "0.1.0", path = "../embassy-time" }
embassy-sync = { version = "0.2.0", path = "../embassy-sync"}
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
embedded-hal = { version = "1.0.0-alpha.10" }
embedded-hal-async = { version = "=0.2.0-alpha.1" }
noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] }
#noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] }
heapless = "0.7.16"

View File

@ -0,0 +1,139 @@
use ch::driver::LinkState;
use defmt::Debug2Format;
use embassy_net_driver_channel as ch;
use heapless::String;
use crate::ioctl::Shared;
use crate::proto::{self, CtrlMsg};
#[derive(Debug)]
pub struct Error {
pub status: u32,
}
pub struct Control<'a> {
state_ch: ch::StateRunner<'a>,
shared: &'a Shared,
}
#[allow(unused)]
enum WifiMode {
None = 0,
Sta = 1,
Ap = 2,
ApSta = 3,
}
impl<'a> Control<'a> {
pub(crate) fn new(state_ch: ch::StateRunner<'a>, shared: &'a Shared) -> Self {
Self { state_ch, shared }
}
pub async fn init(&mut self) {
debug!("wait for init event...");
self.shared.init_wait().await;
debug!("set wifi mode");
self.set_wifi_mode(WifiMode::Sta as _).await;
let mac_addr = self.get_mac_addr().await;
debug!("mac addr: {:02x}", mac_addr);
self.state_ch.set_ethernet_address(mac_addr);
}
pub async fn join(&mut self, ssid: &str, password: &str) {
let req = proto::CtrlMsg {
msg_id: proto::CtrlMsgId::ReqConnectAp as _,
msg_type: proto::CtrlMsgType::Req as _,
payload: Some(proto::CtrlMsgPayload::ReqConnectAp(proto::CtrlMsgReqConnectAp {
ssid: String::from(ssid),
pwd: String::from(password),
bssid: String::new(),
listen_interval: 3,
is_wpa3_supported: false,
})),
};
let resp = self.ioctl(req).await;
let proto::CtrlMsgPayload::RespConnectAp(resp) = resp.payload.unwrap() else { panic!("unexpected resp") };
debug!("======= {:?}", Debug2Format(&resp));
assert_eq!(resp.resp, 0);
self.state_ch.set_link_state(LinkState::Up);
}
async fn get_mac_addr(&mut self) -> [u8; 6] {
let req = proto::CtrlMsg {
msg_id: proto::CtrlMsgId::ReqGetMacAddress as _,
msg_type: proto::CtrlMsgType::Req as _,
payload: Some(proto::CtrlMsgPayload::ReqGetMacAddress(
proto::CtrlMsgReqGetMacAddress {
mode: WifiMode::Sta as _,
},
)),
};
let resp = self.ioctl(req).await;
let proto::CtrlMsgPayload::RespGetMacAddress(resp) = resp.payload.unwrap() else { panic!("unexpected resp") };
assert_eq!(resp.resp, 0);
// WHY IS THIS A STRING? WHYYYY
fn nibble_from_hex(b: u8) -> u8 {
match b {
b'0'..=b'9' => b - b'0',
b'a'..=b'f' => b + 0xa - b'a',
b'A'..=b'F' => b + 0xa - b'A',
_ => panic!("invalid hex digit {}", b),
}
}
let mac = resp.mac.as_bytes();
let mut res = [0; 6];
assert_eq!(mac.len(), 17);
for (i, b) in res.iter_mut().enumerate() {
*b = (nibble_from_hex(mac[i * 3]) << 4) | nibble_from_hex(mac[i * 3 + 1])
}
res
}
async fn set_wifi_mode(&mut self, mode: u32) {
let req = proto::CtrlMsg {
msg_id: proto::CtrlMsgId::ReqSetWifiMode as _,
msg_type: proto::CtrlMsgType::Req as _,
payload: Some(proto::CtrlMsgPayload::ReqSetWifiMode(proto::CtrlMsgReqSetMode { mode })),
};
let resp = self.ioctl(req).await;
let proto::CtrlMsgPayload::RespSetWifiMode(resp) = resp.payload.unwrap() else { panic!("unexpected resp") };
assert_eq!(resp.resp, 0);
}
async fn ioctl(&mut self, req: CtrlMsg) -> CtrlMsg {
debug!("ioctl req: {:?}", &req);
let mut buf = [0u8; 128];
let req_len = noproto::write(&req, &mut buf).unwrap();
struct CancelOnDrop<'a>(&'a Shared);
impl CancelOnDrop<'_> {
fn defuse(self) {
core::mem::forget(self);
}
}
impl Drop for CancelOnDrop<'_> {
fn drop(&mut self) {
self.0.ioctl_cancel();
}
}
let ioctl = CancelOnDrop(self.shared);
let resp_len = ioctl.0.ioctl(&mut buf, req_len).await;
ioctl.defuse();
let res = noproto::read(&buf[..resp_len]).unwrap();
debug!("ioctl resp: {:?}", &res);
res
}
}

View File

@ -0,0 +1,432 @@
syntax = "proto3";
/* Enums similar to ESP IDF */
enum Ctrl_VendorIEType {
Beacon = 0;
Probe_req = 1;
Probe_resp = 2;
Assoc_req = 3;
Assoc_resp = 4;
}
enum Ctrl_VendorIEID {
ID_0 = 0;
ID_1 = 1;
}
enum Ctrl_WifiMode {
NONE = 0;
STA = 1;
AP = 2;
APSTA = 3;
}
enum Ctrl_WifiBw {
BW_Invalid = 0;
HT20 = 1;
HT40 = 2;
}
enum Ctrl_WifiPowerSave {
PS_Invalid = 0;
MIN_MODEM = 1;
MAX_MODEM = 2;
}
enum Ctrl_WifiSecProt {
Open = 0;
WEP = 1;
WPA_PSK = 2;
WPA2_PSK = 3;
WPA_WPA2_PSK = 4;
WPA2_ENTERPRISE = 5;
WPA3_PSK = 6;
WPA2_WPA3_PSK = 7;
}
/* enums for Control path */
enum Ctrl_Status {
Connected = 0;
Not_Connected = 1;
No_AP_Found = 2;
Connection_Fail = 3;
Invalid_Argument = 4;
Out_Of_Range = 5;
}
enum CtrlMsgType {
MsgType_Invalid = 0;
Req = 1;
Resp = 2;
Event = 3;
MsgType_Max = 4;
}
enum CtrlMsgId {
MsgId_Invalid = 0;
/** Request Msgs **/
Req_Base = 100;
Req_GetMACAddress = 101;
Req_SetMacAddress = 102;
Req_GetWifiMode = 103;
Req_SetWifiMode = 104;
Req_GetAPScanList = 105;
Req_GetAPConfig = 106;
Req_ConnectAP = 107;
Req_DisconnectAP = 108;
Req_GetSoftAPConfig = 109;
Req_SetSoftAPVendorSpecificIE = 110;
Req_StartSoftAP = 111;
Req_GetSoftAPConnectedSTAList = 112;
Req_StopSoftAP = 113;
Req_SetPowerSaveMode = 114;
Req_GetPowerSaveMode = 115;
Req_OTABegin = 116;
Req_OTAWrite = 117;
Req_OTAEnd = 118;
Req_SetWifiMaxTxPower = 119;
Req_GetWifiCurrTxPower = 120;
Req_ConfigHeartbeat = 121;
/* Add new control path command response before Req_Max
* and update Req_Max */
Req_Max = 122;
/** Response Msgs **/
Resp_Base = 200;
Resp_GetMACAddress = 201;
Resp_SetMacAddress = 202;
Resp_GetWifiMode = 203;
Resp_SetWifiMode = 204;
Resp_GetAPScanList = 205;
Resp_GetAPConfig = 206;
Resp_ConnectAP = 207;
Resp_DisconnectAP = 208;
Resp_GetSoftAPConfig = 209;
Resp_SetSoftAPVendorSpecificIE = 210;
Resp_StartSoftAP = 211;
Resp_GetSoftAPConnectedSTAList = 212;
Resp_StopSoftAP = 213;
Resp_SetPowerSaveMode = 214;
Resp_GetPowerSaveMode = 215;
Resp_OTABegin = 216;
Resp_OTAWrite = 217;
Resp_OTAEnd = 218;
Resp_SetWifiMaxTxPower = 219;
Resp_GetWifiCurrTxPower = 220;
Resp_ConfigHeartbeat = 221;
/* Add new control path command response before Resp_Max
* and update Resp_Max */
Resp_Max = 222;
/** Event Msgs **/
Event_Base = 300;
Event_ESPInit = 301;
Event_Heartbeat = 302;
Event_StationDisconnectFromAP = 303;
Event_StationDisconnectFromESPSoftAP = 304;
/* Add new control path command notification before Event_Max
* and update Event_Max */
Event_Max = 305;
}
/* internal supporting structures for CtrlMsg */
message ScanResult {
bytes ssid = 1;
uint32 chnl = 2;
int32 rssi = 3;
bytes bssid = 4;
Ctrl_WifiSecProt sec_prot = 5;
}
message ConnectedSTAList {
bytes mac = 1;
int32 rssi = 2;
}
/* Control path structures */
/** Req/Resp structure **/
message CtrlMsg_Req_GetMacAddress {
int32 mode = 1;
}
message CtrlMsg_Resp_GetMacAddress {
bytes mac = 1;
int32 resp = 2;
}
message CtrlMsg_Req_GetMode {
}
message CtrlMsg_Resp_GetMode {
int32 mode = 1;
int32 resp = 2;
}
message CtrlMsg_Req_SetMode {
int32 mode = 1;
}
message CtrlMsg_Resp_SetMode {
int32 resp = 1;
}
message CtrlMsg_Req_GetStatus {
}
message CtrlMsg_Resp_GetStatus {
int32 resp = 1;
}
message CtrlMsg_Req_SetMacAddress {
bytes mac = 1;
int32 mode = 2;
}
message CtrlMsg_Resp_SetMacAddress {
int32 resp = 1;
}
message CtrlMsg_Req_GetAPConfig {
}
message CtrlMsg_Resp_GetAPConfig {
bytes ssid = 1;
bytes bssid = 2;
int32 rssi = 3;
int32 chnl = 4;
Ctrl_WifiSecProt sec_prot = 5;
int32 resp = 6;
}
message CtrlMsg_Req_ConnectAP {
string ssid = 1;
string pwd = 2;
string bssid = 3;
bool is_wpa3_supported = 4;
int32 listen_interval = 5;
}
message CtrlMsg_Resp_ConnectAP {
int32 resp = 1;
bytes mac = 2;
}
message CtrlMsg_Req_GetSoftAPConfig {
}
message CtrlMsg_Resp_GetSoftAPConfig {
bytes ssid = 1;
bytes pwd = 2;
int32 chnl = 3;
Ctrl_WifiSecProt sec_prot = 4;
int32 max_conn = 5;
bool ssid_hidden = 6;
int32 bw = 7;
int32 resp = 8;
}
message CtrlMsg_Req_StartSoftAP {
string ssid = 1;
string pwd = 2;
int32 chnl = 3;
Ctrl_WifiSecProt sec_prot = 4;
int32 max_conn = 5;
bool ssid_hidden = 6;
int32 bw = 7;
}
message CtrlMsg_Resp_StartSoftAP {
int32 resp = 1;
bytes mac = 2;
}
message CtrlMsg_Req_ScanResult {
}
message CtrlMsg_Resp_ScanResult {
uint32 count = 1;
repeated ScanResult entries = 2;
int32 resp = 3;
}
message CtrlMsg_Req_SoftAPConnectedSTA {
}
message CtrlMsg_Resp_SoftAPConnectedSTA {
uint32 num = 1;
repeated ConnectedSTAList stations = 2;
int32 resp = 3;
}
message CtrlMsg_Req_OTABegin {
}
message CtrlMsg_Resp_OTABegin {
int32 resp = 1;
}
message CtrlMsg_Req_OTAWrite {
bytes ota_data = 1;
}
message CtrlMsg_Resp_OTAWrite {
int32 resp = 1;
}
message CtrlMsg_Req_OTAEnd {
}
message CtrlMsg_Resp_OTAEnd {
int32 resp = 1;
}
message CtrlMsg_Req_VendorIEData {
int32 element_id = 1;
int32 length = 2;
bytes vendor_oui = 3;
int32 vendor_oui_type = 4;
bytes payload = 5;
}
message CtrlMsg_Req_SetSoftAPVendorSpecificIE {
bool enable = 1;
Ctrl_VendorIEType type = 2;
Ctrl_VendorIEID idx = 3;
CtrlMsg_Req_VendorIEData vendor_ie_data = 4;
}
message CtrlMsg_Resp_SetSoftAPVendorSpecificIE {
int32 resp = 1;
}
message CtrlMsg_Req_SetWifiMaxTxPower {
int32 wifi_max_tx_power = 1;
}
message CtrlMsg_Resp_SetWifiMaxTxPower {
int32 resp = 1;
}
message CtrlMsg_Req_GetWifiCurrTxPower {
}
message CtrlMsg_Resp_GetWifiCurrTxPower {
int32 wifi_curr_tx_power = 1;
int32 resp = 2;
}
message CtrlMsg_Req_ConfigHeartbeat {
bool enable = 1;
int32 duration = 2;
}
message CtrlMsg_Resp_ConfigHeartbeat {
int32 resp = 1;
}
/** Event structure **/
message CtrlMsg_Event_ESPInit {
bytes init_data = 1;
}
message CtrlMsg_Event_Heartbeat {
int32 hb_num = 1;
}
message CtrlMsg_Event_StationDisconnectFromAP {
int32 resp = 1;
}
message CtrlMsg_Event_StationDisconnectFromESPSoftAP {
int32 resp = 1;
bytes mac = 2;
}
message CtrlMsg {
/* msg_type could be req, resp or Event */
CtrlMsgType msg_type = 1;
/* msg id */
CtrlMsgId msg_id = 2;
/* union of all msg ids */
oneof payload {
/** Requests **/
CtrlMsg_Req_GetMacAddress req_get_mac_address = 101;
CtrlMsg_Req_SetMacAddress req_set_mac_address = 102;
CtrlMsg_Req_GetMode req_get_wifi_mode = 103;
CtrlMsg_Req_SetMode req_set_wifi_mode = 104;
CtrlMsg_Req_ScanResult req_scan_ap_list = 105;
CtrlMsg_Req_GetAPConfig req_get_ap_config = 106;
CtrlMsg_Req_ConnectAP req_connect_ap = 107;
CtrlMsg_Req_GetStatus req_disconnect_ap = 108;
CtrlMsg_Req_GetSoftAPConfig req_get_softap_config = 109;
CtrlMsg_Req_SetSoftAPVendorSpecificIE req_set_softap_vendor_specific_ie = 110;
CtrlMsg_Req_StartSoftAP req_start_softap = 111;
CtrlMsg_Req_SoftAPConnectedSTA req_softap_connected_stas_list = 112;
CtrlMsg_Req_GetStatus req_stop_softap = 113;
CtrlMsg_Req_SetMode req_set_power_save_mode = 114;
CtrlMsg_Req_GetMode req_get_power_save_mode = 115;
CtrlMsg_Req_OTABegin req_ota_begin = 116;
CtrlMsg_Req_OTAWrite req_ota_write = 117;
CtrlMsg_Req_OTAEnd req_ota_end = 118;
CtrlMsg_Req_SetWifiMaxTxPower req_set_wifi_max_tx_power = 119;
CtrlMsg_Req_GetWifiCurrTxPower req_get_wifi_curr_tx_power = 120;
CtrlMsg_Req_ConfigHeartbeat req_config_heartbeat = 121;
/** Responses **/
CtrlMsg_Resp_GetMacAddress resp_get_mac_address = 201;
CtrlMsg_Resp_SetMacAddress resp_set_mac_address = 202;
CtrlMsg_Resp_GetMode resp_get_wifi_mode = 203;
CtrlMsg_Resp_SetMode resp_set_wifi_mode = 204;
CtrlMsg_Resp_ScanResult resp_scan_ap_list = 205;
CtrlMsg_Resp_GetAPConfig resp_get_ap_config = 206;
CtrlMsg_Resp_ConnectAP resp_connect_ap = 207;
CtrlMsg_Resp_GetStatus resp_disconnect_ap = 208;
CtrlMsg_Resp_GetSoftAPConfig resp_get_softap_config = 209;
CtrlMsg_Resp_SetSoftAPVendorSpecificIE resp_set_softap_vendor_specific_ie = 210;
CtrlMsg_Resp_StartSoftAP resp_start_softap = 211;
CtrlMsg_Resp_SoftAPConnectedSTA resp_softap_connected_stas_list = 212;
CtrlMsg_Resp_GetStatus resp_stop_softap = 213;
CtrlMsg_Resp_SetMode resp_set_power_save_mode = 214;
CtrlMsg_Resp_GetMode resp_get_power_save_mode = 215;
CtrlMsg_Resp_OTABegin resp_ota_begin = 216;
CtrlMsg_Resp_OTAWrite resp_ota_write = 217;
CtrlMsg_Resp_OTAEnd resp_ota_end = 218;
CtrlMsg_Resp_SetWifiMaxTxPower resp_set_wifi_max_tx_power = 219;
CtrlMsg_Resp_GetWifiCurrTxPower resp_get_wifi_curr_tx_power = 220;
CtrlMsg_Resp_ConfigHeartbeat resp_config_heartbeat = 221;
/** Notifications **/
CtrlMsg_Event_ESPInit event_esp_init = 301;
CtrlMsg_Event_Heartbeat event_heartbeat = 302;
CtrlMsg_Event_StationDisconnectFromAP event_station_disconnect_from_AP = 303;
CtrlMsg_Event_StationDisconnectFromESPSoftAP event_station_disconnect_from_ESP_SoftAP = 304;
}
}

View File

@ -0,0 +1,257 @@
#![macro_use]
#![allow(unused_macros)]
use core::fmt::{Debug, Display, LowerHex};
#[cfg(all(feature = "defmt", feature = "log"))]
compile_error!("You may not enable both `defmt` and `log` features.");
macro_rules! assert {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::assert!($($x)*);
#[cfg(feature = "defmt")]
::defmt::assert!($($x)*);
}
};
}
macro_rules! assert_eq {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::assert_eq!($($x)*);
#[cfg(feature = "defmt")]
::defmt::assert_eq!($($x)*);
}
};
}
macro_rules! assert_ne {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::assert_ne!($($x)*);
#[cfg(feature = "defmt")]
::defmt::assert_ne!($($x)*);
}
};
}
macro_rules! debug_assert {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::debug_assert!($($x)*);
#[cfg(feature = "defmt")]
::defmt::debug_assert!($($x)*);
}
};
}
macro_rules! debug_assert_eq {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::debug_assert_eq!($($x)*);
#[cfg(feature = "defmt")]
::defmt::debug_assert_eq!($($x)*);
}
};
}
macro_rules! debug_assert_ne {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::debug_assert_ne!($($x)*);
#[cfg(feature = "defmt")]
::defmt::debug_assert_ne!($($x)*);
}
};
}
macro_rules! todo {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::todo!($($x)*);
#[cfg(feature = "defmt")]
::defmt::todo!($($x)*);
}
};
}
#[cfg(not(feature = "defmt"))]
macro_rules! unreachable {
($($x:tt)*) => {
::core::unreachable!($($x)*)
};
}
#[cfg(feature = "defmt")]
macro_rules! unreachable {
($($x:tt)*) => {
::defmt::unreachable!($($x)*);
};
}
macro_rules! panic {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::panic!($($x)*);
#[cfg(feature = "defmt")]
::defmt::panic!($($x)*);
}
};
}
macro_rules! trace {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::trace!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::trace!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
macro_rules! debug {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::debug!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::debug!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
macro_rules! info {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::info!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::info!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
macro_rules! warn {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::warn!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::warn!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
macro_rules! error {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::error!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::error!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
#[cfg(feature = "defmt")]
macro_rules! unwrap {
($($x:tt)*) => {
::defmt::unwrap!($($x)*)
};
}
#[cfg(not(feature = "defmt"))]
macro_rules! unwrap {
($arg:expr) => {
match $crate::fmt::Try::into_result($arg) {
::core::result::Result::Ok(t) => t,
::core::result::Result::Err(e) => {
::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
}
}
};
($arg:expr, $($msg:expr),+ $(,)? ) => {
match $crate::fmt::Try::into_result($arg) {
::core::result::Result::Ok(t) => t,
::core::result::Result::Err(e) => {
::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
}
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct NoneError;
pub trait Try {
type Ok;
type Error;
fn into_result(self) -> Result<Self::Ok, Self::Error>;
}
impl<T> Try for Option<T> {
type Ok = T;
type Error = NoneError;
#[inline]
fn into_result(self) -> Result<T, NoneError> {
self.ok_or(NoneError)
}
}
impl<T, E> Try for Result<T, E> {
type Ok = T;
type Error = E;
#[inline]
fn into_result(self) -> Self {
self
}
}
pub struct Bytes<'a>(pub &'a [u8]);
impl<'a> Debug for Bytes<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:#02x?}", self.0)
}
}
impl<'a> Display for Bytes<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:#02x?}", self.0)
}
}
impl<'a> LowerHex for Bytes<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:#02x?}", self.0)
}
}
#[cfg(feature = "defmt")]
impl<'a> defmt::Format for Bytes<'a> {
fn format(&self, fmt: defmt::Formatter) {
defmt::write!(fmt, "{:02x}", self.0)
}
}

View File

@ -0,0 +1,123 @@
use core::cell::RefCell;
use core::future::poll_fn;
use core::task::Poll;
use embassy_sync::waitqueue::WakerRegistration;
use crate::fmt::Bytes;
#[derive(Clone, Copy)]
pub struct PendingIoctl {
pub buf: *mut [u8],
pub req_len: usize,
}
#[derive(Clone, Copy)]
enum IoctlState {
Pending(PendingIoctl),
Sent { buf: *mut [u8] },
Done { resp_len: usize },
}
pub struct Shared(RefCell<SharedInner>);
struct SharedInner {
ioctl: IoctlState,
is_init: bool,
control_waker: WakerRegistration,
runner_waker: WakerRegistration,
}
impl Shared {
pub fn new() -> Self {
Self(RefCell::new(SharedInner {
ioctl: IoctlState::Done { resp_len: 0 },
is_init: false,
control_waker: WakerRegistration::new(),
runner_waker: WakerRegistration::new(),
}))
}
pub async fn ioctl_wait_complete(&self) -> usize {
poll_fn(|cx| {
let mut this = self.0.borrow_mut();
if let IoctlState::Done { resp_len } = this.ioctl {
Poll::Ready(resp_len)
} else {
this.control_waker.register(cx.waker());
Poll::Pending
}
})
.await
}
pub async fn ioctl_wait_pending(&self) -> PendingIoctl {
let pending = poll_fn(|cx| {
let mut this = self.0.borrow_mut();
if let IoctlState::Pending(pending) = this.ioctl {
Poll::Ready(pending)
} else {
this.runner_waker.register(cx.waker());
Poll::Pending
}
})
.await;
self.0.borrow_mut().ioctl = IoctlState::Sent { buf: pending.buf };
pending
}
pub fn ioctl_cancel(&self) {
self.0.borrow_mut().ioctl = IoctlState::Done { resp_len: 0 };
}
pub async fn ioctl(&self, buf: &mut [u8], req_len: usize) -> usize {
trace!("ioctl req bytes: {:02x}", Bytes(&buf[..req_len]));
{
let mut this = self.0.borrow_mut();
this.ioctl = IoctlState::Pending(PendingIoctl { buf, req_len });
this.runner_waker.wake();
}
self.ioctl_wait_complete().await
}
pub fn ioctl_done(&self, response: &[u8]) {
let mut this = self.0.borrow_mut();
if let IoctlState::Sent { buf } = this.ioctl {
trace!("ioctl resp bytes: {:02x}", Bytes(response));
// TODO fix this
(unsafe { &mut *buf }[..response.len()]).copy_from_slice(response);
this.ioctl = IoctlState::Done {
resp_len: response.len(),
};
this.control_waker.wake();
} else {
warn!("IOCTL Response but no pending Ioctl");
}
}
// // // // // // // // // // // // // // // // // // // //
pub fn init_done(&self) {
let mut this = self.0.borrow_mut();
this.is_init = true;
this.control_waker.wake();
}
pub async fn init_wait(&self) {
poll_fn(|cx| {
let mut this = self.0.borrow_mut();
if this.is_init {
Poll::Ready(())
} else {
this.control_waker.register(cx.waker());
Poll::Pending
}
})
.await
}
}

View File

@ -0,0 +1,337 @@
#![no_std]
use control::Control;
use embassy_futures::select::{select3, Either3};
use embassy_net_driver_channel as ch;
use embassy_time::{Duration, Instant, Timer};
use embedded_hal::digital::{InputPin, OutputPin};
use embedded_hal_async::digital::Wait;
use embedded_hal_async::spi::SpiDevice;
use ioctl::Shared;
use proto::CtrlMsg;
use crate::ioctl::PendingIoctl;
use crate::proto::CtrlMsgPayload;
mod proto;
// must be first
mod fmt;
mod control;
mod ioctl;
const MTU: usize = 1514;
macro_rules! impl_bytes {
($t:ident) => {
impl $t {
pub const SIZE: usize = core::mem::size_of::<Self>();
#[allow(unused)]
pub fn to_bytes(&self) -> [u8; Self::SIZE] {
unsafe { core::mem::transmute(*self) }
}
#[allow(unused)]
pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> &Self {
let alignment = core::mem::align_of::<Self>();
assert_eq!(
bytes.as_ptr().align_offset(alignment),
0,
"{} is not aligned",
core::any::type_name::<Self>()
);
unsafe { core::mem::transmute(bytes) }
}
#[allow(unused)]
pub fn from_bytes_mut(bytes: &mut [u8; Self::SIZE]) -> &mut Self {
let alignment = core::mem::align_of::<Self>();
assert_eq!(
bytes.as_ptr().align_offset(alignment),
0,
"{} is not aligned",
core::any::type_name::<Self>()
);
unsafe { core::mem::transmute(bytes) }
}
}
};
}
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Default)]
struct PayloadHeader {
/// InterfaceType on lower 4 bits, number on higher 4 bits.
if_type_and_num: u8,
/// Flags.
///
/// bit 0: more fragments.
flags: u8,
len: u16,
offset: u16,
checksum: u16,
seq_num: u16,
reserved2: u8,
/// Packet type for HCI or PRIV interface, reserved otherwise
hci_priv_packet_type: u8,
}
impl_bytes!(PayloadHeader);
#[allow(unused)]
#[repr(u8)]
enum InterfaceType {
Sta = 0,
Ap = 1,
Serial = 2,
Hci = 3,
Priv = 4,
Test = 5,
}
const MAX_SPI_BUFFER_SIZE: usize = 1600;
pub struct State {
shared: Shared,
ch: ch::State<MTU, 4, 4>,
}
impl State {
pub fn new() -> Self {
Self {
shared: Shared::new(),
ch: ch::State::new(),
}
}
}
pub type NetDriver<'a> = ch::Device<'a, MTU>;
pub async fn new<'a, SPI, IN, OUT>(
state: &'a mut State,
spi: SPI,
handshake: IN,
ready: IN,
reset: OUT,
) -> (NetDriver<'a>, Control<'a>, Runner<'a, SPI, IN, OUT>)
where
SPI: SpiDevice,
IN: InputPin + Wait,
OUT: OutputPin,
{
let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]);
let state_ch = ch_runner.state_runner();
let mut runner = Runner {
ch: ch_runner,
shared: &state.shared,
next_seq: 1,
handshake,
ready,
reset,
spi,
};
runner.init().await;
(device, Control::new(state_ch, &state.shared), runner)
}
pub struct Runner<'a, SPI, IN, OUT> {
ch: ch::Runner<'a, MTU>,
shared: &'a Shared,
next_seq: u16,
spi: SPI,
handshake: IN,
ready: IN,
reset: OUT,
}
impl<'a, SPI, IN, OUT> Runner<'a, SPI, IN, OUT>
where
SPI: SpiDevice,
IN: InputPin + Wait,
OUT: OutputPin,
{
async fn init(&mut self) {}
pub async fn run(mut self) -> ! {
debug!("resetting...");
self.reset.set_low().unwrap();
Timer::after(Duration::from_millis(100)).await;
self.reset.set_high().unwrap();
Timer::after(Duration::from_millis(1000)).await;
let mut tx_buf = [0u8; MAX_SPI_BUFFER_SIZE];
let mut rx_buf = [0u8; MAX_SPI_BUFFER_SIZE];
loop {
self.handshake.wait_for_high().await.unwrap();
let ioctl = self.shared.ioctl_wait_pending();
let tx = self.ch.tx_buf();
let ev = async { self.ready.wait_for_high().await.unwrap() };
match select3(ioctl, tx, ev).await {
Either3::First(PendingIoctl { buf, req_len }) => {
tx_buf[12..24].copy_from_slice(b"\x01\x08\x00ctrlResp\x02");
tx_buf[24..26].copy_from_slice(&(req_len as u16).to_le_bytes());
tx_buf[26..][..req_len].copy_from_slice(&unsafe { &*buf }[..req_len]);
let mut header = PayloadHeader {
if_type_and_num: InterfaceType::Serial as _,
len: (req_len + 14) as _,
offset: PayloadHeader::SIZE as _,
seq_num: self.next_seq,
..Default::default()
};
self.next_seq = self.next_seq.wrapping_add(1);
// Calculate checksum
tx_buf[0..12].copy_from_slice(&header.to_bytes());
header.checksum = checksum(&tx_buf[..26 + req_len]);
tx_buf[0..12].copy_from_slice(&header.to_bytes());
}
Either3::Second(packet) => {
tx_buf[12..][..packet.len()].copy_from_slice(packet);
let mut header = PayloadHeader {
if_type_and_num: InterfaceType::Sta as _,
len: packet.len() as _,
offset: PayloadHeader::SIZE as _,
seq_num: self.next_seq,
..Default::default()
};
self.next_seq = self.next_seq.wrapping_add(1);
// Calculate checksum
tx_buf[0..12].copy_from_slice(&header.to_bytes());
header.checksum = checksum(&tx_buf[..12 + packet.len()]);
tx_buf[0..12].copy_from_slice(&header.to_bytes());
self.ch.tx_done();
}
Either3::Third(()) => {
tx_buf[..PayloadHeader::SIZE].fill(0);
}
}
if tx_buf[0] != 0 {
trace!("tx: {:02x}", &tx_buf[..40]);
}
self.spi.transfer(&mut rx_buf, &tx_buf).await.unwrap();
// The esp-hosted firmware deasserts the HANSHAKE pin a few us AFTER ending the SPI transfer
// If we check it again too fast, we'll see it's high from the previous transfer, and if we send it
// data it will get lost.
// Make sure we check it after 100us at minimum.
let delay_until = Instant::now() + Duration::from_micros(100);
self.handle_rx(&mut rx_buf);
Timer::at(delay_until).await;
}
}
fn handle_rx(&mut self, buf: &mut [u8]) {
trace!("rx: {:02x}", &buf[..40]);
let buf_len = buf.len();
let h = PayloadHeader::from_bytes_mut((&mut buf[..PayloadHeader::SIZE]).try_into().unwrap());
if h.len == 0 || h.offset as usize != PayloadHeader::SIZE {
return;
}
let payload_len = h.len as usize;
if buf_len < PayloadHeader::SIZE + payload_len {
warn!("rx: len too big");
return;
}
let if_type_and_num = h.if_type_and_num;
let want_checksum = h.checksum;
h.checksum = 0;
let got_checksum = checksum(&buf[..PayloadHeader::SIZE + payload_len]);
if want_checksum != got_checksum {
warn!("rx: bad checksum. Got {:04x}, want {:04x}", got_checksum, want_checksum);
return;
}
let payload = &mut buf[PayloadHeader::SIZE..][..payload_len];
match if_type_and_num & 0x0f {
// STA
0 => match self.ch.try_rx_buf() {
Some(buf) => {
buf[..payload.len()].copy_from_slice(payload);
self.ch.rx_done(payload.len())
}
None => warn!("failed to push rxd packet to the channel."),
},
// serial
2 => {
trace!("serial rx: {:02x}", payload);
if payload.len() < 14 {
warn!("serial rx: too short");
return;
}
let is_event = match &payload[..12] {
b"\x01\x08\x00ctrlResp\x02" => false,
b"\x01\x08\x00ctrlEvnt\x02" => true,
_ => {
warn!("serial rx: bad tlv");
return;
}
};
let len = u16::from_le_bytes(payload[12..14].try_into().unwrap()) as usize;
if payload.len() < 14 + len {
warn!("serial rx: too short 2");
return;
}
let data = &payload[14..][..len];
if is_event {
self.handle_event(data);
} else {
self.shared.ioctl_done(data);
}
}
_ => warn!("unknown iftype {}", if_type_and_num),
}
}
fn handle_event(&self, data: &[u8]) {
let Ok(event) = noproto::read::<CtrlMsg>(data) else {
warn!("failed to parse event");
return
};
debug!("event: {:?}", &event);
let Some(payload) = &event.payload else {
warn!("event without payload?");
return
};
match payload {
CtrlMsgPayload::EventEspInit(_) => self.shared.init_done(),
_ => {}
}
}
}
fn checksum(buf: &[u8]) -> u16 {
let mut res = 0u16;
for &b in buf {
res = res.wrapping_add(b as _);
}
res
}

View File

@ -0,0 +1,652 @@
use heapless::{String, Vec};
/// internal supporting structures for CtrlMsg
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ScanResult {
#[noproto(tag = "1")]
pub ssid: String<32>,
#[noproto(tag = "2")]
pub chnl: u32,
#[noproto(tag = "3")]
pub rssi: u32,
#[noproto(tag = "4")]
pub bssid: String<32>,
#[noproto(tag = "5")]
pub sec_prot: CtrlWifiSecProt,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ConnectedStaList {
#[noproto(tag = "1")]
pub mac: String<32>,
#[noproto(tag = "2")]
pub rssi: u32,
}
/// * Req/Resp structure *
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgReqGetMacAddress {
#[noproto(tag = "1")]
pub mode: u32,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgRespGetMacAddress {
#[noproto(tag = "1")]
pub mac: String<32>,
#[noproto(tag = "2")]
pub resp: u32,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgReqGetMode {}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgRespGetMode {
#[noproto(tag = "1")]
pub mode: u32,
#[noproto(tag = "2")]
pub resp: u32,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgReqSetMode {
#[noproto(tag = "1")]
pub mode: u32,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgRespSetMode {
#[noproto(tag = "1")]
pub resp: u32,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgReqGetStatus {}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgRespGetStatus {
#[noproto(tag = "1")]
pub resp: u32,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgReqSetMacAddress {
#[noproto(tag = "1")]
pub mac: String<32>,
#[noproto(tag = "2")]
pub mode: u32,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgRespSetMacAddress {
#[noproto(tag = "1")]
pub resp: u32,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgReqGetApConfig {}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgRespGetApConfig {
#[noproto(tag = "1")]
pub ssid: String<32>,
#[noproto(tag = "2")]
pub bssid: String<32>,
#[noproto(tag = "3")]
pub rssi: u32,
#[noproto(tag = "4")]
pub chnl: u32,
#[noproto(tag = "5")]
pub sec_prot: CtrlWifiSecProt,
#[noproto(tag = "6")]
pub resp: u32,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgReqConnectAp {
#[noproto(tag = "1")]
pub ssid: String<32>,
#[noproto(tag = "2")]
pub pwd: String<32>,
#[noproto(tag = "3")]
pub bssid: String<32>,
#[noproto(tag = "4")]
pub is_wpa3_supported: bool,
#[noproto(tag = "5")]
pub listen_interval: u32,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgRespConnectAp {
#[noproto(tag = "1")]
pub resp: u32,
#[noproto(tag = "2")]
pub mac: String<32>,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgReqGetSoftApConfig {}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgRespGetSoftApConfig {
#[noproto(tag = "1")]
pub ssid: String<32>,
#[noproto(tag = "2")]
pub pwd: String<32>,
#[noproto(tag = "3")]
pub chnl: u32,
#[noproto(tag = "4")]
pub sec_prot: CtrlWifiSecProt,
#[noproto(tag = "5")]
pub max_conn: u32,
#[noproto(tag = "6")]
pub ssid_hidden: bool,
#[noproto(tag = "7")]
pub bw: u32,
#[noproto(tag = "8")]
pub resp: u32,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgReqStartSoftAp {
#[noproto(tag = "1")]
pub ssid: String<32>,
#[noproto(tag = "2")]
pub pwd: String<32>,
#[noproto(tag = "3")]
pub chnl: u32,
#[noproto(tag = "4")]
pub sec_prot: CtrlWifiSecProt,
#[noproto(tag = "5")]
pub max_conn: u32,
#[noproto(tag = "6")]
pub ssid_hidden: bool,
#[noproto(tag = "7")]
pub bw: u32,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgRespStartSoftAp {
#[noproto(tag = "1")]
pub resp: u32,
#[noproto(tag = "2")]
pub mac: String<32>,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgReqScanResult {}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgRespScanResult {
#[noproto(tag = "1")]
pub count: u32,
#[noproto(repeated, tag = "2")]
pub entries: Vec<ScanResult, 16>,
#[noproto(tag = "3")]
pub resp: u32,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgReqSoftApConnectedSta {}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgRespSoftApConnectedSta {
#[noproto(tag = "1")]
pub num: u32,
#[noproto(repeated, tag = "2")]
pub stations: Vec<ConnectedStaList, 16>,
#[noproto(tag = "3")]
pub resp: u32,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgReqOtaBegin {}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgRespOtaBegin {
#[noproto(tag = "1")]
pub resp: u32,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgReqOtaWrite {
#[noproto(tag = "1")]
pub ota_data: Vec<u8, 1024>,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgRespOtaWrite {
#[noproto(tag = "1")]
pub resp: u32,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgReqOtaEnd {}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgRespOtaEnd {
#[noproto(tag = "1")]
pub resp: u32,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgReqVendorIeData {
#[noproto(tag = "1")]
pub element_id: u32,
#[noproto(tag = "2")]
pub length: u32,
#[noproto(tag = "3")]
pub vendor_oui: Vec<u8, 8>,
#[noproto(tag = "4")]
pub vendor_oui_type: u32,
#[noproto(tag = "5")]
pub payload: Vec<u8, 64>,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgReqSetSoftApVendorSpecificIe {
#[noproto(tag = "1")]
pub enable: bool,
#[noproto(tag = "2")]
pub r#type: CtrlVendorIeType,
#[noproto(tag = "3")]
pub idx: CtrlVendorIeid,
#[noproto(optional, tag = "4")]
pub vendor_ie_data: Option<CtrlMsgReqVendorIeData>,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgRespSetSoftApVendorSpecificIe {
#[noproto(tag = "1")]
pub resp: u32,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgReqSetWifiMaxTxPower {
#[noproto(tag = "1")]
pub wifi_max_tx_power: u32,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgRespSetWifiMaxTxPower {
#[noproto(tag = "1")]
pub resp: u32,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgReqGetWifiCurrTxPower {}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgRespGetWifiCurrTxPower {
#[noproto(tag = "1")]
pub wifi_curr_tx_power: u32,
#[noproto(tag = "2")]
pub resp: u32,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgReqConfigHeartbeat {
#[noproto(tag = "1")]
pub enable: bool,
#[noproto(tag = "2")]
pub duration: u32,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgRespConfigHeartbeat {
#[noproto(tag = "1")]
pub resp: u32,
}
/// * Event structure *
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgEventEspInit {
#[noproto(tag = "1")]
pub init_data: Vec<u8, 64>,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgEventHeartbeat {
#[noproto(tag = "1")]
pub hb_num: u32,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgEventStationDisconnectFromAp {
#[noproto(tag = "1")]
pub resp: u32,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsgEventStationDisconnectFromEspSoftAp {
#[noproto(tag = "1")]
pub resp: u32,
#[noproto(tag = "2")]
pub mac: String<32>,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, noproto::Message)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CtrlMsg {
/// msg_type could be req, resp or Event
#[noproto(tag = "1")]
pub msg_type: CtrlMsgType,
/// msg id
#[noproto(tag = "2")]
pub msg_id: CtrlMsgId,
/// union of all msg ids
#[noproto(
oneof,
tags = "101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 301, 302, 303, 304"
)]
pub payload: Option<CtrlMsgPayload>,
}
/// union of all msg ids
#[derive(Debug, Clone, Eq, PartialEq, noproto::Oneof)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CtrlMsgPayload {
/// * Requests *
#[noproto(tag = "101")]
ReqGetMacAddress(CtrlMsgReqGetMacAddress),
#[noproto(tag = "102")]
ReqSetMacAddress(CtrlMsgReqSetMacAddress),
#[noproto(tag = "103")]
ReqGetWifiMode(CtrlMsgReqGetMode),
#[noproto(tag = "104")]
ReqSetWifiMode(CtrlMsgReqSetMode),
#[noproto(tag = "105")]
ReqScanApList(CtrlMsgReqScanResult),
#[noproto(tag = "106")]
ReqGetApConfig(CtrlMsgReqGetApConfig),
#[noproto(tag = "107")]
ReqConnectAp(CtrlMsgReqConnectAp),
#[noproto(tag = "108")]
ReqDisconnectAp(CtrlMsgReqGetStatus),
#[noproto(tag = "109")]
ReqGetSoftapConfig(CtrlMsgReqGetSoftApConfig),
#[noproto(tag = "110")]
ReqSetSoftapVendorSpecificIe(CtrlMsgReqSetSoftApVendorSpecificIe),
#[noproto(tag = "111")]
ReqStartSoftap(CtrlMsgReqStartSoftAp),
#[noproto(tag = "112")]
ReqSoftapConnectedStasList(CtrlMsgReqSoftApConnectedSta),
#[noproto(tag = "113")]
ReqStopSoftap(CtrlMsgReqGetStatus),
#[noproto(tag = "114")]
ReqSetPowerSaveMode(CtrlMsgReqSetMode),
#[noproto(tag = "115")]
ReqGetPowerSaveMode(CtrlMsgReqGetMode),
#[noproto(tag = "116")]
ReqOtaBegin(CtrlMsgReqOtaBegin),
#[noproto(tag = "117")]
ReqOtaWrite(CtrlMsgReqOtaWrite),
#[noproto(tag = "118")]
ReqOtaEnd(CtrlMsgReqOtaEnd),
#[noproto(tag = "119")]
ReqSetWifiMaxTxPower(CtrlMsgReqSetWifiMaxTxPower),
#[noproto(tag = "120")]
ReqGetWifiCurrTxPower(CtrlMsgReqGetWifiCurrTxPower),
#[noproto(tag = "121")]
ReqConfigHeartbeat(CtrlMsgReqConfigHeartbeat),
/// * Responses *
#[noproto(tag = "201")]
RespGetMacAddress(CtrlMsgRespGetMacAddress),
#[noproto(tag = "202")]
RespSetMacAddress(CtrlMsgRespSetMacAddress),
#[noproto(tag = "203")]
RespGetWifiMode(CtrlMsgRespGetMode),
#[noproto(tag = "204")]
RespSetWifiMode(CtrlMsgRespSetMode),
#[noproto(tag = "205")]
RespScanApList(CtrlMsgRespScanResult),
#[noproto(tag = "206")]
RespGetApConfig(CtrlMsgRespGetApConfig),
#[noproto(tag = "207")]
RespConnectAp(CtrlMsgRespConnectAp),
#[noproto(tag = "208")]
RespDisconnectAp(CtrlMsgRespGetStatus),
#[noproto(tag = "209")]
RespGetSoftapConfig(CtrlMsgRespGetSoftApConfig),
#[noproto(tag = "210")]
RespSetSoftapVendorSpecificIe(CtrlMsgRespSetSoftApVendorSpecificIe),
#[noproto(tag = "211")]
RespStartSoftap(CtrlMsgRespStartSoftAp),
#[noproto(tag = "212")]
RespSoftapConnectedStasList(CtrlMsgRespSoftApConnectedSta),
#[noproto(tag = "213")]
RespStopSoftap(CtrlMsgRespGetStatus),
#[noproto(tag = "214")]
RespSetPowerSaveMode(CtrlMsgRespSetMode),
#[noproto(tag = "215")]
RespGetPowerSaveMode(CtrlMsgRespGetMode),
#[noproto(tag = "216")]
RespOtaBegin(CtrlMsgRespOtaBegin),
#[noproto(tag = "217")]
RespOtaWrite(CtrlMsgRespOtaWrite),
#[noproto(tag = "218")]
RespOtaEnd(CtrlMsgRespOtaEnd),
#[noproto(tag = "219")]
RespSetWifiMaxTxPower(CtrlMsgRespSetWifiMaxTxPower),
#[noproto(tag = "220")]
RespGetWifiCurrTxPower(CtrlMsgRespGetWifiCurrTxPower),
#[noproto(tag = "221")]
RespConfigHeartbeat(CtrlMsgRespConfigHeartbeat),
/// * Notifications *
#[noproto(tag = "301")]
EventEspInit(CtrlMsgEventEspInit),
#[noproto(tag = "302")]
EventHeartbeat(CtrlMsgEventHeartbeat),
#[noproto(tag = "303")]
EventStationDisconnectFromAp(CtrlMsgEventStationDisconnectFromAp),
#[noproto(tag = "304")]
EventStationDisconnectFromEspSoftAp(CtrlMsgEventStationDisconnectFromEspSoftAp),
}
/// Enums similar to ESP IDF
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)]
#[repr(u32)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CtrlVendorIeType {
#[default]
Beacon = 0,
ProbeReq = 1,
ProbeResp = 2,
AssocReq = 3,
AssocResp = 4,
}
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)]
#[repr(u32)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CtrlVendorIeid {
#[default]
Id0 = 0,
Id1 = 1,
}
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)]
#[repr(u32)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CtrlWifiMode {
#[default]
None = 0,
Sta = 1,
Ap = 2,
Apsta = 3,
}
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)]
#[repr(u32)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CtrlWifiBw {
#[default]
BwInvalid = 0,
Ht20 = 1,
Ht40 = 2,
}
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)]
#[repr(u32)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CtrlWifiPowerSave {
#[default]
PsInvalid = 0,
MinModem = 1,
MaxModem = 2,
}
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)]
#[repr(u32)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CtrlWifiSecProt {
#[default]
Open = 0,
Wep = 1,
WpaPsk = 2,
Wpa2Psk = 3,
WpaWpa2Psk = 4,
Wpa2Enterprise = 5,
Wpa3Psk = 6,
Wpa2Wpa3Psk = 7,
}
/// enums for Control path
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)]
#[repr(u32)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CtrlStatus {
#[default]
Connected = 0,
NotConnected = 1,
NoApFound = 2,
ConnectionFail = 3,
InvalidArgument = 4,
OutOfRange = 5,
}
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)]
#[repr(u32)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CtrlMsgType {
#[default]
MsgTypeInvalid = 0,
Req = 1,
Resp = 2,
Event = 3,
MsgTypeMax = 4,
}
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, noproto::Enumeration)]
#[repr(u32)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CtrlMsgId {
#[default]
MsgIdInvalid = 0,
/// * Request Msgs *
ReqBase = 100,
ReqGetMacAddress = 101,
ReqSetMacAddress = 102,
ReqGetWifiMode = 103,
ReqSetWifiMode = 104,
ReqGetApScanList = 105,
ReqGetApConfig = 106,
ReqConnectAp = 107,
ReqDisconnectAp = 108,
ReqGetSoftApConfig = 109,
ReqSetSoftApVendorSpecificIe = 110,
ReqStartSoftAp = 111,
ReqGetSoftApConnectedStaList = 112,
ReqStopSoftAp = 113,
ReqSetPowerSaveMode = 114,
ReqGetPowerSaveMode = 115,
ReqOtaBegin = 116,
ReqOtaWrite = 117,
ReqOtaEnd = 118,
ReqSetWifiMaxTxPower = 119,
ReqGetWifiCurrTxPower = 120,
ReqConfigHeartbeat = 121,
/// Add new control path command response before Req_Max
/// and update Req_Max
ReqMax = 122,
/// * Response Msgs *
RespBase = 200,
RespGetMacAddress = 201,
RespSetMacAddress = 202,
RespGetWifiMode = 203,
RespSetWifiMode = 204,
RespGetApScanList = 205,
RespGetApConfig = 206,
RespConnectAp = 207,
RespDisconnectAp = 208,
RespGetSoftApConfig = 209,
RespSetSoftApVendorSpecificIe = 210,
RespStartSoftAp = 211,
RespGetSoftApConnectedStaList = 212,
RespStopSoftAp = 213,
RespSetPowerSaveMode = 214,
RespGetPowerSaveMode = 215,
RespOtaBegin = 216,
RespOtaWrite = 217,
RespOtaEnd = 218,
RespSetWifiMaxTxPower = 219,
RespGetWifiCurrTxPower = 220,
RespConfigHeartbeat = 221,
/// Add new control path command response before Resp_Max
/// and update Resp_Max
RespMax = 222,
/// * Event Msgs *
EventBase = 300,
EventEspInit = 301,
EventHeartbeat = 302,
EventStationDisconnectFromAp = 303,
EventStationDisconnectFromEspSoftAp = 304,
/// Add new control path command notification before Event_Max
/// and update Event_Max
EventMax = 305,
}

View File

@ -1,5 +1,6 @@
//! [`embassy-net`](https://crates.io/crates/embassy-net) driver for the WIZnet W5500 ethernet chip.
#![no_std] #![no_std]
/// [`embassy-net`](crates.io/crates/embassy-net) driver for the WIZnet W5500 ethernet chip.
mod device; mod device;
mod socket; mod socket;
mod spi; mod spi;
@ -77,7 +78,7 @@ impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> {
} }
} }
/// Obtain a driver for using the W5500 with [`embassy-net`](crates.io/crates/embassy-net). /// Obtain a driver for using the W5500 with [`embassy-net`](https://crates.io/crates/embassy-net).
pub async fn new<'a, const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT: Wait, RST: OutputPin>( pub async fn new<'a, const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT: Wait, RST: OutputPin>(
mac_addr: [u8; 6], mac_addr: [u8; 6],
state: &'a mut State<N_RX, N_TX>, state: &'a mut State<N_RX, N_TX>,

View File

@ -3,7 +3,13 @@ name = "embassy-net"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
description = "Async TCP/IP network stack for embedded systems"
repository = "https://github.com/embassy-rs/embassy"
categories = [
"embedded",
"no-std",
"asynchronous",
]
[package.metadata.embassy_docs] [package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/" src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/"
@ -26,7 +32,8 @@ unstable-traits = []
udp = ["smoltcp/socket-udp"] udp = ["smoltcp/socket-udp"]
tcp = ["smoltcp/socket-tcp"] tcp = ["smoltcp/socket-tcp"]
dns = ["smoltcp/socket-dns", "smoltcp/proto-dns"] dns = ["smoltcp/socket-dns", "smoltcp/proto-dns"]
dhcpv4 = ["medium-ethernet", "smoltcp/socket-dhcpv4"] dhcpv4 = ["proto-ipv4", "medium-ethernet", "smoltcp/socket-dhcpv4"]
proto-ipv4 = ["smoltcp/proto-ipv4"]
proto-ipv6 = ["smoltcp/proto-ipv6"] proto-ipv6 = ["smoltcp/proto-ipv6"]
medium-ethernet = ["smoltcp/medium-ethernet"] medium-ethernet = ["smoltcp/medium-ethernet"]
medium-ip = ["smoltcp/medium-ip"] medium-ip = ["smoltcp/medium-ip"]
@ -37,14 +44,12 @@ igmp = ["smoltcp/proto-igmp"]
defmt = { version = "0.3", optional = true } defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true } log = { version = "0.4.14", optional = true }
smoltcp = { version = "0.9.0", default-features = false, features = [ smoltcp = { version = "0.10.0", default-features = false, features = [
"proto-ipv4",
"socket", "socket",
"async", "async",
]} ] }
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" }
embassy-time = { version = "0.1.0", path = "../embassy-time" } embassy-time = { version = "0.1.0", path = "../embassy-time" }
embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
embedded-io = { version = "0.4.0", optional = true } embedded-io = { version = "0.4.0", optional = true }

View File

@ -1,30 +1,56 @@
# embassy-net # embassy-net
embassy-net contains an async network API based on smoltcp and embassy, designed `embassy-net` is a no-std no-alloc async network stack, designed for embedded systems.
for embedded systems.
## Running the example It builds on [`smoltcp`](https://github.com/smoltcp-rs/smoltcp). It provides a higher-level and more opinionated
API. It glues together the components provided by `smoltcp`, handling the low-level details with defaults and
memory management designed to work well for embedded systems, aiiming for a more "Just Works" experience.
First, create the tap0 interface. You only need to do this once. ## Features
```sh - IPv4, IPv6
sudo ip tuntap add name tap0 mode tap user $USER - Ethernet and bare-IP mediums.
sudo ip link set tap0 up - TCP, UDP, DNS, DHCPv4, IGMPv4
sudo ip addr add 192.168.69.100/24 dev tap0 - TCP sockets implement the `embedded-io` async traits.
sudo ip -6 addr add fe80::100/64 dev tap0
sudo ip -6 addr add fdaa::100/64 dev tap0
sudo ip -6 route add fe80::/64 dev tap0
sudo ip -6 route add fdaa::/64 dev tap0
```
Second, have something listening there. For example `nc -l 8000` See the [`smoltcp`](https://github.com/smoltcp-rs/smoltcp) README for a detailed list of implemented and
unimplemented features of the network protocols.
Then run the example located in the `examples` folder: ## Hardware support
```sh - [`esp-wifi`](https://github.com/esp-rs/esp-wifi) for WiFi support on bare-metal ESP32 chips. Maintained by Espressif.
cd $EMBASSY_ROOT/examples/std/ - [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W
cargo run --bin net -- --static-ip - [`embassy-usb`](https://github.com/embassy-rs/embassy/tree/main/embassy-usb) for Ethernet-over-USB (CDC NCM) support.
``` - [`embassy-stm32`](https://github.com/embassy-rs/embassy/tree/main/embassy-stm32) for the builtin Ethernet MAC in all STM32 chips (STM32F1, STM32F2, STM32F4, STM32F7, STM32H7, STM32H5).
- [`embassy-net-w5500`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-w5500) for Wiznet W5500 SPI Ethernet MAC+PHY chip.
- [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU.
## Examples
- For usage with Embassy HALs and network chip drivers, search [here](https://github.com/embassy-rs/embassy/tree/main/examples) for `eth` or `wifi`.
- The [`esp-wifi` repo](https://github.com/esp-rs/esp-wifi) has examples for use on bare-metal ESP32 chips.
- For usage on `std` platforms, see [the `std` examples](https://github.com/embassy-rs/embassy/tree/main/examples/std/src/bin)
## Adding support for new hardware
To add `embassy-net` support for new hardware (i.e. a new Ethernet or WiFi chip, or
an Ethernet/WiFi MCU peripheral), you have to implement the [`embassy-net-driver`](https://crates.io/crates/embassy-net-driver)
traits.
Alternatively, [`embassy-net-driver-channel`](https://crates.io/crates/embassy-net-driver-channel) provides a higer-level API
to construct a driver that processes packets in its own background task and communicates with the `embassy-net` task via
packet queues for RX and TX.
Drivers should depend only on `embassy-net-driver` or `embassy-net-driver-channel`. Never on the main `embassy-net` crate.
This allows existing drivers to continue working for newer `embassy-net` major versions, without needing an update, if the driver
trait has not had breaking changes.
## Interoperability
This crate can run on any executor.
[`embassy-time`](https://crates.io/crates/embassy-net-driver) is used for timekeeping and timeouts. You must
link an `embassy-time` driver in your project to use this crate.
## License ## License

View File

@ -51,15 +51,19 @@ where
Medium::Ethernet => phy::Medium::Ethernet, Medium::Ethernet => phy::Medium::Ethernet,
#[cfg(feature = "medium-ip")] #[cfg(feature = "medium-ip")]
Medium::Ip => phy::Medium::Ip, Medium::Ip => phy::Medium::Ip,
#[allow(unreachable_patterns)]
_ => panic!( _ => panic!(
"Unsupported medium {:?}. MAke sure to enable it in embassy-net's Cargo features.", "Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.",
caps.medium caps.medium
), ),
}; };
smolcaps.checksum.ipv4 = convert(caps.checksum.ipv4); smolcaps.checksum.ipv4 = convert(caps.checksum.ipv4);
smolcaps.checksum.tcp = convert(caps.checksum.tcp); smolcaps.checksum.tcp = convert(caps.checksum.tcp);
smolcaps.checksum.udp = convert(caps.checksum.udp); smolcaps.checksum.udp = convert(caps.checksum.udp);
smolcaps.checksum.icmpv4 = convert(caps.checksum.icmpv4); #[cfg(feature = "proto-ipv4")]
{
smolcaps.checksum.icmpv4 = convert(caps.checksum.icmpv4);
}
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
{ {
smolcaps.checksum.icmpv6 = convert(caps.checksum.icmpv6); smolcaps.checksum.icmpv6 = convert(caps.checksum.icmpv6);

View File

@ -88,6 +88,7 @@ where
let addrs = self.query(host, qtype).await?; let addrs = self.query(host, qtype).await?;
if let Some(first) = addrs.get(0) { if let Some(first) = addrs.get(0) {
Ok(match first { Ok(match first {
#[cfg(feature = "proto-ipv4")]
IpAddress::Ipv4(addr) => IpAddr::V4(addr.0.into()), IpAddress::Ipv4(addr) => IpAddr::V4(addr.0.into()),
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
IpAddress::Ipv6(addr) => IpAddr::V6(addr.0.into()), IpAddress::Ipv6(addr) => IpAddr::V6(addr.0.into()),

View File

@ -34,7 +34,9 @@ use smoltcp::socket::dhcpv4::{self, RetryConfig};
pub use smoltcp::wire::IpListenEndpoint; pub use smoltcp::wire::IpListenEndpoint;
#[cfg(feature = "medium-ethernet")] #[cfg(feature = "medium-ethernet")]
pub use smoltcp::wire::{EthernetAddress, HardwareAddress}; pub use smoltcp::wire::{EthernetAddress, HardwareAddress};
pub use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}; pub use smoltcp::wire::{IpAddress, IpCidr};
#[cfg(feature = "proto-ipv4")]
pub use smoltcp::wire::{Ipv4Address, Ipv4Cidr};
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
pub use smoltcp::wire::{Ipv6Address, Ipv6Cidr}; pub use smoltcp::wire::{Ipv6Address, Ipv6Cidr};
@ -55,7 +57,7 @@ pub struct StackResources<const SOCK: usize> {
impl<const SOCK: usize> StackResources<SOCK> { impl<const SOCK: usize> StackResources<SOCK> {
/// Create a new set of stack resources. /// Create a new set of stack resources.
pub fn new() -> Self { pub const fn new() -> Self {
#[cfg(feature = "dns")] #[cfg(feature = "dns")]
const INIT: Option<dns::DnsQuery> = None; const INIT: Option<dns::DnsQuery> = None;
Self { Self {
@ -67,8 +69,9 @@ impl<const SOCK: usize> StackResources<SOCK> {
} }
/// Static IP address configuration. /// Static IP address configuration.
#[cfg(feature = "proto-ipv4")]
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct StaticConfig { pub struct StaticConfigV4 {
/// IP address and subnet mask. /// IP address and subnet mask.
pub address: Ipv4Cidr, pub address: Ipv4Cidr,
/// Default gateway. /// Default gateway.
@ -77,6 +80,18 @@ pub struct StaticConfig {
pub dns_servers: Vec<Ipv4Address, 3>, pub dns_servers: Vec<Ipv4Address, 3>,
} }
/// Static IPv6 address configuration
#[cfg(feature = "proto-ipv6")]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StaticConfigV6 {
/// IP address and subnet mask.
pub address: Ipv6Cidr,
/// Default gateway.
pub gateway: Option<Ipv6Address>,
/// DNS servers.
pub dns_servers: Vec<Ipv6Address, 3>,
}
/// DHCP configuration. /// DHCP configuration.
#[cfg(feature = "dhcpv4")] #[cfg(feature = "dhcpv4")]
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -112,12 +127,71 @@ impl Default for DhcpConfig {
} }
/// Network stack configuration. /// Network stack configuration.
pub enum Config { pub struct Config {
/// Use a static IP address configuration. /// IPv4 configuration
Static(StaticConfig), #[cfg(feature = "proto-ipv4")]
pub ipv4: ConfigV4,
/// IPv6 configuration
#[cfg(feature = "proto-ipv6")]
pub ipv6: ConfigV6,
}
impl Config {
/// IPv4 configuration with static addressing.
#[cfg(feature = "proto-ipv4")]
pub fn ipv4_static(config: StaticConfigV4) -> Self {
Self {
ipv4: ConfigV4::Static(config),
#[cfg(feature = "proto-ipv6")]
ipv6: ConfigV6::None,
}
}
/// IPv6 configuration with static addressing.
#[cfg(feature = "proto-ipv6")]
pub fn ipv6_static(config: StaticConfigV6) -> Self {
Self {
#[cfg(feature = "proto-ipv4")]
ipv4: ConfigV4::None,
ipv6: ConfigV6::Static(config),
}
}
/// IPv6 configuration with dynamic addressing.
///
/// # Example
/// ```rust
/// let _cfg = Config::dhcpv4(Default::default());
/// ```
#[cfg(feature = "dhcpv4")]
pub fn dhcpv4(config: DhcpConfig) -> Self {
Self {
ipv4: ConfigV4::Dhcp(config),
#[cfg(feature = "proto-ipv6")]
ipv6: ConfigV6::None,
}
}
}
/// Network stack IPv4 configuration.
#[cfg(feature = "proto-ipv4")]
pub enum ConfigV4 {
/// Use a static IPv4 address configuration.
Static(StaticConfigV4),
/// Use DHCP to obtain an IP address configuration. /// Use DHCP to obtain an IP address configuration.
#[cfg(feature = "dhcpv4")] #[cfg(feature = "dhcpv4")]
Dhcp(DhcpConfig), Dhcp(DhcpConfig),
/// Do not configure IPv6.
None,
}
/// Network stack IPv6 configuration.
#[cfg(feature = "proto-ipv6")]
pub enum ConfigV6 {
/// Use a static IPv6 address configuration.
Static(StaticConfigV6),
/// Do not configure IPv6.
None,
} }
/// A network stack. /// A network stack.
@ -131,7 +205,10 @@ pub struct Stack<D: Driver> {
struct Inner<D: Driver> { struct Inner<D: Driver> {
device: D, device: D,
link_up: bool, link_up: bool,
config: Option<StaticConfig>, #[cfg(feature = "proto-ipv4")]
static_v4: Option<StaticConfigV4>,
#[cfg(feature = "proto-ipv6")]
static_v6: Option<StaticConfigV6>,
#[cfg(feature = "dhcpv4")] #[cfg(feature = "dhcpv4")]
dhcp_socket: Option<SocketHandle>, dhcp_socket: Option<SocketHandle>,
#[cfg(feature = "dns")] #[cfg(feature = "dns")]
@ -158,12 +235,19 @@ impl<D: Driver + 'static> Stack<D> {
#[cfg(feature = "medium-ethernet")] #[cfg(feature = "medium-ethernet")]
let medium = device.capabilities().medium; let medium = device.capabilities().medium;
let mut iface_cfg = smoltcp::iface::Config::new(); let hardware_addr = match medium {
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress(device.ethernet_address())),
#[cfg(feature = "medium-ip")]
Medium::Ip => HardwareAddress::Ip,
#[allow(unreachable_patterns)]
_ => panic!(
"Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.",
medium
),
};
let mut iface_cfg = smoltcp::iface::Config::new(hardware_addr);
iface_cfg.random_seed = random_seed; iface_cfg.random_seed = random_seed;
#[cfg(feature = "medium-ethernet")]
if medium == Medium::Ethernet {
iface_cfg.hardware_addr = Some(HardwareAddress::Ethernet(EthernetAddress(device.ethernet_address())));
}
let iface = Interface::new( let iface = Interface::new(
iface_cfg, iface_cfg,
@ -171,6 +255,7 @@ impl<D: Driver + 'static> Stack<D> {
inner: &mut device, inner: &mut device,
cx: None, cx: None,
}, },
instant_to_smoltcp(Instant::now()),
); );
let sockets = SocketSet::new(&mut resources.sockets[..]); let sockets = SocketSet::new(&mut resources.sockets[..]);
@ -187,7 +272,10 @@ impl<D: Driver + 'static> Stack<D> {
let mut inner = Inner { let mut inner = Inner {
device, device,
link_up: false, link_up: false,
config: None, #[cfg(feature = "proto-ipv4")]
static_v4: None,
#[cfg(feature = "proto-ipv6")]
static_v6: None,
#[cfg(feature = "dhcpv4")] #[cfg(feature = "dhcpv4")]
dhcp_socket: None, dhcp_socket: None,
#[cfg(feature = "dns")] #[cfg(feature = "dns")]
@ -199,17 +287,26 @@ impl<D: Driver + 'static> Stack<D> {
dns_waker: WakerRegistration::new(), dns_waker: WakerRegistration::new(),
}; };
match config { #[cfg(feature = "proto-ipv4")]
Config::Static(config) => { match config.ipv4 {
inner.apply_config(&mut socket, config); ConfigV4::Static(config) => {
inner.apply_config_v4(&mut socket, config);
} }
#[cfg(feature = "dhcpv4")] #[cfg(feature = "dhcpv4")]
Config::Dhcp(config) => { ConfigV4::Dhcp(config) => {
let mut dhcp_socket = smoltcp::socket::dhcpv4::Socket::new(); let mut dhcp_socket = smoltcp::socket::dhcpv4::Socket::new();
inner.apply_dhcp_config(&mut dhcp_socket, config); inner.apply_dhcp_config(&mut dhcp_socket, config);
let handle = socket.sockets.add(dhcp_socket); let handle = socket.sockets.add(dhcp_socket);
inner.dhcp_socket = Some(handle); inner.dhcp_socket = Some(handle);
} }
ConfigV4::None => {}
}
#[cfg(feature = "proto-ipv6")]
match config.ipv6 {
ConfigV6::Static(config) => {
inner.apply_config_v6(&mut socket, config);
}
ConfigV6::None => {}
} }
Self { Self {
@ -239,12 +336,40 @@ impl<D: Driver + 'static> Stack<D> {
/// Get whether the network stack has a valid IP configuration. /// Get whether the network stack has a valid IP configuration.
/// This is true if the network stack has a static IP configuration or if DHCP has completed /// This is true if the network stack has a static IP configuration or if DHCP has completed
pub fn is_config_up(&self) -> bool { pub fn is_config_up(&self) -> bool {
self.with(|_s, i| i.config.is_some()) let v4_up;
let v6_up;
#[cfg(feature = "proto-ipv4")]
{
v4_up = self.config_v4().is_some();
}
#[cfg(not(feature = "proto-ipv4"))]
{
v4_up = false;
}
#[cfg(feature = "proto-ipv6")]
{
v6_up = self.config_v6().is_some();
}
#[cfg(not(feature = "proto-ipv6"))]
{
v6_up = false;
}
v4_up || v6_up
} }
/// Get the current IP configuration. /// Get the current IPv4 configuration.
pub fn config(&self) -> Option<StaticConfig> { #[cfg(feature = "proto-ipv4")]
self.with(|_s, i| i.config.clone()) pub fn config_v4(&self) -> Option<StaticConfigV4> {
self.with(|_s, i| i.static_v4.clone())
}
/// Get the current IPv6 configuration.
#[cfg(feature = "proto-ipv6")]
pub fn config_v6(&self) -> Option<StaticConfigV6> {
self.with(|_s, i| i.static_v6.clone())
} }
/// Run the network stack. /// Run the network stack.
@ -264,6 +389,7 @@ impl<D: Driver + 'static> Stack<D> {
pub async fn dns_query(&self, name: &str, qtype: dns::DnsQueryType) -> Result<Vec<IpAddress, 1>, dns::Error> { pub async fn dns_query(&self, name: &str, qtype: dns::DnsQueryType) -> Result<Vec<IpAddress, 1>, dns::Error> {
// For A and AAAA queries we try detect whether `name` is just an IP address // For A and AAAA queries we try detect whether `name` is just an IP address
match qtype { match qtype {
#[cfg(feature = "proto-ipv4")]
dns::DnsQueryType::A => { dns::DnsQueryType::A => {
if let Ok(ip) = name.parse().map(IpAddress::Ipv4) { if let Ok(ip) = name.parse().map(IpAddress::Ipv4) {
return Ok([ip].into_iter().collect()); return Ok([ip].into_iter().collect());
@ -293,7 +419,29 @@ impl<D: Driver + 'static> Stack<D> {
}) })
.await?; .await?;
use embassy_hal_common::drop::OnDrop; #[must_use = "to delay the drop handler invocation to the end of the scope"]
struct OnDrop<F: FnOnce()> {
f: core::mem::MaybeUninit<F>,
}
impl<F: FnOnce()> OnDrop<F> {
fn new(f: F) -> Self {
Self {
f: core::mem::MaybeUninit::new(f),
}
}
fn defuse(self) {
core::mem::forget(self)
}
}
impl<F: FnOnce()> Drop for OnDrop<F> {
fn drop(&mut self) {
unsafe { self.f.as_ptr().read()() }
}
}
let drop = OnDrop::new(|| { let drop = OnDrop::new(|| {
self.with_mut(|s, i| { self.with_mut(|s, i| {
let socket = s.sockets.get_mut::<dns::Socket>(i.dns_socket); let socket = s.sockets.get_mut::<dns::Socket>(i.dns_socket);
@ -374,7 +522,8 @@ impl SocketStack {
} }
impl<D: Driver + 'static> Inner<D> { impl<D: Driver + 'static> Inner<D> {
fn apply_config(&mut self, s: &mut SocketStack, config: StaticConfig) { #[cfg(feature = "proto-ipv4")]
fn apply_config_v4(&mut self, s: &mut SocketStack, config: StaticConfigV4) {
#[cfg(feature = "medium-ethernet")] #[cfg(feature = "medium-ethernet")]
let medium = self.device.capabilities().medium; let medium = self.device.capabilities().medium;
@ -403,14 +552,86 @@ impl<D: Driver + 'static> Inner<D> {
debug!(" DNS server {}: {}", i, s); debug!(" DNS server {}: {}", i, s);
} }
self.static_v4 = Some(config);
#[cfg(feature = "dns")] #[cfg(feature = "dns")]
{ {
let socket = s.sockets.get_mut::<smoltcp::socket::dns::Socket>(self.dns_socket); self.update_dns_servers(s)
let servers: Vec<IpAddress, 3> = config.dns_servers.iter().map(|c| IpAddress::Ipv4(*c)).collect(); }
socket.update_servers(&servers[..]); }
/// Replaces the current IPv6 static configuration with a newly supplied config.
#[cfg(feature = "proto-ipv6")]
fn apply_config_v6(&mut self, s: &mut SocketStack, config: StaticConfigV6) {
#[cfg(feature = "medium-ethernet")]
let medium = self.device.capabilities().medium;
debug!("Acquired IPv6 configuration:");
debug!(" IP address: {}", config.address);
s.iface.update_ip_addrs(|addrs| {
if addrs.is_empty() {
addrs.push(IpCidr::Ipv6(config.address)).unwrap();
} else {
addrs[0] = IpCidr::Ipv6(config.address);
}
});
#[cfg(feature = "medium-ethernet")]
if Medium::Ethernet == medium {
if let Some(gateway) = config.gateway {
debug!(" Default gateway: {}", gateway);
s.iface.routes_mut().add_default_ipv6_route(gateway).unwrap();
} else {
debug!(" Default gateway: None");
s.iface.routes_mut().remove_default_ipv6_route();
}
}
for (i, s) in config.dns_servers.iter().enumerate() {
debug!(" DNS server {}: {}", i, s);
} }
self.config = Some(config) self.static_v6 = Some(config);
#[cfg(feature = "dns")]
{
self.update_dns_servers(s)
}
}
#[cfg(feature = "dns")]
fn update_dns_servers(&mut self, s: &mut SocketStack) {
let socket = s.sockets.get_mut::<smoltcp::socket::dns::Socket>(self.dns_socket);
let servers_v4;
#[cfg(feature = "proto-ipv4")]
{
servers_v4 = self
.static_v4
.iter()
.flat_map(|cfg| cfg.dns_servers.iter().map(|c| IpAddress::Ipv4(*c)));
};
#[cfg(not(feature = "proto-ipv4"))]
{
servers_v4 = core::iter::empty();
}
let servers_v6;
#[cfg(feature = "proto-ipv6")]
{
servers_v6 = self
.static_v6
.iter()
.flat_map(|cfg| cfg.dns_servers.iter().map(|c| IpAddress::Ipv6(*c)));
}
#[cfg(not(feature = "proto-ipv6"))]
{
servers_v6 = core::iter::empty();
}
// Prefer the v6 DNS servers over the v4 servers
let servers: Vec<IpAddress, 6> = servers_v6.chain(servers_v4).collect();
socket.update_servers(&servers[..]);
} }
#[cfg(feature = "dhcpv4")] #[cfg(feature = "dhcpv4")]
@ -430,9 +651,15 @@ impl<D: Driver + 'static> Inner<D> {
s.iface.update_ip_addrs(|ip_addrs| ip_addrs.clear()); s.iface.update_ip_addrs(|ip_addrs| ip_addrs.clear());
#[cfg(feature = "medium-ethernet")] #[cfg(feature = "medium-ethernet")]
if medium == Medium::Ethernet { if medium == Medium::Ethernet {
s.iface.routes_mut().remove_default_ipv4_route(); #[cfg(feature = "proto-ipv4")]
{
s.iface.routes_mut().remove_default_ipv4_route();
}
}
#[cfg(feature = "proto-ipv4")]
{
self.static_v4 = None
} }
self.config = None
} }
fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) { fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) {
@ -470,12 +697,12 @@ impl<D: Driver + 'static> Inner<D> {
None => {} None => {}
Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s), Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s),
Some(dhcpv4::Event::Configured(config)) => { Some(dhcpv4::Event::Configured(config)) => {
let config = StaticConfig { let config = StaticConfigV4 {
address: config.address, address: config.address,
gateway: config.router, gateway: config.router,
dns_servers: config.dns_servers, dns_servers: config.dns_servers,
}; };
self.apply_config(s, config) self.apply_config_v4(s, config)
} }
} }
} else if old_link_up { } else if old_link_up {

View File

@ -278,10 +278,18 @@ impl<'a> TcpSocket<'a> {
self.io.with(|s, _| s.may_send()) self.io.with(|s, _| s.may_send())
} }
/// Get whether the socket is ready to receive data, i.e. whether there is some pending data in the receive buffer. /// return whether the recieve half of the full-duplex connection is open.
/// This function returns true if its possible to receive data from the remote endpoint.
/// It will return true while there is data in the receive buffer, and if there isnt,
/// as long as the remote endpoint has not closed the connection.
pub fn may_recv(&self) -> bool { pub fn may_recv(&self) -> bool {
self.io.with(|s, _| s.may_recv()) self.io.with(|s, _| s.may_recv())
} }
/// Get whether the socket is ready to receive data, i.e. whether there is some pending data in the receive buffer.
pub fn can_recv(&self) -> bool {
self.io.with(|s, _| s.can_recv())
}
} }
impl<'a> Drop for TcpSocket<'a> { impl<'a> Drop for TcpSocket<'a> {
@ -472,7 +480,10 @@ pub mod client {
Self: 'a, Self: 'a,
{ {
let addr: crate::IpAddress = match remote.ip() { let addr: crate::IpAddress = match remote.ip() {
#[cfg(feature = "proto-ipv4")]
IpAddr::V4(addr) => crate::IpAddress::Ipv4(crate::Ipv4Address::from_bytes(&addr.octets())), IpAddr::V4(addr) => crate::IpAddress::Ipv4(crate::Ipv4Address::from_bytes(&addr.octets())),
#[cfg(not(feature = "proto-ipv4"))]
IpAddr::V4(_) => panic!("ipv4 support not enabled"),
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
IpAddr::V6(addr) => crate::IpAddress::Ipv6(crate::Ipv6Address::from_bytes(&addr.octets())), IpAddr::V6(addr) => crate::IpAddress::Ipv6(crate::Ipv6Address::from_bytes(&addr.octets())),
#[cfg(not(feature = "proto-ipv6"))] #[cfg(not(feature = "proto-ipv6"))]

View File

@ -104,7 +104,7 @@ impl<'a> UdpSocket<'a> {
pub async fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, IpEndpoint), Error> { pub async fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, IpEndpoint), Error> {
poll_fn(move |cx| { poll_fn(move |cx| {
self.with_mut(|s, _| match s.recv_slice(buf) { self.with_mut(|s, _| match s.recv_slice(buf) {
Ok(x) => Poll::Ready(Ok(x)), Ok((n, meta)) => Poll::Ready(Ok((n, meta.endpoint))),
// No data ready // No data ready
Err(udp::RecvError::Exhausted) => { Err(udp::RecvError::Exhausted) => {
s.register_recv_waker(cx.waker()); s.register_recv_waker(cx.waker());

View File

@ -16,7 +16,8 @@ flavors = [
] ]
[features] [features]
default = [ default = ["rt"]
rt = [
"nrf52805-pac?/rt", "nrf52805-pac?/rt",
"nrf52810-pac?/rt", "nrf52810-pac?/rt",
"nrf52811-pac?/rt", "nrf52811-pac?/rt",
@ -31,7 +32,7 @@ default = [
time = ["dep:embassy-time"] time = ["dep:embassy-time"]
defmt = ["dep:defmt", "embassy-executor/defmt", "embassy-sync/defmt", "embassy-usb-driver?/defmt", "embedded-io?/defmt", "embassy-embedded-hal/defmt"] defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-usb-driver?/defmt", "embedded-io?/defmt", "embassy-embedded-hal/defmt"]
# Enable nightly-only features # Enable nightly-only features
nightly = ["embedded-hal-1", "embedded-hal-async", "dep:embassy-usb-driver", "embedded-storage-async", "dep:embedded-io", "embassy-embedded-hal/nightly"] nightly = ["embedded-hal-1", "embedded-hal-async", "dep:embassy-usb-driver", "embedded-storage-async", "dep:embedded-io", "embassy-embedded-hal/nightly"]
@ -90,11 +91,9 @@ _dppi = []
_gpio-p1 = [] _gpio-p1 = []
[dependencies] [dependencies]
embassy-executor = { version = "0.2.0", path = "../embassy-executor", optional = true }
embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true } embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true }
embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-3"]} embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common", features = ["cortex-m", "prio-bits-3"] }
embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" }
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true }

View File

@ -15,7 +15,6 @@ use core::slice;
use core::sync::atomic::{compiler_fence, AtomicU8, AtomicUsize, Ordering}; use core::sync::atomic::{compiler_fence, AtomicU8, AtomicUsize, Ordering};
use core::task::Poll; use core::task::Poll;
use embassy_cortex_m::interrupt::Interrupt;
use embassy_hal_common::atomic_ring_buffer::RingBuffer; use embassy_hal_common::atomic_ring_buffer::RingBuffer;
use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker; use embassy_sync::waitqueue::AtomicWaker;
@ -24,13 +23,13 @@ pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Pari
use crate::gpio::sealed::Pin; use crate::gpio::sealed::Pin;
use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits};
use crate::interrupt::{self}; use crate::interrupt::typelevel::Interrupt;
use crate::ppi::{ use crate::ppi::{
self, AnyConfigurableChannel, AnyGroup, Channel, ConfigurableChannel, Event, Group, Ppi, PpiGroup, Task, self, AnyConfigurableChannel, AnyGroup, Channel, ConfigurableChannel, Event, Group, Ppi, PpiGroup, Task,
}; };
use crate::timer::{Instance as TimerInstance, Timer}; use crate::timer::{Instance as TimerInstance, Timer};
use crate::uarte::{apply_workaround_for_enable_anomaly, Config, Instance as UarteInstance}; use crate::uarte::{apply_workaround_for_enable_anomaly, Config, Instance as UarteInstance};
use crate::{pac, Peripheral}; use crate::{interrupt, pac, Peripheral};
mod sealed { mod sealed {
use super::*; use super::*;
@ -77,7 +76,7 @@ pub struct InterruptHandler<U: UarteInstance> {
_phantom: PhantomData<U>, _phantom: PhantomData<U>,
} }
impl<U: UarteInstance> interrupt::Handler<U::Interrupt> for InterruptHandler<U> { impl<U: UarteInstance> interrupt::typelevel::Handler<U::Interrupt> for InterruptHandler<U> {
unsafe fn on_interrupt() { unsafe fn on_interrupt() {
//trace!("irq: start"); //trace!("irq: start");
let r = U::regs(); let r = U::regs();
@ -202,7 +201,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
ppi_ch1: impl Peripheral<P = impl ConfigurableChannel> + 'd, ppi_ch1: impl Peripheral<P = impl ConfigurableChannel> + 'd,
ppi_ch2: impl Peripheral<P = impl ConfigurableChannel> + 'd, ppi_ch2: impl Peripheral<P = impl ConfigurableChannel> + 'd,
ppi_group: impl Peripheral<P = impl Group> + 'd, ppi_group: impl Peripheral<P = impl Group> + 'd,
_irq: impl interrupt::Binding<U::Interrupt, InterruptHandler<U>> + 'd, _irq: impl interrupt::typelevel::Binding<U::Interrupt, InterruptHandler<U>> + 'd,
rxd: impl Peripheral<P = impl GpioPin> + 'd, rxd: impl Peripheral<P = impl GpioPin> + 'd,
txd: impl Peripheral<P = impl GpioPin> + 'd, txd: impl Peripheral<P = impl GpioPin> + 'd,
config: Config, config: Config,
@ -237,7 +236,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
ppi_ch1: impl Peripheral<P = impl ConfigurableChannel> + 'd, ppi_ch1: impl Peripheral<P = impl ConfigurableChannel> + 'd,
ppi_ch2: impl Peripheral<P = impl ConfigurableChannel> + 'd, ppi_ch2: impl Peripheral<P = impl ConfigurableChannel> + 'd,
ppi_group: impl Peripheral<P = impl Group> + 'd, ppi_group: impl Peripheral<P = impl Group> + 'd,
_irq: impl interrupt::Binding<U::Interrupt, InterruptHandler<U>> + 'd, _irq: impl interrupt::typelevel::Binding<U::Interrupt, InterruptHandler<U>> + 'd,
rxd: impl Peripheral<P = impl GpioPin> + 'd, rxd: impl Peripheral<P = impl GpioPin> + 'd,
txd: impl Peripheral<P = impl GpioPin> + 'd, txd: impl Peripheral<P = impl GpioPin> + 'd,
cts: impl Peripheral<P = impl GpioPin> + 'd, cts: impl Peripheral<P = impl GpioPin> + 'd,

View File

@ -208,33 +208,29 @@ impl_ppi_channel!(PPI_CH31, 31 => static);
impl_saadc_input!(P0_04, ANALOG_INPUT2); impl_saadc_input!(P0_04, ANALOG_INPUT2);
impl_saadc_input!(P0_05, ANALOG_INPUT3); impl_saadc_input!(P0_05, ANALOG_INPUT3);
pub mod irqs { embassy_hal_common::interrupt_mod!(
use embassy_cortex_m::interrupt::_export::declare; POWER_CLOCK,
RADIO,
use crate::pac::Interrupt as InterruptEnum; UARTE0_UART0,
TWIM0_TWIS0_TWI0,
declare!(POWER_CLOCK); SPIM0_SPIS0_SPI0,
declare!(RADIO); GPIOTE,
declare!(UARTE0_UART0); SAADC,
declare!(TWIM0_TWIS0_TWI0); TIMER0,
declare!(SPIM0_SPIS0_SPI0); TIMER1,
declare!(GPIOTE); TIMER2,
declare!(SAADC); RTC0,
declare!(TIMER0); TEMP,
declare!(TIMER1); RNG,
declare!(TIMER2); ECB,
declare!(RTC0); CCM_AAR,
declare!(TEMP); WDT,
declare!(RNG); RTC1,
declare!(ECB); QDEC,
declare!(CCM_AAR); SWI0_EGU0,
declare!(WDT); SWI1_EGU1,
declare!(RTC1); SWI2,
declare!(QDEC); SWI3,
declare!(SWI0_EGU0); SWI4,
declare!(SWI1_EGU1); SWI5,
declare!(SWI2); );
declare!(SWI3);
declare!(SWI4);
declare!(SWI5);
}

View File

@ -234,36 +234,32 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5);
impl_saadc_input!(P0_30, ANALOG_INPUT6); impl_saadc_input!(P0_30, ANALOG_INPUT6);
impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_saadc_input!(P0_31, ANALOG_INPUT7);
pub mod irqs { embassy_hal_common::interrupt_mod!(
use embassy_cortex_m::interrupt::_export::declare; POWER_CLOCK,
RADIO,
use crate::pac::Interrupt as InterruptEnum; UARTE0_UART0,
TWIM0_TWIS0_TWI0,
declare!(POWER_CLOCK); SPIM0_SPIS0_SPI0,
declare!(RADIO); GPIOTE,
declare!(UARTE0_UART0); SAADC,
declare!(TWIM0_TWIS0_TWI0); TIMER0,
declare!(SPIM0_SPIS0_SPI0); TIMER1,
declare!(GPIOTE); TIMER2,
declare!(SAADC); RTC0,
declare!(TIMER0); TEMP,
declare!(TIMER1); RNG,
declare!(TIMER2); ECB,
declare!(RTC0); CCM_AAR,
declare!(TEMP); WDT,
declare!(RNG); RTC1,
declare!(ECB); QDEC,
declare!(CCM_AAR); COMP,
declare!(WDT); SWI0_EGU0,
declare!(RTC1); SWI1_EGU1,
declare!(QDEC); SWI2,
declare!(COMP); SWI3,
declare!(SWI0_EGU0); SWI4,
declare!(SWI1_EGU1); SWI5,
declare!(SWI2); PWM0,
declare!(SWI3); PDM,
declare!(SWI4); );
declare!(SWI5);
declare!(PWM0);
declare!(PDM);
}

View File

@ -236,36 +236,32 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5);
impl_saadc_input!(P0_30, ANALOG_INPUT6); impl_saadc_input!(P0_30, ANALOG_INPUT6);
impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_saadc_input!(P0_31, ANALOG_INPUT7);
pub mod irqs { embassy_hal_common::interrupt_mod!(
use embassy_cortex_m::interrupt::_export::declare; POWER_CLOCK,
RADIO,
use crate::pac::Interrupt as InterruptEnum; UARTE0_UART0,
TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0,
declare!(POWER_CLOCK); SPIM1_SPIS1_SPI1,
declare!(RADIO); GPIOTE,
declare!(UARTE0_UART0); SAADC,
declare!(TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0); TIMER0,
declare!(SPIM1_SPIS1_SPI1); TIMER1,
declare!(GPIOTE); TIMER2,
declare!(SAADC); RTC0,
declare!(TIMER0); TEMP,
declare!(TIMER1); RNG,
declare!(TIMER2); ECB,
declare!(RTC0); CCM_AAR,
declare!(TEMP); WDT,
declare!(RNG); RTC1,
declare!(ECB); QDEC,
declare!(CCM_AAR); COMP,
declare!(WDT); SWI0_EGU0,
declare!(RTC1); SWI1_EGU1,
declare!(QDEC); SWI2,
declare!(COMP); SWI3,
declare!(SWI0_EGU0); SWI4,
declare!(SWI1_EGU1); SWI5,
declare!(SWI2); PWM0,
declare!(SWI3); PDM,
declare!(SWI4); );
declare!(SWI5);
declare!(PWM0);
declare!(PDM);
}

View File

@ -224,35 +224,31 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH30, 30 => static);
impl_ppi_channel!(PPI_CH31, 31 => static); impl_ppi_channel!(PPI_CH31, 31 => static);
pub mod irqs { embassy_hal_common::interrupt_mod!(
use embassy_cortex_m::interrupt::_export::declare; POWER_CLOCK,
RADIO,
use crate::pac::Interrupt as InterruptEnum; UARTE0_UART0,
SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0,
declare!(POWER_CLOCK); SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1,
declare!(RADIO); GPIOTE,
declare!(UARTE0_UART0); TIMER0,
declare!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); TIMER1,
declare!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); TIMER2,
declare!(GPIOTE); RTC0,
declare!(TIMER0); TEMP,
declare!(TIMER1); RNG,
declare!(TIMER2); ECB,
declare!(RTC0); CCM_AAR,
declare!(TEMP); WDT,
declare!(RNG); RTC1,
declare!(ECB); QDEC,
declare!(CCM_AAR); COMP,
declare!(WDT); SWI0_EGU0,
declare!(RTC1); SWI1_EGU1,
declare!(QDEC); SWI2_EGU2,
declare!(COMP); SWI3_EGU3,
declare!(SWI0_EGU0); SWI4_EGU4,
declare!(SWI1_EGU1); SWI5_EGU5,
declare!(SWI2_EGU2); TIMER3,
declare!(SWI3_EGU3); USBD,
declare!(SWI4_EGU4); );
declare!(SWI5_EGU5);
declare!(TIMER3);
declare!(USBD);
}

View File

@ -263,46 +263,42 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7);
impl_i2s!(I2S, I2S, I2S); impl_i2s!(I2S, I2S, I2S);
pub mod irqs { embassy_hal_common::interrupt_mod!(
use embassy_cortex_m::interrupt::_export::declare; POWER_CLOCK,
RADIO,
use crate::pac::Interrupt as InterruptEnum; UARTE0_UART0,
SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0,
declare!(POWER_CLOCK); SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1,
declare!(RADIO); NFCT,
declare!(UARTE0_UART0); GPIOTE,
declare!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); SAADC,
declare!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); TIMER0,
declare!(NFCT); TIMER1,
declare!(GPIOTE); TIMER2,
declare!(SAADC); RTC0,
declare!(TIMER0); TEMP,
declare!(TIMER1); RNG,
declare!(TIMER2); ECB,
declare!(RTC0); CCM_AAR,
declare!(TEMP); WDT,
declare!(RNG); RTC1,
declare!(ECB); QDEC,
declare!(CCM_AAR); COMP_LPCOMP,
declare!(WDT); SWI0_EGU0,
declare!(RTC1); SWI1_EGU1,
declare!(QDEC); SWI2_EGU2,
declare!(COMP_LPCOMP); SWI3_EGU3,
declare!(SWI0_EGU0); SWI4_EGU4,
declare!(SWI1_EGU1); SWI5_EGU5,
declare!(SWI2_EGU2); TIMER3,
declare!(SWI3_EGU3); TIMER4,
declare!(SWI4_EGU4); PWM0,
declare!(SWI5_EGU5); PDM,
declare!(TIMER3); MWU,
declare!(TIMER4); PWM1,
declare!(PWM0); PWM2,
declare!(PDM); SPIM2_SPIS2_SPI2,
declare!(MWU); RTC2,
declare!(PWM1); FPU,
declare!(PWM2); I2S,
declare!(SPIM2_SPIS2_SPI2); );
declare!(RTC2);
declare!(FPU);
declare!(I2S);
}

View File

@ -306,50 +306,46 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7);
impl_i2s!(I2S, I2S, I2S); impl_i2s!(I2S, I2S, I2S);
pub mod irqs { embassy_hal_common::interrupt_mod!(
use embassy_cortex_m::interrupt::_export::declare; POWER_CLOCK,
RADIO,
use crate::pac::Interrupt as InterruptEnum; UARTE0_UART0,
SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0,
declare!(POWER_CLOCK); SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1,
declare!(RADIO); NFCT,
declare!(UARTE0_UART0); GPIOTE,
declare!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); SAADC,
declare!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); TIMER0,
declare!(NFCT); TIMER1,
declare!(GPIOTE); TIMER2,
declare!(SAADC); RTC0,
declare!(TIMER0); TEMP,
declare!(TIMER1); RNG,
declare!(TIMER2); ECB,
declare!(RTC0); CCM_AAR,
declare!(TEMP); WDT,
declare!(RNG); RTC1,
declare!(ECB); QDEC,
declare!(CCM_AAR); COMP_LPCOMP,
declare!(WDT); SWI0_EGU0,
declare!(RTC1); SWI1_EGU1,
declare!(QDEC); SWI2_EGU2,
declare!(COMP_LPCOMP); SWI3_EGU3,
declare!(SWI0_EGU0); SWI4_EGU4,
declare!(SWI1_EGU1); SWI5_EGU5,
declare!(SWI2_EGU2); TIMER3,
declare!(SWI3_EGU3); TIMER4,
declare!(SWI4_EGU4); PWM0,
declare!(SWI5_EGU5); PDM,
declare!(TIMER3); MWU,
declare!(TIMER4); PWM1,
declare!(PWM0); PWM2,
declare!(PDM); SPIM2_SPIS2_SPI2,
declare!(MWU); RTC2,
declare!(PWM1); FPU,
declare!(PWM2); USBD,
declare!(SPIM2_SPIS2_SPI2); UARTE1,
declare!(RTC2); PWM3,
declare!(FPU); SPIM3,
declare!(USBD); I2S,
declare!(UARTE1); );
declare!(PWM3);
declare!(SPIM3);
declare!(I2S);
}

View File

@ -311,52 +311,48 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7);
impl_i2s!(I2S, I2S, I2S); impl_i2s!(I2S, I2S, I2S);
pub mod irqs { embassy_hal_common::interrupt_mod!(
use embassy_cortex_m::interrupt::_export::declare; POWER_CLOCK,
RADIO,
use crate::pac::Interrupt as InterruptEnum; UARTE0_UART0,
SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0,
declare!(POWER_CLOCK); SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1,
declare!(RADIO); NFCT,
declare!(UARTE0_UART0); GPIOTE,
declare!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); SAADC,
declare!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); TIMER0,
declare!(NFCT); TIMER1,
declare!(GPIOTE); TIMER2,
declare!(SAADC); RTC0,
declare!(TIMER0); TEMP,
declare!(TIMER1); RNG,
declare!(TIMER2); ECB,
declare!(RTC0); CCM_AAR,
declare!(TEMP); WDT,
declare!(RNG); RTC1,
declare!(ECB); QDEC,
declare!(CCM_AAR); COMP_LPCOMP,
declare!(WDT); SWI0_EGU0,
declare!(RTC1); SWI1_EGU1,
declare!(QDEC); SWI2_EGU2,
declare!(COMP_LPCOMP); SWI3_EGU3,
declare!(SWI0_EGU0); SWI4_EGU4,
declare!(SWI1_EGU1); SWI5_EGU5,
declare!(SWI2_EGU2); TIMER3,
declare!(SWI3_EGU3); TIMER4,
declare!(SWI4_EGU4); PWM0,
declare!(SWI5_EGU5); PDM,
declare!(TIMER3); MWU,
declare!(TIMER4); PWM1,
declare!(PWM0); PWM2,
declare!(PDM); SPIM2_SPIS2_SPI2,
declare!(MWU); RTC2,
declare!(PWM1); FPU,
declare!(PWM2); USBD,
declare!(SPIM2_SPIS2_SPI2); UARTE1,
declare!(RTC2); QSPI,
declare!(FPU); CRYPTOCELL,
declare!(USBD); PWM3,
declare!(UARTE1); SPIM3,
declare!(QSPI); I2S,
declare!(CRYPTOCELL); );
declare!(PWM3);
declare!(SPIM3);
declare!(I2S);
}

View File

@ -5,6 +5,8 @@ pub mod pac {
// The nRF5340 has a secure and non-secure (NS) mode. // The nRF5340 has a secure and non-secure (NS) mode.
// To avoid cfg spam, we remove _ns or _s suffixes here. // To avoid cfg spam, we remove _ns or _s suffixes here.
pub use nrf5340_app_pac::NVIC_PRIO_BITS;
#[doc(no_inline)] #[doc(no_inline)]
pub use nrf5340_app_pac::{ pub use nrf5340_app_pac::{
interrupt, interrupt,
@ -504,50 +506,46 @@ impl_saadc_input!(P0_18, ANALOG_INPUT5);
impl_saadc_input!(P0_19, ANALOG_INPUT6); impl_saadc_input!(P0_19, ANALOG_INPUT6);
impl_saadc_input!(P0_20, ANALOG_INPUT7); impl_saadc_input!(P0_20, ANALOG_INPUT7);
pub mod irqs { embassy_hal_common::interrupt_mod!(
use embassy_cortex_m::interrupt::_export::declare; FPU,
CACHE,
use crate::pac::Interrupt as InterruptEnum; SPU,
CLOCK_POWER,
declare!(FPU); SERIAL0,
declare!(CACHE); SERIAL1,
declare!(SPU); SPIM4,
declare!(CLOCK_POWER); SERIAL2,
declare!(SERIAL0); SERIAL3,
declare!(SERIAL1); GPIOTE0,
declare!(SPIM4); SAADC,
declare!(SERIAL2); TIMER0,
declare!(SERIAL3); TIMER1,
declare!(GPIOTE0); TIMER2,
declare!(SAADC); RTC0,
declare!(TIMER0); RTC1,
declare!(TIMER1); WDT0,
declare!(TIMER2); WDT1,
declare!(RTC0); COMP_LPCOMP,
declare!(RTC1); EGU0,
declare!(WDT0); EGU1,
declare!(WDT1); EGU2,
declare!(COMP_LPCOMP); EGU3,
declare!(EGU0); EGU4,
declare!(EGU1); EGU5,
declare!(EGU2); PWM0,
declare!(EGU3); PWM1,
declare!(EGU4); PWM2,
declare!(EGU5); PWM3,
declare!(PWM0); PDM0,
declare!(PWM1); I2S0,
declare!(PWM2); IPC,
declare!(PWM3); QSPI,
declare!(PDM0); NFCT,
declare!(I2S0); GPIOTE1,
declare!(IPC); QDEC0,
declare!(QSPI); QDEC1,
declare!(NFCT); USBD,
declare!(GPIOTE1); USBREGULATOR,
declare!(QDEC0); KMU,
declare!(QDEC1); CRYPTOCELL,
declare!(USBD); );
declare!(USBREGULATOR);
declare!(KMU);
declare!(CRYPTOCELL);
}

View File

@ -5,6 +5,8 @@ pub mod pac {
// The nRF5340 has a secure and non-secure (NS) mode. // The nRF5340 has a secure and non-secure (NS) mode.
// To avoid cfg spam, we remove _ns or _s suffixes here. // To avoid cfg spam, we remove _ns or _s suffixes here.
pub use nrf5340_net_pac::NVIC_PRIO_BITS;
#[doc(no_inline)] #[doc(no_inline)]
pub use nrf5340_net_pac::{ pub use nrf5340_net_pac::{
interrupt, interrupt,
@ -340,29 +342,25 @@ impl_ppi_channel!(PPI_CH29, 29 => configurable);
impl_ppi_channel!(PPI_CH30, 30 => configurable); impl_ppi_channel!(PPI_CH30, 30 => configurable);
impl_ppi_channel!(PPI_CH31, 31 => configurable); impl_ppi_channel!(PPI_CH31, 31 => configurable);
pub mod irqs { embassy_hal_common::interrupt_mod!(
use embassy_cortex_m::interrupt::_export::declare; CLOCK_POWER,
RADIO,
use crate::pac::Interrupt as InterruptEnum; RNG,
GPIOTE,
declare!(CLOCK_POWER); WDT,
declare!(RADIO); TIMER0,
declare!(RNG); ECB,
declare!(GPIOTE); AAR_CCM,
declare!(WDT); TEMP,
declare!(TIMER0); RTC0,
declare!(ECB); IPC,
declare!(AAR_CCM); SERIAL0,
declare!(TEMP); EGU0,
declare!(RTC0); RTC1,
declare!(IPC); TIMER1,
declare!(SERIAL0); TIMER2,
declare!(EGU0); SWI0,
declare!(RTC1); SWI1,
declare!(TIMER1); SWI2,
declare!(TIMER2); SWI3,
declare!(SWI0); );
declare!(SWI1);
declare!(SWI2);
declare!(SWI3);
}

View File

@ -5,6 +5,8 @@ pub mod pac {
// The nRF9160 has a secure and non-secure (NS) mode. // The nRF9160 has a secure and non-secure (NS) mode.
// To avoid cfg spam, we remove _ns or _s suffixes here. // To avoid cfg spam, we remove _ns or _s suffixes here.
pub use nrf9160_pac::NVIC_PRIO_BITS;
#[doc(no_inline)] #[doc(no_inline)]
pub use nrf9160_pac::{ pub use nrf9160_pac::{
interrupt, interrupt,
@ -366,40 +368,36 @@ impl_saadc_input!(P0_18, ANALOG_INPUT5);
impl_saadc_input!(P0_19, ANALOG_INPUT6); impl_saadc_input!(P0_19, ANALOG_INPUT6);
impl_saadc_input!(P0_20, ANALOG_INPUT7); impl_saadc_input!(P0_20, ANALOG_INPUT7);
pub mod irqs { embassy_hal_common::interrupt_mod!(
use embassy_cortex_m::interrupt::_export::declare; SPU,
CLOCK_POWER,
use crate::pac::Interrupt as InterruptEnum; UARTE0_SPIM0_SPIS0_TWIM0_TWIS0,
UARTE1_SPIM1_SPIS1_TWIM1_TWIS1,
declare!(SPU); UARTE2_SPIM2_SPIS2_TWIM2_TWIS2,
declare!(CLOCK_POWER); UARTE3_SPIM3_SPIS3_TWIM3_TWIS3,
declare!(UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); GPIOTE0,
declare!(UARTE1_SPIM1_SPIS1_TWIM1_TWIS1); SAADC,
declare!(UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); TIMER0,
declare!(UARTE3_SPIM3_SPIS3_TWIM3_TWIS3); TIMER1,
declare!(GPIOTE0); TIMER2,
declare!(SAADC); RTC0,
declare!(TIMER0); RTC1,
declare!(TIMER1); WDT,
declare!(TIMER2); EGU0,
declare!(RTC0); EGU1,
declare!(RTC1); EGU2,
declare!(WDT); EGU3,
declare!(EGU0); EGU4,
declare!(EGU1); EGU5,
declare!(EGU2); PWM0,
declare!(EGU3); PWM1,
declare!(EGU4); PWM2,
declare!(EGU5); PDM,
declare!(PWM0); PWM3,
declare!(PWM1); I2S,
declare!(PWM2); IPC,
declare!(PDM); FPU,
declare!(PWM3); GPIOTE1,
declare!(I2S); KMU,
declare!(IPC); CRYPTOCELL,
declare!(FPU); );
declare!(GPIOTE1);
declare!(KMU);
declare!(CRYPTOCELL);
}

View File

@ -9,7 +9,7 @@ use embassy_sync::waitqueue::AtomicWaker;
use crate::gpio::sealed::Pin as _; use crate::gpio::sealed::Pin as _;
use crate::gpio::{AnyPin, Flex, Input, Output, Pin as GpioPin}; use crate::gpio::{AnyPin, Flex, Input, Output, Pin as GpioPin};
use crate::interrupt::Interrupt; use crate::interrupt::InterruptExt;
use crate::ppi::{Event, Task}; use crate::ppi::{Event, Task};
use crate::{interrupt, pac, peripherals}; use crate::{interrupt, pac, peripherals};
@ -75,15 +75,15 @@ pub(crate) fn init(irq_prio: crate::interrupt::Priority) {
// Enable interrupts // Enable interrupts
#[cfg(any(feature = "nrf5340-app-s", feature = "nrf9160-s"))] #[cfg(any(feature = "nrf5340-app-s", feature = "nrf9160-s"))]
type Irq = interrupt::GPIOTE0; let irq = interrupt::GPIOTE0;
#[cfg(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns"))] #[cfg(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns"))]
type Irq = interrupt::GPIOTE1; let irq = interrupt::GPIOTE1;
#[cfg(any(feature = "_nrf52", feature = "nrf5340-net"))] #[cfg(any(feature = "_nrf52", feature = "nrf5340-net"))]
type Irq = interrupt::GPIOTE; let irq = interrupt::GPIOTE;
Irq::unpend(); irq.unpend();
Irq::set_priority(irq_prio); irq.set_priority(irq_prio);
unsafe { Irq::enable() }; unsafe { irq.enable() };
let g = regs(); let g = regs();
g.events_port.write(|w| w); g.events_port.write(|w| w);
@ -91,18 +91,21 @@ pub(crate) fn init(irq_prio: crate::interrupt::Priority) {
} }
#[cfg(any(feature = "nrf5340-app-s", feature = "nrf9160-s"))] #[cfg(any(feature = "nrf5340-app-s", feature = "nrf9160-s"))]
#[cfg(feature = "rt")]
#[interrupt] #[interrupt]
fn GPIOTE0() { fn GPIOTE0() {
unsafe { handle_gpiote_interrupt() }; unsafe { handle_gpiote_interrupt() };
} }
#[cfg(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns"))] #[cfg(any(feature = "nrf5340-app-ns", feature = "nrf9160-ns"))]
#[cfg(feature = "rt")]
#[interrupt] #[interrupt]
fn GPIOTE1() { fn GPIOTE1() {
unsafe { handle_gpiote_interrupt() }; unsafe { handle_gpiote_interrupt() };
} }
#[cfg(any(feature = "_nrf52", feature = "nrf5340-net"))] #[cfg(any(feature = "_nrf52", feature = "nrf5340-net"))]
#[cfg(feature = "rt")]
#[interrupt] #[interrupt]
fn GPIOTE() { fn GPIOTE() {
unsafe { handle_gpiote_interrupt() }; unsafe { handle_gpiote_interrupt() };

View File

@ -13,10 +13,10 @@ use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_hal_common::{into_ref, PeripheralRef};
use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::gpio::{AnyPin, Pin as GpioPin};
use crate::interrupt::{self, Interrupt}; use crate::interrupt::typelevel::Interrupt;
use crate::pac::i2s::RegisterBlock; use crate::pac::i2s::RegisterBlock;
use crate::util::{slice_in_ram_or, slice_ptr_parts}; use crate::util::{slice_in_ram_or, slice_ptr_parts};
use crate::{Peripheral, EASY_DMA_SIZE}; use crate::{interrupt, Peripheral, EASY_DMA_SIZE};
/// Type alias for `MultiBuffering` with 2 buffers. /// Type alias for `MultiBuffering` with 2 buffers.
pub type DoubleBuffering<S, const NS: usize> = MultiBuffering<S, 2, NS>; pub type DoubleBuffering<S, const NS: usize> = MultiBuffering<S, 2, NS>;
@ -367,7 +367,7 @@ pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>, _phantom: PhantomData<T>,
} }
impl<T: Instance> interrupt::Handler<T::Interrupt> for InterruptHandler<T> { impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() { unsafe fn on_interrupt() {
let device = Device::<T>::new(); let device = Device::<T>::new();
let s = T::state(); let s = T::state();
@ -408,7 +408,7 @@ impl<'d, T: Instance> I2S<'d, T> {
/// Create a new I2S in master mode /// Create a new I2S in master mode
pub fn new_master( pub fn new_master(
i2s: impl Peripheral<P = T> + 'd, i2s: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::Binding<T::Interrupt, InterruptHandler<T>> + 'd, _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
mck: impl Peripheral<P = impl GpioPin> + 'd, mck: impl Peripheral<P = impl GpioPin> + 'd,
sck: impl Peripheral<P = impl GpioPin> + 'd, sck: impl Peripheral<P = impl GpioPin> + 'd,
lrck: impl Peripheral<P = impl GpioPin> + 'd, lrck: impl Peripheral<P = impl GpioPin> + 'd,
@ -431,7 +431,7 @@ impl<'d, T: Instance> I2S<'d, T> {
/// Create a new I2S in slave mode /// Create a new I2S in slave mode
pub fn new_slave( pub fn new_slave(
i2s: impl Peripheral<P = T> + 'd, i2s: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::Binding<T::Interrupt, InterruptHandler<T>> + 'd, _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
sck: impl Peripheral<P = impl GpioPin> + 'd, sck: impl Peripheral<P = impl GpioPin> + 'd,
lrck: impl Peripheral<P = impl GpioPin> + 'd, lrck: impl Peripheral<P = impl GpioPin> + 'd,
config: Config, config: Config,
@ -1173,7 +1173,7 @@ pub(crate) mod sealed {
/// I2S peripheral instance. /// I2S peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send { pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
/// Interrupt for this peripheral. /// Interrupt for this peripheral.
type Interrupt: Interrupt; type Interrupt: interrupt::typelevel::Interrupt;
} }
macro_rules! impl_i2s { macro_rules! impl_i2s {
@ -1188,7 +1188,7 @@ macro_rules! impl_i2s {
} }
} }
impl crate::i2s::Instance for peripherals::$type { impl crate::i2s::Instance for peripherals::$type {
type Interrupt = crate::interrupt::$irq; type Interrupt = crate::interrupt::typelevel::$irq;
} }
}; };
} }

View File

@ -93,21 +93,14 @@ pub mod wdt;
#[cfg_attr(feature = "_nrf9160", path = "chips/nrf9160.rs")] #[cfg_attr(feature = "_nrf9160", path = "chips/nrf9160.rs")]
mod chip; mod chip;
pub mod interrupt { /// Macro to bind interrupts to handlers.
//! Interrupt definitions and macros to bind them. ///
pub use cortex_m::interrupt::{CriticalSection, Mutex}; /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)
pub use embassy_cortex_m::interrupt::{Binding, Handler, Interrupt, Priority}; /// and implements the right [`Binding`]s for it. You can pass this struct to drivers to
/// prove at compile-time that the right interrupts have been bound.
pub use crate::chip::irqs::*; // developer note: this macro can't be in `embassy-hal-common` due to the use of `$crate`.
#[macro_export]
/// Macro to bind interrupts to handlers. macro_rules! bind_interrupts {
///
/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)
/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to
/// prove at compile-time that the right interrupts have been bound.
// developer note: this macro can't be in `embassy-cortex-m` due to the use of `$crate`.
#[macro_export]
macro_rules! bind_interrupts {
($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => {
$vis struct $name; $vis struct $name;
@ -116,17 +109,16 @@ pub mod interrupt {
#[no_mangle] #[no_mangle]
unsafe extern "C" fn $irq() { unsafe extern "C" fn $irq() {
$( $(
<$handler as $crate::interrupt::Handler<$crate::interrupt::$irq>>::on_interrupt(); <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();
)* )*
} }
$( $(
unsafe impl $crate::interrupt::Binding<$crate::interrupt::$irq, $handler> for $name {} unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {}
)* )*
)* )*
}; };
} }
}
// Reexports // Reexports
@ -135,10 +127,11 @@ pub use chip::pac;
#[cfg(not(feature = "unstable-pac"))] #[cfg(not(feature = "unstable-pac"))]
pub(crate) use chip::pac; pub(crate) use chip::pac;
pub use chip::{peripherals, Peripherals, EASY_DMA_SIZE}; pub use chip::{peripherals, Peripherals, EASY_DMA_SIZE};
pub use embassy_cortex_m::executor;
pub use embassy_cortex_m::interrupt::_export::interrupt;
pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
pub use crate::chip::interrupt;
pub use crate::pac::NVIC_PRIO_BITS;
pub mod config { pub mod config {
//! Configuration options used when initializing the HAL. //! Configuration options used when initializing the HAL.
@ -417,13 +410,13 @@ pub fn init(config: config::Config) -> Peripherals {
warn!( warn!(
"You have requested enabling chip reset functionality on the reset pin, by not enabling the Cargo feature `reset-pin-as-gpio`.\n\ "You have requested enabling chip reset functionality on the reset pin, by not enabling the Cargo feature `reset-pin-as-gpio`.\n\
However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\ However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\
To fix this, erase UICR manually, for example using `probe-rs-cli erase` or `nrfjprog --eraseuicr`." To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`."
); );
#[cfg(feature = "reset-pin-as-gpio")] #[cfg(feature = "reset-pin-as-gpio")]
warn!( warn!(
"You have requested using the reset pin as GPIO, by enabling the Cargo feature `reset-pin-as-gpio`.\n\ "You have requested using the reset pin as GPIO, by enabling the Cargo feature `reset-pin-as-gpio`.\n\
However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\ However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\
To fix this, erase UICR manually, for example using `probe-rs-cli erase` or `nrfjprog --eraseuicr`." To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`."
); );
} }
} }
@ -439,7 +432,7 @@ pub fn init(config: config::Config) -> Peripherals {
warn!( warn!(
"You have requested to use P0.09 and P0.10 pins for NFC, by not enabling the Cargo feature `nfc-pins-as-gpio`.\n\ "You have requested to use P0.09 and P0.10 pins for NFC, by not enabling the Cargo feature `nfc-pins-as-gpio`.\n\
However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\ However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\
To fix this, erase UICR manually, for example using `probe-rs-cli erase` or `nrfjprog --eraseuicr`." To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`."
); );
} }
} }

View File

@ -6,7 +6,6 @@ use core::marker::PhantomData;
use core::sync::atomic::{compiler_fence, Ordering}; use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll; use core::task::Poll;
use embassy_cortex_m::interrupt::Interrupt;
use embassy_hal_common::drop::OnDrop; use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_hal_common::{into_ref, PeripheralRef};
use futures::future::poll_fn; use futures::future::poll_fn;
@ -14,15 +13,15 @@ use futures::future::poll_fn;
use crate::chip::EASY_DMA_SIZE; use crate::chip::EASY_DMA_SIZE;
use crate::gpio::sealed::Pin; use crate::gpio::sealed::Pin;
use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::gpio::{AnyPin, Pin as GpioPin};
use crate::interrupt::{self}; use crate::interrupt::typelevel::Interrupt;
use crate::Peripheral; use crate::{interrupt, Peripheral};
/// Interrupt handler. /// Interrupt handler.
pub struct InterruptHandler<T: Instance> { pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>, _phantom: PhantomData<T>,
} }
impl<T: Instance> interrupt::Handler<T::Interrupt> for InterruptHandler<T> { impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() { unsafe fn on_interrupt() {
T::regs().intenclr.write(|w| w.end().clear()); T::regs().intenclr.write(|w| w.end().clear());
T::state().waker.wake(); T::state().waker.wake();
@ -53,7 +52,7 @@ impl<'d, T: Instance> Pdm<'d, T> {
/// Create PDM driver /// Create PDM driver
pub fn new( pub fn new(
pdm: impl Peripheral<P = T> + 'd, pdm: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::Binding<T::Interrupt, InterruptHandler<T>> + 'd, _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
clk: impl Peripheral<P = impl GpioPin> + 'd, clk: impl Peripheral<P = impl GpioPin> + 'd,
din: impl Peripheral<P = impl GpioPin> + 'd, din: impl Peripheral<P = impl GpioPin> + 'd,
config: Config, config: Config,
@ -274,7 +273,7 @@ pub(crate) mod sealed {
/// PDM peripheral instance. /// PDM peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send { pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
/// Interrupt for this peripheral. /// Interrupt for this peripheral.
type Interrupt: Interrupt; type Interrupt: interrupt::typelevel::Interrupt;
} }
macro_rules! impl_pdm { macro_rules! impl_pdm {
@ -289,7 +288,7 @@ macro_rules! impl_pdm {
} }
} }
impl crate::pdm::Instance for peripherals::$type { impl crate::pdm::Instance for peripherals::$type {
type Interrupt = crate::interrupt::$irq; type Interrupt = crate::interrupt::typelevel::$irq;
} }
}; };
} }

View File

@ -137,6 +137,11 @@ impl Task {
Self(ptr) Self(ptr)
} }
/// Triggers this task.
pub fn trigger(&mut self) {
unsafe { self.0.as_ptr().write_volatile(1) };
}
pub(crate) fn from_reg<T>(reg: &T) -> Self { pub(crate) fn from_reg<T>(reg: &T) -> Self {
Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) }) Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
} }
@ -173,6 +178,16 @@ impl Event {
Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) }) Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
} }
/// Describes whether this Event is currently in a triggered state.
pub fn is_triggered(&self) -> bool {
unsafe { self.0.as_ptr().read_volatile() == 1 }
}
/// Clear the current register's triggered state, reverting it to 0.
pub fn clear(&mut self) {
unsafe { self.0.as_ptr().write_volatile(0) };
}
/// Address of publish register for this event. /// Address of publish register for this event.
#[cfg(feature = "_dppi")] #[cfg(feature = "_dppi")]
pub fn publish_reg(&self) -> *mut u32 { pub fn publish_reg(&self) -> *mut u32 {

View File

@ -8,10 +8,9 @@ use embassy_hal_common::{into_ref, PeripheralRef};
use crate::gpio::sealed::Pin as _; use crate::gpio::sealed::Pin as _;
use crate::gpio::{AnyPin, Pin as GpioPin, PselBits}; use crate::gpio::{AnyPin, Pin as GpioPin, PselBits};
use crate::interrupt::Interrupt;
use crate::ppi::{Event, Task}; use crate::ppi::{Event, Task};
use crate::util::slice_in_ram_or; use crate::util::slice_in_ram_or;
use crate::{pac, Peripheral}; use crate::{interrupt, pac, Peripheral};
/// SimplePwm is the traditional pwm interface you're probably used to, allowing /// SimplePwm is the traditional pwm interface you're probably used to, allowing
/// to simply set a duty cycle across up to four channels. /// to simply set a duty cycle across up to four channels.
@ -843,7 +842,7 @@ pub(crate) mod sealed {
/// PWM peripheral instance. /// PWM peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static { pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
/// Interrupt for this peripheral. /// Interrupt for this peripheral.
type Interrupt: Interrupt; type Interrupt: interrupt::typelevel::Interrupt;
} }
macro_rules! impl_pwm { macro_rules! impl_pwm {
@ -854,7 +853,7 @@ macro_rules! impl_pwm {
} }
} }
impl crate::pwm::Instance for peripherals::$type { impl crate::pwm::Instance for peripherals::$type {
type Interrupt = crate::interrupt::$irq; type Interrupt = crate::interrupt::typelevel::$irq;
} }
}; };
} }

View File

@ -10,7 +10,7 @@ use embassy_hal_common::{into_ref, PeripheralRef};
use crate::gpio::sealed::Pin as _; use crate::gpio::sealed::Pin as _;
use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::gpio::{AnyPin, Pin as GpioPin};
use crate::interrupt::Interrupt; use crate::interrupt::typelevel::Interrupt;
use crate::{interrupt, Peripheral}; use crate::{interrupt, Peripheral};
/// Quadrature decoder driver. /// Quadrature decoder driver.
@ -50,7 +50,7 @@ pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>, _phantom: PhantomData<T>,
} }
impl<T: Instance> interrupt::Handler<T::Interrupt> for InterruptHandler<T> { impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() { unsafe fn on_interrupt() {
T::regs().intenclr.write(|w| w.reportrdy().clear()); T::regs().intenclr.write(|w| w.reportrdy().clear());
T::state().waker.wake(); T::state().waker.wake();
@ -61,7 +61,7 @@ impl<'d, T: Instance> Qdec<'d, T> {
/// Create a new QDEC. /// Create a new QDEC.
pub fn new( pub fn new(
qdec: impl Peripheral<P = T> + 'd, qdec: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::Binding<T::Interrupt, InterruptHandler<T>> + 'd, _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
a: impl Peripheral<P = impl GpioPin> + 'd, a: impl Peripheral<P = impl GpioPin> + 'd,
b: impl Peripheral<P = impl GpioPin> + 'd, b: impl Peripheral<P = impl GpioPin> + 'd,
config: Config, config: Config,
@ -73,7 +73,7 @@ impl<'d, T: Instance> Qdec<'d, T> {
/// Create a new QDEC, with a pin for LED output. /// Create a new QDEC, with a pin for LED output.
pub fn new_with_led( pub fn new_with_led(
qdec: impl Peripheral<P = T> + 'd, qdec: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::Binding<T::Interrupt, InterruptHandler<T>> + 'd, _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
a: impl Peripheral<P = impl GpioPin> + 'd, a: impl Peripheral<P = impl GpioPin> + 'd,
b: impl Peripheral<P = impl GpioPin> + 'd, b: impl Peripheral<P = impl GpioPin> + 'd,
led: impl Peripheral<P = impl GpioPin> + 'd, led: impl Peripheral<P = impl GpioPin> + 'd,
@ -271,7 +271,7 @@ pub(crate) mod sealed {
/// qdec peripheral instance. /// qdec peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send { pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
/// Interrupt for this peripheral. /// Interrupt for this peripheral.
type Interrupt: Interrupt; type Interrupt: interrupt::typelevel::Interrupt;
} }
macro_rules! impl_qdec { macro_rules! impl_qdec {
@ -286,7 +286,7 @@ macro_rules! impl_qdec {
} }
} }
impl crate::qdec::Instance for peripherals::$type { impl crate::qdec::Instance for peripherals::$type {
type Interrupt = crate::interrupt::$irq; type Interrupt = crate::interrupt::typelevel::$irq;
} }
}; };
} }

View File

@ -12,12 +12,12 @@ use embassy_hal_common::{into_ref, PeripheralRef};
use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
use crate::gpio::{self, Pin as GpioPin}; use crate::gpio::{self, Pin as GpioPin};
use crate::interrupt::{self, Interrupt}; use crate::interrupt::typelevel::Interrupt;
pub use crate::pac::qspi::ifconfig0::{ pub use crate::pac::qspi::ifconfig0::{
ADDRMODE_A as AddressMode, PPSIZE_A as WritePageSize, READOC_A as ReadOpcode, WRITEOC_A as WriteOpcode, ADDRMODE_A as AddressMode, PPSIZE_A as WritePageSize, READOC_A as ReadOpcode, WRITEOC_A as WriteOpcode,
}; };
pub use crate::pac::qspi::ifconfig1::SPIMODE_A as SpiMode; pub use crate::pac::qspi::ifconfig1::SPIMODE_A as SpiMode;
use crate::Peripheral; use crate::{interrupt, Peripheral};
/// Deep power-down config. /// Deep power-down config.
pub struct DeepPowerDownConfig { pub struct DeepPowerDownConfig {
@ -120,7 +120,7 @@ pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>, _phantom: PhantomData<T>,
} }
impl<T: Instance> interrupt::Handler<T::Interrupt> for InterruptHandler<T> { impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() { unsafe fn on_interrupt() {
let r = T::regs(); let r = T::regs();
let s = T::state(); let s = T::state();
@ -143,7 +143,7 @@ impl<'d, T: Instance> Qspi<'d, T> {
/// Create a new QSPI driver. /// Create a new QSPI driver.
pub fn new( pub fn new(
qspi: impl Peripheral<P = T> + 'd, qspi: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::Binding<T::Interrupt, InterruptHandler<T>> + 'd, _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
sck: impl Peripheral<P = impl GpioPin> + 'd, sck: impl Peripheral<P = impl GpioPin> + 'd,
csn: impl Peripheral<P = impl GpioPin> + 'd, csn: impl Peripheral<P = impl GpioPin> + 'd,
io0: impl Peripheral<P = impl GpioPin> + 'd, io0: impl Peripheral<P = impl GpioPin> + 'd,
@ -644,7 +644,7 @@ pub(crate) mod sealed {
/// QSPI peripheral instance. /// QSPI peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send { pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
/// Interrupt for this peripheral. /// Interrupt for this peripheral.
type Interrupt: Interrupt; type Interrupt: interrupt::typelevel::Interrupt;
} }
macro_rules! impl_qspi { macro_rules! impl_qspi {
@ -659,7 +659,7 @@ macro_rules! impl_qspi {
} }
} }
impl crate::qspi::Instance for peripherals::$type { impl crate::qspi::Instance for peripherals::$type {
type Interrupt = crate::interrupt::$irq; type Interrupt = crate::interrupt::typelevel::$irq;
} }
}; };
} }

View File

@ -12,7 +12,7 @@ use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker; use embassy_sync::waitqueue::AtomicWaker;
use crate::interrupt::Interrupt; use crate::interrupt::typelevel::Interrupt;
use crate::{interrupt, Peripheral}; use crate::{interrupt, Peripheral};
/// Interrupt handler. /// Interrupt handler.
@ -20,7 +20,7 @@ pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>, _phantom: PhantomData<T>,
} }
impl<T: Instance> interrupt::Handler<T::Interrupt> for InterruptHandler<T> { impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() { unsafe fn on_interrupt() {
let s = T::state(); let s = T::state();
let r = T::regs(); let r = T::regs();
@ -89,7 +89,7 @@ impl<'d, T: Instance> Rng<'d, T> {
/// The synchronous API is safe. /// The synchronous API is safe.
pub fn new( pub fn new(
rng: impl Peripheral<P = T> + 'd, rng: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::Binding<T::Interrupt, InterruptHandler<T>> + 'd, _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
) -> Self { ) -> Self {
into_ref!(rng); into_ref!(rng);
@ -255,7 +255,7 @@ pub(crate) mod sealed {
/// RNG peripheral instance. /// RNG peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send { pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
/// Interrupt for this peripheral. /// Interrupt for this peripheral.
type Interrupt: Interrupt; type Interrupt: interrupt::typelevel::Interrupt;
} }
macro_rules! impl_rng { macro_rules! impl_rng {
@ -270,7 +270,7 @@ macro_rules! impl_rng {
} }
} }
impl crate::rng::Instance for peripherals::$type { impl crate::rng::Instance for peripherals::$type {
type Interrupt = crate::interrupt::$irq; type Interrupt = crate::interrupt::typelevel::$irq;
} }
}; };
} }

View File

@ -6,7 +6,6 @@ use core::future::poll_fn;
use core::sync::atomic::{compiler_fence, Ordering}; use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll; use core::task::Poll;
use embassy_cortex_m::interrupt::Interrupt;
use embassy_hal_common::drop::OnDrop; use embassy_hal_common::drop::OnDrop;
use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker; use embassy_sync::waitqueue::AtomicWaker;
@ -18,6 +17,7 @@ use saadc::oversample::OVERSAMPLE_A;
use saadc::resolution::VAL_A; use saadc::resolution::VAL_A;
use self::sealed::Input as _; use self::sealed::Input as _;
use crate::interrupt::InterruptExt;
use crate::ppi::{ConfigurableChannel, Event, Ppi, Task}; use crate::ppi::{ConfigurableChannel, Event, Ppi, Task};
use crate::timer::{Frequency, Instance as TimerInstance, Timer}; use crate::timer::{Frequency, Instance as TimerInstance, Timer};
use crate::{interrupt, pac, peripherals, Peripheral}; use crate::{interrupt, pac, peripherals, Peripheral};
@ -33,7 +33,7 @@ pub struct InterruptHandler {
_private: (), _private: (),
} }
impl interrupt::Handler<interrupt::SAADC> for InterruptHandler { impl interrupt::typelevel::Handler<interrupt::typelevel::SAADC> for InterruptHandler {
unsafe fn on_interrupt() { unsafe fn on_interrupt() {
let r = unsafe { &*SAADC::ptr() }; let r = unsafe { &*SAADC::ptr() };
@ -144,7 +144,7 @@ impl<'d, const N: usize> Saadc<'d, N> {
/// Create a new SAADC driver. /// Create a new SAADC driver.
pub fn new( pub fn new(
saadc: impl Peripheral<P = peripherals::SAADC> + 'd, saadc: impl Peripheral<P = peripherals::SAADC> + 'd,
_irq: impl interrupt::Binding<interrupt::SAADC, InterruptHandler> + 'd, _irq: impl interrupt::typelevel::Binding<interrupt::typelevel::SAADC, InterruptHandler> + 'd,
config: Config, config: Config,
channel_configs: [ChannelConfig; N], channel_configs: [ChannelConfig; N],
) -> Self { ) -> Self {
@ -189,8 +189,8 @@ impl<'d, const N: usize> Saadc<'d, N> {
// Disable all events interrupts // Disable all events interrupts
r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) }); r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) });
interrupt::SAADC::unpend(); interrupt::SAADC.unpend();
unsafe { interrupt::SAADC::enable() }; unsafe { interrupt::SAADC.enable() };
Self { _p: saadc } Self { _p: saadc }
} }

View File

@ -15,9 +15,9 @@ pub use pac::spim0::frequency::FREQUENCY_A as Frequency;
use crate::chip::FORCE_COPY_BUFFER_SIZE; use crate::chip::FORCE_COPY_BUFFER_SIZE;
use crate::gpio::sealed::Pin as _; use crate::gpio::sealed::Pin as _;
use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits};
use crate::interrupt::{self, Interrupt}; use crate::interrupt::typelevel::Interrupt;
use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut};
use crate::{pac, Peripheral}; use crate::{interrupt, pac, Peripheral};
/// SPIM error /// SPIM error
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -63,7 +63,7 @@ pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>, _phantom: PhantomData<T>,
} }
impl<T: Instance> interrupt::Handler<T::Interrupt> for InterruptHandler<T> { impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() { unsafe fn on_interrupt() {
let r = T::regs(); let r = T::regs();
let s = T::state(); let s = T::state();
@ -84,7 +84,7 @@ impl<'d, T: Instance> Spim<'d, T> {
/// Create a new SPIM driver. /// Create a new SPIM driver.
pub fn new( pub fn new(
spim: impl Peripheral<P = T> + 'd, spim: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::Binding<T::Interrupt, InterruptHandler<T>> + 'd, _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
sck: impl Peripheral<P = impl GpioPin> + 'd, sck: impl Peripheral<P = impl GpioPin> + 'd,
miso: impl Peripheral<P = impl GpioPin> + 'd, miso: impl Peripheral<P = impl GpioPin> + 'd,
mosi: impl Peripheral<P = impl GpioPin> + 'd, mosi: impl Peripheral<P = impl GpioPin> + 'd,
@ -103,7 +103,7 @@ impl<'d, T: Instance> Spim<'d, T> {
/// Create a new SPIM driver, capable of TX only (MOSI only). /// Create a new SPIM driver, capable of TX only (MOSI only).
pub fn new_txonly( pub fn new_txonly(
spim: impl Peripheral<P = T> + 'd, spim: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::Binding<T::Interrupt, InterruptHandler<T>> + 'd, _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
sck: impl Peripheral<P = impl GpioPin> + 'd, sck: impl Peripheral<P = impl GpioPin> + 'd,
mosi: impl Peripheral<P = impl GpioPin> + 'd, mosi: impl Peripheral<P = impl GpioPin> + 'd,
config: Config, config: Config,
@ -115,7 +115,7 @@ impl<'d, T: Instance> Spim<'d, T> {
/// Create a new SPIM driver, capable of RX only (MISO only). /// Create a new SPIM driver, capable of RX only (MISO only).
pub fn new_rxonly( pub fn new_rxonly(
spim: impl Peripheral<P = T> + 'd, spim: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::Binding<T::Interrupt, InterruptHandler<T>> + 'd, _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
sck: impl Peripheral<P = impl GpioPin> + 'd, sck: impl Peripheral<P = impl GpioPin> + 'd,
miso: impl Peripheral<P = impl GpioPin> + 'd, miso: impl Peripheral<P = impl GpioPin> + 'd,
config: Config, config: Config,
@ -408,7 +408,7 @@ pub(crate) mod sealed {
/// SPIM peripheral instance /// SPIM peripheral instance
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static { pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
/// Interrupt for this peripheral. /// Interrupt for this peripheral.
type Interrupt: Interrupt; type Interrupt: interrupt::typelevel::Interrupt;
} }
macro_rules! impl_spim { macro_rules! impl_spim {
@ -423,7 +423,7 @@ macro_rules! impl_spim {
} }
} }
impl crate::spim::Instance for peripherals::$type { impl crate::spim::Instance for peripherals::$type {
type Interrupt = crate::interrupt::$irq; type Interrupt = crate::interrupt::typelevel::$irq;
} }
}; };
} }

View File

@ -13,9 +13,9 @@ pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MO
use crate::chip::FORCE_COPY_BUFFER_SIZE; use crate::chip::FORCE_COPY_BUFFER_SIZE;
use crate::gpio::sealed::Pin as _; use crate::gpio::sealed::Pin as _;
use crate::gpio::{self, AnyPin, Pin as GpioPin}; use crate::gpio::{self, AnyPin, Pin as GpioPin};
use crate::interrupt::{self, Interrupt}; use crate::interrupt::typelevel::Interrupt;
use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut}; use crate::util::{slice_in_ram_or, slice_ptr_parts, slice_ptr_parts_mut};
use crate::{pac, Peripheral}; use crate::{interrupt, pac, Peripheral};
/// SPIS error /// SPIS error
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -68,7 +68,7 @@ pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>, _phantom: PhantomData<T>,
} }
impl<T: Instance> interrupt::Handler<T::Interrupt> for InterruptHandler<T> { impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() { unsafe fn on_interrupt() {
let r = T::regs(); let r = T::regs();
let s = T::state(); let s = T::state();
@ -94,7 +94,7 @@ impl<'d, T: Instance> Spis<'d, T> {
/// Create a new SPIS driver. /// Create a new SPIS driver.
pub fn new( pub fn new(
spis: impl Peripheral<P = T> + 'd, spis: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::Binding<T::Interrupt, InterruptHandler<T>> + 'd, _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
cs: impl Peripheral<P = impl GpioPin> + 'd, cs: impl Peripheral<P = impl GpioPin> + 'd,
sck: impl Peripheral<P = impl GpioPin> + 'd, sck: impl Peripheral<P = impl GpioPin> + 'd,
miso: impl Peripheral<P = impl GpioPin> + 'd, miso: impl Peripheral<P = impl GpioPin> + 'd,
@ -115,7 +115,7 @@ impl<'d, T: Instance> Spis<'d, T> {
/// Create a new SPIS driver, capable of TX only (MISO only). /// Create a new SPIS driver, capable of TX only (MISO only).
pub fn new_txonly( pub fn new_txonly(
spis: impl Peripheral<P = T> + 'd, spis: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::Binding<T::Interrupt, InterruptHandler<T>> + 'd, _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
cs: impl Peripheral<P = impl GpioPin> + 'd, cs: impl Peripheral<P = impl GpioPin> + 'd,
sck: impl Peripheral<P = impl GpioPin> + 'd, sck: impl Peripheral<P = impl GpioPin> + 'd,
miso: impl Peripheral<P = impl GpioPin> + 'd, miso: impl Peripheral<P = impl GpioPin> + 'd,
@ -128,7 +128,7 @@ impl<'d, T: Instance> Spis<'d, T> {
/// Create a new SPIS driver, capable of RX only (MOSI only). /// Create a new SPIS driver, capable of RX only (MOSI only).
pub fn new_rxonly( pub fn new_rxonly(
spis: impl Peripheral<P = T> + 'd, spis: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::Binding<T::Interrupt, InterruptHandler<T>> + 'd, _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
cs: impl Peripheral<P = impl GpioPin> + 'd, cs: impl Peripheral<P = impl GpioPin> + 'd,
sck: impl Peripheral<P = impl GpioPin> + 'd, sck: impl Peripheral<P = impl GpioPin> + 'd,
mosi: impl Peripheral<P = impl GpioPin> + 'd, mosi: impl Peripheral<P = impl GpioPin> + 'd,
@ -480,7 +480,7 @@ pub(crate) mod sealed {
/// SPIS peripheral instance /// SPIS peripheral instance
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static { pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
/// Interrupt for this peripheral. /// Interrupt for this peripheral.
type Interrupt: Interrupt; type Interrupt: interrupt::typelevel::Interrupt;
} }
macro_rules! impl_spis { macro_rules! impl_spis {
@ -495,7 +495,7 @@ macro_rules! impl_spis {
} }
} }
impl crate::spis::Instance for peripherals::$type { impl crate::spis::Instance for peripherals::$type {
type Interrupt = crate::interrupt::$irq; type Interrupt = crate::interrupt::typelevel::$irq;
} }
}; };
} }

View File

@ -8,7 +8,7 @@ use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker; use embassy_sync::waitqueue::AtomicWaker;
use fixed::types::I30F2; use fixed::types::I30F2;
use crate::interrupt::Interrupt; use crate::interrupt::InterruptExt;
use crate::peripherals::TEMP; use crate::peripherals::TEMP;
use crate::{interrupt, pac, Peripheral}; use crate::{interrupt, pac, Peripheral};
@ -17,7 +17,7 @@ pub struct InterruptHandler {
_private: (), _private: (),
} }
impl interrupt::Handler<interrupt::TEMP> for InterruptHandler { impl interrupt::typelevel::Handler<interrupt::typelevel::TEMP> for InterruptHandler {
unsafe fn on_interrupt() { unsafe fn on_interrupt() {
let r = unsafe { &*pac::TEMP::PTR }; let r = unsafe { &*pac::TEMP::PTR };
r.intenclr.write(|w| w.datardy().clear()); r.intenclr.write(|w| w.datardy().clear());
@ -36,13 +36,13 @@ impl<'d> Temp<'d> {
/// Create a new temperature sensor driver. /// Create a new temperature sensor driver.
pub fn new( pub fn new(
_peri: impl Peripheral<P = TEMP> + 'd, _peri: impl Peripheral<P = TEMP> + 'd,
_irq: impl interrupt::Binding<interrupt::TEMP, InterruptHandler> + 'd, _irq: impl interrupt::typelevel::Binding<interrupt::typelevel::TEMP, InterruptHandler> + 'd,
) -> Self { ) -> Self {
into_ref!(_peri); into_ref!(_peri);
// Enable interrupt that signals temperature values // Enable interrupt that signals temperature values
interrupt::TEMP::unpend(); interrupt::TEMP.unpend();
unsafe { interrupt::TEMP::enable() }; unsafe { interrupt::TEMP.enable() };
Self { _peri } Self { _peri }
} }

View File

@ -7,7 +7,7 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex; use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex;
use embassy_time::driver::{AlarmHandle, Driver}; use embassy_time::driver::{AlarmHandle, Driver};
use crate::interrupt::Interrupt; use crate::interrupt::InterruptExt;
use crate::{interrupt, pac}; use crate::{interrupt, pac};
fn rtc() -> &'static pac::rtc0::RegisterBlock { fn rtc() -> &'static pac::rtc0::RegisterBlock {
@ -142,8 +142,8 @@ impl RtcDriver {
// Wait for clear // Wait for clear
while r.counter.read().bits() != 0 {} while r.counter.read().bits() != 0 {}
interrupt::RTC1::set_priority(irq_prio); interrupt::RTC1.set_priority(irq_prio);
unsafe { interrupt::RTC1::enable() }; unsafe { interrupt::RTC1.enable() };
} }
fn on_interrupt(&self) { fn on_interrupt(&self) {
@ -295,6 +295,7 @@ impl Driver for RtcDriver {
} }
} }
#[cfg(feature = "rt")]
#[interrupt] #[interrupt]
fn RTC1() { fn RTC1() {
DRIVER.on_interrupt() DRIVER.on_interrupt()

View File

@ -8,7 +8,6 @@
use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_hal_common::{into_ref, PeripheralRef};
use crate::interrupt::Interrupt;
use crate::ppi::{Event, Task}; use crate::ppi::{Event, Task};
use crate::{pac, Peripheral}; use crate::{pac, Peripheral};
@ -29,7 +28,7 @@ pub(crate) mod sealed {
/// Basic Timer instance. /// Basic Timer instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send { pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
/// Interrupt for this peripheral. /// Interrupt for this peripheral.
type Interrupt: Interrupt; type Interrupt: crate::interrupt::typelevel::Interrupt;
} }
/// Extended timer instance. /// Extended timer instance.
@ -44,7 +43,7 @@ macro_rules! impl_timer {
} }
} }
impl crate::timer::Instance for peripherals::$type { impl crate::timer::Instance for peripherals::$type {
type Interrupt = crate::interrupt::$irq; type Interrupt = crate::interrupt::typelevel::$irq;
} }
}; };
($type:ident, $pac_type:ident, $irq:ident) => { ($type:ident, $pac_type:ident, $irq:ident) => {

View File

@ -16,9 +16,9 @@ use embassy_time::{Duration, Instant};
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
use crate::gpio::Pin as GpioPin; use crate::gpio::Pin as GpioPin;
use crate::interrupt::{self, Interrupt}; use crate::interrupt::typelevel::Interrupt;
use crate::util::{slice_in_ram, slice_in_ram_or}; use crate::util::{slice_in_ram, slice_in_ram_or};
use crate::{gpio, pac, Peripheral}; use crate::{gpio, interrupt, pac, Peripheral};
/// TWI frequency /// TWI frequency
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@ -98,7 +98,7 @@ pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>, _phantom: PhantomData<T>,
} }
impl<T: Instance> interrupt::Handler<T::Interrupt> for InterruptHandler<T> { impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() { unsafe fn on_interrupt() {
let r = T::regs(); let r = T::regs();
let s = T::state(); let s = T::state();
@ -123,7 +123,7 @@ impl<'d, T: Instance> Twim<'d, T> {
/// Create a new TWI driver. /// Create a new TWI driver.
pub fn new( pub fn new(
twim: impl Peripheral<P = T> + 'd, twim: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::Binding<T::Interrupt, InterruptHandler<T>> + 'd, _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
sda: impl Peripheral<P = impl GpioPin> + 'd, sda: impl Peripheral<P = impl GpioPin> + 'd,
scl: impl Peripheral<P = impl GpioPin> + 'd, scl: impl Peripheral<P = impl GpioPin> + 'd,
config: Config, config: Config,
@ -750,7 +750,7 @@ pub(crate) mod sealed {
/// TWIM peripheral instance. /// TWIM peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static { pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
/// Interrupt for this peripheral. /// Interrupt for this peripheral.
type Interrupt: Interrupt; type Interrupt: interrupt::typelevel::Interrupt;
} }
macro_rules! impl_twim { macro_rules! impl_twim {
@ -765,7 +765,7 @@ macro_rules! impl_twim {
} }
} }
impl crate::twim::Instance for peripherals::$type { impl crate::twim::Instance for peripherals::$type {
type Interrupt = crate::interrupt::$irq; type Interrupt = crate::interrupt::typelevel::$irq;
} }
}; };
} }

View File

@ -15,9 +15,9 @@ use embassy_time::{Duration, Instant};
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
use crate::gpio::Pin as GpioPin; use crate::gpio::Pin as GpioPin;
use crate::interrupt::{self, Interrupt}; use crate::interrupt::typelevel::Interrupt;
use crate::util::slice_in_ram_or; use crate::util::slice_in_ram_or;
use crate::{gpio, pac, Peripheral}; use crate::{gpio, interrupt, pac, Peripheral};
/// TWIS config. /// TWIS config.
#[non_exhaustive] #[non_exhaustive]
@ -114,7 +114,7 @@ pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>, _phantom: PhantomData<T>,
} }
impl<T: Instance> interrupt::Handler<T::Interrupt> for InterruptHandler<T> { impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() { unsafe fn on_interrupt() {
let r = T::regs(); let r = T::regs();
let s = T::state(); let s = T::state();
@ -143,7 +143,7 @@ impl<'d, T: Instance> Twis<'d, T> {
/// Create a new TWIS driver. /// Create a new TWIS driver.
pub fn new( pub fn new(
twis: impl Peripheral<P = T> + 'd, twis: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::Binding<T::Interrupt, InterruptHandler<T>> + 'd, _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
sda: impl Peripheral<P = impl GpioPin> + 'd, sda: impl Peripheral<P = impl GpioPin> + 'd,
scl: impl Peripheral<P = impl GpioPin> + 'd, scl: impl Peripheral<P = impl GpioPin> + 'd,
config: Config, config: Config,
@ -778,7 +778,7 @@ pub(crate) mod sealed {
/// TWIS peripheral instance. /// TWIS peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static { pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static {
/// Interrupt for this peripheral. /// Interrupt for this peripheral.
type Interrupt: Interrupt; type Interrupt: interrupt::typelevel::Interrupt;
} }
macro_rules! impl_twis { macro_rules! impl_twis {
@ -793,7 +793,7 @@ macro_rules! impl_twis {
} }
} }
impl crate::twis::Instance for peripherals::$type { impl crate::twis::Instance for peripherals::$type {
type Interrupt = crate::interrupt::$irq; type Interrupt = crate::interrupt::typelevel::$irq;
} }
}; };
} }

View File

@ -27,11 +27,11 @@ pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Pari
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
use crate::gpio::sealed::Pin as _; use crate::gpio::sealed::Pin as _;
use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits}; use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits};
use crate::interrupt::{self, Interrupt}; use crate::interrupt::typelevel::Interrupt;
use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task}; use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task};
use crate::timer::{Frequency, Instance as TimerInstance, Timer}; use crate::timer::{Frequency, Instance as TimerInstance, Timer};
use crate::util::slice_in_ram_or; use crate::util::slice_in_ram_or;
use crate::{pac, Peripheral}; use crate::{interrupt, pac, Peripheral};
/// UARTE config. /// UARTE config.
#[derive(Clone)] #[derive(Clone)]
@ -68,7 +68,7 @@ pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>, _phantom: PhantomData<T>,
} }
impl<T: Instance> interrupt::Handler<T::Interrupt> for InterruptHandler<T> { impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() { unsafe fn on_interrupt() {
let r = T::regs(); let r = T::regs();
let s = T::state(); let s = T::state();
@ -108,7 +108,7 @@ impl<'d, T: Instance> Uarte<'d, T> {
/// Create a new UARTE without hardware flow control /// Create a new UARTE without hardware flow control
pub fn new( pub fn new(
uarte: impl Peripheral<P = T> + 'd, uarte: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::Binding<T::Interrupt, InterruptHandler<T>> + 'd, _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
rxd: impl Peripheral<P = impl GpioPin> + 'd, rxd: impl Peripheral<P = impl GpioPin> + 'd,
txd: impl Peripheral<P = impl GpioPin> + 'd, txd: impl Peripheral<P = impl GpioPin> + 'd,
config: Config, config: Config,
@ -120,7 +120,7 @@ impl<'d, T: Instance> Uarte<'d, T> {
/// Create a new UARTE with hardware flow control (RTS/CTS) /// Create a new UARTE with hardware flow control (RTS/CTS)
pub fn new_with_rtscts( pub fn new_with_rtscts(
uarte: impl Peripheral<P = T> + 'd, uarte: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::Binding<T::Interrupt, InterruptHandler<T>> + 'd, _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
rxd: impl Peripheral<P = impl GpioPin> + 'd, rxd: impl Peripheral<P = impl GpioPin> + 'd,
txd: impl Peripheral<P = impl GpioPin> + 'd, txd: impl Peripheral<P = impl GpioPin> + 'd,
cts: impl Peripheral<P = impl GpioPin> + 'd, cts: impl Peripheral<P = impl GpioPin> + 'd,
@ -205,50 +205,7 @@ impl<'d, T: Instance> Uarte<'d, T> {
ppi_ch1: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd, ppi_ch1: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
ppi_ch2: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd, ppi_ch2: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
) -> (UarteTx<'d, T>, UarteRxWithIdle<'d, T, U>) { ) -> (UarteTx<'d, T>, UarteRxWithIdle<'d, T, U>) {
let timer = Timer::new(timer); (self.tx, self.rx.with_idle(timer, ppi_ch1, ppi_ch2))
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 /// Return the endtx event for use with PPI
@ -313,7 +270,7 @@ impl<'d, T: Instance> UarteTx<'d, T> {
/// Create a new tx-only UARTE without hardware flow control /// Create a new tx-only UARTE without hardware flow control
pub fn new( pub fn new(
uarte: impl Peripheral<P = T> + 'd, uarte: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::Binding<T::Interrupt, InterruptHandler<T>> + 'd, _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
txd: impl Peripheral<P = impl GpioPin> + 'd, txd: impl Peripheral<P = impl GpioPin> + 'd,
config: Config, config: Config,
) -> Self { ) -> Self {
@ -324,7 +281,7 @@ impl<'d, T: Instance> UarteTx<'d, T> {
/// Create a new tx-only UARTE with hardware flow control (RTS/CTS) /// Create a new tx-only UARTE with hardware flow control (RTS/CTS)
pub fn new_with_rtscts( pub fn new_with_rtscts(
uarte: impl Peripheral<P = T> + 'd, uarte: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::Binding<T::Interrupt, InterruptHandler<T>> + 'd, _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
txd: impl Peripheral<P = impl GpioPin> + 'd, txd: impl Peripheral<P = impl GpioPin> + 'd,
cts: impl Peripheral<P = impl GpioPin> + 'd, cts: impl Peripheral<P = impl GpioPin> + 'd,
config: Config, config: Config,
@ -509,7 +466,7 @@ impl<'d, T: Instance> UarteRx<'d, T> {
/// Create a new rx-only UARTE without hardware flow control /// Create a new rx-only UARTE without hardware flow control
pub fn new( pub fn new(
uarte: impl Peripheral<P = T> + 'd, uarte: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::Binding<T::Interrupt, InterruptHandler<T>> + 'd, _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
rxd: impl Peripheral<P = impl GpioPin> + 'd, rxd: impl Peripheral<P = impl GpioPin> + 'd,
config: Config, config: Config,
) -> Self { ) -> Self {
@ -520,7 +477,7 @@ impl<'d, T: Instance> UarteRx<'d, T> {
/// Create a new rx-only UARTE with hardware flow control (RTS/CTS) /// Create a new rx-only UARTE with hardware flow control (RTS/CTS)
pub fn new_with_rtscts( pub fn new_with_rtscts(
uarte: impl Peripheral<P = T> + 'd, uarte: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::Binding<T::Interrupt, InterruptHandler<T>> + 'd, _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
rxd: impl Peripheral<P = impl GpioPin> + 'd, rxd: impl Peripheral<P = impl GpioPin> + 'd,
rts: impl Peripheral<P = impl GpioPin> + 'd, rts: impl Peripheral<P = impl GpioPin> + 'd,
config: Config, config: Config,
@ -563,6 +520,56 @@ impl<'d, T: Instance> UarteRx<'d, T> {
Self { _p: uarte } Self { _p: uarte }
} }
/// Upgrade to an instance that supports idle line detection.
pub fn 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,
) -> UarteRxWithIdle<'d, T, U> {
let 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();
UarteRxWithIdle {
rx: self,
timer,
ppi_ch1: ppi_ch1,
_ppi_ch2: ppi_ch2,
}
}
/// Read bytes until the buffer is filled. /// Read bytes until the buffer is filled.
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
if buffer.len() == 0 { if buffer.len() == 0 {
@ -889,7 +896,7 @@ pub(crate) mod sealed {
/// UARTE peripheral instance. /// UARTE peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send { pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
/// Interrupt for this peripheral. /// Interrupt for this peripheral.
type Interrupt: Interrupt; type Interrupt: interrupt::typelevel::Interrupt;
} }
macro_rules! impl_uarte { macro_rules! impl_uarte {
@ -908,7 +915,7 @@ macro_rules! impl_uarte {
} }
} }
impl crate::uarte::Instance for peripherals::$type { impl crate::uarte::Instance for peripherals::$type {
type Interrupt = crate::interrupt::$irq; type Interrupt = crate::interrupt::typelevel::$irq;
} }
}; };
} }

View File

@ -18,9 +18,9 @@ use embassy_usb_driver::{Direction, EndpointAddress, EndpointError, EndpointInfo
use pac::usbd::RegisterBlock; use pac::usbd::RegisterBlock;
use self::vbus_detect::VbusDetect; use self::vbus_detect::VbusDetect;
use crate::interrupt::{self, Interrupt}; use crate::interrupt::typelevel::Interrupt;
use crate::util::slice_in_ram; use crate::util::slice_in_ram;
use crate::{pac, Peripheral}; use crate::{interrupt, pac, Peripheral};
const NEW_AW: AtomicWaker = AtomicWaker::new(); const NEW_AW: AtomicWaker = AtomicWaker::new();
static BUS_WAKER: AtomicWaker = NEW_AW; static BUS_WAKER: AtomicWaker = NEW_AW;
@ -34,7 +34,7 @@ pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>, _phantom: PhantomData<T>,
} }
impl<T: Instance> interrupt::Handler<T::Interrupt> for InterruptHandler<T> { impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() { unsafe fn on_interrupt() {
let regs = T::regs(); let regs = T::regs();
@ -98,7 +98,7 @@ impl<'d, T: Instance, V: VbusDetect> Driver<'d, T, V> {
/// Create a new USB driver. /// Create a new USB driver.
pub fn new( pub fn new(
usb: impl Peripheral<P = T> + 'd, usb: impl Peripheral<P = T> + 'd,
_irq: impl interrupt::Binding<T::Interrupt, InterruptHandler<T>> + 'd, _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
vbus_detect: V, vbus_detect: V,
) -> Self { ) -> Self {
into_ref!(usb); into_ref!(usb);
@ -804,7 +804,7 @@ pub(crate) mod sealed {
/// USB peripheral instance. /// USB peripheral instance.
pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send { pub trait Instance: Peripheral<P = Self> + sealed::Instance + 'static + Send {
/// Interrupt for this peripheral. /// Interrupt for this peripheral.
type Interrupt: Interrupt; type Interrupt: interrupt::typelevel::Interrupt;
} }
macro_rules! impl_usb { macro_rules! impl_usb {
@ -815,7 +815,7 @@ macro_rules! impl_usb {
} }
} }
impl crate::usb::Instance for peripherals::$type { impl crate::usb::Instance for peripherals::$type {
type Interrupt = crate::interrupt::$irq; type Interrupt = crate::interrupt::typelevel::$irq;
} }
}; };
} }

View File

@ -7,8 +7,8 @@ use core::task::Poll;
use embassy_sync::waitqueue::AtomicWaker; use embassy_sync::waitqueue::AtomicWaker;
use super::BUS_WAKER; use super::BUS_WAKER;
use crate::interrupt::{self, Interrupt}; use crate::interrupt::typelevel::Interrupt;
use crate::pac; use crate::{interrupt, pac};
/// Trait for detecting USB VBUS power. /// Trait for detecting USB VBUS power.
/// ///
@ -29,9 +29,9 @@ pub trait VbusDetect {
} }
#[cfg(not(feature = "_nrf5340"))] #[cfg(not(feature = "_nrf5340"))]
type UsbRegIrq = interrupt::POWER_CLOCK; type UsbRegIrq = interrupt::typelevel::POWER_CLOCK;
#[cfg(feature = "_nrf5340")] #[cfg(feature = "_nrf5340")]
type UsbRegIrq = interrupt::USBREGULATOR; type UsbRegIrq = interrupt::typelevel::USBREGULATOR;
#[cfg(not(feature = "_nrf5340"))] #[cfg(not(feature = "_nrf5340"))]
type UsbRegPeri = pac::POWER; type UsbRegPeri = pac::POWER;
@ -43,7 +43,7 @@ pub struct InterruptHandler {
_private: (), _private: (),
} }
impl interrupt::Handler<UsbRegIrq> for InterruptHandler { impl interrupt::typelevel::Handler<UsbRegIrq> for InterruptHandler {
unsafe fn on_interrupt() { unsafe fn on_interrupt() {
let regs = unsafe { &*UsbRegPeri::ptr() }; let regs = unsafe { &*UsbRegPeri::ptr() };
@ -77,7 +77,7 @@ static POWER_WAKER: AtomicWaker = AtomicWaker::new();
impl HardwareVbusDetect { impl HardwareVbusDetect {
/// Create a new `VbusDetectNative`. /// Create a new `VbusDetectNative`.
pub fn new(_irq: impl interrupt::Binding<UsbRegIrq, InterruptHandler> + 'static) -> Self { pub fn new(_irq: impl interrupt::typelevel::Binding<UsbRegIrq, InterruptHandler> + 'static) -> Self {
let regs = unsafe { &*UsbRegPeri::ptr() }; let regs = unsafe { &*UsbRegPeri::ptr() };
UsbRegIrq::unpend(); UsbRegIrq::unpend();

View File

@ -13,7 +13,8 @@ flavors = [
] ]
[features] [features]
default = [ "rp-pac/rt" ] default = [ "rt" ]
rt = [ "rp-pac/rt" ]
defmt = ["dep:defmt", "embassy-usb-driver?/defmt", "embassy-hal-common/defmt"] defmt = ["dep:defmt", "embassy-usb-driver?/defmt", "embassy-hal-common/defmt"]
@ -41,8 +42,13 @@ boot2-ram-memcpy = []
boot2-w25q080 = [] boot2-w25q080 = []
boot2-w25x10cl = [] boot2-w25x10cl = []
# Indicate code is running from RAM.
# Set this if all code is in RAM, and the cores never access memory-mapped flash memory through XIP.
# This allows the flash driver to not force pausing execution on both cores when doing flash operations.
run-from-ram = []
# Enable nightly-only features # Enable nightly-only features
nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io"] nightly = ["embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io"]
# Implement embedded-hal 1.0 alpha traits. # Implement embedded-hal 1.0 alpha traits.
# Implement embedded-hal-async traits if `nightly` is set as well. # Implement embedded-hal-async traits if `nightly` is set as well.
@ -50,11 +56,9 @@ unstable-traits = ["embedded-hal-1", "embedded-hal-nb"]
[dependencies] [dependencies]
embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
embassy-executor = { version = "0.2.0", path = "../embassy-executor" }
embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] } embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-2"]} embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common", features = ["cortex-m", "prio-bits-2"] }
embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" }
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true }
atomic-polyfill = "1.0.1" atomic-polyfill = "1.0.1"
@ -72,7 +76,7 @@ embedded-storage = { version = "0.3" }
rand_core = "0.6.4" rand_core = "0.6.4"
fixed = "1.23.1" fixed = "1.23.1"
rp-pac = { version = "4" } rp-pac = { version = "6" }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true} embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true}
@ -85,5 +89,5 @@ pio = {version= "0.2.1" }
rp2040-boot2 = "0.3" rp2040-boot2 = "0.3"
[dev-dependencies] [dev-dependencies]
embassy-executor = { version = "0.2.0", path = "../embassy-executor", features = ["arch-std", "executor-thread"] } embassy-executor = { version = "0.2.0", path = "../embassy-executor", features = ["nightly", "arch-std", "executor-thread"] }
static_cell = "1.1" static_cell = "1.1"

View File

@ -3,14 +3,14 @@ use core::marker::PhantomData;
use core::sync::atomic::{compiler_fence, Ordering}; use core::sync::atomic::{compiler_fence, Ordering};
use core::task::Poll; use core::task::Poll;
use embassy_cortex_m::interrupt::{Binding, Interrupt};
use embassy_sync::waitqueue::AtomicWaker; use embassy_sync::waitqueue::AtomicWaker;
use embedded_hal_02::adc::{Channel, OneShot}; use embedded_hal_02::adc::{Channel, OneShot};
use crate::gpio::Pin; use crate::gpio::Pin;
use crate::interrupt::{self, ADC_IRQ_FIFO}; use crate::interrupt::typelevel::Binding;
use crate::interrupt::InterruptExt;
use crate::peripherals::ADC; use crate::peripherals::ADC;
use crate::{pac, peripherals, Peripheral}; use crate::{interrupt, pac, peripherals, Peripheral};
static WAKER: AtomicWaker = AtomicWaker::new(); static WAKER: AtomicWaker = AtomicWaker::new();
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -47,105 +47,97 @@ impl<'d> Adc<'d> {
pub fn new( pub fn new(
_inner: impl Peripheral<P = ADC> + 'd, _inner: impl Peripheral<P = ADC> + 'd,
_irq: impl Binding<ADC_IRQ_FIFO, InterruptHandler>, _irq: impl Binding<interrupt::typelevel::ADC_IRQ_FIFO, InterruptHandler>,
_config: Config, _config: Config,
) -> Self { ) -> Self {
unsafe { let reset = Self::reset();
let reset = Self::reset(); crate::reset::reset(reset);
crate::reset::reset(reset); crate::reset::unreset_wait(reset);
crate::reset::unreset_wait(reset); let r = Self::regs();
let r = Self::regs(); // Enable ADC
// Enable ADC r.cs().write(|w| w.set_en(true));
r.cs().write(|w| w.set_en(true)); // Wait for ADC ready
// Wait for ADC ready while !r.cs().read().ready() {}
while !r.cs().read().ready() {}
}
// Setup IRQ // Setup IRQ
unsafe { interrupt::ADC_IRQ_FIFO.unpend();
ADC_IRQ_FIFO::unpend(); unsafe { interrupt::ADC_IRQ_FIFO.enable() };
ADC_IRQ_FIFO::enable();
};
Self { phantom: PhantomData } Self { phantom: PhantomData }
} }
async fn wait_for_ready() { async fn wait_for_ready() {
let r = Self::regs(); let r = Self::regs();
unsafe { r.inte().write(|w| w.set_fifo(true));
r.inte().write(|w| w.set_fifo(true)); compiler_fence(Ordering::SeqCst);
compiler_fence(Ordering::SeqCst); poll_fn(|cx| {
poll_fn(|cx| { WAKER.register(cx.waker());
WAKER.register(cx.waker()); if r.cs().read().ready() {
if r.cs().read().ready() { return Poll::Ready(());
return Poll::Ready(()); }
} Poll::Pending
Poll::Pending })
}) .await;
.await;
}
} }
pub async fn read<PIN: Channel<Adc<'d>, ID = u8> + Pin>(&mut self, pin: &mut PIN) -> u16 { pub async fn read<PIN: Channel<Adc<'d>, ID = u8> + Pin>(&mut self, pin: &mut PIN) -> u16 {
let r = Self::regs(); let r = Self::regs();
unsafe { // disable pull-down and pull-up resistors
// disable pull-down and pull-up resistors // pull-down resistors are enabled by default
// pull-down resistors are enabled by default pin.pad_ctrl().modify(|w| {
pin.pad_ctrl().modify(|w| { w.set_ie(true);
w.set_ie(true); let (pu, pd) = (false, false);
let (pu, pd) = (false, false); w.set_pue(pu);
w.set_pue(pu); w.set_pde(pd);
w.set_pde(pd); });
}); r.cs().modify(|w| {
r.cs().modify(|w| { w.set_ainsel(PIN::channel());
w.set_ainsel(PIN::channel()); w.set_start_once(true)
w.set_start_once(true) });
}); Self::wait_for_ready().await;
Self::wait_for_ready().await; r.result().read().result().into()
r.result().read().result().into()
}
} }
pub async fn read_temperature(&mut self) -> u16 { pub async fn read_temperature(&mut self) -> u16 {
let r = Self::regs(); let r = Self::regs();
unsafe { r.cs().modify(|w| w.set_ts_en(true));
r.cs().modify(|w| w.set_ts_en(true)); if !r.cs().read().ready() {
if !r.cs().read().ready() {
Self::wait_for_ready().await;
}
r.cs().modify(|w| {
w.set_ainsel(4);
w.set_start_once(true)
});
Self::wait_for_ready().await; Self::wait_for_ready().await;
r.result().read().result().into()
} }
r.cs().modify(|w| {
w.set_ainsel(4);
w.set_start_once(true)
});
Self::wait_for_ready().await;
r.result().read().result().into()
} }
pub fn blocking_read<PIN: Channel<Adc<'d>, ID = u8>>(&mut self, _pin: &mut PIN) -> u16 { pub fn blocking_read<PIN: Channel<Adc<'d>, ID = u8> + Pin>(&mut self, pin: &mut PIN) -> u16 {
let r = Self::regs(); let r = Self::regs();
unsafe { pin.pad_ctrl().modify(|w| {
r.cs().modify(|w| { w.set_ie(true);
w.set_ainsel(PIN::channel()); let (pu, pd) = (false, false);
w.set_start_once(true) w.set_pue(pu);
}); w.set_pde(pd);
while !r.cs().read().ready() {} });
r.result().read().result().into() r.cs().modify(|w| {
} w.set_ainsel(PIN::channel());
w.set_start_once(true)
});
while !r.cs().read().ready() {}
r.result().read().result().into()
} }
pub fn blocking_read_temperature(&mut self) -> u16 { pub fn blocking_read_temperature(&mut self) -> u16 {
let r = Self::regs(); let r = Self::regs();
unsafe { r.cs().modify(|w| w.set_ts_en(true));
r.cs().modify(|w| w.set_ts_en(true)); while !r.cs().read().ready() {}
while !r.cs().read().ready() {} r.cs().modify(|w| {
r.cs().modify(|w| { w.set_ainsel(4);
w.set_ainsel(4); w.set_start_once(true)
w.set_start_once(true) });
}); while !r.cs().read().ready() {}
while !r.cs().read().ready() {} r.result().read().result().into()
r.result().read().result().into()
}
} }
} }
@ -164,7 +156,7 @@ pub struct InterruptHandler {
_empty: (), _empty: (),
} }
impl interrupt::Handler<ADC_IRQ_FIFO> for InterruptHandler { impl interrupt::typelevel::Handler<interrupt::typelevel::ADC_IRQ_FIFO> for InterruptHandler {
unsafe fn on_interrupt() { unsafe fn on_interrupt() {
let r = Adc::regs(); let r = Adc::regs();
r.inte().write(|w| w.set_fifo(false)); r.inte().write(|w| w.set_fifo(false));
@ -180,7 +172,7 @@ impl_pin!(PIN_29, 3);
impl<WORD, PIN> OneShot<Adc<'static>, WORD, PIN> for Adc<'static> impl<WORD, PIN> OneShot<Adc<'static>, WORD, PIN> for Adc<'static>
where where
WORD: From<u16>, WORD: From<u16>,
PIN: Channel<Adc<'static>, ID = u8>, PIN: Channel<Adc<'static>, ID = u8> + Pin,
{ {
type Error = (); type Error = ();
fn read(&mut self, pin: &mut PIN) -> nb::Result<WORD, Self::Error> { fn read(&mut self, pin: &mut PIN) -> nb::Result<WORD, Self::Error> {

View File

@ -46,13 +46,13 @@ static CLOCKS: Clocks = Clocks {
#[non_exhaustive] #[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PeriClkSrc { pub enum PeriClkSrc {
Sys = ClkPeriCtrlAuxsrc::CLK_SYS.0, Sys = ClkPeriCtrlAuxsrc::CLK_SYS as _,
PllSys = ClkPeriCtrlAuxsrc::CLKSRC_PLL_SYS.0, PllSys = ClkPeriCtrlAuxsrc::CLKSRC_PLL_SYS as _,
PllUsb = ClkPeriCtrlAuxsrc::CLKSRC_PLL_USB.0, PllUsb = ClkPeriCtrlAuxsrc::CLKSRC_PLL_USB as _,
Rosc = ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH.0, Rosc = ClkPeriCtrlAuxsrc::ROSC_CLKSRC_PH as _,
Xosc = ClkPeriCtrlAuxsrc::XOSC_CLKSRC.0, Xosc = ClkPeriCtrlAuxsrc::XOSC_CLKSRC as _,
// Gpin0 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN0.0, // Gpin0 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN0 as _ ,
// Gpin1 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN1.0, // Gpin1 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN1 as _ ,
} }
#[non_exhaustive] #[non_exhaustive]
@ -251,12 +251,12 @@ pub struct SysClkConfig {
#[non_exhaustive] #[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum UsbClkSrc { pub enum UsbClkSrc {
PllUsb = ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB.0, PllUsb = ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB as _,
PllSys = ClkUsbCtrlAuxsrc::CLKSRC_PLL_SYS.0, PllSys = ClkUsbCtrlAuxsrc::CLKSRC_PLL_SYS as _,
Rosc = ClkUsbCtrlAuxsrc::ROSC_CLKSRC_PH.0, Rosc = ClkUsbCtrlAuxsrc::ROSC_CLKSRC_PH as _,
Xosc = ClkUsbCtrlAuxsrc::XOSC_CLKSRC.0, Xosc = ClkUsbCtrlAuxsrc::XOSC_CLKSRC as _,
// Gpin0 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN0.0, // Gpin0 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN0 as _ ,
// Gpin1 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN1.0, // Gpin1 = ClkUsbCtrlAuxsrc::CLKSRC_GPIN1 as _ ,
} }
pub struct UsbClkConfig { pub struct UsbClkConfig {
@ -269,12 +269,12 @@ pub struct UsbClkConfig {
#[non_exhaustive] #[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AdcClkSrc { pub enum AdcClkSrc {
PllUsb = ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB.0, PllUsb = ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB as _,
PllSys = ClkAdcCtrlAuxsrc::CLKSRC_PLL_SYS.0, PllSys = ClkAdcCtrlAuxsrc::CLKSRC_PLL_SYS as _,
Rosc = ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH.0, Rosc = ClkAdcCtrlAuxsrc::ROSC_CLKSRC_PH as _,
Xosc = ClkAdcCtrlAuxsrc::XOSC_CLKSRC.0, Xosc = ClkAdcCtrlAuxsrc::XOSC_CLKSRC as _,
// Gpin0 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN0.0, // Gpin0 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN0 as _ ,
// Gpin1 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN1.0, // Gpin1 = ClkAdcCtrlAuxsrc::CLKSRC_GPIN1 as _ ,
} }
pub struct AdcClkConfig { pub struct AdcClkConfig {
@ -287,12 +287,12 @@ pub struct AdcClkConfig {
#[non_exhaustive] #[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RtcClkSrc { pub enum RtcClkSrc {
PllUsb = ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB.0, PllUsb = ClkRtcCtrlAuxsrc::CLKSRC_PLL_USB as _,
PllSys = ClkRtcCtrlAuxsrc::CLKSRC_PLL_SYS.0, PllSys = ClkRtcCtrlAuxsrc::CLKSRC_PLL_SYS as _,
Rosc = ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH.0, Rosc = ClkRtcCtrlAuxsrc::ROSC_CLKSRC_PH as _,
Xosc = ClkRtcCtrlAuxsrc::XOSC_CLKSRC.0, Xosc = ClkRtcCtrlAuxsrc::XOSC_CLKSRC as _,
// Gpin0 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN0.0, // Gpin0 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN0 as _ ,
// Gpin1 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN1.0, // Gpin1 = ClkRtcCtrlAuxsrc::CLKSRC_GPIN1 as _ ,
} }
pub struct RtcClkConfig { pub struct RtcClkConfig {
@ -396,7 +396,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
w.set_src(ref_src); w.set_src(ref_src);
w.set_auxsrc(ref_aux); w.set_auxsrc(ref_aux);
}); });
while c.clk_ref_selected().read() != 1 << ref_src.0 {} while c.clk_ref_selected().read() != 1 << ref_src as u32 {}
c.clk_ref_div().write(|w| { c.clk_ref_div().write(|w| {
w.set_int(config.ref_clk.div); w.set_int(config.ref_clk.div);
}); });
@ -425,13 +425,13 @@ pub(crate) unsafe fn init(config: ClockConfig) {
CLOCKS.sys.store(clk_sys_freq, Ordering::Relaxed); CLOCKS.sys.store(clk_sys_freq, Ordering::Relaxed);
if sys_src != ClkSysCtrlSrc::CLK_REF { if sys_src != ClkSysCtrlSrc::CLK_REF {
c.clk_sys_ctrl().write(|w| w.set_src(ClkSysCtrlSrc::CLK_REF)); c.clk_sys_ctrl().write(|w| w.set_src(ClkSysCtrlSrc::CLK_REF));
while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF.0 {} while c.clk_sys_selected().read() != 1 << ClkSysCtrlSrc::CLK_REF as u32 {}
} }
c.clk_sys_ctrl().write(|w| { c.clk_sys_ctrl().write(|w| {
w.set_auxsrc(sys_aux); w.set_auxsrc(sys_aux);
w.set_src(sys_src); w.set_src(sys_src);
}); });
while c.clk_sys_selected().read() != 1 << sys_src.0 {} while c.clk_sys_selected().read() != 1 << sys_src as u32 {}
c.clk_sys_div().write(|w| { c.clk_sys_div().write(|w| {
w.set_int(config.sys_clk.div_int); w.set_int(config.sys_clk.div_int);
w.set_frac(config.sys_clk.div_frac); w.set_frac(config.sys_clk.div_frac);
@ -442,7 +442,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
if let Some(src) = config.peri_clk_src { if let Some(src) = config.peri_clk_src {
c.clk_peri_ctrl().write(|w| { c.clk_peri_ctrl().write(|w| {
w.set_enable(true); w.set_enable(true);
w.set_auxsrc(ClkPeriCtrlAuxsrc(src as _)); w.set_auxsrc(ClkPeriCtrlAuxsrc::from_bits(src as _));
}); });
let peri_freq = match src { let peri_freq = match src {
PeriClkSrc::Sys => clk_sys_freq, PeriClkSrc::Sys => clk_sys_freq,
@ -468,7 +468,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
c.clk_usb_ctrl().write(|w| { c.clk_usb_ctrl().write(|w| {
w.set_phase(conf.phase); w.set_phase(conf.phase);
w.set_enable(true); w.set_enable(true);
w.set_auxsrc(ClkUsbCtrlAuxsrc(conf.src as _)); w.set_auxsrc(ClkUsbCtrlAuxsrc::from_bits(conf.src as _));
}); });
let usb_freq = match conf.src { let usb_freq = match conf.src {
UsbClkSrc::PllUsb => pll_usb_freq, UsbClkSrc::PllUsb => pll_usb_freq,
@ -491,7 +491,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
c.clk_adc_ctrl().write(|w| { c.clk_adc_ctrl().write(|w| {
w.set_phase(conf.phase); w.set_phase(conf.phase);
w.set_enable(true); w.set_enable(true);
w.set_auxsrc(ClkAdcCtrlAuxsrc(conf.src as _)); w.set_auxsrc(ClkAdcCtrlAuxsrc::from_bits(conf.src as _));
}); });
let adc_in_freq = match conf.src { let adc_in_freq = match conf.src {
AdcClkSrc::PllUsb => pll_usb_freq, AdcClkSrc::PllUsb => pll_usb_freq,
@ -517,7 +517,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
c.clk_rtc_ctrl().write(|w| { c.clk_rtc_ctrl().write(|w| {
w.set_phase(conf.phase); w.set_phase(conf.phase);
w.set_enable(true); w.set_enable(true);
w.set_auxsrc(ClkRtcCtrlAuxsrc(conf.src as _)); w.set_auxsrc(ClkRtcCtrlAuxsrc::from_bits(conf.src as _));
}); });
let rtc_in_freq = match conf.src { let rtc_in_freq = match conf.src {
RtcClkSrc::PllUsb => pll_usb_freq, RtcClkSrc::PllUsb => pll_usb_freq,
@ -542,7 +542,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
reset::unreset_wait(peris); reset::unreset_wait(peris);
} }
unsafe fn configure_rosc(config: RoscConfig) -> u32 { fn configure_rosc(config: RoscConfig) -> u32 {
let p = pac::ROSC; let p = pac::ROSC;
p.freqa().write(|w| { p.freqa().write(|w| {
@ -620,7 +620,7 @@ pub fn clk_rtc_freq() -> u16 {
CLOCKS.rtc.load(Ordering::Relaxed) CLOCKS.rtc.load(Ordering::Relaxed)
} }
unsafe fn start_xosc(crystal_hz: u32) { fn start_xosc(crystal_hz: u32) {
pac::XOSC pac::XOSC
.ctrl() .ctrl()
.write(|w| w.set_freq_range(pac::xosc::vals::CtrlFreqRange::_1_15MHZ)); .write(|w| w.set_freq_range(pac::xosc::vals::CtrlFreqRange::_1_15MHZ));
@ -635,7 +635,7 @@ unsafe fn start_xosc(crystal_hz: u32) {
} }
#[inline(always)] #[inline(always)]
unsafe fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> u32 { fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> u32 {
let ref_freq = input_freq / config.refdiv as u32; let ref_freq = input_freq / config.refdiv as u32;
assert!(config.fbdiv >= 16 && config.fbdiv <= 320); assert!(config.fbdiv >= 16 && config.fbdiv <= 320);
assert!(config.post_div1 >= 1 && config.post_div1 <= 7); assert!(config.post_div1 >= 1 && config.post_div1 <= 7);
@ -700,9 +700,7 @@ impl<'d, T: Pin> Gpin<'d, T> {
pub fn new<P: GpinPin>(gpin: impl Peripheral<P = P> + 'd) -> Gpin<'d, P> { pub fn new<P: GpinPin>(gpin: impl Peripheral<P = P> + 'd) -> Gpin<'d, P> {
into_ref!(gpin); into_ref!(gpin);
unsafe { gpin.io().ctrl().write(|w| w.set_funcsel(0x08));
gpin.io().ctrl().write(|w| w.set_funcsel(0x08));
}
Gpin { Gpin {
gpin: gpin.map_into(), gpin: gpin.map_into(),
@ -717,12 +715,10 @@ impl<'d, T: Pin> Gpin<'d, T> {
impl<'d, T: Pin> Drop for Gpin<'d, T> { impl<'d, T: Pin> Drop for Gpin<'d, T> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { self.gpin
self.gpin .io()
.io() .ctrl()
.ctrl() .write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _));
.write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0));
}
} }
} }
@ -747,17 +743,17 @@ impl_gpoutpin!(PIN_25, 3);
#[repr(u8)] #[repr(u8)]
pub enum GpoutSrc { pub enum GpoutSrc {
PllSys = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS.0, PllSys = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS as _,
// Gpin0 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0.0, // Gpin0 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN0 as _ ,
// Gpin1 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1.0, // Gpin1 = ClkGpoutCtrlAuxsrc::CLKSRC_GPIN1 as _ ,
PllUsb = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_USB.0, PllUsb = ClkGpoutCtrlAuxsrc::CLKSRC_PLL_USB as _,
Rosc = ClkGpoutCtrlAuxsrc::ROSC_CLKSRC.0, Rosc = ClkGpoutCtrlAuxsrc::ROSC_CLKSRC as _,
Xosc = ClkGpoutCtrlAuxsrc::XOSC_CLKSRC.0, Xosc = ClkGpoutCtrlAuxsrc::XOSC_CLKSRC as _,
Sys = ClkGpoutCtrlAuxsrc::CLK_SYS.0, Sys = ClkGpoutCtrlAuxsrc::CLK_SYS as _,
Usb = ClkGpoutCtrlAuxsrc::CLK_USB.0, Usb = ClkGpoutCtrlAuxsrc::CLK_USB as _,
Adc = ClkGpoutCtrlAuxsrc::CLK_ADC.0, Adc = ClkGpoutCtrlAuxsrc::CLK_ADC as _,
Rtc = ClkGpoutCtrlAuxsrc::CLK_RTC.0, Rtc = ClkGpoutCtrlAuxsrc::CLK_RTC as _,
Ref = ClkGpoutCtrlAuxsrc::CLK_REF.0, Ref = ClkGpoutCtrlAuxsrc::CLK_REF as _,
} }
pub struct Gpout<'d, T: GpoutPin> { pub struct Gpout<'d, T: GpoutPin> {
@ -768,53 +764,43 @@ impl<'d, T: GpoutPin> Gpout<'d, T> {
pub fn new(gpout: impl Peripheral<P = T> + 'd) -> Self { pub fn new(gpout: impl Peripheral<P = T> + 'd) -> Self {
into_ref!(gpout); into_ref!(gpout);
unsafe { gpout.io().ctrl().write(|w| w.set_funcsel(0x08));
gpout.io().ctrl().write(|w| w.set_funcsel(0x08));
}
Self { gpout } Self { gpout }
} }
pub fn set_div(&self, int: u32, frac: u8) { pub fn set_div(&self, int: u32, frac: u8) {
unsafe { let c = pac::CLOCKS;
let c = pac::CLOCKS; c.clk_gpout_div(self.gpout.number()).write(|w| {
c.clk_gpout_div(self.gpout.number()).write(|w| { w.set_int(int);
w.set_int(int); w.set_frac(frac);
w.set_frac(frac); });
});
}
} }
pub fn set_src(&self, src: GpoutSrc) { pub fn set_src(&self, src: GpoutSrc) {
unsafe { let c = pac::CLOCKS;
let c = pac::CLOCKS; c.clk_gpout_ctrl(self.gpout.number()).modify(|w| {
c.clk_gpout_ctrl(self.gpout.number()).modify(|w| { w.set_auxsrc(ClkGpoutCtrlAuxsrc::from_bits(src as _));
w.set_auxsrc(ClkGpoutCtrlAuxsrc(src as _)); });
});
}
} }
pub fn enable(&self) { pub fn enable(&self) {
unsafe { let c = pac::CLOCKS;
let c = pac::CLOCKS; c.clk_gpout_ctrl(self.gpout.number()).modify(|w| {
c.clk_gpout_ctrl(self.gpout.number()).modify(|w| { w.set_enable(true);
w.set_enable(true); });
});
}
} }
pub fn disable(&self) { pub fn disable(&self) {
unsafe { let c = pac::CLOCKS;
let c = pac::CLOCKS; c.clk_gpout_ctrl(self.gpout.number()).modify(|w| {
c.clk_gpout_ctrl(self.gpout.number()).modify(|w| { w.set_enable(false);
w.set_enable(false); });
});
}
} }
pub fn get_freq(&self) -> u32 { pub fn get_freq(&self) -> u32 {
let c = pac::CLOCKS; let c = pac::CLOCKS;
let src = unsafe { c.clk_gpout_ctrl(self.gpout.number()).read().auxsrc() }; let src = c.clk_gpout_ctrl(self.gpout.number()).read().auxsrc();
let base = match src { let base = match src {
ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(), ClkGpoutCtrlAuxsrc::CLKSRC_PLL_SYS => pll_sys_freq(),
@ -831,7 +817,7 @@ impl<'d, T: GpoutPin> Gpout<'d, T> {
_ => unreachable!(), _ => unreachable!(),
}; };
let div = unsafe { c.clk_gpout_div(self.gpout.number()).read() }; let div = c.clk_gpout_div(self.gpout.number()).read();
let int = if div.int() == 0 { 65536 } else { div.int() } as u64; let int = if div.int() == 0 { 65536 } else { div.int() } as u64;
let frac = div.frac() as u64; let frac = div.frac() as u64;
@ -842,12 +828,10 @@ impl<'d, T: GpoutPin> Gpout<'d, T> {
impl<'d, T: GpoutPin> Drop for Gpout<'d, T> { impl<'d, T: GpoutPin> Drop for Gpout<'d, T> {
fn drop(&mut self) { fn drop(&mut self) {
self.disable(); self.disable();
unsafe { self.gpout
self.gpout .io()
.io() .ctrl()
.ctrl() .write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _));
.write(|w| w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0));
}
} }
} }
@ -864,7 +848,7 @@ impl RoscRng {
let mut acc = 0; let mut acc = 0;
for _ in 0..u8::BITS { for _ in 0..u8::BITS {
acc <<= 1; acc <<= 1;
acc |= unsafe { random_reg.read().randombit() as u8 }; acc |= random_reg.read().randombit() as u8;
} }
acc acc
} }

View File

@ -103,14 +103,11 @@ where
/// Try to claim the spinlock. Will return `Some(Self)` if the lock is obtained, and `None` if the lock is /// Try to claim the spinlock. Will return `Some(Self)` if the lock is obtained, and `None` if the lock is
/// already in use somewhere else. /// already in use somewhere else.
pub fn try_claim() -> Option<Self> { pub fn try_claim() -> Option<Self> {
// Safety: We're only reading from this register let lock = pac::SIO.spinlock(N).read();
unsafe { if lock > 0 {
let lock = pac::SIO.spinlock(N).read(); Some(Self(core::marker::PhantomData))
if lock > 0 { } else {
Some(Self(core::marker::PhantomData)) None
} else {
None
}
} }
} }
@ -120,10 +117,8 @@ where
/// ///
/// Only call this function if you hold the spin-lock. /// Only call this function if you hold the spin-lock.
pub unsafe fn release() { pub unsafe fn release() {
unsafe { // Write (any value): release the lock
// Write (any value): release the lock pac::SIO.spinlock(N).write_value(1);
pac::SIO.spinlock(N).write_value(1);
}
} }
} }

View File

@ -4,16 +4,17 @@ use core::pin::Pin;
use core::sync::atomic::{compiler_fence, Ordering}; use core::sync::atomic::{compiler_fence, Ordering};
use core::task::{Context, Poll}; use core::task::{Context, Poll};
use embassy_cortex_m::interrupt::Interrupt;
use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker; use embassy_sync::waitqueue::AtomicWaker;
use pac::dma::vals::DataSize; use pac::dma::vals::DataSize;
use crate::interrupt::InterruptExt;
use crate::pac::dma::vals; use crate::pac::dma::vals;
use crate::{interrupt, pac, peripherals}; use crate::{interrupt, pac, peripherals};
#[cfg(feature = "rt")]
#[interrupt] #[interrupt]
unsafe fn DMA_IRQ_0() { fn DMA_IRQ_0() {
let ints0 = pac::DMA.ints0().read().ints0(); let ints0 = pac::DMA.ints0().read().ints0();
for channel in 0..CHANNEL_COUNT { for channel in 0..CHANNEL_COUNT {
let ctrl_trig = pac::DMA.ch(channel).ctrl_trig().read(); let ctrl_trig = pac::DMA.ch(channel).ctrl_trig().read();
@ -29,12 +30,12 @@ unsafe fn DMA_IRQ_0() {
} }
pub(crate) unsafe fn init() { pub(crate) unsafe fn init() {
interrupt::DMA_IRQ_0::disable(); interrupt::DMA_IRQ_0.disable();
interrupt::DMA_IRQ_0::set_priority(interrupt::Priority::P3); interrupt::DMA_IRQ_0.set_priority(interrupt::Priority::P3);
pac::DMA.inte0().write(|w| w.set_inte0(0xFFFF)); pac::DMA.inte0().write(|w| w.set_inte0(0xFFFF));
interrupt::DMA_IRQ_0::enable(); interrupt::DMA_IRQ_0.enable();
} }
pub unsafe fn read<'a, C: Channel, W: Word>( pub unsafe fn read<'a, C: Channel, W: Word>(
@ -75,16 +76,17 @@ pub unsafe fn write<'a, C: Channel, W: Word>(
) )
} }
static DUMMY: u32 = 0;
pub unsafe fn write_repeated<'a, C: Channel, W: Word>( pub unsafe fn write_repeated<'a, C: Channel, W: Word>(
ch: impl Peripheral<P = C> + 'a, ch: impl Peripheral<P = C> + 'a,
to: *mut W, to: *mut W,
len: usize, len: usize,
dreq: u8, dreq: u8,
) -> Transfer<'a, C> { ) -> Transfer<'a, C> {
let dummy: u32 = 0;
copy_inner( copy_inner(
ch, ch,
&dummy as *const u32, &DUMMY as *const u32,
to as *mut u32, to as *mut u32,
len, len,
W::size(), W::size(),
@ -126,28 +128,26 @@ fn copy_inner<'a, C: Channel>(
) -> Transfer<'a, C> { ) -> Transfer<'a, C> {
into_ref!(ch); into_ref!(ch);
unsafe { let p = ch.regs();
let p = ch.regs();
p.read_addr().write_value(from as u32); p.read_addr().write_value(from as u32);
p.write_addr().write_value(to as u32); p.write_addr().write_value(to as u32);
p.trans_count().write_value(len as u32); p.trans_count().write_value(len as u32);
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
p.ctrl_trig().write(|w| { p.ctrl_trig().write(|w| {
// TODO: Add all DREQ options to pac vals::TreqSel, and use // TODO: Add all DREQ options to pac vals::TreqSel, and use
// `set_treq:sel` // `set_treq:sel`
w.0 = ((dreq as u32) & 0x3f) << 15usize; w.0 = ((dreq as u32) & 0x3f) << 15usize;
w.set_data_size(data_size); w.set_data_size(data_size);
w.set_incr_read(incr_read); w.set_incr_read(incr_read);
w.set_incr_write(incr_write); w.set_incr_write(incr_write);
w.set_chain_to(ch.number()); w.set_chain_to(ch.number());
w.set_en(true); w.set_en(true);
}); });
compiler_fence(Ordering::SeqCst); compiler_fence(Ordering::SeqCst);
}
Transfer::new(ch) Transfer::new(ch)
} }
@ -167,12 +167,10 @@ impl<'a, C: Channel> Transfer<'a, C> {
impl<'a, C: Channel> Drop for Transfer<'a, C> { impl<'a, C: Channel> Drop for Transfer<'a, C> {
fn drop(&mut self) { fn drop(&mut self) {
let p = self.channel.regs(); let p = self.channel.regs();
unsafe { pac::DMA
pac::DMA .chan_abort()
.chan_abort() .modify(|m| m.set_chan_abort(1 << self.channel.number()));
.modify(|m| m.set_chan_abort(1 << self.channel.number())); while p.ctrl_trig().read().busy() {}
while p.ctrl_trig().read().busy() {}
}
} }
} }
@ -184,7 +182,7 @@ impl<'a, C: Channel> Future for Transfer<'a, C> {
// calls to wake will deregister the waker. // calls to wake will deregister the waker.
CHANNEL_WAKERS[self.channel.number() as usize].register(cx.waker()); CHANNEL_WAKERS[self.channel.number() as usize].register(cx.waker());
if unsafe { self.channel.regs().ctrl_trig().read().busy() } { if self.channel.regs().ctrl_trig().read().busy() {
Poll::Pending Poll::Pending
} else { } else {
Poll::Ready(()) Poll::Ready(())

View File

@ -9,7 +9,11 @@ use embedded_storage::nor_flash::{
use crate::pac; use crate::pac;
use crate::peripherals::FLASH; use crate::peripherals::FLASH;
pub const FLASH_BASE: usize = 0x10000000; pub const FLASH_BASE: *const u32 = 0x10000000 as _;
// If running from RAM, we might have no boot2. Use bootrom `flash_enter_cmd_xip` instead.
// TODO: when run-from-ram is set, completely skip the "pause cores and jumpp to RAM" dance.
pub const USE_BOOT2: bool = !cfg!(feature = "run-from-ram");
// **NOTE**: // **NOTE**:
// //
@ -63,8 +67,8 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> {
pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> {
trace!( trace!(
"Reading from 0x{:x} to 0x{:x}", "Reading from 0x{:x} to 0x{:x}",
FLASH_BASE + offset as usize, FLASH_BASE as u32 + offset,
FLASH_BASE + offset as usize + bytes.len() FLASH_BASE as u32 + offset + bytes.len() as u32
); );
check_read(self, offset, bytes.len())?; check_read(self, offset, bytes.len())?;
@ -89,7 +93,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> {
let len = to - from; let len = to - from;
unsafe { self.in_ram(|| ram_helpers::flash_range_erase(from, len, true))? }; unsafe { self.in_ram(|| ram_helpers::flash_range_erase(from, len))? };
Ok(()) Ok(())
} }
@ -114,7 +118,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> {
let unaligned_offset = offset as usize - start; let unaligned_offset = offset as usize - start;
unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf, true))? } unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf))? }
} }
let remaining_len = bytes.len() - start_padding; let remaining_len = bytes.len() - start_padding;
@ -132,12 +136,12 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> {
if bytes.as_ptr() as usize >= 0x2000_0000 { if bytes.as_ptr() as usize >= 0x2000_0000 {
let aligned_data = &bytes[start_padding..end_padding]; let aligned_data = &bytes[start_padding..end_padding];
unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, aligned_data, true))? } unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, aligned_data))? }
} else { } else {
for chunk in bytes[start_padding..end_padding].chunks_exact(PAGE_SIZE) { for chunk in bytes[start_padding..end_padding].chunks_exact(PAGE_SIZE) {
let mut ram_buf = [0xFF_u8; PAGE_SIZE]; let mut ram_buf = [0xFF_u8; PAGE_SIZE];
ram_buf.copy_from_slice(chunk); ram_buf.copy_from_slice(chunk);
unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, &ram_buf, true))? } unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, &ram_buf))? }
aligned_offset += PAGE_SIZE; aligned_offset += PAGE_SIZE;
} }
} }
@ -152,7 +156,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> {
let unaligned_offset = end_offset - (PAGE_SIZE - rem_offset); let unaligned_offset = end_offset - (PAGE_SIZE - rem_offset);
unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf, true))? } unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf))? }
} }
Ok(()) Ok(())
@ -163,7 +167,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> {
/// - DMA must not access flash memory /// - DMA must not access flash memory
unsafe fn in_ram(&mut self, operation: impl FnOnce()) -> Result<(), Error> { unsafe fn in_ram(&mut self, operation: impl FnOnce()) -> Result<(), Error> {
// Make sure we're running on CORE0 // Make sure we're running on CORE0
let core_id: u32 = unsafe { pac::SIO.cpuid().read() }; let core_id: u32 = pac::SIO.cpuid().read();
if core_id != 0 { if core_id != 0 {
return Err(Error::InvalidCore); return Err(Error::InvalidCore);
} }
@ -190,7 +194,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> {
/// Read SPI flash unique ID /// Read SPI flash unique ID
pub fn unique_id(&mut self, uid: &mut [u8]) -> Result<(), Error> { pub fn unique_id(&mut self, uid: &mut [u8]) -> Result<(), Error> {
unsafe { self.in_ram(|| ram_helpers::flash_unique_id(uid, true))? }; unsafe { self.in_ram(|| ram_helpers::flash_unique_id(uid))? };
Ok(()) Ok(())
} }
@ -199,7 +203,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> {
let mut jedec = None; let mut jedec = None;
unsafe { unsafe {
self.in_ram(|| { self.in_ram(|| {
jedec.replace(ram_helpers::flash_jedec_id(true)); jedec.replace(ram_helpers::flash_jedec_id());
})?; })?;
}; };
Ok(jedec.unwrap()) Ok(jedec.unwrap())
@ -242,6 +246,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, FLASH_S
mod ram_helpers { mod ram_helpers {
use core::marker::PhantomData; use core::marker::PhantomData;
use super::*;
use crate::rom_data; use crate::rom_data;
#[repr(C)] #[repr(C)]
@ -306,7 +311,7 @@ mod ram_helpers {
/// ///
/// `addr` and `len` must be multiples of 4096 /// `addr` and `len` must be multiples of 4096
/// ///
/// If `use_boot2` is `true`, a copy of the 2nd stage boot loader /// If `USE_BOOT2` is `true`, a copy of the 2nd stage boot loader
/// is used to re-initialize the XIP engine after flashing. /// is used to re-initialize the XIP engine after flashing.
/// ///
/// # Safety /// # Safety
@ -318,10 +323,10 @@ mod ram_helpers {
/// - DMA must not access flash memory /// - DMA must not access flash memory
/// ///
/// `addr` and `len` parameters must be valid and are not checked. /// `addr` and `len` parameters must be valid and are not checked.
pub unsafe fn flash_range_erase(addr: u32, len: u32, use_boot2: bool) { pub unsafe fn flash_range_erase(addr: u32, len: u32) {
let mut boot2 = [0u32; 256 / 4]; let mut boot2 = [0u32; 256 / 4];
let ptrs = if use_boot2 { let ptrs = if USE_BOOT2 {
rom_data::memcpy44(&mut boot2 as *mut _, super::FLASH_BASE as *const _, 256); rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256);
flash_function_pointers_with_boot2(true, false, &boot2) flash_function_pointers_with_boot2(true, false, &boot2)
} else { } else {
flash_function_pointers(true, false) flash_function_pointers(true, false)
@ -336,7 +341,7 @@ mod ram_helpers {
/// ///
/// `addr` and `data.len()` must be multiples of 4096 /// `addr` and `data.len()` must be multiples of 4096
/// ///
/// If `use_boot2` is `true`, a copy of the 2nd stage boot loader /// If `USE_BOOT2` is `true`, a copy of the 2nd stage boot loader
/// is used to re-initialize the XIP engine after flashing. /// is used to re-initialize the XIP engine after flashing.
/// ///
/// # Safety /// # Safety
@ -348,10 +353,10 @@ mod ram_helpers {
/// - DMA must not access flash memory /// - DMA must not access flash memory
/// ///
/// `addr` and `len` parameters must be valid and are not checked. /// `addr` and `len` parameters must be valid and are not checked.
pub unsafe fn flash_range_erase_and_program(addr: u32, data: &[u8], use_boot2: bool) { pub unsafe fn flash_range_erase_and_program(addr: u32, data: &[u8]) {
let mut boot2 = [0u32; 256 / 4]; let mut boot2 = [0u32; 256 / 4];
let ptrs = if use_boot2 { let ptrs = if USE_BOOT2 {
rom_data::memcpy44(&mut boot2 as *mut _, super::FLASH_BASE as *const _, 256); rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256);
flash_function_pointers_with_boot2(true, true, &boot2) flash_function_pointers_with_boot2(true, true, &boot2)
} else { } else {
flash_function_pointers(true, true) flash_function_pointers(true, true)
@ -371,7 +376,7 @@ mod ram_helpers {
/// ///
/// `addr` and `data.len()` must be multiples of 256 /// `addr` and `data.len()` must be multiples of 256
/// ///
/// If `use_boot2` is `true`, a copy of the 2nd stage boot loader /// If `USE_BOOT2` is `true`, a copy of the 2nd stage boot loader
/// is used to re-initialize the XIP engine after flashing. /// is used to re-initialize the XIP engine after flashing.
/// ///
/// # Safety /// # Safety
@ -383,10 +388,10 @@ mod ram_helpers {
/// - DMA must not access flash memory /// - DMA must not access flash memory
/// ///
/// `addr` and `len` parameters must be valid and are not checked. /// `addr` and `len` parameters must be valid and are not checked.
pub unsafe fn flash_range_program(addr: u32, data: &[u8], use_boot2: bool) { pub unsafe fn flash_range_program(addr: u32, data: &[u8]) {
let mut boot2 = [0u32; 256 / 4]; let mut boot2 = [0u32; 256 / 4];
let ptrs = if use_boot2 { let ptrs = if USE_BOOT2 {
rom_data::memcpy44(&mut boot2 as *mut _, super::FLASH_BASE as *const _, 256); rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256);
flash_function_pointers_with_boot2(false, true, &boot2) flash_function_pointers_with_boot2(false, true, &boot2)
} else { } else {
flash_function_pointers(false, true) flash_function_pointers(false, true)
@ -508,10 +513,10 @@ mod ram_helpers {
/// - DMA must not access flash memory /// - DMA must not access flash memory
/// ///
/// Credit: taken from `rp2040-flash` (also licensed Apache+MIT) /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT)
pub unsafe fn flash_unique_id(out: &mut [u8], use_boot2: bool) { pub unsafe fn flash_unique_id(out: &mut [u8]) {
let mut boot2 = [0u32; 256 / 4]; let mut boot2 = [0u32; 256 / 4];
let ptrs = if use_boot2 { let ptrs = if USE_BOOT2 {
rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256); rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256);
flash_function_pointers_with_boot2(false, false, &boot2) flash_function_pointers_with_boot2(false, false, &boot2)
} else { } else {
flash_function_pointers(false, false) flash_function_pointers(false, false)
@ -536,10 +541,10 @@ mod ram_helpers {
/// - DMA must not access flash memory /// - DMA must not access flash memory
/// ///
/// Credit: taken from `rp2040-flash` (also licensed Apache+MIT) /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT)
pub unsafe fn flash_jedec_id(use_boot2: bool) -> u32 { pub unsafe fn flash_jedec_id() -> u32 {
let mut boot2 = [0u32; 256 / 4]; let mut boot2 = [0u32; 256 / 4];
let ptrs = if use_boot2 { let ptrs = if USE_BOOT2 {
rom_data::memcpy44(&mut boot2 as *mut _, 0x10000000 as *const _, 256); rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256);
flash_function_pointers_with_boot2(false, false, &boot2) flash_function_pointers_with_boot2(false, false, &boot2)
} else { } else {
flash_function_pointers(false, false) flash_function_pointers(false, false)
@ -586,7 +591,6 @@ mod ram_helpers {
"ldr r4, [r5, #4]", "ldr r4, [r5, #4]",
"blx r4", // flash_exit_xip() "blx r4", // flash_exit_xip()
"mov r7, r10", // cmd
"movs r4, #0x18", "movs r4, #0x18",
"lsls r4, r4, #24", // 0x18000000, SSI, RP2040 datasheet 4.10.13 "lsls r4, r4, #24", // 0x18000000, SSI, RP2040 datasheet 4.10.13
@ -603,8 +607,9 @@ mod ram_helpers {
"str r1, [r4, #0]", "str r1, [r4, #0]",
// Write ctrlr1 with len-1 // Write ctrlr1 with len-1
"ldr r0, [r7, #8]", // dummy_len "mov r3, r10", // cmd
"ldr r1, [r7, #16]", // data_len "ldr r0, [r3, #8]", // dummy_len
"ldr r1, [r3, #16]", // data_len
"add r0, r1", "add r0, r1",
"subs r0, #1", "subs r0, #1",
"str r0, [r4, #0x04]", // CTRLR1 "str r0, [r4, #0x04]", // CTRLR1
@ -616,8 +621,8 @@ mod ram_helpers {
// Write cmd/addr phase to DR // Write cmd/addr phase to DR
"mov r2, r4", "mov r2, r4",
"adds r2, 0x60", // &DR "adds r2, 0x60", // &DR
"ldr r0, [r7, #0]", // cmd_addr "ldr r0, [r3, #0]", // cmd_addr
"ldr r1, [r7, #4]", // cmd_addr_len "ldr r1, [r3, #4]", // cmd_addr_len
"10:", "10:",
"ldrb r3, [r0]", "ldrb r3, [r0]",
"strb r3, [r2]", // DR "strb r3, [r2]", // DR
@ -626,7 +631,8 @@ mod ram_helpers {
"bne 10b", "bne 10b",
// Skip any dummy cycles // Skip any dummy cycles
"ldr r1, [r7, #8]", // dummy_len "mov r3, r10", // cmd
"ldr r1, [r3, #8]", // dummy_len
"cmp r1, #0", "cmp r1, #0",
"beq 9f", "beq 9f",
"4:", "4:",
@ -643,8 +649,9 @@ mod ram_helpers {
// Read RX fifo // Read RX fifo
"9:", "9:",
"ldr r0, [r7, #12]", // data "mov r2, r10", // cmd
"ldr r1, [r7, #16]", // data_len "ldr r0, [r2, #12]", // data
"ldr r1, [r2, #16]", // data_len
"2:", "2:",
"ldr r3, [r4, #0x28]", // SR "ldr r3, [r4, #0x28]", // SR
@ -678,13 +685,12 @@ mod ram_helpers {
out("r2") _, out("r2") _,
out("r3") _, out("r3") _,
out("r4") _, out("r4") _,
out("r5") _,
// Registers r8-r10 are used to store values // Registers r8-r10 are used to store values
// from r0-r2 in registers not clobbered by // from r0-r2 in registers not clobbered by
// function calls. // function calls.
// The values can't be passed in using r8-r10 directly // The values can't be passed in using r8-r10 directly
// due to https://github.com/rust-lang/rust/issues/99071 // due to https://github.com/rust-lang/rust/issues/99071
out("r8") _,
out("r9") _,
out("r10") _, out("r10") _,
clobber_abi("C"), clobber_abi("C"),
); );

View File

@ -17,45 +17,43 @@ where
{ {
let sio = rp_pac::SIO; let sio = rp_pac::SIO;
unsafe { // Since we can't save the signed-ness of the calculation, we have to make
// Since we can't save the signed-ness of the calculation, we have to make // sure that there's at least an 8 cycle delay before we read the result.
// sure that there's at least an 8 cycle delay before we read the result. // The Pico SDK ensures this by using a 6 cycle push and two 1 cycle reads.
// The Pico SDK ensures this by using a 6 cycle push and two 1 cycle reads. // Since we can't be sure the Rust implementation will optimize to the same,
// Since we can't be sure the Rust implementation will optimize to the same, // just use an explicit wait.
// just use an explicit wait. while !sio.div().csr().read().ready() {}
while !sio.div().csr().read().ready() {}
// Read the quotient last, since that's what clears the dirty flag // Read the quotient last, since that's what clears the dirty flag
let dividend = sio.div().udividend().read(); let dividend = sio.div().udividend().read();
let divisor = sio.div().udivisor().read(); let divisor = sio.div().udivisor().read();
let remainder = sio.div().remainder().read(); let remainder = sio.div().remainder().read();
let quotient = sio.div().quotient().read(); let quotient = sio.div().quotient().read();
// If we get interrupted here (before a write sets the DIRTY flag) its fine, since // If we get interrupted here (before a write sets the DIRTY flag) its fine, since
// we have the full state, so the interruptor doesn't have to restore it. Once the // we have the full state, so the interruptor doesn't have to restore it. Once the
// write happens and the DIRTY flag is set, the interruptor becomes responsible for // write happens and the DIRTY flag is set, the interruptor becomes responsible for
// restoring our state. // restoring our state.
let result = f(); let result = f();
// If we are interrupted here, then the interruptor will start an incorrect calculation // If we are interrupted here, then the interruptor will start an incorrect calculation
// using a wrong divisor, but we'll restore the divisor and result ourselves correctly. // using a wrong divisor, but we'll restore the divisor and result ourselves correctly.
// This sets DIRTY, so any interruptor will save the state. // This sets DIRTY, so any interruptor will save the state.
sio.div().udividend().write_value(dividend); sio.div().udividend().write_value(dividend);
// If we are interrupted here, the the interruptor may start the calculation using // If we are interrupted here, the the interruptor may start the calculation using
// incorrectly signed inputs, but we'll restore the result ourselves. // incorrectly signed inputs, but we'll restore the result ourselves.
// This sets DIRTY, so any interruptor will save the state. // This sets DIRTY, so any interruptor will save the state.
sio.div().udivisor().write_value(divisor); sio.div().udivisor().write_value(divisor);
// If we are interrupted here, the interruptor will have restored everything but the // If we are interrupted here, the interruptor will have restored everything but the
// quotient may be wrongly signed. If the calculation started by the above writes is // quotient may be wrongly signed. If the calculation started by the above writes is
// still ongoing it is stopped, so it won't replace the result we're restoring. // still ongoing it is stopped, so it won't replace the result we're restoring.
// DIRTY and READY set, but only DIRTY matters to make the interruptor save the state. // DIRTY and READY set, but only DIRTY matters to make the interruptor save the state.
sio.div().remainder().write_value(remainder); sio.div().remainder().write_value(remainder);
// State fully restored after the quotient write. This sets both DIRTY and READY, so // State fully restored after the quotient write. This sets both DIRTY and READY, so
// whatever we may have interrupted can read the result. // whatever we may have interrupted can read the result.
sio.div().quotient().write_value(quotient); sio.div().quotient().write_value(quotient);
result result
}
} }
fn save_divider<F, R>(f: F) -> R fn save_divider<F, R>(f: F) -> R
@ -63,7 +61,7 @@ where
F: FnOnce() -> R, F: FnOnce() -> R,
{ {
let sio = rp_pac::SIO; let sio = rp_pac::SIO;
if unsafe { !sio.div().csr().read().dirty() } { if !sio.div().csr().read().dirty() {
// Not dirty, so nothing is waiting for the calculation. So we can just // Not dirty, so nothing is waiting for the calculation. So we can just
// issue it directly without a save/restore. // issue it directly without a save/restore.
f() f()

View File

@ -3,10 +3,10 @@ use core::future::Future;
use core::pin::Pin as FuturePin; use core::pin::Pin as FuturePin;
use core::task::{Context, Poll}; use core::task::{Context, Poll};
use embassy_cortex_m::interrupt::Interrupt;
use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker; use embassy_sync::waitqueue::AtomicWaker;
use crate::interrupt::InterruptExt;
use crate::pac::common::{Reg, RW}; use crate::pac::common::{Reg, RW};
use crate::pac::SIO; use crate::pac::SIO;
use crate::{interrupt, pac, peripherals, Peripheral, RegExt}; use crate::{interrupt, pac, peripherals, Peripheral, RegExt};
@ -137,13 +137,14 @@ pub enum InterruptTrigger {
} }
pub(crate) unsafe fn init() { pub(crate) unsafe fn init() {
interrupt::IO_IRQ_BANK0::disable(); interrupt::IO_IRQ_BANK0.disable();
interrupt::IO_IRQ_BANK0::set_priority(interrupt::Priority::P3); interrupt::IO_IRQ_BANK0.set_priority(interrupt::Priority::P3);
interrupt::IO_IRQ_BANK0::enable(); interrupt::IO_IRQ_BANK0.enable();
} }
#[cfg(feature = "rt")]
#[interrupt] #[interrupt]
unsafe fn IO_IRQ_BANK0() { fn IO_IRQ_BANK0() {
let cpu = SIO.cpuid().read() as usize; let cpu = SIO.cpuid().read() as usize;
// There are two sets of interrupt registers, one for cpu0 and one for cpu1 // There are two sets of interrupt registers, one for cpu0 and one for cpu1
// and here we are selecting the set that belongs to the currently executing // and here we are selecting the set that belongs to the currently executing
@ -184,46 +185,44 @@ struct InputFuture<'a, T: Pin> {
impl<'d, T: Pin> InputFuture<'d, T> { impl<'d, T: Pin> InputFuture<'d, T> {
pub fn new(pin: impl Peripheral<P = T> + 'd, level: InterruptTrigger) -> Self { pub fn new(pin: impl Peripheral<P = T> + 'd, level: InterruptTrigger) -> Self {
into_ref!(pin); into_ref!(pin);
unsafe { let pin_group = (pin.pin() % 8) as usize;
let pin_group = (pin.pin() % 8) as usize; // first, clear the INTR register bits. without this INTR will still
// first, clear the INTR register bits. without this INTR will still // contain reports of previous edges, causing the IRQ to fire early
// contain reports of previous edges, causing the IRQ to fire early // on stale state. clearing these means that we can only detect edges
// on stale state. clearing these means that we can only detect edges // that occur *after* the clear happened, but since both this and the
// that occur *after* the clear happened, but since both this and the // alternative are fundamentally racy it's probably fine.
// alternative are fundamentally racy it's probably fine. // (the alternative being checking the current level and waiting for
// (the alternative being checking the current level and waiting for // its inverse, but that requires reading the current level and thus
// its inverse, but that requires reading the current level and thus // missing anything that happened before the level was read.)
// missing anything that happened before the level was read.) pac::IO_BANK0.intr(pin.pin() as usize / 8).write(|w| {
pac::IO_BANK0.intr(pin.pin() as usize / 8).write(|w| { w.set_edge_high(pin_group, true);
w.set_edge_high(pin_group, true); w.set_edge_low(pin_group, true);
w.set_edge_low(pin_group, true); });
});
// Each INTR register is divided into 8 groups, one group for each // Each INTR register is divided into 8 groups, one group for each
// pin, and each group consists of LEVEL_LOW, LEVEL_HIGH, EDGE_LOW, // pin, and each group consists of LEVEL_LOW, LEVEL_HIGH, EDGE_LOW,
// and EGDE_HIGH. // and EGDE_HIGH.
pin.int_proc() pin.int_proc()
.inte((pin.pin() / 8) as usize) .inte((pin.pin() / 8) as usize)
.write_set(|w| match level { .write_set(|w| match level {
InterruptTrigger::LevelHigh => { InterruptTrigger::LevelHigh => {
trace!("InputFuture::new enable LevelHigh for pin {}", pin.pin()); trace!("InputFuture::new enable LevelHigh for pin {}", pin.pin());
w.set_level_high(pin_group, true); w.set_level_high(pin_group, true);
} }
InterruptTrigger::LevelLow => { InterruptTrigger::LevelLow => {
w.set_level_low(pin_group, true); w.set_level_low(pin_group, true);
} }
InterruptTrigger::EdgeHigh => { InterruptTrigger::EdgeHigh => {
w.set_edge_high(pin_group, true); w.set_edge_high(pin_group, true);
} }
InterruptTrigger::EdgeLow => { InterruptTrigger::EdgeLow => {
w.set_edge_low(pin_group, true); w.set_edge_low(pin_group, true);
} }
InterruptTrigger::AnyEdge => { InterruptTrigger::AnyEdge => {
w.set_edge_high(pin_group, true); w.set_edge_high(pin_group, true);
w.set_edge_low(pin_group, true); w.set_edge_low(pin_group, true);
} }
}); });
}
Self { pin, level } Self { pin, level }
} }
@ -241,7 +240,7 @@ impl<'d, T: Pin> Future for InputFuture<'d, T> {
// then we want to access the interrupt enable register for our // then we want to access the interrupt enable register for our
// pin (there are 4 of these PROC0_INTE0, PROC0_INTE1, PROC0_INTE2, and // pin (there are 4 of these PROC0_INTE0, PROC0_INTE1, PROC0_INTE2, and
// PROC0_INTE3 per cpu). // PROC0_INTE3 per cpu).
let inte: pac::io::regs::Int = unsafe { self.pin.int_proc().inte((self.pin.pin() / 8) as usize).read() }; let inte: pac::io::regs::Int = self.pin.int_proc().inte((self.pin.pin() / 8) as usize).read();
// The register is divided into groups of four, one group for // The register is divided into groups of four, one group for
// each pin. Each group consists of four trigger levels LEVEL_LOW, // each pin. Each group consists of four trigger levels LEVEL_LOW,
// LEVEL_HIGH, EDGE_LOW, and EDGE_HIGH for each pin. // LEVEL_HIGH, EDGE_LOW, and EDGE_HIGH for each pin.
@ -448,15 +447,13 @@ impl<'d, T: Pin> Flex<'d, T> {
pub fn new(pin: impl Peripheral<P = T> + 'd) -> Self { pub fn new(pin: impl Peripheral<P = T> + 'd) -> Self {
into_ref!(pin); into_ref!(pin);
unsafe { pin.pad_ctrl().write(|w| {
pin.pad_ctrl().write(|w| { w.set_ie(true);
w.set_ie(true); });
});
pin.io().ctrl().write(|w| { pin.io().ctrl().write(|w| {
w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::SIO_0.0); w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::SIO_0 as _);
}); });
}
Self { pin } Self { pin }
} }
@ -469,43 +466,37 @@ impl<'d, T: Pin> Flex<'d, T> {
/// Set the pin's pull. /// Set the pin's pull.
#[inline] #[inline]
pub fn set_pull(&mut self, pull: Pull) { pub fn set_pull(&mut self, pull: Pull) {
unsafe { self.pin.pad_ctrl().modify(|w| {
self.pin.pad_ctrl().modify(|w| { w.set_ie(true);
w.set_ie(true); let (pu, pd) = match pull {
let (pu, pd) = match pull { Pull::Up => (true, false),
Pull::Up => (true, false), Pull::Down => (false, true),
Pull::Down => (false, true), Pull::None => (false, false),
Pull::None => (false, false), };
}; w.set_pue(pu);
w.set_pue(pu); w.set_pde(pd);
w.set_pde(pd); });
});
}
} }
/// Set the pin's drive strength. /// Set the pin's drive strength.
#[inline] #[inline]
pub fn set_drive_strength(&mut self, strength: Drive) { pub fn set_drive_strength(&mut self, strength: Drive) {
unsafe { self.pin.pad_ctrl().modify(|w| {
self.pin.pad_ctrl().modify(|w| { w.set_drive(match strength {
w.set_drive(match strength { Drive::_2mA => pac::pads::vals::Drive::_2MA,
Drive::_2mA => pac::pads::vals::Drive::_2MA, Drive::_4mA => pac::pads::vals::Drive::_4MA,
Drive::_4mA => pac::pads::vals::Drive::_4MA, Drive::_8mA => pac::pads::vals::Drive::_8MA,
Drive::_8mA => pac::pads::vals::Drive::_8MA, Drive::_12mA => pac::pads::vals::Drive::_12MA,
Drive::_12mA => pac::pads::vals::Drive::_12MA,
});
}); });
} });
} }
// Set the pin's slew rate. // Set the pin's slew rate.
#[inline] #[inline]
pub fn set_slew_rate(&mut self, slew_rate: SlewRate) { pub fn set_slew_rate(&mut self, slew_rate: SlewRate) {
unsafe { self.pin.pad_ctrl().modify(|w| {
self.pin.pad_ctrl().modify(|w| { w.set_slewfast(slew_rate == SlewRate::Fast);
w.set_slewfast(slew_rate == SlewRate::Fast); });
});
}
} }
/// Put the pin into input mode. /// Put the pin into input mode.
@ -513,7 +504,7 @@ impl<'d, T: Pin> Flex<'d, T> {
/// The pull setting is left unchanged. /// The pull setting is left unchanged.
#[inline] #[inline]
pub fn set_as_input(&mut self) { pub fn set_as_input(&mut self) {
unsafe { self.pin.sio_oe().value_clr().write_value(self.bit()) } self.pin.sio_oe().value_clr().write_value(self.bit())
} }
/// Put the pin into output mode. /// Put the pin into output mode.
@ -522,17 +513,17 @@ impl<'d, T: Pin> Flex<'d, T> {
/// at a specific level, call `set_high`/`set_low` on the pin first. /// at a specific level, call `set_high`/`set_low` on the pin first.
#[inline] #[inline]
pub fn set_as_output(&mut self) { pub fn set_as_output(&mut self) {
unsafe { self.pin.sio_oe().value_set().write_value(self.bit()) } self.pin.sio_oe().value_set().write_value(self.bit())
} }
#[inline] #[inline]
fn is_set_as_output(&self) -> bool { fn is_set_as_output(&self) -> bool {
unsafe { (self.pin.sio_oe().value().read() & self.bit()) != 0 } (self.pin.sio_oe().value().read() & self.bit()) != 0
} }
#[inline] #[inline]
pub fn toggle_set_as_output(&mut self) { pub fn toggle_set_as_output(&mut self) {
unsafe { self.pin.sio_oe().value_xor().write_value(self.bit()) } self.pin.sio_oe().value_xor().write_value(self.bit())
} }
#[inline] #[inline]
@ -542,7 +533,7 @@ impl<'d, T: Pin> Flex<'d, T> {
#[inline] #[inline]
pub fn is_low(&self) -> bool { pub fn is_low(&self) -> bool {
unsafe { self.pin.sio_in().read() & self.bit() == 0 } self.pin.sio_in().read() & self.bit() == 0
} }
/// Returns current pin level /// Returns current pin level
@ -554,13 +545,13 @@ impl<'d, T: Pin> Flex<'d, T> {
/// Set the output as high. /// Set the output as high.
#[inline] #[inline]
pub fn set_high(&mut self) { pub fn set_high(&mut self) {
unsafe { self.pin.sio_out().value_set().write_value(self.bit()) } self.pin.sio_out().value_set().write_value(self.bit())
} }
/// Set the output as low. /// Set the output as low.
#[inline] #[inline]
pub fn set_low(&mut self) { pub fn set_low(&mut self) {
unsafe { self.pin.sio_out().value_clr().write_value(self.bit()) } self.pin.sio_out().value_clr().write_value(self.bit())
} }
/// Set the output level. /// Set the output level.
@ -575,7 +566,7 @@ impl<'d, T: Pin> Flex<'d, T> {
/// Is the output level high? /// Is the output level high?
#[inline] #[inline]
pub fn is_set_high(&self) -> bool { pub fn is_set_high(&self) -> bool {
unsafe { (self.pin.sio_out().value().read() & self.bit()) == 0 } (self.pin.sio_out().value().read() & self.bit()) == 0
} }
/// Is the output level low? /// Is the output level low?
@ -593,7 +584,7 @@ impl<'d, T: Pin> Flex<'d, T> {
/// Toggle pin output /// Toggle pin output
#[inline] #[inline]
pub fn toggle(&mut self) { pub fn toggle(&mut self) {
unsafe { self.pin.sio_out().value_xor().write_value(self.bit()) } self.pin.sio_out().value_xor().write_value(self.bit())
} }
#[inline] #[inline]
@ -625,12 +616,10 @@ impl<'d, T: Pin> Flex<'d, T> {
impl<'d, T: Pin> Drop for Flex<'d, T> { impl<'d, T: Pin> Drop for Flex<'d, T> {
#[inline] #[inline]
fn drop(&mut self) { fn drop(&mut self) {
unsafe { self.pin.pad_ctrl().write(|_| {});
self.pin.pad_ctrl().write(|_| {}); self.pin.io().ctrl().write(|w| {
self.pin.io().ctrl().write(|w| { w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL as _);
w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0); });
});
}
} }
} }
@ -687,7 +676,7 @@ pub(crate) mod sealed {
Bank::Bank0 => crate::pac::IO_BANK0, Bank::Bank0 => crate::pac::IO_BANK0,
Bank::Qspi => crate::pac::IO_QSPI, Bank::Qspi => crate::pac::IO_QSPI,
}; };
let proc = unsafe { SIO.cpuid().read() }; let proc = SIO.cpuid().read();
io_block.int_proc(proc as _) io_block.int_proc(proc as _)
} }
} }

View File

@ -2,14 +2,14 @@ use core::future;
use core::marker::PhantomData; use core::marker::PhantomData;
use core::task::Poll; use core::task::Poll;
use embassy_cortex_m::interrupt::{self, Binding, Interrupt};
use embassy_hal_common::{into_ref, PeripheralRef}; use embassy_hal_common::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker; use embassy_sync::waitqueue::AtomicWaker;
use pac::i2c; use pac::i2c;
use crate::gpio::sealed::Pin; use crate::gpio::sealed::Pin;
use crate::gpio::AnyPin; use crate::gpio::AnyPin;
use crate::{pac, peripherals, Peripheral}; use crate::interrupt::typelevel::{Binding, Interrupt};
use crate::{interrupt, pac, peripherals, Peripheral};
/// I2C error abort reason /// I2C error abort reason
#[derive(Debug)] #[derive(Debug)]
@ -85,7 +85,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> {
let r = T::regs(); let r = T::regs();
// mask everything initially // mask everything initially
unsafe { r.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0)) } r.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0));
T::Interrupt::unpend(); T::Interrupt::unpend();
unsafe { T::Interrupt::enable() }; unsafe { T::Interrupt::enable() };
@ -135,13 +135,11 @@ impl<'d, T: Instance> I2c<'d, T, Async> {
let last = remaining_queue == 0; let last = remaining_queue == 0;
batch += 1; batch += 1;
unsafe { p.ic_data_cmd().write(|w| {
p.ic_data_cmd().write(|w| { w.set_restart(restart && remaining_queue == buffer.len() - 1);
w.set_restart(restart && remaining_queue == buffer.len() - 1); w.set_stop(last && send_stop);
w.set_stop(last && send_stop); w.set_cmd(true);
w.set_cmd(true); });
});
}
} }
// We've either run out of txfifo or just plain finished setting up // We've either run out of txfifo or just plain finished setting up
@ -161,7 +159,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> {
Poll::Pending Poll::Pending
} }
}, },
|_me| unsafe { |_me| {
// Set the read threshold to the number of bytes we're // Set the read threshold to the number of bytes we're
// expecting so we don't get spurious interrupts. // expecting so we don't get spurious interrupts.
p.ic_rx_tl().write(|w| w.set_rx_tl(batch - 1)); p.ic_rx_tl().write(|w| w.set_rx_tl(batch - 1));
@ -185,7 +183,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> {
let rxbytes = (rxfifo as usize).min(remaining); let rxbytes = (rxfifo as usize).min(remaining);
let received = buffer.len() - remaining; let received = buffer.len() - remaining;
for b in &mut buffer[received..received + rxbytes] { for b in &mut buffer[received..received + rxbytes] {
*b = unsafe { p.ic_data_cmd().read().dat() }; *b = p.ic_data_cmd().read().dat();
} }
remaining -= rxbytes; remaining -= rxbytes;
} }
@ -211,13 +209,11 @@ impl<'d, T: Instance> I2c<'d, T, Async> {
if let Some(byte) = bytes.next() { if let Some(byte) = bytes.next() {
let last = bytes.peek().is_none(); let last = bytes.peek().is_none();
unsafe { p.ic_data_cmd().write(|w| {
p.ic_data_cmd().write(|w| { w.set_stop(last && send_stop);
w.set_stop(last && send_stop); w.set_cmd(false);
w.set_cmd(false); w.set_dat(byte);
w.set_dat(byte); });
});
}
} else { } else {
break 'xmit Ok(()); break 'xmit Ok(());
} }
@ -235,7 +231,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> {
Poll::Pending Poll::Pending
} }
}, },
|_me| unsafe { |_me| {
// Set tx "free" threshold a little high so that we get // Set tx "free" threshold a little high so that we get
// woken before the fifo completely drains to minimize // woken before the fifo completely drains to minimize
// transfer stalls. // transfer stalls.
@ -267,7 +263,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> {
let had_abort2 = self let had_abort2 = self
.wait_on( .wait_on(
|me| unsafe { |me| {
// We could see an abort while processing fifo backlog, // We could see an abort while processing fifo backlog,
// so handle it here. // so handle it here.
let abort = me.read_and_clear_abort_reason(); let abort = me.read_and_clear_abort_reason();
@ -279,7 +275,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> {
Poll::Pending Poll::Pending
} }
}, },
|_me| unsafe { |_me| {
p.ic_intr_mask().modify(|w| { p.ic_intr_mask().modify(|w| {
w.set_m_stop_det(true); w.set_m_stop_det(true);
w.set_m_tx_abrt(true); w.set_m_tx_abrt(true);
@ -287,9 +283,7 @@ impl<'d, T: Instance> I2c<'d, T, Async> {
}, },
) )
.await; .await;
unsafe { p.ic_clr_stop_det().read();
p.ic_clr_stop_det().read();
}
had_abort.and(had_abort2) had_abort.and(had_abort2)
} else { } else {
@ -312,7 +306,7 @@ pub struct InterruptHandler<T: Instance> {
_uart: PhantomData<T>, _uart: PhantomData<T>,
} }
impl<T: Instance> interrupt::Handler<T::Interrupt> for InterruptHandler<T> { impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
// Mask interrupts and wake any task waiting for this interrupt // Mask interrupts and wake any task waiting for this interrupt
unsafe fn on_interrupt() { unsafe fn on_interrupt() {
let i2c = T::regs(); let i2c = T::regs();
@ -336,95 +330,93 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
let p = T::regs(); let p = T::regs();
unsafe { let reset = T::reset();
let reset = T::reset(); crate::reset::reset(reset);
crate::reset::reset(reset); crate::reset::unreset_wait(reset);
crate::reset::unreset_wait(reset);
p.ic_enable().write(|w| w.set_enable(false)); p.ic_enable().write(|w| w.set_enable(false));
// Select controller mode & speed // Select controller mode & speed
p.ic_con().modify(|w| { p.ic_con().modify(|w| {
// Always use "fast" mode (<= 400 kHz, works fine for standard // Always use "fast" mode (<= 400 kHz, works fine for standard
// mode too) // mode too)
w.set_speed(i2c::vals::Speed::FAST); w.set_speed(i2c::vals::Speed::FAST);
w.set_master_mode(true); w.set_master_mode(true);
w.set_ic_slave_disable(true); w.set_ic_slave_disable(true);
w.set_ic_restart_en(true); w.set_ic_restart_en(true);
w.set_tx_empty_ctrl(true); w.set_tx_empty_ctrl(true);
}); });
// Set FIFO watermarks to 1 to make things simpler. This is encoded // Set FIFO watermarks to 1 to make things simpler. This is encoded
// by a register value of 0. // by a register value of 0.
p.ic_tx_tl().write(|w| w.set_tx_tl(0)); p.ic_tx_tl().write(|w| w.set_tx_tl(0));
p.ic_rx_tl().write(|w| w.set_rx_tl(0)); p.ic_rx_tl().write(|w| w.set_rx_tl(0));
// Configure SCL & SDA pins // Configure SCL & SDA pins
scl.io().ctrl().write(|w| w.set_funcsel(3)); scl.io().ctrl().write(|w| w.set_funcsel(3));
sda.io().ctrl().write(|w| w.set_funcsel(3)); sda.io().ctrl().write(|w| w.set_funcsel(3));
scl.pad_ctrl().write(|w| { scl.pad_ctrl().write(|w| {
w.set_schmitt(true); w.set_schmitt(true);
w.set_ie(true); w.set_ie(true);
w.set_od(false); w.set_od(false);
w.set_pue(true); w.set_pue(true);
w.set_pde(false); w.set_pde(false);
}); });
sda.pad_ctrl().write(|w| { sda.pad_ctrl().write(|w| {
w.set_schmitt(true); w.set_schmitt(true);
w.set_ie(true); w.set_ie(true);
w.set_od(false); w.set_od(false);
w.set_pue(true); w.set_pue(true);
w.set_pde(false); w.set_pde(false);
}); });
// Configure baudrate // Configure baudrate
// There are some subtleties to I2C timing which we are completely // There are some subtleties to I2C timing which we are completely
// ignoring here See: // ignoring here See:
// https://github.com/raspberrypi/pico-sdk/blob/bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7/src/rp2_common/hardware_i2c/i2c.c#L69 // https://github.com/raspberrypi/pico-sdk/blob/bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7/src/rp2_common/hardware_i2c/i2c.c#L69
let clk_base = crate::clocks::clk_peri_freq(); let clk_base = crate::clocks::clk_peri_freq();
let period = (clk_base + config.frequency / 2) / config.frequency; let period = (clk_base + config.frequency / 2) / config.frequency;
let lcnt = period * 3 / 5; // spend 3/5 (60%) of the period low let lcnt = period * 3 / 5; // spend 3/5 (60%) of the period low
let hcnt = period - lcnt; // and 2/5 (40%) of the period high let hcnt = period - lcnt; // and 2/5 (40%) of the period high
// Check for out-of-range divisors: // Check for out-of-range divisors:
assert!(hcnt <= 0xffff); assert!(hcnt <= 0xffff);
assert!(lcnt <= 0xffff); assert!(lcnt <= 0xffff);
assert!(hcnt >= 8); assert!(hcnt >= 8);
assert!(lcnt >= 8); assert!(lcnt >= 8);
// Per I2C-bus specification a device in standard or fast mode must // Per I2C-bus specification a device in standard or fast mode must
// internally provide a hold time of at least 300ns for the SDA // internally provide a hold time of at least 300ns for the SDA
// signal to bridge the undefined region of the falling edge of SCL. // signal to bridge the undefined region of the falling edge of SCL.
// A smaller hold time of 120ns is used for fast mode plus. // A smaller hold time of 120ns is used for fast mode plus.
let sda_tx_hold_count = if config.frequency < 1_000_000 { let sda_tx_hold_count = if config.frequency < 1_000_000 {
// sda_tx_hold_count = clk_base [cycles/s] * 300ns * (1s / // sda_tx_hold_count = clk_base [cycles/s] * 300ns * (1s /
// 1e9ns) Reduce 300/1e9 to 3/1e7 to avoid numbers that don't // 1e9ns) Reduce 300/1e9 to 3/1e7 to avoid numbers that don't
// fit in uint. Add 1 to avoid division truncation. // fit in uint. Add 1 to avoid division truncation.
((clk_base * 3) / 10_000_000) + 1 ((clk_base * 3) / 10_000_000) + 1
} else { } else {
// fast mode plus requires a clk_base > 32MHz // fast mode plus requires a clk_base > 32MHz
assert!(clk_base >= 32_000_000); assert!(clk_base >= 32_000_000);
// sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s / // sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s /
// 1e9ns) Reduce 120/1e9 to 3/25e6 to avoid numbers that don't // 1e9ns) Reduce 120/1e9 to 3/25e6 to avoid numbers that don't
// fit in uint. Add 1 to avoid division truncation. // fit in uint. Add 1 to avoid division truncation.
((clk_base * 3) / 25_000_000) + 1 ((clk_base * 3) / 25_000_000) + 1
}; };
assert!(sda_tx_hold_count <= lcnt - 2); assert!(sda_tx_hold_count <= lcnt - 2);
p.ic_fs_scl_hcnt().write(|w| w.set_ic_fs_scl_hcnt(hcnt as u16)); p.ic_fs_scl_hcnt().write(|w| w.set_ic_fs_scl_hcnt(hcnt as u16));
p.ic_fs_scl_lcnt().write(|w| w.set_ic_fs_scl_lcnt(lcnt as u16)); p.ic_fs_scl_lcnt().write(|w| w.set_ic_fs_scl_lcnt(lcnt as u16));
p.ic_fs_spklen() p.ic_fs_spklen()
.write(|w| w.set_ic_fs_spklen(if lcnt < 16 { 1 } else { (lcnt / 16) as u8 })); .write(|w| w.set_ic_fs_spklen(if lcnt < 16 { 1 } else { (lcnt / 16) as u8 }));
p.ic_sda_hold() p.ic_sda_hold()
.modify(|w| w.set_ic_sda_tx_hold(sda_tx_hold_count as u16)); .modify(|w| w.set_ic_sda_tx_hold(sda_tx_hold_count as u16));
// Enable I2C block // Enable I2C block
p.ic_enable().write(|w| w.set_enable(true)); p.ic_enable().write(|w| w.set_enable(true));
}
Self { phantom: PhantomData } Self { phantom: PhantomData }
} }
@ -439,11 +431,9 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
} }
let p = T::regs(); let p = T::regs();
unsafe { p.ic_enable().write(|w| w.set_enable(false));
p.ic_enable().write(|w| w.set_enable(false)); p.ic_tar().write(|w| w.set_ic_tar(addr));
p.ic_tar().write(|w| w.set_ic_tar(addr)); p.ic_enable().write(|w| w.set_enable(true));
p.ic_enable().write(|w| w.set_enable(true));
}
Ok(()) Ok(())
} }
@ -455,40 +445,38 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
#[inline] #[inline]
fn tx_fifo_capacity() -> u8 { fn tx_fifo_capacity() -> u8 {
let p = T::regs(); let p = T::regs();
unsafe { FIFO_SIZE - p.ic_txflr().read().txflr() } FIFO_SIZE - p.ic_txflr().read().txflr()
} }
#[inline] #[inline]
fn rx_fifo_len() -> u8 { fn rx_fifo_len() -> u8 {
let p = T::regs(); let p = T::regs();
unsafe { p.ic_rxflr().read().rxflr() } p.ic_rxflr().read().rxflr()
} }
fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> { fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> {
let p = T::regs(); let p = T::regs();
unsafe { let abort_reason = p.ic_tx_abrt_source().read();
let abort_reason = p.ic_tx_abrt_source().read(); if abort_reason.0 != 0 {
if abort_reason.0 != 0 { // Note clearing the abort flag also clears the reason, and this
// Note clearing the abort flag also clears the reason, and this // instance of flag is clear-on-read! Note also the
// instance of flag is clear-on-read! Note also the // IC_CLR_TX_ABRT register always reads as 0.
// IC_CLR_TX_ABRT register always reads as 0. p.ic_clr_tx_abrt().read();
p.ic_clr_tx_abrt().read();
let reason = if abort_reason.abrt_7b_addr_noack() let reason = if abort_reason.abrt_7b_addr_noack()
| abort_reason.abrt_10addr1_noack() | abort_reason.abrt_10addr1_noack()
| abort_reason.abrt_10addr2_noack() | abort_reason.abrt_10addr2_noack()
{ {
AbortReason::NoAcknowledge AbortReason::NoAcknowledge
} else if abort_reason.arb_lost() { } else if abort_reason.arb_lost() {
AbortReason::ArbitrationLoss AbortReason::ArbitrationLoss
} else {
AbortReason::Other(abort_reason.0)
};
Err(Error::Abort(reason))
} else { } else {
Ok(()) AbortReason::Other(abort_reason.0)
} };
Err(Error::Abort(reason))
} else {
Ok(())
} }
} }
@ -503,24 +491,21 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
let first = i == 0; let first = i == 0;
let last = i == lastindex; let last = i == lastindex;
// NOTE(unsafe) We have &mut self // wait until there is space in the FIFO to write the next byte
unsafe { while Self::tx_fifo_full() {}
// wait until there is space in the FIFO to write the next byte
while Self::tx_fifo_full() {}
p.ic_data_cmd().write(|w| { p.ic_data_cmd().write(|w| {
w.set_restart(restart && first); w.set_restart(restart && first);
w.set_stop(send_stop && last); w.set_stop(send_stop && last);
w.set_cmd(true); w.set_cmd(true);
}); });
while Self::rx_fifo_len() == 0 { while Self::rx_fifo_len() == 0 {
self.read_and_clear_abort_reason()?; self.read_and_clear_abort_reason()?;
}
*byte = p.ic_data_cmd().read().dat();
} }
*byte = p.ic_data_cmd().read().dat();
} }
Ok(()) Ok(())
@ -536,36 +521,33 @@ impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
for (i, byte) in write.iter().enumerate() { for (i, byte) in write.iter().enumerate() {
let last = i == write.len() - 1; let last = i == write.len() - 1;
// NOTE(unsafe) We have &mut self p.ic_data_cmd().write(|w| {
unsafe { w.set_stop(send_stop && last);
p.ic_data_cmd().write(|w| { w.set_dat(*byte);
w.set_stop(send_stop && last); });
w.set_dat(*byte);
});
// Wait until the transmission of the address/data from the // Wait until the transmission of the address/data from the
// internal shift register has completed. For this to function // internal shift register has completed. For this to function
// correctly, the TX_EMPTY_CTRL flag in IC_CON must be set. The // correctly, the TX_EMPTY_CTRL flag in IC_CON must be set. The
// TX_EMPTY_CTRL flag was set in i2c_init. // TX_EMPTY_CTRL flag was set in i2c_init.
while !p.ic_raw_intr_stat().read().tx_empty() {} while !p.ic_raw_intr_stat().read().tx_empty() {}
let abort_reason = self.read_and_clear_abort_reason(); let abort_reason = self.read_and_clear_abort_reason();
if abort_reason.is_err() || (send_stop && last) { if abort_reason.is_err() || (send_stop && last) {
// If the transaction was aborted or if it completed // If the transaction was aborted or if it completed
// successfully wait until the STOP condition has occurred. // successfully wait until the STOP condition has occurred.
while !p.ic_raw_intr_stat().read().stop_det() {} while !p.ic_raw_intr_stat().read().stop_det() {}
p.ic_clr_stop_det().read().clr_stop_det(); p.ic_clr_stop_det().read().clr_stop_det();
}
// Note the hardware issues a STOP automatically on an abort
// condition. Note also the hardware clears RX FIFO as well as
// TX on abort, ecause we set hwparam
// IC_AVOID_RX_FIFO_FLUSH_ON_TX_ABRT to 0.
abort_reason?;
} }
// Note the hardware issues a STOP automatically on an abort
// condition. Note also the hardware clears RX FIFO as well as
// TX on abort, ecause we set hwparam
// IC_AVOID_RX_FIFO_FLUSH_ON_TX_ABRT to 0.
abort_reason?;
} }
Ok(()) Ok(())
} }
@ -760,14 +742,15 @@ fn i2c_reserved_addr(addr: u16) -> bool {
} }
mod sealed { mod sealed {
use embassy_cortex_m::interrupt::Interrupt;
use embassy_sync::waitqueue::AtomicWaker; use embassy_sync::waitqueue::AtomicWaker;
use crate::interrupt;
pub trait Instance { pub trait Instance {
const TX_DREQ: u8; const TX_DREQ: u8;
const RX_DREQ: u8; const RX_DREQ: u8;
type Interrupt: Interrupt; type Interrupt: interrupt::typelevel::Interrupt;
fn regs() -> crate::pac::i2c::I2c; fn regs() -> crate::pac::i2c::I2c;
fn reset() -> crate::pac::resets::regs::Peripherals; fn reset() -> crate::pac::resets::regs::Peripherals;
@ -803,7 +786,7 @@ macro_rules! impl_instance {
const TX_DREQ: u8 = $tx_dreq; const TX_DREQ: u8 = $tx_dreq;
const RX_DREQ: u8 = $rx_dreq; const RX_DREQ: u8 = $rx_dreq;
type Interrupt = crate::interrupt::$irq; type Interrupt = crate::interrupt::typelevel::$irq;
#[inline] #[inline]
fn regs() -> pac::i2c::I2c { fn regs() -> pac::i2c::I2c {

View File

@ -1,65 +0,0 @@
//! Interrupt definitions and macros to bind them.
pub use cortex_m::interrupt::{CriticalSection, Mutex};
use embassy_cortex_m::interrupt::_export::declare;
pub use embassy_cortex_m::interrupt::{Binding, Handler, Interrupt, Priority};
use crate::pac::Interrupt as InterruptEnum;
declare!(TIMER_IRQ_0);
declare!(TIMER_IRQ_1);
declare!(TIMER_IRQ_2);
declare!(TIMER_IRQ_3);
declare!(PWM_IRQ_WRAP);
declare!(USBCTRL_IRQ);
declare!(XIP_IRQ);
declare!(PIO0_IRQ_0);
declare!(PIO0_IRQ_1);
declare!(PIO1_IRQ_0);
declare!(PIO1_IRQ_1);
declare!(DMA_IRQ_0);
declare!(DMA_IRQ_1);
declare!(IO_IRQ_BANK0);
declare!(IO_IRQ_QSPI);
declare!(SIO_IRQ_PROC0);
declare!(SIO_IRQ_PROC1);
declare!(CLOCKS_IRQ);
declare!(SPI0_IRQ);
declare!(SPI1_IRQ);
declare!(UART0_IRQ);
declare!(UART1_IRQ);
declare!(ADC_IRQ_FIFO);
declare!(I2C0_IRQ);
declare!(I2C1_IRQ);
declare!(RTC_IRQ);
declare!(SWI_IRQ_0);
declare!(SWI_IRQ_1);
declare!(SWI_IRQ_2);
declare!(SWI_IRQ_3);
declare!(SWI_IRQ_4);
declare!(SWI_IRQ_5);
/// Macro to bind interrupts to handlers.
///
/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)
/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to
/// prove at compile-time that the right interrupts have been bound.
// developer note: this macro can't be in `embassy-cortex-m` due to the use of `$crate`.
#[macro_export]
macro_rules! bind_interrupts {
($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => {
$vis struct $name;
$(
#[allow(non_snake_case)]
#[no_mangle]
unsafe extern "C" fn $irq() {
$(
<$handler as $crate::interrupt::Handler<$crate::interrupt::$irq>>::on_interrupt();
)*
}
$(
unsafe impl $crate::interrupt::Binding<$crate::interrupt::$irq, $handler> for $name {}
)*
)*
};
}

View File

@ -16,7 +16,6 @@ pub mod flash;
mod float; mod float;
pub mod gpio; pub mod gpio;
pub mod i2c; pub mod i2c;
pub mod interrupt;
pub mod multicore; pub mod multicore;
pub mod pwm; pub mod pwm;
mod reset; mod reset;
@ -37,14 +36,77 @@ pub mod pio_instr_util;
pub mod relocate; pub mod relocate;
// Reexports // Reexports
pub use embassy_cortex_m::executor;
pub use embassy_cortex_m::interrupt::_export::interrupt;
pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
#[cfg(feature = "unstable-pac")] #[cfg(feature = "unstable-pac")]
pub use rp_pac as pac; pub use rp_pac as pac;
#[cfg(not(feature = "unstable-pac"))] #[cfg(not(feature = "unstable-pac"))]
pub(crate) use rp_pac as pac; pub(crate) use rp_pac as pac;
#[cfg(feature = "rt")]
pub use crate::pac::NVIC_PRIO_BITS;
embassy_hal_common::interrupt_mod!(
TIMER_IRQ_0,
TIMER_IRQ_1,
TIMER_IRQ_2,
TIMER_IRQ_3,
PWM_IRQ_WRAP,
USBCTRL_IRQ,
XIP_IRQ,
PIO0_IRQ_0,
PIO0_IRQ_1,
PIO1_IRQ_0,
PIO1_IRQ_1,
DMA_IRQ_0,
DMA_IRQ_1,
IO_IRQ_BANK0,
IO_IRQ_QSPI,
SIO_IRQ_PROC0,
SIO_IRQ_PROC1,
CLOCKS_IRQ,
SPI0_IRQ,
SPI1_IRQ,
UART0_IRQ,
UART1_IRQ,
ADC_IRQ_FIFO,
I2C0_IRQ,
I2C1_IRQ,
RTC_IRQ,
SWI_IRQ_0,
SWI_IRQ_1,
SWI_IRQ_2,
SWI_IRQ_3,
SWI_IRQ_4,
SWI_IRQ_5,
);
/// Macro to bind interrupts to handlers.
///
/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)
/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to
/// prove at compile-time that the right interrupts have been bound.
// developer note: this macro can't be in `embassy-hal-common` due to the use of `$crate`.
#[macro_export]
macro_rules! bind_interrupts {
($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => {
$vis struct $name;
$(
#[allow(non_snake_case)]
#[no_mangle]
unsafe extern "C" fn $irq() {
$(
<$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt();
)*
}
$(
unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {}
)*
)*
};
}
embassy_hal_common::peripherals! { embassy_hal_common::peripherals! {
PIN_0, PIN_0,
PIN_1, PIN_1,
@ -199,33 +261,39 @@ pub fn init(config: config::Config) -> Peripherals {
/// Extension trait for PAC regs, adding atomic xor/bitset/bitclear writes. /// Extension trait for PAC regs, adding atomic xor/bitset/bitclear writes.
trait RegExt<T: Copy> { trait RegExt<T: Copy> {
unsafe fn write_xor<R>(&self, f: impl FnOnce(&mut T) -> R) -> R; fn write_xor<R>(&self, f: impl FnOnce(&mut T) -> R) -> R;
unsafe fn write_set<R>(&self, f: impl FnOnce(&mut T) -> R) -> R; fn write_set<R>(&self, f: impl FnOnce(&mut T) -> R) -> R;
unsafe fn write_clear<R>(&self, f: impl FnOnce(&mut T) -> R) -> R; fn write_clear<R>(&self, f: impl FnOnce(&mut T) -> R) -> R;
} }
impl<T: Default + Copy, A: pac::common::Write> RegExt<T> for pac::common::Reg<T, A> { impl<T: Default + Copy, A: pac::common::Write> RegExt<T> for pac::common::Reg<T, A> {
unsafe fn write_xor<R>(&self, f: impl FnOnce(&mut T) -> R) -> R { fn write_xor<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
let mut val = Default::default(); let mut val = Default::default();
let res = f(&mut val); let res = f(&mut val);
let ptr = (self.ptr() as *mut u8).add(0x1000) as *mut T; unsafe {
ptr.write_volatile(val); let ptr = (self.as_ptr() as *mut u8).add(0x1000) as *mut T;
ptr.write_volatile(val);
}
res res
} }
unsafe fn write_set<R>(&self, f: impl FnOnce(&mut T) -> R) -> R { fn write_set<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
let mut val = Default::default(); let mut val = Default::default();
let res = f(&mut val); let res = f(&mut val);
let ptr = (self.ptr() as *mut u8).add(0x2000) as *mut T; unsafe {
ptr.write_volatile(val); let ptr = (self.as_ptr() as *mut u8).add(0x2000) as *mut T;
ptr.write_volatile(val);
}
res res
} }
unsafe fn write_clear<R>(&self, f: impl FnOnce(&mut T) -> R) -> R { fn write_clear<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
let mut val = Default::default(); let mut val = Default::default();
let res = f(&mut val); let res = f(&mut val);
let ptr = (self.ptr() as *mut u8).add(0x3000) as *mut T; unsafe {
ptr.write_volatile(val); let ptr = (self.as_ptr() as *mut u8).add(0x3000) as *mut T;
ptr.write_volatile(val);
}
res res
} }
} }

View File

@ -50,7 +50,7 @@
use core::mem::ManuallyDrop; use core::mem::ManuallyDrop;
use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; use core::sync::atomic::{compiler_fence, AtomicBool, Ordering};
use crate::interrupt::Interrupt; use crate::interrupt::InterruptExt;
use crate::peripherals::CORE1; use crate::peripherals::CORE1;
use crate::{gpio, interrupt, pac}; use crate::{gpio, interrupt, pac};
@ -106,6 +106,7 @@ impl<const SIZE: usize> Stack<SIZE> {
} }
} }
#[cfg(feature = "rt")]
#[interrupt] #[interrupt]
#[link_section = ".data.ram_func"] #[link_section = ".data.ram_func"]
unsafe fn SIO_IRQ_PROC1() { unsafe fn SIO_IRQ_PROC1() {
@ -156,20 +157,18 @@ where
IS_CORE1_INIT.store(true, Ordering::Release); IS_CORE1_INIT.store(true, Ordering::Release);
// Enable fifo interrupt on CORE1 for `pause` functionality. // Enable fifo interrupt on CORE1 for `pause` functionality.
unsafe { interrupt::SIO_IRQ_PROC1::enable() }; unsafe { interrupt::SIO_IRQ_PROC1.enable() };
entry() entry()
} }
// Reset the core // Reset the core
unsafe { let psm = pac::PSM;
let psm = pac::PSM; psm.frce_off().modify(|w| w.set_proc1(true));
psm.frce_off().modify(|w| w.set_proc1(true)); while !psm.frce_off().read().proc1() {
while !psm.frce_off().read().proc1() { cortex_m::asm::nop();
cortex_m::asm::nop();
}
psm.frce_off().modify(|w| w.set_proc1(false));
} }
psm.frce_off().modify(|w| w.set_proc1(false));
// The ARM AAPCS ABI requires 8-byte stack alignment. // The ARM AAPCS ABI requires 8-byte stack alignment.
// #[align] on `struct Stack` ensures the bottom is aligned, but the top could still be // #[align] on `struct Stack` ensures the bottom is aligned, but the top could still be
@ -269,14 +268,12 @@ pub fn resume_core1() {
// Push a value to the inter-core FIFO, block until space is available // Push a value to the inter-core FIFO, block until space is available
#[inline(always)] #[inline(always)]
fn fifo_write(value: u32) { fn fifo_write(value: u32) {
unsafe { let sio = pac::SIO;
let sio = pac::SIO; // Wait for the FIFO to have enough space
// Wait for the FIFO to have enough space while !sio.fifo().st().read().rdy() {
while !sio.fifo().st().read().rdy() { cortex_m::asm::nop();
cortex_m::asm::nop();
}
sio.fifo().wr().write_value(value);
} }
sio.fifo().wr().write_value(value);
// Fire off an event to the other core. // Fire off an event to the other core.
// This is required as the other core may be `wfe` (waiting for event) // This is required as the other core may be `wfe` (waiting for event)
cortex_m::asm::sev(); cortex_m::asm::sev();
@ -285,37 +282,32 @@ fn fifo_write(value: u32) {
// Pop a value from inter-core FIFO, block until available // Pop a value from inter-core FIFO, block until available
#[inline(always)] #[inline(always)]
fn fifo_read() -> u32 { fn fifo_read() -> u32 {
unsafe { let sio = pac::SIO;
let sio = pac::SIO; // Wait until FIFO has data
// Wait until FIFO has data while !sio.fifo().st().read().vld() {
while !sio.fifo().st().read().vld() { cortex_m::asm::nop();
cortex_m::asm::nop();
}
sio.fifo().rd().read()
} }
sio.fifo().rd().read()
} }
// Pop a value from inter-core FIFO, `wfe` until available // Pop a value from inter-core FIFO, `wfe` until available
#[inline(always)] #[inline(always)]
#[allow(unused)]
fn fifo_read_wfe() -> u32 { fn fifo_read_wfe() -> u32 {
unsafe { let sio = pac::SIO;
let sio = pac::SIO; // Wait until FIFO has data
// Wait until FIFO has data while !sio.fifo().st().read().vld() {
while !sio.fifo().st().read().vld() { cortex_m::asm::wfe();
cortex_m::asm::wfe();
}
sio.fifo().rd().read()
} }
sio.fifo().rd().read()
} }
// Drain inter-core FIFO // Drain inter-core FIFO
#[inline(always)] #[inline(always)]
fn fifo_drain() { fn fifo_drain() {
unsafe { let sio = pac::SIO;
let sio = pac::SIO; while sio.fifo().st().read().vld() {
while sio.fifo().st().read().vld() { let _ = sio.fifo().rd().read();
let _ = sio.fifo().rd().read();
}
} }
} }

View File

@ -5,7 +5,6 @@ use core::sync::atomic::{compiler_fence, Ordering};
use core::task::{Context, Poll}; use core::task::{Context, Poll};
use atomic_polyfill::{AtomicU32, AtomicU8}; use atomic_polyfill::{AtomicU32, AtomicU8};
use embassy_cortex_m::interrupt::Interrupt;
use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker; use embassy_sync::waitqueue::AtomicWaker;
use fixed::types::extra::U8; use fixed::types::extra::U8;
@ -17,6 +16,7 @@ use pio::{SideSet, Wrap};
use crate::dma::{Channel, Transfer, Word}; use crate::dma::{Channel, Transfer, Word};
use crate::gpio::sealed::Pin as SealedPin; use crate::gpio::sealed::Pin as SealedPin;
use crate::gpio::{self, AnyPin, Drive, Level, Pull, SlewRate}; use crate::gpio::{self, AnyPin, Drive, Level, Pull, SlewRate};
use crate::interrupt::InterruptExt;
use crate::pac::dma::vals::TreqSel; use crate::pac::dma::vals::TreqSel;
use crate::relocate::RelocatedProgram; use crate::relocate::RelocatedProgram;
use crate::{interrupt, pac, peripherals, pio_instr_util, RegExt}; use crate::{interrupt, pac, peripherals, pio_instr_util, RegExt};
@ -85,8 +85,9 @@ const RXNEMPTY_MASK: u32 = 1 << 0;
const TXNFULL_MASK: u32 = 1 << 4; const TXNFULL_MASK: u32 = 1 << 4;
const SMIRQ_MASK: u32 = 1 << 8; const SMIRQ_MASK: u32 = 1 << 8;
#[cfg(feature = "rt")]
#[interrupt] #[interrupt]
unsafe fn PIO0_IRQ_0() { fn PIO0_IRQ_0() {
use crate::pac; use crate::pac;
let ints = pac::PIO0.irqs(0).ints().read().0; let ints = pac::PIO0.irqs(0).ints().read().0;
for bit in 0..12 { for bit in 0..12 {
@ -97,8 +98,9 @@ unsafe fn PIO0_IRQ_0() {
pac::PIO0.irqs(0).inte().write_clear(|m| m.0 = ints); pac::PIO0.irqs(0).inte().write_clear(|m| m.0 = ints);
} }
#[cfg(feature = "rt")]
#[interrupt] #[interrupt]
unsafe fn PIO1_IRQ_0() { fn PIO1_IRQ_0() {
use crate::pac; use crate::pac;
let ints = pac::PIO1.irqs(0).ints().read().0; let ints = pac::PIO1.irqs(0).ints().read().0;
for bit in 0..12 { for bit in 0..12 {
@ -110,15 +112,15 @@ unsafe fn PIO1_IRQ_0() {
} }
pub(crate) unsafe fn init() { pub(crate) unsafe fn init() {
interrupt::PIO0_IRQ_0::disable(); interrupt::PIO0_IRQ_0.disable();
interrupt::PIO0_IRQ_0::set_priority(interrupt::Priority::P3); interrupt::PIO0_IRQ_0.set_priority(interrupt::Priority::P3);
pac::PIO0.irqs(0).inte().write(|m| m.0 = 0); pac::PIO0.irqs(0).inte().write(|m| m.0 = 0);
interrupt::PIO0_IRQ_0::enable(); interrupt::PIO0_IRQ_0.enable();
interrupt::PIO1_IRQ_0::disable(); interrupt::PIO1_IRQ_0.disable();
interrupt::PIO1_IRQ_0::set_priority(interrupt::Priority::P3); interrupt::PIO1_IRQ_0.set_priority(interrupt::Priority::P3);
pac::PIO1.irqs(0).inte().write(|m| m.0 = 0); pac::PIO1.irqs(0).inte().write(|m| m.0 = 0);
interrupt::PIO1_IRQ_0::enable(); interrupt::PIO1_IRQ_0.enable();
} }
/// Future that waits for TX-FIFO to become writable /// Future that waits for TX-FIFO to become writable
@ -143,11 +145,9 @@ impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoOutFuture<'a, 'd, PI
Poll::Ready(()) Poll::Ready(())
} else { } else {
WAKERS[PIO::PIO_NO as usize].fifo_out()[SM].register(cx.waker()); WAKERS[PIO::PIO_NO as usize].fifo_out()[SM].register(cx.waker());
unsafe { PIO::PIO.irqs(0).inte().write_set(|m| {
PIO::PIO.irqs(0).inte().write_set(|m| { m.0 = TXNFULL_MASK << SM;
m.0 = TXNFULL_MASK << SM; });
});
}
// debug!("Pending"); // debug!("Pending");
Poll::Pending Poll::Pending
} }
@ -156,11 +156,9 @@ impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoOutFuture<'a, 'd, PI
impl<'a, 'd, PIO: Instance, const SM: usize> Drop for FifoOutFuture<'a, 'd, PIO, SM> { impl<'a, 'd, PIO: Instance, const SM: usize> Drop for FifoOutFuture<'a, 'd, PIO, SM> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { PIO::PIO.irqs(0).inte().write_clear(|m| {
PIO::PIO.irqs(0).inte().write_clear(|m| { m.0 = TXNFULL_MASK << SM;
m.0 = TXNFULL_MASK << SM; });
});
}
} }
} }
@ -184,11 +182,9 @@ impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoInFuture<'a, 'd, PIO
Poll::Ready(v) Poll::Ready(v)
} else { } else {
WAKERS[PIO::PIO_NO as usize].fifo_in()[SM].register(cx.waker()); WAKERS[PIO::PIO_NO as usize].fifo_in()[SM].register(cx.waker());
unsafe { PIO::PIO.irqs(0).inte().write_set(|m| {
PIO::PIO.irqs(0).inte().write_set(|m| { m.0 = RXNEMPTY_MASK << SM;
m.0 = RXNEMPTY_MASK << SM; });
});
}
//debug!("Pending"); //debug!("Pending");
Poll::Pending Poll::Pending
} }
@ -197,11 +193,9 @@ impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoInFuture<'a, 'd, PIO
impl<'a, 'd, PIO: Instance, const SM: usize> Drop for FifoInFuture<'a, 'd, PIO, SM> { impl<'a, 'd, PIO: Instance, const SM: usize> Drop for FifoInFuture<'a, 'd, PIO, SM> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { PIO::PIO.irqs(0).inte().write_clear(|m| {
PIO::PIO.irqs(0).inte().write_clear(|m| { m.0 = RXNEMPTY_MASK << SM;
m.0 = RXNEMPTY_MASK << SM; });
});
}
} }
} }
@ -218,30 +212,24 @@ impl<'a, 'd, PIO: Instance> Future for IrqFuture<'a, 'd, PIO> {
//debug!("Poll {},{}", PIO::PIO_NO, SM); //debug!("Poll {},{}", PIO::PIO_NO, SM);
// Check if IRQ flag is already set // Check if IRQ flag is already set
if unsafe { PIO::PIO.irq().read().0 & (1 << self.irq_no) != 0 } { if PIO::PIO.irq().read().0 & (1 << self.irq_no) != 0 {
unsafe { PIO::PIO.irq().write(|m| m.0 = 1 << self.irq_no);
PIO::PIO.irq().write(|m| m.0 = 1 << self.irq_no);
}
return Poll::Ready(()); return Poll::Ready(());
} }
WAKERS[PIO::PIO_NO as usize].irq()[self.irq_no as usize].register(cx.waker()); WAKERS[PIO::PIO_NO as usize].irq()[self.irq_no as usize].register(cx.waker());
unsafe { PIO::PIO.irqs(0).inte().write_set(|m| {
PIO::PIO.irqs(0).inte().write_set(|m| { m.0 = SMIRQ_MASK << self.irq_no;
m.0 = SMIRQ_MASK << self.irq_no; });
});
}
Poll::Pending Poll::Pending
} }
} }
impl<'a, 'd, PIO: Instance> Drop for IrqFuture<'a, 'd, PIO> { impl<'a, 'd, PIO: Instance> Drop for IrqFuture<'a, 'd, PIO> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { PIO::PIO.irqs(0).inte().write_clear(|m| {
PIO::PIO.irqs(0).inte().write_clear(|m| { m.0 = SMIRQ_MASK << self.irq_no;
m.0 = SMIRQ_MASK << self.irq_no; });
});
}
} }
} }
@ -254,57 +242,47 @@ impl<'l, PIO: Instance> Pin<'l, PIO> {
/// Set the pin's drive strength. /// Set the pin's drive strength.
#[inline] #[inline]
pub fn set_drive_strength(&mut self, strength: Drive) { pub fn set_drive_strength(&mut self, strength: Drive) {
unsafe { self.pin.pad_ctrl().modify(|w| {
self.pin.pad_ctrl().modify(|w| { w.set_drive(match strength {
w.set_drive(match strength { Drive::_2mA => pac::pads::vals::Drive::_2MA,
Drive::_2mA => pac::pads::vals::Drive::_2MA, Drive::_4mA => pac::pads::vals::Drive::_4MA,
Drive::_4mA => pac::pads::vals::Drive::_4MA, Drive::_8mA => pac::pads::vals::Drive::_8MA,
Drive::_8mA => pac::pads::vals::Drive::_8MA, Drive::_12mA => pac::pads::vals::Drive::_12MA,
Drive::_12mA => pac::pads::vals::Drive::_12MA,
});
}); });
} });
} }
// Set the pin's slew rate. // Set the pin's slew rate.
#[inline] #[inline]
pub fn set_slew_rate(&mut self, slew_rate: SlewRate) { pub fn set_slew_rate(&mut self, slew_rate: SlewRate) {
unsafe { self.pin.pad_ctrl().modify(|w| {
self.pin.pad_ctrl().modify(|w| { w.set_slewfast(slew_rate == SlewRate::Fast);
w.set_slewfast(slew_rate == SlewRate::Fast); });
});
}
} }
/// Set the pin's pull. /// Set the pin's pull.
#[inline] #[inline]
pub fn set_pull(&mut self, pull: Pull) { pub fn set_pull(&mut self, pull: Pull) {
unsafe { self.pin.pad_ctrl().modify(|w| {
self.pin.pad_ctrl().modify(|w| { w.set_pue(pull == Pull::Up);
w.set_pue(pull == Pull::Up); w.set_pde(pull == Pull::Down);
w.set_pde(pull == Pull::Down); });
});
}
} }
/// Set the pin's schmitt trigger. /// Set the pin's schmitt trigger.
#[inline] #[inline]
pub fn set_schmitt(&mut self, enable: bool) { pub fn set_schmitt(&mut self, enable: bool) {
unsafe { self.pin.pad_ctrl().modify(|w| {
self.pin.pad_ctrl().modify(|w| { w.set_schmitt(enable);
w.set_schmitt(enable); });
});
}
} }
pub fn set_input_sync_bypass<'a>(&mut self, bypass: bool) { pub fn set_input_sync_bypass<'a>(&mut self, bypass: bool) {
let mask = 1 << self.pin(); let mask = 1 << self.pin();
unsafe { if bypass {
if bypass { PIO::PIO.input_sync_bypass().write_set(|w| *w = mask);
PIO::PIO.input_sync_bypass().write_set(|w| *w = mask); } else {
} else { PIO::PIO.input_sync_bypass().write_clear(|w| *w = mask);
PIO::PIO.input_sync_bypass().write_clear(|w| *w = mask);
}
} }
} }
@ -319,41 +297,37 @@ pub struct StateMachineRx<'d, PIO: Instance, const SM: usize> {
impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> { impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> {
pub fn empty(&self) -> bool { pub fn empty(&self) -> bool {
unsafe { PIO::PIO.fstat().read().rxempty() & (1u8 << SM) != 0 } PIO::PIO.fstat().read().rxempty() & (1u8 << SM) != 0
} }
pub fn full(&self) -> bool { pub fn full(&self) -> bool {
unsafe { PIO::PIO.fstat().read().rxfull() & (1u8 << SM) != 0 } PIO::PIO.fstat().read().rxfull() & (1u8 << SM) != 0
} }
pub fn level(&self) -> u8 { pub fn level(&self) -> u8 {
unsafe { (PIO::PIO.flevel().read().0 >> (SM * 8 + 4)) as u8 & 0x0f } (PIO::PIO.flevel().read().0 >> (SM * 8 + 4)) as u8 & 0x0f
} }
pub fn stalled(&self) -> bool { pub fn stalled(&self) -> bool {
unsafe { let fdebug = PIO::PIO.fdebug();
let fdebug = PIO::PIO.fdebug(); let ret = fdebug.read().rxstall() & (1 << SM) != 0;
let ret = fdebug.read().rxstall() & (1 << SM) != 0; if ret {
if ret { fdebug.write(|w| w.set_rxstall(1 << SM));
fdebug.write(|w| w.set_rxstall(1 << SM));
}
ret
} }
ret
} }
pub fn underflowed(&self) -> bool { pub fn underflowed(&self) -> bool {
unsafe { let fdebug = PIO::PIO.fdebug();
let fdebug = PIO::PIO.fdebug(); let ret = fdebug.read().rxunder() & (1 << SM) != 0;
let ret = fdebug.read().rxunder() & (1 << SM) != 0; if ret {
if ret { fdebug.write(|w| w.set_rxunder(1 << SM));
fdebug.write(|w| w.set_rxunder(1 << SM));
}
ret
} }
ret
} }
pub fn pull(&mut self) -> u32 { pub fn pull(&mut self) -> u32 {
unsafe { PIO::PIO.rxf(SM).read() } PIO::PIO.rxf(SM).read()
} }
pub fn try_pull(&mut self) -> Option<u32> { pub fn try_pull(&mut self) -> Option<u32> {
@ -372,24 +346,22 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> {
ch: PeripheralRef<'a, C>, ch: PeripheralRef<'a, C>,
data: &'a mut [W], data: &'a mut [W],
) -> Transfer<'a, C> { ) -> Transfer<'a, C> {
unsafe { let pio_no = PIO::PIO_NO;
let pio_no = PIO::PIO_NO; let p = ch.regs();
let p = ch.regs(); p.write_addr().write_value(data.as_ptr() as u32);
p.write_addr().write_value(data.as_ptr() as u32); p.read_addr().write_value(PIO::PIO.rxf(SM).as_ptr() as u32);
p.read_addr().write_value(PIO::PIO.rxf(SM).ptr() as u32); p.trans_count().write_value(data.len() as u32);
p.trans_count().write_value(data.len() as u32); compiler_fence(Ordering::SeqCst);
compiler_fence(Ordering::SeqCst); p.ctrl_trig().write(|w| {
p.ctrl_trig().write(|w| { // Set RX DREQ for this statemachine
// Set RX DREQ for this statemachine w.set_treq_sel(TreqSel(pio_no * 8 + SM as u8 + 4));
w.set_treq_sel(TreqSel(pio_no * 8 + SM as u8 + 4)); w.set_data_size(W::size());
w.set_data_size(W::size()); w.set_chain_to(ch.number());
w.set_chain_to(ch.number()); w.set_incr_read(false);
w.set_incr_read(false); w.set_incr_write(true);
w.set_incr_write(true); w.set_en(true);
w.set_en(true); });
}); compiler_fence(Ordering::SeqCst);
compiler_fence(Ordering::SeqCst);
}
Transfer::new(ch) Transfer::new(ch)
} }
} }
@ -400,42 +372,36 @@ pub struct StateMachineTx<'d, PIO: Instance, const SM: usize> {
impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> { impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> {
pub fn empty(&self) -> bool { pub fn empty(&self) -> bool {
unsafe { PIO::PIO.fstat().read().txempty() & (1u8 << SM) != 0 } PIO::PIO.fstat().read().txempty() & (1u8 << SM) != 0
} }
pub fn full(&self) -> bool { pub fn full(&self) -> bool {
unsafe { PIO::PIO.fstat().read().txfull() & (1u8 << SM) != 0 } PIO::PIO.fstat().read().txfull() & (1u8 << SM) != 0
} }
pub fn level(&self) -> u8 { pub fn level(&self) -> u8 {
unsafe { (PIO::PIO.flevel().read().0 >> (SM * 8)) as u8 & 0x0f } (PIO::PIO.flevel().read().0 >> (SM * 8)) as u8 & 0x0f
} }
pub fn stalled(&self) -> bool { pub fn stalled(&self) -> bool {
unsafe { let fdebug = PIO::PIO.fdebug();
let fdebug = PIO::PIO.fdebug(); let ret = fdebug.read().txstall() & (1 << SM) != 0;
let ret = fdebug.read().txstall() & (1 << SM) != 0; if ret {
if ret { fdebug.write(|w| w.set_txstall(1 << SM));
fdebug.write(|w| w.set_txstall(1 << SM));
}
ret
} }
ret
} }
pub fn overflowed(&self) -> bool { pub fn overflowed(&self) -> bool {
unsafe { let fdebug = PIO::PIO.fdebug();
let fdebug = PIO::PIO.fdebug(); let ret = fdebug.read().txover() & (1 << SM) != 0;
let ret = fdebug.read().txover() & (1 << SM) != 0; if ret {
if ret { fdebug.write(|w| w.set_txover(1 << SM));
fdebug.write(|w| w.set_txover(1 << SM));
}
ret
} }
ret
} }
pub fn push(&mut self, v: u32) { pub fn push(&mut self, v: u32) {
unsafe { PIO::PIO.txf(SM).write_value(v);
PIO::PIO.txf(SM).write_value(v);
}
} }
pub fn try_push(&mut self, v: u32) -> bool { pub fn try_push(&mut self, v: u32) -> bool {
@ -451,24 +417,22 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> {
} }
pub fn dma_push<'a, C: Channel, W: Word>(&'a mut self, ch: PeripheralRef<'a, C>, data: &'a [W]) -> Transfer<'a, C> { pub fn dma_push<'a, C: Channel, W: Word>(&'a mut self, ch: PeripheralRef<'a, C>, data: &'a [W]) -> Transfer<'a, C> {
unsafe { let pio_no = PIO::PIO_NO;
let pio_no = PIO::PIO_NO; let p = ch.regs();
let p = ch.regs(); p.read_addr().write_value(data.as_ptr() as u32);
p.read_addr().write_value(data.as_ptr() as u32); p.write_addr().write_value(PIO::PIO.txf(SM).as_ptr() as u32);
p.write_addr().write_value(PIO::PIO.txf(SM).ptr() as u32); p.trans_count().write_value(data.len() as u32);
p.trans_count().write_value(data.len() as u32); compiler_fence(Ordering::SeqCst);
compiler_fence(Ordering::SeqCst); p.ctrl_trig().write(|w| {
p.ctrl_trig().write(|w| { // Set TX DREQ for this statemachine
// Set TX DREQ for this statemachine w.set_treq_sel(TreqSel(pio_no * 8 + SM as u8));
w.set_treq_sel(TreqSel(pio_no * 8 + SM as u8)); w.set_data_size(W::size());
w.set_data_size(W::size()); w.set_chain_to(ch.number());
w.set_chain_to(ch.number()); w.set_incr_read(true);
w.set_incr_read(true); w.set_incr_write(false);
w.set_incr_write(false); w.set_en(true);
w.set_en(true); });
}); compiler_fence(Ordering::SeqCst);
compiler_fence(Ordering::SeqCst);
}
Transfer::new(ch) Transfer::new(ch)
} }
} }
@ -480,9 +444,7 @@ pub struct StateMachine<'d, PIO: Instance, const SM: usize> {
impl<'d, PIO: Instance, const SM: usize> Drop for StateMachine<'d, PIO, SM> { impl<'d, PIO: Instance, const SM: usize> Drop for StateMachine<'d, PIO, SM> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { PIO::PIO.ctrl().write_clear(|w| w.set_sm_enable(1 << SM));
PIO::PIO.ctrl().write_clear(|w| w.set_sm_enable(1 << SM));
}
on_pio_drop::<PIO>(); on_pio_drop::<PIO>();
} }
} }
@ -645,45 +607,43 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> {
assert!(config.shift_in.threshold <= 32, "shift_in.threshold must be <= 32"); assert!(config.shift_in.threshold <= 32, "shift_in.threshold must be <= 32");
assert!(config.shift_out.threshold <= 32, "shift_out.threshold must be <= 32"); assert!(config.shift_out.threshold <= 32, "shift_out.threshold must be <= 32");
let sm = Self::this_sm(); let sm = Self::this_sm();
unsafe { sm.clkdiv().write(|w| w.0 = config.clock_divider.to_bits() << 8);
sm.clkdiv().write(|w| w.0 = config.clock_divider.to_bits() << 8); sm.execctrl().write(|w| {
sm.execctrl().write(|w| { w.set_side_en(config.exec.side_en);
w.set_side_en(config.exec.side_en); w.set_side_pindir(config.exec.side_pindir);
w.set_side_pindir(config.exec.side_pindir); w.set_jmp_pin(config.exec.jmp_pin);
w.set_jmp_pin(config.exec.jmp_pin); w.set_out_en_sel(config.out_en_sel);
w.set_out_en_sel(config.out_en_sel); w.set_inline_out_en(config.inline_out_en);
w.set_inline_out_en(config.inline_out_en); w.set_out_sticky(config.out_sticky);
w.set_out_sticky(config.out_sticky); w.set_wrap_top(config.exec.wrap_top);
w.set_wrap_top(config.exec.wrap_top); w.set_wrap_bottom(config.exec.wrap_bottom);
w.set_wrap_bottom(config.exec.wrap_bottom); w.set_status_sel(match config.status_sel {
w.set_status_sel(match config.status_sel { StatusSource::TxFifoLevel => SmExecctrlStatusSel::TXLEVEL,
StatusSource::TxFifoLevel => SmExecctrlStatusSel::TXLEVEL, StatusSource::RxFifoLevel => SmExecctrlStatusSel::RXLEVEL,
StatusSource::RxFifoLevel => SmExecctrlStatusSel::RXLEVEL,
});
w.set_status_n(config.status_n);
}); });
sm.shiftctrl().write(|w| { w.set_status_n(config.status_n);
w.set_fjoin_rx(config.fifo_join == FifoJoin::RxOnly); });
w.set_fjoin_tx(config.fifo_join == FifoJoin::TxOnly); sm.shiftctrl().write(|w| {
w.set_pull_thresh(config.shift_out.threshold); w.set_fjoin_rx(config.fifo_join == FifoJoin::RxOnly);
w.set_push_thresh(config.shift_in.threshold); w.set_fjoin_tx(config.fifo_join == FifoJoin::TxOnly);
w.set_out_shiftdir(config.shift_out.direction == ShiftDirection::Right); w.set_pull_thresh(config.shift_out.threshold);
w.set_in_shiftdir(config.shift_in.direction == ShiftDirection::Right); w.set_push_thresh(config.shift_in.threshold);
w.set_autopull(config.shift_out.auto_fill); w.set_out_shiftdir(config.shift_out.direction == ShiftDirection::Right);
w.set_autopush(config.shift_in.auto_fill); w.set_in_shiftdir(config.shift_in.direction == ShiftDirection::Right);
}); w.set_autopull(config.shift_out.auto_fill);
sm.pinctrl().write(|w| { w.set_autopush(config.shift_in.auto_fill);
w.set_sideset_count(config.pins.sideset_count); });
w.set_set_count(config.pins.set_count); sm.pinctrl().write(|w| {
w.set_out_count(config.pins.out_count); w.set_sideset_count(config.pins.sideset_count);
w.set_in_base(config.pins.in_base); w.set_set_count(config.pins.set_count);
w.set_sideset_base(config.pins.sideset_base); w.set_out_count(config.pins.out_count);
w.set_set_base(config.pins.set_base); w.set_in_base(config.pins.in_base);
w.set_out_base(config.pins.out_base); w.set_sideset_base(config.pins.sideset_base);
}); w.set_set_base(config.pins.set_base);
if let Some(origin) = config.origin { w.set_out_base(config.pins.out_base);
pio_instr_util::exec_jmp(self, origin); });
} if let Some(origin) = config.origin {
unsafe { pio_instr_util::exec_jmp(self, origin) }
} }
} }
@ -694,45 +654,35 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> {
pub fn restart(&mut self) { pub fn restart(&mut self) {
let mask = 1u8 << SM; let mask = 1u8 << SM;
unsafe { PIO::PIO.ctrl().write_set(|w| w.set_sm_restart(mask));
PIO::PIO.ctrl().write_set(|w| w.set_sm_restart(mask));
}
} }
pub fn set_enable(&mut self, enable: bool) { pub fn set_enable(&mut self, enable: bool) {
let mask = 1u8 << SM; let mask = 1u8 << SM;
unsafe { if enable {
if enable { PIO::PIO.ctrl().write_set(|w| w.set_sm_enable(mask));
PIO::PIO.ctrl().write_set(|w| w.set_sm_enable(mask)); } else {
} else { PIO::PIO.ctrl().write_clear(|w| w.set_sm_enable(mask));
PIO::PIO.ctrl().write_clear(|w| w.set_sm_enable(mask));
}
} }
} }
pub fn is_enabled(&self) -> bool { pub fn is_enabled(&self) -> bool {
unsafe { PIO::PIO.ctrl().read().sm_enable() & (1u8 << SM) != 0 } PIO::PIO.ctrl().read().sm_enable() & (1u8 << SM) != 0
} }
pub fn clkdiv_restart(&mut self) { pub fn clkdiv_restart(&mut self) {
let mask = 1u8 << SM; let mask = 1u8 << SM;
unsafe { PIO::PIO.ctrl().write_set(|w| w.set_clkdiv_restart(mask));
PIO::PIO.ctrl().write_set(|w| w.set_clkdiv_restart(mask));
}
} }
fn with_paused(&mut self, f: impl FnOnce(&mut Self)) { fn with_paused(&mut self, f: impl FnOnce(&mut Self)) {
let enabled = self.is_enabled(); let enabled = self.is_enabled();
self.set_enable(false); self.set_enable(false);
let pincfg = unsafe { Self::this_sm().pinctrl().read() }; let pincfg = Self::this_sm().pinctrl().read();
let execcfg = unsafe { Self::this_sm().execctrl().read() }; let execcfg = Self::this_sm().execctrl().read();
unsafe { Self::this_sm().execctrl().write_clear(|w| w.set_out_sticky(true));
Self::this_sm().execctrl().write_clear(|w| w.set_out_sticky(true));
}
f(self); f(self);
unsafe { Self::this_sm().pinctrl().write_value(pincfg);
Self::this_sm().pinctrl().write_value(pincfg); Self::this_sm().execctrl().write_value(execcfg);
Self::this_sm().execctrl().write_value(execcfg);
}
self.set_enable(enabled); self.set_enable(enabled);
} }
@ -741,14 +691,12 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> {
pub fn set_pin_dirs(&mut self, dir: Direction, pins: &[&Pin<'d, PIO>]) { pub fn set_pin_dirs(&mut self, dir: Direction, pins: &[&Pin<'d, PIO>]) {
self.with_paused(|sm| { self.with_paused(|sm| {
for pin in pins { for pin in pins {
unsafe { Self::this_sm().pinctrl().write(|w| {
Self::this_sm().pinctrl().write(|w| { w.set_set_base(pin.pin());
w.set_set_base(pin.pin()); w.set_set_count(1);
w.set_set_count(1); });
}); // SET PINDIRS, (dir)
// SET PINDIRS, (dir) unsafe { sm.exec_instr(0b111_00000_100_00000 | dir as u16) };
sm.exec_instr(0b111_00000_100_00000 | dir as u16);
}
} }
}); });
} }
@ -758,29 +706,25 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> {
pub fn set_pins(&mut self, level: Level, pins: &[&Pin<'d, PIO>]) { pub fn set_pins(&mut self, level: Level, pins: &[&Pin<'d, PIO>]) {
self.with_paused(|sm| { self.with_paused(|sm| {
for pin in pins { for pin in pins {
unsafe { Self::this_sm().pinctrl().write(|w| {
Self::this_sm().pinctrl().write(|w| { w.set_set_base(pin.pin());
w.set_set_base(pin.pin()); w.set_set_count(1);
w.set_set_count(1); });
}); // SET PINS, (dir)
// SET PINS, (dir) unsafe { sm.exec_instr(0b111_00000_000_00000 | level as u16) };
sm.exec_instr(0b111_00000_000_00000 | level as u16);
}
} }
}); });
} }
pub fn clear_fifos(&mut self) { pub fn clear_fifos(&mut self) {
// Toggle FJOIN_RX to flush FIFOs // Toggle FJOIN_RX to flush FIFOs
unsafe { let shiftctrl = Self::this_sm().shiftctrl();
let shiftctrl = Self::this_sm().shiftctrl(); shiftctrl.modify(|w| {
shiftctrl.modify(|w| { w.set_fjoin_rx(!w.fjoin_rx());
w.set_fjoin_rx(!w.fjoin_rx()); });
}); shiftctrl.modify(|w| {
shiftctrl.modify(|w| { w.set_fjoin_rx(!w.fjoin_rx());
w.set_fjoin_rx(!w.fjoin_rx()); });
});
}
} }
pub unsafe fn exec_instr(&mut self, instr: u16) { pub unsafe fn exec_instr(&mut self, instr: u16) {
@ -854,11 +798,9 @@ impl<'d, PIO: Instance> Common<'d, PIO> {
if (self.instructions_used | used_mask) & mask != 0 { if (self.instructions_used | used_mask) & mask != 0 {
return Err(addr); return Err(addr);
} }
unsafe { PIO::PIO.instr_mem(addr).write(|w| {
PIO::PIO.instr_mem(addr).write(|w| { w.set_instr_mem(instr);
w.set_instr_mem(instr); });
});
}
used_mask |= mask; used_mask |= mask;
} }
self.instructions_used |= used_mask; self.instructions_used |= used_mask;
@ -875,17 +817,15 @@ impl<'d, PIO: Instance> Common<'d, PIO> {
} }
pub fn set_input_sync_bypass<'a>(&'a mut self, bypass: u32, mask: u32) { pub fn set_input_sync_bypass<'a>(&'a mut self, bypass: u32, mask: u32) {
unsafe { // this can interfere with per-pin bypass functions. splitting the
// this can interfere with per-pin bypass functions. splitting the // modification is going to be fine since nothing that relies on
// modification is going to be fine since nothing that relies on // it can reasonably run before we finish.
// it can reasonably run before we finish. PIO::PIO.input_sync_bypass().write_set(|w| *w = mask & bypass);
PIO::PIO.input_sync_bypass().write_set(|w| *w = mask & bypass); PIO::PIO.input_sync_bypass().write_clear(|w| *w = mask & !bypass);
PIO::PIO.input_sync_bypass().write_clear(|w| *w = mask & !bypass);
}
} }
pub fn get_input_sync_bypass(&self) -> u32 { pub fn get_input_sync_bypass(&self) -> u32 {
unsafe { PIO::PIO.input_sync_bypass().read() } PIO::PIO.input_sync_bypass().read()
} }
/// Register a pin for PIO usage. Pins will be released from the PIO block /// Register a pin for PIO usage. Pins will be released from the PIO block
@ -894,9 +834,7 @@ impl<'d, PIO: Instance> Common<'d, PIO> {
/// of [`Pio`] do not keep pin registrations alive.** /// of [`Pio`] do not keep pin registrations alive.**
pub fn make_pio_pin(&mut self, pin: impl Peripheral<P = impl PioPin + 'd> + 'd) -> Pin<'d, PIO> { pub fn make_pio_pin(&mut self, pin: impl Peripheral<P = impl PioPin + 'd> + 'd) -> Pin<'d, PIO> {
into_ref!(pin); into_ref!(pin);
unsafe { pin.io().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL as _));
pin.io().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL.0));
}
// we can be relaxed about this because we're &mut here and nothing is cached // we can be relaxed about this because we're &mut here and nothing is cached
PIO::state().used_pins.fetch_or(1 << pin.pin_bank(), Ordering::Relaxed); PIO::state().used_pins.fetch_or(1 << pin.pin_bank(), Ordering::Relaxed);
Pin { Pin {
@ -914,13 +852,11 @@ impl<'d, PIO: Instance> Common<'d, PIO> {
_pio: PhantomData, _pio: PhantomData,
}; };
f(&mut batch); f(&mut batch);
unsafe { PIO::PIO.ctrl().modify(|w| {
PIO::PIO.ctrl().modify(|w| { w.set_clkdiv_restart(batch.clkdiv_restart);
w.set_clkdiv_restart(batch.clkdiv_restart); w.set_sm_restart(batch.sm_restart);
w.set_sm_restart(batch.sm_restart); w.set_sm_enable((w.sm_enable() & !batch.sm_enable_mask) | batch.sm_enable);
w.set_sm_enable((w.sm_enable() & !batch.sm_enable_mask) | batch.sm_enable); });
});
}
} }
} }
@ -972,11 +908,11 @@ impl<'d, PIO: Instance> IrqFlags<'d, PIO> {
} }
pub fn check_any(&self, irqs: u8) -> bool { pub fn check_any(&self, irqs: u8) -> bool {
unsafe { PIO::PIO.irq().read().irq() & irqs != 0 } PIO::PIO.irq().read().irq() & irqs != 0
} }
pub fn check_all(&self, irqs: u8) -> bool { pub fn check_all(&self, irqs: u8) -> bool {
unsafe { PIO::PIO.irq().read().irq() & irqs == irqs } PIO::PIO.irq().read().irq() & irqs == irqs
} }
pub fn clear(&self, irq_no: usize) { pub fn clear(&self, irq_no: usize) {
@ -985,7 +921,7 @@ impl<'d, PIO: Instance> IrqFlags<'d, PIO> {
} }
pub fn clear_all(&self, irqs: u8) { pub fn clear_all(&self, irqs: u8) {
unsafe { PIO::PIO.irq().write(|w| w.set_irq(irqs)) } PIO::PIO.irq().write(|w| w.set_irq(irqs))
} }
pub fn set(&self, irq_no: usize) { pub fn set(&self, irq_no: usize) {
@ -994,7 +930,7 @@ impl<'d, PIO: Instance> IrqFlags<'d, PIO> {
} }
pub fn set_all(&self, irqs: u8) { pub fn set_all(&self, irqs: u8) {
unsafe { PIO::PIO.irq_force().write(|w| w.set_irq_force(irqs)) } PIO::PIO.irq_force().write(|w| w.set_irq_force(irqs))
} }
} }
@ -1062,13 +998,11 @@ fn on_pio_drop<PIO: Instance>() {
let state = PIO::state(); let state = PIO::state();
if state.users.fetch_sub(1, Ordering::AcqRel) == 1 { if state.users.fetch_sub(1, Ordering::AcqRel) == 1 {
let used_pins = state.used_pins.load(Ordering::Relaxed); let used_pins = state.used_pins.load(Ordering::Relaxed);
let null = Gpio0ctrlFuncsel::NULL.0; let null = Gpio0ctrlFuncsel::NULL as _;
// we only have 30 pins. don't test the other two since gpio() asserts. // we only have 30 pins. don't test the other two since gpio() asserts.
for i in 0..30 { for i in 0..30 {
if used_pins & (1 << i) != 0 { if used_pins & (1 << i) != 0 {
unsafe { pac::IO_BANK0.gpio(i).ctrl().write(|w| w.set_funcsel(null));
pac::IO_BANK0.gpio(i).ctrl().write(|w| w.set_funcsel(null));
}
} }
} }
} }

View File

@ -71,20 +71,18 @@ impl<'d, T: Channel> Pwm<'d, T> {
into_ref!(inner); into_ref!(inner);
let p = inner.regs(); let p = inner.regs();
unsafe { p.csr().modify(|w| {
p.csr().modify(|w| { w.set_divmode(divmode);
w.set_divmode(divmode); w.set_en(false);
w.set_en(false); });
}); p.ctr().write(|w| w.0 = 0);
p.ctr().write(|w| w.0 = 0); Self::configure(p, &config);
Self::configure(p, &config);
if let Some(pin) = &a { if let Some(pin) = &a {
pin.io().ctrl().write(|w| w.set_funcsel(4)); pin.io().ctrl().write(|w| w.set_funcsel(4));
} }
if let Some(pin) = &b { if let Some(pin) = &b {
pin.io().ctrl().write(|w| w.set_funcsel(4)); pin.io().ctrl().write(|w| w.set_funcsel(4));
}
} }
Self { Self {
inner, inner,
@ -161,31 +159,29 @@ impl<'d, T: Channel> Pwm<'d, T> {
panic!("Requested divider is too large"); panic!("Requested divider is too large");
} }
unsafe { p.div().write_value(ChDiv(config.divider.to_bits() as u32));
p.div().write_value(ChDiv(config.divider.to_bits() as u32)); p.cc().write(|w| {
p.cc().write(|w| { w.set_a(config.compare_a);
w.set_a(config.compare_a); w.set_b(config.compare_b);
w.set_b(config.compare_b); });
}); p.top().write(|w| w.set_top(config.top));
p.top().write(|w| w.set_top(config.top)); p.csr().modify(|w| {
p.csr().modify(|w| { w.set_a_inv(config.invert_a);
w.set_a_inv(config.invert_a); w.set_b_inv(config.invert_b);
w.set_b_inv(config.invert_b); w.set_ph_correct(config.phase_correct);
w.set_ph_correct(config.phase_correct); w.set_en(config.enable);
w.set_en(config.enable); });
});
}
} }
#[inline] #[inline]
pub unsafe fn phase_advance(&mut self) { pub fn phase_advance(&mut self) {
let p = self.inner.regs(); let p = self.inner.regs();
p.csr().write_set(|w| w.set_ph_adv(true)); p.csr().write_set(|w| w.set_ph_adv(true));
while p.csr().read().ph_adv() {} while p.csr().read().ph_adv() {}
} }
#[inline] #[inline]
pub unsafe fn phase_retard(&mut self) { pub fn phase_retard(&mut self) {
let p = self.inner.regs(); let p = self.inner.regs();
p.csr().write_set(|w| w.set_ph_ret(true)); p.csr().write_set(|w| w.set_ph_ret(true));
while p.csr().read().ph_ret() {} while p.csr().read().ph_ret() {}
@ -193,12 +189,12 @@ impl<'d, T: Channel> Pwm<'d, T> {
#[inline] #[inline]
pub fn counter(&self) -> u16 { pub fn counter(&self) -> u16 {
unsafe { self.inner.regs().ctr().read().ctr() } self.inner.regs().ctr().read().ctr()
} }
#[inline] #[inline]
pub fn set_counter(&self, ctr: u16) { pub fn set_counter(&self, ctr: u16) {
unsafe { self.inner.regs().ctr().write(|w| w.set_ctr(ctr)) } self.inner.regs().ctr().write(|w| w.set_ctr(ctr))
} }
#[inline] #[inline]
@ -209,14 +205,12 @@ impl<'d, T: Channel> Pwm<'d, T> {
#[inline] #[inline]
pub fn wrapped(&mut self) -> bool { pub fn wrapped(&mut self) -> bool {
unsafe { pac::PWM.intr().read().0 & self.bit() != 0 } pac::PWM.intr().read().0 & self.bit() != 0
} }
#[inline] #[inline]
pub fn clear_wrapped(&mut self) { pub fn clear_wrapped(&mut self) {
unsafe { pac::PWM.intr().write_value(Intr(self.bit() as _));
pac::PWM.intr().write_value(Intr(self.bit() as _));
}
} }
#[inline] #[inline]
@ -237,26 +231,22 @@ impl PwmBatch {
pub fn set_enabled(enabled: bool, batch: impl FnOnce(&mut PwmBatch)) { pub fn set_enabled(enabled: bool, batch: impl FnOnce(&mut PwmBatch)) {
let mut en = PwmBatch(0); let mut en = PwmBatch(0);
batch(&mut en); batch(&mut en);
unsafe { if enabled {
if enabled { pac::PWM.en().write_set(|w| w.0 = en.0);
pac::PWM.en().write_set(|w| w.0 = en.0); } else {
} else { pac::PWM.en().write_clear(|w| w.0 = en.0);
pac::PWM.en().write_clear(|w| w.0 = en.0);
}
} }
} }
} }
impl<'d, T: Channel> Drop for Pwm<'d, T> { impl<'d, T: Channel> Drop for Pwm<'d, T> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { self.inner.regs().csr().write_clear(|w| w.set_en(false));
self.inner.regs().csr().write_clear(|w| w.set_en(false)); if let Some(pin) = &self.pin_a {
if let Some(pin) = &self.pin_a { pin.io().ctrl().write(|w| w.set_funcsel(31));
pin.io().ctrl().write(|w| w.set_funcsel(31)); }
} if let Some(pin) = &self.pin_b {
if let Some(pin) = &self.pin_b { pin.io().ctrl().write(|w| w.set_funcsel(31));
pin.io().ctrl().write(|w| w.set_funcsel(31));
}
} }
} }
} }

View File

@ -4,11 +4,11 @@ use crate::pac;
pub const ALL_PERIPHERALS: Peripherals = Peripherals(0x01ffffff); pub const ALL_PERIPHERALS: Peripherals = Peripherals(0x01ffffff);
pub unsafe fn reset(peris: Peripherals) { pub(crate) fn reset(peris: Peripherals) {
pac::RESETS.reset().write_value(peris); pac::RESETS.reset().write_value(peris);
} }
pub unsafe fn unreset_wait(peris: Peripherals) { pub(crate) fn unreset_wait(peris: Peripherals) {
// TODO use the "atomic clear" register version // TODO use the "atomic clear" register version
pac::RESETS.reset().modify(|v| *v = Peripherals(v.0 & !peris.0)); pac::RESETS.reset().modify(|v| *v = Peripherals(v.0 & !peris.0));
while ((!pac::RESETS.reset_done().read().0) & peris.0) != 0 {} while ((!pac::RESETS.reset_done().read().0) & peris.0) != 0 {}

View File

@ -26,7 +26,7 @@ impl<'d, T: Instance> RealTimeClock<'d, T> {
into_ref!(inner); into_ref!(inner);
// Set the RTC divider // Set the RTC divider
unsafe { inner.regs().clkdiv_m1().write(|w| w.set_clkdiv_m1(clk_rtc_freq() - 1)) }; inner.regs().clkdiv_m1().write(|w| w.set_clkdiv_m1(clk_rtc_freq() - 1));
let mut result = Self { inner }; let mut result = Self { inner };
result.set_leap_year_check(true); // should be on by default, make sure this is the case. result.set_leap_year_check(true); // should be on by default, make sure this is the case.
@ -38,17 +38,14 @@ impl<'d, T: Instance> RealTimeClock<'d, T> {
/// ///
/// Leap year checking is enabled by default. /// Leap year checking is enabled by default.
pub fn set_leap_year_check(&mut self, leap_year_check_enabled: bool) { pub fn set_leap_year_check(&mut self, leap_year_check_enabled: bool) {
unsafe { self.inner.regs().ctrl().modify(|w| {
self.inner w.set_force_notleapyear(!leap_year_check_enabled);
.regs() });
.ctrl()
.modify(|w| w.set_force_notleapyear(!leap_year_check_enabled))
};
} }
/// Checks to see if this RealTimeClock is running /// Checks to see if this RealTimeClock is running
pub fn is_running(&self) -> bool { pub fn is_running(&self) -> bool {
unsafe { self.inner.regs().ctrl().read().rtc_active() } self.inner.regs().ctrl().read().rtc_active()
} }
/// Set the datetime to a new value. /// Set the datetime to a new value.
@ -60,25 +57,23 @@ impl<'d, T: Instance> RealTimeClock<'d, T> {
self::datetime::validate_datetime(&t).map_err(RtcError::InvalidDateTime)?; self::datetime::validate_datetime(&t).map_err(RtcError::InvalidDateTime)?;
// disable RTC while we configure it // disable RTC while we configure it
unsafe { self.inner.regs().ctrl().modify(|w| w.set_rtc_enable(false));
self.inner.regs().ctrl().modify(|w| w.set_rtc_enable(false)); while self.inner.regs().ctrl().read().rtc_active() {
while self.inner.regs().ctrl().read().rtc_active() { core::hint::spin_loop();
core::hint::spin_loop(); }
}
self.inner.regs().setup_0().write(|w| { self.inner.regs().setup_0().write(|w| {
self::datetime::write_setup_0(&t, w); self::datetime::write_setup_0(&t, w);
}); });
self.inner.regs().setup_1().write(|w| { self.inner.regs().setup_1().write(|w| {
self::datetime::write_setup_1(&t, w); self::datetime::write_setup_1(&t, w);
}); });
// Load the new datetime and re-enable RTC // Load the new datetime and re-enable RTC
self.inner.regs().ctrl().write(|w| w.set_load(true)); self.inner.regs().ctrl().write(|w| w.set_load(true));
self.inner.regs().ctrl().write(|w| w.set_rtc_enable(true)); self.inner.regs().ctrl().write(|w| w.set_rtc_enable(true));
while !self.inner.regs().ctrl().read().rtc_active() { while !self.inner.regs().ctrl().read().rtc_active() {
core::hint::spin_loop(); core::hint::spin_loop();
}
} }
Ok(()) Ok(())
} }
@ -93,8 +88,8 @@ impl<'d, T: Instance> RealTimeClock<'d, T> {
return Err(RtcError::NotRunning); return Err(RtcError::NotRunning);
} }
let rtc_0 = unsafe { self.inner.regs().rtc_0().read() }; let rtc_0 = self.inner.regs().rtc_0().read();
let rtc_1 = unsafe { self.inner.regs().rtc_1().read() }; let rtc_1 = self.inner.regs().rtc_1().read();
self::datetime::datetime_from_registers(rtc_0, rtc_1).map_err(RtcError::InvalidDateTime) self::datetime::datetime_from_registers(rtc_0, rtc_1).map_err(RtcError::InvalidDateTime)
} }
@ -103,12 +98,10 @@ impl<'d, T: Instance> RealTimeClock<'d, T> {
/// ///
/// [`schedule_alarm`]: #method.schedule_alarm /// [`schedule_alarm`]: #method.schedule_alarm
pub fn disable_alarm(&mut self) { pub fn disable_alarm(&mut self) {
unsafe { self.inner.regs().irq_setup_0().modify(|s| s.set_match_ena(false));
self.inner.regs().irq_setup_0().modify(|s| s.set_match_ena(false));
while self.inner.regs().irq_setup_0().read().match_active() { while self.inner.regs().irq_setup_0().read().match_active() {
core::hint::spin_loop(); core::hint::spin_loop();
}
} }
} }
@ -132,21 +125,19 @@ impl<'d, T: Instance> RealTimeClock<'d, T> {
pub fn schedule_alarm(&mut self, filter: DateTimeFilter) { pub fn schedule_alarm(&mut self, filter: DateTimeFilter) {
self.disable_alarm(); self.disable_alarm();
unsafe { self.inner.regs().irq_setup_0().write(|w| {
self.inner.regs().irq_setup_0().write(|w| { filter.write_setup_0(w);
filter.write_setup_0(w); });
}); self.inner.regs().irq_setup_1().write(|w| {
self.inner.regs().irq_setup_1().write(|w| { filter.write_setup_1(w);
filter.write_setup_1(w); });
});
self.inner.regs().inte().modify(|w| w.set_rtc(true)); self.inner.regs().inte().modify(|w| w.set_rtc(true));
// Set the enable bit and check if it is set // Set the enable bit and check if it is set
self.inner.regs().irq_setup_0().modify(|w| w.set_match_ena(true)); self.inner.regs().irq_setup_0().modify(|w| w.set_match_ena(true));
while !self.inner.regs().irq_setup_0().read().match_active() { while !self.inner.regs().irq_setup_0().read().match_active() {
core::hint::spin_loop(); core::hint::spin_loop();
}
} }
} }

View File

@ -79,39 +79,37 @@ impl<'d, T: Instance, M: Mode> Spi<'d, T, M> {
) -> Self { ) -> Self {
into_ref!(inner); into_ref!(inner);
unsafe { let p = inner.regs();
let p = inner.regs(); let (presc, postdiv) = calc_prescs(config.frequency);
let (presc, postdiv) = calc_prescs(config.frequency);
p.cpsr().write(|w| w.set_cpsdvsr(presc)); p.cpsr().write(|w| w.set_cpsdvsr(presc));
p.cr0().write(|w| { p.cr0().write(|w| {
w.set_dss(0b0111); // 8bit w.set_dss(0b0111); // 8bit
w.set_spo(config.polarity == Polarity::IdleHigh); w.set_spo(config.polarity == Polarity::IdleHigh);
w.set_sph(config.phase == Phase::CaptureOnSecondTransition); w.set_sph(config.phase == Phase::CaptureOnSecondTransition);
w.set_scr(postdiv); w.set_scr(postdiv);
}); });
// Always enable DREQ signals -- harmless if DMA is not listening // Always enable DREQ signals -- harmless if DMA is not listening
p.dmacr().write(|reg| { p.dmacr().write(|reg| {
reg.set_rxdmae(true); reg.set_rxdmae(true);
reg.set_txdmae(true); reg.set_txdmae(true);
}); });
// finally, enable. // finally, enable.
p.cr1().write(|w| w.set_sse(true)); p.cr1().write(|w| w.set_sse(true));
if let Some(pin) = &clk { if let Some(pin) = &clk {
pin.io().ctrl().write(|w| w.set_funcsel(1)); pin.io().ctrl().write(|w| w.set_funcsel(1));
} }
if let Some(pin) = &mosi { if let Some(pin) = &mosi {
pin.io().ctrl().write(|w| w.set_funcsel(1)); pin.io().ctrl().write(|w| w.set_funcsel(1));
} }
if let Some(pin) = &miso { if let Some(pin) = &miso {
pin.io().ctrl().write(|w| w.set_funcsel(1)); pin.io().ctrl().write(|w| w.set_funcsel(1));
} }
if let Some(pin) = &cs { if let Some(pin) = &cs {
pin.io().ctrl().write(|w| w.set_funcsel(1)); pin.io().ctrl().write(|w| w.set_funcsel(1));
}
} }
Self { Self {
inner, inner,
@ -122,60 +120,52 @@ impl<'d, T: Instance, M: Mode> Spi<'d, T, M> {
} }
pub fn blocking_write(&mut self, data: &[u8]) -> Result<(), Error> { pub fn blocking_write(&mut self, data: &[u8]) -> Result<(), Error> {
unsafe { let p = self.inner.regs();
let p = self.inner.regs(); for &b in data {
for &b in data { while !p.sr().read().tnf() {}
while !p.sr().read().tnf() {} p.dr().write(|w| w.set_data(b as _));
p.dr().write(|w| w.set_data(b as _)); while !p.sr().read().rne() {}
while !p.sr().read().rne() {} let _ = p.dr().read();
let _ = p.dr().read();
}
} }
self.flush()?; self.flush()?;
Ok(()) Ok(())
} }
pub fn blocking_transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Error> { pub fn blocking_transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Error> {
unsafe { let p = self.inner.regs();
let p = self.inner.regs(); for b in data {
for b in data { while !p.sr().read().tnf() {}
while !p.sr().read().tnf() {} p.dr().write(|w| w.set_data(*b as _));
p.dr().write(|w| w.set_data(*b as _)); while !p.sr().read().rne() {}
while !p.sr().read().rne() {} *b = p.dr().read().data() as u8;
*b = p.dr().read().data() as u8;
}
} }
self.flush()?; self.flush()?;
Ok(()) Ok(())
} }
pub fn blocking_read(&mut self, data: &mut [u8]) -> Result<(), Error> { pub fn blocking_read(&mut self, data: &mut [u8]) -> Result<(), Error> {
unsafe { let p = self.inner.regs();
let p = self.inner.regs(); for b in data {
for b in data { while !p.sr().read().tnf() {}
while !p.sr().read().tnf() {} p.dr().write(|w| w.set_data(0));
p.dr().write(|w| w.set_data(0)); while !p.sr().read().rne() {}
while !p.sr().read().rne() {} *b = p.dr().read().data() as u8;
*b = p.dr().read().data() as u8;
}
} }
self.flush()?; self.flush()?;
Ok(()) Ok(())
} }
pub fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> { pub fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {
unsafe { let p = self.inner.regs();
let p = self.inner.regs(); let len = read.len().max(write.len());
let len = read.len().max(write.len()); for i in 0..len {
for i in 0..len { let wb = write.get(i).copied().unwrap_or(0);
let wb = write.get(i).copied().unwrap_or(0); while !p.sr().read().tnf() {}
while !p.sr().read().tnf() {} p.dr().write(|w| w.set_data(wb as _));
p.dr().write(|w| w.set_data(wb as _)); while !p.sr().read().rne() {}
while !p.sr().read().rne() {} let rb = p.dr().read().data() as u8;
let rb = p.dr().read().data() as u8; if let Some(r) = read.get_mut(i) {
if let Some(r) = read.get_mut(i) { *r = rb;
*r = rb;
}
} }
} }
self.flush()?; self.flush()?;
@ -183,29 +173,25 @@ impl<'d, T: Instance, M: Mode> Spi<'d, T, M> {
} }
pub fn flush(&mut self) -> Result<(), Error> { pub fn flush(&mut self) -> Result<(), Error> {
unsafe { let p = self.inner.regs();
let p = self.inner.regs(); while p.sr().read().bsy() {}
while p.sr().read().bsy() {}
}
Ok(()) Ok(())
} }
pub fn set_frequency(&mut self, freq: u32) { pub fn set_frequency(&mut self, freq: u32) {
let (presc, postdiv) = calc_prescs(freq); let (presc, postdiv) = calc_prescs(freq);
let p = self.inner.regs(); let p = self.inner.regs();
unsafe { // disable
// disable p.cr1().write(|w| w.set_sse(false));
p.cr1().write(|w| w.set_sse(false));
// change stuff // change stuff
p.cpsr().write(|w| w.set_cpsdvsr(presc)); p.cpsr().write(|w| w.set_cpsdvsr(presc));
p.cr0().modify(|w| { p.cr0().modify(|w| {
w.set_scr(postdiv); w.set_scr(postdiv);
}); });
// enable // enable
p.cr1().write(|w| w.set_sse(true)); p.cr1().write(|w| w.set_sse(true));
}
} }
} }
@ -337,21 +323,19 @@ impl<'d, T: Instance> Spi<'d, T, Async> {
let tx_transfer = unsafe { let tx_transfer = unsafe {
// If we don't assign future to a variable, the data register pointer // If we don't assign future to a variable, the data register pointer
// is held across an await and makes the future non-Send. // is held across an await and makes the future non-Send.
crate::dma::write(tx_ch, buffer, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ) crate::dma::write(tx_ch, buffer, self.inner.regs().dr().as_ptr() as *mut _, T::TX_DREQ)
}; };
tx_transfer.await; tx_transfer.await;
let p = self.inner.regs(); let p = self.inner.regs();
unsafe { while p.sr().read().bsy() {}
while p.sr().read().bsy() {}
// clear RX FIFO contents to prevent stale reads // clear RX FIFO contents to prevent stale reads
while p.sr().read().rne() { while p.sr().read().rne() {
let _: u16 = p.dr().read().data(); let _: u16 = p.dr().read().data();
}
// clear RX overrun interrupt
p.icr().write(|w| w.set_roric(true));
} }
// clear RX overrun interrupt
p.icr().write(|w| w.set_roric(true));
Ok(()) Ok(())
} }
@ -363,14 +347,19 @@ impl<'d, T: Instance> Spi<'d, T, Async> {
let rx_transfer = unsafe { let rx_transfer = unsafe {
// If we don't assign future to a variable, the data register pointer // If we don't assign future to a variable, the data register pointer
// is held across an await and makes the future non-Send. // is held across an await and makes the future non-Send.
crate::dma::read(rx_ch, self.inner.regs().dr().ptr() as *const _, buffer, T::RX_DREQ) crate::dma::read(rx_ch, self.inner.regs().dr().as_ptr() as *const _, buffer, T::RX_DREQ)
}; };
let tx_ch = self.tx_dma.as_mut().unwrap(); let tx_ch = self.tx_dma.as_mut().unwrap();
let tx_transfer = unsafe { let tx_transfer = unsafe {
// If we don't assign future to a variable, the data register pointer // If we don't assign future to a variable, the data register pointer
// is held across an await and makes the future non-Send. // is held across an await and makes the future non-Send.
crate::dma::write_repeated(tx_ch, self.inner.regs().dr().ptr() as *mut u8, buffer.len(), T::TX_DREQ) crate::dma::write_repeated(
tx_ch,
self.inner.regs().dr().as_ptr() as *mut u8,
buffer.len(),
T::TX_DREQ,
)
}; };
join(tx_transfer, rx_transfer).await; join(tx_transfer, rx_transfer).await;
Ok(()) Ok(())
@ -394,7 +383,7 @@ impl<'d, T: Instance> Spi<'d, T, Async> {
let rx_transfer = unsafe { let rx_transfer = unsafe {
// If we don't assign future to a variable, the data register pointer // If we don't assign future to a variable, the data register pointer
// is held across an await and makes the future non-Send. // is held across an await and makes the future non-Send.
crate::dma::read(rx_ch, self.inner.regs().dr().ptr() as *const _, rx_ptr, T::RX_DREQ) crate::dma::read(rx_ch, self.inner.regs().dr().as_ptr() as *const _, rx_ptr, T::RX_DREQ)
}; };
let mut tx_ch = self.tx_dma.as_mut().unwrap(); let mut tx_ch = self.tx_dma.as_mut().unwrap();
@ -403,13 +392,13 @@ impl<'d, T: Instance> Spi<'d, T, Async> {
let tx_transfer = async { let tx_transfer = async {
let p = self.inner.regs(); let p = self.inner.regs();
unsafe { unsafe {
crate::dma::write(&mut tx_ch, tx_ptr, p.dr().ptr() as *mut _, T::TX_DREQ).await; crate::dma::write(&mut tx_ch, tx_ptr, p.dr().as_ptr() as *mut _, T::TX_DREQ).await;
if rx_len > tx_len { if rx_len > tx_len {
let write_bytes_len = rx_len - tx_len; let write_bytes_len = rx_len - tx_len;
// write dummy data // write dummy data
// this will disable incrementation of the buffers // this will disable incrementation of the buffers
crate::dma::write_repeated(tx_ch, p.dr().ptr() as *mut u8, write_bytes_len, T::TX_DREQ).await crate::dma::write_repeated(tx_ch, p.dr().as_ptr() as *mut u8, write_bytes_len, T::TX_DREQ).await
} }
} }
}; };
@ -418,16 +407,14 @@ impl<'d, T: Instance> Spi<'d, T, Async> {
// if tx > rx we should clear any overflow of the FIFO SPI buffer // if tx > rx we should clear any overflow of the FIFO SPI buffer
if tx_len > rx_len { if tx_len > rx_len {
let p = self.inner.regs(); let p = self.inner.regs();
unsafe { while p.sr().read().bsy() {}
while p.sr().read().bsy() {}
// clear RX FIFO contents to prevent stale reads // clear RX FIFO contents to prevent stale reads
while p.sr().read().rne() { while p.sr().read().rne() {
let _: u16 = p.dr().read().data(); let _: u16 = p.dr().read().data();
}
// clear RX overrun interrupt
p.icr().write(|w| w.set_roric(true));
} }
// clear RX overrun interrupt
p.icr().write(|w| w.set_roric(true));
} }
Ok(()) Ok(())
@ -625,14 +612,12 @@ impl<'d, T: Instance, M: Mode> SetConfig for Spi<'d, T, M> {
fn set_config(&mut self, config: &Self::Config) { fn set_config(&mut self, config: &Self::Config) {
let p = self.inner.regs(); let p = self.inner.regs();
let (presc, postdiv) = calc_prescs(config.frequency); let (presc, postdiv) = calc_prescs(config.frequency);
unsafe { p.cpsr().write(|w| w.set_cpsdvsr(presc));
p.cpsr().write(|w| w.set_cpsdvsr(presc)); p.cr0().write(|w| {
p.cr0().write(|w| { w.set_dss(0b0111); // 8bit
w.set_dss(0b0111); // 8bit w.set_spo(config.polarity == Polarity::IdleHigh);
w.set_spo(config.polarity == Polarity::IdleHigh); w.set_sph(config.phase == Phase::CaptureOnSecondTransition);
w.set_sph(config.phase == Phase::CaptureOnSecondTransition); w.set_scr(postdiv);
w.set_scr(postdiv); });
});
}
} }
} }

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