Compare commits
267 Commits
embassy-fu
...
embassy-ti
Author | SHA1 | Date | |
---|---|---|---|
61560e740d | |||
d2246ae693 | |||
7f499f3edc | |||
01e23bf9dd | |||
e5097a8866 | |||
f9da6271ce | |||
4976cbbe60 | |||
9b86de770b | |||
9cac649fcf | |||
ff76fde299 | |||
560eecdb73 | |||
ac6995f9e6 | |||
eeb072d9cb | |||
1b249ca72d | |||
66611a80ca | |||
d1eee52625 | |||
71cc6833e1 | |||
0c9ec8dc36 | |||
ea868920e6 | |||
7a6732adcf | |||
52c03cf0a4 | |||
ac61e0ee9f | |||
33f75419e5 | |||
6062978d58 | |||
ca8afacfd0 | |||
9ad7e85288 | |||
9b209ffe1c | |||
5f02bee388 | |||
1bed02296c | |||
516f4ce946 | |||
545cc9326b | |||
d99841fea9 | |||
e3cf4255c6 | |||
4ce4131f8b | |||
f78c706b89 | |||
4d5550070f | |||
53608a87ac | |||
ba6e452cc5 | |||
c2404ee8ca | |||
ce1cba761c | |||
495ca6108c | |||
73d06dd67b | |||
866a42f3ae | |||
f45d34ce7c | |||
bf0ad38640 | |||
de103a5f4f | |||
8c42b26fc6 | |||
fa495b8e88 | |||
d9c773f475 | |||
a669f4cfd8 | |||
9d2641f2f5 | |||
6c5d81ada5 | |||
18453ee64c | |||
02a3cdb507 | |||
e4c2b2aa9a | |||
a4afab4640 | |||
b7d0944265 | |||
1559374a19 | |||
5846b4ff7d | |||
83fcc360fe | |||
71a56292d6 | |||
4da6320e63 | |||
327d3cf0df | |||
79ba20d315 | |||
9d5b524bb0 | |||
ef533e6df4 | |||
e1faf88607 | |||
3d0ba58b2d | |||
f554962f54 | |||
aa8ba2115c | |||
322cfafed3 | |||
df7174ecb0 | |||
f8fd6ab208 | |||
6718ca3a94 | |||
9dca368c3d | |||
d49d1b6b1c | |||
1b9479197d | |||
530182d668 | |||
94606833aa | |||
59765590e0 | |||
4fd831e4a8 | |||
cae8499179 | |||
f075e62444 | |||
e8bb8faa23 | |||
63f5602111 | |||
753781a263 | |||
73208d5248 | |||
09afece93d | |||
1ee4bb22de | |||
5e2c52ee5b | |||
72b645b0c9 | |||
8d38eacae4 | |||
90d392205f | |||
c96581879c | |||
d5abd32da2 | |||
9f77dbf5ae | |||
aabc02506b | |||
99284b8304 | |||
a283c47557 | |||
a7fdeac560 | |||
88a3c360e8 | |||
1d6f5493e7 | |||
dab1762709 | |||
ebf5a92ab2 | |||
874384826d | |||
f4ebc36b63 | |||
38faae26e5 | |||
a77e2c3512 | |||
8b9f4ad259 | |||
72c2e985bb | |||
f9c62d4f1d | |||
dc90006982 | |||
bb84d7a0ae | |||
526e90d3f3 | |||
77ece3f903 | |||
d7f7614b22 | |||
823bd714fb | |||
a89a0c2f12 | |||
9bb43ffe9a | |||
bf1da0497c | |||
44c46e3c93 | |||
b0d91e9f31 | |||
53c34ccc39 | |||
be68d8ebb7 | |||
603513e76e | |||
bcd3ab4ba1 | |||
820e6462b6 | |||
5c882cf4fa | |||
17d8d11f73 | |||
82d4360756 | |||
e129a97d48 | |||
93354b812c | |||
65907204d6 | |||
cd539ba3a0 | |||
86fd480672 | |||
de0070948c | |||
7bbb4c22a1 | |||
5bf6564e95 | |||
c863acd24f | |||
f76444bdc4 | |||
b3dfd06dd6 | |||
1db9e464ff | |||
d6af0f6286 | |||
f2239d34cc | |||
ee76831f93 | |||
75e93cc142 | |||
049c31613b | |||
1e95c4fcff | |||
daf2744716 | |||
49070c75b6 | |||
f27a47a37b | |||
f4f5824972 | |||
7f7c14b7bc | |||
dc376a2390 | |||
fa7781c48d | |||
a9efbf18c6 | |||
3c06a18b94 | |||
6fa74b0c02 | |||
b2a327a858 | |||
7f16b1cd23 | |||
b743d9f48c | |||
3c24ad2db6 | |||
a226e86503 | |||
c5ce02b30e | |||
8536666148 | |||
ca92302d03 | |||
a45fb2d718 | |||
eeb1515e9f | |||
b4f2c2a05e | |||
2e7916c5fe | |||
dacbc9acd5 | |||
10d1ad2343 | |||
a0487380da | |||
c1e25067da | |||
897b72c872 | |||
5914d80968 | |||
5f7e0eb2ae | |||
15b4f9db90 | |||
0f55f5a73d | |||
3d708a459c | |||
3b58ac1bf8 | |||
1d3e41f970 | |||
44d7a84e47 | |||
b418c0e4d6 | |||
11da25800b | |||
d0fe654c82 | |||
6663390224 | |||
ac13675f3a | |||
0c6933fefb | |||
295cc997ae | |||
ab1a6889a6 | |||
336ebe54c0 | |||
1c657d2d55 | |||
c495c765df | |||
feead3ae89 | |||
f7267d493f | |||
ec10460547 | |||
79654510b7 | |||
70a3b85acc | |||
9794bc59cc | |||
c4d5c047d7 | |||
809a4a127b | |||
ea5f2c71e0 | |||
b2d0f8d590 | |||
31d85da78a | |||
9611e7c9f2 | |||
573c433f64 | |||
34ed3441ce | |||
5886679006 | |||
7004b095c3 | |||
22c32b5d5c | |||
107bb0946a | |||
f66f20b1ce | |||
6264fe39a5 | |||
7d5c1fcebf | |||
6cdff72d6d | |||
96eb669b34 | |||
506e5a4493 | |||
d6e8a11ea7 | |||
50af13d470 | |||
3aa0c13ba5 | |||
8b464d2668 | |||
3ca7314476 | |||
5327b9c289 | |||
835b69456d | |||
efe456ab14 | |||
9ff5c50774 | |||
71c130488b | |||
e2181cb439 | |||
b934f3f12e | |||
3fce6ec649 | |||
27905f1be1 | |||
7954cbc4e7 | |||
99dd2a9386 | |||
6d347af9fa | |||
c8ecc55710 | |||
44150c4830 | |||
07c64d902e | |||
e7d4bf258a | |||
838f3065ea | |||
1fb6bfbec9 | |||
8ba421f324 | |||
38900a7fb0 | |||
fe08bdf0d8 | |||
30641d0564 | |||
464ae67108 | |||
171077bacf | |||
b2720117c4 | |||
4781feafc4 | |||
dcd8c62169 | |||
c0b7fd910e | |||
47069dfbe1 | |||
c30b38586a | |||
2636a8dc2e | |||
6dab322c58 | |||
5d114479ff | |||
1f36da5ca6 | |||
af845b7d44 | |||
308ca4b8e3 | |||
60ca5e8479 | |||
84240d49ea | |||
f31116cafa | |||
69d80c086d | |||
6ee29ff0bd | |||
8e8106ef55 | |||
61c666212f | |||
9a873d1dbf |
86
.github/workflows/doc.yml
vendored
Normal file
86
.github/workflows/doc.yml
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
name: Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
|
||||
env:
|
||||
BUILDER_THREADS: '1'
|
||||
|
||||
jobs:
|
||||
doc:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Since stm32 crates take SO LONG to build, we split them
|
||||
# into a separate job. This way it doesn't slow down updating
|
||||
# the rest.
|
||||
strategy:
|
||||
matrix:
|
||||
crates:
|
||||
- stm32
|
||||
- rest
|
||||
|
||||
# This will ensure at most one doc build job is running at a time
|
||||
# (for stm32 and non-stm32 independently).
|
||||
# If another job is already running, the new job will wait.
|
||||
# If another job is already waiting, it'll be canceled.
|
||||
# This means some commits will be skipped, but that's fine because
|
||||
# we only care that the latest gets built.
|
||||
concurrency: doc-${{ matrix.crates }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install Rust targets
|
||||
run: |
|
||||
rustup target add x86_64-unknown-linux-gnu
|
||||
rustup target add wasm32-unknown-unknown
|
||||
rustup target add thumbv6m-none-eabi
|
||||
rustup target add thumbv7m-none-eabi
|
||||
rustup target add thumbv7em-none-eabi
|
||||
rustup target add thumbv7em-none-eabihf
|
||||
rustup target add thumbv8m.base-none-eabi
|
||||
rustup target add thumbv8m.main-none-eabi
|
||||
rustup target add thumbv8m.main-none-eabihf
|
||||
|
||||
- name: Install docserver
|
||||
run: |
|
||||
wget -q -O /usr/local/bin/builder "https://github.com/embassy-rs/docserver/releases/download/v0.3/builder"
|
||||
chmod +x /usr/local/bin/builder
|
||||
|
||||
- name: build-stm32
|
||||
if: ${{ matrix.crates=='stm32' }}
|
||||
run: |
|
||||
mkdir crates
|
||||
builder ./embassy-stm32 crates/embassy-stm32/git.zup
|
||||
builder ./stm32-metapac crates/stm32-metapac/git.zup
|
||||
|
||||
- name: build-rest
|
||||
if: ${{ matrix.crates=='rest' }}
|
||||
run: |
|
||||
mkdir crates
|
||||
builder ./embassy-boot/boot crates/embassy-boot/git.zup
|
||||
builder ./embassy-boot/nrf crates/embassy-boot-nrf/git.zup
|
||||
builder ./embassy-boot/stm32 crates/embassy-boot-stm32/git.zup
|
||||
builder ./embassy-cortex-m crates/embassy-cortex-m/git.zup
|
||||
builder ./embassy-embedded-hal crates/embassy-embedded-hal/git.zup
|
||||
builder ./embassy-executor crates/embassy-executor/git.zup
|
||||
builder ./embassy-futures crates/embassy-futures/git.zup
|
||||
builder ./embassy-lora crates/embassy-lora/git.zup
|
||||
builder ./embassy-net crates/embassy-net/git.zup
|
||||
builder ./embassy-nrf crates/embassy-nrf/git.zup
|
||||
builder ./embassy-rp crates/embassy-rp/git.zup
|
||||
builder ./embassy-sync crates/embassy-sync/git.zup
|
||||
builder ./embassy-time crates/embassy-time/git.zup
|
||||
builder ./embassy-usb crates/embassy-usb/git.zup
|
||||
builder ./embassy-usb-driver crates/embassy-usb-driver/git.zup
|
||||
|
||||
- name: upload
|
||||
run: |
|
||||
mkdir -p ~/.kube
|
||||
echo "${{secrets.KUBECONFIG}}" > ~/.kube/config
|
||||
POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name})
|
||||
kubectl cp crates $POD:/data
|
||||
|
||||
|
2
.github/workflows/rust.yml
vendored
2
.github/workflows/rust.yml
vendored
@ -11,7 +11,7 @@ env:
|
||||
|
||||
jobs:
|
||||
all:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-nightly, build-stable, test]
|
||||
steps:
|
||||
- name: Done
|
||||
|
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@ -20,10 +20,13 @@
|
||||
//"embassy-executor/Cargo.toml",
|
||||
//"embassy-sync/Cargo.toml",
|
||||
"examples/nrf/Cargo.toml",
|
||||
// "examples/nrf-rtos-trace/Cargo.toml",
|
||||
// "examples/rp/Cargo.toml",
|
||||
// "examples/std/Cargo.toml",
|
||||
// "examples/stm32f0/Cargo.toml",
|
||||
// "examples/stm32f1/Cargo.toml",
|
||||
// "examples/stm32f2/Cargo.toml",
|
||||
// "examples/stm32f3/Cargo.toml",
|
||||
// "examples/stm32f4/Cargo.toml",
|
||||
// "examples/stm32f7/Cargo.toml",
|
||||
// "examples/stm32g0/Cargo.toml",
|
||||
@ -32,8 +35,11 @@
|
||||
// "examples/stm32l0/Cargo.toml",
|
||||
// "examples/stm32l1/Cargo.toml",
|
||||
// "examples/stm32l4/Cargo.toml",
|
||||
// "examples/stm32l5/Cargo.toml",
|
||||
// "examples/stm32u5/Cargo.toml",
|
||||
// "examples/stm32wb/Cargo.toml",
|
||||
// "examples/stm32wb55/Cargo.toml",
|
||||
// "examples/stm32wl/Cargo.toml",
|
||||
// "examples/stm32wl55/Cargo.toml",
|
||||
// "examples/wasm/Cargo.toml",
|
||||
],
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
Embassy is the next-generation framework for embedded applications. Write safe, correct and energy-efficient embedded code faster, using the Rust programming language, its async facilities, and the Embassy libraries.
|
||||
|
||||
## <a href="https://embassy.dev/embassy/dev/index.html">Documentation</a> - <a href="https://docs.embassy.dev/">API reference</a> - <a href="https://embassy.dev/">Website</a> - <a href="https://matrix.to/#/#embassy-rs:matrix.org">Chat</a>
|
||||
## <a href="https://embassy.dev/dev/index.html">Documentation</a> - <a href="https://docs.embassy.dev/">API reference</a> - <a href="https://embassy.dev/">Website</a> - <a href="https://matrix.to/#/#embassy-rs:matrix.org">Chat</a>
|
||||
## Rust + async ❤️ embedded
|
||||
|
||||
The Rust programming language is blazingly fast and memory-efficient, with no runtime, garbage collector or OS. It catches a wide variety of bugs at compile time, thanks to its full memory- and thread-safety, and expressive type system.
|
||||
@ -31,7 +31,7 @@ The <a href="https://docs.embassy.dev/embassy-net/">embassy-net</a> network stac
|
||||
The <a href="https://github.com/embassy-rs/nrf-softdevice">nrf-softdevice</a> crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers.
|
||||
|
||||
- **LoRa** -
|
||||
<a href="https://docs.embassy.dev/embassy-lora/">embassy-lora</a> supports LoRa networking on STM32WL wireless microcontrollers and Semtech SX127x transceivers.
|
||||
<a href="https://docs.embassy.dev/embassy-lora/">embassy-lora</a> supports LoRa networking on STM32WL wireless microcontrollers and Semtech SX126x and SX127x transceivers.
|
||||
|
||||
- **USB** -
|
||||
<a href="https://docs.embassy.dev/embassy-usb/">embassy-usb</a> implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own.
|
||||
|
44
ci.sh
44
ci.sh
@ -36,6 +36,10 @@ cargo batch \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,log \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-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-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16 \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,unstable-traits \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,nightly \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,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,nrf52810,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52811,gpiote,time-driver-rtc1 \
|
||||
@ -54,25 +58,26 @@ cargo batch \
|
||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits,log \
|
||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits \
|
||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f410tb,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f411ce,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f429zi,log,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l476vg,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wb15cc,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l072cz,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l041f6,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32l151cb-a,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f398ve,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32g0c1ve,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f217zg,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,stm32l552ze,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32wl54jc-cm0p,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wle5ub,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f107vc,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f103re,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,intrinsics \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f410tb,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f411ce,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f429zi,log,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l476vg,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wb15cc,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l072cz,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l041f6,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32l151cb-a,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f398ve,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32g0c1ve,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f217zg,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,stm32l552ze,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32wl54jc-cm0p,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wle5ub,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f107vc,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f103re,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840 \
|
||||
--- build --release --manifest-path embassy-boot/stm32/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32wl55jc-cm4 \
|
||||
--- build --release --manifest-path docs/modules/ROOT/examples/basic/Cargo.toml --target thumbv7em-none-eabi \
|
||||
@ -94,6 +99,7 @@ cargo batch \
|
||||
--- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32l0 \
|
||||
--- build --release --manifest-path examples/stm32l1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32l1 \
|
||||
--- build --release --manifest-path examples/stm32l4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32l4 \
|
||||
--- build --release --manifest-path examples/stm32l5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32l5 \
|
||||
--- 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/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wl \
|
||||
|
68
ci_stable.sh
68
ci_stable.sh
@ -13,6 +13,8 @@ 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 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,dhcpv4,medium-ethernet,pool-16 \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,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 nrf52810,gpiote,time-driver-rtc1 \
|
||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time-driver-rtc1 \
|
||||
@ -30,38 +32,38 @@ cargo batch \
|
||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features unstable-traits,defmt \
|
||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features unstable-traits,log \
|
||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g473cc,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585zi,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55vy,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55uc-cm4,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4r9zi,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303vc,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any,embassy-time?/tick-32768hz \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any,embassy-time?/tick-32768hz \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any,embassy-time?/tick-32768hz \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any,embassy-time?/tick-32768hz \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any,embassy-time?/tick-32768hz \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any,embassy-time?/tick-32768hz \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any,embassy-time?/tick-32768hz \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,embassy-time?/tick-32768hz \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,embassy-time?/tick-32768hz \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,embassy-time?/tick-32768hz \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,embassy-time?/tick-32768hz \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,embassy-time?/tick-32768hz \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,embassy-time?/tick-32768hz \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,embassy-time?/tick-32768hz,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g473cc,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585zi,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55vy,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55uc-cm4,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4r9zi,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303vc,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any \
|
||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,unstable-traits \
|
||||
--- build --release --manifest-path examples/nrf/Cargo.toml --target thumbv7em-none-eabi --no-default-features --out-dir out/examples/nrf --bin raw_spawn \
|
||||
--- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --no-default-features --out-dir out/examples/stm32l0 --bin raw_spawn \
|
||||
|
@ -3,16 +3,16 @@ authors = ["Dario Nieuwenhuis <dirbaio@dirbaio.net>"]
|
||||
edition = "2018"
|
||||
name = "embassy-basic-example"
|
||||
version = "0.1.0"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly"] }
|
||||
embassy-executor = { version = "0.1.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers"] }
|
||||
embassy-time = { version = "0.1.0", path = "../../../../../embassy-time", features = ["defmt", "nightly"] }
|
||||
embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] }
|
||||
|
||||
defmt = "0.3"
|
||||
defmt-rtt = "0.3"
|
||||
|
||||
cortex-m = "0.7.3"
|
||||
cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
|
||||
cortex-m-rt = "0.7.0"
|
||||
embedded-hal = "0.2.6"
|
||||
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
||||
|
35
docs/modules/ROOT/examples/basic/build.rs
Normal file
35
docs/modules/ROOT/examples/basic/build.rs
Normal file
@ -0,0 +1,35 @@
|
||||
//! This build script copies the `memory.x` file from the crate root into
|
||||
//! a directory where the linker can always find it at build time.
|
||||
//! For many projects this is optional, as the linker always searches the
|
||||
//! project root directory -- wherever `Cargo.toml` is. However, if you
|
||||
//! are using a workspace or have a more complicated build setup, this
|
||||
//! build script becomes required. Additionally, by requesting that
|
||||
//! Cargo re-run the build script whenever `memory.x` is changed,
|
||||
//! updating `memory.x` ensures a rebuild of the application with the
|
||||
//! new memory settings.
|
||||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
// Put `memory.x` in our output directory and ensure it's
|
||||
// on the linker search path.
|
||||
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||
File::create(out.join("memory.x"))
|
||||
.unwrap()
|
||||
.write_all(include_bytes!("memory.x"))
|
||||
.unwrap();
|
||||
println!("cargo:rustc-link-search={}", out.display());
|
||||
|
||||
// By default, Cargo will re-run a build script whenever
|
||||
// any file in the project changes. By specifying `memory.x`
|
||||
// here, we ensure the build script is only re-run when
|
||||
// `memory.x` is changed.
|
||||
println!("cargo:rerun-if-changed=memory.x");
|
||||
|
||||
println!("cargo:rustc-link-arg-bins=--nmagic");
|
||||
println!("cargo:rustc-link-arg-bins=-Tlink.x");
|
||||
println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
|
||||
}
|
7
docs/modules/ROOT/examples/basic/memory.x
Normal file
7
docs/modules/ROOT/examples/basic/memory.x
Normal file
@ -0,0 +1,7 @@
|
||||
MEMORY
|
||||
{
|
||||
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
||||
/* These values correspond to the NRF52840 with Softdevices S140 7.0.1 */
|
||||
FLASH : ORIGIN = 0x00000000, LENGTH = 1024K
|
||||
RAM : ORIGIN = 0x20000000, LENGTH = 256K
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
name = "blinky-async"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
cortex-m = "0.7"
|
||||
|
@ -2,6 +2,7 @@
|
||||
name = "blinky-hal"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
cortex-m = "0.7"
|
||||
|
@ -2,6 +2,7 @@
|
||||
name = "blinky-irq"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
cortex-m = "0.7"
|
||||
|
@ -2,6 +2,7 @@
|
||||
name = "blinky-pac"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
cortex-m = "0.7"
|
||||
|
@ -25,10 +25,19 @@ image::bootloader_flash.png[Bootloader flash layout]
|
||||
The bootloader divides the storage into 4 main partitions, configurable when creating the bootloader
|
||||
instance or via linker scripts:
|
||||
|
||||
* BOOTLOADER - Where the bootloader is placed. The bootloader itself consumes about 8kB of flash.
|
||||
* ACTIVE - Where the main application is placed. The bootloader will attempt to load the application at the start of this partition. This partition is only written to by the bootloader.
|
||||
* DFU - Where the application-to-be-swapped is placed. This partition is written to by the application.
|
||||
* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a flag is set to instruct the bootloader that the partitions should be swapped.
|
||||
* BOOTLOADER - Where the bootloader is placed. The bootloader itself consumes about 8kB of flash, but if you need to debug it and have space available, increasing this to 24kB will allow you to run the bootloader with probe-rs.
|
||||
* ACTIVE - Where the main application is placed. The bootloader will attempt to load the application at the start of this partition. This partition is only written to by the bootloader. The size required for this partition depends on the size of your application.
|
||||
* DFU - Where the application-to-be-swapped is placed. This partition is written to by the application. This partition must be at least 1 page bigger than the ACTIVE partition, since the swap algorithm uses the extra space to ensure power safe copy of data:
|
||||
+
|
||||
Partition Size~dfu~= Partition Size~active~+ Page Size~active~
|
||||
+
|
||||
All values are specified in bytes.
|
||||
|
||||
* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a magic field is written to instruct the bootloader that the partitions should be swapped. This partition must be able to store a magic field as well as the partition swap progress. The partition size given by:
|
||||
+
|
||||
Partition Size~state~ = Write Size~state~ + (2 × Partition Size~active~ / Page Size~active~)
|
||||
+
|
||||
All values are specified in bytes.
|
||||
|
||||
The partitions for ACTIVE (+BOOTLOADER), DFU and BOOTLOADER_STATE may be placed in separate flash. The page size used by the bootloader is determined by the lowest common multiple of the ACTIVE and DFU page sizes.
|
||||
The BOOTLOADER_STATE partition must be big enough to store one word per page in the ACTIVE and DFU partitions combined.
|
||||
|
@ -20,7 +20,7 @@ IMPORTANT: The executor relies on tasks not blocking indefinitely, as this preve
|
||||
|
||||
image::embassy_executor.png[Executor model]
|
||||
|
||||
If you use the `#[embassy::main]` macro in your application, it creates the `Executor` for you and spawns the main entry point as the first task. You can also create the Executor manually, and you can in fact create multiple Executors.
|
||||
If you use the `#[embassy_executor::main]` macro in your application, it creates the `Executor` for you and spawns the main entry point as the first task. You can also create the Executor manually, and you can in fact create multiple Executors.
|
||||
|
||||
|
||||
== Interrupts
|
||||
|
30
embassy-boot/README.md
Normal file
30
embassy-boot/README.md
Normal file
@ -0,0 +1,30 @@
|
||||
# embassy-boot
|
||||
|
||||
An [Embassy](https://embassy.dev) project.
|
||||
|
||||
A lightweight bootloader supporting firmware updates in a power-fail-safe way, with trial boots and rollbacks.
|
||||
|
||||
The bootloader can be used either as a library or be flashed directly with the default configuration derived from linker scripts.
|
||||
|
||||
By design, the bootloader does not provide any network capabilities. Networking capabilities for fetching new firmware can be provided by the user application, using the bootloader as a library for updating the firmware, or by using the bootloader as a library and adding this capability yourself.
|
||||
|
||||
## Hardware support
|
||||
|
||||
The bootloader supports different hardware in separate crates:
|
||||
|
||||
* `embassy-boot-nrf` - for the nRF microcontrollers.
|
||||
* `embassy-boot-stm32` - for the STM32 microcontrollers.
|
||||
|
||||
## Minimum supported Rust version (MSRV)
|
||||
|
||||
`embassy-boot` requires Rust nightly to compile as it relies on async traits for interacting with the flash peripherals.
|
||||
|
||||
## 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.
|
@ -3,6 +3,7 @@ edition = "2021"
|
||||
name = "embassy-boot"
|
||||
version = "0.1.0"
|
||||
description = "Bootloader using Embassy"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-v$VERSION/embassy-boot/boot/src/"
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -3,6 +3,7 @@ edition = "2021"
|
||||
name = "embassy-boot-nrf"
|
||||
version = "0.1.0"
|
||||
description = "Bootloader lib for nRF chips"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-nrf-v$VERSION/embassy-boot/nrf/src/"
|
||||
|
@ -1,19 +1,20 @@
|
||||
#![no_std]
|
||||
#![feature(generic_associated_types)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![allow(incomplete_features)]
|
||||
#![feature(generic_const_exprs)]
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![doc = include_str!("../../README.md")]
|
||||
mod fmt;
|
||||
|
||||
pub use embassy_boot::{FirmwareUpdater, FlashConfig, FlashProvider, Partition, SingleFlashProvider};
|
||||
pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig};
|
||||
use embassy_nrf::nvmc::{Nvmc, PAGE_SIZE};
|
||||
use embassy_nrf::peripherals::WDT;
|
||||
use embassy_nrf::wdt;
|
||||
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
|
||||
|
||||
/// A bootloader for nRF devices.
|
||||
pub struct BootLoader {
|
||||
boot: embassy_boot::BootLoader<PAGE_SIZE>,
|
||||
boot: embassy_boot::BootLoader,
|
||||
magic: AlignedBuffer<4>,
|
||||
page: AlignedBuffer<PAGE_SIZE>,
|
||||
}
|
||||
|
||||
impl BootLoader {
|
||||
@ -58,21 +59,25 @@ impl BootLoader {
|
||||
pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self {
|
||||
Self {
|
||||
boot: embassy_boot::BootLoader::new(active, dfu, state),
|
||||
magic: AlignedBuffer([0; 4]),
|
||||
page: AlignedBuffer([0; PAGE_SIZE]),
|
||||
}
|
||||
}
|
||||
|
||||
/// Boots the application without softdevice mechanisms
|
||||
pub fn prepare<F: FlashProvider>(&mut self, flash: &mut F) -> usize
|
||||
where
|
||||
[(); <<F as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE]:,
|
||||
[(); <<F as FlashProvider>::ACTIVE as FlashConfig>::FLASH::ERASE_SIZE]:,
|
||||
{
|
||||
match self.boot.prepare_boot(flash) {
|
||||
/// Inspect the bootloader state and perform actions required before booting, such as swapping
|
||||
/// firmware.
|
||||
pub fn prepare<F: FlashConfig>(&mut self, flash: &mut F) -> usize {
|
||||
match self.boot.prepare_boot(flash, &mut self.magic.0, &mut self.page.0) {
|
||||
Ok(_) => self.boot.boot_address(),
|
||||
Err(_) => panic!("boot prepare error!"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Boots the application without softdevice mechanisms.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This modifies the stack pointer and reset vector and will run code placed in the active partition.
|
||||
#[cfg(not(feature = "softdevice"))]
|
||||
pub unsafe fn load(&mut self, start: usize) -> ! {
|
||||
let mut p = cortex_m::Peripherals::steal();
|
||||
@ -81,6 +86,11 @@ impl BootLoader {
|
||||
cortex_m::asm::bootload(start as *const u32)
|
||||
}
|
||||
|
||||
/// Boots the application assuming softdevice is present.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This modifies the stack pointer and reset vector and will run code placed in the active partition.
|
||||
#[cfg(feature = "softdevice")]
|
||||
pub unsafe fn load(&mut self, _app: usize) -> ! {
|
||||
use nrf_softdevice_mbr as mbr;
|
||||
|
@ -3,6 +3,7 @@ edition = "2021"
|
||||
name = "embassy-boot-stm32"
|
||||
version = "0.1.0"
|
||||
description = "Bootloader lib for STM32 chips"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-nrf-v$VERSION/embassy-boot/stm32/src/"
|
||||
|
@ -1,19 +1,19 @@
|
||||
#![no_std]
|
||||
#![feature(generic_associated_types)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![allow(incomplete_features)]
|
||||
#![feature(generic_const_exprs)]
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![doc = include_str!("../../README.md")]
|
||||
mod fmt;
|
||||
|
||||
pub use embassy_boot::{FirmwareUpdater, FlashConfig, FlashProvider, Partition, SingleFlashProvider, State};
|
||||
use embedded_storage::nor_flash::NorFlash;
|
||||
pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State};
|
||||
|
||||
pub struct BootLoader<const PAGE_SIZE: usize> {
|
||||
boot: embassy_boot::BootLoader<PAGE_SIZE>,
|
||||
/// A bootloader for STM32 devices.
|
||||
pub struct BootLoader<const PAGE_SIZE: usize, const WRITE_SIZE: usize> {
|
||||
boot: embassy_boot::BootLoader,
|
||||
magic: AlignedBuffer<WRITE_SIZE>,
|
||||
page: AlignedBuffer<PAGE_SIZE>,
|
||||
}
|
||||
|
||||
impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
|
||||
impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize> BootLoader<PAGE_SIZE, WRITE_SIZE> {
|
||||
/// Create a new bootloader instance using parameters from linker script
|
||||
pub fn default() -> Self {
|
||||
extern "C" {
|
||||
@ -55,21 +55,25 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
|
||||
pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self {
|
||||
Self {
|
||||
boot: embassy_boot::BootLoader::new(active, dfu, state),
|
||||
magic: AlignedBuffer([0; WRITE_SIZE]),
|
||||
page: AlignedBuffer([0; PAGE_SIZE]),
|
||||
}
|
||||
}
|
||||
|
||||
/// Boots the application
|
||||
pub fn prepare<F: FlashProvider>(&mut self, flash: &mut F) -> usize
|
||||
where
|
||||
[(); <<F as FlashProvider>::STATE as FlashConfig>::FLASH::WRITE_SIZE]:,
|
||||
[(); <<F as FlashProvider>::ACTIVE as FlashConfig>::FLASH::ERASE_SIZE]:,
|
||||
{
|
||||
match self.boot.prepare_boot(flash) {
|
||||
/// Inspect the bootloader state and perform actions required before booting, such as swapping
|
||||
/// firmware.
|
||||
pub fn prepare<F: FlashConfig>(&mut self, flash: &mut F) -> usize {
|
||||
match self.boot.prepare_boot(flash, self.magic.as_mut(), self.page.as_mut()) {
|
||||
Ok(_) => embassy_stm32::flash::FLASH_BASE + self.boot.boot_address(),
|
||||
Err(_) => panic!("boot prepare error!"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Boots the application.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This modifies the stack pointer and reset vector and will run code placed in the active partition.
|
||||
pub unsafe fn load(&mut self, start: usize) -> ! {
|
||||
trace!("Loading app at 0x{:x}", start);
|
||||
#[allow(unused_mut)]
|
||||
|
@ -2,6 +2,7 @@
|
||||
name = "embassy-cortex-m"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-cortex-m-v$VERSION/embassy-cortex-m/src/"
|
||||
|
@ -2,13 +2,14 @@
|
||||
name = "embassy-embedded-hal"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-embedded-hal-v$VERSION/embassy-embedded-hal/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-embedded-hal/src/"
|
||||
features = ["nightly", "std"]
|
||||
target = "thumbv7em-none-eabi"
|
||||
target = "x86_64-unknown-linux-gnu"
|
||||
|
||||
[features]
|
||||
std = []
|
||||
@ -18,8 +19,8 @@ nightly = ["embedded-hal-async", "embedded-storage-async"]
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
|
||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" }
|
||||
embedded-hal-async = { version = "0.1.0-alpha.1", optional = true }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" }
|
||||
embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true }
|
||||
embedded-storage = "0.3.0"
|
||||
embedded-storage-async = { version = "0.3.0", optional = true }
|
||||
nb = "1.0.0"
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))]
|
||||
#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
//! Utilities to use `embedded-hal` traits with Embassy.
|
||||
|
@ -29,7 +29,7 @@ use core::future::Future;
|
||||
|
||||
use embassy_sync::blocking_mutex::raw::RawMutex;
|
||||
use embassy_sync::mutex::Mutex;
|
||||
use embedded_hal_1::digital::blocking::OutputPin;
|
||||
use embedded_hal_1::digital::OutputPin;
|
||||
use embedded_hal_1::spi::ErrorType;
|
||||
use embedded_hal_async::spi;
|
||||
|
||||
@ -57,7 +57,7 @@ where
|
||||
type Error = SpiDeviceError<BUS::Error, CS::Error>;
|
||||
}
|
||||
|
||||
impl<M, BUS, CS> spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
|
||||
unsafe impl<M, BUS, CS> spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
|
||||
where
|
||||
M: RawMutex + 'static,
|
||||
BUS: spi::SpiBusFlush + 'static,
|
||||
@ -122,7 +122,7 @@ where
|
||||
type Error = SpiDeviceError<BUS::Error, CS::Error>;
|
||||
}
|
||||
|
||||
impl<M, BUS, CS> spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
|
||||
unsafe impl<M, BUS, CS> spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
|
||||
where
|
||||
M: RawMutex + 'static,
|
||||
BUS: spi::SpiBusFlush + SetConfig + 'static,
|
||||
|
@ -20,8 +20,7 @@ use core::cell::RefCell;
|
||||
|
||||
use embassy_sync::blocking_mutex::raw::RawMutex;
|
||||
use embassy_sync::blocking_mutex::Mutex;
|
||||
use embedded_hal_1::i2c::blocking::{I2c, Operation};
|
||||
use embedded_hal_1::i2c::ErrorType;
|
||||
use embedded_hal_1::i2c::{ErrorType, I2c, Operation};
|
||||
|
||||
use crate::shared_bus::I2cDeviceError;
|
||||
use crate::SetConfig;
|
||||
|
@ -22,9 +22,9 @@ use core::cell::RefCell;
|
||||
|
||||
use embassy_sync::blocking_mutex::raw::RawMutex;
|
||||
use embassy_sync::blocking_mutex::Mutex;
|
||||
use embedded_hal_1::digital::blocking::OutputPin;
|
||||
use embedded_hal_1::digital::OutputPin;
|
||||
use embedded_hal_1::spi;
|
||||
use embedded_hal_1::spi::blocking::SpiBusFlush;
|
||||
use embedded_hal_1::spi::SpiBusFlush;
|
||||
|
||||
use crate::shared_bus::SpiDeviceError;
|
||||
use crate::SetConfig;
|
||||
@ -50,7 +50,7 @@ where
|
||||
type Error = SpiDeviceError<BUS::Error, CS::Error>;
|
||||
}
|
||||
|
||||
impl<BUS, M, CS> embedded_hal_1::spi::blocking::SpiDevice for SpiDevice<'_, M, BUS, CS>
|
||||
impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
|
||||
where
|
||||
M: RawMutex,
|
||||
BUS: SpiBusFlush,
|
||||
@ -146,7 +146,7 @@ where
|
||||
type Error = SpiDeviceError<BUS::Error, CS::Error>;
|
||||
}
|
||||
|
||||
impl<BUS, M, CS> embedded_hal_1::spi::blocking::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
|
||||
impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
|
||||
where
|
||||
M: RawMutex,
|
||||
BUS: SpiBusFlush + SetConfig,
|
||||
|
@ -2,12 +2,13 @@
|
||||
name = "embassy-executor"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/"
|
||||
features = ["nightly", "defmt", "unstable-traits"]
|
||||
features = ["nightly", "defmt"]
|
||||
flavors = [
|
||||
{ name = "std", target = "x86_64-unknown-linux-gnu", features = ["std"] },
|
||||
{ name = "wasm", target = "wasm32-unknown-unknown", features = ["wasm"] },
|
||||
@ -22,7 +23,7 @@ flavors = [
|
||||
|
||||
[features]
|
||||
default = []
|
||||
std = ["embassy-macros/std"]
|
||||
std = ["embassy-macros/std", "critical-section/std"]
|
||||
wasm = ["dep:wasm-bindgen", "dep:js-sys", "embassy-macros/wasm"]
|
||||
|
||||
# Enable nightly-only features
|
||||
|
@ -354,46 +354,54 @@ impl Executor {
|
||||
/// somehow schedule for `poll()` to be called later, at a time you know for sure there's
|
||||
/// no `poll()` already running.
|
||||
pub unsafe fn poll(&'static self) {
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
self.timer_queue.dequeue_expired(Instant::now(), |task| wake_task(task));
|
||||
loop {
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
self.timer_queue.dequeue_expired(Instant::now(), |task| wake_task(task));
|
||||
|
||||
self.run_queue.dequeue_all(|p| {
|
||||
let task = p.as_ref();
|
||||
self.run_queue.dequeue_all(|p| {
|
||||
let task = p.as_ref();
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
task.expires_at.set(Instant::MAX);
|
||||
|
||||
let state = task.state.fetch_and(!STATE_RUN_QUEUED, Ordering::AcqRel);
|
||||
if state & STATE_SPAWNED == 0 {
|
||||
// If task is not running, ignore it. This can happen in the following scenario:
|
||||
// - Task gets dequeued, poll starts
|
||||
// - While task is being polled, it gets woken. It gets placed in the queue.
|
||||
// - Task poll finishes, returning done=true
|
||||
// - RUNNING bit is cleared, but the task is already in the queue.
|
||||
return;
|
||||
}
|
||||
|
||||
#[cfg(feature = "rtos-trace")]
|
||||
trace::task_exec_begin(p.as_ptr() as u32);
|
||||
|
||||
// Run the task
|
||||
task.poll_fn.read()(p as _);
|
||||
|
||||
#[cfg(feature = "rtos-trace")]
|
||||
trace::task_exec_end();
|
||||
|
||||
// Enqueue or update into timer_queue
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
self.timer_queue.update(p);
|
||||
});
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
task.expires_at.set(Instant::MAX);
|
||||
|
||||
let state = task.state.fetch_and(!STATE_RUN_QUEUED, Ordering::AcqRel);
|
||||
if state & STATE_SPAWNED == 0 {
|
||||
// If task is not running, ignore it. This can happen in the following scenario:
|
||||
// - Task gets dequeued, poll starts
|
||||
// - While task is being polled, it gets woken. It gets placed in the queue.
|
||||
// - Task poll finishes, returning done=true
|
||||
// - RUNNING bit is cleared, but the task is already in the queue.
|
||||
return;
|
||||
{
|
||||
// If this is already in the past, set_alarm might return false
|
||||
// In that case do another poll loop iteration.
|
||||
let next_expiration = self.timer_queue.next_expiration();
|
||||
if driver::set_alarm(self.alarm, next_expiration.as_ticks()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rtos-trace")]
|
||||
trace::task_exec_begin(p.as_ptr() as u32);
|
||||
|
||||
// Run the task
|
||||
task.poll_fn.read()(p as _);
|
||||
|
||||
#[cfg(feature = "rtos-trace")]
|
||||
trace::task_exec_end();
|
||||
|
||||
// Enqueue or update into timer_queue
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
self.timer_queue.update(p);
|
||||
});
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
{
|
||||
// If this is already in the past, set_alarm will immediately trigger the alarm.
|
||||
// This will cause `signal_fn` to be called, which will cause `poll()` to be called again,
|
||||
// so we immediately do another poll loop iteration.
|
||||
let next_expiration = self.timer_queue.next_expiration();
|
||||
driver::set_alarm(self.alarm, next_expiration.as_ticks());
|
||||
#[cfg(not(feature = "integrated-timers"))]
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rtos-trace")]
|
||||
@ -436,14 +444,21 @@ pub unsafe fn wake_task(task: NonNull<TaskHeader>) {
|
||||
}
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
#[no_mangle]
|
||||
unsafe fn _embassy_time_schedule_wake(at: Instant, waker: &core::task::Waker) {
|
||||
let task = waker::task_from_waker(waker);
|
||||
let task = task.as_ref();
|
||||
let expires_at = task.expires_at.get();
|
||||
task.expires_at.set(expires_at.min(at));
|
||||
struct TimerQueue;
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
impl embassy_time::queue::TimerQueue for TimerQueue {
|
||||
fn schedule_wake(&'static self, at: Instant, waker: &core::task::Waker) {
|
||||
let task = waker::task_from_waker(waker);
|
||||
let task = unsafe { task.as_ref() };
|
||||
let expires_at = task.expires_at.get();
|
||||
task.expires_at.set(expires_at.min(at));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
embassy_time::timer_queue_impl!(static TIMER_QUEUE: TimerQueue = TimerQueue);
|
||||
|
||||
#[cfg(feature = "rtos-trace")]
|
||||
impl rtos_trace::RtosTraceOSCallbacks for Executor {
|
||||
fn task_list() {
|
||||
|
@ -1,10 +1,9 @@
|
||||
use core::future::poll_fn;
|
||||
use core::marker::PhantomData;
|
||||
use core::mem;
|
||||
use core::ptr::NonNull;
|
||||
use core::task::Poll;
|
||||
|
||||
use futures_util::future::poll_fn;
|
||||
|
||||
use super::raw;
|
||||
|
||||
/// Token to spawn a newly-created task in an executor.
|
||||
|
@ -2,6 +2,7 @@
|
||||
name = "embassy-hal-common"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[features]
|
||||
|
||||
|
@ -6,7 +6,7 @@ use core::ops::{Deref, DerefMut};
|
||||
/// This is functionally the same as a `&'a mut T`. The reason for having a
|
||||
/// dedicated struct is memory efficiency:
|
||||
///
|
||||
/// Peripheral singletons are typically either zero-sized (for concrete peripehrals
|
||||
/// Peripheral singletons are typically either zero-sized (for concrete peripherals
|
||||
/// like `PA9` or `Spi4`) or very small (for example `AnyPin` which is 1 byte).
|
||||
/// However `&mut T` is always 4 bytes for 32-bit targets, even if T is zero-sized.
|
||||
/// PeripheralRef stores a copy of `T` instead, so it's the same size.
|
||||
|
@ -2,22 +2,26 @@
|
||||
name = "embassy-lora"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-lora-v$VERSION/embassy-lora/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-lora/src/"
|
||||
features = ["time", "defmt"]
|
||||
flavors = [
|
||||
{ name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any", "embassy-time/tick-32768hz"] },
|
||||
{ name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any", "embassy-time/tick-32768hz"] },
|
||||
{ name = "sx126x", target = "thumbv7em-none-eabihf", features = ["sx126x"] },
|
||||
{ name = "sx127x", target = "thumbv7em-none-eabihf", features = ["sx127x", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] },
|
||||
{ name = "stm32wl", target = "thumbv7em-none-eabihf", features = ["stm32wl", "embassy-stm32/stm32wl55jc-cm4", "embassy-stm32/time-driver-any"] },
|
||||
]
|
||||
|
||||
[lib]
|
||||
|
||||
[features]
|
||||
sx126x = []
|
||||
sx127x = []
|
||||
stm32wl = ["embassy-stm32", "embassy-stm32/subghz"]
|
||||
time = []
|
||||
defmt = ["dep:defmt", "lorawan/defmt", "lorawan-device/defmt"]
|
||||
|
||||
[dependencies]
|
||||
|
||||
@ -27,12 +31,12 @@ log = { version = "0.4.14", optional = true }
|
||||
embassy-time = { version = "0.1.0", path = "../embassy-time" }
|
||||
embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
|
||||
embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" }
|
||||
embedded-hal-async = { version = "0.1.0-alpha.1" }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" }
|
||||
embedded-hal-async = { version = "=0.1.0-alpha.3" }
|
||||
embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common", default-features = false }
|
||||
futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
|
||||
embedded-hal = { version = "0.2", features = ["unproven"] }
|
||||
bit_field = { version = "0.10" }
|
||||
|
||||
lorawan-device = { version = "0.7.1", default-features = false, features = ["async"] }
|
||||
lorawan-device = { version = "0.8.0", default-features = false, features = ["async"] }
|
||||
lorawan = { version = "0.7.1", default-features = false }
|
||||
|
@ -1,6 +1,5 @@
|
||||
#![no_std]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![feature(generic_associated_types)]
|
||||
//! embassy-lora is a collection of async radio drivers that integrate with the lorawan-device
|
||||
//! crate's async LoRaWAN MAC implementation.
|
||||
|
||||
@ -8,16 +7,40 @@ pub(crate) mod fmt;
|
||||
|
||||
#[cfg(feature = "stm32wl")]
|
||||
pub mod stm32wl;
|
||||
#[cfg(feature = "sx126x")]
|
||||
pub mod sx126x;
|
||||
#[cfg(feature = "sx127x")]
|
||||
pub mod sx127x;
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
use embassy_time::{Duration, Instant, Timer};
|
||||
|
||||
/// A convenience timer to use with the LoRaWAN crate
|
||||
pub struct LoraTimer;
|
||||
#[cfg(feature = "time")]
|
||||
pub struct LoraTimer {
|
||||
start: Instant,
|
||||
}
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
impl LoraTimer {
|
||||
pub fn new() -> Self {
|
||||
Self { start: Instant::now() }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "time")]
|
||||
impl lorawan_device::async_device::radio::Timer for LoraTimer {
|
||||
fn reset(&mut self) {
|
||||
self.start = Instant::now();
|
||||
}
|
||||
|
||||
type AtFuture<'m> = impl core::future::Future<Output = ()> + 'm;
|
||||
fn at<'m>(&'m mut self, millis: u64) -> Self::AtFuture<'m> {
|
||||
Timer::at(self.start + Duration::from_millis(millis))
|
||||
}
|
||||
|
||||
type DelayFuture<'m> = impl core::future::Future<Output = ()> + 'm;
|
||||
fn delay_ms<'m>(&'m mut self, millis: u64) -> Self::DelayFuture<'m> {
|
||||
embassy_time::Timer::after(embassy_time::Duration::from_millis(millis))
|
||||
Timer::after(Duration::from_millis(millis))
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,16 @@
|
||||
//! A radio driver integration for the radio found on STM32WL family devices.
|
||||
use core::future::Future;
|
||||
use core::mem::MaybeUninit;
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
|
||||
use embassy_stm32::dma::NoDma;
|
||||
use embassy_stm32::gpio::{AnyPin, Output};
|
||||
use embassy_stm32::interrupt::{InterruptExt, SUBGHZ_RADIO};
|
||||
use embassy_stm32::interrupt::{Interrupt, InterruptExt, SUBGHZ_RADIO};
|
||||
use embassy_stm32::subghz::{
|
||||
CalibrateImage, CfgIrq, CodingRate, Error, HeaderType, Irq, LoRaBandwidth, LoRaModParams, LoRaPacketParams,
|
||||
LoRaSyncWord, Ocp, PaConfig, PaSel, PacketType, RampTime, RegMode, RfFreq, SpreadingFactor as SF, StandbyClk,
|
||||
CalibrateImage, CfgIrq, CodingRate, Error, HeaderType, HseTrim, Irq, LoRaBandwidth, LoRaModParams,
|
||||
LoRaPacketParams, LoRaSyncWord, Ocp, PaConfig, PacketType, RegMode, RfFreq, SpreadingFactor as SF, StandbyClk,
|
||||
Status, SubGhz, TcxoMode, TcxoTrim, Timeout, TxParams,
|
||||
};
|
||||
use embassy_stm32::Peripheral;
|
||||
use embassy_sync::signal::Signal;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use lorawan_device::async_device::radio::{Bandwidth, PhyRxTx, RfConfig, RxQuality, SpreadingFactor, TxConfig};
|
||||
use lorawan_device::async_device::Timings;
|
||||
|
||||
@ -28,102 +26,52 @@ pub enum State {
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct RadioError;
|
||||
|
||||
static IRQ: Signal<(Status, u16)> = Signal::new();
|
||||
|
||||
struct StateInner<'d> {
|
||||
radio: SubGhz<'d, NoDma, NoDma>,
|
||||
switch: RadioSwitch<'d>,
|
||||
}
|
||||
|
||||
/// External state storage for the radio state
|
||||
pub struct SubGhzState<'a>(MaybeUninit<StateInner<'a>>);
|
||||
impl<'d> SubGhzState<'d> {
|
||||
pub const fn new() -> Self {
|
||||
Self(MaybeUninit::uninit())
|
||||
}
|
||||
}
|
||||
static IRQ_WAKER: AtomicWaker = AtomicWaker::new();
|
||||
|
||||
/// The radio peripheral keeping the radio state and owning the radio IRQ.
|
||||
pub struct SubGhzRadio<'d> {
|
||||
state: *mut StateInner<'d>,
|
||||
_irq: PeripheralRef<'d, SUBGHZ_RADIO>,
|
||||
pub struct SubGhzRadio<'d, RS> {
|
||||
radio: SubGhz<'d, NoDma, NoDma>,
|
||||
switch: RS,
|
||||
irq: PeripheralRef<'d, SUBGHZ_RADIO>,
|
||||
}
|
||||
|
||||
impl<'d> SubGhzRadio<'d> {
|
||||
#[derive(Default)]
|
||||
#[non_exhaustive]
|
||||
pub struct SubGhzRadioConfig {
|
||||
pub reg_mode: RegMode,
|
||||
pub calibrate_image: CalibrateImage,
|
||||
pub pa_config: PaConfig,
|
||||
pub tx_params: TxParams,
|
||||
}
|
||||
|
||||
impl<'d, RS: RadioSwitch> SubGhzRadio<'d, RS> {
|
||||
/// Create a new instance of a SubGhz radio for LoRaWAN.
|
||||
///
|
||||
/// # Safety
|
||||
/// Do not leak self or futures
|
||||
pub unsafe fn new(
|
||||
state: &'d mut SubGhzState<'d>,
|
||||
radio: SubGhz<'d, NoDma, NoDma>,
|
||||
switch: RadioSwitch<'d>,
|
||||
pub fn new(
|
||||
mut radio: SubGhz<'d, NoDma, NoDma>,
|
||||
switch: RS,
|
||||
irq: impl Peripheral<P = SUBGHZ_RADIO> + 'd,
|
||||
) -> Self {
|
||||
config: SubGhzRadioConfig,
|
||||
) -> Result<Self, RadioError> {
|
||||
into_ref!(irq);
|
||||
|
||||
let mut inner = StateInner { radio, switch };
|
||||
inner.radio.reset();
|
||||
|
||||
let state_ptr = state.0.as_mut_ptr();
|
||||
state_ptr.write(inner);
|
||||
radio.reset();
|
||||
|
||||
irq.disable();
|
||||
irq.set_handler(|p| {
|
||||
// This is safe because we only get interrupts when configured for, so
|
||||
// the radio will be awaiting on the signal at this point. If not, the ISR will
|
||||
// anyway only adjust the state in the IRQ signal state.
|
||||
let state = &mut *(p as *mut StateInner<'d>);
|
||||
state.on_interrupt();
|
||||
irq.set_handler(|_| {
|
||||
IRQ_WAKER.wake();
|
||||
unsafe { SUBGHZ_RADIO::steal().disable() };
|
||||
});
|
||||
irq.set_handler_context(state_ptr as *mut ());
|
||||
irq.enable();
|
||||
|
||||
Self {
|
||||
state: state_ptr,
|
||||
_irq: irq,
|
||||
}
|
||||
}
|
||||
}
|
||||
configure_radio(&mut radio, config)?;
|
||||
|
||||
impl<'d> StateInner<'d> {
|
||||
/// Configure radio settings in preparation for TX or RX
|
||||
pub(crate) fn configure(&mut self) -> Result<(), RadioError> {
|
||||
trace!("Configuring STM32WL SUBGHZ radio");
|
||||
self.radio.set_standby(StandbyClk::Rc)?;
|
||||
let tcxo_mode = TcxoMode::new()
|
||||
.set_txco_trim(TcxoTrim::Volts1pt7)
|
||||
.set_timeout(Timeout::from_duration_sat(core::time::Duration::from_millis(40)));
|
||||
|
||||
self.radio.set_tcxo_mode(&tcxo_mode)?;
|
||||
self.radio.set_regulator_mode(RegMode::Ldo)?;
|
||||
|
||||
self.radio.calibrate_image(CalibrateImage::ISM_863_870)?;
|
||||
|
||||
self.radio.set_buffer_base_address(0, 0)?;
|
||||
|
||||
self.radio
|
||||
.set_pa_config(&PaConfig::new().set_pa_duty_cycle(0x1).set_hp_max(0x0).set_pa(PaSel::Lp))?;
|
||||
|
||||
self.radio.set_pa_ocp(Ocp::Max140m)?;
|
||||
|
||||
// let tx_params = TxParams::LP_14.set_ramp_time(RampTime::Micros40);
|
||||
self.radio
|
||||
.set_tx_params(&TxParams::new().set_ramp_time(RampTime::Micros40).set_power(0x0A))?;
|
||||
|
||||
self.radio.set_packet_type(PacketType::LoRa)?;
|
||||
self.radio.set_lora_sync_word(LoRaSyncWord::Public)?;
|
||||
trace!("Done initializing STM32WL SUBGHZ radio");
|
||||
Ok(())
|
||||
Ok(Self { radio, switch, irq })
|
||||
}
|
||||
|
||||
/// Perform a transmission with the given parameters and payload. Returns any time adjustements needed form
|
||||
/// the upcoming RX window start.
|
||||
async fn do_tx(&mut self, config: TxConfig, buf: &[u8]) -> Result<u32, RadioError> {
|
||||
//trace!("TX Request: {}", config);
|
||||
trace!("TX START");
|
||||
self.switch.set_tx_lp();
|
||||
self.configure()?;
|
||||
trace!("TX request: {}", config);
|
||||
self.switch.set_tx();
|
||||
|
||||
self.radio
|
||||
.set_rf_frequency(&RfFreq::from_frequency(config.rf.frequency))?;
|
||||
@ -139,34 +87,26 @@ impl<'d> StateInner<'d> {
|
||||
|
||||
self.radio.set_lora_packet_params(&packet_params)?;
|
||||
|
||||
let irq_cfg = CfgIrq::new()
|
||||
.irq_enable_all(Irq::TxDone)
|
||||
.irq_enable_all(Irq::RxDone)
|
||||
.irq_enable_all(Irq::Timeout);
|
||||
let irq_cfg = CfgIrq::new().irq_enable_all(Irq::TxDone).irq_enable_all(Irq::Timeout);
|
||||
self.radio.set_irq_cfg(&irq_cfg)?;
|
||||
|
||||
self.radio.set_buffer_base_address(0, 0)?;
|
||||
self.radio.write_buffer(0, buf)?;
|
||||
|
||||
self.radio.set_tx(Timeout::DISABLED)?;
|
||||
// The maximum airtime for any LoRaWAN package is 2793.5ms.
|
||||
// The value of 4000ms is copied from C driver and gives us a good safety margin.
|
||||
self.radio.set_tx(Timeout::from_millis_sat(4000))?;
|
||||
trace!("TX started");
|
||||
|
||||
loop {
|
||||
let (_status, irq_status) = IRQ.wait().await;
|
||||
IRQ.reset();
|
||||
let (_status, irq_status) = self.irq_wait().await;
|
||||
|
||||
if irq_status & Irq::TxDone.mask() != 0 {
|
||||
let stats = self.radio.lora_stats()?;
|
||||
let (status, error_mask) = self.radio.op_error()?;
|
||||
trace!(
|
||||
"TX done. Stats: {:?}. OP error: {:?}, mask {:?}",
|
||||
stats,
|
||||
status,
|
||||
error_mask
|
||||
);
|
||||
|
||||
trace!("TX done");
|
||||
return Ok(0);
|
||||
} else if irq_status & Irq::Timeout.mask() != 0 {
|
||||
trace!("TX timeout");
|
||||
}
|
||||
|
||||
if irq_status & Irq::Timeout.mask() != 0 {
|
||||
return Err(RadioError);
|
||||
}
|
||||
}
|
||||
@ -174,10 +114,15 @@ impl<'d> StateInner<'d> {
|
||||
|
||||
fn set_lora_mod_params(&mut self, config: RfConfig) -> Result<(), Error> {
|
||||
let mod_params = LoRaModParams::new()
|
||||
.set_sf(convert_spreading_factor(config.spreading_factor))
|
||||
.set_bw(convert_bandwidth(config.bandwidth))
|
||||
.set_sf(convert_spreading_factor(&config.spreading_factor))
|
||||
.set_bw(convert_bandwidth(&config.bandwidth))
|
||||
.set_cr(CodingRate::Cr45)
|
||||
.set_ldro_en(true);
|
||||
.set_ldro_en(matches!(
|
||||
(config.spreading_factor, config.bandwidth),
|
||||
(SpreadingFactor::_12, Bandwidth::_125KHz)
|
||||
| (SpreadingFactor::_12, Bandwidth::_250KHz)
|
||||
| (SpreadingFactor::_11, Bandwidth::_125KHz)
|
||||
));
|
||||
self.radio.set_lora_mod_params(&mod_params)
|
||||
}
|
||||
|
||||
@ -185,10 +130,8 @@ impl<'d> StateInner<'d> {
|
||||
/// be able to hold a single LoRaWAN packet.
|
||||
async fn do_rx(&mut self, config: RfConfig, buf: &mut [u8]) -> Result<(usize, RxQuality), RadioError> {
|
||||
assert!(buf.len() >= 255);
|
||||
trace!("RX START");
|
||||
// trace!("Starting RX: {}", config);
|
||||
trace!("RX request: {}", config);
|
||||
self.switch.set_rx();
|
||||
self.configure()?;
|
||||
|
||||
self.radio.set_rf_frequency(&RfFreq::from_frequency(config.frequency))?;
|
||||
|
||||
@ -198,77 +141,110 @@ impl<'d> StateInner<'d> {
|
||||
.set_preamble_len(8)
|
||||
.set_header_type(HeaderType::Variable)
|
||||
.set_payload_len(0xFF)
|
||||
.set_crc_en(true)
|
||||
.set_crc_en(false)
|
||||
.set_invert_iq(true);
|
||||
self.radio.set_lora_packet_params(&packet_params)?;
|
||||
|
||||
let irq_cfg = CfgIrq::new()
|
||||
.irq_enable_all(Irq::RxDone)
|
||||
.irq_enable_all(Irq::PreambleDetected)
|
||||
.irq_enable_all(Irq::HeaderValid)
|
||||
.irq_enable_all(Irq::HeaderErr)
|
||||
.irq_enable_all(Irq::Timeout)
|
||||
.irq_enable_all(Irq::Err);
|
||||
.irq_enable_all(Irq::Err)
|
||||
.irq_enable_all(Irq::Timeout);
|
||||
self.radio.set_irq_cfg(&irq_cfg)?;
|
||||
|
||||
self.radio.set_buffer_base_address(0, 0)?;
|
||||
|
||||
// NOTE: Upper layer handles timeout by cancelling the future
|
||||
self.radio.set_rx(Timeout::DISABLED)?;
|
||||
|
||||
trace!("RX started");
|
||||
|
||||
loop {
|
||||
let (status, irq_status) = IRQ.wait().await;
|
||||
IRQ.reset();
|
||||
trace!("RX IRQ {:?}, {:?}", status, irq_status);
|
||||
if irq_status & Irq::RxDone.mask() != 0 {
|
||||
let (status, len, ptr) = self.radio.rx_buffer_status()?;
|
||||
let (_status, irq_status) = self.irq_wait().await;
|
||||
|
||||
if irq_status & Irq::RxDone.mask() != 0 {
|
||||
let (_status, len, ptr) = self.radio.rx_buffer_status()?;
|
||||
let packet_status = self.radio.lora_packet_status()?;
|
||||
let rssi = packet_status.rssi_pkt().to_integer();
|
||||
let snr = packet_status.snr_pkt().to_integer();
|
||||
trace!(
|
||||
"RX done. Received {} bytes. RX status: {:?}. Pkt status: {:?}",
|
||||
len,
|
||||
status.cmd(),
|
||||
packet_status,
|
||||
);
|
||||
self.radio.read_buffer(ptr, &mut buf[..len as usize])?;
|
||||
self.radio.set_standby(StandbyClk::Rc)?;
|
||||
|
||||
trace!("RX done: {=[u8]:#02X}", &mut buf[..len as usize]);
|
||||
return Ok((len as usize, RxQuality::new(rssi, snr as i8)));
|
||||
} else if irq_status & (Irq::Timeout.mask() | Irq::TxDone.mask()) != 0 {
|
||||
}
|
||||
|
||||
if irq_status & Irq::Timeout.mask() != 0 {
|
||||
return Err(RadioError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Read interrupt status and store in global signal
|
||||
fn on_interrupt(&mut self) {
|
||||
let (status, irq_status) = self.radio.irq_status().expect("error getting irq status");
|
||||
self.radio
|
||||
.clear_irq_status(irq_status)
|
||||
.expect("error clearing irq status");
|
||||
if irq_status & Irq::PreambleDetected.mask() != 0 {
|
||||
trace!("Preamble detected, ignoring");
|
||||
} else {
|
||||
IRQ.signal((status, irq_status));
|
||||
}
|
||||
async fn irq_wait(&mut self) -> (Status, u16) {
|
||||
poll_fn(|cx| {
|
||||
self.irq.unpend();
|
||||
self.irq.enable();
|
||||
IRQ_WAKER.register(cx.waker());
|
||||
|
||||
let (status, irq_status) = self.radio.irq_status().expect("error getting irq status");
|
||||
self.radio
|
||||
.clear_irq_status(irq_status)
|
||||
.expect("error clearing irq status");
|
||||
|
||||
trace!("SUGHZ IRQ 0b{=u16:b}, {:?}", irq_status, status);
|
||||
|
||||
if irq_status == 0 {
|
||||
Poll::Pending
|
||||
} else {
|
||||
Poll::Ready((status, irq_status))
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl PhyRxTx for SubGhzRadio<'static> {
|
||||
fn configure_radio(radio: &mut SubGhz<'_, NoDma, NoDma>, config: SubGhzRadioConfig) -> Result<(), RadioError> {
|
||||
trace!("Configuring STM32WL SUBGHZ radio");
|
||||
|
||||
radio.set_regulator_mode(config.reg_mode)?;
|
||||
radio.set_standby(StandbyClk::Rc)?;
|
||||
|
||||
let tcxo_mode = TcxoMode::new()
|
||||
.set_txco_trim(TcxoTrim::Volts1pt7)
|
||||
.set_timeout(Timeout::from_duration_sat(core::time::Duration::from_millis(100)));
|
||||
radio.set_tcxo_mode(&tcxo_mode)?;
|
||||
// Reduce input capacitance as shown in Reference Manual "Figure 23. HSE32 TCXO control".
|
||||
// The STM32CUBE C driver also does this.
|
||||
radio.set_hse_in_trim(HseTrim::MIN)?;
|
||||
|
||||
// Re-calibrate everything after setting the TXCO config.
|
||||
radio.calibrate(0x7F)?;
|
||||
radio.calibrate_image(config.calibrate_image)?;
|
||||
|
||||
radio.set_pa_config(&config.pa_config)?;
|
||||
radio.set_tx_params(&config.tx_params)?;
|
||||
radio.set_pa_ocp(Ocp::Max140m)?;
|
||||
|
||||
radio.set_packet_type(PacketType::LoRa)?;
|
||||
radio.set_lora_sync_word(LoRaSyncWord::Public)?;
|
||||
|
||||
trace!("Done initializing STM32WL SUBGHZ radio");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl<'d, RS: RadioSwitch> PhyRxTx for SubGhzRadio<'d, RS> {
|
||||
type PhyError = RadioError;
|
||||
|
||||
type TxFuture<'m> = impl Future<Output = Result<u32, Self::PhyError>> + 'm;
|
||||
type TxFuture<'m> = impl Future<Output = Result<u32, Self::PhyError>> + 'm where Self: 'm;
|
||||
fn tx<'m>(&'m mut self, config: TxConfig, buf: &'m [u8]) -> Self::TxFuture<'m> {
|
||||
async move {
|
||||
let inner = unsafe { &mut *self.state };
|
||||
inner.do_tx(config, buf).await
|
||||
}
|
||||
async move { self.do_tx(config, buf).await }
|
||||
}
|
||||
|
||||
type RxFuture<'m> = impl Future<Output = Result<(usize, RxQuality), Self::PhyError>> + 'm;
|
||||
type RxFuture<'m> = impl Future<Output = Result<(usize, RxQuality), Self::PhyError>> + 'm where Self: 'm;
|
||||
fn rx<'m>(&'m mut self, config: RfConfig, buf: &'m mut [u8]) -> Self::RxFuture<'m> {
|
||||
async move {
|
||||
let inner = unsafe { &mut *self.state };
|
||||
inner.do_rx(config, buf).await
|
||||
}
|
||||
async move { self.do_rx(config, buf).await }
|
||||
}
|
||||
}
|
||||
|
||||
@ -278,48 +254,21 @@ impl From<embassy_stm32::spi::Error> for RadioError {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> Timings for SubGhzRadio<'d> {
|
||||
impl<'d, RS> Timings for SubGhzRadio<'d, RS> {
|
||||
fn get_rx_window_offset_ms(&self) -> i32 {
|
||||
-200
|
||||
-500
|
||||
}
|
||||
fn get_rx_window_duration_ms(&self) -> u32 {
|
||||
800
|
||||
3000
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the radio switch found on STM32WL based boards, used to control the radio for transmission or reception.
|
||||
pub struct RadioSwitch<'d> {
|
||||
ctrl1: Output<'d, AnyPin>,
|
||||
ctrl2: Output<'d, AnyPin>,
|
||||
ctrl3: Output<'d, AnyPin>,
|
||||
pub trait RadioSwitch {
|
||||
fn set_rx(&mut self);
|
||||
fn set_tx(&mut self);
|
||||
}
|
||||
|
||||
impl<'d> RadioSwitch<'d> {
|
||||
pub fn new(ctrl1: Output<'d, AnyPin>, ctrl2: Output<'d, AnyPin>, ctrl3: Output<'d, AnyPin>) -> Self {
|
||||
Self { ctrl1, ctrl2, ctrl3 }
|
||||
}
|
||||
|
||||
pub(crate) fn set_rx(&mut self) {
|
||||
self.ctrl1.set_high();
|
||||
self.ctrl2.set_low();
|
||||
self.ctrl3.set_high();
|
||||
}
|
||||
|
||||
pub(crate) fn set_tx_lp(&mut self) {
|
||||
self.ctrl1.set_high();
|
||||
self.ctrl2.set_high();
|
||||
self.ctrl3.set_high();
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn set_tx_hp(&mut self) {
|
||||
self.ctrl2.set_high();
|
||||
self.ctrl1.set_low();
|
||||
self.ctrl3.set_high();
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_spreading_factor(sf: SpreadingFactor) -> SF {
|
||||
fn convert_spreading_factor(sf: &SpreadingFactor) -> SF {
|
||||
match sf {
|
||||
SpreadingFactor::_7 => SF::Sf7,
|
||||
SpreadingFactor::_8 => SF::Sf8,
|
||||
@ -330,7 +279,7 @@ fn convert_spreading_factor(sf: SpreadingFactor) -> SF {
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_bandwidth(bw: Bandwidth) -> LoRaBandwidth {
|
||||
fn convert_bandwidth(bw: &Bandwidth) -> LoRaBandwidth {
|
||||
match bw {
|
||||
Bandwidth::_125KHz => LoRaBandwidth::Bw125,
|
||||
Bandwidth::_250KHz => LoRaBandwidth::Bw250,
|
||||
|
153
embassy-lora/src/sx126x/mod.rs
Normal file
153
embassy-lora/src/sx126x/mod.rs
Normal file
@ -0,0 +1,153 @@
|
||||
use core::future::Future;
|
||||
|
||||
use defmt::Format;
|
||||
use embedded_hal::digital::v2::OutputPin;
|
||||
use embedded_hal_async::digital::Wait;
|
||||
use embedded_hal_async::spi::*;
|
||||
use lorawan_device::async_device::radio::{PhyRxTx, RfConfig, RxQuality, TxConfig};
|
||||
use lorawan_device::async_device::Timings;
|
||||
|
||||
mod sx126x_lora;
|
||||
use sx126x_lora::LoRa;
|
||||
|
||||
use self::sx126x_lora::mod_params::RadioError;
|
||||
|
||||
/// Semtech Sx126x LoRa peripheral
|
||||
pub struct Sx126xRadio<SPI, CTRL, WAIT, BUS>
|
||||
where
|
||||
SPI: SpiBus<u8, Error = BUS> + 'static,
|
||||
CTRL: OutputPin + 'static,
|
||||
WAIT: Wait + 'static,
|
||||
BUS: Error + Format + 'static,
|
||||
{
|
||||
pub lora: LoRa<SPI, CTRL, WAIT>,
|
||||
}
|
||||
|
||||
impl<SPI, CTRL, WAIT, BUS> Sx126xRadio<SPI, CTRL, WAIT, BUS>
|
||||
where
|
||||
SPI: SpiBus<u8, Error = BUS> + 'static,
|
||||
CTRL: OutputPin + 'static,
|
||||
WAIT: Wait + 'static,
|
||||
BUS: Error + Format + 'static,
|
||||
{
|
||||
pub async fn new(
|
||||
spi: SPI,
|
||||
cs: CTRL,
|
||||
reset: CTRL,
|
||||
antenna_rx: CTRL,
|
||||
antenna_tx: CTRL,
|
||||
dio1: WAIT,
|
||||
busy: WAIT,
|
||||
enable_public_network: bool,
|
||||
) -> Result<Self, RadioError<BUS>> {
|
||||
let mut lora = LoRa::new(spi, cs, reset, antenna_rx, antenna_tx, dio1, busy);
|
||||
lora.init().await?;
|
||||
lora.set_lora_modem(enable_public_network).await?;
|
||||
Ok(Self { lora })
|
||||
}
|
||||
}
|
||||
|
||||
impl<SPI, CTRL, WAIT, BUS> Timings for Sx126xRadio<SPI, CTRL, WAIT, BUS>
|
||||
where
|
||||
SPI: SpiBus<u8, Error = BUS> + 'static,
|
||||
CTRL: OutputPin + 'static,
|
||||
WAIT: Wait + 'static,
|
||||
BUS: Error + Format + 'static,
|
||||
{
|
||||
fn get_rx_window_offset_ms(&self) -> i32 {
|
||||
-500
|
||||
}
|
||||
fn get_rx_window_duration_ms(&self) -> u32 {
|
||||
800
|
||||
}
|
||||
}
|
||||
|
||||
impl<SPI, CTRL, WAIT, BUS> PhyRxTx for Sx126xRadio<SPI, CTRL, WAIT, BUS>
|
||||
where
|
||||
SPI: SpiBus<u8, Error = BUS> + 'static,
|
||||
CTRL: OutputPin + 'static,
|
||||
WAIT: Wait + 'static,
|
||||
BUS: Error + Format + 'static,
|
||||
{
|
||||
type PhyError = RadioError<BUS>;
|
||||
|
||||
type TxFuture<'m> = impl Future<Output = Result<u32, Self::PhyError>> + 'm
|
||||
where
|
||||
SPI: 'm,
|
||||
CTRL: 'm,
|
||||
WAIT: 'm,
|
||||
BUS: 'm;
|
||||
|
||||
fn tx<'m>(&'m mut self, config: TxConfig, buffer: &'m [u8]) -> Self::TxFuture<'m> {
|
||||
trace!("TX START");
|
||||
async move {
|
||||
self.lora
|
||||
.set_tx_config(
|
||||
config.pw,
|
||||
config.rf.spreading_factor.into(),
|
||||
config.rf.bandwidth.into(),
|
||||
config.rf.coding_rate.into(),
|
||||
4,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
0,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
self.lora.set_max_payload_length(buffer.len() as u8).await?;
|
||||
self.lora.set_channel(config.rf.frequency).await?;
|
||||
self.lora.send(buffer, 0xffffff).await?;
|
||||
self.lora.process_irq(None, None, None).await?;
|
||||
trace!("TX DONE");
|
||||
return Ok(0);
|
||||
}
|
||||
}
|
||||
|
||||
type RxFuture<'m> = impl Future<Output = Result<(usize, RxQuality), Self::PhyError>> + 'm
|
||||
where
|
||||
SPI: 'm,
|
||||
CTRL: 'm,
|
||||
WAIT: 'm,
|
||||
BUS: 'm;
|
||||
|
||||
fn rx<'m>(&'m mut self, config: RfConfig, receiving_buffer: &'m mut [u8]) -> Self::RxFuture<'m> {
|
||||
trace!("RX START");
|
||||
async move {
|
||||
self.lora
|
||||
.set_rx_config(
|
||||
config.spreading_factor.into(),
|
||||
config.bandwidth.into(),
|
||||
config.coding_rate.into(),
|
||||
4,
|
||||
4,
|
||||
false,
|
||||
0u8,
|
||||
true,
|
||||
false,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
self.lora.set_max_payload_length(receiving_buffer.len() as u8).await?;
|
||||
self.lora.set_channel(config.frequency).await?;
|
||||
self.lora.rx(90 * 1000).await?;
|
||||
let mut received_len = 0u8;
|
||||
self.lora
|
||||
.process_irq(Some(receiving_buffer), Some(&mut received_len), None)
|
||||
.await?;
|
||||
trace!("RX DONE");
|
||||
|
||||
let packet_status = self.lora.get_latest_packet_status();
|
||||
let mut rssi = 0i16;
|
||||
let mut snr = 0i8;
|
||||
if packet_status.is_some() {
|
||||
rssi = packet_status.unwrap().rssi as i16;
|
||||
snr = packet_status.unwrap().snr;
|
||||
}
|
||||
|
||||
Ok((received_len as usize, RxQuality::new(rssi, snr)))
|
||||
}
|
||||
}
|
||||
}
|
256
embassy-lora/src/sx126x/sx126x_lora/board_specific.rs
Normal file
256
embassy-lora/src/sx126x/sx126x_lora/board_specific.rs
Normal file
@ -0,0 +1,256 @@
|
||||
use embassy_time::{Duration, Timer};
|
||||
use embedded_hal::digital::v2::OutputPin;
|
||||
use embedded_hal_async::digital::Wait;
|
||||
use embedded_hal_async::spi::SpiBus;
|
||||
|
||||
use super::mod_params::RadioError::*;
|
||||
use super::mod_params::*;
|
||||
use super::LoRa;
|
||||
|
||||
// Defines the time required for the TCXO to wakeup [ms].
|
||||
const BRD_TCXO_WAKEUP_TIME: u32 = 10;
|
||||
|
||||
// Provides board-specific functionality for Semtech SX126x-based boards.
|
||||
|
||||
impl<SPI, CTRL, WAIT, BUS> LoRa<SPI, CTRL, WAIT>
|
||||
where
|
||||
SPI: SpiBus<u8, Error = BUS>,
|
||||
CTRL: OutputPin,
|
||||
WAIT: Wait,
|
||||
{
|
||||
// De-initialize the radio I/Os pins interface. Useful when going into MCU low power modes.
|
||||
pub(super) async fn brd_io_deinit(&mut self) -> Result<(), RadioError<BUS>> {
|
||||
Ok(()) // no operation currently
|
||||
}
|
||||
|
||||
// Initialize the TCXO power pin
|
||||
pub(super) async fn brd_io_tcxo_init(&mut self) -> Result<(), RadioError<BUS>> {
|
||||
let timeout = self.brd_get_board_tcxo_wakeup_time() << 6;
|
||||
self.sub_set_dio3_as_tcxo_ctrl(TcxoCtrlVoltage::Ctrl1V7, timeout)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Initialize RF switch control pins
|
||||
pub(super) async fn brd_io_rf_switch_init(&mut self) -> Result<(), RadioError<BUS>> {
|
||||
self.sub_set_dio2_as_rf_switch_ctrl(true).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Initialize the radio debug pins
|
||||
pub(super) async fn brd_io_dbg_init(&mut self) -> Result<(), RadioError<BUS>> {
|
||||
Ok(()) // no operation currently
|
||||
}
|
||||
|
||||
// Hardware reset of the radio
|
||||
pub(super) async fn brd_reset(&mut self) -> Result<(), RadioError<BUS>> {
|
||||
Timer::after(Duration::from_millis(10)).await;
|
||||
self.reset.set_low().map_err(|_| Reset)?;
|
||||
Timer::after(Duration::from_millis(20)).await;
|
||||
self.reset.set_high().map_err(|_| Reset)?;
|
||||
Timer::after(Duration::from_millis(10)).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Wait while the busy pin is high
|
||||
pub(super) async fn brd_wait_on_busy(&mut self) -> Result<(), RadioError<BUS>> {
|
||||
self.busy.wait_for_low().await.map_err(|_| Busy)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Wake up the radio
|
||||
pub(super) async fn brd_wakeup(&mut self) -> Result<(), RadioError<BUS>> {
|
||||
self.cs.set_low().map_err(|_| CS)?;
|
||||
self.spi.write(&[OpCode::GetStatus.value()]).await.map_err(SPI)?;
|
||||
self.spi.write(&[0x00]).await.map_err(SPI)?;
|
||||
self.cs.set_high().map_err(|_| CS)?;
|
||||
|
||||
self.brd_wait_on_busy().await?;
|
||||
self.brd_set_operating_mode(RadioMode::StandbyRC);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Send a command that writes data to the radio
|
||||
pub(super) async fn brd_write_command(&mut self, op_code: OpCode, buffer: &[u8]) -> Result<(), RadioError<BUS>> {
|
||||
self.sub_check_device_ready().await?;
|
||||
|
||||
self.cs.set_low().map_err(|_| CS)?;
|
||||
self.spi.write(&[op_code.value()]).await.map_err(SPI)?;
|
||||
self.spi.write(buffer).await.map_err(SPI)?;
|
||||
self.cs.set_high().map_err(|_| CS)?;
|
||||
|
||||
if op_code != OpCode::SetSleep {
|
||||
self.brd_wait_on_busy().await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Send a command that reads data from the radio, filling the provided buffer and returning a status
|
||||
pub(super) async fn brd_read_command(&mut self, op_code: OpCode, buffer: &mut [u8]) -> Result<u8, RadioError<BUS>> {
|
||||
let mut status = [0u8];
|
||||
let mut input = [0u8];
|
||||
|
||||
self.sub_check_device_ready().await?;
|
||||
|
||||
self.cs.set_low().map_err(|_| CS)?;
|
||||
self.spi.write(&[op_code.value()]).await.map_err(SPI)?;
|
||||
self.spi.transfer(&mut status, &[0x00]).await.map_err(SPI)?;
|
||||
for i in 0..buffer.len() {
|
||||
self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?;
|
||||
buffer[i] = input[0];
|
||||
}
|
||||
self.cs.set_high().map_err(|_| CS)?;
|
||||
|
||||
self.brd_wait_on_busy().await?;
|
||||
|
||||
Ok(status[0])
|
||||
}
|
||||
|
||||
// Write one or more bytes of data to the radio memory
|
||||
pub(super) async fn brd_write_registers(
|
||||
&mut self,
|
||||
start_register: Register,
|
||||
buffer: &[u8],
|
||||
) -> Result<(), RadioError<BUS>> {
|
||||
self.sub_check_device_ready().await?;
|
||||
|
||||
self.cs.set_low().map_err(|_| CS)?;
|
||||
self.spi.write(&[OpCode::WriteRegister.value()]).await.map_err(SPI)?;
|
||||
self.spi
|
||||
.write(&[
|
||||
((start_register.addr() & 0xFF00) >> 8) as u8,
|
||||
(start_register.addr() & 0x00FF) as u8,
|
||||
])
|
||||
.await
|
||||
.map_err(SPI)?;
|
||||
self.spi.write(buffer).await.map_err(SPI)?;
|
||||
self.cs.set_high().map_err(|_| CS)?;
|
||||
|
||||
self.brd_wait_on_busy().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Read one or more bytes of data from the radio memory
|
||||
pub(super) async fn brd_read_registers(
|
||||
&mut self,
|
||||
start_register: Register,
|
||||
buffer: &mut [u8],
|
||||
) -> Result<(), RadioError<BUS>> {
|
||||
let mut input = [0u8];
|
||||
|
||||
self.sub_check_device_ready().await?;
|
||||
|
||||
self.cs.set_low().map_err(|_| CS)?;
|
||||
self.spi.write(&[OpCode::ReadRegister.value()]).await.map_err(SPI)?;
|
||||
self.spi
|
||||
.write(&[
|
||||
((start_register.addr() & 0xFF00) >> 8) as u8,
|
||||
(start_register.addr() & 0x00FF) as u8,
|
||||
0x00u8,
|
||||
])
|
||||
.await
|
||||
.map_err(SPI)?;
|
||||
for i in 0..buffer.len() {
|
||||
self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?;
|
||||
buffer[i] = input[0];
|
||||
}
|
||||
self.cs.set_high().map_err(|_| CS)?;
|
||||
|
||||
self.brd_wait_on_busy().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Write data to the buffer holding the payload in the radio
|
||||
pub(super) async fn brd_write_buffer(&mut self, offset: u8, buffer: &[u8]) -> Result<(), RadioError<BUS>> {
|
||||
self.sub_check_device_ready().await?;
|
||||
|
||||
self.cs.set_low().map_err(|_| CS)?;
|
||||
self.spi.write(&[OpCode::WriteBuffer.value()]).await.map_err(SPI)?;
|
||||
self.spi.write(&[offset]).await.map_err(SPI)?;
|
||||
self.spi.write(buffer).await.map_err(SPI)?;
|
||||
self.cs.set_high().map_err(|_| CS)?;
|
||||
|
||||
self.brd_wait_on_busy().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Read data from the buffer holding the payload in the radio
|
||||
pub(super) async fn brd_read_buffer(&mut self, offset: u8, buffer: &mut [u8]) -> Result<(), RadioError<BUS>> {
|
||||
let mut input = [0u8];
|
||||
|
||||
self.sub_check_device_ready().await?;
|
||||
|
||||
self.cs.set_low().map_err(|_| CS)?;
|
||||
self.spi.write(&[OpCode::ReadBuffer.value()]).await.map_err(SPI)?;
|
||||
self.spi.write(&[offset]).await.map_err(SPI)?;
|
||||
self.spi.write(&[0x00]).await.map_err(SPI)?;
|
||||
for i in 0..buffer.len() {
|
||||
self.spi.transfer(&mut input, &[0x00]).await.map_err(SPI)?;
|
||||
buffer[i] = input[0];
|
||||
}
|
||||
self.cs.set_high().map_err(|_| CS)?;
|
||||
|
||||
self.brd_wait_on_busy().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Set the radio output power
|
||||
pub(super) async fn brd_set_rf_tx_power(&mut self, power: i8) -> Result<(), RadioError<BUS>> {
|
||||
self.sub_set_tx_params(power, RampTime::Ramp40Us).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Get the radio type
|
||||
pub(super) fn brd_get_radio_type(&mut self) -> RadioType {
|
||||
RadioType::SX1262
|
||||
}
|
||||
|
||||
// Quiesce the antenna(s).
|
||||
pub(super) fn brd_ant_sleep(&mut self) -> Result<(), RadioError<BUS>> {
|
||||
self.antenna_tx.set_low().map_err(|_| AntTx)?;
|
||||
self.antenna_rx.set_low().map_err(|_| AntRx)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Prepare the antenna(s) for a receive operation
|
||||
pub(super) fn brd_ant_set_rx(&mut self) -> Result<(), RadioError<BUS>> {
|
||||
self.antenna_tx.set_low().map_err(|_| AntTx)?;
|
||||
self.antenna_rx.set_high().map_err(|_| AntRx)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Prepare the antenna(s) for a send operation
|
||||
pub(super) fn brd_ant_set_tx(&mut self) -> Result<(), RadioError<BUS>> {
|
||||
self.antenna_rx.set_low().map_err(|_| AntRx)?;
|
||||
self.antenna_tx.set_high().map_err(|_| AntTx)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Check if the given RF frequency is supported by the hardware
|
||||
pub(super) async fn brd_check_rf_frequency(&mut self, _frequency: u32) -> Result<bool, RadioError<BUS>> {
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
// Get the duration required for the TCXO to wakeup [ms].
|
||||
pub(super) fn brd_get_board_tcxo_wakeup_time(&mut self) -> u32 {
|
||||
BRD_TCXO_WAKEUP_TIME
|
||||
}
|
||||
|
||||
/* Get current state of the DIO1 pin - not currently needed if waiting on DIO1 instead of using an IRQ process
|
||||
pub(super) async fn brd_get_dio1_pin_state(
|
||||
&mut self,
|
||||
) -> Result<u32, RadioError<BUS>> {
|
||||
Ok(0)
|
||||
}
|
||||
*/
|
||||
|
||||
// Get the current radio operatiing mode
|
||||
pub(super) fn brd_get_operating_mode(&mut self) -> RadioMode {
|
||||
self.operating_mode
|
||||
}
|
||||
|
||||
// Set/Update the current radio operating mode This function is only required to reflect the current radio operating mode when processing interrupts.
|
||||
pub(super) fn brd_set_operating_mode(&mut self, mode: RadioMode) {
|
||||
self.operating_mode = mode;
|
||||
}
|
||||
}
|
732
embassy-lora/src/sx126x/sx126x_lora/mod.rs
Normal file
732
embassy-lora/src/sx126x/sx126x_lora/mod.rs
Normal file
@ -0,0 +1,732 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use embassy_time::{Duration, Timer};
|
||||
use embedded_hal::digital::v2::OutputPin;
|
||||
use embedded_hal_async::digital::Wait;
|
||||
use embedded_hal_async::spi::SpiBus;
|
||||
|
||||
mod board_specific;
|
||||
pub mod mod_params;
|
||||
mod subroutine;
|
||||
|
||||
use mod_params::RadioError::*;
|
||||
use mod_params::*;
|
||||
|
||||
// Syncwords for public and private networks
|
||||
const LORA_MAC_PUBLIC_SYNCWORD: u16 = 0x3444;
|
||||
const LORA_MAC_PRIVATE_SYNCWORD: u16 = 0x1424;
|
||||
|
||||
// Maximum number of registers that can be added to the retention list
|
||||
const MAX_NUMBER_REGS_IN_RETENTION: u8 = 4;
|
||||
|
||||
// Possible LoRa bandwidths
|
||||
const LORA_BANDWIDTHS: [Bandwidth; 3] = [Bandwidth::_125KHz, Bandwidth::_250KHz, Bandwidth::_500KHz];
|
||||
|
||||
// Radio complete wakeup time with margin for temperature compensation [ms]
|
||||
const RADIO_WAKEUP_TIME: u32 = 3;
|
||||
|
||||
/// Provides high-level access to Semtech SX126x-based boards
|
||||
pub struct LoRa<SPI, CTRL, WAIT> {
|
||||
spi: SPI,
|
||||
cs: CTRL,
|
||||
reset: CTRL,
|
||||
antenna_rx: CTRL,
|
||||
antenna_tx: CTRL,
|
||||
dio1: WAIT,
|
||||
busy: WAIT,
|
||||
operating_mode: RadioMode,
|
||||
rx_continuous: bool,
|
||||
max_payload_length: u8,
|
||||
modulation_params: Option<ModulationParams>,
|
||||
packet_type: PacketType,
|
||||
packet_params: Option<PacketParams>,
|
||||
packet_status: Option<PacketStatus>,
|
||||
image_calibrated: bool,
|
||||
frequency_error: u32,
|
||||
}
|
||||
|
||||
impl<SPI, CTRL, WAIT, BUS> LoRa<SPI, CTRL, WAIT>
|
||||
where
|
||||
SPI: SpiBus<u8, Error = BUS>,
|
||||
CTRL: OutputPin,
|
||||
WAIT: Wait,
|
||||
{
|
||||
/// Builds and returns a new instance of the radio. Only one instance of the radio should exist at a time ()
|
||||
pub fn new(spi: SPI, cs: CTRL, reset: CTRL, antenna_rx: CTRL, antenna_tx: CTRL, dio1: WAIT, busy: WAIT) -> Self {
|
||||
Self {
|
||||
spi,
|
||||
cs,
|
||||
reset,
|
||||
antenna_rx,
|
||||
antenna_tx,
|
||||
dio1,
|
||||
busy,
|
||||
operating_mode: RadioMode::Sleep,
|
||||
rx_continuous: false,
|
||||
max_payload_length: 0xFFu8,
|
||||
modulation_params: None,
|
||||
packet_type: PacketType::LoRa,
|
||||
packet_params: None,
|
||||
packet_status: None,
|
||||
image_calibrated: false,
|
||||
frequency_error: 0u32, // where is volatile FrequencyError modified ???
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize the radio
|
||||
pub async fn init(&mut self) -> Result<(), RadioError<BUS>> {
|
||||
self.sub_init().await?;
|
||||
self.sub_set_standby(StandbyMode::RC).await?;
|
||||
self.sub_set_regulator_mode(RegulatorMode::UseDCDC).await?;
|
||||
self.sub_set_buffer_base_address(0x00u8, 0x00u8).await?;
|
||||
self.sub_set_tx_params(0i8, RampTime::Ramp200Us).await?;
|
||||
self.sub_set_dio_irq_params(
|
||||
IrqMask::All.value(),
|
||||
IrqMask::All.value(),
|
||||
IrqMask::None.value(),
|
||||
IrqMask::None.value(),
|
||||
)
|
||||
.await?;
|
||||
self.add_register_to_retention_list(Register::RxGain.addr()).await?;
|
||||
self.add_register_to_retention_list(Register::TxModulation.addr())
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Return current radio state
|
||||
pub fn get_status(&mut self) -> RadioState {
|
||||
match self.brd_get_operating_mode() {
|
||||
RadioMode::Transmit => RadioState::TxRunning,
|
||||
RadioMode::Receive => RadioState::RxRunning,
|
||||
RadioMode::ChannelActivityDetection => RadioState::ChannelActivityDetecting,
|
||||
_ => RadioState::Idle,
|
||||
}
|
||||
}
|
||||
|
||||
/// Configure the radio for LoRa (FSK support should be provided in a separate driver, if desired)
|
||||
pub async fn set_lora_modem(&mut self, enable_public_network: bool) -> Result<(), RadioError<BUS>> {
|
||||
self.sub_set_packet_type(PacketType::LoRa).await?;
|
||||
if enable_public_network {
|
||||
self.brd_write_registers(
|
||||
Register::LoRaSyncword,
|
||||
&[
|
||||
((LORA_MAC_PUBLIC_SYNCWORD >> 8) & 0xFF) as u8,
|
||||
(LORA_MAC_PUBLIC_SYNCWORD & 0xFF) as u8,
|
||||
],
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
self.brd_write_registers(
|
||||
Register::LoRaSyncword,
|
||||
&[
|
||||
((LORA_MAC_PRIVATE_SYNCWORD >> 8) & 0xFF) as u8,
|
||||
(LORA_MAC_PRIVATE_SYNCWORD & 0xFF) as u8,
|
||||
],
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets the channel frequency
|
||||
pub async fn set_channel(&mut self, frequency: u32) -> Result<(), RadioError<BUS>> {
|
||||
self.sub_set_rf_frequency(frequency).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/* Checks if the channel is free for the given time. This is currently not implemented until a substitute
|
||||
for switching to the FSK modem is found.
|
||||
|
||||
pub async fn is_channel_free(&mut self, frequency: u32, rxBandwidth: u32, rssiThresh: i16, maxCarrierSenseTime: u32) -> bool;
|
||||
*/
|
||||
|
||||
/// Generate a 32 bit random value based on the RSSI readings, after disabling all interrupts. Ensure set_lora_modem() is called befrorehand.
|
||||
/// After calling this function either set_rx_config() or set_tx_config() must be called.
|
||||
pub async fn get_random_value(&mut self) -> Result<u32, RadioError<BUS>> {
|
||||
self.sub_set_dio_irq_params(
|
||||
IrqMask::None.value(),
|
||||
IrqMask::None.value(),
|
||||
IrqMask::None.value(),
|
||||
IrqMask::None.value(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let result = self.sub_get_random().await?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Set the reception parameters for the LoRa modem (only). Ensure set_lora_modem() is called befrorehand.
|
||||
/// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol]
|
||||
/// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved]
|
||||
/// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
|
||||
/// preamble_length length in symbols (the hardware adds 4 more symbols)
|
||||
/// symb_timeout RxSingle timeout value in symbols
|
||||
/// fixed_len fixed length packets [0: variable, 1: fixed]
|
||||
/// payload_len payload length when fixed length is used
|
||||
/// crc_on [0: OFF, 1: ON]
|
||||
/// freq_hop_on intra-packet frequency hopping [0: OFF, 1: ON]
|
||||
/// hop_period number of symbols between each hop
|
||||
/// iq_inverted invert IQ signals [0: not inverted, 1: inverted]
|
||||
/// rx_continuous reception mode [false: single mode, true: continuous mode]
|
||||
pub async fn set_rx_config(
|
||||
&mut self,
|
||||
spreading_factor: SpreadingFactor,
|
||||
bandwidth: Bandwidth,
|
||||
coding_rate: CodingRate,
|
||||
preamble_length: u16,
|
||||
symb_timeout: u16,
|
||||
fixed_len: bool,
|
||||
payload_len: u8,
|
||||
crc_on: bool,
|
||||
_freq_hop_on: bool,
|
||||
_hop_period: u8,
|
||||
iq_inverted: bool,
|
||||
rx_continuous: bool,
|
||||
) -> Result<(), RadioError<BUS>> {
|
||||
let mut symb_timeout_final = symb_timeout;
|
||||
|
||||
self.rx_continuous = rx_continuous;
|
||||
if self.rx_continuous {
|
||||
symb_timeout_final = 0;
|
||||
}
|
||||
if fixed_len {
|
||||
self.max_payload_length = payload_len;
|
||||
} else {
|
||||
self.max_payload_length = 0xFFu8;
|
||||
}
|
||||
|
||||
self.sub_set_stop_rx_timer_on_preamble_detect(false).await?;
|
||||
|
||||
let mut low_data_rate_optimize = 0x00u8;
|
||||
if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12))
|
||||
&& (bandwidth == Bandwidth::_125KHz))
|
||||
|| ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz))
|
||||
{
|
||||
low_data_rate_optimize = 0x01u8;
|
||||
}
|
||||
|
||||
let modulation_params = ModulationParams {
|
||||
spreading_factor: spreading_factor,
|
||||
bandwidth: bandwidth,
|
||||
coding_rate: coding_rate,
|
||||
low_data_rate_optimize: low_data_rate_optimize,
|
||||
};
|
||||
|
||||
let mut preamble_length_final = preamble_length;
|
||||
if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6))
|
||||
&& (preamble_length < 12)
|
||||
{
|
||||
preamble_length_final = 12;
|
||||
}
|
||||
|
||||
let packet_params = PacketParams {
|
||||
preamble_length: preamble_length_final,
|
||||
implicit_header: fixed_len,
|
||||
payload_length: self.max_payload_length,
|
||||
crc_on: crc_on,
|
||||
iq_inverted: iq_inverted,
|
||||
};
|
||||
|
||||
self.modulation_params = Some(modulation_params);
|
||||
self.packet_params = Some(packet_params);
|
||||
|
||||
self.standby().await?;
|
||||
self.sub_set_modulation_params().await?;
|
||||
self.sub_set_packet_params().await?;
|
||||
self.sub_set_lora_symb_num_timeout(symb_timeout_final).await?;
|
||||
|
||||
// Optimize the Inverted IQ Operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4)
|
||||
let mut iq_polarity = [0x00u8];
|
||||
self.brd_read_registers(Register::IQPolarity, &mut iq_polarity).await?;
|
||||
if iq_inverted {
|
||||
self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] & (!(1 << 2))])
|
||||
.await?;
|
||||
} else {
|
||||
self.brd_write_registers(Register::IQPolarity, &[iq_polarity[0] | (1 << 2)])
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the transmission parameters for the LoRa modem (only).
|
||||
/// power output power [dBm]
|
||||
/// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol]
|
||||
/// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved]
|
||||
/// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
|
||||
/// preamble_length length in symbols (the hardware adds 4 more symbols)
|
||||
/// fixed_len fixed length packets [0: variable, 1: fixed]
|
||||
/// crc_on [0: OFF, 1: ON]
|
||||
/// freq_hop_on intra-packet frequency hopping [0: OFF, 1: ON]
|
||||
/// hop_period number of symbols between each hop
|
||||
/// iq_inverted invert IQ signals [0: not inverted, 1: inverted]
|
||||
pub async fn set_tx_config(
|
||||
&mut self,
|
||||
power: i8,
|
||||
spreading_factor: SpreadingFactor,
|
||||
bandwidth: Bandwidth,
|
||||
coding_rate: CodingRate,
|
||||
preamble_length: u16,
|
||||
fixed_len: bool,
|
||||
crc_on: bool,
|
||||
_freq_hop_on: bool,
|
||||
_hop_period: u8,
|
||||
iq_inverted: bool,
|
||||
) -> Result<(), RadioError<BUS>> {
|
||||
let mut low_data_rate_optimize = 0x00u8;
|
||||
if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12))
|
||||
&& (bandwidth == Bandwidth::_125KHz))
|
||||
|| ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz))
|
||||
{
|
||||
low_data_rate_optimize = 0x01u8;
|
||||
}
|
||||
|
||||
let modulation_params = ModulationParams {
|
||||
spreading_factor: spreading_factor,
|
||||
bandwidth: bandwidth,
|
||||
coding_rate: coding_rate,
|
||||
low_data_rate_optimize: low_data_rate_optimize,
|
||||
};
|
||||
|
||||
let mut preamble_length_final = preamble_length;
|
||||
if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6))
|
||||
&& (preamble_length < 12)
|
||||
{
|
||||
preamble_length_final = 12;
|
||||
}
|
||||
|
||||
let packet_params = PacketParams {
|
||||
preamble_length: preamble_length_final,
|
||||
implicit_header: fixed_len,
|
||||
payload_length: self.max_payload_length,
|
||||
crc_on: crc_on,
|
||||
iq_inverted: iq_inverted,
|
||||
};
|
||||
|
||||
self.modulation_params = Some(modulation_params);
|
||||
self.packet_params = Some(packet_params);
|
||||
|
||||
self.standby().await?;
|
||||
self.sub_set_modulation_params().await?;
|
||||
self.sub_set_packet_params().await?;
|
||||
|
||||
// Handle modulation quality with the 500 kHz LoRa bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1)
|
||||
|
||||
let mut tx_modulation = [0x00u8];
|
||||
self.brd_read_registers(Register::TxModulation, &mut tx_modulation)
|
||||
.await?;
|
||||
if bandwidth == Bandwidth::_500KHz {
|
||||
self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] & (!(1 << 2))])
|
||||
.await?;
|
||||
} else {
|
||||
self.brd_write_registers(Register::TxModulation, &[tx_modulation[0] | (1 << 2)])
|
||||
.await?;
|
||||
}
|
||||
|
||||
self.brd_set_rf_tx_power(power).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check if the given RF frequency is supported by the hardware [true: supported, false: unsupported]
|
||||
pub async fn check_rf_frequency(&mut self, frequency: u32) -> Result<bool, RadioError<BUS>> {
|
||||
Ok(self.brd_check_rf_frequency(frequency).await?)
|
||||
}
|
||||
|
||||
/// Computes the packet time on air in ms for the given payload for a LoRa modem (can only be called once set_rx_config or set_tx_config have been called)
|
||||
/// spreading_factor [6: 64, 7: 128, 8: 256, 9: 512, 10: 1024, 11: 2048, 12: 4096 chips/symbol]
|
||||
/// bandwidth [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved]
|
||||
/// coding_rate [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8]
|
||||
/// preamble_length length in symbols (the hardware adds 4 more symbols)
|
||||
/// fixed_len fixed length packets [0: variable, 1: fixed]
|
||||
/// payload_len sets payload length when fixed length is used
|
||||
/// crc_on [0: OFF, 1: ON]
|
||||
pub fn get_time_on_air(
|
||||
&mut self,
|
||||
spreading_factor: SpreadingFactor,
|
||||
bandwidth: Bandwidth,
|
||||
coding_rate: CodingRate,
|
||||
preamble_length: u16,
|
||||
fixed_len: bool,
|
||||
payload_len: u8,
|
||||
crc_on: bool,
|
||||
) -> Result<u32, RadioError<BUS>> {
|
||||
let numerator = 1000
|
||||
* Self::get_lora_time_on_air_numerator(
|
||||
spreading_factor,
|
||||
bandwidth,
|
||||
coding_rate,
|
||||
preamble_length,
|
||||
fixed_len,
|
||||
payload_len,
|
||||
crc_on,
|
||||
);
|
||||
let denominator = bandwidth.value_in_hz();
|
||||
if denominator == 0 {
|
||||
Err(RadioError::InvalidBandwidth)
|
||||
} else {
|
||||
Ok((numerator + denominator - 1) / denominator)
|
||||
}
|
||||
}
|
||||
|
||||
/// Send the buffer of the given size. Prepares the packet to be sent and sets the radio in transmission [timeout in ms]
|
||||
pub async fn send(&mut self, buffer: &[u8], timeout: u32) -> Result<(), RadioError<BUS>> {
|
||||
if self.packet_params.is_some() {
|
||||
self.sub_set_dio_irq_params(
|
||||
IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(),
|
||||
IrqMask::TxDone.value() | IrqMask::RxTxTimeout.value(),
|
||||
IrqMask::None.value(),
|
||||
IrqMask::None.value(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut packet_params = self.packet_params.as_mut().unwrap();
|
||||
packet_params.payload_length = buffer.len() as u8;
|
||||
self.sub_set_packet_params().await?;
|
||||
self.sub_send_payload(buffer, timeout).await?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(RadioError::PacketParamsMissing)
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the radio in sleep mode
|
||||
pub async fn sleep(&mut self) -> Result<(), RadioError<BUS>> {
|
||||
self.sub_set_sleep(SleepParams {
|
||||
wakeup_rtc: false,
|
||||
reset: false,
|
||||
warm_start: true,
|
||||
})
|
||||
.await?;
|
||||
Timer::after(Duration::from_millis(2)).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the radio in standby mode
|
||||
pub async fn standby(&mut self) -> Result<(), RadioError<BUS>> {
|
||||
self.sub_set_standby(StandbyMode::RC).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the radio in reception mode for the given duration [0: continuous, others: timeout (ms)]
|
||||
pub async fn rx(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
|
||||
self.sub_set_dio_irq_params(
|
||||
IrqMask::All.value(),
|
||||
IrqMask::All.value(),
|
||||
IrqMask::None.value(),
|
||||
IrqMask::None.value(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
if self.rx_continuous {
|
||||
self.sub_set_rx(0xFFFFFF).await?;
|
||||
} else {
|
||||
self.sub_set_rx(timeout << 6).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Start a Channel Activity Detection
|
||||
pub async fn start_cad(&mut self) -> Result<(), RadioError<BUS>> {
|
||||
self.sub_set_dio_irq_params(
|
||||
IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(),
|
||||
IrqMask::CADDone.value() | IrqMask::CADActivityDetected.value(),
|
||||
IrqMask::None.value(),
|
||||
IrqMask::None.value(),
|
||||
)
|
||||
.await?;
|
||||
self.sub_set_cad().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets the radio in continuous wave transmission mode
|
||||
/// frequency channel RF frequency
|
||||
/// power output power [dBm]
|
||||
/// timeout transmission mode timeout [s]
|
||||
pub async fn set_tx_continuous_wave(
|
||||
&mut self,
|
||||
frequency: u32,
|
||||
power: i8,
|
||||
_timeout: u16,
|
||||
) -> Result<(), RadioError<BUS>> {
|
||||
self.sub_set_rf_frequency(frequency).await?;
|
||||
self.brd_set_rf_tx_power(power).await?;
|
||||
self.sub_set_tx_continuous_wave().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read the current RSSI value for the LoRa modem (only) [dBm]
|
||||
pub async fn get_rssi(&mut self) -> Result<i16, RadioError<BUS>> {
|
||||
let value = self.sub_get_rssi_inst().await?;
|
||||
Ok(value as i16)
|
||||
}
|
||||
|
||||
/// Write one or more radio registers with a buffer of a given size, starting at the first register address
|
||||
pub async fn write_registers_from_buffer(
|
||||
&mut self,
|
||||
start_register: Register,
|
||||
buffer: &[u8],
|
||||
) -> Result<(), RadioError<BUS>> {
|
||||
self.brd_write_registers(start_register, buffer).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read one or more radio registers into a buffer of a given size, starting at the first register address
|
||||
pub async fn read_registers_into_buffer(
|
||||
&mut self,
|
||||
start_register: Register,
|
||||
buffer: &mut [u8],
|
||||
) -> Result<(), RadioError<BUS>> {
|
||||
self.brd_read_registers(start_register, buffer).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the maximum payload length (in bytes) for a LoRa modem (only).
|
||||
pub async fn set_max_payload_length(&mut self, max: u8) -> Result<(), RadioError<BUS>> {
|
||||
if self.packet_params.is_some() {
|
||||
let packet_params = self.packet_params.as_mut().unwrap();
|
||||
self.max_payload_length = max;
|
||||
packet_params.payload_length = max;
|
||||
self.sub_set_packet_params().await?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(RadioError::PacketParamsMissing)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the time required for the board plus radio to get out of sleep [ms]
|
||||
pub fn get_wakeup_time(&mut self) -> u32 {
|
||||
self.brd_get_board_tcxo_wakeup_time() + RADIO_WAKEUP_TIME
|
||||
}
|
||||
|
||||
/// Process the radio irq
|
||||
pub async fn process_irq(
|
||||
&mut self,
|
||||
receiving_buffer: Option<&mut [u8]>,
|
||||
received_len: Option<&mut u8>,
|
||||
cad_activity_detected: Option<&mut bool>,
|
||||
) -> Result<(), RadioError<BUS>> {
|
||||
loop {
|
||||
trace!("process_irq loop entered");
|
||||
|
||||
let de = self.sub_get_device_errors().await?;
|
||||
trace!("device_errors: rc_64khz_calibration = {}, rc_13mhz_calibration = {}, pll_calibration = {}, adc_calibration = {}, image_calibration = {}, xosc_start = {}, pll_lock = {}, pa_ramp = {}",
|
||||
de.rc_64khz_calibration, de.rc_13mhz_calibration, de.pll_calibration, de.adc_calibration, de.image_calibration, de.xosc_start, de.pll_lock, de.pa_ramp);
|
||||
let st = self.sub_get_status().await?;
|
||||
trace!(
|
||||
"radio status: cmd_status: {:x}, chip_mode: {:x}",
|
||||
st.cmd_status,
|
||||
st.chip_mode
|
||||
);
|
||||
|
||||
self.dio1.wait_for_high().await.map_err(|_| DIO1)?;
|
||||
let operating_mode = self.brd_get_operating_mode();
|
||||
let irq_flags = self.sub_get_irq_status().await?;
|
||||
self.sub_clear_irq_status(irq_flags).await?;
|
||||
trace!("process_irq DIO1 satisfied: irq_flags = {:x}", irq_flags);
|
||||
|
||||
// check for errors and unexpected interrupt masks (based on operation mode)
|
||||
if (irq_flags & IrqMask::HeaderError.value()) == IrqMask::HeaderError.value() {
|
||||
if !self.rx_continuous {
|
||||
self.brd_set_operating_mode(RadioMode::StandbyRC);
|
||||
}
|
||||
return Err(RadioError::HeaderError);
|
||||
} else if (irq_flags & IrqMask::CRCError.value()) == IrqMask::CRCError.value() {
|
||||
if operating_mode == RadioMode::Receive {
|
||||
if !self.rx_continuous {
|
||||
self.brd_set_operating_mode(RadioMode::StandbyRC);
|
||||
}
|
||||
return Err(RadioError::CRCErrorOnReceive);
|
||||
} else {
|
||||
return Err(RadioError::CRCErrorUnexpected);
|
||||
}
|
||||
} else if (irq_flags & IrqMask::RxTxTimeout.value()) == IrqMask::RxTxTimeout.value() {
|
||||
if operating_mode == RadioMode::Transmit {
|
||||
self.brd_set_operating_mode(RadioMode::StandbyRC);
|
||||
return Err(RadioError::TransmitTimeout);
|
||||
} else if operating_mode == RadioMode::Receive {
|
||||
self.brd_set_operating_mode(RadioMode::StandbyRC);
|
||||
return Err(RadioError::ReceiveTimeout);
|
||||
} else {
|
||||
return Err(RadioError::TimeoutUnexpected);
|
||||
}
|
||||
} else if ((irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value())
|
||||
&& (operating_mode != RadioMode::Transmit)
|
||||
{
|
||||
return Err(RadioError::TransmitDoneUnexpected);
|
||||
} else if ((irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value())
|
||||
&& (operating_mode != RadioMode::Receive)
|
||||
{
|
||||
return Err(RadioError::ReceiveDoneUnexpected);
|
||||
} else if (((irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value())
|
||||
|| ((irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value()))
|
||||
&& (operating_mode != RadioMode::ChannelActivityDetection)
|
||||
{
|
||||
return Err(RadioError::CADUnexpected);
|
||||
}
|
||||
|
||||
if (irq_flags & IrqMask::HeaderValid.value()) == IrqMask::HeaderValid.value() {
|
||||
trace!("HeaderValid");
|
||||
} else if (irq_flags & IrqMask::PreambleDetected.value()) == IrqMask::PreambleDetected.value() {
|
||||
trace!("PreambleDetected");
|
||||
} else if (irq_flags & IrqMask::SyncwordValid.value()) == IrqMask::SyncwordValid.value() {
|
||||
trace!("SyncwordValid");
|
||||
}
|
||||
|
||||
// handle completions
|
||||
if (irq_flags & IrqMask::TxDone.value()) == IrqMask::TxDone.value() {
|
||||
self.brd_set_operating_mode(RadioMode::StandbyRC);
|
||||
return Ok(());
|
||||
} else if (irq_flags & IrqMask::RxDone.value()) == IrqMask::RxDone.value() {
|
||||
if !self.rx_continuous {
|
||||
self.brd_set_operating_mode(RadioMode::StandbyRC);
|
||||
|
||||
// implicit header mode timeout behavior (see DS_SX1261-2_V1.2 datasheet chapter 15.3)
|
||||
self.brd_write_registers(Register::RTCCtrl, &[0x00]).await?;
|
||||
let mut evt_clr = [0x00u8];
|
||||
self.brd_read_registers(Register::EvtClr, &mut evt_clr).await?;
|
||||
evt_clr[0] |= 1 << 1;
|
||||
self.brd_write_registers(Register::EvtClr, &evt_clr).await?;
|
||||
}
|
||||
|
||||
if receiving_buffer.is_some() && received_len.is_some() {
|
||||
*(received_len.unwrap()) = self.sub_get_payload(receiving_buffer.unwrap()).await?;
|
||||
}
|
||||
self.packet_status = self.sub_get_packet_status().await?.into();
|
||||
return Ok(());
|
||||
} else if (irq_flags & IrqMask::CADDone.value()) == IrqMask::CADDone.value() {
|
||||
if cad_activity_detected.is_some() {
|
||||
*(cad_activity_detected.unwrap()) =
|
||||
(irq_flags & IrqMask::CADActivityDetected.value()) == IrqMask::CADActivityDetected.value();
|
||||
}
|
||||
self.brd_set_operating_mode(RadioMode::StandbyRC);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// if DIO1 was driven high for reasons other than an error or operation completion (currently, PreambleDetected, SyncwordValid, and HeaderValid
|
||||
// are in that category), loop to wait again
|
||||
}
|
||||
}
|
||||
|
||||
// SX126x-specific functions
|
||||
|
||||
/// Set the radio in reception mode with Max LNA gain for the given time (SX126x radios only) [0: continuous, others timeout in ms]
|
||||
pub async fn set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
|
||||
self.sub_set_dio_irq_params(
|
||||
IrqMask::All.value(),
|
||||
IrqMask::All.value(),
|
||||
IrqMask::None.value(),
|
||||
IrqMask::None.value(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
if self.rx_continuous {
|
||||
self.sub_set_rx_boosted(0xFFFFFF).await?; // Rx continuous
|
||||
} else {
|
||||
self.sub_set_rx_boosted(timeout << 6).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the Rx duty cycle management parameters (SX126x radios only)
|
||||
/// rx_time structure describing reception timeout value
|
||||
/// sleep_time structure describing sleep timeout value
|
||||
pub async fn set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError<BUS>> {
|
||||
self.sub_set_rx_duty_cycle(rx_time, sleep_time).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_latest_packet_status(&mut self) -> Option<PacketStatus> {
|
||||
self.packet_status
|
||||
}
|
||||
|
||||
// Utilities
|
||||
|
||||
async fn add_register_to_retention_list(&mut self, register_address: u16) -> Result<(), RadioError<BUS>> {
|
||||
let mut buffer = [0x00u8; (1 + (2 * MAX_NUMBER_REGS_IN_RETENTION)) as usize];
|
||||
|
||||
// Read the address and registers already added to the list
|
||||
self.brd_read_registers(Register::RetentionList, &mut buffer).await?;
|
||||
|
||||
let number_of_registers = buffer[0];
|
||||
for i in 0..number_of_registers {
|
||||
if register_address
|
||||
== ((buffer[(1 + (2 * i)) as usize] as u16) << 8) | (buffer[(2 + (2 * i)) as usize] as u16)
|
||||
{
|
||||
return Ok(()); // register already in list
|
||||
}
|
||||
}
|
||||
|
||||
if number_of_registers < MAX_NUMBER_REGS_IN_RETENTION {
|
||||
buffer[0] += 1; // increment number of registers
|
||||
|
||||
buffer[(1 + (2 * number_of_registers)) as usize] = ((register_address >> 8) & 0xFF) as u8;
|
||||
buffer[(2 + (2 * number_of_registers)) as usize] = (register_address & 0xFF) as u8;
|
||||
self.brd_write_registers(Register::RetentionList, &buffer).await?;
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(RadioError::RetentionListExceeded)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_lora_time_on_air_numerator(
|
||||
spreading_factor: SpreadingFactor,
|
||||
bandwidth: Bandwidth,
|
||||
coding_rate: CodingRate,
|
||||
preamble_length: u16,
|
||||
fixed_len: bool,
|
||||
payload_len: u8,
|
||||
crc_on: bool,
|
||||
) -> u32 {
|
||||
let cell_denominator;
|
||||
let cr_denominator = (coding_rate.value() as i32) + 4;
|
||||
|
||||
// Ensure that the preamble length is at least 12 symbols when using SF5 or SF6
|
||||
let mut preamble_length_final = preamble_length;
|
||||
if ((spreading_factor == SpreadingFactor::_5) || (spreading_factor == SpreadingFactor::_6))
|
||||
&& (preamble_length < 12)
|
||||
{
|
||||
preamble_length_final = 12;
|
||||
}
|
||||
|
||||
let mut low_data_rate_optimize = false;
|
||||
if (((spreading_factor == SpreadingFactor::_11) || (spreading_factor == SpreadingFactor::_12))
|
||||
&& (bandwidth == Bandwidth::_125KHz))
|
||||
|| ((spreading_factor == SpreadingFactor::_12) && (bandwidth == Bandwidth::_250KHz))
|
||||
{
|
||||
low_data_rate_optimize = true;
|
||||
}
|
||||
|
||||
let mut cell_numerator = ((payload_len as i32) << 3) + (if crc_on { 16 } else { 0 })
|
||||
- (4 * spreading_factor.value() as i32)
|
||||
+ (if fixed_len { 0 } else { 20 });
|
||||
|
||||
if spreading_factor.value() <= 6 {
|
||||
cell_denominator = 4 * (spreading_factor.value() as i32);
|
||||
} else {
|
||||
cell_numerator += 8;
|
||||
if low_data_rate_optimize {
|
||||
cell_denominator = 4 * ((spreading_factor.value() as i32) - 2);
|
||||
} else {
|
||||
cell_denominator = 4 * (spreading_factor.value() as i32);
|
||||
}
|
||||
}
|
||||
|
||||
if cell_numerator < 0 {
|
||||
cell_numerator = 0;
|
||||
}
|
||||
|
||||
let mut intermediate: i32 = (((cell_numerator + cell_denominator - 1) / cell_denominator) * cr_denominator)
|
||||
+ (preamble_length_final as i32)
|
||||
+ 12;
|
||||
|
||||
if spreading_factor.value() <= 6 {
|
||||
intermediate = intermediate + 2;
|
||||
}
|
||||
|
||||
(((4 * intermediate) + 1) * (1 << (spreading_factor.value() - 2))) as u32
|
||||
}
|
||||
}
|
469
embassy-lora/src/sx126x/sx126x_lora/mod_params.rs
Normal file
469
embassy-lora/src/sx126x/sx126x_lora/mod_params.rs
Normal file
@ -0,0 +1,469 @@
|
||||
use core::fmt::Debug;
|
||||
|
||||
use lorawan_device::async_device::radio as device;
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum RadioError<BUS> {
|
||||
SPI(BUS),
|
||||
CS,
|
||||
Reset,
|
||||
AntRx,
|
||||
AntTx,
|
||||
Busy,
|
||||
DIO1,
|
||||
PayloadSizeMismatch(usize, usize),
|
||||
RetentionListExceeded,
|
||||
InvalidBandwidth,
|
||||
ModulationParamsMissing,
|
||||
PacketParamsMissing,
|
||||
HeaderError,
|
||||
CRCErrorUnexpected,
|
||||
CRCErrorOnReceive,
|
||||
TransmitTimeout,
|
||||
ReceiveTimeout,
|
||||
TimeoutUnexpected,
|
||||
TransmitDoneUnexpected,
|
||||
ReceiveDoneUnexpected,
|
||||
CADUnexpected,
|
||||
}
|
||||
|
||||
pub struct RadioSystemError {
|
||||
pub rc_64khz_calibration: bool,
|
||||
pub rc_13mhz_calibration: bool,
|
||||
pub pll_calibration: bool,
|
||||
pub adc_calibration: bool,
|
||||
pub image_calibration: bool,
|
||||
pub xosc_start: bool,
|
||||
pub pll_lock: bool,
|
||||
pub pa_ramp: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum PacketType {
|
||||
GFSK = 0x00,
|
||||
LoRa = 0x01,
|
||||
None = 0x0F,
|
||||
}
|
||||
|
||||
impl PacketType {
|
||||
pub const fn value(self) -> u8 {
|
||||
self as u8
|
||||
}
|
||||
pub fn to_enum(value: u8) -> Self {
|
||||
if value == 0x00 {
|
||||
PacketType::GFSK
|
||||
} else if value == 0x01 {
|
||||
PacketType::LoRa
|
||||
} else {
|
||||
PacketType::None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct PacketStatus {
|
||||
pub rssi: i8,
|
||||
pub snr: i8,
|
||||
pub signal_rssi: i8,
|
||||
pub freq_error: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum RadioType {
|
||||
SX1261,
|
||||
SX1262,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum RadioMode {
|
||||
Sleep = 0x00, // sleep mode
|
||||
StandbyRC = 0x01, // standby mode with RC oscillator
|
||||
StandbyXOSC = 0x02, // standby mode with XOSC oscillator
|
||||
FrequencySynthesis = 0x03, // frequency synthesis mode
|
||||
Transmit = 0x04, // transmit mode
|
||||
Receive = 0x05, // receive mode
|
||||
ReceiveDutyCycle = 0x06, // receive duty cycle mode
|
||||
ChannelActivityDetection = 0x07, // channel activity detection mode
|
||||
}
|
||||
|
||||
impl RadioMode {
|
||||
/// Returns the value of the mode.
|
||||
pub const fn value(self) -> u8 {
|
||||
self as u8
|
||||
}
|
||||
pub fn to_enum(value: u8) -> Self {
|
||||
if value == 0x00 {
|
||||
RadioMode::Sleep
|
||||
} else if value == 0x01 {
|
||||
RadioMode::StandbyRC
|
||||
} else if value == 0x02 {
|
||||
RadioMode::StandbyXOSC
|
||||
} else if value == 0x03 {
|
||||
RadioMode::FrequencySynthesis
|
||||
} else if value == 0x04 {
|
||||
RadioMode::Transmit
|
||||
} else if value == 0x05 {
|
||||
RadioMode::Receive
|
||||
} else if value == 0x06 {
|
||||
RadioMode::ReceiveDutyCycle
|
||||
} else if value == 0x07 {
|
||||
RadioMode::ChannelActivityDetection
|
||||
} else {
|
||||
RadioMode::Sleep
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum RadioState {
|
||||
Idle = 0x00,
|
||||
RxRunning = 0x01,
|
||||
TxRunning = 0x02,
|
||||
ChannelActivityDetecting = 0x03,
|
||||
}
|
||||
|
||||
impl RadioState {
|
||||
/// Returns the value of the state.
|
||||
pub fn value(self) -> u8 {
|
||||
self as u8
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RadioStatus {
|
||||
pub cmd_status: u8,
|
||||
pub chip_mode: u8,
|
||||
}
|
||||
|
||||
impl RadioStatus {
|
||||
pub fn value(self) -> u8 {
|
||||
(self.chip_mode << 4) | (self.cmd_status << 1)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum IrqMask {
|
||||
None = 0x0000,
|
||||
TxDone = 0x0001,
|
||||
RxDone = 0x0002,
|
||||
PreambleDetected = 0x0004,
|
||||
SyncwordValid = 0x0008,
|
||||
HeaderValid = 0x0010,
|
||||
HeaderError = 0x0020,
|
||||
CRCError = 0x0040,
|
||||
CADDone = 0x0080,
|
||||
CADActivityDetected = 0x0100,
|
||||
RxTxTimeout = 0x0200,
|
||||
All = 0xFFFF,
|
||||
}
|
||||
|
||||
impl IrqMask {
|
||||
pub fn value(self) -> u16 {
|
||||
self as u16
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Register {
|
||||
PacketParams = 0x0704, // packet configuration
|
||||
PayloadLength = 0x0702, // payload size
|
||||
SynchTimeout = 0x0706, // recalculated number of symbols
|
||||
Syncword = 0x06C0, // Syncword values
|
||||
LoRaSyncword = 0x0740, // LoRa Syncword value
|
||||
GeneratedRandomNumber = 0x0819, //32-bit generated random number
|
||||
AnaLNA = 0x08E2, // disable the LNA
|
||||
AnaMixer = 0x08E5, // disable the mixer
|
||||
RxGain = 0x08AC, // RX gain (0x94: power saving, 0x96: rx boosted)
|
||||
XTATrim = 0x0911, // device internal trimming capacitor
|
||||
OCP = 0x08E7, // over current protection max value
|
||||
RetentionList = 0x029F, // retention list
|
||||
IQPolarity = 0x0736, // optimize the inverted IQ operation (see DS_SX1261-2_V1.2 datasheet chapter 15.4)
|
||||
TxModulation = 0x0889, // modulation quality with 500 kHz LoRa Bandwidth (see DS_SX1261-2_V1.2 datasheet chapter 15.1)
|
||||
TxClampCfg = 0x08D8, // better resistance to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2)
|
||||
RTCCtrl = 0x0902, // RTC control
|
||||
EvtClr = 0x0944, // event clear
|
||||
}
|
||||
|
||||
impl Register {
|
||||
pub fn addr(self) -> u16 {
|
||||
self as u16
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum OpCode {
|
||||
GetStatus = 0xC0,
|
||||
WriteRegister = 0x0D,
|
||||
ReadRegister = 0x1D,
|
||||
WriteBuffer = 0x0E,
|
||||
ReadBuffer = 0x1E,
|
||||
SetSleep = 0x84,
|
||||
SetStandby = 0x80,
|
||||
SetFS = 0xC1,
|
||||
SetTx = 0x83,
|
||||
SetRx = 0x82,
|
||||
SetRxDutyCycle = 0x94,
|
||||
SetCAD = 0xC5,
|
||||
SetTxContinuousWave = 0xD1,
|
||||
SetTxContinuousPremable = 0xD2,
|
||||
SetPacketType = 0x8A,
|
||||
GetPacketType = 0x11,
|
||||
SetRFFrequency = 0x86,
|
||||
SetTxParams = 0x8E,
|
||||
SetPAConfig = 0x95,
|
||||
SetCADParams = 0x88,
|
||||
SetBufferBaseAddress = 0x8F,
|
||||
SetModulationParams = 0x8B,
|
||||
SetPacketParams = 0x8C,
|
||||
GetRxBufferStatus = 0x13,
|
||||
GetPacketStatus = 0x14,
|
||||
GetRSSIInst = 0x15,
|
||||
GetStats = 0x10,
|
||||
ResetStats = 0x00,
|
||||
CfgDIOIrq = 0x08,
|
||||
GetIrqStatus = 0x12,
|
||||
ClrIrqStatus = 0x02,
|
||||
Calibrate = 0x89,
|
||||
CalibrateImage = 0x98,
|
||||
SetRegulatorMode = 0x96,
|
||||
GetErrors = 0x17,
|
||||
ClrErrors = 0x07,
|
||||
SetTCXOMode = 0x97,
|
||||
SetTxFallbackMode = 0x93,
|
||||
SetRFSwitchMode = 0x9D,
|
||||
SetStopRxTimerOnPreamble = 0x9F,
|
||||
SetLoRaSymbTimeout = 0xA0,
|
||||
}
|
||||
|
||||
impl OpCode {
|
||||
pub fn value(self) -> u8 {
|
||||
self as u8
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SleepParams {
|
||||
pub wakeup_rtc: bool, // get out of sleep mode if wakeup signal received from RTC
|
||||
pub reset: bool,
|
||||
pub warm_start: bool,
|
||||
}
|
||||
|
||||
impl SleepParams {
|
||||
pub fn value(self) -> u8 {
|
||||
((self.warm_start as u8) << 2) | ((self.reset as u8) << 1) | (self.wakeup_rtc as u8)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum StandbyMode {
|
||||
RC = 0x00,
|
||||
XOSC = 0x01,
|
||||
}
|
||||
|
||||
impl StandbyMode {
|
||||
pub fn value(self) -> u8 {
|
||||
self as u8
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum RegulatorMode {
|
||||
UseLDO = 0x00,
|
||||
UseDCDC = 0x01,
|
||||
}
|
||||
|
||||
impl RegulatorMode {
|
||||
pub fn value(self) -> u8 {
|
||||
self as u8
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct CalibrationParams {
|
||||
pub rc64k_enable: bool, // calibrate RC64K clock
|
||||
pub rc13m_enable: bool, // calibrate RC13M clock
|
||||
pub pll_enable: bool, // calibrate PLL
|
||||
pub adc_pulse_enable: bool, // calibrate ADC Pulse
|
||||
pub adc_bulkn_enable: bool, // calibrate ADC bulkN
|
||||
pub adc_bulkp_enable: bool, // calibrate ADC bulkP
|
||||
pub img_enable: bool,
|
||||
}
|
||||
|
||||
impl CalibrationParams {
|
||||
pub fn value(self) -> u8 {
|
||||
((self.img_enable as u8) << 6)
|
||||
| ((self.adc_bulkp_enable as u8) << 5)
|
||||
| ((self.adc_bulkn_enable as u8) << 4)
|
||||
| ((self.adc_pulse_enable as u8) << 3)
|
||||
| ((self.pll_enable as u8) << 2)
|
||||
| ((self.rc13m_enable as u8) << 1)
|
||||
| ((self.rc64k_enable as u8) << 0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum TcxoCtrlVoltage {
|
||||
Ctrl1V6 = 0x00,
|
||||
Ctrl1V7 = 0x01,
|
||||
Ctrl1V8 = 0x02,
|
||||
Ctrl2V2 = 0x03,
|
||||
Ctrl2V4 = 0x04,
|
||||
Ctrl2V7 = 0x05,
|
||||
Ctrl3V0 = 0x06,
|
||||
Ctrl3V3 = 0x07,
|
||||
}
|
||||
|
||||
impl TcxoCtrlVoltage {
|
||||
pub fn value(self) -> u8 {
|
||||
self as u8
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum RampTime {
|
||||
Ramp10Us = 0x00,
|
||||
Ramp20Us = 0x01,
|
||||
Ramp40Us = 0x02,
|
||||
Ramp80Us = 0x03,
|
||||
Ramp200Us = 0x04,
|
||||
Ramp800Us = 0x05,
|
||||
Ramp1700Us = 0x06,
|
||||
Ramp3400Us = 0x07,
|
||||
}
|
||||
|
||||
impl RampTime {
|
||||
pub fn value(self) -> u8 {
|
||||
self as u8
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum SpreadingFactor {
|
||||
_5 = 0x05,
|
||||
_6 = 0x06,
|
||||
_7 = 0x07,
|
||||
_8 = 0x08,
|
||||
_9 = 0x09,
|
||||
_10 = 0x0A,
|
||||
_11 = 0x0B,
|
||||
_12 = 0x0C,
|
||||
}
|
||||
|
||||
impl SpreadingFactor {
|
||||
pub fn value(self) -> u8 {
|
||||
self as u8
|
||||
}
|
||||
}
|
||||
|
||||
impl From<device::SpreadingFactor> for SpreadingFactor {
|
||||
fn from(sf: device::SpreadingFactor) -> Self {
|
||||
match sf {
|
||||
device::SpreadingFactor::_7 => SpreadingFactor::_7,
|
||||
device::SpreadingFactor::_8 => SpreadingFactor::_8,
|
||||
device::SpreadingFactor::_9 => SpreadingFactor::_9,
|
||||
device::SpreadingFactor::_10 => SpreadingFactor::_10,
|
||||
device::SpreadingFactor::_11 => SpreadingFactor::_11,
|
||||
device::SpreadingFactor::_12 => SpreadingFactor::_12,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum Bandwidth {
|
||||
_500KHz = 0x06,
|
||||
_250KHz = 0x05,
|
||||
_125KHz = 0x04,
|
||||
}
|
||||
|
||||
impl Bandwidth {
|
||||
pub fn value(self) -> u8 {
|
||||
self as u8
|
||||
}
|
||||
|
||||
pub fn value_in_hz(self) -> u32 {
|
||||
match self {
|
||||
Bandwidth::_125KHz => 125000u32,
|
||||
Bandwidth::_250KHz => 250000u32,
|
||||
Bandwidth::_500KHz => 500000u32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<device::Bandwidth> for Bandwidth {
|
||||
fn from(bw: device::Bandwidth) -> Self {
|
||||
match bw {
|
||||
device::Bandwidth::_500KHz => Bandwidth::_500KHz,
|
||||
device::Bandwidth::_250KHz => Bandwidth::_250KHz,
|
||||
device::Bandwidth::_125KHz => Bandwidth::_125KHz,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum CodingRate {
|
||||
_4_5 = 0x01,
|
||||
_4_6 = 0x02,
|
||||
_4_7 = 0x03,
|
||||
_4_8 = 0x04,
|
||||
}
|
||||
|
||||
impl CodingRate {
|
||||
pub fn value(self) -> u8 {
|
||||
self as u8
|
||||
}
|
||||
}
|
||||
|
||||
impl From<device::CodingRate> for CodingRate {
|
||||
fn from(cr: device::CodingRate) -> Self {
|
||||
match cr {
|
||||
device::CodingRate::_4_5 => CodingRate::_4_5,
|
||||
device::CodingRate::_4_6 => CodingRate::_4_6,
|
||||
device::CodingRate::_4_7 => CodingRate::_4_7,
|
||||
device::CodingRate::_4_8 => CodingRate::_4_8,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ModulationParams {
|
||||
pub spreading_factor: SpreadingFactor,
|
||||
pub bandwidth: Bandwidth,
|
||||
pub coding_rate: CodingRate,
|
||||
pub low_data_rate_optimize: u8,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct PacketParams {
|
||||
pub preamble_length: u16, // number of LoRa symbols in the preamble
|
||||
pub implicit_header: bool, // if the header is explicit, it will be transmitted in the LoRa packet, but is not transmitted if the header is implicit (known fixed length)
|
||||
pub payload_length: u8,
|
||||
pub crc_on: bool,
|
||||
pub iq_inverted: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum CADSymbols {
|
||||
_1 = 0x00,
|
||||
_2 = 0x01,
|
||||
_4 = 0x02,
|
||||
_8 = 0x03,
|
||||
_16 = 0x04,
|
||||
}
|
||||
|
||||
impl CADSymbols {
|
||||
pub fn value(self) -> u8 {
|
||||
self as u8
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum CADExitMode {
|
||||
CADOnly = 0x00,
|
||||
CADRx = 0x01,
|
||||
CADLBT = 0x10,
|
||||
}
|
||||
|
||||
impl CADExitMode {
|
||||
pub fn value(self) -> u8 {
|
||||
self as u8
|
||||
}
|
||||
}
|
674
embassy-lora/src/sx126x/sx126x_lora/subroutine.rs
Normal file
674
embassy-lora/src/sx126x/sx126x_lora/subroutine.rs
Normal file
@ -0,0 +1,674 @@
|
||||
use embedded_hal::digital::v2::OutputPin;
|
||||
use embedded_hal_async::digital::Wait;
|
||||
use embedded_hal_async::spi::SpiBus;
|
||||
|
||||
use super::mod_params::*;
|
||||
use super::LoRa;
|
||||
|
||||
// Internal frequency of the radio
|
||||
const SX126X_XTAL_FREQ: u32 = 32000000;
|
||||
|
||||
// Scaling factor used to perform fixed-point operations
|
||||
const SX126X_PLL_STEP_SHIFT_AMOUNT: u32 = 14;
|
||||
|
||||
// PLL step - scaled with SX126X_PLL_STEP_SHIFT_AMOUNT
|
||||
const SX126X_PLL_STEP_SCALED: u32 = SX126X_XTAL_FREQ >> (25 - SX126X_PLL_STEP_SHIFT_AMOUNT);
|
||||
|
||||
// Maximum value for parameter symbNum
|
||||
const SX126X_MAX_LORA_SYMB_NUM_TIMEOUT: u8 = 248;
|
||||
|
||||
// Provides board-specific functionality for Semtech SX126x-based boards
|
||||
|
||||
impl<SPI, CTRL, WAIT, BUS> LoRa<SPI, CTRL, WAIT>
|
||||
where
|
||||
SPI: SpiBus<u8, Error = BUS>,
|
||||
CTRL: OutputPin,
|
||||
WAIT: Wait,
|
||||
{
|
||||
// Initialize the radio driver
|
||||
pub(super) async fn sub_init(&mut self) -> Result<(), RadioError<BUS>> {
|
||||
self.brd_reset().await?;
|
||||
self.brd_wakeup().await?;
|
||||
self.sub_set_standby(StandbyMode::RC).await?;
|
||||
self.brd_io_tcxo_init().await?;
|
||||
self.brd_io_rf_switch_init().await?;
|
||||
self.image_calibrated = false;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Wakeup the radio if it is in Sleep mode and check that Busy is low
|
||||
pub(super) async fn sub_check_device_ready(&mut self) -> Result<(), RadioError<BUS>> {
|
||||
let operating_mode = self.brd_get_operating_mode();
|
||||
if operating_mode == RadioMode::Sleep || operating_mode == RadioMode::ReceiveDutyCycle {
|
||||
self.brd_wakeup().await?;
|
||||
}
|
||||
self.brd_wait_on_busy().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Save the payload to be sent in the radio buffer
|
||||
pub(super) async fn sub_set_payload(&mut self, payload: &[u8]) -> Result<(), RadioError<BUS>> {
|
||||
self.brd_write_buffer(0x00, payload).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Read the payload received.
|
||||
pub(super) async fn sub_get_payload(&mut self, buffer: &mut [u8]) -> Result<u8, RadioError<BUS>> {
|
||||
let (size, offset) = self.sub_get_rx_buffer_status().await?;
|
||||
if (size as usize) > buffer.len() {
|
||||
Err(RadioError::PayloadSizeMismatch(size as usize, buffer.len()))
|
||||
} else {
|
||||
self.brd_read_buffer(offset, buffer).await?;
|
||||
Ok(size)
|
||||
}
|
||||
}
|
||||
|
||||
// Send a payload
|
||||
pub(super) async fn sub_send_payload(&mut self, payload: &[u8], timeout: u32) -> Result<(), RadioError<BUS>> {
|
||||
self.sub_set_payload(payload).await?;
|
||||
self.sub_set_tx(timeout).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Get a 32-bit random value generated by the radio. A valid packet type must have been configured before using this command.
|
||||
//
|
||||
// The radio must be in reception mode before executing this function. This code can potentially result in interrupt generation. It is the responsibility of
|
||||
// the calling code to disable radio interrupts before calling this function, and re-enable them afterwards if necessary, or be certain that any interrupts
|
||||
// generated during this process will not cause undesired side-effects in the software.
|
||||
//
|
||||
// The random numbers produced by the generator do not have a uniform or Gaussian distribution. If uniformity is needed, perform appropriate software post-processing.
|
||||
pub(super) async fn sub_get_random(&mut self) -> Result<u32, RadioError<BUS>> {
|
||||
let mut reg_ana_lna_buffer_original = [0x00u8];
|
||||
let mut reg_ana_mixer_buffer_original = [0x00u8];
|
||||
let mut reg_ana_lna_buffer = [0x00u8];
|
||||
let mut reg_ana_mixer_buffer = [0x00u8];
|
||||
let mut number_buffer = [0x00u8, 0x00u8, 0x00u8, 0x00u8];
|
||||
|
||||
self.brd_read_registers(Register::AnaLNA, &mut reg_ana_lna_buffer_original)
|
||||
.await?;
|
||||
reg_ana_lna_buffer[0] = reg_ana_lna_buffer_original[0] & (!(1 << 0));
|
||||
self.brd_write_registers(Register::AnaLNA, ®_ana_lna_buffer).await?;
|
||||
|
||||
self.brd_read_registers(Register::AnaMixer, &mut reg_ana_mixer_buffer_original)
|
||||
.await?;
|
||||
reg_ana_mixer_buffer[0] = reg_ana_mixer_buffer_original[0] & (!(1 << 7));
|
||||
self.brd_write_registers(Register::AnaMixer, ®_ana_mixer_buffer)
|
||||
.await?;
|
||||
|
||||
// Set radio in continuous reception
|
||||
self.sub_set_rx(0xFFFFFFu32).await?;
|
||||
|
||||
self.brd_read_registers(Register::GeneratedRandomNumber, &mut number_buffer)
|
||||
.await?;
|
||||
|
||||
self.sub_set_standby(StandbyMode::RC).await?;
|
||||
|
||||
self.brd_write_registers(Register::AnaLNA, ®_ana_lna_buffer_original)
|
||||
.await?;
|
||||
self.brd_write_registers(Register::AnaMixer, ®_ana_mixer_buffer_original)
|
||||
.await?;
|
||||
|
||||
Ok(Self::convert_u8_buffer_to_u32(&number_buffer))
|
||||
}
|
||||
|
||||
// Set the radio in sleep mode
|
||||
pub(super) async fn sub_set_sleep(&mut self, sleep_config: SleepParams) -> Result<(), RadioError<BUS>> {
|
||||
self.brd_ant_sleep()?;
|
||||
|
||||
if !sleep_config.warm_start {
|
||||
self.image_calibrated = false;
|
||||
}
|
||||
|
||||
self.brd_write_command(OpCode::SetSleep, &[sleep_config.value()])
|
||||
.await?;
|
||||
self.brd_set_operating_mode(RadioMode::Sleep);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Set the radio in configuration mode
|
||||
pub(super) async fn sub_set_standby(&mut self, mode: StandbyMode) -> Result<(), RadioError<BUS>> {
|
||||
self.brd_write_command(OpCode::SetStandby, &[mode.value()]).await?;
|
||||
if mode == StandbyMode::RC {
|
||||
self.brd_set_operating_mode(RadioMode::StandbyRC);
|
||||
} else {
|
||||
self.brd_set_operating_mode(RadioMode::StandbyXOSC);
|
||||
}
|
||||
|
||||
self.brd_ant_sleep()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Set the radio in FS mode
|
||||
pub(super) async fn sub_set_fs(&mut self) -> Result<(), RadioError<BUS>> {
|
||||
// antenna settings ???
|
||||
self.brd_write_command(OpCode::SetFS, &[]).await?;
|
||||
self.brd_set_operating_mode(RadioMode::FrequencySynthesis);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Set the radio in transmission mode with timeout specified
|
||||
pub(super) async fn sub_set_tx(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
|
||||
let buffer = [
|
||||
Self::timeout_1(timeout),
|
||||
Self::timeout_2(timeout),
|
||||
Self::timeout_3(timeout),
|
||||
];
|
||||
|
||||
self.brd_ant_set_tx()?;
|
||||
|
||||
self.brd_set_operating_mode(RadioMode::Transmit);
|
||||
self.brd_write_command(OpCode::SetTx, &buffer).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Set the radio in reception mode with timeout specified
|
||||
pub(super) async fn sub_set_rx(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
|
||||
let buffer = [
|
||||
Self::timeout_1(timeout),
|
||||
Self::timeout_2(timeout),
|
||||
Self::timeout_3(timeout),
|
||||
];
|
||||
|
||||
self.brd_ant_set_rx()?;
|
||||
|
||||
self.brd_set_operating_mode(RadioMode::Receive);
|
||||
self.brd_write_registers(Register::RxGain, &[0x94u8]).await?;
|
||||
self.brd_write_command(OpCode::SetRx, &buffer).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Set the radio in reception mode with Boosted LNA gain and timeout specified
|
||||
pub(super) async fn sub_set_rx_boosted(&mut self, timeout: u32) -> Result<(), RadioError<BUS>> {
|
||||
let buffer = [
|
||||
Self::timeout_1(timeout),
|
||||
Self::timeout_2(timeout),
|
||||
Self::timeout_3(timeout),
|
||||
];
|
||||
|
||||
self.brd_ant_set_rx()?;
|
||||
|
||||
self.brd_set_operating_mode(RadioMode::Receive);
|
||||
// set max LNA gain, increase current by ~2mA for around ~3dB in sensitivity
|
||||
self.brd_write_registers(Register::RxGain, &[0x96u8]).await?;
|
||||
self.brd_write_command(OpCode::SetRx, &buffer).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Set the Rx duty cycle management parameters
|
||||
pub(super) async fn sub_set_rx_duty_cycle(&mut self, rx_time: u32, sleep_time: u32) -> Result<(), RadioError<BUS>> {
|
||||
let buffer = [
|
||||
((rx_time >> 16) & 0xFF) as u8,
|
||||
((rx_time >> 8) & 0xFF) as u8,
|
||||
(rx_time & 0xFF) as u8,
|
||||
((sleep_time >> 16) & 0xFF) as u8,
|
||||
((sleep_time >> 8) & 0xFF) as u8,
|
||||
(sleep_time & 0xFF) as u8,
|
||||
];
|
||||
|
||||
// antenna settings ???
|
||||
|
||||
self.brd_write_command(OpCode::SetRxDutyCycle, &buffer).await?;
|
||||
self.brd_set_operating_mode(RadioMode::ReceiveDutyCycle);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Set the radio in CAD mode
|
||||
pub(super) async fn sub_set_cad(&mut self) -> Result<(), RadioError<BUS>> {
|
||||
self.brd_ant_set_rx()?;
|
||||
|
||||
self.brd_write_command(OpCode::SetCAD, &[]).await?;
|
||||
self.brd_set_operating_mode(RadioMode::ChannelActivityDetection);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Set the radio in continuous wave transmission mode
|
||||
pub(super) async fn sub_set_tx_continuous_wave(&mut self) -> Result<(), RadioError<BUS>> {
|
||||
self.brd_ant_set_tx()?;
|
||||
|
||||
self.brd_write_command(OpCode::SetTxContinuousWave, &[]).await?;
|
||||
self.brd_set_operating_mode(RadioMode::Transmit);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Set the radio in continuous preamble transmission mode
|
||||
pub(super) async fn sub_set_tx_infinite_preamble(&mut self) -> Result<(), RadioError<BUS>> {
|
||||
self.brd_ant_set_tx()?;
|
||||
|
||||
self.brd_write_command(OpCode::SetTxContinuousPremable, &[]).await?;
|
||||
self.brd_set_operating_mode(RadioMode::Transmit);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Decide which interrupt will stop the internal radio rx timer.
|
||||
// false timer stop after header/syncword detection
|
||||
// true timer stop after preamble detection
|
||||
pub(super) async fn sub_set_stop_rx_timer_on_preamble_detect(
|
||||
&mut self,
|
||||
enable: bool,
|
||||
) -> Result<(), RadioError<BUS>> {
|
||||
self.brd_write_command(OpCode::SetStopRxTimerOnPreamble, &[enable as u8])
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Set the number of symbols the radio will wait to validate a reception
|
||||
pub(super) async fn sub_set_lora_symb_num_timeout(&mut self, symb_num: u16) -> Result<(), RadioError<BUS>> {
|
||||
let mut exp = 0u8;
|
||||
let mut reg;
|
||||
let mut mant = ((core::cmp::min(symb_num, SX126X_MAX_LORA_SYMB_NUM_TIMEOUT as u16) as u8) + 1) >> 1;
|
||||
while mant > 31 {
|
||||
mant = (mant + 3) >> 2;
|
||||
exp += 1;
|
||||
}
|
||||
reg = mant << ((2 * exp) + 1);
|
||||
|
||||
self.brd_write_command(OpCode::SetLoRaSymbTimeout, &[reg]).await?;
|
||||
|
||||
if symb_num != 0 {
|
||||
reg = exp + (mant << 3);
|
||||
self.brd_write_registers(Register::SynchTimeout, &[reg]).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Set the power regulators operating mode (LDO or DC_DC). Using only LDO implies that the Rx or Tx current is doubled
|
||||
pub(super) async fn sub_set_regulator_mode(&mut self, mode: RegulatorMode) -> Result<(), RadioError<BUS>> {
|
||||
self.brd_write_command(OpCode::SetRegulatorMode, &[mode.value()])
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Calibrate the given radio block
|
||||
pub(super) async fn sub_calibrate(&mut self, calibrate_params: CalibrationParams) -> Result<(), RadioError<BUS>> {
|
||||
self.brd_write_command(OpCode::Calibrate, &[calibrate_params.value()])
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Calibrate the image rejection based on the given frequency
|
||||
pub(super) async fn sub_calibrate_image(&mut self, freq: u32) -> Result<(), RadioError<BUS>> {
|
||||
let mut cal_freq = [0x00u8, 0x00u8];
|
||||
|
||||
if freq > 900000000 {
|
||||
cal_freq[0] = 0xE1;
|
||||
cal_freq[1] = 0xE9;
|
||||
} else if freq > 850000000 {
|
||||
cal_freq[0] = 0xD7;
|
||||
cal_freq[1] = 0xDB;
|
||||
} else if freq > 770000000 {
|
||||
cal_freq[0] = 0xC1;
|
||||
cal_freq[1] = 0xC5;
|
||||
} else if freq > 460000000 {
|
||||
cal_freq[0] = 0x75;
|
||||
cal_freq[1] = 0x81;
|
||||
} else if freq > 425000000 {
|
||||
cal_freq[0] = 0x6B;
|
||||
cal_freq[1] = 0x6F;
|
||||
}
|
||||
self.brd_write_command(OpCode::CalibrateImage, &cal_freq).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Activate the extention of the timeout when a long preamble is used
|
||||
pub(super) async fn sub_set_long_preamble(&mut self, _enable: u8) -> Result<(), RadioError<BUS>> {
|
||||
Ok(()) // no operation currently
|
||||
}
|
||||
|
||||
// Set the transmission parameters
|
||||
// hp_max 0 for sx1261, 7 for sx1262
|
||||
// device_sel 1 for sx1261, 0 for sx1262
|
||||
// pa_lut 0 for 14dBm LUT, 1 for 22dBm LUT
|
||||
pub(super) async fn sub_set_pa_config(
|
||||
&mut self,
|
||||
pa_duty_cycle: u8,
|
||||
hp_max: u8,
|
||||
device_sel: u8,
|
||||
pa_lut: u8,
|
||||
) -> Result<(), RadioError<BUS>> {
|
||||
self.brd_write_command(OpCode::SetPAConfig, &[pa_duty_cycle, hp_max, device_sel, pa_lut])
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Define into which mode the chip goes after a TX / RX done
|
||||
pub(super) async fn sub_set_rx_tx_fallback_mode(&mut self, fallback_mode: u8) -> Result<(), RadioError<BUS>> {
|
||||
self.brd_write_command(OpCode::SetTxFallbackMode, &[fallback_mode])
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Set the IRQ mask and DIO masks
|
||||
pub(super) async fn sub_set_dio_irq_params(
|
||||
&mut self,
|
||||
irq_mask: u16,
|
||||
dio1_mask: u16,
|
||||
dio2_mask: u16,
|
||||
dio3_mask: u16,
|
||||
) -> Result<(), RadioError<BUS>> {
|
||||
let mut buffer = [0x00u8; 8];
|
||||
|
||||
buffer[0] = ((irq_mask >> 8) & 0x00FF) as u8;
|
||||
buffer[1] = (irq_mask & 0x00FF) as u8;
|
||||
buffer[2] = ((dio1_mask >> 8) & 0x00FF) as u8;
|
||||
buffer[3] = (dio1_mask & 0x00FF) as u8;
|
||||
buffer[4] = ((dio2_mask >> 8) & 0x00FF) as u8;
|
||||
buffer[5] = (dio2_mask & 0x00FF) as u8;
|
||||
buffer[6] = ((dio3_mask >> 8) & 0x00FF) as u8;
|
||||
buffer[7] = (dio3_mask & 0x00FF) as u8;
|
||||
self.brd_write_command(OpCode::CfgDIOIrq, &buffer).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Return the current IRQ status
|
||||
pub(super) async fn sub_get_irq_status(&mut self) -> Result<u16, RadioError<BUS>> {
|
||||
let mut irq_status = [0x00u8, 0x00u8];
|
||||
self.brd_read_command(OpCode::GetIrqStatus, &mut irq_status).await?;
|
||||
Ok(((irq_status[0] as u16) << 8) | (irq_status[1] as u16))
|
||||
}
|
||||
|
||||
// Indicate if DIO2 is used to control an RF Switch
|
||||
pub(super) async fn sub_set_dio2_as_rf_switch_ctrl(&mut self, enable: bool) -> Result<(), RadioError<BUS>> {
|
||||
self.brd_write_command(OpCode::SetRFSwitchMode, &[enable as u8]).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Indicate if the radio main clock is supplied from a TCXO
|
||||
// tcxo_voltage voltage used to control the TCXO on/off from DIO3
|
||||
// timeout duration given to the TCXO to go to 32MHz
|
||||
pub(super) async fn sub_set_dio3_as_tcxo_ctrl(
|
||||
&mut self,
|
||||
tcxo_voltage: TcxoCtrlVoltage,
|
||||
timeout: u32,
|
||||
) -> Result<(), RadioError<BUS>> {
|
||||
let buffer = [
|
||||
tcxo_voltage.value() & 0x07,
|
||||
Self::timeout_1(timeout),
|
||||
Self::timeout_2(timeout),
|
||||
Self::timeout_3(timeout),
|
||||
];
|
||||
self.brd_write_command(OpCode::SetTCXOMode, &buffer).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Set the RF frequency (Hz)
|
||||
pub(super) async fn sub_set_rf_frequency(&mut self, frequency: u32) -> Result<(), RadioError<BUS>> {
|
||||
let mut buffer = [0x00u8; 4];
|
||||
|
||||
if !self.image_calibrated {
|
||||
self.sub_calibrate_image(frequency).await?;
|
||||
self.image_calibrated = true;
|
||||
}
|
||||
|
||||
let freq_in_pll_steps = Self::convert_freq_in_hz_to_pll_step(frequency);
|
||||
|
||||
buffer[0] = ((freq_in_pll_steps >> 24) & 0xFF) as u8;
|
||||
buffer[1] = ((freq_in_pll_steps >> 16) & 0xFF) as u8;
|
||||
buffer[2] = ((freq_in_pll_steps >> 8) & 0xFF) as u8;
|
||||
buffer[3] = (freq_in_pll_steps & 0xFF) as u8;
|
||||
self.brd_write_command(OpCode::SetRFFrequency, &buffer).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Set the radio for the given protocol (LoRa or GFSK). This method has to be called before setting RF frequency, modulation paramaters, and packet paramaters.
|
||||
pub(super) async fn sub_set_packet_type(&mut self, packet_type: PacketType) -> Result<(), RadioError<BUS>> {
|
||||
self.packet_type = packet_type;
|
||||
self.brd_write_command(OpCode::SetPacketType, &[packet_type.value()])
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Get the current radio protocol (LoRa or GFSK)
|
||||
pub(super) fn sub_get_packet_type(&mut self) -> PacketType {
|
||||
self.packet_type
|
||||
}
|
||||
|
||||
// Set the transmission parameters
|
||||
// power RF output power [-18..13] dBm
|
||||
// ramp_time transmission ramp up time
|
||||
pub(super) async fn sub_set_tx_params(
|
||||
&mut self,
|
||||
mut power: i8,
|
||||
ramp_time: RampTime,
|
||||
) -> Result<(), RadioError<BUS>> {
|
||||
if self.brd_get_radio_type() == RadioType::SX1261 {
|
||||
if power == 15 {
|
||||
self.sub_set_pa_config(0x06, 0x00, 0x01, 0x01).await?;
|
||||
} else {
|
||||
self.sub_set_pa_config(0x04, 0x00, 0x01, 0x01).await?;
|
||||
}
|
||||
|
||||
if power >= 14 {
|
||||
power = 14;
|
||||
} else if power < -17 {
|
||||
power = -17;
|
||||
}
|
||||
} else {
|
||||
// Provide better resistance of the SX1262 Tx to antenna mismatch (see DS_SX1261-2_V1.2 datasheet chapter 15.2)
|
||||
let mut tx_clamp_cfg = [0x00u8];
|
||||
self.brd_read_registers(Register::TxClampCfg, &mut tx_clamp_cfg).await?;
|
||||
tx_clamp_cfg[0] = tx_clamp_cfg[0] | (0x0F << 1);
|
||||
self.brd_write_registers(Register::TxClampCfg, &tx_clamp_cfg).await?;
|
||||
|
||||
self.sub_set_pa_config(0x04, 0x07, 0x00, 0x01).await?;
|
||||
|
||||
if power > 22 {
|
||||
power = 22;
|
||||
} else if power < -9 {
|
||||
power = -9;
|
||||
}
|
||||
}
|
||||
|
||||
// power conversion of negative number from i8 to u8 ???
|
||||
self.brd_write_command(OpCode::SetTxParams, &[power as u8, ramp_time.value()])
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Set the modulation parameters
|
||||
pub(super) async fn sub_set_modulation_params(&mut self) -> Result<(), RadioError<BUS>> {
|
||||
if self.modulation_params.is_some() {
|
||||
let mut buffer = [0x00u8; 4];
|
||||
|
||||
// Since this driver only supports LoRa, ensure the packet type is set accordingly
|
||||
self.sub_set_packet_type(PacketType::LoRa).await?;
|
||||
|
||||
let modulation_params = self.modulation_params.unwrap();
|
||||
buffer[0] = modulation_params.spreading_factor.value();
|
||||
buffer[1] = modulation_params.bandwidth.value();
|
||||
buffer[2] = modulation_params.coding_rate.value();
|
||||
buffer[3] = modulation_params.low_data_rate_optimize;
|
||||
|
||||
self.brd_write_command(OpCode::SetModulationParams, &buffer).await?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(RadioError::ModulationParamsMissing)
|
||||
}
|
||||
}
|
||||
|
||||
// Set the packet parameters
|
||||
pub(super) async fn sub_set_packet_params(&mut self) -> Result<(), RadioError<BUS>> {
|
||||
if self.packet_params.is_some() {
|
||||
let mut buffer = [0x00u8; 6];
|
||||
|
||||
// Since this driver only supports LoRa, ensure the packet type is set accordingly
|
||||
self.sub_set_packet_type(PacketType::LoRa).await?;
|
||||
|
||||
let packet_params = self.packet_params.unwrap();
|
||||
buffer[0] = ((packet_params.preamble_length >> 8) & 0xFF) as u8;
|
||||
buffer[1] = (packet_params.preamble_length & 0xFF) as u8;
|
||||
buffer[2] = packet_params.implicit_header as u8;
|
||||
buffer[3] = packet_params.payload_length;
|
||||
buffer[4] = packet_params.crc_on as u8;
|
||||
buffer[5] = packet_params.iq_inverted as u8;
|
||||
|
||||
self.brd_write_command(OpCode::SetPacketParams, &buffer).await?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(RadioError::PacketParamsMissing)
|
||||
}
|
||||
}
|
||||
|
||||
// Set the channel activity detection (CAD) parameters
|
||||
// symbols number of symbols to use for CAD operations
|
||||
// det_peak limit for detection of SNR peak used in the CAD
|
||||
// det_min minimum symbol recognition for CAD
|
||||
// exit_mode operation to be done at the end of CAD action
|
||||
// timeout timeout value to abort the CAD activity
|
||||
|
||||
pub(super) async fn sub_set_cad_params(
|
||||
&mut self,
|
||||
symbols: CADSymbols,
|
||||
det_peak: u8,
|
||||
det_min: u8,
|
||||
exit_mode: CADExitMode,
|
||||
timeout: u32,
|
||||
) -> Result<(), RadioError<BUS>> {
|
||||
let mut buffer = [0x00u8; 7];
|
||||
|
||||
buffer[0] = symbols.value();
|
||||
buffer[1] = det_peak;
|
||||
buffer[2] = det_min;
|
||||
buffer[3] = exit_mode.value();
|
||||
buffer[4] = Self::timeout_1(timeout);
|
||||
buffer[5] = Self::timeout_2(timeout);
|
||||
buffer[6] = Self::timeout_3(timeout);
|
||||
|
||||
self.brd_write_command(OpCode::SetCADParams, &buffer).await?;
|
||||
self.brd_set_operating_mode(RadioMode::ChannelActivityDetection);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Set the data buffer base address for transmission and reception
|
||||
pub(super) async fn sub_set_buffer_base_address(
|
||||
&mut self,
|
||||
tx_base_address: u8,
|
||||
rx_base_address: u8,
|
||||
) -> Result<(), RadioError<BUS>> {
|
||||
self.brd_write_command(OpCode::SetBufferBaseAddress, &[tx_base_address, rx_base_address])
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Get the current radio status
|
||||
pub(super) async fn sub_get_status(&mut self) -> Result<RadioStatus, RadioError<BUS>> {
|
||||
let status = self.brd_read_command(OpCode::GetStatus, &mut []).await?;
|
||||
Ok(RadioStatus {
|
||||
cmd_status: (status & (0x07 << 1)) >> 1,
|
||||
chip_mode: (status & (0x07 << 4)) >> 4,
|
||||
})
|
||||
}
|
||||
|
||||
// Get the instantaneous RSSI value for the last packet received
|
||||
pub(super) async fn sub_get_rssi_inst(&mut self) -> Result<i8, RadioError<BUS>> {
|
||||
let mut buffer = [0x00u8];
|
||||
self.brd_read_command(OpCode::GetRSSIInst, &mut buffer).await?;
|
||||
let rssi: i8 = ((-(buffer[0] as i32)) >> 1) as i8; // check this ???
|
||||
Ok(rssi)
|
||||
}
|
||||
|
||||
// Get the last received packet buffer status
|
||||
pub(super) async fn sub_get_rx_buffer_status(&mut self) -> Result<(u8, u8), RadioError<BUS>> {
|
||||
if self.packet_params.is_some() {
|
||||
let mut status = [0x00u8; 2];
|
||||
let mut payload_length_buffer = [0x00u8];
|
||||
|
||||
self.brd_read_command(OpCode::GetRxBufferStatus, &mut status).await?;
|
||||
if (self.sub_get_packet_type() == PacketType::LoRa) && self.packet_params.unwrap().implicit_header {
|
||||
self.brd_read_registers(Register::PayloadLength, &mut payload_length_buffer)
|
||||
.await?;
|
||||
} else {
|
||||
payload_length_buffer[0] = status[0];
|
||||
}
|
||||
|
||||
let payload_length = payload_length_buffer[0];
|
||||
let offset = status[1];
|
||||
|
||||
Ok((payload_length, offset))
|
||||
} else {
|
||||
Err(RadioError::PacketParamsMissing)
|
||||
}
|
||||
}
|
||||
|
||||
// Get the last received packet payload status
|
||||
pub(super) async fn sub_get_packet_status(&mut self) -> Result<PacketStatus, RadioError<BUS>> {
|
||||
let mut status = [0x00u8; 3];
|
||||
self.brd_read_command(OpCode::GetPacketStatus, &mut status).await?;
|
||||
|
||||
// check this ???
|
||||
let rssi = ((-(status[0] as i32)) >> 1) as i8;
|
||||
let snr = ((status[1] as i8) + 2) >> 2;
|
||||
let signal_rssi = ((-(status[2] as i32)) >> 1) as i8;
|
||||
let freq_error = self.frequency_error;
|
||||
|
||||
Ok(PacketStatus {
|
||||
rssi,
|
||||
snr,
|
||||
signal_rssi,
|
||||
freq_error,
|
||||
})
|
||||
}
|
||||
|
||||
// Get the possible system errors
|
||||
pub(super) async fn sub_get_device_errors(&mut self) -> Result<RadioSystemError, RadioError<BUS>> {
|
||||
let mut errors = [0x00u8; 2];
|
||||
self.brd_read_command(OpCode::GetErrors, &mut errors).await?;
|
||||
|
||||
Ok(RadioSystemError {
|
||||
rc_64khz_calibration: (errors[1] & (1 << 0)) != 0,
|
||||
rc_13mhz_calibration: (errors[1] & (1 << 1)) != 0,
|
||||
pll_calibration: (errors[1] & (1 << 2)) != 0,
|
||||
adc_calibration: (errors[1] & (1 << 3)) != 0,
|
||||
image_calibration: (errors[1] & (1 << 4)) != 0,
|
||||
xosc_start: (errors[1] & (1 << 5)) != 0,
|
||||
pll_lock: (errors[1] & (1 << 6)) != 0,
|
||||
pa_ramp: (errors[0] & (1 << 0)) != 0,
|
||||
})
|
||||
}
|
||||
|
||||
// Clear all the errors in the device
|
||||
pub(super) async fn sub_clear_device_errors(&mut self) -> Result<(), RadioError<BUS>> {
|
||||
self.brd_write_command(OpCode::ClrErrors, &[0x00u8, 0x00u8]).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Clear the IRQs
|
||||
pub(super) async fn sub_clear_irq_status(&mut self, irq: u16) -> Result<(), RadioError<BUS>> {
|
||||
let mut buffer = [0x00u8, 0x00u8];
|
||||
buffer[0] = ((irq >> 8) & 0xFF) as u8;
|
||||
buffer[1] = (irq & 0xFF) as u8;
|
||||
self.brd_write_command(OpCode::ClrIrqStatus, &buffer).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Utility functions
|
||||
|
||||
fn timeout_1(timeout: u32) -> u8 {
|
||||
((timeout >> 16) & 0xFF) as u8
|
||||
}
|
||||
fn timeout_2(timeout: u32) -> u8 {
|
||||
((timeout >> 8) & 0xFF) as u8
|
||||
}
|
||||
fn timeout_3(timeout: u32) -> u8 {
|
||||
(timeout & 0xFF) as u8
|
||||
}
|
||||
|
||||
// check this ???
|
||||
fn convert_u8_buffer_to_u32(buffer: &[u8; 4]) -> u32 {
|
||||
let b0 = buffer[0] as u32;
|
||||
let b1 = buffer[1] as u32;
|
||||
let b2 = buffer[2] as u32;
|
||||
let b3 = buffer[3] as u32;
|
||||
(b0 << 24) | (b1 << 16) | (b2 << 8) | b3
|
||||
}
|
||||
|
||||
fn convert_freq_in_hz_to_pll_step(freq_in_hz: u32) -> u32 {
|
||||
// Get integer and fractional parts of the frequency computed with a PLL step scaled value
|
||||
let steps_int = freq_in_hz / SX126X_PLL_STEP_SCALED;
|
||||
let steps_frac = freq_in_hz - (steps_int * SX126X_PLL_STEP_SCALED);
|
||||
|
||||
(steps_int << SX126X_PLL_STEP_SHIFT_AMOUNT)
|
||||
+ (((steps_frac << SX126X_PLL_STEP_SHIFT_AMOUNT) + (SX126X_PLL_STEP_SCALED >> 1)) / SX126X_PLL_STEP_SCALED)
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
name = "embassy-macros"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
syn = { version = "1.0.76", features = ["full", "extra-traits"] }
|
||||
|
@ -2,12 +2,13 @@
|
||||
name = "embassy-net"
|
||||
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-net-v$VERSION/embassy-net/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/src/"
|
||||
features = [ "pool-4", "defmt", "tcp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "embassy-time/tick-1mhz"]
|
||||
features = ["pool-4", "defmt", "tcp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip"]
|
||||
target = "thumbv7em-none-eabi"
|
||||
|
||||
[features]
|
||||
@ -16,6 +17,9 @@ std = []
|
||||
|
||||
defmt = ["dep:defmt", "smoltcp/defmt"]
|
||||
|
||||
nightly = ["dep:embedded-io", "embedded-io?/async", "dep:embedded-nal-async"]
|
||||
unstable-traits = []
|
||||
|
||||
udp = ["smoltcp/socket-udp"]
|
||||
tcp = ["smoltcp/socket-tcp"]
|
||||
dns = ["smoltcp/socket-dns"]
|
||||
@ -30,7 +34,6 @@ pool-16 = []
|
||||
pool-32 = []
|
||||
pool-64 = []
|
||||
pool-128 = []
|
||||
unstable-traits = []
|
||||
|
||||
[dependencies]
|
||||
|
||||
@ -39,7 +42,7 @@ log = { version = "0.4.14", optional = true }
|
||||
|
||||
embassy-time = { version = "0.1.0", path = "../embassy-time" }
|
||||
embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
|
||||
embedded-io = { version = "0.3.0", features = [ "async" ] }
|
||||
embedded-io = { version = "0.3.1", optional = true }
|
||||
|
||||
managed = { version = "0.8.0", default-features = false, features = [ "map" ] }
|
||||
heapless = { version = "0.7.5", default-features = false }
|
||||
@ -49,7 +52,7 @@ stable_deref_trait = { version = "1.2.0", default-features = false }
|
||||
futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
|
||||
atomic-pool = "1.0"
|
||||
atomic-polyfill = "1.0.1"
|
||||
embedded-nal-async = "0.2.0"
|
||||
embedded-nal-async = { version = "0.2.0", optional = true }
|
||||
|
||||
[dependencies.smoltcp]
|
||||
version = "0.8.0"
|
||||
|
@ -1,6 +1,5 @@
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![allow(clippy::new_without_default)]
|
||||
#![feature(generic_associated_types, type_alias_impl_trait)]
|
||||
#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))]
|
||||
|
||||
// This mod MUST go first, so that the others see its macros.
|
||||
pub(crate) mod fmt;
|
||||
|
@ -1,10 +1,9 @@
|
||||
use core::cell::UnsafeCell;
|
||||
use core::future::Future;
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
use embassy_sync::waitqueue::WakerRegistration;
|
||||
use embassy_time::{Instant, Timer};
|
||||
use futures::future::poll_fn;
|
||||
use futures::pin_mut;
|
||||
use heapless::Vec;
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
|
@ -1,9 +1,8 @@
|
||||
use core::cell::UnsafeCell;
|
||||
use core::future::Future;
|
||||
use core::future::poll_fn;
|
||||
use core::mem;
|
||||
use core::task::Poll;
|
||||
|
||||
use futures::future::poll_fn;
|
||||
use smoltcp::iface::{Interface, SocketHandle};
|
||||
use smoltcp::socket::tcp;
|
||||
use smoltcp::time::Duration;
|
||||
@ -55,6 +54,18 @@ pub struct TcpWriter<'a> {
|
||||
io: TcpIo<'a>,
|
||||
}
|
||||
|
||||
impl<'a> TcpReader<'a> {
|
||||
pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
self.io.read(buf).await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TcpWriter<'a> {
|
||||
pub async fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
|
||||
self.io.write(buf).await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TcpSocket<'a> {
|
||||
pub fn new<D: Device>(stack: &'a Stack<D>, rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self {
|
||||
// safety: not accessed reentrantly.
|
||||
@ -92,7 +103,7 @@ impl<'a> TcpSocket<'a> {
|
||||
Err(tcp::ConnectError::Unaddressable) => return Err(ConnectError::NoRoute),
|
||||
}
|
||||
|
||||
futures::future::poll_fn(|cx| unsafe {
|
||||
poll_fn(|cx| unsafe {
|
||||
self.io.with_mut(|s, _| match s.state() {
|
||||
tcp::State::Closed | tcp::State::TimeWait => Poll::Ready(Err(ConnectError::ConnectionReset)),
|
||||
tcp::State::Listen => unreachable!(),
|
||||
@ -117,7 +128,7 @@ impl<'a> TcpSocket<'a> {
|
||||
Err(tcp::ListenError::Unaddressable) => return Err(AcceptError::InvalidPort),
|
||||
}
|
||||
|
||||
futures::future::poll_fn(|cx| unsafe {
|
||||
poll_fn(|cx| unsafe {
|
||||
self.io.with_mut(|s, _| match s.state() {
|
||||
tcp::State::Listen | tcp::State::SynSent | tcp::State::SynReceived => {
|
||||
s.register_send_waker(cx.waker());
|
||||
@ -129,6 +140,14 @@ impl<'a> TcpSocket<'a> {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
self.io.read(buf).await
|
||||
}
|
||||
|
||||
pub async fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
|
||||
self.io.write(buf).await
|
||||
}
|
||||
|
||||
pub fn set_timeout(&mut self, duration: Option<Duration>) {
|
||||
unsafe { self.io.with_mut(|s, _| s.set_timeout(duration)) }
|
||||
}
|
||||
@ -241,6 +260,7 @@ impl<'d> TcpIo<'d> {
|
||||
.await
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
async fn flush(&mut self) -> Result<(), Error> {
|
||||
poll_fn(move |_| {
|
||||
Poll::Ready(Ok(())) // TODO: Is there a better implementation for this?
|
||||
@ -249,88 +269,96 @@ impl<'d> TcpIo<'d> {
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_io::Error for ConnectError {
|
||||
fn kind(&self) -> embedded_io::ErrorKind {
|
||||
embedded_io::ErrorKind::Other
|
||||
#[cfg(feature = "nightly")]
|
||||
mod embedded_io_impls {
|
||||
use core::future::Future;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl embedded_io::Error for ConnectError {
|
||||
fn kind(&self) -> embedded_io::ErrorKind {
|
||||
embedded_io::ErrorKind::Other
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_io::Error for Error {
|
||||
fn kind(&self) -> embedded_io::ErrorKind {
|
||||
embedded_io::ErrorKind::Other
|
||||
impl embedded_io::Error for Error {
|
||||
fn kind(&self) -> embedded_io::ErrorKind {
|
||||
embedded_io::ErrorKind::Other
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> embedded_io::Io for TcpSocket<'d> {
|
||||
type Error = Error;
|
||||
}
|
||||
impl<'d> embedded_io::Io for TcpSocket<'d> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d> embedded_io::asynch::Read for TcpSocket<'d> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
|
||||
impl<'d> embedded_io::asynch::Read for TcpSocket<'d> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.io.read(buf)
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.io.read(buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> embedded_io::asynch::Write for TcpSocket<'d> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
|
||||
impl<'d> embedded_io::asynch::Write for TcpSocket<'d> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.io.write(buf)
|
||||
}
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.io.write(buf)
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>>
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
self.io.flush()
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
self.io.flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> embedded_io::Io for TcpReader<'d> {
|
||||
type Error = Error;
|
||||
}
|
||||
impl<'d> embedded_io::Io for TcpReader<'d> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d> embedded_io::asynch::Read for TcpReader<'d> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
|
||||
impl<'d> embedded_io::asynch::Read for TcpReader<'d> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.io.read(buf)
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.io.read(buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> embedded_io::Io for TcpWriter<'d> {
|
||||
type Error = Error;
|
||||
}
|
||||
impl<'d> embedded_io::Io for TcpWriter<'d> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d> embedded_io::asynch::Write for TcpWriter<'d> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
|
||||
impl<'d> embedded_io::asynch::Write for TcpWriter<'d> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.io.write(buf)
|
||||
}
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.io.write(buf)
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>>
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
self.io.flush()
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
self.io.flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-traits")]
|
||||
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
|
||||
pub mod client {
|
||||
use core::future::Future;
|
||||
use core::mem::MaybeUninit;
|
||||
use core::ptr::NonNull;
|
||||
|
||||
@ -417,7 +445,7 @@ pub mod client {
|
||||
impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::asynch::Read
|
||||
for TcpConnection<'d, N, TX_SZ, RX_SZ>
|
||||
{
|
||||
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
|
||||
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
@ -429,7 +457,7 @@ pub mod client {
|
||||
impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::asynch::Write
|
||||
for TcpConnection<'d, N, TX_SZ, RX_SZ>
|
||||
{
|
||||
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
|
||||
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
@ -437,7 +465,7 @@ pub mod client {
|
||||
self.socket.write(buf)
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>>
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
use core::cell::UnsafeCell;
|
||||
use core::future::poll_fn;
|
||||
use core::mem;
|
||||
use core::task::Poll;
|
||||
|
||||
use futures::future::poll_fn;
|
||||
use smoltcp::iface::{Interface, SocketHandle};
|
||||
use smoltcp::socket::udp::{self, PacketMetadata};
|
||||
use smoltcp::wire::{IpEndpoint, IpListenEndpoint};
|
||||
|
@ -2,6 +2,7 @@
|
||||
name = "embassy-nrf"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-nrf-v$VERSION/embassy-nrf/src/"
|
||||
@ -18,10 +19,10 @@ flavors = [
|
||||
|
||||
time = ["dep:embassy-time"]
|
||||
|
||||
defmt = ["dep:defmt", "embassy-executor/defmt", "embassy-sync/defmt", "embassy-usb?/defmt", "embedded-io?/defmt", "embassy-embedded-hal/defmt"]
|
||||
defmt = ["dep:defmt", "embassy-executor/defmt", "embassy-sync/defmt", "embassy-usb-driver?/defmt", "embedded-io?/defmt", "embassy-embedded-hal/defmt"]
|
||||
|
||||
# Enable nightly-only features
|
||||
nightly = ["embedded-hal-1", "embedded-hal-async", "embassy-usb", "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"]
|
||||
|
||||
# Reexport the PAC for the currently enabled chip at `embassy_nrf::pac`.
|
||||
# This is unstable because semver-minor (non-breaking) releases of embassy-nrf may major-bump (breaking) the PAC version.
|
||||
@ -57,7 +58,7 @@ _nrf5340-net = ["_nrf5340", "nrf5340-net-pac"]
|
||||
_nrf5340 = ["_gpio-p1", "_dppi"]
|
||||
_nrf9160 = ["nrf9160-pac", "_dppi"]
|
||||
|
||||
_time-driver = ["dep:embassy-time", "embassy-time?/tick-32768hz"]
|
||||
_time-driver = ["dep:embassy-time", "embassy-time?/tick-hz-32_768"]
|
||||
|
||||
_ppi = []
|
||||
_dppi = []
|
||||
@ -70,12 +71,12 @@ embassy-sync = { version = "0.1.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" }
|
||||
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
|
||||
embassy-usb = {version = "0.1.0", path = "../embassy-usb", optional=true }
|
||||
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true }
|
||||
|
||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true}
|
||||
embedded-hal-async = { version = "0.1.0-alpha.1", optional = true}
|
||||
embedded-io = { version = "0.3.0", features = ["async"], optional = true }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true}
|
||||
embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true}
|
||||
embedded-io = { version = "0.3.1", features = ["async"], optional = true }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
@ -89,13 +90,13 @@ embedded-storage = "0.3.0"
|
||||
embedded-storage-async = { version = "0.3.0", optional = true }
|
||||
cfg-if = "1.0.0"
|
||||
|
||||
nrf52805-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
|
||||
nrf52810-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
|
||||
nrf52811-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
|
||||
nrf52820-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
|
||||
nrf52832-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
|
||||
nrf52833-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
|
||||
nrf52840-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
|
||||
nrf5340-app-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
|
||||
nrf5340-net-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
|
||||
nrf9160-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
|
||||
nrf52805-pac = { version = "0.12.0", optional = true, features = [ "rt" ] }
|
||||
nrf52810-pac = { version = "0.12.0", optional = true, features = [ "rt" ] }
|
||||
nrf52811-pac = { version = "0.12.0", optional = true, features = [ "rt" ] }
|
||||
nrf52820-pac = { version = "0.12.0", optional = true, features = [ "rt" ] }
|
||||
nrf52832-pac = { version = "0.12.0", optional = true, features = [ "rt" ] }
|
||||
nrf52833-pac = { version = "0.12.0", optional = true, features = [ "rt" ] }
|
||||
nrf52840-pac = { version = "0.12.0", optional = true, features = [ "rt" ] }
|
||||
nrf5340-app-pac = { version = "0.12.0", optional = true, features = [ "rt" ] }
|
||||
nrf5340-net-pac = { version = "0.12.0", optional = true, features = [ "rt" ] }
|
||||
nrf9160-pac = { version = "0.12.0", optional = true, features = [ "rt" ] }
|
||||
|
@ -13,8 +13,9 @@
|
||||
//!
|
||||
//! Please also see [crate::uarte] to understand when [BufferedUarte] should be used.
|
||||
|
||||
use core::cell::RefCell;
|
||||
use core::cmp::min;
|
||||
use core::future::Future;
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
use core::task::Poll;
|
||||
|
||||
@ -22,11 +23,10 @@ use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorag
|
||||
use embassy_hal_common::ring_buffer::RingBuffer;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::WakerRegistration;
|
||||
use futures::future::poll_fn;
|
||||
// Re-export SVD variants to allow user to directly set values
|
||||
pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity};
|
||||
|
||||
use crate::gpio::Pin as GpioPin;
|
||||
use crate::gpio::{self, Pin as GpioPin};
|
||||
use crate::interrupt::InterruptExt;
|
||||
use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task};
|
||||
use crate::timer::{Frequency, Instance as TimerInstance, Timer};
|
||||
@ -71,7 +71,7 @@ struct StateInner<'d, U: UarteInstance, T: TimerInstance> {
|
||||
|
||||
/// Interface to a UARTE instance
|
||||
pub struct BufferedUarte<'d, U: UarteInstance, T: TimerInstance> {
|
||||
inner: PeripheralMutex<'d, StateInner<'d, U, T>>,
|
||||
inner: RefCell<PeripheralMutex<'d, StateInner<'d, U, T>>>,
|
||||
}
|
||||
|
||||
impl<'d, U: UarteInstance, T: TimerInstance> Unpin for BufferedUarte<'d, U, T> {}
|
||||
@ -169,7 +169,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
|
||||
ppi_ch2.enable();
|
||||
|
||||
Self {
|
||||
inner: PeripheralMutex::new(irq, &mut state.0, move || StateInner {
|
||||
inner: RefCell::new(PeripheralMutex::new(irq, &mut state.0, move || StateInner {
|
||||
_peri: peri,
|
||||
timer,
|
||||
_ppi_ch1: ppi_ch1,
|
||||
@ -182,13 +182,13 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
|
||||
tx: RingBuffer::new(tx_buffer),
|
||||
tx_state: TxState::Idle,
|
||||
tx_waker: WakerRegistration::new(),
|
||||
}),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
/// Adjust the baud rate to the provided value.
|
||||
pub fn set_baudrate(&mut self, baudrate: Baudrate) {
|
||||
self.inner.with(|state| {
|
||||
self.inner.borrow_mut().with(|state| {
|
||||
let r = U::regs();
|
||||
|
||||
let timeout = 0x8000_0000 / (baudrate as u32 / 40);
|
||||
@ -198,21 +198,16 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
|
||||
r.baudrate.write(|w| w.baudrate().variant(baudrate));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarte<'d, U, T> {
|
||||
type Error = core::convert::Infallible;
|
||||
}
|
||||
pub fn split<'u>(&'u mut self) -> (BufferedUarteRx<'u, 'd, U, T>, BufferedUarteTx<'u, 'd, U, T>) {
|
||||
(BufferedUarteRx { inner: self }, BufferedUarteTx { inner: self })
|
||||
}
|
||||
|
||||
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarte<'d, U, T> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
async fn inner_read<'a>(&'a self, buf: &'a mut [u8]) -> Result<usize, core::convert::Infallible> {
|
||||
poll_fn(move |cx| {
|
||||
let mut do_pend = false;
|
||||
let res = self.inner.with(|state| {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
let res = inner.with(|state| {
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
trace!("poll_read");
|
||||
|
||||
@ -232,62 +227,18 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for Buffe
|
||||
Poll::Pending
|
||||
});
|
||||
if do_pend {
|
||||
self.inner.pend();
|
||||
inner.pend();
|
||||
}
|
||||
|
||||
res
|
||||
})
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarte<'d, U, T> {
|
||||
type FillBufFuture<'a> = impl Future<Output = Result<&'a [u8], Self::Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> {
|
||||
async fn inner_write<'a>(&'a self, buf: &'a [u8]) -> Result<usize, core::convert::Infallible> {
|
||||
poll_fn(move |cx| {
|
||||
self.inner.with(|state| {
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
trace!("fill_buf");
|
||||
|
||||
// We have data ready in buffer? Return it.
|
||||
let buf = state.rx.pop_buf();
|
||||
if !buf.is_empty() {
|
||||
trace!(" got {:?} {:?}", buf.as_ptr() as u32, buf.len());
|
||||
let buf: &[u8] = buf;
|
||||
// Safety: buffer lives as long as uart
|
||||
let buf: &[u8] = unsafe { core::mem::transmute(buf) };
|
||||
return Poll::Ready(Ok(buf));
|
||||
}
|
||||
|
||||
trace!(" empty");
|
||||
state.rx_waker.register(cx.waker());
|
||||
Poll::<Result<&[u8], Self::Error>>::Pending
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
let signal = self.inner.with(|state| {
|
||||
let full = state.rx.is_full();
|
||||
state.rx.pop(amt);
|
||||
full
|
||||
});
|
||||
if signal {
|
||||
self.inner.pend();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarte<'d, U, T> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
poll_fn(move |cx| {
|
||||
let res = self.inner.with(|state| {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
let res = inner.with(|state| {
|
||||
trace!("poll_write: {:?}", buf.len());
|
||||
|
||||
let tx_buf = state.tx.push_buf();
|
||||
@ -308,19 +259,16 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for Buff
|
||||
Poll::Ready(Ok(n))
|
||||
});
|
||||
|
||||
self.inner.pend();
|
||||
inner.pend();
|
||||
|
||||
res
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>>
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
async fn inner_flush<'a>(&'a self) -> Result<(), core::convert::Infallible> {
|
||||
poll_fn(move |cx| {
|
||||
self.inner.with(|state| {
|
||||
self.inner.borrow_mut().with(|state| {
|
||||
trace!("poll_flush");
|
||||
|
||||
if !state.tx.is_empty() {
|
||||
@ -332,6 +280,147 @@ impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for Buff
|
||||
Poll::Ready(Ok(()))
|
||||
})
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn inner_fill_buf<'a>(&'a self) -> Result<&'a [u8], core::convert::Infallible> {
|
||||
poll_fn(move |cx| {
|
||||
self.inner.borrow_mut().with(|state| {
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
trace!("fill_buf");
|
||||
|
||||
// We have data ready in buffer? Return it.
|
||||
let buf = state.rx.pop_buf();
|
||||
if !buf.is_empty() {
|
||||
trace!(" got {:?} {:?}", buf.as_ptr() as u32, buf.len());
|
||||
let buf: &[u8] = buf;
|
||||
// Safety: buffer lives as long as uart
|
||||
let buf: &[u8] = unsafe { core::mem::transmute(buf) };
|
||||
return Poll::Ready(Ok(buf));
|
||||
}
|
||||
|
||||
trace!(" empty");
|
||||
state.rx_waker.register(cx.waker());
|
||||
Poll::<Result<&[u8], core::convert::Infallible>>::Pending
|
||||
})
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
fn inner_consume(&self, amt: usize) {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
let signal = inner.with(|state| {
|
||||
let full = state.rx.is_full();
|
||||
state.rx.pop(amt);
|
||||
full
|
||||
});
|
||||
if signal {
|
||||
inner.pend();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BufferedUarteTx<'u, 'd, U: UarteInstance, T: TimerInstance> {
|
||||
inner: &'u BufferedUarte<'d, U, T>,
|
||||
}
|
||||
|
||||
pub struct BufferedUarteRx<'u, 'd, U: UarteInstance, T: TimerInstance> {
|
||||
inner: &'u BufferedUarte<'d, U, T>,
|
||||
}
|
||||
|
||||
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarte<'d, U, T> {
|
||||
type Error = core::convert::Infallible;
|
||||
}
|
||||
|
||||
impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteRx<'u, 'd, U, T> {
|
||||
type Error = core::convert::Infallible;
|
||||
}
|
||||
|
||||
impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteTx<'u, 'd, U, T> {
|
||||
type Error = core::convert::Infallible;
|
||||
}
|
||||
|
||||
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarte<'d, U, T> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.inner_read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarteRx<'u, 'd, U, T> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.inner.inner_read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarte<'d, U, T> {
|
||||
type FillBufFuture<'a> = impl Future<Output = Result<&'a [u8], Self::Error>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> {
|
||||
self.inner_fill_buf()
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
self.inner_consume(amt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarteRx<'u, 'd, U, T> {
|
||||
type FillBufFuture<'a> = impl Future<Output = Result<&'a [u8], Self::Error>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> {
|
||||
self.inner.inner_fill_buf()
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
self.inner.inner_consume(amt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarte<'d, U, T> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.inner_write(buf)
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
self.inner_flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarteTx<'u, 'd, U, T> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.inner.inner_write(buf)
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
self.inner.inner_flush()
|
||||
}
|
||||
}
|
||||
|
||||
@ -339,21 +428,23 @@ impl<'a, U: UarteInstance, T: TimerInstance> Drop for StateInner<'a, U, T> {
|
||||
fn drop(&mut self) {
|
||||
let r = U::regs();
|
||||
|
||||
// TODO this probably deadlocks. do like Uarte instead.
|
||||
|
||||
self.timer.stop();
|
||||
if let RxState::Receiving = self.rx_state {
|
||||
r.tasks_stoprx.write(|w| unsafe { w.bits(1) });
|
||||
}
|
||||
if let TxState::Transmitting(_) = self.tx_state {
|
||||
r.tasks_stoptx.write(|w| unsafe { w.bits(1) });
|
||||
}
|
||||
if let RxState::Receiving = self.rx_state {
|
||||
low_power_wait_until(|| r.events_endrx.read().bits() == 1);
|
||||
}
|
||||
if let TxState::Transmitting(_) = self.tx_state {
|
||||
low_power_wait_until(|| r.events_endtx.read().bits() == 1);
|
||||
}
|
||||
|
||||
r.inten.reset();
|
||||
r.events_rxto.reset();
|
||||
r.tasks_stoprx.write(|w| unsafe { w.bits(1) });
|
||||
r.events_txstopped.reset();
|
||||
r.tasks_stoptx.write(|w| unsafe { w.bits(1) });
|
||||
|
||||
while r.events_txstopped.read().bits() == 0 {}
|
||||
while r.events_rxto.read().bits() == 0 {}
|
||||
|
||||
r.enable.write(|w| w.enable().disabled());
|
||||
|
||||
gpio::deconfigure_pin(r.psel.rxd.read().bits());
|
||||
gpio::deconfigure_pin(r.psel.txd.read().bits());
|
||||
gpio::deconfigure_pin(r.psel.rts.read().bits());
|
||||
gpio::deconfigure_pin(r.psel.cts.read().bits());
|
||||
}
|
||||
}
|
||||
|
||||
@ -459,13 +550,3 @@ impl<'a, U: UarteInstance, T: TimerInstance> PeripheralState for StateInner<'a,
|
||||
trace!("irq: end");
|
||||
}
|
||||
}
|
||||
|
||||
/// Low power blocking wait loop using WFE/SEV.
|
||||
fn low_power_wait_until(mut condition: impl FnMut() -> bool) {
|
||||
while !condition() {
|
||||
// WFE might "eat" an event that would have otherwise woken the executor.
|
||||
cortex_m::asm::wfe();
|
||||
}
|
||||
// Retrigger an event to be transparent to the executor.
|
||||
cortex_m::asm::sev();
|
||||
}
|
||||
|
@ -193,8 +193,8 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
|
||||
impl_ppi_channel!(PPI_CH30, 30 => static);
|
||||
impl_ppi_channel!(PPI_CH31, 31 => static);
|
||||
|
||||
impl_saadc_input!(P0_04, ANALOGINPUT2);
|
||||
impl_saadc_input!(P0_05, ANALOGINPUT3);
|
||||
impl_saadc_input!(P0_04, ANALOG_INPUT2);
|
||||
impl_saadc_input!(P0_05, ANALOG_INPUT3);
|
||||
|
||||
pub mod irqs {
|
||||
use embassy_cortex_m::interrupt::_export::declare;
|
||||
|
@ -128,6 +128,9 @@ embassy_hal_common::peripherals! {
|
||||
|
||||
// QDEC
|
||||
QDEC,
|
||||
|
||||
// PDM
|
||||
PDM,
|
||||
}
|
||||
|
||||
impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
|
||||
@ -208,14 +211,14 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
|
||||
impl_ppi_channel!(PPI_CH30, 30 => static);
|
||||
impl_ppi_channel!(PPI_CH31, 31 => static);
|
||||
|
||||
impl_saadc_input!(P0_02, ANALOGINPUT0);
|
||||
impl_saadc_input!(P0_03, ANALOGINPUT1);
|
||||
impl_saadc_input!(P0_04, ANALOGINPUT2);
|
||||
impl_saadc_input!(P0_05, ANALOGINPUT3);
|
||||
impl_saadc_input!(P0_28, ANALOGINPUT4);
|
||||
impl_saadc_input!(P0_29, ANALOGINPUT5);
|
||||
impl_saadc_input!(P0_30, ANALOGINPUT6);
|
||||
impl_saadc_input!(P0_31, ANALOGINPUT7);
|
||||
impl_saadc_input!(P0_02, ANALOG_INPUT0);
|
||||
impl_saadc_input!(P0_03, ANALOG_INPUT1);
|
||||
impl_saadc_input!(P0_04, ANALOG_INPUT2);
|
||||
impl_saadc_input!(P0_05, ANALOG_INPUT3);
|
||||
impl_saadc_input!(P0_28, ANALOG_INPUT4);
|
||||
impl_saadc_input!(P0_29, ANALOG_INPUT5);
|
||||
impl_saadc_input!(P0_30, ANALOG_INPUT6);
|
||||
impl_saadc_input!(P0_31, ANALOG_INPUT7);
|
||||
|
||||
pub mod irqs {
|
||||
use embassy_cortex_m::interrupt::_export::declare;
|
||||
|
@ -128,6 +128,9 @@ embassy_hal_common::peripherals! {
|
||||
|
||||
// QDEC
|
||||
QDEC,
|
||||
|
||||
// PDM
|
||||
PDM,
|
||||
}
|
||||
|
||||
impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
|
||||
@ -209,14 +212,14 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
|
||||
impl_ppi_channel!(PPI_CH30, 30 => static);
|
||||
impl_ppi_channel!(PPI_CH31, 31 => static);
|
||||
|
||||
impl_saadc_input!(P0_02, ANALOGINPUT0);
|
||||
impl_saadc_input!(P0_03, ANALOGINPUT1);
|
||||
impl_saadc_input!(P0_04, ANALOGINPUT2);
|
||||
impl_saadc_input!(P0_05, ANALOGINPUT3);
|
||||
impl_saadc_input!(P0_28, ANALOGINPUT4);
|
||||
impl_saadc_input!(P0_29, ANALOGINPUT5);
|
||||
impl_saadc_input!(P0_30, ANALOGINPUT6);
|
||||
impl_saadc_input!(P0_31, ANALOGINPUT7);
|
||||
impl_saadc_input!(P0_02, ANALOG_INPUT0);
|
||||
impl_saadc_input!(P0_03, ANALOG_INPUT1);
|
||||
impl_saadc_input!(P0_04, ANALOG_INPUT2);
|
||||
impl_saadc_input!(P0_05, ANALOG_INPUT3);
|
||||
impl_saadc_input!(P0_28, ANALOG_INPUT4);
|
||||
impl_saadc_input!(P0_29, ANALOG_INPUT5);
|
||||
impl_saadc_input!(P0_30, ANALOG_INPUT6);
|
||||
impl_saadc_input!(P0_31, ANALOG_INPUT7);
|
||||
|
||||
pub mod irqs {
|
||||
use embassy_cortex_m::interrupt::_export::declare;
|
||||
|
@ -225,14 +225,14 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
|
||||
impl_ppi_channel!(PPI_CH30, 30 => static);
|
||||
impl_ppi_channel!(PPI_CH31, 31 => static);
|
||||
|
||||
impl_saadc_input!(P0_02, ANALOGINPUT0);
|
||||
impl_saadc_input!(P0_03, ANALOGINPUT1);
|
||||
impl_saadc_input!(P0_04, ANALOGINPUT2);
|
||||
impl_saadc_input!(P0_05, ANALOGINPUT3);
|
||||
impl_saadc_input!(P0_28, ANALOGINPUT4);
|
||||
impl_saadc_input!(P0_29, ANALOGINPUT5);
|
||||
impl_saadc_input!(P0_30, ANALOGINPUT6);
|
||||
impl_saadc_input!(P0_31, ANALOGINPUT7);
|
||||
impl_saadc_input!(P0_02, ANALOG_INPUT0);
|
||||
impl_saadc_input!(P0_03, ANALOG_INPUT1);
|
||||
impl_saadc_input!(P0_04, ANALOG_INPUT2);
|
||||
impl_saadc_input!(P0_05, ANALOG_INPUT3);
|
||||
impl_saadc_input!(P0_28, ANALOG_INPUT4);
|
||||
impl_saadc_input!(P0_29, ANALOG_INPUT5);
|
||||
impl_saadc_input!(P0_30, ANALOG_INPUT6);
|
||||
impl_saadc_input!(P0_31, ANALOG_INPUT7);
|
||||
|
||||
pub mod irqs {
|
||||
use embassy_cortex_m::interrupt::_export::declare;
|
||||
|
@ -158,6 +158,9 @@ embassy_hal_common::peripherals! {
|
||||
|
||||
// QDEC
|
||||
QDEC,
|
||||
|
||||
// PDM
|
||||
PDM,
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
@ -268,14 +271,14 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
|
||||
impl_ppi_channel!(PPI_CH30, 30 => static);
|
||||
impl_ppi_channel!(PPI_CH31, 31 => static);
|
||||
|
||||
impl_saadc_input!(P0_02, ANALOGINPUT0);
|
||||
impl_saadc_input!(P0_03, ANALOGINPUT1);
|
||||
impl_saadc_input!(P0_04, ANALOGINPUT2);
|
||||
impl_saadc_input!(P0_05, ANALOGINPUT3);
|
||||
impl_saadc_input!(P0_28, ANALOGINPUT4);
|
||||
impl_saadc_input!(P0_29, ANALOGINPUT5);
|
||||
impl_saadc_input!(P0_30, ANALOGINPUT6);
|
||||
impl_saadc_input!(P0_31, ANALOGINPUT7);
|
||||
impl_saadc_input!(P0_02, ANALOG_INPUT0);
|
||||
impl_saadc_input!(P0_03, ANALOG_INPUT1);
|
||||
impl_saadc_input!(P0_04, ANALOG_INPUT2);
|
||||
impl_saadc_input!(P0_05, ANALOG_INPUT3);
|
||||
impl_saadc_input!(P0_28, ANALOG_INPUT4);
|
||||
impl_saadc_input!(P0_29, ANALOG_INPUT5);
|
||||
impl_saadc_input!(P0_30, ANALOG_INPUT6);
|
||||
impl_saadc_input!(P0_31, ANALOG_INPUT7);
|
||||
|
||||
pub mod irqs {
|
||||
use embassy_cortex_m::interrupt::_export::declare;
|
||||
|
@ -161,6 +161,9 @@ embassy_hal_common::peripherals! {
|
||||
|
||||
// TEMP
|
||||
TEMP,
|
||||
|
||||
// PDM
|
||||
PDM,
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
@ -273,14 +276,14 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
|
||||
impl_ppi_channel!(PPI_CH30, 30 => static);
|
||||
impl_ppi_channel!(PPI_CH31, 31 => static);
|
||||
|
||||
impl_saadc_input!(P0_02, ANALOGINPUT0);
|
||||
impl_saadc_input!(P0_03, ANALOGINPUT1);
|
||||
impl_saadc_input!(P0_04, ANALOGINPUT2);
|
||||
impl_saadc_input!(P0_05, ANALOGINPUT3);
|
||||
impl_saadc_input!(P0_28, ANALOGINPUT4);
|
||||
impl_saadc_input!(P0_29, ANALOGINPUT5);
|
||||
impl_saadc_input!(P0_30, ANALOGINPUT6);
|
||||
impl_saadc_input!(P0_31, ANALOGINPUT7);
|
||||
impl_saadc_input!(P0_02, ANALOG_INPUT0);
|
||||
impl_saadc_input!(P0_03, ANALOG_INPUT1);
|
||||
impl_saadc_input!(P0_04, ANALOG_INPUT2);
|
||||
impl_saadc_input!(P0_05, ANALOG_INPUT3);
|
||||
impl_saadc_input!(P0_28, ANALOG_INPUT4);
|
||||
impl_saadc_input!(P0_29, ANALOG_INPUT5);
|
||||
impl_saadc_input!(P0_30, ANALOG_INPUT6);
|
||||
impl_saadc_input!(P0_31, ANALOG_INPUT7);
|
||||
|
||||
pub mod irqs {
|
||||
use embassy_cortex_m::interrupt::_export::declare;
|
||||
|
@ -458,14 +458,14 @@ impl_ppi_channel!(PPI_CH29, 29 => configurable);
|
||||
impl_ppi_channel!(PPI_CH30, 30 => configurable);
|
||||
impl_ppi_channel!(PPI_CH31, 31 => configurable);
|
||||
|
||||
impl_saadc_input!(P0_13, ANALOGINPUT0);
|
||||
impl_saadc_input!(P0_14, ANALOGINPUT1);
|
||||
impl_saadc_input!(P0_15, ANALOGINPUT2);
|
||||
impl_saadc_input!(P0_16, ANALOGINPUT3);
|
||||
impl_saadc_input!(P0_17, ANALOGINPUT4);
|
||||
impl_saadc_input!(P0_18, ANALOGINPUT5);
|
||||
impl_saadc_input!(P0_19, ANALOGINPUT6);
|
||||
impl_saadc_input!(P0_20, ANALOGINPUT7);
|
||||
impl_saadc_input!(P0_13, ANALOG_INPUT0);
|
||||
impl_saadc_input!(P0_14, ANALOG_INPUT1);
|
||||
impl_saadc_input!(P0_15, ANALOG_INPUT2);
|
||||
impl_saadc_input!(P0_16, ANALOG_INPUT3);
|
||||
impl_saadc_input!(P0_17, ANALOG_INPUT4);
|
||||
impl_saadc_input!(P0_18, ANALOG_INPUT5);
|
||||
impl_saadc_input!(P0_19, ANALOG_INPUT6);
|
||||
impl_saadc_input!(P0_20, ANALOG_INPUT7);
|
||||
|
||||
pub mod irqs {
|
||||
use embassy_cortex_m::interrupt::_export::declare;
|
||||
|
@ -260,6 +260,9 @@ embassy_hal_common::peripherals! {
|
||||
P0_29,
|
||||
P0_30,
|
||||
P0_31,
|
||||
|
||||
// PDM
|
||||
PDM,
|
||||
}
|
||||
|
||||
impl_uarte!(UARTETWISPI0, UARTE0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0);
|
||||
@ -336,14 +339,14 @@ impl_ppi_channel!(PPI_CH13, 13 => configurable);
|
||||
impl_ppi_channel!(PPI_CH14, 14 => configurable);
|
||||
impl_ppi_channel!(PPI_CH15, 15 => configurable);
|
||||
|
||||
impl_saadc_input!(P0_13, ANALOGINPUT0);
|
||||
impl_saadc_input!(P0_14, ANALOGINPUT1);
|
||||
impl_saadc_input!(P0_15, ANALOGINPUT2);
|
||||
impl_saadc_input!(P0_16, ANALOGINPUT3);
|
||||
impl_saadc_input!(P0_17, ANALOGINPUT4);
|
||||
impl_saadc_input!(P0_18, ANALOGINPUT5);
|
||||
impl_saadc_input!(P0_19, ANALOGINPUT6);
|
||||
impl_saadc_input!(P0_20, ANALOGINPUT7);
|
||||
impl_saadc_input!(P0_13, ANALOG_INPUT0);
|
||||
impl_saadc_input!(P0_14, ANALOG_INPUT1);
|
||||
impl_saadc_input!(P0_15, ANALOG_INPUT2);
|
||||
impl_saadc_input!(P0_16, ANALOG_INPUT3);
|
||||
impl_saadc_input!(P0_17, ANALOG_INPUT4);
|
||||
impl_saadc_input!(P0_18, ANALOG_INPUT5);
|
||||
impl_saadc_input!(P0_19, ANALOG_INPUT6);
|
||||
impl_saadc_input!(P0_20, ANALOG_INPUT7);
|
||||
|
||||
pub mod irqs {
|
||||
use embassy_cortex_m::interrupt::_export::declare;
|
||||
|
@ -574,7 +574,7 @@ mod eh1 {
|
||||
type Error = Infallible;
|
||||
}
|
||||
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::blocking::InputPin for Input<'d, T> {
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Input<'d, T> {
|
||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_high())
|
||||
}
|
||||
@ -588,7 +588,7 @@ mod eh1 {
|
||||
type Error = Infallible;
|
||||
}
|
||||
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for Output<'d, T> {
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Output<'d, T> {
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(self.set_high())
|
||||
}
|
||||
@ -598,7 +598,7 @@ mod eh1 {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for Output<'d, T> {
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Output<'d, T> {
|
||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_set_high())
|
||||
}
|
||||
@ -615,7 +615,7 @@ mod eh1 {
|
||||
/// Implement [`InputPin`] for [`Flex`];
|
||||
///
|
||||
/// If the pin is not in input mode the result is unspecified.
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::blocking::InputPin for Flex<'d, T> {
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Flex<'d, T> {
|
||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_high())
|
||||
}
|
||||
@ -625,7 +625,7 @@ mod eh1 {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for Flex<'d, T> {
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Flex<'d, T> {
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(self.set_high())
|
||||
}
|
||||
@ -635,7 +635,7 @@ mod eh1 {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for Flex<'d, T> {
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Flex<'d, T> {
|
||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_set_high())
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
use core::convert::Infallible;
|
||||
use core::future::Future;
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
use embassy_hal_common::{impl_peripheral, Peripheral, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use futures::future::poll_fn;
|
||||
|
||||
use crate::gpio::sealed::Pin as _;
|
||||
use crate::gpio::{AnyPin, Flex, Input, Output, Pin as GpioPin};
|
||||
@ -458,7 +457,7 @@ mod eh1 {
|
||||
type Error = Infallible;
|
||||
}
|
||||
|
||||
impl<'d, C: Channel, T: GpioPin> embedded_hal_1::digital::blocking::InputPin for InputChannel<'d, C, T> {
|
||||
impl<'d, C: Channel, T: GpioPin> embedded_hal_1::digital::InputPin for InputChannel<'d, C, T> {
|
||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self.pin.is_high())
|
||||
}
|
||||
@ -469,72 +468,73 @@ mod eh1 {
|
||||
}
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] {
|
||||
use futures::FutureExt;
|
||||
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
|
||||
mod eha {
|
||||
use futures::FutureExt;
|
||||
|
||||
impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for Input<'d, T> {
|
||||
type WaitForHighFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
use super::*;
|
||||
|
||||
fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> {
|
||||
self.wait_for_high().map(Ok)
|
||||
}
|
||||
impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for Input<'d, T> {
|
||||
type WaitForHighFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
type WaitForLowFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> {
|
||||
self.wait_for_low().map(Ok)
|
||||
}
|
||||
|
||||
type WaitForRisingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> {
|
||||
self.wait_for_rising_edge().map(Ok)
|
||||
}
|
||||
|
||||
type WaitForFallingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> {
|
||||
self.wait_for_falling_edge().map(Ok)
|
||||
}
|
||||
|
||||
type WaitForAnyEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> {
|
||||
self.wait_for_any_edge().map(Ok)
|
||||
}
|
||||
fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> {
|
||||
self.wait_for_high().map(Ok)
|
||||
}
|
||||
|
||||
impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for Flex<'d, T> {
|
||||
type WaitForHighFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
type WaitForLowFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> {
|
||||
self.wait_for_high().map(Ok)
|
||||
}
|
||||
fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> {
|
||||
self.wait_for_low().map(Ok)
|
||||
}
|
||||
|
||||
type WaitForLowFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
type WaitForRisingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> {
|
||||
self.wait_for_low().map(Ok)
|
||||
}
|
||||
fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> {
|
||||
self.wait_for_rising_edge().map(Ok)
|
||||
}
|
||||
|
||||
type WaitForRisingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
type WaitForFallingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> {
|
||||
self.wait_for_rising_edge().map(Ok)
|
||||
}
|
||||
fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> {
|
||||
self.wait_for_falling_edge().map(Ok)
|
||||
}
|
||||
|
||||
type WaitForFallingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
type WaitForAnyEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> {
|
||||
self.wait_for_falling_edge().map(Ok)
|
||||
}
|
||||
fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> {
|
||||
self.wait_for_any_edge().map(Ok)
|
||||
}
|
||||
}
|
||||
|
||||
type WaitForAnyEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for Flex<'d, T> {
|
||||
type WaitForHighFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> {
|
||||
self.wait_for_any_edge().map(Ok)
|
||||
}
|
||||
fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> {
|
||||
self.wait_for_high().map(Ok)
|
||||
}
|
||||
|
||||
type WaitForLowFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> {
|
||||
self.wait_for_low().map(Ok)
|
||||
}
|
||||
|
||||
type WaitForRisingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> {
|
||||
self.wait_for_rising_edge().map(Ok)
|
||||
}
|
||||
|
||||
type WaitForFallingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> {
|
||||
self.wait_for_falling_edge().map(Ok)
|
||||
}
|
||||
|
||||
type WaitForAnyEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> {
|
||||
self.wait_for_any_edge().map(Ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@
|
||||
//! mutable slices always reside in RAM.
|
||||
|
||||
#![no_std]
|
||||
#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))]
|
||||
#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))]
|
||||
|
||||
#[cfg(not(any(
|
||||
feature = "nrf51",
|
||||
@ -76,6 +76,14 @@ pub mod gpio;
|
||||
pub mod gpiote;
|
||||
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
|
||||
pub mod nvmc;
|
||||
#[cfg(any(
|
||||
feature = "nrf52810",
|
||||
feature = "nrf52811",
|
||||
feature = "nrf52833",
|
||||
feature = "nrf52840",
|
||||
feature = "_nrf9160"
|
||||
))]
|
||||
pub mod pdm;
|
||||
pub mod ppi;
|
||||
#[cfg(not(any(feature = "nrf52805", feature = "nrf52820", feature = "_nrf5340-net")))]
|
||||
pub mod pwm;
|
||||
|
242
embassy-nrf/src/pdm.rs
Normal file
242
embassy-nrf/src/pdm.rs
Normal file
@ -0,0 +1,242 @@
|
||||
//! PDM mirophone interface
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_common::drop::OnDrop;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use futures::future::poll_fn;
|
||||
|
||||
use crate::chip::EASY_DMA_SIZE;
|
||||
use crate::gpio::sealed::Pin;
|
||||
use crate::gpio::{AnyPin, Pin as GpioPin};
|
||||
use crate::interrupt::{self, InterruptExt};
|
||||
use crate::peripherals::PDM;
|
||||
use crate::{pac, Peripheral};
|
||||
|
||||
/// PDM microphone interface
|
||||
pub struct Pdm<'d> {
|
||||
irq: PeripheralRef<'d, interrupt::PDM>,
|
||||
phantom: PhantomData<&'d PDM>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
BufferTooLong,
|
||||
BufferZeroLength,
|
||||
NotRunning,
|
||||
}
|
||||
|
||||
static WAKER: AtomicWaker = AtomicWaker::new();
|
||||
static DUMMY_BUFFER: [i16; 1] = [0; 1];
|
||||
|
||||
impl<'d> Pdm<'d> {
|
||||
/// Create PDM driver
|
||||
pub fn new(
|
||||
pdm: impl Peripheral<P = PDM> + 'd,
|
||||
irq: impl Peripheral<P = interrupt::PDM> + 'd,
|
||||
clk: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
din: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(clk, din);
|
||||
Self::new_inner(pdm, irq, clk.map_into(), din.map_into(), config)
|
||||
}
|
||||
|
||||
fn new_inner(
|
||||
_pdm: impl Peripheral<P = PDM> + 'd,
|
||||
irq: impl Peripheral<P = interrupt::PDM> + 'd,
|
||||
clk: PeripheralRef<'d, AnyPin>,
|
||||
din: PeripheralRef<'d, AnyPin>,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(irq);
|
||||
|
||||
let r = Self::regs();
|
||||
|
||||
// setup gpio pins
|
||||
din.conf().write(|w| w.input().set_bit());
|
||||
r.psel.din.write(|w| unsafe { w.bits(din.psel_bits()) });
|
||||
clk.set_low();
|
||||
clk.conf().write(|w| w.dir().output());
|
||||
r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) });
|
||||
|
||||
// configure
|
||||
// use default for
|
||||
// - gain right
|
||||
// - gain left
|
||||
// - clk
|
||||
// - ratio
|
||||
r.mode.write(|w| {
|
||||
w.edge().bit(config.edge == Edge::LeftRising);
|
||||
w.operation().bit(config.operation_mode == OperationMode::Mono);
|
||||
w
|
||||
});
|
||||
r.gainl.write(|w| w.gainl().default_gain());
|
||||
r.gainr.write(|w| w.gainr().default_gain());
|
||||
|
||||
// IRQ
|
||||
irq.disable();
|
||||
irq.set_handler(|_| {
|
||||
let r = Self::regs();
|
||||
r.intenclr.write(|w| w.end().clear());
|
||||
WAKER.wake();
|
||||
});
|
||||
irq.enable();
|
||||
|
||||
r.enable.write(|w| w.enable().set_bit());
|
||||
|
||||
Self {
|
||||
phantom: PhantomData,
|
||||
irq,
|
||||
}
|
||||
}
|
||||
|
||||
/// Start sampling microphon data into a dummy buffer
|
||||
/// Usefull to start the microphon and keep it active between recording samples
|
||||
pub async fn start(&mut self) {
|
||||
let r = Self::regs();
|
||||
|
||||
// start dummy sampling because microphon needs some setup time
|
||||
r.sample
|
||||
.ptr
|
||||
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
|
||||
r.sample
|
||||
.maxcnt
|
||||
.write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
|
||||
|
||||
r.tasks_start.write(|w| w.tasks_start().set_bit());
|
||||
}
|
||||
|
||||
/// Stop sampling microphon data inta a dummy buffer
|
||||
pub async fn stop(&mut self) {
|
||||
let r = Self::regs();
|
||||
r.tasks_stop.write(|w| w.tasks_stop().set_bit());
|
||||
r.events_started.reset();
|
||||
}
|
||||
|
||||
pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> {
|
||||
if buffer.len() == 0 {
|
||||
return Err(Error::BufferZeroLength);
|
||||
}
|
||||
if buffer.len() > EASY_DMA_SIZE {
|
||||
return Err(Error::BufferTooLong);
|
||||
}
|
||||
|
||||
let r = Self::regs();
|
||||
|
||||
if r.events_started.read().events_started().bit_is_clear() {
|
||||
return Err(Error::NotRunning);
|
||||
}
|
||||
|
||||
let drop = OnDrop::new(move || {
|
||||
r.intenclr.write(|w| w.end().clear());
|
||||
r.events_stopped.reset();
|
||||
|
||||
// reset to dummy buffer
|
||||
r.sample
|
||||
.ptr
|
||||
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
|
||||
r.sample
|
||||
.maxcnt
|
||||
.write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
|
||||
|
||||
while r.events_stopped.read().bits() == 0 {}
|
||||
});
|
||||
|
||||
// setup user buffer
|
||||
let ptr = buffer.as_ptr();
|
||||
let len = buffer.len();
|
||||
r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(ptr as u32) });
|
||||
r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(len as _) });
|
||||
|
||||
// wait till the current sample is finished and the user buffer sample is started
|
||||
Self::wait_for_sample().await;
|
||||
|
||||
// reset the buffer back to the dummy buffer
|
||||
r.sample
|
||||
.ptr
|
||||
.write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
|
||||
r.sample
|
||||
.maxcnt
|
||||
.write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
|
||||
|
||||
// wait till the user buffer is sampled
|
||||
Self::wait_for_sample().await;
|
||||
|
||||
drop.defuse();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wait_for_sample() {
|
||||
let r = Self::regs();
|
||||
|
||||
r.events_end.reset();
|
||||
r.intenset.write(|w| w.end().set());
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
poll_fn(|cx| {
|
||||
WAKER.register(cx.waker());
|
||||
if r.events_end.read().events_end().bit_is_set() {
|
||||
return Poll::Ready(());
|
||||
}
|
||||
Poll::Pending
|
||||
})
|
||||
.await;
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
}
|
||||
|
||||
fn regs() -> &'static pac::pdm::RegisterBlock {
|
||||
unsafe { &*pac::PDM::ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
/// PDM microphone driver Config
|
||||
pub struct Config {
|
||||
/// Use stero or mono operation
|
||||
pub operation_mode: OperationMode,
|
||||
/// On which edge the left channel should be samples
|
||||
pub edge: Edge,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
operation_mode: OperationMode::Mono,
|
||||
edge: Edge::LeftFalling,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum OperationMode {
|
||||
Mono,
|
||||
Stereo,
|
||||
}
|
||||
#[derive(PartialEq)]
|
||||
pub enum Edge {
|
||||
LeftRising,
|
||||
LeftFalling,
|
||||
}
|
||||
|
||||
impl<'d> Drop for Pdm<'d> {
|
||||
fn drop(&mut self) {
|
||||
let r = Self::regs();
|
||||
|
||||
r.tasks_stop.write(|w| w.tasks_stop().set_bit());
|
||||
|
||||
self.irq.disable();
|
||||
|
||||
r.enable.write(|w| w.enable().disabled());
|
||||
|
||||
r.psel.din.reset();
|
||||
r.psel.clk.reset();
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
//! Quadrature decoder interface
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use futures::future::poll_fn;
|
||||
|
||||
use crate::gpio::sealed::Pin as _;
|
||||
use crate::gpio::{AnyPin, Pin as GpioPin};
|
||||
|
@ -1,11 +1,11 @@
|
||||
#![macro_use]
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::ptr;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_common::drop::DropBomb;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use futures::future::poll_fn;
|
||||
|
||||
use crate::gpio::{self, Pin as GpioPin};
|
||||
use crate::interrupt::{Interrupt, InterruptExt};
|
||||
|
@ -1,3 +1,4 @@
|
||||
use core::future::poll_fn;
|
||||
use core::ptr;
|
||||
use core::sync::atomic::{AtomicPtr, Ordering};
|
||||
use core::task::Poll;
|
||||
@ -5,7 +6,6 @@ use core::task::Poll;
|
||||
use embassy_hal_common::drop::OnDrop;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use futures::future::poll_fn;
|
||||
|
||||
use crate::interrupt::InterruptExt;
|
||||
use crate::peripherals::RNG;
|
||||
|
@ -1,11 +1,12 @@
|
||||
#![macro_use]
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_common::drop::OnDrop;
|
||||
use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use futures::future::poll_fn;
|
||||
use pac::{saadc, SAADC};
|
||||
use saadc::ch::config::{GAIN_A, REFSEL_A, RESP_A, TACQ_A};
|
||||
// We treat the positive and negative channels with the same enum values to keep our type tidy and given they are the same
|
||||
@ -219,7 +220,13 @@ impl<'d, const N: usize> Saadc<'d, N> {
|
||||
}
|
||||
|
||||
/// One shot sampling. The buffer must be the same size as the number of channels configured.
|
||||
/// The sampling is stopped prior to returning in order to reduce power consumption (power
|
||||
/// consumption remains higher if sampling is not stopped explicitly). Cancellation will
|
||||
/// also cause the sampling to be stopped.
|
||||
pub async fn sample(&mut self, buf: &mut [i16; N]) {
|
||||
// In case the future is dropped, stop the task and wait for it to end.
|
||||
OnDrop::new(Self::stop_sampling_immediately);
|
||||
|
||||
let r = Self::regs();
|
||||
|
||||
// Set up the DMA
|
||||
@ -270,6 +277,12 @@ impl<'d, const N: usize> Saadc<'d, N> {
|
||||
/// taken to acquire the samples into a single buffer. You should measure the
|
||||
/// time taken by the callback and set the sample buffer size accordingly.
|
||||
/// Exceeding this time can lead to samples becoming dropped.
|
||||
///
|
||||
/// The sampling is stopped prior to returning in order to reduce power consumption (power
|
||||
/// consumption remains higher if sampling is not stopped explicitly), and to
|
||||
/// free the buffers from being used by the peripheral. Cancellation will
|
||||
/// also cause the sampling to be stopped.
|
||||
|
||||
pub async fn run_task_sampler<S, T: TimerInstance, const N0: usize>(
|
||||
&mut self,
|
||||
timer: &mut T,
|
||||
@ -321,6 +334,9 @@ impl<'d, const N: usize> Saadc<'d, N> {
|
||||
I: FnMut(),
|
||||
S: FnMut(&[[i16; N]]) -> SamplerState,
|
||||
{
|
||||
// In case the future is dropped, stop the task and wait for it to end.
|
||||
OnDrop::new(Self::stop_sampling_immediately);
|
||||
|
||||
let r = Self::regs();
|
||||
|
||||
// Establish mode and sample rate
|
||||
@ -404,6 +420,19 @@ impl<'d, const N: usize> Saadc<'d, N> {
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
// Stop sampling and wait for it to stop in a blocking fashion
|
||||
fn stop_sampling_immediately() {
|
||||
let r = Self::regs();
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
r.events_stopped.reset();
|
||||
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||
|
||||
while r.events_stopped.read().bits() == 0 {}
|
||||
r.events_stopped.reset();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> Saadc<'d, 1> {
|
||||
|
@ -1,12 +1,12 @@
|
||||
#![macro_use]
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_embedded_hal::SetConfig;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
|
||||
use futures::future::poll_fn;
|
||||
pub use pac::spim0::frequency::FREQUENCY_A as Frequency;
|
||||
|
||||
use crate::chip::FORCE_COPY_BUFFER_SIZE;
|
||||
@ -446,25 +446,25 @@ mod eh1 {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusFlush for Spim<'d, T> {
|
||||
impl<'d, T: Instance> embedded_hal_1::spi::SpiBusFlush for Spim<'d, T> {
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusRead<u8> for Spim<'d, T> {
|
||||
impl<'d, T: Instance> embedded_hal_1::spi::SpiBusRead<u8> for Spim<'d, T> {
|
||||
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_transfer(words, &[])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusWrite<u8> for Spim<'d, T> {
|
||||
impl<'d, T: Instance> embedded_hal_1::spi::SpiBusWrite<u8> for Spim<'d, T> {
|
||||
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(words)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBus<u8> for Spim<'d, T> {
|
||||
impl<'d, T: Instance> embedded_hal_1::spi::SpiBus<u8> for Spim<'d, T> {
|
||||
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_transfer(read, write)
|
||||
}
|
||||
@ -475,49 +475,47 @@ mod eh1 {
|
||||
}
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] {
|
||||
use core::future::Future;
|
||||
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
|
||||
mod eha {
|
||||
use core::future::Future;
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusFlush for Spim<'d, T> {
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
use super::*;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
async move { Ok(()) }
|
||||
}
|
||||
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusFlush for Spim<'d, T> {
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
async move { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusRead<u8> for Spim<'d, T> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, words: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.read(words)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusWrite<u8> for Spim<'d, T> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, data: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.write(data)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::spi::SpiBus<u8> for Spim<'d, T> {
|
||||
type TransferFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn transfer<'a>(&'a mut self, rx: &'a mut [u8], tx: &'a [u8]) -> Self::TransferFuture<'a> {
|
||||
self.transfer(rx, tx)
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusRead<u8> for Spim<'d, T> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
type TransferInPlaceFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, words: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.read(words)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusWrite<u8> for Spim<'d, T> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, data: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.write(data)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::spi::SpiBus<u8> for Spim<'d, T> {
|
||||
type TransferFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn transfer<'a>(&'a mut self, rx: &'a mut [u8], tx: &'a [u8]) -> Self::TransferFuture<'a> {
|
||||
self.transfer(rx, tx)
|
||||
}
|
||||
|
||||
type TransferInPlaceFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn transfer_in_place<'a>(
|
||||
&'a mut self,
|
||||
words: &'a mut [u8],
|
||||
) -> Self::TransferInPlaceFuture<'a> {
|
||||
self.transfer_in_place(words)
|
||||
}
|
||||
fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Self::TransferInPlaceFuture<'a> {
|
||||
self.transfer_in_place(words)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
//! Temperature sensor interface.
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_common::drop::OnDrop;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use fixed::types::I30F2;
|
||||
use futures::future::poll_fn;
|
||||
|
||||
use crate::interrupt::InterruptExt;
|
||||
use crate::peripherals::TEMP;
|
||||
|
@ -243,22 +243,25 @@ impl Driver for RtcDriver {
|
||||
})
|
||||
}
|
||||
|
||||
fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) {
|
||||
fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
|
||||
critical_section::with(|cs| {
|
||||
let n = alarm.id() as _;
|
||||
let alarm = self.get_alarm(cs, alarm);
|
||||
alarm.timestamp.set(timestamp);
|
||||
|
||||
let t = self.now();
|
||||
|
||||
// If alarm timestamp has passed, trigger it instantly.
|
||||
if timestamp <= t {
|
||||
self.trigger_alarm(n, cs);
|
||||
return;
|
||||
}
|
||||
|
||||
let r = rtc();
|
||||
|
||||
let t = self.now();
|
||||
if timestamp <= t {
|
||||
// If alarm timestamp has passed the alarm will not fire.
|
||||
// Disarm the alarm and return `false` to indicate that.
|
||||
r.intenclr.write(|w| unsafe { w.bits(compare_n(n)) });
|
||||
|
||||
alarm.timestamp.set(u64::MAX);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// If it hasn't triggered yet, setup it in the compare channel.
|
||||
|
||||
// Write the CC value regardless of whether we're going to enable it now or not.
|
||||
@ -287,6 +290,8 @@ impl Driver for RtcDriver {
|
||||
// It will be setup later by `next_period`.
|
||||
r.intenclr.write(|w| unsafe { w.bits(compare_n(n)) });
|
||||
}
|
||||
|
||||
true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
#![macro_use]
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::marker::PhantomData;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_common::drop::OnDrop;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use futures::future::poll_fn;
|
||||
|
||||
use crate::interrupt::{Interrupt, InterruptExt};
|
||||
use crate::ppi::{Event, Task};
|
||||
|
@ -6,7 +6,7 @@
|
||||
//!
|
||||
//! - nRF52832: Section 33
|
||||
//! - nRF52840: Section 6.31
|
||||
use core::future::Future;
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::sync::atomic::compiler_fence;
|
||||
use core::sync::atomic::Ordering::SeqCst;
|
||||
use core::task::Poll;
|
||||
@ -16,7 +16,6 @@ use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
#[cfg(feature = "time")]
|
||||
use embassy_time::{Duration, Instant};
|
||||
use futures::future::poll_fn;
|
||||
|
||||
use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE};
|
||||
use crate::gpio::Pin as GpioPin;
|
||||
@ -794,7 +793,7 @@ mod eh1 {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_1::i2c::blocking::I2c for Twim<'d, T> {
|
||||
impl<'d, T: Instance> embedded_hal_1::i2c::I2c for Twim<'d, T> {
|
||||
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_read(address, buffer)
|
||||
}
|
||||
@ -824,57 +823,57 @@ mod eh1 {
|
||||
fn transaction<'a>(
|
||||
&mut self,
|
||||
_address: u8,
|
||||
_operations: &mut [embedded_hal_1::i2c::blocking::Operation<'a>],
|
||||
_operations: &mut [embedded_hal_1::i2c::Operation<'a>],
|
||||
) -> Result<(), Self::Error> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn transaction_iter<'a, O>(&mut self, _address: u8, _operations: O) -> Result<(), Self::Error>
|
||||
where
|
||||
O: IntoIterator<Item = embedded_hal_1::i2c::blocking::Operation<'a>>,
|
||||
O: IntoIterator<Item = embedded_hal_1::i2c::Operation<'a>>,
|
||||
{
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] {
|
||||
impl<'d, T: Instance> embedded_hal_async::i2c::I2c for Twim<'d, T> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
|
||||
mod eha {
|
||||
use super::*;
|
||||
impl<'d, T: Instance> embedded_hal_async::i2c::I2c for Twim<'d, T> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.read(address, buffer)
|
||||
}
|
||||
fn read<'a>(&'a mut self, address: u8, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.read(address, buffer)
|
||||
}
|
||||
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.write(address, bytes)
|
||||
}
|
||||
fn write<'a>(&'a mut self, address: u8, bytes: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.write(address, bytes)
|
||||
}
|
||||
|
||||
type WriteReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
type WriteReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn write_read<'a>(
|
||||
&'a mut self,
|
||||
address: u8,
|
||||
wr_buffer: &'a [u8],
|
||||
rd_buffer: &'a mut [u8],
|
||||
) -> Self::WriteReadFuture<'a> {
|
||||
self.write_read(address, wr_buffer, rd_buffer)
|
||||
}
|
||||
fn write_read<'a>(
|
||||
&'a mut self,
|
||||
address: u8,
|
||||
wr_buffer: &'a [u8],
|
||||
rd_buffer: &'a mut [u8],
|
||||
) -> Self::WriteReadFuture<'a> {
|
||||
self.write_read(address, wr_buffer, rd_buffer)
|
||||
}
|
||||
|
||||
type TransactionFuture<'a, 'b> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a, 'b: 'a;
|
||||
type TransactionFuture<'a, 'b> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a, 'b: 'a;
|
||||
|
||||
fn transaction<'a, 'b>(
|
||||
&'a mut self,
|
||||
address: u8,
|
||||
operations: &'a mut [embedded_hal_async::i2c::Operation<'b>],
|
||||
) -> Self::TransactionFuture<'a, 'b> {
|
||||
let _ = address;
|
||||
let _ = operations;
|
||||
async move { todo!() }
|
||||
}
|
||||
fn transaction<'a, 'b>(
|
||||
&'a mut self,
|
||||
address: u8,
|
||||
operations: &'a mut [embedded_hal_async::i2c::Operation<'b>],
|
||||
) -> Self::TransactionFuture<'a, 'b> {
|
||||
let _ = address;
|
||||
let _ = operations;
|
||||
async move { todo!() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,12 +13,12 @@
|
||||
//! memory may be used given that buffers are passed in directly to its read and write
|
||||
//! methods.
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_common::drop::OnDrop;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use futures::future::poll_fn;
|
||||
use pac::uarte0::RegisterBlock;
|
||||
// Re-export SVD variants to allow user to directly set values.
|
||||
pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity};
|
||||
@ -173,6 +173,61 @@ impl<'d, T: Instance> Uarte<'d, T> {
|
||||
(self.tx, self.rx)
|
||||
}
|
||||
|
||||
/// Split the Uarte into a transmitter and receiver that will
|
||||
/// return on idle, which is determined as the time it takes
|
||||
/// for two bytes to be received.
|
||||
pub fn split_with_idle<U: TimerInstance>(
|
||||
self,
|
||||
timer: impl Peripheral<P = U> + 'd,
|
||||
ppi_ch1: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
|
||||
ppi_ch2: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
|
||||
) -> (UarteTx<'d, T>, UarteRxWithIdle<'d, T, U>) {
|
||||
let mut timer = Timer::new(timer);
|
||||
|
||||
into_ref!(ppi_ch1, ppi_ch2);
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
// BAUDRATE register values are `baudrate * 2^32 / 16000000`
|
||||
// source: https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values
|
||||
//
|
||||
// We want to stop RX if line is idle for 2 bytes worth of time
|
||||
// That is 20 bits (each byte is 1 start bit + 8 data bits + 1 stop bit)
|
||||
// This gives us the amount of 16M ticks for 20 bits.
|
||||
let baudrate = r.baudrate.read().baudrate().variant().unwrap();
|
||||
let timeout = 0x8000_0000 / (baudrate as u32 / 40);
|
||||
|
||||
timer.set_frequency(Frequency::F16MHz);
|
||||
timer.cc(0).write(timeout);
|
||||
timer.cc(0).short_compare_clear();
|
||||
timer.cc(0).short_compare_stop();
|
||||
|
||||
let mut ppi_ch1 = Ppi::new_one_to_two(
|
||||
ppi_ch1.map_into(),
|
||||
Event::from_reg(&r.events_rxdrdy),
|
||||
timer.task_clear(),
|
||||
timer.task_start(),
|
||||
);
|
||||
ppi_ch1.enable();
|
||||
|
||||
let mut ppi_ch2 = Ppi::new_one_to_one(
|
||||
ppi_ch2.map_into(),
|
||||
timer.cc(0).event_compare(),
|
||||
Task::from_reg(&r.tasks_stoprx),
|
||||
);
|
||||
ppi_ch2.enable();
|
||||
|
||||
(
|
||||
self.tx,
|
||||
UarteRxWithIdle {
|
||||
rx: self.rx,
|
||||
timer,
|
||||
ppi_ch1: ppi_ch1,
|
||||
_ppi_ch2: ppi_ch2,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Return the endtx event for use with PPI
|
||||
pub fn event_endtx(&self) -> Event {
|
||||
let r = T::regs();
|
||||
@ -597,6 +652,117 @@ impl<'a, T: Instance> Drop for UarteRx<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UarteRxWithIdle<'d, T: Instance, U: TimerInstance> {
|
||||
rx: UarteRx<'d, T>,
|
||||
timer: Timer<'d, U>,
|
||||
ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 2>,
|
||||
_ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 1>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> {
|
||||
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
self.ppi_ch1.disable();
|
||||
self.rx.read(buffer).await
|
||||
}
|
||||
|
||||
pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
self.ppi_ch1.disable();
|
||||
self.rx.blocking_read(buffer)
|
||||
}
|
||||
|
||||
pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
if buffer.len() == 0 {
|
||||
return Err(Error::BufferZeroLength);
|
||||
}
|
||||
if buffer.len() > EASY_DMA_SIZE {
|
||||
return Err(Error::BufferTooLong);
|
||||
}
|
||||
|
||||
let ptr = buffer.as_ptr();
|
||||
let len = buffer.len();
|
||||
|
||||
let r = T::regs();
|
||||
let s = T::state();
|
||||
|
||||
self.ppi_ch1.enable();
|
||||
|
||||
let drop = OnDrop::new(|| {
|
||||
self.timer.stop();
|
||||
|
||||
r.intenclr.write(|w| w.endrx().clear());
|
||||
r.events_rxto.reset();
|
||||
r.tasks_stoprx.write(|w| unsafe { w.bits(1) });
|
||||
|
||||
while r.events_endrx.read().bits() == 0 {}
|
||||
});
|
||||
|
||||
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
|
||||
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
|
||||
|
||||
r.events_endrx.reset();
|
||||
r.intenset.write(|w| w.endrx().set());
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
|
||||
|
||||
poll_fn(|cx| {
|
||||
s.endrx_waker.register(cx.waker());
|
||||
if r.events_endrx.read().bits() != 0 {
|
||||
return Poll::Ready(());
|
||||
}
|
||||
Poll::Pending
|
||||
})
|
||||
.await;
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
let n = r.rxd.amount.read().amount().bits() as usize;
|
||||
|
||||
self.timer.stop();
|
||||
r.events_rxstarted.reset();
|
||||
|
||||
drop.defuse();
|
||||
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
if buffer.len() == 0 {
|
||||
return Err(Error::BufferZeroLength);
|
||||
}
|
||||
if buffer.len() > EASY_DMA_SIZE {
|
||||
return Err(Error::BufferTooLong);
|
||||
}
|
||||
|
||||
let ptr = buffer.as_ptr();
|
||||
let len = buffer.len();
|
||||
|
||||
let r = T::regs();
|
||||
|
||||
self.ppi_ch1.enable();
|
||||
|
||||
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
|
||||
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
|
||||
|
||||
r.events_endrx.reset();
|
||||
r.intenclr.write(|w| w.endrx().clear());
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
|
||||
|
||||
while r.events_endrx.read().bits() == 0 {}
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
let n = r.rxd.amount.read().amount().bits() as usize;
|
||||
|
||||
self.timer.stop();
|
||||
r.events_rxstarted.reset();
|
||||
|
||||
Ok(n)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(feature = "_nrf9160", feature = "nrf5340")))]
|
||||
pub(crate) fn apply_workaround_for_enable_anomaly(_r: &crate::pac::uarte0::RegisterBlock) {
|
||||
// Do nothing
|
||||
@ -665,270 +831,6 @@ pub(crate) fn drop_tx_rx(r: &pac::uarte0::RegisterBlock, s: &sealed::State) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Interface to an UARTE peripheral that uses an additional timer and two PPI channels,
|
||||
/// allowing it to implement the ReadUntilIdle trait.
|
||||
pub struct UarteWithIdle<'d, U: Instance, T: TimerInstance> {
|
||||
tx: UarteTx<'d, U>,
|
||||
rx: UarteRxWithIdle<'d, U, T>,
|
||||
}
|
||||
|
||||
impl<'d, U: Instance, T: TimerInstance> UarteWithIdle<'d, U, T> {
|
||||
/// Create a new UARTE without hardware flow control
|
||||
pub fn new(
|
||||
uarte: impl Peripheral<P = U> + 'd,
|
||||
timer: impl Peripheral<P = T> + 'd,
|
||||
ppi_ch1: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
|
||||
ppi_ch2: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
|
||||
irq: impl Peripheral<P = U::Interrupt> + 'd,
|
||||
rxd: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
txd: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(rxd, txd);
|
||||
Self::new_inner(
|
||||
uarte,
|
||||
timer,
|
||||
ppi_ch1,
|
||||
ppi_ch2,
|
||||
irq,
|
||||
rxd.map_into(),
|
||||
txd.map_into(),
|
||||
None,
|
||||
None,
|
||||
config,
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a new UARTE with hardware flow control (RTS/CTS)
|
||||
pub fn new_with_rtscts(
|
||||
uarte: impl Peripheral<P = U> + 'd,
|
||||
timer: impl Peripheral<P = T> + 'd,
|
||||
ppi_ch1: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
|
||||
ppi_ch2: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
|
||||
irq: impl Peripheral<P = U::Interrupt> + 'd,
|
||||
rxd: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
txd: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
cts: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
rts: impl Peripheral<P = impl GpioPin> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(rxd, txd, cts, rts);
|
||||
Self::new_inner(
|
||||
uarte,
|
||||
timer,
|
||||
ppi_ch1,
|
||||
ppi_ch2,
|
||||
irq,
|
||||
rxd.map_into(),
|
||||
txd.map_into(),
|
||||
Some(cts.map_into()),
|
||||
Some(rts.map_into()),
|
||||
config,
|
||||
)
|
||||
}
|
||||
|
||||
fn new_inner(
|
||||
uarte: impl Peripheral<P = U> + 'd,
|
||||
timer: impl Peripheral<P = T> + 'd,
|
||||
ppi_ch1: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
|
||||
ppi_ch2: impl Peripheral<P = impl ConfigurableChannel + 'd> + 'd,
|
||||
irq: impl Peripheral<P = U::Interrupt> + 'd,
|
||||
rxd: PeripheralRef<'d, AnyPin>,
|
||||
txd: PeripheralRef<'d, AnyPin>,
|
||||
cts: Option<PeripheralRef<'d, AnyPin>>,
|
||||
rts: Option<PeripheralRef<'d, AnyPin>>,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
let baudrate = config.baudrate;
|
||||
let (tx, rx) = Uarte::new_inner(uarte, irq, rxd, txd, cts, rts, config).split();
|
||||
|
||||
let mut timer = Timer::new(timer);
|
||||
|
||||
into_ref!(ppi_ch1, ppi_ch2);
|
||||
|
||||
let r = U::regs();
|
||||
|
||||
// BAUDRATE register values are `baudrate * 2^32 / 16000000`
|
||||
// source: https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values
|
||||
//
|
||||
// We want to stop RX if line is idle for 2 bytes worth of time
|
||||
// That is 20 bits (each byte is 1 start bit + 8 data bits + 1 stop bit)
|
||||
// This gives us the amount of 16M ticks for 20 bits.
|
||||
let timeout = 0x8000_0000 / (baudrate as u32 / 40);
|
||||
|
||||
timer.set_frequency(Frequency::F16MHz);
|
||||
timer.cc(0).write(timeout);
|
||||
timer.cc(0).short_compare_clear();
|
||||
timer.cc(0).short_compare_stop();
|
||||
|
||||
let mut ppi_ch1 = Ppi::new_one_to_two(
|
||||
ppi_ch1.map_into(),
|
||||
Event::from_reg(&r.events_rxdrdy),
|
||||
timer.task_clear(),
|
||||
timer.task_start(),
|
||||
);
|
||||
ppi_ch1.enable();
|
||||
|
||||
let mut ppi_ch2 = Ppi::new_one_to_one(
|
||||
ppi_ch2.map_into(),
|
||||
timer.cc(0).event_compare(),
|
||||
Task::from_reg(&r.tasks_stoprx),
|
||||
);
|
||||
ppi_ch2.enable();
|
||||
|
||||
Self {
|
||||
tx,
|
||||
rx: UarteRxWithIdle {
|
||||
rx,
|
||||
timer,
|
||||
ppi_ch1: ppi_ch1,
|
||||
_ppi_ch2: ppi_ch2,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Split the Uarte into a transmitter and receiver, which is
|
||||
/// particuarly useful when having two tasks correlating to
|
||||
/// transmitting and receiving.
|
||||
pub fn split(self) -> (UarteTx<'d, U>, UarteRxWithIdle<'d, U, T>) {
|
||||
(self.tx, self.rx)
|
||||
}
|
||||
|
||||
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
self.rx.read(buffer).await
|
||||
}
|
||||
|
||||
pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
||||
self.tx.write(buffer).await
|
||||
}
|
||||
|
||||
pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
self.rx.blocking_read(buffer)
|
||||
}
|
||||
|
||||
pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
||||
self.tx.blocking_write(buffer)
|
||||
}
|
||||
|
||||
pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
self.rx.read_until_idle(buffer).await
|
||||
}
|
||||
|
||||
pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
self.rx.blocking_read_until_idle(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UarteRxWithIdle<'d, U: Instance, T: TimerInstance> {
|
||||
rx: UarteRx<'d, U>,
|
||||
timer: Timer<'d, T>,
|
||||
ppi_ch1: Ppi<'d, AnyConfigurableChannel, 1, 2>,
|
||||
_ppi_ch2: Ppi<'d, AnyConfigurableChannel, 1, 1>,
|
||||
}
|
||||
|
||||
impl<'d, U: Instance, T: TimerInstance> UarteRxWithIdle<'d, U, T> {
|
||||
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
self.ppi_ch1.disable();
|
||||
self.rx.read(buffer).await
|
||||
}
|
||||
|
||||
pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
self.ppi_ch1.disable();
|
||||
self.rx.blocking_read(buffer)
|
||||
}
|
||||
|
||||
pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
if buffer.len() == 0 {
|
||||
return Err(Error::BufferZeroLength);
|
||||
}
|
||||
if buffer.len() > EASY_DMA_SIZE {
|
||||
return Err(Error::BufferTooLong);
|
||||
}
|
||||
|
||||
let ptr = buffer.as_ptr();
|
||||
let len = buffer.len();
|
||||
|
||||
let r = U::regs();
|
||||
let s = U::state();
|
||||
|
||||
self.ppi_ch1.enable();
|
||||
|
||||
let drop = OnDrop::new(|| {
|
||||
self.timer.stop();
|
||||
|
||||
r.intenclr.write(|w| w.endrx().clear());
|
||||
r.events_rxto.reset();
|
||||
r.tasks_stoprx.write(|w| unsafe { w.bits(1) });
|
||||
|
||||
while r.events_endrx.read().bits() == 0 {}
|
||||
});
|
||||
|
||||
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
|
||||
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
|
||||
|
||||
r.events_endrx.reset();
|
||||
r.intenset.write(|w| w.endrx().set());
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
|
||||
|
||||
poll_fn(|cx| {
|
||||
s.endrx_waker.register(cx.waker());
|
||||
if r.events_endrx.read().bits() != 0 {
|
||||
return Poll::Ready(());
|
||||
}
|
||||
Poll::Pending
|
||||
})
|
||||
.await;
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
let n = r.rxd.amount.read().amount().bits() as usize;
|
||||
|
||||
self.timer.stop();
|
||||
r.events_rxstarted.reset();
|
||||
|
||||
drop.defuse();
|
||||
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
pub fn blocking_read_until_idle(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
if buffer.len() == 0 {
|
||||
return Err(Error::BufferZeroLength);
|
||||
}
|
||||
if buffer.len() > EASY_DMA_SIZE {
|
||||
return Err(Error::BufferTooLong);
|
||||
}
|
||||
|
||||
let ptr = buffer.as_ptr();
|
||||
let len = buffer.len();
|
||||
|
||||
let r = U::regs();
|
||||
|
||||
self.ppi_ch1.enable();
|
||||
|
||||
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
|
||||
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) });
|
||||
|
||||
r.events_endrx.reset();
|
||||
r.intenclr.write(|w| w.endrx().clear());
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
|
||||
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
|
||||
|
||||
while r.events_endrx.read().bits() == 0 {}
|
||||
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
let n = r.rxd.amount.read().amount().bits() as usize;
|
||||
|
||||
self.timer.stop();
|
||||
r.events_rxstarted.reset();
|
||||
|
||||
Ok(n)
|
||||
}
|
||||
}
|
||||
pub(crate) mod sealed {
|
||||
use core::sync::atomic::AtomicU8;
|
||||
|
||||
@ -1006,18 +908,6 @@ mod eh02 {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, U: Instance, T: TimerInstance> embedded_hal_02::blocking::serial::Write<u8> for UarteWithIdle<'d, U, T> {
|
||||
type Error = Error;
|
||||
|
||||
fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(buffer)
|
||||
}
|
||||
|
||||
fn bflush(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-traits")]
|
||||
@ -1040,7 +930,7 @@ mod eh1 {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_1::serial::blocking::Write for Uarte<'d, T> {
|
||||
impl<'d, T: Instance> embedded_hal_1::serial::Write for Uarte<'d, T> {
|
||||
fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(buffer)
|
||||
}
|
||||
@ -1054,7 +944,7 @@ mod eh1 {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_1::serial::blocking::Write for UarteTx<'d, T> {
|
||||
impl<'d, T: Instance> embedded_hal_1::serial::Write for UarteTx<'d, T> {
|
||||
fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(buffer)
|
||||
}
|
||||
@ -1067,84 +957,59 @@ mod eh1 {
|
||||
impl<'d, T: Instance> embedded_hal_1::serial::ErrorType for UarteRx<'d, T> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, U: Instance, T: TimerInstance> embedded_hal_1::serial::ErrorType for UarteWithIdle<'d, U, T> {
|
||||
type Error = Error;
|
||||
}
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(feature = "unstable-traits", feature = "nightly", feature = "_todo_embedded_hal_serial"))] {
|
||||
use core::future::Future;
|
||||
#[cfg(all(
|
||||
feature = "unstable-traits",
|
||||
feature = "nightly",
|
||||
feature = "_todo_embedded_hal_serial"
|
||||
))]
|
||||
mod eha {
|
||||
use core::future::Future;
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::serial::Read for Uarte<'d, T> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
use super::*;
|
||||
|
||||
fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.read(buffer)
|
||||
}
|
||||
impl<'d, T: Instance> embedded_hal_async::serial::Read for Uarte<'d, T> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.read(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::serial::Write for Uarte<'d, T> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.write(buffer)
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::serial::Write for Uarte<'d, T> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.write(buffer)
|
||||
}
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
async move { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
impl<'d, T: Instance> embedded_hal_async::serial::Write for UarteTx<'d, T> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
async move { Ok(()) }
|
||||
}
|
||||
fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.write(buffer)
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::serial::Write for UarteTx<'d, T> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.write(buffer)
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
async move { Ok(()) }
|
||||
}
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
async move { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::serial::Read for UarteRx<'d, T> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
impl<'d, T: Instance> embedded_hal_async::serial::Read for UarteRx<'d, T> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.read(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Read
|
||||
for UarteWithIdle<'d, U, T>
|
||||
{
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.read(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, U: Instance, T: TimerInstance> embedded_hal_async::serial::Write
|
||||
for UarteWithIdle<'d, U, T>
|
||||
{
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buffer: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.write(buffer)
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
async move { Ok(()) }
|
||||
}
|
||||
fn read<'a>(&'a mut self, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.read(buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#![macro_use]
|
||||
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::marker::PhantomData;
|
||||
use core::mem::MaybeUninit;
|
||||
use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering};
|
||||
@ -8,11 +9,8 @@ use core::task::Poll;
|
||||
use cortex_m::peripheral::NVIC;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
pub use embassy_usb;
|
||||
use embassy_usb::driver::{self, EndpointError, Event, Unsupported};
|
||||
use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection};
|
||||
use futures::future::poll_fn;
|
||||
use futures::Future;
|
||||
use embassy_usb_driver as driver;
|
||||
use embassy_usb_driver::{Direction, EndpointAddress, EndpointError, EndpointInfo, EndpointType, Event, Unsupported};
|
||||
use pac::usbd::RegisterBlock;
|
||||
|
||||
use crate::interrupt::{Interrupt, InterruptExt};
|
||||
@ -244,7 +242,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P>
|
||||
interval: u8,
|
||||
) -> Result<Self::EndpointIn, driver::EndpointAllocError> {
|
||||
let index = self.alloc_in.allocate(ep_type)?;
|
||||
let ep_addr = EndpointAddress::from_parts(index, UsbDirection::In);
|
||||
let ep_addr = EndpointAddress::from_parts(index, Direction::In);
|
||||
Ok(Endpoint::new(EndpointInfo {
|
||||
addr: ep_addr,
|
||||
ep_type,
|
||||
@ -260,7 +258,7 @@ impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P>
|
||||
interval: u8,
|
||||
) -> Result<Self::EndpointOut, driver::EndpointAllocError> {
|
||||
let index = self.alloc_out.allocate(ep_type)?;
|
||||
let ep_addr = EndpointAddress::from_parts(index, UsbDirection::Out);
|
||||
let ep_addr = EndpointAddress::from_parts(index, Direction::Out);
|
||||
Ok(Endpoint::new(EndpointInfo {
|
||||
addr: ep_addr,
|
||||
ep_type,
|
||||
@ -315,7 +313,7 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> {
|
||||
}
|
||||
})
|
||||
.await;
|
||||
regs.eventcause.write(|w| w.ready().set_bit()); // Write 1 to clear.
|
||||
regs.eventcause.write(|w| w.ready().clear_bit_by_one());
|
||||
|
||||
errata::post_enable();
|
||||
|
||||
@ -369,24 +367,24 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> {
|
||||
let r = regs.eventcause.read();
|
||||
|
||||
if r.isooutcrc().bit() {
|
||||
regs.eventcause.write(|w| w.isooutcrc().set_bit());
|
||||
regs.eventcause.write(|w| w.isooutcrc().detected());
|
||||
trace!("USB event: isooutcrc");
|
||||
}
|
||||
if r.usbwuallowed().bit() {
|
||||
regs.eventcause.write(|w| w.usbwuallowed().set_bit());
|
||||
regs.eventcause.write(|w| w.usbwuallowed().allowed());
|
||||
trace!("USB event: usbwuallowed");
|
||||
}
|
||||
if r.suspend().bit() {
|
||||
regs.eventcause.write(|w| w.suspend().set_bit());
|
||||
regs.eventcause.write(|w| w.suspend().detected());
|
||||
regs.lowpower.write(|w| w.lowpower().low_power());
|
||||
return Poll::Ready(Event::Suspend);
|
||||
}
|
||||
if r.resume().bit() {
|
||||
regs.eventcause.write(|w| w.resume().set_bit());
|
||||
regs.eventcause.write(|w| w.resume().detected());
|
||||
return Poll::Ready(Event::Resume);
|
||||
}
|
||||
if r.ready().bit() {
|
||||
regs.eventcause.write(|w| w.ready().set_bit());
|
||||
regs.eventcause.write(|w| w.ready().ready());
|
||||
trace!("USB event: ready");
|
||||
}
|
||||
|
||||
@ -429,8 +427,8 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> {
|
||||
let regs = T::regs();
|
||||
let i = ep_addr.index();
|
||||
match ep_addr.direction() {
|
||||
UsbDirection::Out => regs.halted.epout[i].read().getstatus().is_halted(),
|
||||
UsbDirection::In => regs.halted.epin[i].read().getstatus().is_halted(),
|
||||
Direction::Out => regs.halted.epout[i].read().getstatus().is_halted(),
|
||||
Direction::In => regs.halted.epin[i].read().getstatus().is_halted(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -443,7 +441,7 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> {
|
||||
debug!("endpoint_set_enabled {:?} {}", ep_addr, enabled);
|
||||
|
||||
match ep_addr.direction() {
|
||||
UsbDirection::In => {
|
||||
Direction::In => {
|
||||
let mut was_enabled = false;
|
||||
regs.epinen.modify(|r, w| {
|
||||
let mut bits = r.bits();
|
||||
@ -467,7 +465,7 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> {
|
||||
|
||||
In::waker(i).wake();
|
||||
}
|
||||
UsbDirection::Out => {
|
||||
Direction::Out => {
|
||||
regs.epouten.modify(|r, w| {
|
||||
let mut bits = r.bits();
|
||||
if enabled {
|
||||
@ -514,7 +512,7 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> {
|
||||
} else if r.resume().bit() {
|
||||
Poll::Ready(())
|
||||
} else if r.usbwuallowed().bit() {
|
||||
regs.eventcause.write(|w| w.usbwuallowed().set_bit());
|
||||
regs.eventcause.write(|w| w.usbwuallowed().allowed());
|
||||
|
||||
regs.dpdmvalue.write(|w| w.state().resume());
|
||||
regs.tasks_dpdmdrive.write(|w| w.tasks_dpdmdrive().set_bit());
|
||||
|
@ -2,17 +2,18 @@
|
||||
name = "embassy-rp"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-rp-v$VERSION/embassy-rp/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-rp/src/"
|
||||
features = ["nightly", "defmt", "unstable-pac", "unstable-traits"]
|
||||
features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "time-driver"]
|
||||
flavors = [
|
||||
{ name = "rp2040", target = "thumbv6m-none-eabi" },
|
||||
]
|
||||
|
||||
[features]
|
||||
defmt = ["dep:defmt", "embassy-usb?/defmt"]
|
||||
defmt = ["dep:defmt", "embassy-usb-driver?/defmt"]
|
||||
|
||||
# Reexport the PAC for the currently enabled chip at `embassy_rp::pac`.
|
||||
# This is unstable because semver-minor (non-breaking) releases of embassy-rp may major-bump (breaking) the PAC version.
|
||||
@ -20,21 +21,28 @@ defmt = ["dep:defmt", "embassy-usb?/defmt"]
|
||||
# There are no plans to make this stable.
|
||||
unstable-pac = []
|
||||
|
||||
time-driver = []
|
||||
|
||||
rom-func-cache = []
|
||||
intrinsics = []
|
||||
rom-v2-intrinsics = []
|
||||
|
||||
# Enable nightly-only features
|
||||
nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb"]
|
||||
nightly = ["embassy-executor/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-async traits if `nightly` is set as well.
|
||||
unstable-traits = ["embedded-hal-1"]
|
||||
unstable-traits = ["embedded-hal-1", "embedded-hal-nb"]
|
||||
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
|
||||
embassy-executor = { version = "0.1.0", path = "../embassy-executor" }
|
||||
embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ "tick-1mhz" ] }
|
||||
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-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" }
|
||||
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
|
||||
embassy-usb = {version = "0.1.0", path = "../embassy-usb", optional = true }
|
||||
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true }
|
||||
atomic-polyfill = "1.0.1"
|
||||
defmt = { version = "0.3", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
@ -44,10 +52,13 @@ cortex-m-rt = ">=0.6.15,<0.8"
|
||||
cortex-m = "0.7.6"
|
||||
critical-section = "1.1"
|
||||
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
|
||||
chrono = { version = "0.4", default-features = false, optional = true }
|
||||
embedded-io = { version = "0.3.1", features = ["async"], optional = true }
|
||||
|
||||
rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] }
|
||||
#rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] }
|
||||
|
||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true}
|
||||
embedded-hal-async = { version = "0.1.0-alpha.1", optional = true}
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true}
|
||||
embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true}
|
||||
embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true}
|
||||
|
@ -122,7 +122,7 @@ pub(crate) fn clk_peri_freq() -> u32 {
|
||||
125_000_000
|
||||
}
|
||||
|
||||
pub(crate) fn _clk_rtc_freq() -> u32 {
|
||||
pub(crate) fn clk_rtc_freq() -> u32 {
|
||||
46875
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
use core::task::{Context, Poll};
|
||||
@ -5,7 +6,6 @@ use core::task::{Context, Poll};
|
||||
use embassy_cortex_m::interrupt::{Interrupt, InterruptExt};
|
||||
use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use futures::Future;
|
||||
use pac::dma::vals::DataSize;
|
||||
|
||||
use crate::pac::dma::vals;
|
||||
@ -40,10 +40,10 @@ pub(crate) unsafe fn init() {
|
||||
pub unsafe fn read<'a, C: Channel, W: Word>(
|
||||
ch: impl Peripheral<P = C> + 'a,
|
||||
from: *const W,
|
||||
to: &mut [W],
|
||||
to: *mut [W],
|
||||
dreq: u8,
|
||||
) -> Transfer<'a, C> {
|
||||
let (to_ptr, len) = crate::dma::slice_ptr_parts_mut(to);
|
||||
let (to_ptr, len) = crate::dma::slice_ptr_parts(to);
|
||||
copy_inner(
|
||||
ch,
|
||||
from as *const u32,
|
||||
@ -58,7 +58,7 @@ pub unsafe fn read<'a, C: Channel, W: Word>(
|
||||
|
||||
pub unsafe fn write<'a, C: Channel, W: Word>(
|
||||
ch: impl Peripheral<P = C> + 'a,
|
||||
from: &[W],
|
||||
from: *const [W],
|
||||
to: *mut W,
|
||||
dreq: u8,
|
||||
) -> Transfer<'a, C> {
|
||||
@ -75,6 +75,25 @@ pub unsafe fn write<'a, C: Channel, W: Word>(
|
||||
)
|
||||
}
|
||||
|
||||
pub unsafe fn write_repeated<'a, C: Channel, W: Word>(
|
||||
ch: impl Peripheral<P = C> + 'a,
|
||||
to: *mut W,
|
||||
len: usize,
|
||||
dreq: u8,
|
||||
) -> Transfer<'a, C> {
|
||||
let dummy: u32 = 0;
|
||||
copy_inner(
|
||||
ch,
|
||||
&dummy as *const u32,
|
||||
to as *mut u32,
|
||||
len,
|
||||
W::size(),
|
||||
false,
|
||||
false,
|
||||
dreq,
|
||||
)
|
||||
}
|
||||
|
||||
pub unsafe fn copy<'a, C: Channel, W: Word>(
|
||||
ch: impl Peripheral<P = C> + 'a,
|
||||
from: &[W],
|
||||
|
@ -159,7 +159,7 @@ unsafe fn IO_IRQ_BANK0() {
|
||||
w.set_edge_low(pin_group, false);
|
||||
}
|
||||
InterruptTrigger::LevelHigh => {
|
||||
debug!("IO_IRQ_BANK0 pin {} LevelHigh triggered\n", pin);
|
||||
debug!("IO_IRQ_BANK0 pin {} LevelHigh triggered", pin);
|
||||
w.set_level_high(pin_group, false);
|
||||
}
|
||||
InterruptTrigger::LevelLow => {
|
||||
@ -198,7 +198,7 @@ impl<'d, T: Pin> InputFuture<'d, T> {
|
||||
critical_section::with(|_| {
|
||||
pin.int_proc().inte((pin.pin() / 8) as usize).modify(|w| match level {
|
||||
InterruptTrigger::LevelHigh => {
|
||||
debug!("InputFuture::new enable LevelHigh for pin {} \n", pin.pin());
|
||||
debug!("InputFuture::new enable LevelHigh for pin {}", pin.pin());
|
||||
w.set_level_high(pin_group, true);
|
||||
}
|
||||
InterruptTrigger::LevelLow => {
|
||||
@ -245,45 +245,45 @@ impl<'d, T: Pin> Future for InputFuture<'d, T> {
|
||||
// the pin and if it has been disabled that means it was done by the
|
||||
// interrupt service routine, so we then know that the event/trigger
|
||||
// happened and Poll::Ready will be returned.
|
||||
debug!("{:?} for pin {}\n", self.level, self.pin.pin());
|
||||
debug!("{:?} for pin {}", self.level, self.pin.pin());
|
||||
match self.level {
|
||||
InterruptTrigger::AnyEdge => {
|
||||
if !inte.edge_high(pin_group) && !inte.edge_low(pin_group) {
|
||||
#[rustfmt::skip]
|
||||
debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin());
|
||||
debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin());
|
||||
return Poll::Ready(());
|
||||
}
|
||||
}
|
||||
InterruptTrigger::LevelHigh => {
|
||||
if !inte.level_high(pin_group) {
|
||||
#[rustfmt::skip]
|
||||
debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin());
|
||||
debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin());
|
||||
return Poll::Ready(());
|
||||
}
|
||||
}
|
||||
InterruptTrigger::LevelLow => {
|
||||
if !inte.level_low(pin_group) {
|
||||
#[rustfmt::skip]
|
||||
debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin());
|
||||
debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin());
|
||||
return Poll::Ready(());
|
||||
}
|
||||
}
|
||||
InterruptTrigger::EdgeHigh => {
|
||||
if !inte.edge_high(pin_group) {
|
||||
#[rustfmt::skip]
|
||||
debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin());
|
||||
debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin());
|
||||
return Poll::Ready(());
|
||||
}
|
||||
}
|
||||
InterruptTrigger::EdgeLow => {
|
||||
if !inte.edge_low(pin_group) {
|
||||
#[rustfmt::skip]
|
||||
debug!("{:?} for pin {} was cleared, return Poll::Ready\n", self.level, self.pin.pin());
|
||||
debug!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin());
|
||||
return Poll::Ready(());
|
||||
}
|
||||
}
|
||||
}
|
||||
debug!("InputFuture::poll return Poll::Pending\n");
|
||||
debug!("InputFuture::poll return Poll::Pending");
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
@ -599,12 +599,12 @@ pub(crate) mod sealed {
|
||||
fn pin_bank(&self) -> u8;
|
||||
|
||||
#[inline]
|
||||
fn pin(&self) -> u8 {
|
||||
fn _pin(&self) -> u8 {
|
||||
self.pin_bank() & 0x1f
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn bank(&self) -> Bank {
|
||||
fn _bank(&self) -> Bank {
|
||||
if self.pin_bank() & 0x20 == 0 {
|
||||
Bank::Bank0
|
||||
} else {
|
||||
@ -613,35 +613,35 @@ pub(crate) mod sealed {
|
||||
}
|
||||
|
||||
fn io(&self) -> pac::io::Gpio {
|
||||
let block = match self.bank() {
|
||||
let block = match self._bank() {
|
||||
Bank::Bank0 => crate::pac::IO_BANK0,
|
||||
Bank::Qspi => crate::pac::IO_QSPI,
|
||||
};
|
||||
block.gpio(self.pin() as _)
|
||||
block.gpio(self._pin() as _)
|
||||
}
|
||||
|
||||
fn pad_ctrl(&self) -> Reg<pac::pads::regs::GpioCtrl, RW> {
|
||||
let block = match self.bank() {
|
||||
let block = match self._bank() {
|
||||
Bank::Bank0 => crate::pac::PADS_BANK0,
|
||||
Bank::Qspi => crate::pac::PADS_QSPI,
|
||||
};
|
||||
block.gpio(self.pin() as _)
|
||||
block.gpio(self._pin() as _)
|
||||
}
|
||||
|
||||
fn sio_out(&self) -> pac::sio::Gpio {
|
||||
SIO.gpio_out(self.bank() as _)
|
||||
SIO.gpio_out(self._bank() as _)
|
||||
}
|
||||
|
||||
fn sio_oe(&self) -> pac::sio::Gpio {
|
||||
SIO.gpio_oe(self.bank() as _)
|
||||
SIO.gpio_oe(self._bank() as _)
|
||||
}
|
||||
|
||||
fn sio_in(&self) -> Reg<u32, RW> {
|
||||
SIO.gpio_in(self.bank() as _)
|
||||
SIO.gpio_in(self._bank() as _)
|
||||
}
|
||||
|
||||
fn int_proc(&self) -> pac::io::Int {
|
||||
let io_block = match self.bank() {
|
||||
let io_block = match self._bank() {
|
||||
Bank::Bank0 => crate::pac::IO_BANK0,
|
||||
Bank::Qspi => crate::pac::IO_QSPI,
|
||||
};
|
||||
@ -658,6 +658,18 @@ pub trait Pin: Peripheral<P = Self> + Into<AnyPin> + sealed::Pin + Sized + 'stat
|
||||
pin_bank: self.pin_bank(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the pin number within a bank
|
||||
#[inline]
|
||||
fn pin(&self) -> u8 {
|
||||
self._pin()
|
||||
}
|
||||
|
||||
/// Returns the bank of this pin
|
||||
#[inline]
|
||||
fn bank(&self) -> Bank {
|
||||
self._bank()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AnyPin {
|
||||
@ -867,7 +879,7 @@ mod eh1 {
|
||||
type Error = Infallible;
|
||||
}
|
||||
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::blocking::InputPin for Input<'d, T> {
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Input<'d, T> {
|
||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_high())
|
||||
}
|
||||
@ -881,7 +893,7 @@ mod eh1 {
|
||||
type Error = Infallible;
|
||||
}
|
||||
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for Output<'d, T> {
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Output<'d, T> {
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(self.set_high())
|
||||
}
|
||||
@ -891,7 +903,7 @@ mod eh1 {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for Output<'d, T> {
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Output<'d, T> {
|
||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_set_high())
|
||||
}
|
||||
@ -901,7 +913,7 @@ mod eh1 {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::blocking::ToggleableOutputPin for Output<'d, T> {
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::ToggleableOutputPin for Output<'d, T> {
|
||||
fn toggle(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(self.toggle())
|
||||
}
|
||||
@ -911,7 +923,7 @@ mod eh1 {
|
||||
type Error = Infallible;
|
||||
}
|
||||
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for OutputOpenDrain<'d, T> {
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for OutputOpenDrain<'d, T> {
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(self.set_high())
|
||||
}
|
||||
@ -921,7 +933,7 @@ mod eh1 {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for OutputOpenDrain<'d, T> {
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for OutputOpenDrain<'d, T> {
|
||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_set_high())
|
||||
}
|
||||
@ -931,7 +943,7 @@ mod eh1 {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::blocking::ToggleableOutputPin for OutputOpenDrain<'d, T> {
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::ToggleableOutputPin for OutputOpenDrain<'d, T> {
|
||||
fn toggle(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(self.toggle())
|
||||
}
|
||||
@ -941,7 +953,7 @@ mod eh1 {
|
||||
type Error = Infallible;
|
||||
}
|
||||
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::blocking::InputPin for Flex<'d, T> {
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::InputPin for Flex<'d, T> {
|
||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_high())
|
||||
}
|
||||
@ -951,7 +963,7 @@ mod eh1 {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for Flex<'d, T> {
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::OutputPin for Flex<'d, T> {
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(self.set_high())
|
||||
}
|
||||
@ -961,7 +973,7 @@ mod eh1 {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for Flex<'d, T> {
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::StatefulOutputPin for Flex<'d, T> {
|
||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_set_high())
|
||||
}
|
||||
@ -971,7 +983,7 @@ mod eh1 {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::blocking::ToggleableOutputPin for Flex<'d, T> {
|
||||
impl<'d, T: Pin> embedded_hal_1::digital::ToggleableOutputPin for Flex<'d, T> {
|
||||
fn toggle(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(self.toggle())
|
||||
}
|
||||
|
919
embassy-rp/src/i2c.rs
Normal file
919
embassy-rp/src/i2c.rs
Normal file
@ -0,0 +1,919 @@
|
||||
use core::future;
|
||||
use core::marker::PhantomData;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_cortex_m::interrupt::InterruptExt;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use pac::i2c;
|
||||
|
||||
use crate::gpio::sealed::Pin;
|
||||
use crate::gpio::AnyPin;
|
||||
use crate::{pac, peripherals, Peripheral};
|
||||
|
||||
/// I2C error abort reason
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum AbortReason {
|
||||
/// A bus operation was not acknowledged, e.g. due to the addressed device
|
||||
/// not being available on the bus or the device not being ready to process
|
||||
/// requests at the moment
|
||||
NoAcknowledge,
|
||||
/// The arbitration was lost, e.g. electrical problems with the clock signal
|
||||
ArbitrationLoss,
|
||||
Other(u32),
|
||||
}
|
||||
|
||||
/// I2C error
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error {
|
||||
/// I2C abort with error
|
||||
Abort(AbortReason),
|
||||
/// User passed in a read buffer that was 0 length
|
||||
InvalidReadBufferLength,
|
||||
/// User passed in a write buffer that was 0 length
|
||||
InvalidWriteBufferLength,
|
||||
/// Target i2c address is out of range
|
||||
AddressOutOfRange(u16),
|
||||
/// Target i2c address is reserved
|
||||
AddressReserved(u16),
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Config {
|
||||
pub frequency: u32,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self { frequency: 100_000 }
|
||||
}
|
||||
}
|
||||
|
||||
const FIFO_SIZE: u8 = 16;
|
||||
|
||||
pub struct I2c<'d, T: Instance, M: Mode> {
|
||||
phantom: PhantomData<(&'d mut T, M)>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> I2c<'d, T, Blocking> {
|
||||
pub fn new_blocking(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
|
||||
sda: impl Peripheral<P = impl SdaPin<T>> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(scl, sda);
|
||||
Self::new_inner(peri, scl.map_into(), sda.map_into(), config)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> I2c<'d, T, Async> {
|
||||
pub fn new_async(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
scl: impl Peripheral<P = impl SclPin<T>> + 'd,
|
||||
sda: impl Peripheral<P = impl SdaPin<T>> + 'd,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(scl, sda, irq);
|
||||
|
||||
let i2c = Self::new_inner(peri, scl.map_into(), sda.map_into(), config);
|
||||
|
||||
irq.set_handler(Self::on_interrupt);
|
||||
unsafe {
|
||||
let i2c = T::regs();
|
||||
|
||||
// mask everything initially
|
||||
i2c.ic_intr_mask().write_value(i2c::regs::IcIntrMask(0));
|
||||
}
|
||||
irq.unpend();
|
||||
debug_assert!(!irq.is_pending());
|
||||
irq.enable();
|
||||
|
||||
i2c
|
||||
}
|
||||
|
||||
/// Calls `f` to check if we are ready or not.
|
||||
/// If not, `g` is called once the waker is set (to eg enable the required interrupts).
|
||||
async fn wait_on<F, U, G>(&mut self, mut f: F, mut g: G) -> U
|
||||
where
|
||||
F: FnMut(&mut Self) -> Poll<U>,
|
||||
G: FnMut(&mut Self),
|
||||
{
|
||||
future::poll_fn(|cx| {
|
||||
let r = f(self);
|
||||
|
||||
if r.is_pending() {
|
||||
T::waker().register(cx.waker());
|
||||
g(self);
|
||||
}
|
||||
r
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
// Mask interrupts and wake any task waiting for this interrupt
|
||||
unsafe fn on_interrupt(_: *mut ()) {
|
||||
let i2c = T::regs();
|
||||
i2c.ic_intr_mask().write_value(pac::i2c::regs::IcIntrMask::default());
|
||||
|
||||
T::waker().wake();
|
||||
}
|
||||
|
||||
async fn read_async_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> {
|
||||
if buffer.is_empty() {
|
||||
return Err(Error::InvalidReadBufferLength);
|
||||
}
|
||||
|
||||
let p = T::regs();
|
||||
|
||||
let mut remaining = buffer.len();
|
||||
let mut remaining_queue = buffer.len();
|
||||
|
||||
let mut abort_reason = Ok(());
|
||||
|
||||
while remaining > 0 {
|
||||
// Waggle SCK - basically the same as write
|
||||
let tx_fifo_space = Self::tx_fifo_capacity();
|
||||
let mut batch = 0;
|
||||
|
||||
debug_assert!(remaining_queue > 0);
|
||||
|
||||
for _ in 0..remaining_queue.min(tx_fifo_space as usize) {
|
||||
remaining_queue -= 1;
|
||||
let last = remaining_queue == 0;
|
||||
batch += 1;
|
||||
|
||||
unsafe {
|
||||
p.ic_data_cmd().write(|w| {
|
||||
w.set_restart(restart && remaining_queue == buffer.len() - 1);
|
||||
w.set_stop(last && send_stop);
|
||||
w.set_cmd(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// We've either run out of txfifo or just plain finished setting up
|
||||
// the clocks for the message - either way we need to wait for rx
|
||||
// data.
|
||||
|
||||
debug_assert!(batch > 0);
|
||||
let res = self
|
||||
.wait_on(
|
||||
|me| {
|
||||
let rxfifo = Self::rx_fifo_len();
|
||||
if let Err(abort_reason) = me.read_and_clear_abort_reason() {
|
||||
Poll::Ready(Err(abort_reason))
|
||||
} else if rxfifo >= batch {
|
||||
Poll::Ready(Ok(rxfifo))
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
},
|
||||
|_me| unsafe {
|
||||
// Set the read threshold to the number of bytes we're
|
||||
// expecting so we don't get spurious interrupts.
|
||||
p.ic_rx_tl().write(|w| w.set_rx_tl(batch - 1));
|
||||
|
||||
p.ic_intr_mask().modify(|w| {
|
||||
w.set_m_rx_full(true);
|
||||
w.set_m_tx_abrt(true);
|
||||
});
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
match res {
|
||||
Err(reason) => {
|
||||
abort_reason = Err(reason);
|
||||
break;
|
||||
}
|
||||
Ok(rxfifo) => {
|
||||
// Fetch things from rx fifo. We're assuming we're the only
|
||||
// rxfifo reader, so nothing else can take things from it.
|
||||
let rxbytes = (rxfifo as usize).min(remaining);
|
||||
let received = buffer.len() - remaining;
|
||||
for b in &mut buffer[received..received + rxbytes] {
|
||||
*b = unsafe { p.ic_data_cmd().read().dat() };
|
||||
}
|
||||
remaining -= rxbytes;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
self.wait_stop_det(abort_reason, send_stop).await
|
||||
}
|
||||
|
||||
async fn write_async_internal(
|
||||
&mut self,
|
||||
bytes: impl IntoIterator<Item = u8>,
|
||||
send_stop: bool,
|
||||
) -> Result<(), Error> {
|
||||
let p = T::regs();
|
||||
|
||||
let mut bytes = bytes.into_iter().peekable();
|
||||
|
||||
let res = 'xmit: loop {
|
||||
let tx_fifo_space = Self::tx_fifo_capacity();
|
||||
|
||||
for _ in 0..tx_fifo_space {
|
||||
if let Some(byte) = bytes.next() {
|
||||
let last = bytes.peek().is_none();
|
||||
|
||||
unsafe {
|
||||
p.ic_data_cmd().write(|w| {
|
||||
w.set_stop(last && send_stop);
|
||||
w.set_cmd(false);
|
||||
w.set_dat(byte);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
break 'xmit Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let res = self
|
||||
.wait_on(
|
||||
|me| {
|
||||
if let abort_reason @ Err(_) = me.read_and_clear_abort_reason() {
|
||||
Poll::Ready(abort_reason)
|
||||
} else if !Self::tx_fifo_full() {
|
||||
// resume if there's any space free in the tx fifo
|
||||
Poll::Ready(Ok(()))
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
},
|
||||
|_me| unsafe {
|
||||
// Set tx "free" threshold a little high so that we get
|
||||
// woken before the fifo completely drains to minimize
|
||||
// transfer stalls.
|
||||
p.ic_tx_tl().write(|w| w.set_tx_tl(1));
|
||||
|
||||
p.ic_intr_mask().modify(|w| {
|
||||
w.set_m_tx_empty(true);
|
||||
w.set_m_tx_abrt(true);
|
||||
})
|
||||
},
|
||||
)
|
||||
.await;
|
||||
if res.is_err() {
|
||||
break res;
|
||||
}
|
||||
};
|
||||
|
||||
self.wait_stop_det(res, send_stop).await
|
||||
}
|
||||
|
||||
/// Helper to wait for a stop bit, for both tx and rx. If we had an abort,
|
||||
/// then we'll get a hardware-generated stop, otherwise wait for a stop if
|
||||
/// we're expecting it.
|
||||
///
|
||||
/// Also handles an abort which arises while processing the tx fifo.
|
||||
async fn wait_stop_det(&mut self, had_abort: Result<(), Error>, do_stop: bool) -> Result<(), Error> {
|
||||
if had_abort.is_err() || do_stop {
|
||||
let p = T::regs();
|
||||
|
||||
let had_abort2 = self
|
||||
.wait_on(
|
||||
|me| unsafe {
|
||||
// We could see an abort while processing fifo backlog,
|
||||
// so handle it here.
|
||||
let abort = me.read_and_clear_abort_reason();
|
||||
if had_abort.is_ok() && abort.is_err() {
|
||||
Poll::Ready(abort)
|
||||
} else if p.ic_raw_intr_stat().read().stop_det() {
|
||||
Poll::Ready(Ok(()))
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
},
|
||||
|_me| unsafe {
|
||||
p.ic_intr_mask().modify(|w| {
|
||||
w.set_m_stop_det(true);
|
||||
w.set_m_tx_abrt(true);
|
||||
});
|
||||
},
|
||||
)
|
||||
.await;
|
||||
unsafe {
|
||||
p.ic_clr_stop_det().read();
|
||||
}
|
||||
|
||||
had_abort.and(had_abort2)
|
||||
} else {
|
||||
had_abort
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn read_async(&mut self, addr: u16, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
Self::setup(addr)?;
|
||||
self.read_async_internal(buffer, false, true).await
|
||||
}
|
||||
|
||||
pub async fn write_async(&mut self, addr: u16, bytes: impl IntoIterator<Item = u8>) -> Result<(), Error> {
|
||||
Self::setup(addr)?;
|
||||
self.write_async_internal(bytes, true).await
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + 'd, M: Mode> I2c<'d, T, M> {
|
||||
fn new_inner(
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
scl: PeripheralRef<'d, AnyPin>,
|
||||
sda: PeripheralRef<'d, AnyPin>,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(_peri);
|
||||
|
||||
assert!(config.frequency <= 1_000_000);
|
||||
assert!(config.frequency > 0);
|
||||
|
||||
let p = T::regs();
|
||||
|
||||
unsafe {
|
||||
let reset = T::reset();
|
||||
crate::reset::reset(reset);
|
||||
crate::reset::unreset_wait(reset);
|
||||
|
||||
p.ic_enable().write(|w| w.set_enable(false));
|
||||
|
||||
// Select controller mode & speed
|
||||
p.ic_con().modify(|w| {
|
||||
// Always use "fast" mode (<= 400 kHz, works fine for standard
|
||||
// mode too)
|
||||
w.set_speed(i2c::vals::Speed::FAST);
|
||||
w.set_master_mode(true);
|
||||
w.set_ic_slave_disable(true);
|
||||
w.set_ic_restart_en(true);
|
||||
w.set_tx_empty_ctrl(true);
|
||||
});
|
||||
|
||||
// Set FIFO watermarks to 1 to make things simpler. This is encoded
|
||||
// by a register value of 0.
|
||||
p.ic_tx_tl().write(|w| w.set_tx_tl(0));
|
||||
p.ic_rx_tl().write(|w| w.set_rx_tl(0));
|
||||
|
||||
// Configure SCL & SDA pins
|
||||
scl.io().ctrl().write(|w| w.set_funcsel(3));
|
||||
sda.io().ctrl().write(|w| w.set_funcsel(3));
|
||||
|
||||
scl.pad_ctrl().write(|w| {
|
||||
w.set_schmitt(true);
|
||||
w.set_ie(true);
|
||||
w.set_od(false);
|
||||
w.set_pue(true);
|
||||
w.set_pde(false);
|
||||
});
|
||||
sda.pad_ctrl().write(|w| {
|
||||
w.set_schmitt(true);
|
||||
w.set_ie(true);
|
||||
w.set_od(false);
|
||||
w.set_pue(true);
|
||||
w.set_pde(false);
|
||||
});
|
||||
|
||||
// Configure baudrate
|
||||
|
||||
// There are some subtleties to I2C timing which we are completely
|
||||
// ignoring here See:
|
||||
// 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 period = (clk_base + config.frequency / 2) / config.frequency;
|
||||
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
|
||||
|
||||
// Check for out-of-range divisors:
|
||||
assert!(hcnt <= 0xffff);
|
||||
assert!(lcnt <= 0xffff);
|
||||
assert!(hcnt >= 8);
|
||||
assert!(lcnt >= 8);
|
||||
|
||||
// Per I2C-bus specification a device in standard or fast mode must
|
||||
// internally provide a hold time of at least 300ns for the SDA
|
||||
// signal to bridge the undefined region of the falling edge of SCL.
|
||||
// A smaller hold time of 120ns is used for fast mode plus.
|
||||
let sda_tx_hold_count = if config.frequency < 1_000_000 {
|
||||
// sda_tx_hold_count = clk_base [cycles/s] * 300ns * (1s /
|
||||
// 1e9ns) Reduce 300/1e9 to 3/1e7 to avoid numbers that don't
|
||||
// fit in uint. Add 1 to avoid division truncation.
|
||||
((clk_base * 3) / 10_000_000) + 1
|
||||
} else {
|
||||
// fast mode plus requires a clk_base > 32MHz
|
||||
assert!(clk_base >= 32_000_000);
|
||||
|
||||
// sda_tx_hold_count = clk_base [cycles/s] * 120ns * (1s /
|
||||
// 1e9ns) Reduce 120/1e9 to 3/25e6 to avoid numbers that don't
|
||||
// fit in uint. Add 1 to avoid division truncation.
|
||||
((clk_base * 3) / 25_000_000) + 1
|
||||
};
|
||||
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_lcnt().write(|w| w.set_ic_fs_scl_lcnt(lcnt as u16));
|
||||
p.ic_fs_spklen()
|
||||
.write(|w| w.set_ic_fs_spklen(if lcnt < 16 { 1 } else { (lcnt / 16) as u8 }));
|
||||
p.ic_sda_hold()
|
||||
.modify(|w| w.set_ic_sda_tx_hold(sda_tx_hold_count as u16));
|
||||
|
||||
// Enable I2C block
|
||||
p.ic_enable().write(|w| w.set_enable(true));
|
||||
}
|
||||
|
||||
Self { phantom: PhantomData }
|
||||
}
|
||||
|
||||
fn setup(addr: u16) -> Result<(), Error> {
|
||||
if addr >= 0x80 {
|
||||
return Err(Error::AddressOutOfRange(addr));
|
||||
}
|
||||
|
||||
if i2c_reserved_addr(addr) {
|
||||
return Err(Error::AddressReserved(addr));
|
||||
}
|
||||
|
||||
let p = T::regs();
|
||||
unsafe {
|
||||
p.ic_enable().write(|w| w.set_enable(false));
|
||||
p.ic_tar().write(|w| w.set_ic_tar(addr));
|
||||
p.ic_enable().write(|w| w.set_enable(true));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn tx_fifo_full() -> bool {
|
||||
Self::tx_fifo_capacity() == 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn tx_fifo_capacity() -> u8 {
|
||||
let p = T::regs();
|
||||
unsafe { FIFO_SIZE - p.ic_txflr().read().txflr() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn rx_fifo_len() -> u8 {
|
||||
let p = T::regs();
|
||||
unsafe { p.ic_rxflr().read().rxflr() }
|
||||
}
|
||||
|
||||
fn read_and_clear_abort_reason(&mut self) -> Result<(), Error> {
|
||||
let p = T::regs();
|
||||
unsafe {
|
||||
let abort_reason = p.ic_tx_abrt_source().read();
|
||||
if abort_reason.0 != 0 {
|
||||
// Note clearing the abort flag also clears the reason, and this
|
||||
// instance of flag is clear-on-read! Note also the
|
||||
// IC_CLR_TX_ABRT register always reads as 0.
|
||||
p.ic_clr_tx_abrt().read();
|
||||
|
||||
let reason = if abort_reason.abrt_7b_addr_noack()
|
||||
| abort_reason.abrt_10addr1_noack()
|
||||
| abort_reason.abrt_10addr2_noack()
|
||||
{
|
||||
AbortReason::NoAcknowledge
|
||||
} else if abort_reason.arb_lost() {
|
||||
AbortReason::ArbitrationLoss
|
||||
} else {
|
||||
AbortReason::Other(abort_reason.0)
|
||||
};
|
||||
|
||||
Err(Error::Abort(reason))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_blocking_internal(&mut self, buffer: &mut [u8], restart: bool, send_stop: bool) -> Result<(), Error> {
|
||||
if buffer.is_empty() {
|
||||
return Err(Error::InvalidReadBufferLength);
|
||||
}
|
||||
|
||||
let p = T::regs();
|
||||
let lastindex = buffer.len() - 1;
|
||||
for (i, byte) in buffer.iter_mut().enumerate() {
|
||||
let first = i == 0;
|
||||
let last = i == lastindex;
|
||||
|
||||
// NOTE(unsafe) We have &mut self
|
||||
unsafe {
|
||||
// wait until there is space in the FIFO to write the next byte
|
||||
while Self::tx_fifo_full() {}
|
||||
|
||||
p.ic_data_cmd().write(|w| {
|
||||
w.set_restart(restart && first);
|
||||
w.set_stop(send_stop && last);
|
||||
|
||||
w.set_cmd(true);
|
||||
});
|
||||
|
||||
while Self::rx_fifo_len() == 0 {
|
||||
self.read_and_clear_abort_reason()?;
|
||||
}
|
||||
|
||||
*byte = p.ic_data_cmd().read().dat();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_blocking_internal(&mut self, bytes: &[u8], send_stop: bool) -> Result<(), Error> {
|
||||
if bytes.is_empty() {
|
||||
return Err(Error::InvalidWriteBufferLength);
|
||||
}
|
||||
|
||||
let p = T::regs();
|
||||
|
||||
for (i, byte) in bytes.iter().enumerate() {
|
||||
let last = i == bytes.len() - 1;
|
||||
|
||||
// NOTE(unsafe) We have &mut self
|
||||
unsafe {
|
||||
p.ic_data_cmd().write(|w| {
|
||||
w.set_stop(send_stop && last);
|
||||
w.set_dat(*byte);
|
||||
});
|
||||
|
||||
// Wait until the transmission of the address/data from the
|
||||
// internal shift register has completed. For this to function
|
||||
// correctly, the TX_EMPTY_CTRL flag in IC_CON must be set. The
|
||||
// TX_EMPTY_CTRL flag was set in i2c_init.
|
||||
while !p.ic_raw_intr_stat().read().tx_empty() {}
|
||||
|
||||
let abort_reason = self.read_and_clear_abort_reason();
|
||||
|
||||
if abort_reason.is_err() || (send_stop && last) {
|
||||
// If the transaction was aborted or if it completed
|
||||
// successfully wait until the STOP condition has occured.
|
||||
|
||||
while !p.ic_raw_intr_stat().read().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?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// =========================
|
||||
// Blocking public API
|
||||
// =========================
|
||||
|
||||
pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
Self::setup(address.into())?;
|
||||
self.read_blocking_internal(buffer, true, true)
|
||||
// Automatic Stop
|
||||
}
|
||||
|
||||
pub fn blocking_write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> {
|
||||
Self::setup(address.into())?;
|
||||
self.write_blocking_internal(bytes, true)
|
||||
}
|
||||
|
||||
pub fn blocking_write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> {
|
||||
Self::setup(address.into())?;
|
||||
self.write_blocking_internal(bytes, false)?;
|
||||
self.read_blocking_internal(buffer, true, true)
|
||||
// Automatic Stop
|
||||
}
|
||||
}
|
||||
|
||||
mod eh02 {
|
||||
use super::*;
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, T, M> {
|
||||
type Error = Error;
|
||||
|
||||
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_read(address, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, T, M> {
|
||||
type Error = Error;
|
||||
|
||||
fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(address, bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, T, M> {
|
||||
type Error = Error;
|
||||
|
||||
fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write_read(address, bytes, buffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-traits")]
|
||||
mod eh1 {
|
||||
use super::*;
|
||||
|
||||
impl embedded_hal_1::i2c::Error for Error {
|
||||
fn kind(&self) -> embedded_hal_1::i2c::ErrorKind {
|
||||
match *self {
|
||||
Self::Abort(AbortReason::ArbitrationLoss) => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss,
|
||||
Self::Abort(AbortReason::NoAcknowledge) => {
|
||||
embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Address)
|
||||
}
|
||||
Self::Abort(AbortReason::Other(_)) => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
Self::InvalidReadBufferLength => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
Self::InvalidWriteBufferLength => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
Self::AddressOutOfRange(_) => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
Self::AddressReserved(_) => embedded_hal_1::i2c::ErrorKind::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, T, M> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, T, M> {
|
||||
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_read(address, buffer)
|
||||
}
|
||||
|
||||
fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(address, buffer)
|
||||
}
|
||||
|
||||
fn write_iter<B>(&mut self, address: u8, bytes: B) -> Result<(), Self::Error>
|
||||
where
|
||||
B: IntoIterator<Item = u8>,
|
||||
{
|
||||
let mut peekable = bytes.into_iter().peekable();
|
||||
Self::setup(address.into())?;
|
||||
|
||||
while let Some(tx) = peekable.next() {
|
||||
self.write_blocking_internal(&[tx], peekable.peek().is_none())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_iter_read<B>(&mut self, address: u8, bytes: B, buffer: &mut [u8]) -> Result<(), Self::Error>
|
||||
where
|
||||
B: IntoIterator<Item = u8>,
|
||||
{
|
||||
let peekable = bytes.into_iter().peekable();
|
||||
Self::setup(address.into())?;
|
||||
|
||||
for tx in peekable {
|
||||
self.write_blocking_internal(&[tx], false)?
|
||||
}
|
||||
self.read_blocking_internal(buffer, true, true)
|
||||
}
|
||||
|
||||
fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write_read(address, wr_buffer, rd_buffer)
|
||||
}
|
||||
|
||||
fn transaction<'a>(
|
||||
&mut self,
|
||||
address: u8,
|
||||
operations: &mut [embedded_hal_1::i2c::Operation<'a>],
|
||||
) -> Result<(), Self::Error> {
|
||||
Self::setup(address.into())?;
|
||||
for i in 0..operations.len() {
|
||||
let last = i == operations.len() - 1;
|
||||
match &mut operations[i] {
|
||||
embedded_hal_1::i2c::Operation::Read(buf) => self.read_blocking_internal(buf, false, last)?,
|
||||
embedded_hal_1::i2c::Operation::Write(buf) => self.write_blocking_internal(buf, last)?,
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn transaction_iter<'a, O>(&mut self, address: u8, operations: O) -> Result<(), Self::Error>
|
||||
where
|
||||
O: IntoIterator<Item = embedded_hal_1::i2c::Operation<'a>>,
|
||||
{
|
||||
Self::setup(address.into())?;
|
||||
let mut peekable = operations.into_iter().peekable();
|
||||
while let Some(operation) = peekable.next() {
|
||||
let last = peekable.peek().is_none();
|
||||
match operation {
|
||||
embedded_hal_1::i2c::Operation::Read(buf) => self.read_blocking_internal(buf, false, last)?,
|
||||
embedded_hal_1::i2c::Operation::Write(buf) => self.write_blocking_internal(buf, last)?,
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
|
||||
mod nightly {
|
||||
use core::future::Future;
|
||||
|
||||
use embedded_hal_1::i2c::Operation;
|
||||
use embedded_hal_async::i2c::AddressMode;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl<'d, A, T> embedded_hal_async::i2c::I2c<A> for I2c<'d, T, Async>
|
||||
where
|
||||
A: AddressMode + Into<u16> + 'static,
|
||||
T: Instance + 'd,
|
||||
{
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
|
||||
where Self: 'a;
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
|
||||
where Self: 'a;
|
||||
type WriteReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
|
||||
where Self: 'a;
|
||||
type TransactionFuture<'a, 'b> = impl Future<Output = Result<(), Error>> + 'a
|
||||
where Self: 'a, 'b: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, address: A, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
let addr: u16 = address.into();
|
||||
|
||||
async move {
|
||||
Self::setup(addr)?;
|
||||
self.read_async_internal(buffer, false, true).await
|
||||
}
|
||||
}
|
||||
|
||||
fn write<'a>(&'a mut self, address: A, write: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
let addr: u16 = address.into();
|
||||
|
||||
async move {
|
||||
Self::setup(addr)?;
|
||||
self.write_async_internal(write.iter().copied(), true).await
|
||||
}
|
||||
}
|
||||
|
||||
fn write_read<'a>(
|
||||
&'a mut self,
|
||||
address: A,
|
||||
bytes: &'a [u8],
|
||||
buffer: &'a mut [u8],
|
||||
) -> Self::WriteReadFuture<'a> {
|
||||
let addr: u16 = address.into();
|
||||
|
||||
async move {
|
||||
Self::setup(addr)?;
|
||||
self.write_async_internal(bytes.iter().cloned(), false).await?;
|
||||
self.read_async_internal(buffer, false, true).await
|
||||
}
|
||||
}
|
||||
|
||||
fn transaction<'a, 'b>(
|
||||
&'a mut self,
|
||||
address: A,
|
||||
operations: &'a mut [Operation<'b>],
|
||||
) -> Self::TransactionFuture<'a, 'b> {
|
||||
let addr: u16 = address.into();
|
||||
|
||||
async move {
|
||||
let mut iterator = operations.iter_mut();
|
||||
|
||||
while let Some(op) = iterator.next() {
|
||||
let last = iterator.len() == 0;
|
||||
|
||||
match op {
|
||||
Operation::Read(buffer) => {
|
||||
Self::setup(addr)?;
|
||||
self.read_async_internal(buffer, false, last).await?;
|
||||
}
|
||||
Operation::Write(buffer) => {
|
||||
Self::setup(addr)?;
|
||||
self.write_async_internal(buffer.into_iter().cloned(), last).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn i2c_reserved_addr(addr: u16) -> bool {
|
||||
(addr & 0x78) == 0 || (addr & 0x78) == 0x78
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
use embassy_cortex_m::interrupt::Interrupt;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
|
||||
pub trait Instance {
|
||||
const TX_DREQ: u8;
|
||||
const RX_DREQ: u8;
|
||||
|
||||
type Interrupt: Interrupt;
|
||||
|
||||
fn regs() -> crate::pac::i2c::I2c;
|
||||
fn reset() -> crate::pac::resets::regs::Peripherals;
|
||||
fn waker() -> &'static AtomicWaker;
|
||||
}
|
||||
|
||||
pub trait Mode {}
|
||||
|
||||
pub trait SdaPin<T: Instance> {}
|
||||
pub trait SclPin<T: Instance> {}
|
||||
}
|
||||
|
||||
pub trait Mode: sealed::Mode {}
|
||||
|
||||
macro_rules! impl_mode {
|
||||
($name:ident) => {
|
||||
impl sealed::Mode for $name {}
|
||||
impl Mode for $name {}
|
||||
};
|
||||
}
|
||||
|
||||
pub struct Blocking;
|
||||
pub struct Async;
|
||||
|
||||
impl_mode!(Blocking);
|
||||
impl_mode!(Async);
|
||||
|
||||
pub trait Instance: sealed::Instance {}
|
||||
|
||||
macro_rules! impl_instance {
|
||||
($type:ident, $irq:ident, $reset:ident, $tx_dreq:expr, $rx_dreq:expr) => {
|
||||
impl sealed::Instance for peripherals::$type {
|
||||
const TX_DREQ: u8 = $tx_dreq;
|
||||
const RX_DREQ: u8 = $rx_dreq;
|
||||
|
||||
type Interrupt = crate::interrupt::$irq;
|
||||
|
||||
#[inline]
|
||||
fn regs() -> pac::i2c::I2c {
|
||||
pac::$type
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reset() -> pac::resets::regs::Peripherals {
|
||||
let mut ret = pac::resets::regs::Peripherals::default();
|
||||
ret.$reset(true);
|
||||
ret
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn waker() -> &'static AtomicWaker {
|
||||
static WAKER: AtomicWaker = AtomicWaker::new();
|
||||
|
||||
&WAKER
|
||||
}
|
||||
}
|
||||
impl Instance for peripherals::$type {}
|
||||
};
|
||||
}
|
||||
|
||||
impl_instance!(I2C0, I2C0_IRQ, set_i2c0, 32, 33);
|
||||
impl_instance!(I2C1, I2C1_IRQ, set_i2c1, 34, 35);
|
||||
|
||||
pub trait SdaPin<T: Instance>: sealed::SdaPin<T> + crate::gpio::Pin {}
|
||||
pub trait SclPin<T: Instance>: sealed::SclPin<T> + crate::gpio::Pin {}
|
||||
|
||||
macro_rules! impl_pin {
|
||||
($pin:ident, $instance:ident, $function:ident) => {
|
||||
impl sealed::$function<peripherals::$instance> for peripherals::$pin {}
|
||||
impl $function<peripherals::$instance> for peripherals::$pin {}
|
||||
};
|
||||
}
|
||||
|
||||
impl_pin!(PIN_0, I2C0, SdaPin);
|
||||
impl_pin!(PIN_1, I2C0, SclPin);
|
||||
impl_pin!(PIN_2, I2C1, SdaPin);
|
||||
impl_pin!(PIN_3, I2C1, SclPin);
|
||||
impl_pin!(PIN_4, I2C0, SdaPin);
|
||||
impl_pin!(PIN_5, I2C0, SclPin);
|
||||
impl_pin!(PIN_6, I2C1, SdaPin);
|
||||
impl_pin!(PIN_7, I2C1, SclPin);
|
||||
impl_pin!(PIN_8, I2C0, SdaPin);
|
||||
impl_pin!(PIN_9, I2C0, SclPin);
|
||||
impl_pin!(PIN_10, I2C1, SdaPin);
|
||||
impl_pin!(PIN_11, I2C1, SclPin);
|
||||
impl_pin!(PIN_12, I2C0, SdaPin);
|
||||
impl_pin!(PIN_13, I2C0, SclPin);
|
||||
impl_pin!(PIN_14, I2C1, SdaPin);
|
||||
impl_pin!(PIN_15, I2C1, SclPin);
|
||||
impl_pin!(PIN_16, I2C0, SdaPin);
|
||||
impl_pin!(PIN_17, I2C0, SclPin);
|
||||
impl_pin!(PIN_18, I2C1, SdaPin);
|
||||
impl_pin!(PIN_19, I2C1, SclPin);
|
||||
impl_pin!(PIN_20, I2C0, SdaPin);
|
||||
impl_pin!(PIN_21, I2C0, SclPin);
|
||||
impl_pin!(PIN_22, I2C1, SdaPin);
|
||||
impl_pin!(PIN_23, I2C1, SclPin);
|
||||
impl_pin!(PIN_24, I2C0, SdaPin);
|
||||
impl_pin!(PIN_25, I2C0, SclPin);
|
||||
impl_pin!(PIN_26, I2C1, SdaPin);
|
||||
impl_pin!(PIN_27, I2C1, SclPin);
|
||||
impl_pin!(PIN_28, I2C0, SdaPin);
|
||||
impl_pin!(PIN_29, I2C0, SclPin);
|
276
embassy-rp/src/intrinsics.rs
Normal file
276
embassy-rp/src/intrinsics.rs
Normal file
@ -0,0 +1,276 @@
|
||||
#![macro_use]
|
||||
|
||||
// Credit: taken from `rp-hal` (also licensed Apache+MIT)
|
||||
// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/intrinsics.rs
|
||||
|
||||
/// Generate a series of aliases for an intrinsic function.
|
||||
macro_rules! intrinsics_aliases {
|
||||
(
|
||||
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,
|
||||
) => {};
|
||||
(
|
||||
unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,
|
||||
) => {};
|
||||
|
||||
(
|
||||
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,
|
||||
$alias:ident
|
||||
$($rest:ident)*
|
||||
) => {
|
||||
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
|
||||
intrinsics! {
|
||||
extern $abi fn $alias( $($argname: $ty),* ) -> $ret {
|
||||
$name($($argname),*)
|
||||
}
|
||||
}
|
||||
|
||||
intrinsics_aliases! {
|
||||
extern $abi fn $name( $($argname: $ty),* ) -> $ret,
|
||||
$($rest)*
|
||||
}
|
||||
};
|
||||
|
||||
(
|
||||
unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty,
|
||||
$alias:ident
|
||||
$($rest:ident)*
|
||||
) => {
|
||||
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
|
||||
intrinsics! {
|
||||
unsafe extern $abi fn $alias( $($argname: $ty),* ) -> $ret {
|
||||
$name($($argname),*)
|
||||
}
|
||||
}
|
||||
|
||||
intrinsics_aliases! {
|
||||
unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret,
|
||||
$($rest)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// The macro used to define overridden intrinsics.
|
||||
///
|
||||
/// This is heavily inspired by the macro used by compiler-builtins. The idea
|
||||
/// is to abstract anything special that needs to be done to override an
|
||||
/// intrinsic function. Intrinsic generation is disabled for non-ARM targets
|
||||
/// so things like CI and docs generation do not have problems. Additionally
|
||||
/// they can be disabled by disabling the crate feature `intrinsics` for
|
||||
/// testing or comparing performance.
|
||||
///
|
||||
/// Like the compiler-builtins macro, it accepts a series of functions that
|
||||
/// looks like normal Rust code:
|
||||
///
|
||||
/// intrinsics! {
|
||||
/// extern "C" fn foo(a: i32) -> u32 {
|
||||
/// // ...
|
||||
/// }
|
||||
///
|
||||
/// #[nonstandard_attribute]
|
||||
/// extern "C" fn bar(a: i32) -> u32 {
|
||||
/// // ...
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// Each function can also be decorated with nonstandard attributes to control
|
||||
/// additional behaviour:
|
||||
///
|
||||
/// * `slower_than_default` - indicates that the override is slower than the
|
||||
/// default implementation. Currently this just disables the override
|
||||
/// entirely.
|
||||
/// * `bootrom_v2` - indicates that the override is only available
|
||||
/// on a V2 bootrom or higher. Only enabled when the feature
|
||||
/// `rom-v2-intrinsics` is set.
|
||||
/// * `alias` - accepts a list of names to alias the intrinsic to.
|
||||
/// * `aeabi` - accepts a list of ARM EABI names to alias to.
|
||||
///
|
||||
macro_rules! intrinsics {
|
||||
() => {};
|
||||
|
||||
(
|
||||
#[slower_than_default]
|
||||
$(#[$($attr:tt)*])*
|
||||
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
|
||||
$($body:tt)*
|
||||
}
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
// Not exported, but defined so the actual implementation is
|
||||
// considered used
|
||||
#[allow(dead_code)]
|
||||
fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
|
||||
intrinsics!($($rest)*);
|
||||
};
|
||||
|
||||
(
|
||||
#[bootrom_v2]
|
||||
$(#[$($attr:tt)*])*
|
||||
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
|
||||
$($body:tt)*
|
||||
}
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
// Not exported, but defined so the actual implementation is
|
||||
// considered used
|
||||
#[cfg(not(feature = "rom-v2-intrinsics"))]
|
||||
#[allow(dead_code)]
|
||||
fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
|
||||
#[cfg(feature = "rom-v2-intrinsics")]
|
||||
intrinsics! {
|
||||
$(#[$($attr)*])*
|
||||
extern $abi fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
}
|
||||
|
||||
intrinsics!($($rest)*);
|
||||
};
|
||||
|
||||
(
|
||||
#[alias = $($alias:ident),*]
|
||||
$(#[$($attr:tt)*])*
|
||||
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
|
||||
$($body:tt)*
|
||||
}
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
intrinsics! {
|
||||
$(#[$($attr)*])*
|
||||
extern $abi fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
}
|
||||
|
||||
intrinsics_aliases! {
|
||||
extern $abi fn $name( $($argname: $ty),* ) -> $ret,
|
||||
$($alias) *
|
||||
}
|
||||
|
||||
intrinsics!($($rest)*);
|
||||
};
|
||||
|
||||
(
|
||||
#[alias = $($alias:ident),*]
|
||||
$(#[$($attr:tt)*])*
|
||||
unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
|
||||
$($body:tt)*
|
||||
}
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
intrinsics! {
|
||||
$(#[$($attr)*])*
|
||||
unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
}
|
||||
|
||||
intrinsics_aliases! {
|
||||
unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret,
|
||||
$($alias) *
|
||||
}
|
||||
|
||||
intrinsics!($($rest)*);
|
||||
};
|
||||
|
||||
(
|
||||
#[aeabi = $($alias:ident),*]
|
||||
$(#[$($attr:tt)*])*
|
||||
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
|
||||
$($body:tt)*
|
||||
}
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
intrinsics! {
|
||||
$(#[$($attr)*])*
|
||||
extern $abi fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
}
|
||||
|
||||
intrinsics_aliases! {
|
||||
extern "aapcs" fn $name( $($argname: $ty),* ) -> $ret,
|
||||
$($alias) *
|
||||
}
|
||||
|
||||
intrinsics!($($rest)*);
|
||||
};
|
||||
|
||||
(
|
||||
$(#[$($attr:tt)*])*
|
||||
extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
|
||||
$($body:tt)*
|
||||
}
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
|
||||
$(#[$($attr)*])*
|
||||
extern $abi fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
|
||||
mod $name {
|
||||
#[no_mangle]
|
||||
$(#[$($attr)*])*
|
||||
pub extern $abi fn $name( $($argname: $ty),* ) -> $ret {
|
||||
super::$name($($argname),*)
|
||||
}
|
||||
}
|
||||
|
||||
// Not exported, but defined so the actual implementation is
|
||||
// considered used
|
||||
#[cfg(not(all(target_arch = "arm", feature = "intrinsics")))]
|
||||
#[allow(dead_code)]
|
||||
fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
|
||||
intrinsics!($($rest)*);
|
||||
};
|
||||
|
||||
(
|
||||
$(#[$($attr:tt)*])*
|
||||
unsafe extern $abi:tt fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty {
|
||||
$($body:tt)*
|
||||
}
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
|
||||
$(#[$($attr)*])*
|
||||
unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "arm", feature = "intrinsics"))]
|
||||
mod $name {
|
||||
#[no_mangle]
|
||||
$(#[$($attr)*])*
|
||||
pub unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret {
|
||||
super::$name($($argname),*)
|
||||
}
|
||||
}
|
||||
|
||||
// Not exported, but defined so the actual implementation is
|
||||
// considered used
|
||||
#[cfg(not(all(target_arch = "arm", feature = "intrinsics")))]
|
||||
#[allow(dead_code)]
|
||||
unsafe fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$($body)*
|
||||
}
|
||||
|
||||
intrinsics!($($rest)*);
|
||||
};
|
||||
}
|
@ -1,13 +1,19 @@
|
||||
#![no_std]
|
||||
#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))]
|
||||
#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))]
|
||||
|
||||
// This mod MUST go first, so that the others see its macros.
|
||||
pub(crate) mod fmt;
|
||||
|
||||
mod intrinsics;
|
||||
|
||||
pub mod dma;
|
||||
pub mod gpio;
|
||||
pub mod i2c;
|
||||
pub mod interrupt;
|
||||
pub mod rom_data;
|
||||
pub mod rtc;
|
||||
pub mod spi;
|
||||
#[cfg(feature = "time-driver")]
|
||||
pub mod timer;
|
||||
pub mod uart;
|
||||
#[cfg(feature = "nightly")]
|
||||
@ -70,6 +76,9 @@ embassy_hal_common::peripherals! {
|
||||
SPI0,
|
||||
SPI1,
|
||||
|
||||
I2C0,
|
||||
I2C1,
|
||||
|
||||
DMA_CH0,
|
||||
DMA_CH1,
|
||||
DMA_CH2,
|
||||
@ -84,6 +93,8 @@ embassy_hal_common::peripherals! {
|
||||
DMA_CH11,
|
||||
|
||||
USB,
|
||||
|
||||
RTC,
|
||||
}
|
||||
|
||||
#[link_section = ".boot2"]
|
||||
@ -108,6 +119,7 @@ pub fn init(_config: config::Config) -> Peripherals {
|
||||
|
||||
unsafe {
|
||||
clocks::init();
|
||||
#[cfg(feature = "time-driver")]
|
||||
timer::init();
|
||||
dma::init();
|
||||
}
|
||||
|
733
embassy-rp/src/rom_data.rs
Normal file
733
embassy-rp/src/rom_data.rs
Normal file
@ -0,0 +1,733 @@
|
||||
//! Functions and data from the RPI Bootrom.
|
||||
//!
|
||||
//! From the [RP2040 datasheet](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf), Section 2.8.2.1:
|
||||
//!
|
||||
//! > The Bootrom contains a number of public functions that provide useful
|
||||
//! > RP2040 functionality that might be needed in the absence of any other code
|
||||
//! > on the device, as well as highly optimized versions of certain key
|
||||
//! > functionality that would otherwise have to take up space in most user
|
||||
//! > binaries.
|
||||
|
||||
// Credit: taken from `rp-hal` (also licensed Apache+MIT)
|
||||
// https://github.com/rp-rs/rp-hal/blob/main/rp2040-hal/src/rom_data.rs
|
||||
|
||||
/// A bootrom function table code.
|
||||
pub type RomFnTableCode = [u8; 2];
|
||||
|
||||
/// This function searches for (table)
|
||||
type RomTableLookupFn<T> = unsafe extern "C" fn(*const u16, u32) -> T;
|
||||
|
||||
/// The following addresses are described at `2.8.2. Bootrom Contents`
|
||||
/// Pointer to the lookup table function supplied by the rom.
|
||||
const ROM_TABLE_LOOKUP_PTR: *const u16 = 0x0000_0018 as _;
|
||||
|
||||
/// Pointer to helper functions lookup table.
|
||||
const FUNC_TABLE: *const u16 = 0x0000_0014 as _;
|
||||
|
||||
/// Pointer to the public data lookup table.
|
||||
const DATA_TABLE: *const u16 = 0x0000_0016 as _;
|
||||
|
||||
/// Address of the version number of the ROM.
|
||||
const VERSION_NUMBER: *const u8 = 0x0000_0013 as _;
|
||||
|
||||
/// Retrive rom content from a table using a code.
|
||||
fn rom_table_lookup<T>(table: *const u16, tag: RomFnTableCode) -> T {
|
||||
unsafe {
|
||||
let rom_table_lookup_ptr: *const u32 = rom_hword_as_ptr(ROM_TABLE_LOOKUP_PTR);
|
||||
let rom_table_lookup: RomTableLookupFn<T> = core::mem::transmute(rom_table_lookup_ptr);
|
||||
rom_table_lookup(rom_hword_as_ptr(table) as *const u16, u16::from_le_bytes(tag) as u32)
|
||||
}
|
||||
}
|
||||
|
||||
/// To save space, the ROM likes to store memory pointers (which are 32-bit on
|
||||
/// the Cortex-M0+) using only the bottom 16-bits. The assumption is that the
|
||||
/// values they point at live in the first 64 KiB of ROM, and the ROM is mapped
|
||||
/// to address `0x0000_0000` and so 16-bits are always sufficient.
|
||||
///
|
||||
/// This functions grabs a 16-bit value from ROM and expands it out to a full 32-bit pointer.
|
||||
unsafe fn rom_hword_as_ptr(rom_address: *const u16) -> *const u32 {
|
||||
let ptr: u16 = *rom_address;
|
||||
ptr as *const u32
|
||||
}
|
||||
|
||||
macro_rules! declare_rom_function {
|
||||
(
|
||||
$(#[$outer:meta])*
|
||||
fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty
|
||||
$lookup:block
|
||||
) => {
|
||||
#[doc = r"Additional access for the `"]
|
||||
#[doc = stringify!($name)]
|
||||
#[doc = r"` ROM function."]
|
||||
pub mod $name {
|
||||
/// Retrieve a function pointer.
|
||||
#[cfg(not(feature = "rom-func-cache"))]
|
||||
pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret {
|
||||
let p: *const u32 = $lookup;
|
||||
unsafe {
|
||||
let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
|
||||
func
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve a function pointer.
|
||||
#[cfg(feature = "rom-func-cache")]
|
||||
pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret {
|
||||
use core::sync::atomic::{AtomicU16, Ordering};
|
||||
|
||||
// All pointers in the ROM fit in 16 bits, so we don't need a
|
||||
// full width word to store the cached value.
|
||||
static CACHED_PTR: AtomicU16 = AtomicU16::new(0);
|
||||
// This is safe because the lookup will always resolve
|
||||
// to the same value. So even if an interrupt or another
|
||||
// core starts at the same time, it just repeats some
|
||||
// work and eventually writes back the correct value.
|
||||
let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) {
|
||||
0 => {
|
||||
let raw: *const u32 = $lookup;
|
||||
CACHED_PTR.store(raw as u16, Ordering::Relaxed);
|
||||
raw
|
||||
},
|
||||
val => val as *const u32,
|
||||
};
|
||||
unsafe {
|
||||
let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
|
||||
func
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(#[$outer])*
|
||||
pub extern "C" fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$name::ptr()($($argname),*)
|
||||
}
|
||||
};
|
||||
|
||||
(
|
||||
$(#[$outer:meta])*
|
||||
unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty
|
||||
$lookup:block
|
||||
) => {
|
||||
#[doc = r"Additional access for the `"]
|
||||
#[doc = stringify!($name)]
|
||||
#[doc = r"` ROM function."]
|
||||
pub mod $name {
|
||||
/// Retrieve a function pointer.
|
||||
#[cfg(not(feature = "rom-func-cache"))]
|
||||
pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret {
|
||||
let p: *const u32 = $lookup;
|
||||
unsafe {
|
||||
let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
|
||||
func
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve a function pointer.
|
||||
#[cfg(feature = "rom-func-cache")]
|
||||
pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret {
|
||||
use core::sync::atomic::{AtomicU16, Ordering};
|
||||
|
||||
// All pointers in the ROM fit in 16 bits, so we don't need a
|
||||
// full width word to store the cached value.
|
||||
static CACHED_PTR: AtomicU16 = AtomicU16::new(0);
|
||||
// This is safe because the lookup will always resolve
|
||||
// to the same value. So even if an interrupt or another
|
||||
// core starts at the same time, it just repeats some
|
||||
// work and eventually writes back the correct value.
|
||||
let p: *const u32 = match CACHED_PTR.load(Ordering::Relaxed) {
|
||||
0 => {
|
||||
let raw: *const u32 = $lookup;
|
||||
CACHED_PTR.store(raw as u16, Ordering::Relaxed);
|
||||
raw
|
||||
},
|
||||
val => val as *const u32,
|
||||
};
|
||||
unsafe {
|
||||
let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
|
||||
func
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(#[$outer])*
|
||||
pub unsafe extern "C" fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$name::ptr()($($argname),*)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! rom_functions {
|
||||
() => {};
|
||||
|
||||
(
|
||||
$(#[$outer:meta])*
|
||||
$c:literal fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty;
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
declare_rom_function! {
|
||||
$(#[$outer])*
|
||||
fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$crate::rom_data::rom_table_lookup($crate::rom_data::FUNC_TABLE, *$c)
|
||||
}
|
||||
}
|
||||
|
||||
rom_functions!($($rest)*);
|
||||
};
|
||||
|
||||
(
|
||||
$(#[$outer:meta])*
|
||||
$c:literal unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty;
|
||||
|
||||
$($rest:tt)*
|
||||
) => {
|
||||
declare_rom_function! {
|
||||
$(#[$outer])*
|
||||
unsafe fn $name( $($argname: $ty),* ) -> $ret {
|
||||
$crate::rom_data::rom_table_lookup($crate::rom_data::FUNC_TABLE, *$c)
|
||||
}
|
||||
}
|
||||
|
||||
rom_functions!($($rest)*);
|
||||
};
|
||||
}
|
||||
|
||||
rom_functions! {
|
||||
/// Return a count of the number of 1 bits in value.
|
||||
b"P3" fn popcount32(value: u32) -> u32;
|
||||
|
||||
/// Return the bits of value in the reverse order.
|
||||
b"R3" fn reverse32(value: u32) -> u32;
|
||||
|
||||
/// Return the number of consecutive high order 0 bits of value. If value is zero, returns 32.
|
||||
b"L3" fn clz32(value: u32) -> u32;
|
||||
|
||||
/// Return the number of consecutive low order 0 bits of value. If value is zero, returns 32.
|
||||
b"T3" fn ctz32(value: u32) -> u32;
|
||||
|
||||
/// Resets the RP2040 and uses the watchdog facility to re-start in BOOTSEL mode:
|
||||
/// * gpio_activity_pin_mask is provided to enable an 'activity light' via GPIO attached LED
|
||||
/// for the USB Mass Storage Device:
|
||||
/// * 0 No pins are used as per cold boot.
|
||||
/// * Otherwise a single bit set indicating which GPIO pin should be set to output and
|
||||
/// raised whenever there is mass storage activity from the host.
|
||||
/// * disable_interface_mask may be used to control the exposed USB interfaces:
|
||||
/// * 0 To enable both interfaces (as per cold boot).
|
||||
/// * 1 To disable the USB Mass Storage Interface.
|
||||
/// * 2 to Disable the USB PICOBOOT Interface.
|
||||
b"UB" fn reset_to_usb_boot(gpio_activity_pin_mask: u32, disable_interface_mask: u32) -> ();
|
||||
|
||||
/// Sets n bytes start at ptr to the value c and returns ptr
|
||||
b"MS" unsafe fn memset(ptr: *mut u8, c: u8, n: u32) -> *mut u8;
|
||||
|
||||
/// Sets n bytes start at ptr to the value c and returns ptr.
|
||||
///
|
||||
/// Note this is a slightly more efficient variant of _memset that may only
|
||||
/// be used if ptr is word aligned.
|
||||
// Note the datasheet does not match the actual ROM for the code here, see
|
||||
// https://github.com/raspberrypi/pico-feedback/issues/217
|
||||
b"S4" unsafe fn memset4(ptr: *mut u32, c: u8, n: u32) -> *mut u32;
|
||||
|
||||
/// Copies n bytes starting at src to dest and returns dest. The results are undefined if the
|
||||
/// regions overlap.
|
||||
b"MC" unsafe fn memcpy(dest: *mut u8, src: *const u8, n: u32) -> *mut u8;
|
||||
|
||||
/// Copies n bytes starting at src to dest and returns dest. The results are undefined if the
|
||||
/// regions overlap.
|
||||
///
|
||||
/// Note this is a slightly more efficient variant of _memcpy that may only be
|
||||
/// used if dest and src are word aligned.
|
||||
b"C4" unsafe fn memcpy44(dest: *mut u32, src: *const u32, n: u32) -> *mut u8;
|
||||
|
||||
/// Restore all QSPI pad controls to their default state, and connect the SSI to the QSPI pads.
|
||||
b"IF" unsafe fn connect_internal_flash() -> ();
|
||||
|
||||
/// First set up the SSI for serial-mode operations, then issue the fixed XIP exit sequence.
|
||||
///
|
||||
/// Note that the bootrom code uses the IO forcing logic to drive the CS pin, which must be
|
||||
/// cleared before returning the SSI to XIP mode (e.g. by a call to _flash_flush_cache). This
|
||||
/// function configures the SSI with a fixed SCK clock divisor of /6.
|
||||
b"EX" unsafe fn flash_exit_xip() -> ();
|
||||
|
||||
/// Erase a count bytes, starting at addr (offset from start of flash). Optionally, pass a
|
||||
/// block erase command e.g. D8h block erase, and the size of the block erased by this
|
||||
/// command — this function will use the larger block erase where possible, for much higher
|
||||
/// erase speed. addr must be aligned to a 4096-byte sector, and count must be a multiple of
|
||||
/// 4096 bytes.
|
||||
b"RE" unsafe fn flash_range_erase(addr: u32, count: usize, block_size: u32, block_cmd: u8) -> ();
|
||||
|
||||
/// Program data to a range of flash addresses starting at `addr` (and
|
||||
/// offset from the start of flash) and `count` bytes in size. The value
|
||||
/// `addr` must be aligned to a 256-byte boundary, and `count` must be a
|
||||
/// multiple of 256.
|
||||
b"RP" unsafe fn flash_range_program(addr: u32, data: *const u8, count: usize) -> ();
|
||||
|
||||
/// Flush and enable the XIP cache. Also clears the IO forcing on QSPI CSn, so that the SSI can
|
||||
/// drive the flashchip select as normal.
|
||||
b"FC" unsafe fn flash_flush_cache() -> ();
|
||||
|
||||
/// Configure the SSI to generate a standard 03h serial read command, with 24 address bits,
|
||||
/// upon each XIP access. This is a very slow XIP configuration, but is very widely supported.
|
||||
/// The debugger calls this function after performing a flash erase/programming operation, so
|
||||
/// that the freshly-programmed code and data is visible to the debug host, without having to
|
||||
/// know exactly what kind of flash device is connected.
|
||||
b"CX" unsafe fn flash_enter_cmd_xip() -> ();
|
||||
|
||||
/// This is the method that is entered by core 1 on reset to wait to be launched by core 0.
|
||||
/// There are few cases where you should call this method (resetting core 1 is much better).
|
||||
/// This method does not return and should only ever be called on core 1.
|
||||
b"WV" unsafe fn wait_for_vector() -> !;
|
||||
}
|
||||
|
||||
// Various C intrinsics in the ROM
|
||||
intrinsics! {
|
||||
#[alias = __popcountdi2]
|
||||
extern "C" fn __popcountsi2(x: u32) -> u32 {
|
||||
popcount32(x)
|
||||
}
|
||||
|
||||
#[alias = __clzdi2]
|
||||
extern "C" fn __clzsi2(x: u32) -> u32 {
|
||||
clz32(x)
|
||||
}
|
||||
|
||||
#[alias = __ctzdi2]
|
||||
extern "C" fn __ctzsi2(x: u32) -> u32 {
|
||||
ctz32(x)
|
||||
}
|
||||
|
||||
// __rbit is only unofficial, but it show up in the ARM documentation,
|
||||
// so may as well hook it up.
|
||||
#[alias = __rbitl]
|
||||
extern "C" fn __rbit(x: u32) -> u32 {
|
||||
reverse32(x)
|
||||
}
|
||||
|
||||
unsafe extern "aapcs" fn __aeabi_memset(dest: *mut u8, n: usize, c: i32) -> () {
|
||||
// Different argument order
|
||||
memset(dest, c as u8, n as u32);
|
||||
}
|
||||
|
||||
#[alias = __aeabi_memset8]
|
||||
unsafe extern "aapcs" fn __aeabi_memset4(dest: *mut u8, n: usize, c: i32) -> () {
|
||||
// Different argument order
|
||||
memset4(dest as *mut u32, c as u8, n as u32);
|
||||
}
|
||||
|
||||
unsafe extern "aapcs" fn __aeabi_memclr(dest: *mut u8, n: usize) -> () {
|
||||
memset(dest, 0, n as u32);
|
||||
}
|
||||
|
||||
#[alias = __aeabi_memclr8]
|
||||
unsafe extern "aapcs" fn __aeabi_memclr4(dest: *mut u8, n: usize) -> () {
|
||||
memset4(dest as *mut u32, 0, n as u32);
|
||||
}
|
||||
|
||||
unsafe extern "aapcs" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, n: usize) -> () {
|
||||
memcpy(dest, src, n as u32);
|
||||
}
|
||||
|
||||
#[alias = __aeabi_memcpy8]
|
||||
unsafe extern "aapcs" fn __aeabi_memcpy4(dest: *mut u8, src: *const u8, n: usize) -> () {
|
||||
memcpy44(dest as *mut u32, src as *const u32, n as u32);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn convert_str(s: *const u8) -> &'static str {
|
||||
let mut end = s;
|
||||
while *end != 0 {
|
||||
end = end.add(1);
|
||||
}
|
||||
let s = core::slice::from_raw_parts(s, end.offset_from(s) as usize);
|
||||
core::str::from_utf8_unchecked(s)
|
||||
}
|
||||
|
||||
/// The version number of the rom.
|
||||
pub fn rom_version_number() -> u8 {
|
||||
unsafe { *VERSION_NUMBER }
|
||||
}
|
||||
|
||||
/// The Raspberry Pi Trading Ltd copyright string.
|
||||
pub fn copyright_string() -> &'static str {
|
||||
let s: *const u8 = rom_table_lookup(DATA_TABLE, *b"CR");
|
||||
unsafe { convert_str(s) }
|
||||
}
|
||||
|
||||
/// The 8 most significant hex digits of the Bootrom git revision.
|
||||
pub fn git_revision() -> u32 {
|
||||
let s: *const u32 = rom_table_lookup(DATA_TABLE, *b"GR");
|
||||
unsafe { *s }
|
||||
}
|
||||
|
||||
/// The start address of the floating point library code and data.
|
||||
///
|
||||
/// This and fplib_end along with the individual function pointers in
|
||||
/// soft_float_table can be used to copy the floating point implementation into
|
||||
/// RAM if desired.
|
||||
pub fn fplib_start() -> *const u8 {
|
||||
rom_table_lookup(DATA_TABLE, *b"FS")
|
||||
}
|
||||
|
||||
/// See Table 180 in the RP2040 datasheet for the contents of this table.
|
||||
pub fn soft_float_table() -> *const usize {
|
||||
rom_table_lookup(DATA_TABLE, *b"SF")
|
||||
}
|
||||
|
||||
/// The end address of the floating point library code and data.
|
||||
pub fn fplib_end() -> *const u8 {
|
||||
rom_table_lookup(DATA_TABLE, *b"FE")
|
||||
}
|
||||
|
||||
/// This entry is only present in the V2 bootrom. See Table 182 in the RP2040 datasheet for the contents of this table.
|
||||
pub fn soft_double_table() -> *const usize {
|
||||
if rom_version_number() < 2 {
|
||||
panic!(
|
||||
"Double precision operations require V2 bootrom (found: V{})",
|
||||
rom_version_number()
|
||||
);
|
||||
}
|
||||
rom_table_lookup(DATA_TABLE, *b"SD")
|
||||
}
|
||||
|
||||
/// ROM functions using single-precision arithmetic (i.e. 'f32' in Rust terms)
|
||||
pub mod float_funcs {
|
||||
|
||||
macro_rules! make_functions {
|
||||
(
|
||||
$(
|
||||
$(#[$outer:meta])*
|
||||
$offset:literal $name:ident (
|
||||
$( $aname:ident : $aty:ty ),*
|
||||
) -> $ret:ty;
|
||||
)*
|
||||
) => {
|
||||
$(
|
||||
declare_rom_function! {
|
||||
$(#[$outer])*
|
||||
fn $name( $( $aname : $aty ),* ) -> $ret {
|
||||
let table: *const usize = $crate::rom_data::soft_float_table();
|
||||
unsafe {
|
||||
// This is the entry in the table. Our offset is given as a
|
||||
// byte offset, but we want the table index (each pointer in
|
||||
// the table is 4 bytes long)
|
||||
let entry: *const usize = table.offset($offset / 4);
|
||||
// Read the pointer from the table
|
||||
core::ptr::read(entry) as *const u32
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
make_functions! {
|
||||
/// Calculates `a + b`
|
||||
0x00 fadd(a: f32, b: f32) -> f32;
|
||||
/// Calculates `a - b`
|
||||
0x04 fsub(a: f32, b: f32) -> f32;
|
||||
/// Calculates `a * b`
|
||||
0x08 fmul(a: f32, b: f32) -> f32;
|
||||
/// Calculates `a / b`
|
||||
0x0c fdiv(a: f32, b: f32) -> f32;
|
||||
|
||||
// 0x10 and 0x14 are deprecated
|
||||
|
||||
/// Calculates `sqrt(v)` (or return -Infinity if v is negative)
|
||||
0x18 fsqrt(v: f32) -> f32;
|
||||
/// Converts an f32 to a signed integer,
|
||||
/// rounding towards -Infinity, and clamping the result to lie within the
|
||||
/// range `-0x80000000` to `0x7FFFFFFF`
|
||||
0x1c float_to_int(v: f32) -> i32;
|
||||
/// Converts an f32 to an signed fixed point
|
||||
/// integer representation where n specifies the position of the binary
|
||||
/// point in the resulting fixed point representation, e.g.
|
||||
/// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity,
|
||||
/// and clamps the resulting integer to lie within the range `0x00000000` to
|
||||
/// `0xFFFFFFFF`
|
||||
0x20 float_to_fix(v: f32, n: i32) -> i32;
|
||||
/// Converts an f32 to an unsigned integer,
|
||||
/// rounding towards -Infinity, and clamping the result to lie within the
|
||||
/// range `0x00000000` to `0xFFFFFFFF`
|
||||
0x24 float_to_uint(v: f32) -> u32;
|
||||
/// Converts an f32 to an unsigned fixed point
|
||||
/// integer representation where n specifies the position of the binary
|
||||
/// point in the resulting fixed point representation, e.g.
|
||||
/// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity,
|
||||
/// and clamps the resulting integer to lie within the range `0x00000000` to
|
||||
/// `0xFFFFFFFF`
|
||||
0x28 float_to_ufix(v: f32, n: i32) -> u32;
|
||||
/// Converts a signed integer to the nearest
|
||||
/// f32 value, rounding to even on tie
|
||||
0x2c int_to_float(v: i32) -> f32;
|
||||
/// Converts a signed fixed point integer
|
||||
/// representation to the nearest f32 value, rounding to even on tie. `n`
|
||||
/// specifies the position of the binary point in fixed point, so `f =
|
||||
/// nearest(v/(2^n))`
|
||||
0x30 fix_to_float(v: i32, n: i32) -> f32;
|
||||
/// Converts an unsigned integer to the nearest
|
||||
/// f32 value, rounding to even on tie
|
||||
0x34 uint_to_float(v: u32) -> f32;
|
||||
/// Converts an unsigned fixed point integer
|
||||
/// representation to the nearest f32 value, rounding to even on tie. `n`
|
||||
/// specifies the position of the binary point in fixed point, so `f =
|
||||
/// nearest(v/(2^n))`
|
||||
0x38 ufix_to_float(v: u32, n: i32) -> f32;
|
||||
/// Calculates the cosine of `angle`. The value
|
||||
/// of `angle` is in radians, and must be in the range `-1024` to `1024`
|
||||
0x3c fcos(angle: f32) -> f32;
|
||||
/// Calculates the sine of `angle`. The value of
|
||||
/// `angle` is in radians, and must be in the range `-1024` to `1024`
|
||||
0x40 fsin(angle: f32) -> f32;
|
||||
/// Calculates the tangent of `angle`. The value
|
||||
/// of `angle` is in radians, and must be in the range `-1024` to `1024`
|
||||
0x44 ftan(angle: f32) -> f32;
|
||||
|
||||
// 0x48 is deprecated
|
||||
|
||||
/// Calculates the exponential value of `v`,
|
||||
/// i.e. `e ** v`
|
||||
0x4c fexp(v: f32) -> f32;
|
||||
/// Calculates the natural logarithm of `v`. If `v <= 0` return -Infinity
|
||||
0x50 fln(v: f32) -> f32;
|
||||
}
|
||||
|
||||
macro_rules! make_functions_v2 {
|
||||
(
|
||||
$(
|
||||
$(#[$outer:meta])*
|
||||
$offset:literal $name:ident (
|
||||
$( $aname:ident : $aty:ty ),*
|
||||
) -> $ret:ty;
|
||||
)*
|
||||
) => {
|
||||
$(
|
||||
declare_rom_function! {
|
||||
$(#[$outer])*
|
||||
fn $name( $( $aname : $aty ),* ) -> $ret {
|
||||
if $crate::rom_data::rom_version_number() < 2 {
|
||||
panic!(
|
||||
"Floating point function requires V2 bootrom (found: V{})",
|
||||
$crate::rom_data::rom_version_number()
|
||||
);
|
||||
}
|
||||
let table: *const usize = $crate::rom_data::soft_float_table();
|
||||
unsafe {
|
||||
// This is the entry in the table. Our offset is given as a
|
||||
// byte offset, but we want the table index (each pointer in
|
||||
// the table is 4 bytes long)
|
||||
let entry: *const usize = table.offset($offset / 4);
|
||||
// Read the pointer from the table
|
||||
core::ptr::read(entry) as *const u32
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
// These are only on BootROM v2 or higher
|
||||
make_functions_v2! {
|
||||
/// Compares two floating point numbers, returning:
|
||||
/// • 0 if a == b
|
||||
/// • -1 if a < b
|
||||
/// • 1 if a > b
|
||||
0x54 fcmp(a: f32, b: f32) -> i32;
|
||||
/// Computes the arc tangent of `y/x` using the
|
||||
/// signs of arguments to determine the correct quadrant
|
||||
0x58 fatan2(y: f32, x: f32) -> f32;
|
||||
/// Converts a signed 64-bit integer to the
|
||||
/// nearest f32 value, rounding to even on tie
|
||||
0x5c int64_to_float(v: i64) -> f32;
|
||||
/// Converts a signed fixed point 64-bit integer
|
||||
/// representation to the nearest f32 value, rounding to even on tie. `n`
|
||||
/// specifies the position of the binary point in fixed point, so `f =
|
||||
/// nearest(v/(2^n))`
|
||||
0x60 fix64_to_float(v: i64, n: i32) -> f32;
|
||||
/// Converts an unsigned 64-bit integer to the
|
||||
/// nearest f32 value, rounding to even on tie
|
||||
0x64 uint64_to_float(v: u64) -> f32;
|
||||
/// Converts an unsigned fixed point 64-bit
|
||||
/// integer representation to the nearest f32 value, rounding to even on
|
||||
/// tie. `n` specifies the position of the binary point in fixed point, so
|
||||
/// `f = nearest(v/(2^n))`
|
||||
0x68 ufix64_to_float(v: u64, n: i32) -> f32;
|
||||
/// Convert an f32 to a signed 64-bit integer, rounding towards -Infinity,
|
||||
/// and clamping the result to lie within the range `-0x8000000000000000` to
|
||||
/// `0x7FFFFFFFFFFFFFFF`
|
||||
0x6c float_to_int64(v: f32) -> i64;
|
||||
/// Converts an f32 to a signed fixed point
|
||||
/// 64-bit integer representation where n specifies the position of the
|
||||
/// binary point in the resulting fixed point representation - e.g. `f(0.5f,
|
||||
/// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the
|
||||
/// resulting integer to lie within the range `-0x8000000000000000` to
|
||||
/// `0x7FFFFFFFFFFFFFFF`
|
||||
0x70 float_to_fix64(v: f32, n: i32) -> f32;
|
||||
/// Converts an f32 to an unsigned 64-bit
|
||||
/// integer, rounding towards -Infinity, and clamping the result to lie
|
||||
/// within the range `0x0000000000000000` to `0xFFFFFFFFFFFFFFFF`
|
||||
0x74 float_to_uint64(v: f32) -> u64;
|
||||
/// Converts an f32 to an unsigned fixed point
|
||||
/// 64-bit integer representation where n specifies the position of the
|
||||
/// binary point in the resulting fixed point representation, e.g. `f(0.5f,
|
||||
/// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the
|
||||
/// resulting integer to lie within the range `0x0000000000000000` to
|
||||
/// `0xFFFFFFFFFFFFFFFF`
|
||||
0x78 float_to_ufix64(v: f32, n: i32) -> u64;
|
||||
/// Converts an f32 to an f64.
|
||||
0x7c float_to_double(v: f32) -> f64;
|
||||
}
|
||||
}
|
||||
|
||||
/// Functions using double-precision arithmetic (i.e. 'f64' in Rust terms)
|
||||
pub mod double_funcs {
|
||||
|
||||
macro_rules! make_double_funcs {
|
||||
(
|
||||
$(
|
||||
$(#[$outer:meta])*
|
||||
$offset:literal $name:ident (
|
||||
$( $aname:ident : $aty:ty ),*
|
||||
) -> $ret:ty;
|
||||
)*
|
||||
) => {
|
||||
$(
|
||||
declare_rom_function! {
|
||||
$(#[$outer])*
|
||||
fn $name( $( $aname : $aty ),* ) -> $ret {
|
||||
let table: *const usize = $crate::rom_data::soft_double_table();
|
||||
unsafe {
|
||||
// This is the entry in the table. Our offset is given as a
|
||||
// byte offset, but we want the table index (each pointer in
|
||||
// the table is 4 bytes long)
|
||||
let entry: *const usize = table.offset($offset / 4);
|
||||
// Read the pointer from the table
|
||||
core::ptr::read(entry) as *const u32
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
make_double_funcs! {
|
||||
/// Calculates `a + b`
|
||||
0x00 dadd(a: f64, b: f64) -> f64;
|
||||
/// Calculates `a - b`
|
||||
0x04 dsub(a: f64, b: f64) -> f64;
|
||||
/// Calculates `a * b`
|
||||
0x08 dmul(a: f64, b: f64) -> f64;
|
||||
/// Calculates `a / b`
|
||||
0x0c ddiv(a: f64, b: f64) -> f64;
|
||||
|
||||
// 0x10 and 0x14 are deprecated
|
||||
|
||||
/// Calculates `sqrt(v)` (or return -Infinity if v is negative)
|
||||
0x18 dsqrt(v: f64) -> f64;
|
||||
/// Converts an f64 to a signed integer,
|
||||
/// rounding towards -Infinity, and clamping the result to lie within the
|
||||
/// range `-0x80000000` to `0x7FFFFFFF`
|
||||
0x1c double_to_int(v: f64) -> i32;
|
||||
/// Converts an f64 to an signed fixed point
|
||||
/// integer representation where n specifies the position of the binary
|
||||
/// point in the resulting fixed point representation, e.g.
|
||||
/// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity,
|
||||
/// and clamps the resulting integer to lie within the range `0x00000000` to
|
||||
/// `0xFFFFFFFF`
|
||||
0x20 double_to_fix(v: f64, n: i32) -> i32;
|
||||
/// Converts an f64 to an unsigned integer,
|
||||
/// rounding towards -Infinity, and clamping the result to lie within the
|
||||
/// range `0x00000000` to `0xFFFFFFFF`
|
||||
0x24 double_to_uint(v: f64) -> u32;
|
||||
/// Converts an f64 to an unsigned fixed point
|
||||
/// integer representation where n specifies the position of the binary
|
||||
/// point in the resulting fixed point representation, e.g.
|
||||
/// `f(0.5f, 16) == 0x8000`. This method rounds towards -Infinity,
|
||||
/// and clamps the resulting integer to lie within the range `0x00000000` to
|
||||
/// `0xFFFFFFFF`
|
||||
0x28 double_to_ufix(v: f64, n: i32) -> u32;
|
||||
/// Converts a signed integer to the nearest
|
||||
/// double value, rounding to even on tie
|
||||
0x2c int_to_double(v: i32) -> f64;
|
||||
/// Converts a signed fixed point integer
|
||||
/// representation to the nearest double value, rounding to even on tie. `n`
|
||||
/// specifies the position of the binary point in fixed point, so `f =
|
||||
/// nearest(v/(2^n))`
|
||||
0x30 fix_to_double(v: i32, n: i32) -> f64;
|
||||
/// Converts an unsigned integer to the nearest
|
||||
/// double value, rounding to even on tie
|
||||
0x34 uint_to_double(v: u32) -> f64;
|
||||
/// Converts an unsigned fixed point integer
|
||||
/// representation to the nearest double value, rounding to even on tie. `n`
|
||||
/// specifies the position of the binary point in fixed point, so f =
|
||||
/// nearest(v/(2^n))
|
||||
0x38 ufix_to_double(v: u32, n: i32) -> f64;
|
||||
/// Calculates the cosine of `angle`. The value
|
||||
/// of `angle` is in radians, and must be in the range `-1024` to `1024`
|
||||
0x3c dcos(angle: f64) -> f64;
|
||||
/// Calculates the sine of `angle`. The value of
|
||||
/// `angle` is in radians, and must be in the range `-1024` to `1024`
|
||||
0x40 dsin(angle: f64) -> f64;
|
||||
/// Calculates the tangent of `angle`. The value
|
||||
/// of `angle` is in radians, and must be in the range `-1024` to `1024`
|
||||
0x44 dtan(angle: f64) -> f64;
|
||||
|
||||
// 0x48 is deprecated
|
||||
|
||||
/// Calculates the exponential value of `v`,
|
||||
/// i.e. `e ** v`
|
||||
0x4c dexp(v: f64) -> f64;
|
||||
/// Calculates the natural logarithm of v. If v <= 0 return -Infinity
|
||||
0x50 dln(v: f64) -> f64;
|
||||
|
||||
// These are only on BootROM v2 or higher
|
||||
|
||||
/// Compares two floating point numbers, returning:
|
||||
/// • 0 if a == b
|
||||
/// • -1 if a < b
|
||||
/// • 1 if a > b
|
||||
0x54 dcmp(a: f64, b: f64) -> i32;
|
||||
/// Computes the arc tangent of `y/x` using the
|
||||
/// signs of arguments to determine the correct quadrant
|
||||
0x58 datan2(y: f64, x: f64) -> f64;
|
||||
/// Converts a signed 64-bit integer to the
|
||||
/// nearest double value, rounding to even on tie
|
||||
0x5c int64_to_double(v: i64) -> f64;
|
||||
/// Converts a signed fixed point 64-bit integer
|
||||
/// representation to the nearest double value, rounding to even on tie. `n`
|
||||
/// specifies the position of the binary point in fixed point, so `f =
|
||||
/// nearest(v/(2^n))`
|
||||
0x60 fix64_to_doubl(v: i64, n: i32) -> f64;
|
||||
/// Converts an unsigned 64-bit integer to the
|
||||
/// nearest double value, rounding to even on tie
|
||||
0x64 uint64_to_double(v: u64) -> f64;
|
||||
/// Converts an unsigned fixed point 64-bit
|
||||
/// integer representation to the nearest double value, rounding to even on
|
||||
/// tie. `n` specifies the position of the binary point in fixed point, so
|
||||
/// `f = nearest(v/(2^n))`
|
||||
0x68 ufix64_to_double(v: u64, n: i32) -> f64;
|
||||
/// Convert an f64 to a signed 64-bit integer, rounding towards -Infinity,
|
||||
/// and clamping the result to lie within the range `-0x8000000000000000` to
|
||||
/// `0x7FFFFFFFFFFFFFFF`
|
||||
0x6c double_to_int64(v: f64) -> i64;
|
||||
/// Converts an f64 to a signed fixed point
|
||||
/// 64-bit integer representation where n specifies the position of the
|
||||
/// binary point in the resulting fixed point representation - e.g. `f(0.5f,
|
||||
/// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the
|
||||
/// resulting integer to lie within the range `-0x8000000000000000` to
|
||||
/// `0x7FFFFFFFFFFFFFFF`
|
||||
0x70 double_to_fix64(v: f64, n: i32) -> i64;
|
||||
/// Converts an f64 to an unsigned 64-bit
|
||||
/// integer, rounding towards -Infinity, and clamping the result to lie
|
||||
/// within the range `0x0000000000000000` to `0xFFFFFFFFFFFFFFFF`
|
||||
0x74 double_to_uint64(v: f64) -> u64;
|
||||
/// Converts an f64 to an unsigned fixed point
|
||||
/// 64-bit integer representation where n specifies the position of the
|
||||
/// binary point in the resulting fixed point representation, e.g. `f(0.5f,
|
||||
/// 16) == 0x8000`. This method rounds towards -Infinity, and clamps the
|
||||
/// resulting integer to lie within the range `0x0000000000000000` to
|
||||
/// `0xFFFFFFFFFFFFFFFF`
|
||||
0x78 double_to_ufix64(v: f64, n: i32) -> u64;
|
||||
/// Converts an f64 to an f32
|
||||
0x7c double_to_float(v: f64) -> f32;
|
||||
}
|
||||
}
|
62
embassy-rp/src/rtc/datetime_chrono.rs
Normal file
62
embassy-rp/src/rtc/datetime_chrono.rs
Normal file
@ -0,0 +1,62 @@
|
||||
use chrono::{Datelike, Timelike};
|
||||
|
||||
use crate::pac::rtc::regs::{Rtc0, Rtc1, Setup0, Setup1};
|
||||
|
||||
/// Alias for [`chrono::NaiveDateTime`]
|
||||
pub type DateTime = chrono::NaiveDateTime;
|
||||
/// Alias for [`chrono::Weekday`]
|
||||
pub type DayOfWeek = chrono::Weekday;
|
||||
|
||||
/// Errors regarding the [`DateTime`] and [`DateTimeFilter`] structs.
|
||||
///
|
||||
/// [`DateTimeFilter`]: struct.DateTimeFilter.html
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
/// The [DateTime] has an invalid year. The year must be between 0 and 4095.
|
||||
InvalidYear,
|
||||
/// The [DateTime] contains an invalid date.
|
||||
InvalidDate,
|
||||
/// The [DateTime] contains an invalid time.
|
||||
InvalidTime,
|
||||
}
|
||||
|
||||
pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 {
|
||||
dotw.num_days_from_sunday() as u8
|
||||
}
|
||||
|
||||
pub(crate) fn validate_datetime(dt: &DateTime) -> Result<(), Error> {
|
||||
if dt.year() < 0 || dt.year() > 4095 {
|
||||
// rp2040 can't hold these years
|
||||
Err(Error::InvalidYear)
|
||||
} else {
|
||||
// The rest of the chrono date is assumed to be valid
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn write_setup_0(dt: &DateTime, w: &mut Setup0) {
|
||||
w.set_year(dt.year() as u16);
|
||||
w.set_month(dt.month() as u8);
|
||||
w.set_day(dt.day() as u8);
|
||||
}
|
||||
|
||||
pub(super) fn write_setup_1(dt: &DateTime, w: &mut Setup1) {
|
||||
w.set_dotw(dt.weekday().num_days_from_sunday() as u8);
|
||||
w.set_hour(dt.hour() as u8);
|
||||
w.set_min(dt.minute() as u8);
|
||||
w.set_sec(dt.second() as u8);
|
||||
}
|
||||
|
||||
pub(super) fn datetime_from_registers(rtc_0: Rtc0, rtc_1: Rtc1) -> Result<DateTime, Error> {
|
||||
let year = rtc_1.year() as i32;
|
||||
let month = rtc_1.month() as u32;
|
||||
let day = rtc_1.day() as u32;
|
||||
|
||||
let hour = rtc_0.hour() as u32;
|
||||
let minute = rtc_0.min() as u32;
|
||||
let second = rtc_0.sec() as u32;
|
||||
|
||||
let date = chrono::NaiveDate::from_ymd_opt(year, month, day).ok_or(Error::InvalidDate)?;
|
||||
let time = chrono::NaiveTime::from_hms_opt(hour, minute, second).ok_or(Error::InvalidTime)?;
|
||||
Ok(DateTime::new(date, time))
|
||||
}
|
127
embassy-rp/src/rtc/datetime_no_deps.rs
Normal file
127
embassy-rp/src/rtc/datetime_no_deps.rs
Normal file
@ -0,0 +1,127 @@
|
||||
use crate::pac::rtc::regs::{Rtc0, Rtc1, Setup0, Setup1};
|
||||
|
||||
/// Errors regarding the [`DateTime`] and [`DateTimeFilter`] structs.
|
||||
///
|
||||
/// [`DateTimeFilter`]: struct.DateTimeFilter.html
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
/// The [DateTime] contains an invalid year value. Must be between `0..=4095`.
|
||||
InvalidYear,
|
||||
/// The [DateTime] contains an invalid month value. Must be between `1..=12`.
|
||||
InvalidMonth,
|
||||
/// The [DateTime] contains an invalid day value. Must be between `1..=31`.
|
||||
InvalidDay,
|
||||
/// The [DateTime] contains an invalid day of week. Must be between `0..=6` where 0 is Sunday.
|
||||
InvalidDayOfWeek(
|
||||
/// The value of the DayOfWeek that was given.
|
||||
u8,
|
||||
),
|
||||
/// The [DateTime] contains an invalid hour value. Must be between `0..=23`.
|
||||
InvalidHour,
|
||||
/// The [DateTime] contains an invalid minute value. Must be between `0..=59`.
|
||||
InvalidMinute,
|
||||
/// The [DateTime] contains an invalid second value. Must be between `0..=59`.
|
||||
InvalidSecond,
|
||||
}
|
||||
|
||||
/// Structure containing date and time information
|
||||
pub struct DateTime {
|
||||
/// 0..4095
|
||||
pub year: u16,
|
||||
/// 1..12, 1 is January
|
||||
pub month: u8,
|
||||
/// 1..28,29,30,31 depending on month
|
||||
pub day: u8,
|
||||
///
|
||||
pub day_of_week: DayOfWeek,
|
||||
/// 0..23
|
||||
pub hour: u8,
|
||||
/// 0..59
|
||||
pub minute: u8,
|
||||
/// 0..59
|
||||
pub second: u8,
|
||||
}
|
||||
|
||||
/// A day of the week
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum DayOfWeek {
|
||||
Sunday = 0,
|
||||
Monday = 1,
|
||||
Tuesday = 2,
|
||||
Wednesday = 3,
|
||||
Thursday = 4,
|
||||
Friday = 5,
|
||||
Saturday = 6,
|
||||
}
|
||||
|
||||
fn day_of_week_from_u8(v: u8) -> Result<DayOfWeek, Error> {
|
||||
Ok(match v {
|
||||
0 => DayOfWeek::Sunday,
|
||||
1 => DayOfWeek::Monday,
|
||||
2 => DayOfWeek::Tuesday,
|
||||
3 => DayOfWeek::Wednesday,
|
||||
4 => DayOfWeek::Thursday,
|
||||
5 => DayOfWeek::Friday,
|
||||
6 => DayOfWeek::Saturday,
|
||||
x => return Err(Error::InvalidDayOfWeek(x)),
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 {
|
||||
dotw as u8
|
||||
}
|
||||
|
||||
pub(super) fn validate_datetime(dt: &DateTime) -> Result<(), Error> {
|
||||
if dt.year > 4095 {
|
||||
Err(Error::InvalidYear)
|
||||
} else if dt.month < 1 || dt.month > 12 {
|
||||
Err(Error::InvalidMonth)
|
||||
} else if dt.day < 1 || dt.day > 31 {
|
||||
Err(Error::InvalidDay)
|
||||
} else if dt.hour > 23 {
|
||||
Err(Error::InvalidHour)
|
||||
} else if dt.minute > 59 {
|
||||
Err(Error::InvalidMinute)
|
||||
} else if dt.second > 59 {
|
||||
Err(Error::InvalidSecond)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn write_setup_0(dt: &DateTime, w: &mut Setup0) {
|
||||
w.set_year(dt.year);
|
||||
w.set_month(dt.month);
|
||||
w.set_day(dt.day);
|
||||
}
|
||||
|
||||
pub(super) fn write_setup_1(dt: &DateTime, w: &mut Setup1) {
|
||||
w.set_dotw(dt.day_of_week as u8);
|
||||
w.set_hour(dt.hour);
|
||||
w.set_min(dt.minute);
|
||||
w.set_sec(dt.second);
|
||||
}
|
||||
|
||||
pub(super) fn datetime_from_registers(rtc_0: Rtc0, rtc_1: Rtc1) -> Result<DateTime, Error> {
|
||||
let year = rtc_1.year();
|
||||
let month = rtc_1.month();
|
||||
let day = rtc_1.day();
|
||||
|
||||
let day_of_week = rtc_0.dotw();
|
||||
let hour = rtc_0.hour();
|
||||
let minute = rtc_0.min();
|
||||
let second = rtc_0.sec();
|
||||
|
||||
let day_of_week = day_of_week_from_u8(day_of_week)?;
|
||||
Ok(DateTime {
|
||||
year,
|
||||
month,
|
||||
day,
|
||||
day_of_week,
|
||||
hour,
|
||||
minute,
|
||||
second,
|
||||
})
|
||||
}
|
100
embassy-rp/src/rtc/filter.rs
Normal file
100
embassy-rp/src/rtc/filter.rs
Normal file
@ -0,0 +1,100 @@
|
||||
use super::DayOfWeek;
|
||||
use crate::pac::rtc::regs::{IrqSetup0, IrqSetup1};
|
||||
|
||||
/// A filter used for [`RealTimeClock::schedule_alarm`].
|
||||
///
|
||||
/// [`RealTimeClock::schedule_alarm`]: struct.RealTimeClock.html#method.schedule_alarm
|
||||
#[derive(Default)]
|
||||
pub struct DateTimeFilter {
|
||||
/// The year that this alarm should trigger on, `None` if the RTC alarm should not trigger on a year value.
|
||||
pub year: Option<u16>,
|
||||
/// The month that this alarm should trigger on, `None` if the RTC alarm should not trigger on a month value.
|
||||
pub month: Option<u8>,
|
||||
/// The day that this alarm should trigger on, `None` if the RTC alarm should not trigger on a day value.
|
||||
pub day: Option<u8>,
|
||||
/// The day of week that this alarm should trigger on, `None` if the RTC alarm should not trigger on a day of week value.
|
||||
pub day_of_week: Option<DayOfWeek>,
|
||||
/// The hour that this alarm should trigger on, `None` if the RTC alarm should not trigger on a hour value.
|
||||
pub hour: Option<u8>,
|
||||
/// The minute that this alarm should trigger on, `None` if the RTC alarm should not trigger on a minute value.
|
||||
pub minute: Option<u8>,
|
||||
/// The second that this alarm should trigger on, `None` if the RTC alarm should not trigger on a second value.
|
||||
pub second: Option<u8>,
|
||||
}
|
||||
|
||||
impl DateTimeFilter {
|
||||
/// Set a filter on the given year
|
||||
pub fn year(mut self, year: u16) -> Self {
|
||||
self.year = Some(year);
|
||||
self
|
||||
}
|
||||
/// Set a filter on the given month
|
||||
pub fn month(mut self, month: u8) -> Self {
|
||||
self.month = Some(month);
|
||||
self
|
||||
}
|
||||
/// Set a filter on the given day
|
||||
pub fn day(mut self, day: u8) -> Self {
|
||||
self.day = Some(day);
|
||||
self
|
||||
}
|
||||
/// Set a filter on the given day of the week
|
||||
pub fn day_of_week(mut self, day_of_week: DayOfWeek) -> Self {
|
||||
self.day_of_week = Some(day_of_week);
|
||||
self
|
||||
}
|
||||
/// Set a filter on the given hour
|
||||
pub fn hour(mut self, hour: u8) -> Self {
|
||||
self.hour = Some(hour);
|
||||
self
|
||||
}
|
||||
/// Set a filter on the given minute
|
||||
pub fn minute(mut self, minute: u8) -> Self {
|
||||
self.minute = Some(minute);
|
||||
self
|
||||
}
|
||||
/// Set a filter on the given second
|
||||
pub fn second(mut self, second: u8) -> Self {
|
||||
self.second = Some(second);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// register helper functions
|
||||
impl DateTimeFilter {
|
||||
pub(super) fn write_setup_0(&self, w: &mut IrqSetup0) {
|
||||
if let Some(year) = self.year {
|
||||
w.set_year_ena(true);
|
||||
|
||||
w.set_year(year);
|
||||
}
|
||||
if let Some(month) = self.month {
|
||||
w.set_month_ena(true);
|
||||
w.set_month(month);
|
||||
}
|
||||
if let Some(day) = self.day {
|
||||
w.set_day_ena(true);
|
||||
w.set_day(day);
|
||||
}
|
||||
}
|
||||
pub(super) fn write_setup_1(&self, w: &mut IrqSetup1) {
|
||||
if let Some(day_of_week) = self.day_of_week {
|
||||
w.set_dotw_ena(true);
|
||||
let bits = super::datetime::day_of_week_to_u8(day_of_week);
|
||||
|
||||
w.set_dotw(bits);
|
||||
}
|
||||
if let Some(hour) = self.hour {
|
||||
w.set_hour_ena(true);
|
||||
w.set_hour(hour);
|
||||
}
|
||||
if let Some(minute) = self.minute {
|
||||
w.set_min_ena(true);
|
||||
w.set_min(minute);
|
||||
}
|
||||
if let Some(second) = self.second {
|
||||
w.set_sec_ena(true);
|
||||
w.set_sec(second);
|
||||
}
|
||||
}
|
||||
}
|
190
embassy-rp/src/rtc/mod.rs
Normal file
190
embassy-rp/src/rtc/mod.rs
Normal file
@ -0,0 +1,190 @@
|
||||
mod filter;
|
||||
|
||||
use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
|
||||
|
||||
pub use self::filter::DateTimeFilter;
|
||||
|
||||
#[cfg_attr(feature = "chrono", path = "datetime_chrono.rs")]
|
||||
#[cfg_attr(not(feature = "chrono"), path = "datetime_no_deps.rs")]
|
||||
mod datetime;
|
||||
|
||||
pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError};
|
||||
use crate::clocks::clk_rtc_freq;
|
||||
|
||||
/// A reference to the real time clock of the system
|
||||
pub struct RealTimeClock<'d, T: Instance> {
|
||||
inner: PeripheralRef<'d, T>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> RealTimeClock<'d, T> {
|
||||
/// Create a new instance of the real time clock, with the given date as an initial value.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.
|
||||
pub fn new(inner: impl Peripheral<P = T> + 'd, initial_date: DateTime) -> Result<Self, RtcError> {
|
||||
into_ref!(inner);
|
||||
|
||||
// Set the RTC divider
|
||||
unsafe {
|
||||
inner
|
||||
.regs()
|
||||
.clkdiv_m1()
|
||||
.write(|w| w.set_clkdiv_m1(clk_rtc_freq() as u16 - 1))
|
||||
};
|
||||
|
||||
let mut result = Self { inner };
|
||||
result.set_leap_year_check(true); // should be on by default, make sure this is the case.
|
||||
result.set_datetime(initial_date)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Enable or disable the leap year check. The rp2040 chip will always add a Feb 29th on every year that is divisable by 4, but this may be incorrect (e.g. on century years). This function allows you to disable this check.
|
||||
///
|
||||
/// Leap year checking is enabled by default.
|
||||
pub fn set_leap_year_check(&mut self, leap_year_check_enabled: bool) {
|
||||
unsafe {
|
||||
self.inner
|
||||
.regs()
|
||||
.ctrl()
|
||||
.modify(|w| w.set_force_notleapyear(!leap_year_check_enabled))
|
||||
};
|
||||
}
|
||||
|
||||
/// Checks to see if this RealTimeClock is running
|
||||
pub fn is_running(&self) -> bool {
|
||||
unsafe { self.inner.regs().ctrl().read().rtc_active() }
|
||||
}
|
||||
|
||||
/// Set the datetime to a new value.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.
|
||||
pub fn set_datetime(&mut self, t: DateTime) -> Result<(), RtcError> {
|
||||
self::datetime::validate_datetime(&t).map_err(RtcError::InvalidDateTime)?;
|
||||
|
||||
// disable RTC while we configure it
|
||||
unsafe {
|
||||
self.inner.regs().ctrl().modify(|w| w.set_rtc_enable(false));
|
||||
while self.inner.regs().ctrl().read().rtc_active() {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
self.inner.regs().setup_0().write(|w| {
|
||||
self::datetime::write_setup_0(&t, w);
|
||||
});
|
||||
self.inner.regs().setup_1().write(|w| {
|
||||
self::datetime::write_setup_1(&t, w);
|
||||
});
|
||||
|
||||
// Load the new datetime and re-enable RTC
|
||||
self.inner.regs().ctrl().write(|w| w.set_load(true));
|
||||
self.inner.regs().ctrl().write(|w| w.set_rtc_enable(true));
|
||||
while !self.inner.regs().ctrl().read().rtc_active() {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Return the current datetime.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`].
|
||||
pub fn now(&self) -> Result<DateTime, RtcError> {
|
||||
if !self.is_running() {
|
||||
return Err(RtcError::NotRunning);
|
||||
}
|
||||
|
||||
let rtc_0 = unsafe { self.inner.regs().rtc_0().read() };
|
||||
let rtc_1 = unsafe { self.inner.regs().rtc_1().read() };
|
||||
|
||||
self::datetime::datetime_from_registers(rtc_0, rtc_1).map_err(RtcError::InvalidDateTime)
|
||||
}
|
||||
|
||||
/// Disable the alarm that was scheduled with [`schedule_alarm`].
|
||||
///
|
||||
/// [`schedule_alarm`]: #method.schedule_alarm
|
||||
pub fn disable_alarm(&mut self) {
|
||||
unsafe {
|
||||
self.inner.regs().irq_setup_0().modify(|s| s.set_match_ena(false));
|
||||
|
||||
while self.inner.regs().irq_setup_0().read().match_active() {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Schedule an alarm. The `filter` determines at which point in time this alarm is set.
|
||||
///
|
||||
/// Keep in mind that the filter only triggers on the specified time. If you want to schedule this alarm every minute, you have to call:
|
||||
/// ```no_run
|
||||
/// # #[cfg(feature = "chrono")]
|
||||
/// # fn main() { }
|
||||
/// # #[cfg(not(feature = "chrono"))]
|
||||
/// # fn main() {
|
||||
/// # use embassy_rp::rtc::{RealTimeClock, DateTimeFilter};
|
||||
/// # let mut real_time_clock: RealTimeClock = unsafe { core::mem::zeroed() };
|
||||
/// let now = real_time_clock.now().unwrap();
|
||||
/// real_time_clock.schedule_alarm(
|
||||
/// DateTimeFilter::default()
|
||||
/// .minute(if now.minute == 59 { 0 } else { now.minute + 1 })
|
||||
/// );
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn schedule_alarm(&mut self, filter: DateTimeFilter) {
|
||||
self.disable_alarm();
|
||||
|
||||
unsafe {
|
||||
self.inner.regs().irq_setup_0().write(|w| {
|
||||
filter.write_setup_0(w);
|
||||
});
|
||||
self.inner.regs().irq_setup_1().write(|w| {
|
||||
filter.write_setup_1(w);
|
||||
});
|
||||
|
||||
self.inner.regs().inte().modify(|w| w.set_rtc(true));
|
||||
|
||||
// Set the enable bit and check if it is set
|
||||
self.inner.regs().irq_setup_0().modify(|w| w.set_match_ena(true));
|
||||
while !self.inner.regs().irq_setup_0().read().match_active() {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear the interrupt. This should be called every time the `RTC_IRQ` interrupt is triggered,
|
||||
/// or the next [`schedule_alarm`] will never fire.
|
||||
///
|
||||
/// [`schedule_alarm`]: #method.schedule_alarm
|
||||
pub fn clear_interrupt(&mut self) {
|
||||
self.disable_alarm();
|
||||
}
|
||||
}
|
||||
|
||||
/// Errors that can occur on methods on [RtcClock]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum RtcError {
|
||||
/// An invalid DateTime was given or stored on the hardware.
|
||||
InvalidDateTime(DateTimeError),
|
||||
|
||||
/// The RTC clock is not running
|
||||
NotRunning,
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
pub trait Instance {
|
||||
fn regs(&self) -> crate::pac::rtc::Rtc;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Instance: sealed::Instance {}
|
||||
|
||||
impl sealed::Instance for crate::peripherals::RTC {
|
||||
fn regs(&self) -> crate::pac::rtc::Rtc {
|
||||
crate::pac::RTC
|
||||
}
|
||||
}
|
||||
impl Instance for crate::peripherals::RTC {}
|
@ -1,7 +1,11 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embassy_embedded_hal::SetConfig;
|
||||
use embassy_futures::join::join;
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
pub use embedded_hal_02::spi::{Phase, Polarity};
|
||||
|
||||
use crate::dma::{AnyChannel, Channel};
|
||||
use crate::gpio::sealed::Pin as _;
|
||||
use crate::gpio::{AnyPin, Pin as GpioPin};
|
||||
use crate::{pac, peripherals, Peripheral};
|
||||
@ -30,8 +34,11 @@ impl Default for Config {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Spi<'d, T: Instance> {
|
||||
pub struct Spi<'d, T: Instance, M: Mode> {
|
||||
inner: PeripheralRef<'d, T>,
|
||||
tx_dma: Option<PeripheralRef<'d, AnyChannel>>,
|
||||
rx_dma: Option<PeripheralRef<'d, AnyChannel>>,
|
||||
phantom: PhantomData<(&'d mut T, M)>,
|
||||
}
|
||||
|
||||
fn div_roundup(a: u32, b: u32) -> u32 {
|
||||
@ -57,51 +64,15 @@ fn calc_prescs(freq: u32) -> (u8, u8) {
|
||||
((presc * 2) as u8, (postdiv - 1) as u8)
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Spi<'d, T> {
|
||||
pub fn new(
|
||||
inner: impl Peripheral<P = T> + 'd,
|
||||
clk: impl Peripheral<P = impl ClkPin<T> + 'd> + 'd,
|
||||
mosi: impl Peripheral<P = impl MosiPin<T> + 'd> + 'd,
|
||||
miso: impl Peripheral<P = impl MisoPin<T> + 'd> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(clk, mosi, miso);
|
||||
Self::new_inner(
|
||||
inner,
|
||||
Some(clk.map_into()),
|
||||
Some(mosi.map_into()),
|
||||
Some(miso.map_into()),
|
||||
None,
|
||||
config,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_txonly(
|
||||
inner: impl Peripheral<P = T> + 'd,
|
||||
clk: impl Peripheral<P = impl ClkPin<T> + 'd> + 'd,
|
||||
mosi: impl Peripheral<P = impl MosiPin<T> + 'd> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(clk, mosi);
|
||||
Self::new_inner(inner, Some(clk.map_into()), Some(mosi.map_into()), None, None, config)
|
||||
}
|
||||
|
||||
pub fn new_rxonly(
|
||||
inner: impl Peripheral<P = T> + 'd,
|
||||
clk: impl Peripheral<P = impl ClkPin<T> + 'd> + 'd,
|
||||
miso: impl Peripheral<P = impl MisoPin<T> + 'd> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(clk, miso);
|
||||
Self::new_inner(inner, Some(clk.map_into()), None, Some(miso.map_into()), None, config)
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> Spi<'d, T, M> {
|
||||
fn new_inner(
|
||||
inner: impl Peripheral<P = T> + 'd,
|
||||
clk: Option<PeripheralRef<'d, AnyPin>>,
|
||||
mosi: Option<PeripheralRef<'d, AnyPin>>,
|
||||
miso: Option<PeripheralRef<'d, AnyPin>>,
|
||||
cs: Option<PeripheralRef<'d, AnyPin>>,
|
||||
tx_dma: Option<PeripheralRef<'d, AnyChannel>>,
|
||||
rx_dma: Option<PeripheralRef<'d, AnyChannel>>,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(inner);
|
||||
@ -134,7 +105,12 @@ impl<'d, T: Instance> Spi<'d, T> {
|
||||
pin.io().ctrl().write(|w| w.set_funcsel(1));
|
||||
}
|
||||
}
|
||||
Self { inner }
|
||||
Self {
|
||||
inner,
|
||||
tx_dma,
|
||||
rx_dma,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn blocking_write(&mut self, data: &[u8]) -> Result<(), Error> {
|
||||
@ -225,19 +201,236 @@ impl<'d, T: Instance> Spi<'d, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Spi<'d, T, Blocking> {
|
||||
pub fn new_blocking(
|
||||
inner: impl Peripheral<P = T> + 'd,
|
||||
clk: impl Peripheral<P = impl ClkPin<T> + 'd> + 'd,
|
||||
mosi: impl Peripheral<P = impl MosiPin<T> + 'd> + 'd,
|
||||
miso: impl Peripheral<P = impl MisoPin<T> + 'd> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(clk, mosi, miso);
|
||||
Self::new_inner(
|
||||
inner,
|
||||
Some(clk.map_into()),
|
||||
Some(mosi.map_into()),
|
||||
Some(miso.map_into()),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
config,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_blocking_txonly(
|
||||
inner: impl Peripheral<P = T> + 'd,
|
||||
clk: impl Peripheral<P = impl ClkPin<T> + 'd> + 'd,
|
||||
mosi: impl Peripheral<P = impl MosiPin<T> + 'd> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(clk, mosi);
|
||||
Self::new_inner(
|
||||
inner,
|
||||
Some(clk.map_into()),
|
||||
Some(mosi.map_into()),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
config,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_blocking_rxonly(
|
||||
inner: impl Peripheral<P = T> + 'd,
|
||||
clk: impl Peripheral<P = impl ClkPin<T> + 'd> + 'd,
|
||||
miso: impl Peripheral<P = impl MisoPin<T> + 'd> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(clk, miso);
|
||||
Self::new_inner(
|
||||
inner,
|
||||
Some(clk.map_into()),
|
||||
None,
|
||||
Some(miso.map_into()),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
config,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Spi<'d, T, Async> {
|
||||
pub fn new(
|
||||
inner: impl Peripheral<P = T> + 'd,
|
||||
clk: impl Peripheral<P = impl ClkPin<T> + 'd> + 'd,
|
||||
mosi: impl Peripheral<P = impl MosiPin<T> + 'd> + 'd,
|
||||
miso: impl Peripheral<P = impl MisoPin<T> + 'd> + 'd,
|
||||
tx_dma: impl Peripheral<P = impl Channel> + 'd,
|
||||
rx_dma: impl Peripheral<P = impl Channel> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(tx_dma, rx_dma, clk, mosi, miso);
|
||||
Self::new_inner(
|
||||
inner,
|
||||
Some(clk.map_into()),
|
||||
Some(mosi.map_into()),
|
||||
Some(miso.map_into()),
|
||||
None,
|
||||
Some(tx_dma.map_into()),
|
||||
Some(rx_dma.map_into()),
|
||||
config,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_txonly(
|
||||
inner: impl Peripheral<P = T> + 'd,
|
||||
clk: impl Peripheral<P = impl ClkPin<T> + 'd> + 'd,
|
||||
mosi: impl Peripheral<P = impl MosiPin<T> + 'd> + 'd,
|
||||
tx_dma: impl Peripheral<P = impl Channel> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(tx_dma, clk, mosi);
|
||||
Self::new_inner(
|
||||
inner,
|
||||
Some(clk.map_into()),
|
||||
Some(mosi.map_into()),
|
||||
None,
|
||||
None,
|
||||
Some(tx_dma.map_into()),
|
||||
None,
|
||||
config,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_rxonly(
|
||||
inner: impl Peripheral<P = T> + 'd,
|
||||
clk: impl Peripheral<P = impl ClkPin<T> + 'd> + 'd,
|
||||
miso: impl Peripheral<P = impl MisoPin<T> + 'd> + 'd,
|
||||
rx_dma: impl Peripheral<P = impl Channel> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
into_ref!(rx_dma, clk, miso);
|
||||
Self::new_inner(
|
||||
inner,
|
||||
Some(clk.map_into()),
|
||||
None,
|
||||
Some(miso.map_into()),
|
||||
None,
|
||||
None,
|
||||
Some(rx_dma.map_into()),
|
||||
config,
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
|
||||
let tx_ch = self.tx_dma.as_mut().unwrap();
|
||||
let tx_transfer = unsafe {
|
||||
self.inner.regs().dmacr().modify(|reg| {
|
||||
reg.set_txdmae(true);
|
||||
});
|
||||
// If we don't assign future to a variable, the data register pointer
|
||||
// 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)
|
||||
};
|
||||
tx_transfer.await;
|
||||
|
||||
let p = self.inner.regs();
|
||||
unsafe {
|
||||
while p.sr().read().bsy() {}
|
||||
|
||||
// clear RX FIFO contents to prevent stale reads
|
||||
while p.sr().read().rne() {
|
||||
let _: u16 = p.dr().read().data();
|
||||
}
|
||||
// clear RX overrun interrupt
|
||||
p.icr().write(|w| w.set_roric(true));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
unsafe {
|
||||
self.inner.regs().dmacr().write(|reg| {
|
||||
reg.set_rxdmae(true);
|
||||
reg.set_txdmae(true);
|
||||
})
|
||||
};
|
||||
let tx_ch = self.tx_dma.as_mut().unwrap();
|
||||
let tx_transfer = unsafe {
|
||||
// If we don't assign future to a variable, the data register pointer
|
||||
// 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)
|
||||
};
|
||||
let rx_ch = self.rx_dma.as_mut().unwrap();
|
||||
let rx_transfer = unsafe {
|
||||
// If we don't assign future to a variable, the data register pointer
|
||||
// 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)
|
||||
};
|
||||
join(tx_transfer, rx_transfer).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn transfer(&mut self, rx_buffer: &mut [u8], tx_buffer: &[u8]) -> Result<(), Error> {
|
||||
self.transfer_inner(rx_buffer, tx_buffer).await
|
||||
}
|
||||
|
||||
pub async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Error> {
|
||||
self.transfer_inner(words, words).await
|
||||
}
|
||||
|
||||
async fn transfer_inner(&mut self, rx_ptr: *mut [u8], tx_ptr: *const [u8]) -> Result<(), Error> {
|
||||
let (_, from_len) = crate::dma::slice_ptr_parts(tx_ptr);
|
||||
let (_, to_len) = crate::dma::slice_ptr_parts_mut(rx_ptr);
|
||||
assert_eq!(from_len, to_len);
|
||||
unsafe {
|
||||
self.inner.regs().dmacr().write(|reg| {
|
||||
reg.set_rxdmae(true);
|
||||
reg.set_txdmae(true);
|
||||
})
|
||||
};
|
||||
let tx_ch = self.tx_dma.as_mut().unwrap();
|
||||
let tx_transfer = unsafe {
|
||||
// If we don't assign future to a variable, the data register pointer
|
||||
// is held across an await and makes the future non-Send.
|
||||
crate::dma::write(tx_ch, tx_ptr, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ)
|
||||
};
|
||||
let rx_ch = self.rx_dma.as_mut().unwrap();
|
||||
let rx_transfer = unsafe {
|
||||
// If we don't assign future to a variable, the data register pointer
|
||||
// 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)
|
||||
};
|
||||
join(tx_transfer, rx_transfer).await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
use super::*;
|
||||
|
||||
pub trait Mode {}
|
||||
|
||||
pub trait Instance {
|
||||
const TX_DREQ: u8;
|
||||
const RX_DREQ: u8;
|
||||
|
||||
fn regs(&self) -> pac::spi::Spi;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Mode: sealed::Mode {}
|
||||
pub trait Instance: sealed::Instance {}
|
||||
|
||||
macro_rules! impl_instance {
|
||||
($type:ident, $irq:ident) => {
|
||||
($type:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => {
|
||||
impl sealed::Instance for peripherals::$type {
|
||||
const TX_DREQ: u8 = $tx_dreq;
|
||||
const RX_DREQ: u8 = $rx_dreq;
|
||||
|
||||
fn regs(&self) -> pac::spi::Spi {
|
||||
pac::$type
|
||||
}
|
||||
@ -246,8 +439,8 @@ macro_rules! impl_instance {
|
||||
};
|
||||
}
|
||||
|
||||
impl_instance!(SPI0, Spi0);
|
||||
impl_instance!(SPI1, Spi1);
|
||||
impl_instance!(SPI0, Spi0, 16, 17);
|
||||
impl_instance!(SPI1, Spi1, 18, 19);
|
||||
|
||||
pub trait ClkPin<T: Instance>: GpioPin {}
|
||||
pub trait CsPin<T: Instance>: GpioPin {}
|
||||
@ -281,12 +474,25 @@ impl_pin!(PIN_17, SPI0, CsPin);
|
||||
impl_pin!(PIN_18, SPI0, ClkPin);
|
||||
impl_pin!(PIN_19, SPI0, MosiPin);
|
||||
|
||||
macro_rules! impl_mode {
|
||||
($name:ident) => {
|
||||
impl sealed::Mode for $name {}
|
||||
impl Mode for $name {}
|
||||
};
|
||||
}
|
||||
|
||||
pub struct Blocking;
|
||||
pub struct Async;
|
||||
|
||||
impl_mode!(Blocking);
|
||||
impl_mode!(Async);
|
||||
|
||||
// ====================
|
||||
|
||||
mod eh02 {
|
||||
use super::*;
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_02::blocking::spi::Transfer<u8> for Spi<'d, T> {
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::spi::Transfer<u8> for Spi<'d, T, M> {
|
||||
type Error = Error;
|
||||
fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {
|
||||
self.blocking_transfer_in_place(words)?;
|
||||
@ -294,7 +500,7 @@ mod eh02 {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_02::blocking::spi::Write<u8> for Spi<'d, T> {
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::spi::Write<u8> for Spi<'d, T, M> {
|
||||
type Error = Error;
|
||||
|
||||
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
|
||||
@ -313,29 +519,29 @@ mod eh1 {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_1::spi::ErrorType for Spi<'d, T> {
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::ErrorType for Spi<'d, T, M> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusFlush for Spi<'d, T> {
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBusFlush for Spi<'d, T, M> {
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusRead<u8> for Spi<'d, T> {
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBusRead<u8> for Spi<'d, T, M> {
|
||||
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_transfer(words, &[])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusWrite<u8> for Spi<'d, T> {
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBusWrite<u8> for Spi<'d, T, M> {
|
||||
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(words)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBus<u8> for Spi<'d, T> {
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::SpiBus<u8> for Spi<'d, T, M> {
|
||||
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_transfer(read, write)
|
||||
}
|
||||
@ -346,7 +552,52 @@ mod eh1 {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> SetConfig for Spi<'d, T> {
|
||||
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
|
||||
mod eha {
|
||||
use core::future::Future;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusFlush for Spi<'d, T, Async> {
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
async { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusWrite<u8> for Spi<'d, T, Async> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, data: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.write(data)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusRead<u8> for Spi<'d, T, Async> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, data: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.read(data)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_hal_async::spi::SpiBus<u8> for Spi<'d, T, Async> {
|
||||
type TransferFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn transfer<'a>(&'a mut self, rx: &'a mut [u8], tx: &'a [u8]) -> Self::TransferFuture<'a> {
|
||||
self.transfer(rx, tx)
|
||||
}
|
||||
|
||||
type TransferInPlaceFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Self::TransferInPlaceFuture<'a> {
|
||||
self.transfer_in_place(words)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> SetConfig for Spi<'d, T, M> {
|
||||
type Config = Config;
|
||||
fn set_config(&mut self, config: &Self::Config) {
|
||||
let p = self.inner.regs();
|
||||
|
@ -68,7 +68,7 @@ impl Driver for TimerDriver {
|
||||
})
|
||||
}
|
||||
|
||||
fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) {
|
||||
fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
|
||||
let n = alarm.id() as usize;
|
||||
critical_section::with(|cs| {
|
||||
let alarm = &self.alarms.borrow(cs)[n];
|
||||
@ -81,11 +81,16 @@ impl Driver for TimerDriver {
|
||||
unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) };
|
||||
|
||||
let now = self.now();
|
||||
|
||||
// If alarm timestamp has passed, trigger it instantly.
|
||||
// This disarms it.
|
||||
if timestamp <= now {
|
||||
self.trigger_alarm(n, cs);
|
||||
// If alarm timestamp has passed the alarm will not fire.
|
||||
// Disarm the alarm and return `false` to indicate that.
|
||||
unsafe { pac::TIMER.armed().write(|w| w.set_armed(1 << n)) }
|
||||
|
||||
alarm.timestamp.set(u64::MAX);
|
||||
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
489
embassy-rp/src/uart/buffered.rs
Normal file
489
embassy-rp/src/uart/buffered.rs
Normal file
@ -0,0 +1,489 @@
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::task::{Poll, Waker};
|
||||
|
||||
use atomic_polyfill::{compiler_fence, Ordering};
|
||||
use embassy_cortex_m::peripheral::{PeripheralMutex, PeripheralState, StateStorage};
|
||||
use embassy_hal_common::ring_buffer::RingBuffer;
|
||||
use embassy_sync::waitqueue::WakerRegistration;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct State<'d, T: Instance>(StateStorage<FullStateInner<'d, T>>);
|
||||
impl<'d, T: Instance> State<'d, T> {
|
||||
pub const fn new() -> Self {
|
||||
Self(StateStorage::new())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RxState<'d, T: Instance>(StateStorage<RxStateInner<'d, T>>);
|
||||
impl<'d, T: Instance> RxState<'d, T> {
|
||||
pub const fn new() -> Self {
|
||||
Self(StateStorage::new())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TxState<'d, T: Instance>(StateStorage<TxStateInner<'d, T>>);
|
||||
impl<'d, T: Instance> TxState<'d, T> {
|
||||
pub const fn new() -> Self {
|
||||
Self(StateStorage::new())
|
||||
}
|
||||
}
|
||||
|
||||
struct RxStateInner<'d, T: Instance> {
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
|
||||
waker: WakerRegistration,
|
||||
buf: RingBuffer<'d>,
|
||||
}
|
||||
|
||||
struct TxStateInner<'d, T: Instance> {
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
|
||||
waker: WakerRegistration,
|
||||
buf: RingBuffer<'d>,
|
||||
}
|
||||
|
||||
struct FullStateInner<'d, T: Instance> {
|
||||
rx: RxStateInner<'d, T>,
|
||||
tx: TxStateInner<'d, T>,
|
||||
}
|
||||
|
||||
unsafe impl<'d, T: Instance> Send for RxStateInner<'d, T> {}
|
||||
unsafe impl<'d, T: Instance> Sync for RxStateInner<'d, T> {}
|
||||
|
||||
unsafe impl<'d, T: Instance> Send for TxStateInner<'d, T> {}
|
||||
unsafe impl<'d, T: Instance> Sync for TxStateInner<'d, T> {}
|
||||
|
||||
unsafe impl<'d, T: Instance> Send for FullStateInner<'d, T> {}
|
||||
unsafe impl<'d, T: Instance> Sync for FullStateInner<'d, T> {}
|
||||
|
||||
pub struct BufferedUart<'d, T: Instance> {
|
||||
inner: PeripheralMutex<'d, FullStateInner<'d, T>>,
|
||||
}
|
||||
|
||||
pub struct BufferedUartRx<'d, T: Instance> {
|
||||
inner: PeripheralMutex<'d, RxStateInner<'d, T>>,
|
||||
}
|
||||
|
||||
pub struct BufferedUartTx<'d, T: Instance> {
|
||||
inner: PeripheralMutex<'d, TxStateInner<'d, T>>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Unpin for BufferedUart<'d, T> {}
|
||||
impl<'d, T: Instance> Unpin for BufferedUartRx<'d, T> {}
|
||||
impl<'d, T: Instance> Unpin for BufferedUartTx<'d, T> {}
|
||||
|
||||
impl<'d, T: Instance> BufferedUart<'d, T> {
|
||||
pub fn new<M: Mode>(
|
||||
state: &'d mut State<'d, T>,
|
||||
_uart: Uart<'d, T, M>,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
tx_buffer: &'d mut [u8],
|
||||
rx_buffer: &'d mut [u8],
|
||||
) -> BufferedUart<'d, T> {
|
||||
into_ref!(irq);
|
||||
|
||||
let r = T::regs();
|
||||
unsafe {
|
||||
r.uartimsc().modify(|w| {
|
||||
w.set_rxim(true);
|
||||
w.set_rtim(true);
|
||||
w.set_txim(true);
|
||||
});
|
||||
}
|
||||
|
||||
Self {
|
||||
inner: PeripheralMutex::new(irq, &mut state.0, move || FullStateInner {
|
||||
tx: TxStateInner {
|
||||
phantom: PhantomData,
|
||||
waker: WakerRegistration::new(),
|
||||
buf: RingBuffer::new(tx_buffer),
|
||||
},
|
||||
rx: RxStateInner {
|
||||
phantom: PhantomData,
|
||||
waker: WakerRegistration::new(),
|
||||
buf: RingBuffer::new(rx_buffer),
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> BufferedUartRx<'d, T> {
|
||||
pub fn new<M: Mode>(
|
||||
state: &'d mut RxState<'d, T>,
|
||||
_uart: UartRx<'d, T, M>,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
rx_buffer: &'d mut [u8],
|
||||
) -> BufferedUartRx<'d, T> {
|
||||
into_ref!(irq);
|
||||
|
||||
let r = T::regs();
|
||||
unsafe {
|
||||
r.uartimsc().modify(|w| {
|
||||
w.set_rxim(true);
|
||||
w.set_rtim(true);
|
||||
});
|
||||
}
|
||||
|
||||
Self {
|
||||
inner: PeripheralMutex::new(irq, &mut state.0, move || RxStateInner {
|
||||
phantom: PhantomData,
|
||||
|
||||
buf: RingBuffer::new(rx_buffer),
|
||||
waker: WakerRegistration::new(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> BufferedUartTx<'d, T> {
|
||||
pub fn new<M: Mode>(
|
||||
state: &'d mut TxState<'d, T>,
|
||||
_uart: UartTx<'d, T, M>,
|
||||
irq: impl Peripheral<P = T::Interrupt> + 'd,
|
||||
tx_buffer: &'d mut [u8],
|
||||
) -> BufferedUartTx<'d, T> {
|
||||
into_ref!(irq);
|
||||
|
||||
let r = T::regs();
|
||||
unsafe {
|
||||
r.uartimsc().modify(|w| {
|
||||
w.set_txim(true);
|
||||
});
|
||||
}
|
||||
|
||||
Self {
|
||||
inner: PeripheralMutex::new(irq, &mut state.0, move || TxStateInner {
|
||||
phantom: PhantomData,
|
||||
|
||||
buf: RingBuffer::new(tx_buffer),
|
||||
waker: WakerRegistration::new(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> PeripheralState for FullStateInner<'d, T>
|
||||
where
|
||||
Self: 'd,
|
||||
{
|
||||
type Interrupt = T::Interrupt;
|
||||
fn on_interrupt(&mut self) {
|
||||
self.rx.on_interrupt();
|
||||
self.tx.on_interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> RxStateInner<'d, T>
|
||||
where
|
||||
Self: 'd,
|
||||
{
|
||||
fn read(&mut self, buf: &mut [u8], waker: &Waker) -> (Poll<Result<usize, Error>>, bool) {
|
||||
// We have data ready in buffer? Return it.
|
||||
let mut do_pend = false;
|
||||
let data = self.buf.pop_buf();
|
||||
if !data.is_empty() {
|
||||
let len = data.len().min(buf.len());
|
||||
buf[..len].copy_from_slice(&data[..len]);
|
||||
|
||||
if self.buf.is_full() {
|
||||
do_pend = true;
|
||||
}
|
||||
self.buf.pop(len);
|
||||
|
||||
return (Poll::Ready(Ok(len)), do_pend);
|
||||
}
|
||||
|
||||
self.waker.register(waker);
|
||||
(Poll::Pending, do_pend)
|
||||
}
|
||||
|
||||
fn fill_buf<'a>(&mut self, waker: &Waker) -> Poll<Result<&'a [u8], Error>> {
|
||||
// We have data ready in buffer? Return it.
|
||||
let buf = self.buf.pop_buf();
|
||||
if !buf.is_empty() {
|
||||
let buf: &[u8] = buf;
|
||||
// Safety: buffer lives as long as uart
|
||||
let buf: &[u8] = unsafe { core::mem::transmute(buf) };
|
||||
return Poll::Ready(Ok(buf));
|
||||
}
|
||||
|
||||
self.waker.register(waker);
|
||||
Poll::Pending
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) -> bool {
|
||||
let full = self.buf.is_full();
|
||||
self.buf.pop(amt);
|
||||
full
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> PeripheralState for RxStateInner<'d, T>
|
||||
where
|
||||
Self: 'd,
|
||||
{
|
||||
type Interrupt = T::Interrupt;
|
||||
fn on_interrupt(&mut self) {
|
||||
let r = T::regs();
|
||||
unsafe {
|
||||
let ris = r.uartris().read();
|
||||
// Clear interrupt flags
|
||||
r.uarticr().modify(|w| {
|
||||
w.set_rxic(true);
|
||||
w.set_rtic(true);
|
||||
});
|
||||
|
||||
if ris.peris() {
|
||||
warn!("Parity error");
|
||||
r.uarticr().modify(|w| {
|
||||
w.set_peic(true);
|
||||
});
|
||||
}
|
||||
if ris.feris() {
|
||||
warn!("Framing error");
|
||||
r.uarticr().modify(|w| {
|
||||
w.set_feic(true);
|
||||
});
|
||||
}
|
||||
if ris.beris() {
|
||||
warn!("Break error");
|
||||
r.uarticr().modify(|w| {
|
||||
w.set_beic(true);
|
||||
});
|
||||
}
|
||||
if ris.oeris() {
|
||||
warn!("Overrun error");
|
||||
r.uarticr().modify(|w| {
|
||||
w.set_oeic(true);
|
||||
});
|
||||
}
|
||||
|
||||
if !r.uartfr().read().rxfe() {
|
||||
let buf = self.buf.push_buf();
|
||||
if !buf.is_empty() {
|
||||
buf[0] = r.uartdr().read().data();
|
||||
self.buf.push(1);
|
||||
} else {
|
||||
warn!("RX buffer full, discard received byte");
|
||||
}
|
||||
|
||||
if self.buf.is_full() {
|
||||
self.waker.wake();
|
||||
}
|
||||
}
|
||||
|
||||
if ris.rtris() {
|
||||
self.waker.wake();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> TxStateInner<'d, T>
|
||||
where
|
||||
Self: 'd,
|
||||
{
|
||||
fn write(&mut self, buf: &[u8], waker: &Waker) -> (Poll<Result<usize, Error>>, bool) {
|
||||
let empty = self.buf.is_empty();
|
||||
let tx_buf = self.buf.push_buf();
|
||||
if tx_buf.is_empty() {
|
||||
self.waker.register(waker);
|
||||
return (Poll::Pending, empty);
|
||||
}
|
||||
|
||||
let n = core::cmp::min(tx_buf.len(), buf.len());
|
||||
tx_buf[..n].copy_from_slice(&buf[..n]);
|
||||
self.buf.push(n);
|
||||
|
||||
(Poll::Ready(Ok(n)), empty)
|
||||
}
|
||||
|
||||
fn flush(&mut self, waker: &Waker) -> Poll<Result<(), Error>> {
|
||||
if !self.buf.is_empty() {
|
||||
self.waker.register(waker);
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> PeripheralState for TxStateInner<'d, T>
|
||||
where
|
||||
Self: 'd,
|
||||
{
|
||||
type Interrupt = T::Interrupt;
|
||||
fn on_interrupt(&mut self) {
|
||||
let r = T::regs();
|
||||
unsafe {
|
||||
let buf = self.buf.pop_buf();
|
||||
if !buf.is_empty() {
|
||||
r.uartimsc().modify(|w| {
|
||||
w.set_txim(true);
|
||||
});
|
||||
r.uartdr().write(|w| w.set_data(buf[0].into()));
|
||||
self.buf.pop(1);
|
||||
self.waker.wake();
|
||||
} else {
|
||||
// Disable interrupt until we have something to transmit again
|
||||
r.uartimsc().modify(|w| {
|
||||
w.set_txim(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_io::Error for Error {
|
||||
fn kind(&self) -> embedded_io::ErrorKind {
|
||||
embedded_io::ErrorKind::Other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_io::Io for BufferedUart<'d, T> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_io::Io for BufferedUartRx<'d, T> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> embedded_io::Io for BufferedUartTx<'d, T> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUart<'d, T> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
poll_fn(move |cx| {
|
||||
let (res, do_pend) = self.inner.with(|state| {
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
state.rx.read(buf, cx.waker())
|
||||
});
|
||||
|
||||
if do_pend {
|
||||
self.inner.pend();
|
||||
}
|
||||
|
||||
res
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + 'd> embedded_io::asynch::Read for BufferedUartRx<'d, T> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
poll_fn(move |cx| {
|
||||
let (res, do_pend) = self.inner.with(|state| {
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
state.read(buf, cx.waker())
|
||||
});
|
||||
|
||||
if do_pend {
|
||||
self.inner.pend();
|
||||
}
|
||||
|
||||
res
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUart<'d, T> {
|
||||
type FillBufFuture<'a> = impl Future<Output = Result<&'a [u8], Self::Error>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> {
|
||||
poll_fn(move |cx| {
|
||||
self.inner.with(|state| {
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
state.rx.fill_buf(cx.waker())
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
let signal = self.inner.with(|state| state.rx.consume(amt));
|
||||
if signal {
|
||||
self.inner.pend();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + 'd> embedded_io::asynch::BufRead for BufferedUartRx<'d, T> {
|
||||
type FillBufFuture<'a> = impl Future<Output = Result<&'a [u8], Self::Error>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn fill_buf<'a>(&'a mut self) -> Self::FillBufFuture<'a> {
|
||||
poll_fn(move |cx| {
|
||||
self.inner.with(|state| {
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
state.fill_buf(cx.waker())
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
let signal = self.inner.with(|state| state.consume(amt));
|
||||
if signal {
|
||||
self.inner.pend();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUart<'d, T> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
poll_fn(move |cx| {
|
||||
let (poll, empty) = self.inner.with(|state| state.tx.write(buf, cx.waker()));
|
||||
if empty {
|
||||
self.inner.pend();
|
||||
}
|
||||
poll
|
||||
})
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
poll_fn(move |cx| self.inner.with(|state| state.tx.flush(cx.waker())))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance + 'd> embedded_io::asynch::Write for BufferedUartTx<'d, T> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
poll_fn(move |cx| {
|
||||
let (poll, empty) = self.inner.with(|state| state.write(buf, cx.waker()));
|
||||
if empty {
|
||||
self.inner.pend();
|
||||
}
|
||||
poll
|
||||
})
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
poll_fn(move |cx| self.inner.with(|state| state.flush(cx.waker())))
|
||||
}
|
||||
}
|
@ -346,6 +346,11 @@ impl<'d, T: Instance, M: Mode> Uart<'d, T, M> {
|
||||
w.set_fen(true);
|
||||
});
|
||||
|
||||
r.uartifls().write(|w| {
|
||||
w.set_rxiflsel(0b000);
|
||||
w.set_txiflsel(0b000);
|
||||
});
|
||||
|
||||
r.uartcr().write(|w| {
|
||||
w.set_uarten(true);
|
||||
w.set_rxe(true);
|
||||
@ -423,9 +428,11 @@ mod eh02 {
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for UartTx<'d, T, M> {
|
||||
type Error = Error;
|
||||
|
||||
fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(buffer)
|
||||
}
|
||||
|
||||
fn bflush(&mut self) -> Result<(), Self::Error> {
|
||||
self.blocking_flush()
|
||||
}
|
||||
@ -433,6 +440,7 @@ mod eh02 {
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Read<u8> for Uart<'d, T, M> {
|
||||
type Error = Error;
|
||||
|
||||
fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
|
||||
embedded_hal_02::serial::Read::read(&mut self.rx)
|
||||
}
|
||||
@ -440,9 +448,11 @@ mod eh02 {
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::serial::Write<u8> for Uart<'d, T, M> {
|
||||
type Error = Error;
|
||||
|
||||
fn bwrite_all(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(buffer)
|
||||
}
|
||||
|
||||
fn bflush(&mut self) -> Result<(), Self::Error> {
|
||||
self.blocking_flush()
|
||||
}
|
||||
@ -475,62 +485,137 @@ mod eh1 {
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::ErrorType for UartRx<'d, T, M> {
|
||||
type Error = Error;
|
||||
}
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(feature = "unstable-traits", feature = "nightly", feature = "_todo_embedded_hal_serial"))] {
|
||||
use core::future::Future;
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Read for UartRx<'d, T, M> {
|
||||
fn read(&mut self) -> nb::Result<u8, Self::Error> {
|
||||
let r = T::regs();
|
||||
unsafe {
|
||||
let dr = r.uartdr().read();
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Write for UartTx<'d, T, M>
|
||||
{
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.write(buf)
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
async move { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Read for UartRx<'d, T, M>
|
||||
{
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Write for Uart<'d, T, M>
|
||||
{
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.write(buf)
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
async move { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Read for Uart<'d, T, M>
|
||||
{
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.read(buf)
|
||||
if dr.oe() {
|
||||
Err(nb::Error::Other(Error::Overrun))
|
||||
} else if dr.be() {
|
||||
Err(nb::Error::Other(Error::Break))
|
||||
} else if dr.pe() {
|
||||
Err(nb::Error::Other(Error::Parity))
|
||||
} else if dr.fe() {
|
||||
Err(nb::Error::Other(Error::Framing))
|
||||
} else if dr.fe() {
|
||||
Ok(dr.data())
|
||||
} else {
|
||||
Err(nb::Error::WouldBlock)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::Write for UartTx<'d, T, M> {
|
||||
fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(buffer)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
self.blocking_flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, T, M> {
|
||||
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
|
||||
self.blocking_write(&[char]).map_err(nb::Error::Other)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> nb::Result<(), Self::Error> {
|
||||
self.blocking_flush().map_err(nb::Error::Other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Read for Uart<'d, T, M> {
|
||||
fn read(&mut self) -> Result<u8, nb::Error<Self::Error>> {
|
||||
embedded_hal_02::serial::Read::read(&mut self.rx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_1::serial::Write for Uart<'d, T, M> {
|
||||
fn write(&mut self, buffer: &[u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_write(buffer)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
self.blocking_flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for Uart<'d, T, M> {
|
||||
fn write(&mut self, char: u8) -> nb::Result<(), Self::Error> {
|
||||
self.blocking_write(&[char]).map_err(nb::Error::Other)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> nb::Result<(), Self::Error> {
|
||||
self.blocking_flush().map_err(nb::Error::Other)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
feature = "unstable-traits",
|
||||
feature = "nightly",
|
||||
feature = "_todo_embedded_hal_serial"
|
||||
))]
|
||||
mod eha {
|
||||
use core::future::Future;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Write for UartTx<'d, T, M> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.write(buf)
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
async move { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Read for UartRx<'d, T, M> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Write for Uart<'d, T, M> {
|
||||
type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||
self.write(buf)
|
||||
}
|
||||
|
||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||
async move { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, M: Mode> embedded_hal_async::serial::Read for Uart<'d, T, M> {
|
||||
type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||
self.read(buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
mod buffered;
|
||||
#[cfg(feature = "nightly")]
|
||||
pub use buffered::*;
|
||||
|
||||
mod sealed {
|
||||
use super::*;
|
||||
|
||||
@ -540,6 +625,8 @@ mod sealed {
|
||||
const TX_DREQ: u8;
|
||||
const RX_DREQ: u8;
|
||||
|
||||
type Interrupt: crate::interrupt::Interrupt;
|
||||
|
||||
fn regs() -> pac::uart::Uart;
|
||||
}
|
||||
pub trait TxPin<T: Instance> {}
|
||||
@ -571,6 +658,8 @@ macro_rules! impl_instance {
|
||||
const TX_DREQ: u8 = $tx_dreq;
|
||||
const RX_DREQ: u8 = $rx_dreq;
|
||||
|
||||
type Interrupt = crate::interrupt::$irq;
|
||||
|
||||
fn regs() -> pac::uart::Uart {
|
||||
pac::$inst
|
||||
}
|
||||
@ -579,8 +668,8 @@ macro_rules! impl_instance {
|
||||
};
|
||||
}
|
||||
|
||||
impl_instance!(UART0, UART0, 20, 21);
|
||||
impl_instance!(UART1, UART1, 22, 23);
|
||||
impl_instance!(UART0, UART0_IRQ, 20, 21);
|
||||
impl_instance!(UART1, UART1_IRQ, 22, 23);
|
||||
|
||||
pub trait TxPin<T: Instance>: sealed::TxPin<T> + crate::gpio::Pin {}
|
||||
pub trait RxPin<T: Instance>: sealed::RxPin<T> + crate::gpio::Pin {}
|
@ -1,3 +1,4 @@
|
||||
use core::future::{poll_fn, Future};
|
||||
use core::marker::PhantomData;
|
||||
use core::slice;
|
||||
use core::sync::atomic::Ordering;
|
||||
@ -6,10 +7,10 @@ use core::task::Poll;
|
||||
use atomic_polyfill::compiler_fence;
|
||||
use embassy_hal_common::into_ref;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use embassy_usb::driver::{self, EndpointAllocError, EndpointError, Event, Unsupported};
|
||||
use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection};
|
||||
use futures::future::poll_fn;
|
||||
use futures::Future;
|
||||
use embassy_usb_driver as driver;
|
||||
use embassy_usb_driver::{
|
||||
Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported,
|
||||
};
|
||||
|
||||
use crate::interrupt::{Interrupt, InterruptExt};
|
||||
use crate::{pac, peripherals, Peripheral, RegExt};
|
||||
@ -205,8 +206,8 @@ impl<'d, T: Instance> Driver<'d, T> {
|
||||
);
|
||||
|
||||
let alloc = match D::dir() {
|
||||
UsbDirection::Out => &mut self.ep_out,
|
||||
UsbDirection::In => &mut self.ep_in,
|
||||
Direction::Out => &mut self.ep_out,
|
||||
Direction::In => &mut self.ep_in,
|
||||
};
|
||||
|
||||
let index = alloc.iter_mut().enumerate().find(|(i, ep)| {
|
||||
@ -255,7 +256,7 @@ impl<'d, T: Instance> Driver<'d, T> {
|
||||
};
|
||||
|
||||
match D::dir() {
|
||||
UsbDirection::Out => unsafe {
|
||||
Direction::Out => unsafe {
|
||||
T::dpram().ep_out_control(index - 1).write(|w| {
|
||||
w.set_enable(false);
|
||||
w.set_buffer_address(addr);
|
||||
@ -263,7 +264,7 @@ impl<'d, T: Instance> Driver<'d, T> {
|
||||
w.set_endpoint_type(ep_type_reg);
|
||||
})
|
||||
},
|
||||
UsbDirection::In => unsafe {
|
||||
Direction::In => unsafe {
|
||||
T::dpram().ep_in_control(index - 1).write(|w| {
|
||||
w.set_enable(false);
|
||||
w.set_buffer_address(addr);
|
||||
@ -430,14 +431,14 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
||||
|
||||
let n = ep_addr.index();
|
||||
match ep_addr.direction() {
|
||||
UsbDirection::In => unsafe {
|
||||
Direction::In => unsafe {
|
||||
T::dpram().ep_in_control(n - 1).modify(|w| w.set_enable(enabled));
|
||||
T::dpram().ep_in_buffer_control(ep_addr.index()).write(|w| {
|
||||
w.set_pid(0, true); // first packet is DATA0, but PID is flipped before
|
||||
});
|
||||
EP_IN_WAKERS[n].wake();
|
||||
},
|
||||
UsbDirection::Out => unsafe {
|
||||
Direction::Out => unsafe {
|
||||
T::dpram().ep_out_control(n - 1).modify(|w| w.set_enable(enabled));
|
||||
|
||||
T::dpram().ep_out_buffer_control(ep_addr.index()).write(|w| {
|
||||
@ -475,14 +476,14 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
||||
}
|
||||
|
||||
trait Dir {
|
||||
fn dir() -> UsbDirection;
|
||||
fn dir() -> Direction;
|
||||
fn waker(i: usize) -> &'static AtomicWaker;
|
||||
}
|
||||
|
||||
pub enum In {}
|
||||
impl Dir for In {
|
||||
fn dir() -> UsbDirection {
|
||||
UsbDirection::In
|
||||
fn dir() -> Direction {
|
||||
Direction::In
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -493,8 +494,8 @@ impl Dir for In {
|
||||
|
||||
pub enum Out {}
|
||||
impl Dir for Out {
|
||||
fn dir() -> UsbDirection {
|
||||
UsbDirection::Out
|
||||
fn dir() -> Direction {
|
||||
Direction::Out
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -521,7 +522,7 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> {
|
||||
trace!("wait_enabled IN WAITING");
|
||||
let index = self.info.addr.index();
|
||||
poll_fn(|cx| {
|
||||
EP_OUT_WAKERS[index].register(cx.waker());
|
||||
EP_IN_WAKERS[index].register(cx.waker());
|
||||
let val = unsafe { T::dpram().ep_in_control(self.info.addr.index() - 1).read() };
|
||||
if val.enable() {
|
||||
Poll::Ready(())
|
||||
@ -810,8 +811,8 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
|
||||
async move {
|
||||
trace!("control: accept");
|
||||
|
||||
let bufcontrol = T::dpram().ep_in_buffer_control(0);
|
||||
unsafe {
|
||||
let bufcontrol = T::dpram().ep_in_buffer_control(0);
|
||||
bufcontrol.write(|w| {
|
||||
w.set_length(0, 0);
|
||||
w.set_pid(0, true);
|
||||
@ -825,6 +826,18 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
|
||||
w.set_available(0, true);
|
||||
});
|
||||
}
|
||||
|
||||
// wait for completion before returning, needed so
|
||||
// set_address() doesn't happen early.
|
||||
poll_fn(|cx| {
|
||||
EP_IN_WAKERS[0].register(cx.waker());
|
||||
if unsafe { bufcontrol.read().available(0) } {
|
||||
Poll::Pending
|
||||
} else {
|
||||
Poll::Ready(())
|
||||
}
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
name = "embassy-stm32"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-stm32-v$VERSION/embassy-stm32/src/"
|
||||
@ -10,7 +11,7 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-stm32
|
||||
# TODO: sdmmc
|
||||
# TODO: net
|
||||
# TODO: subghz
|
||||
features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any", "embassy-time/tick-32768hz"]
|
||||
features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "exti", "time-driver-any"]
|
||||
flavors = [
|
||||
{ regex_feature = "stm32f0.*", target = "thumbv6m-none-eabi" },
|
||||
{ regex_feature = "stm32f1.*", target = "thumbv7m-none-eabi" },
|
||||
@ -34,15 +35,17 @@ flavors = [
|
||||
embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
|
||||
embassy-executor = { version = "0.1.0", path = "../embassy-executor" }
|
||||
embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true }
|
||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||
embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-4"]}
|
||||
embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" }
|
||||
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
|
||||
embassy-net = { version = "0.1.0", path = "../embassy-net", optional = true }
|
||||
embassy-usb = {version = "0.1.0", path = "../embassy-usb", optional = true }
|
||||
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true }
|
||||
|
||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true}
|
||||
embedded-hal-async = { version = "0.1.0-alpha.1", optional = true}
|
||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true}
|
||||
embedded-hal-async = { version = "=0.1.0-alpha.3", optional = true}
|
||||
embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true}
|
||||
|
||||
embedded-storage = "0.3.0"
|
||||
embedded-storage-async = { version = "0.3.0", optional = true }
|
||||
@ -64,7 +67,7 @@ nb = "1.0.0"
|
||||
stm32-fmc = "0.2.4"
|
||||
seq-macro = "0.3.0"
|
||||
cfg-if = "1.0.0"
|
||||
embedded-io = { version = "0.3.0", features = ["async"], optional = true }
|
||||
embedded-io = { version = "0.3.1", features = ["async"], optional = true }
|
||||
|
||||
[build-dependencies]
|
||||
proc-macro2 = "1.0.36"
|
||||
@ -72,16 +75,19 @@ quote = "1.0.15"
|
||||
stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]}
|
||||
|
||||
[features]
|
||||
defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embedded-io?/defmt", "embassy-usb?/defmt"]
|
||||
defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt"]
|
||||
sdmmc-rs = ["embedded-sdmmc"]
|
||||
net = ["embassy-net" ]
|
||||
memory-x = ["stm32-metapac/memory-x"]
|
||||
subghz = []
|
||||
exti = []
|
||||
|
||||
# Enables additional driver features that depend on embassy-time
|
||||
time = ["dep:embassy-time"]
|
||||
|
||||
# Features starting with `_` are for internal use only. They're not intended
|
||||
# to be enabled by other crates, and are not covered by semver guarantees.
|
||||
_time-driver = ["dep:embassy-time"]
|
||||
_time-driver = ["time"]
|
||||
time-driver-any = ["_time-driver"]
|
||||
time-driver-tim2 = ["_time-driver"]
|
||||
time-driver-tim3 = ["_time-driver"]
|
||||
@ -91,7 +97,7 @@ time-driver-tim12 = ["_time-driver"]
|
||||
time-driver-tim15 = ["_time-driver"]
|
||||
|
||||
# Enable nightly-only features
|
||||
nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io", "dep:embassy-usb", "embassy-embedded-hal/nightly"]
|
||||
nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io", "dep:embassy-usb-driver", "embassy-embedded-hal/nightly"]
|
||||
|
||||
# Reexport stm32-metapac at `embassy_stm32::pac`.
|
||||
# This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version.
|
||||
@ -101,7 +107,7 @@ unstable-pac = []
|
||||
|
||||
# Implement embedded-hal 1.0 alpha traits.
|
||||
# Implement embedded-hal-async traits if `nightly` is set as well.
|
||||
unstable-traits = ["embedded-hal-1"]
|
||||
unstable-traits = ["embedded-hal-1", "dep:embedded-hal-nb"]
|
||||
|
||||
# BEGIN GENERATED FEATURES
|
||||
# Generated by stm32-gen-features. DO NOT EDIT.
|
||||
|
@ -86,7 +86,6 @@ pub use sample_time::SampleTime;
|
||||
|
||||
pub struct Adc<'d, T: Instance> {
|
||||
sample_time: SampleTime,
|
||||
calibrated_vdda: u32,
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
}
|
||||
|
||||
@ -122,7 +121,6 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
|
||||
Self {
|
||||
sample_time: Default::default(),
|
||||
calibrated_vdda: VDDA_CALIB_MV,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
@ -162,29 +160,10 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
Temperature {}
|
||||
}
|
||||
|
||||
/// Calculates the system VDDA by sampling the internal VREF channel and comparing
|
||||
/// to the expected value. If the chip's VDDA is not stable, run this before each ADC
|
||||
/// conversion.
|
||||
pub fn calibrate(&mut self, vref: &mut Vref) -> u32 {
|
||||
let old_sample_time = self.sample_time;
|
||||
self.sample_time = SampleTime::Cycles239_5;
|
||||
|
||||
let vref_samp = self.read(vref);
|
||||
self.sample_time = old_sample_time;
|
||||
|
||||
self.calibrated_vdda = (ADC_MAX * VREF_INT) / u32::from(vref_samp);
|
||||
self.calibrated_vdda
|
||||
}
|
||||
|
||||
pub fn set_sample_time(&mut self, sample_time: SampleTime) {
|
||||
self.sample_time = sample_time;
|
||||
}
|
||||
|
||||
/// Convert a measurement to millivolts
|
||||
pub fn to_millivolts(&self, sample: u16) -> u16 {
|
||||
((u32::from(sample) * self.calibrated_vdda) / ADC_MAX) as u16
|
||||
}
|
||||
|
||||
/// Perform a single conversion.
|
||||
fn convert(&mut self) -> u16 {
|
||||
unsafe {
|
||||
|
@ -28,15 +28,20 @@ pub(crate) mod sealed {
|
||||
pub trait AdcPin<T: Instance> {
|
||||
fn channel(&self) -> u8;
|
||||
}
|
||||
|
||||
pub trait InternalChannel<T> {
|
||||
fn channel(&self) -> u8;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(adc_f1))]
|
||||
#[cfg(not(any(adc_f1, adc_v2)))]
|
||||
pub trait Instance: sealed::Instance + 'static {}
|
||||
#[cfg(adc_f1)]
|
||||
#[cfg(any(adc_f1, adc_v2))]
|
||||
pub trait Instance: sealed::Instance + crate::rcc::RccPeripheral + 'static {}
|
||||
#[cfg(all(not(adc_f1), not(adc_v1)))]
|
||||
pub trait Common: sealed::Common + 'static {}
|
||||
pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {}
|
||||
pub trait InternalChannel<T>: sealed::InternalChannel<T> {}
|
||||
|
||||
#[cfg(not(stm32h7))]
|
||||
foreach_peripheral!(
|
||||
|
@ -3,7 +3,9 @@ use core::marker::PhantomData;
|
||||
use embassy_hal_common::into_ref;
|
||||
use embedded_hal_02::blocking::delay::DelayUs;
|
||||
|
||||
use super::InternalChannel;
|
||||
use crate::adc::{AdcPin, Instance};
|
||||
use crate::peripherals::ADC1;
|
||||
use crate::time::Hertz;
|
||||
use crate::Peripheral;
|
||||
|
||||
@ -12,20 +14,8 @@ pub const VREF_DEFAULT_MV: u32 = 3300;
|
||||
/// VREF voltage used for factory calibration of VREFINTCAL register.
|
||||
pub const VREF_CALIB_MV: u32 = 3300;
|
||||
|
||||
#[cfg(not(any(rcc_f4, rcc_f7)))]
|
||||
fn enable() {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[cfg(any(rcc_f4, rcc_f7))]
|
||||
fn enable() {
|
||||
critical_section::with(|_| unsafe {
|
||||
// TODO do not enable all adc clocks if not needed
|
||||
crate::pac::RCC.apb2enr().modify(|w| w.set_adc1en(true));
|
||||
crate::pac::RCC.apb2enr().modify(|w| w.set_adc2en(true));
|
||||
crate::pac::RCC.apb2enr().modify(|w| w.set_adc3en(true));
|
||||
});
|
||||
}
|
||||
/// ADC turn-on time
|
||||
pub const ADC_POWERUP_TIME_US: u32 = 3;
|
||||
|
||||
pub enum Resolution {
|
||||
TwelveBit,
|
||||
@ -61,24 +51,44 @@ impl Resolution {
|
||||
}
|
||||
|
||||
pub struct VrefInt;
|
||||
impl<T: Instance> AdcPin<T> for VrefInt {}
|
||||
impl<T: Instance> super::sealed::AdcPin<T> for VrefInt {
|
||||
impl InternalChannel<ADC1> for VrefInt {}
|
||||
impl super::sealed::InternalChannel<ADC1> for VrefInt {
|
||||
fn channel(&self) -> u8 {
|
||||
17
|
||||
}
|
||||
}
|
||||
|
||||
impl VrefInt {
|
||||
/// Time needed for internal voltage reference to stabilize
|
||||
pub fn start_time_us() -> u32 {
|
||||
10
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Temperature;
|
||||
impl<T: Instance> AdcPin<T> for Temperature {}
|
||||
impl<T: Instance> super::sealed::AdcPin<T> for Temperature {
|
||||
impl InternalChannel<ADC1> for Temperature {}
|
||||
impl super::sealed::InternalChannel<ADC1> for Temperature {
|
||||
fn channel(&self) -> u8 {
|
||||
16
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(stm32f40, stm32f41))] {
|
||||
16
|
||||
} else {
|
||||
18
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Temperature {
|
||||
/// Time needed for temperature sensor readings to stabilize
|
||||
pub fn start_time_us() -> u32 {
|
||||
10
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Vbat;
|
||||
impl<T: Instance> AdcPin<T> for Vbat {}
|
||||
impl<T: Instance> super::sealed::AdcPin<T> for Vbat {
|
||||
impl InternalChannel<ADC1> for Vbat {}
|
||||
impl super::sealed::InternalChannel<ADC1> for Vbat {
|
||||
fn channel(&self) -> u8 {
|
||||
18
|
||||
}
|
||||
@ -153,7 +163,6 @@ impl Prescaler {
|
||||
|
||||
pub struct Adc<'d, T: Instance> {
|
||||
sample_time: SampleTime,
|
||||
vref_mv: u32,
|
||||
resolution: Resolution,
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
}
|
||||
@ -164,26 +173,23 @@ where
|
||||
{
|
||||
pub fn new(_peri: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
|
||||
into_ref!(_peri);
|
||||
enable();
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
let presc = unsafe { Prescaler::from_pclk2(crate::rcc::get_freqs().apb2) };
|
||||
let presc = Prescaler::from_pclk2(T::frequency());
|
||||
unsafe {
|
||||
T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre()));
|
||||
}
|
||||
|
||||
unsafe {
|
||||
// disable before config is set
|
||||
T::regs().cr2().modify(|reg| {
|
||||
reg.set_adon(crate::pac::adc::vals::Adon::DISABLED);
|
||||
reg.set_adon(crate::pac::adc::vals::Adon::ENABLED);
|
||||
});
|
||||
}
|
||||
|
||||
delay.delay_us(20); // TODO?
|
||||
delay.delay_us(ADC_POWERUP_TIME_US);
|
||||
|
||||
Self {
|
||||
sample_time: Default::default(),
|
||||
resolution: Resolution::default(),
|
||||
vref_mv: VREF_DEFAULT_MV,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
@ -196,16 +202,43 @@ where
|
||||
self.resolution = resolution;
|
||||
}
|
||||
|
||||
/// Set VREF value in millivolts. This value is used for [to_millivolts()] sample conversion.
|
||||
///
|
||||
/// Use this if you have a known precise VREF (VDDA) pin reference voltage.
|
||||
pub fn set_vref_mv(&mut self, vref_mv: u32) {
|
||||
self.vref_mv = vref_mv;
|
||||
/// Enables internal voltage reference and returns [VrefInt], which can be used in
|
||||
/// [Adc::read_internal()] to perform conversion.
|
||||
pub fn enable_vrefint(&self) -> VrefInt {
|
||||
unsafe {
|
||||
T::common_regs().ccr().modify(|reg| {
|
||||
reg.set_tsvrefe(crate::pac::adccommon::vals::Tsvrefe::ENABLED);
|
||||
});
|
||||
}
|
||||
|
||||
VrefInt {}
|
||||
}
|
||||
|
||||
/// Convert a measurement to millivolts
|
||||
pub fn to_millivolts(&self, sample: u16) -> u16 {
|
||||
((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16
|
||||
/// Enables internal temperature sensor and returns [Temperature], which can be used in
|
||||
/// [Adc::read_internal()] to perform conversion.
|
||||
///
|
||||
/// On STM32F42 and STM32F43 this can not be used together with [Vbat]. If both are enabled,
|
||||
/// temperature sensor will return vbat value.
|
||||
pub fn enable_temperature(&self) -> Temperature {
|
||||
unsafe {
|
||||
T::common_regs().ccr().modify(|reg| {
|
||||
reg.set_tsvrefe(crate::pac::adccommon::vals::Tsvrefe::ENABLED);
|
||||
});
|
||||
}
|
||||
|
||||
Temperature {}
|
||||
}
|
||||
|
||||
/// Enables vbat input and returns [Vbat], which can be used in
|
||||
/// [Adc::read_internal()] to perform conversion.
|
||||
pub fn enable_vbat(&self) -> Vbat {
|
||||
unsafe {
|
||||
T::common_regs().ccr().modify(|reg| {
|
||||
reg.set_vbate(crate::pac::adccommon::vals::Vbate::ENABLED);
|
||||
});
|
||||
}
|
||||
|
||||
Vbat {}
|
||||
}
|
||||
|
||||
/// Perform a single conversion.
|
||||
@ -238,44 +271,31 @@ where
|
||||
P: crate::gpio::sealed::Pin,
|
||||
{
|
||||
unsafe {
|
||||
// dissable ADC
|
||||
T::regs().cr2().modify(|reg| {
|
||||
reg.set_swstart(false);
|
||||
});
|
||||
T::regs().cr2().modify(|reg| {
|
||||
reg.set_adon(crate::pac::adc::vals::Adon::DISABLED);
|
||||
});
|
||||
|
||||
pin.set_as_analog();
|
||||
|
||||
// Configure ADC
|
||||
T::regs().cr1().modify(|reg| reg.set_res(self.resolution.res()));
|
||||
|
||||
// Select channel
|
||||
T::regs().sqr3().write(|reg| reg.set_sq(0, pin.channel()));
|
||||
|
||||
// Configure channel
|
||||
Self::set_channel_sample_time(pin.channel(), self.sample_time);
|
||||
|
||||
// enable adc
|
||||
T::regs().cr2().modify(|reg| {
|
||||
reg.set_adon(crate::pac::adc::vals::Adon::ENABLED);
|
||||
});
|
||||
|
||||
let val = self.convert();
|
||||
|
||||
// dissable ADC
|
||||
T::regs().cr2().modify(|reg| {
|
||||
reg.set_swstart(false);
|
||||
});
|
||||
T::regs().cr2().modify(|reg| {
|
||||
reg.set_adon(crate::pac::adc::vals::Adon::DISABLED);
|
||||
});
|
||||
|
||||
val
|
||||
self.read_channel(pin.channel())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_internal(&mut self, channel: &mut impl InternalChannel<T>) -> u16 {
|
||||
unsafe { self.read_channel(channel.channel()) }
|
||||
}
|
||||
|
||||
unsafe fn read_channel(&mut self, channel: u8) -> u16 {
|
||||
// Configure ADC
|
||||
T::regs().cr1().modify(|reg| reg.set_res(self.resolution.res()));
|
||||
|
||||
// Select channel
|
||||
T::regs().sqr3().write(|reg| reg.set_sq(0, channel));
|
||||
|
||||
// Configure channel
|
||||
Self::set_channel_sample_time(channel, self.sample_time);
|
||||
|
||||
let val = self.convert();
|
||||
|
||||
val
|
||||
}
|
||||
|
||||
unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
|
||||
if ch <= 9 {
|
||||
T::regs()
|
||||
@ -288,3 +308,9 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Drop for Adc<'d, T> {
|
||||
fn drop(&mut self) {
|
||||
T::disable();
|
||||
}
|
||||
}
|
||||
|
@ -205,7 +205,6 @@ pub use sample_time::SampleTime;
|
||||
|
||||
pub struct Adc<'d, T: Instance> {
|
||||
sample_time: SampleTime,
|
||||
vref_mv: u32,
|
||||
resolution: Resolution,
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
}
|
||||
@ -244,7 +243,6 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
Self {
|
||||
sample_time: Default::default(),
|
||||
resolution: Resolution::default(),
|
||||
vref_mv: VREF_DEFAULT_MV,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
@ -285,31 +283,6 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
Vbat {}
|
||||
}
|
||||
|
||||
/// Calculates the system VDDA by sampling the internal VREFINT channel and comparing
|
||||
/// the result with the value stored at the factory. If the chip's VDDA is not stable, run
|
||||
/// this before each ADC conversion.
|
||||
#[cfg(not(stm32g0))] // TODO is this supposed to be public?
|
||||
#[allow(unused)] // TODO is this supposed to be public?
|
||||
fn calibrate(&mut self, vrefint: &mut VrefInt) {
|
||||
#[cfg(stm32l5)]
|
||||
let vrefint_cal: u32 = todo!();
|
||||
#[cfg(not(stm32l5))]
|
||||
let vrefint_cal = unsafe { crate::pac::VREFINTCAL.data().read().value() };
|
||||
let old_sample_time = self.sample_time;
|
||||
|
||||
// "Table 24. Embedded internal voltage reference" states that the sample time needs to be
|
||||
// at a minimum 4 us. With 640.5 ADC cycles we have a minimum of 8 us at 80 MHz, leaving
|
||||
// some headroom.
|
||||
self.sample_time = SampleTime::Cycles640_5;
|
||||
|
||||
// This can't actually fail, it's just in a result to satisfy hal trait
|
||||
let vrefint_samp = self.read(vrefint);
|
||||
|
||||
self.sample_time = old_sample_time;
|
||||
|
||||
self.vref_mv = (VREF_CALIB_MV * u32::from(vrefint_cal)) / u32::from(vrefint_samp);
|
||||
}
|
||||
|
||||
pub fn set_sample_time(&mut self, sample_time: SampleTime) {
|
||||
self.sample_time = sample_time;
|
||||
}
|
||||
@ -318,18 +291,6 @@ impl<'d, T: Instance> Adc<'d, T> {
|
||||
self.resolution = resolution;
|
||||
}
|
||||
|
||||
/// Set VREF value in millivolts. This value is used for [to_millivolts()] sample conversion.
|
||||
///
|
||||
/// Use this if you have a known precise VREF (VDDA) pin reference voltage.
|
||||
pub fn set_vref_mv(&mut self, vref_mv: u32) {
|
||||
self.vref_mv = vref_mv;
|
||||
}
|
||||
|
||||
/// Convert a measurement to millivolts
|
||||
pub fn to_millivolts(&self, sample: u16) -> u16 {
|
||||
((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16
|
||||
}
|
||||
|
||||
/*
|
||||
/// Convert a raw sample from the `Temperature` to deg C
|
||||
pub fn to_degrees_centigrade(sample: u16) -> f32 {
|
||||
|
@ -5,7 +5,7 @@ use embedded_hal_02::blocking::delay::DelayUs;
|
||||
use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel};
|
||||
use pac::adccommon::vals::Presc;
|
||||
|
||||
use super::{AdcPin, Instance};
|
||||
use super::{AdcPin, Instance, InternalChannel};
|
||||
use crate::time::Hertz;
|
||||
use crate::{pac, Peripheral};
|
||||
|
||||
@ -50,18 +50,10 @@ impl Resolution {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait InternalChannel<T>: sealed::InternalChannel<T> {}
|
||||
|
||||
mod sealed {
|
||||
pub trait InternalChannel<T> {
|
||||
fn channel(&self) -> u8;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Vrefint/Temperature/Vbat are only available on ADC3 on H7, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs
|
||||
pub struct VrefInt;
|
||||
impl<T: Instance> InternalChannel<T> for VrefInt {}
|
||||
impl<T: Instance> sealed::InternalChannel<T> for VrefInt {
|
||||
impl<T: Instance> super::sealed::InternalChannel<T> for VrefInt {
|
||||
fn channel(&self) -> u8 {
|
||||
19
|
||||
}
|
||||
@ -69,7 +61,7 @@ impl<T: Instance> sealed::InternalChannel<T> for VrefInt {
|
||||
|
||||
pub struct Temperature;
|
||||
impl<T: Instance> InternalChannel<T> for Temperature {}
|
||||
impl<T: Instance> sealed::InternalChannel<T> for Temperature {
|
||||
impl<T: Instance> super::sealed::InternalChannel<T> for Temperature {
|
||||
fn channel(&self) -> u8 {
|
||||
18
|
||||
}
|
||||
@ -77,7 +69,7 @@ impl<T: Instance> sealed::InternalChannel<T> for Temperature {
|
||||
|
||||
pub struct Vbat;
|
||||
impl<T: Instance> InternalChannel<T> for Vbat {}
|
||||
impl<T: Instance> sealed::InternalChannel<T> for Vbat {
|
||||
impl<T: Instance> super::sealed::InternalChannel<T> for Vbat {
|
||||
fn channel(&self) -> u8 {
|
||||
// TODO this should be 14 for H7a/b/35
|
||||
17
|
||||
@ -322,7 +314,6 @@ impl Prescaler {
|
||||
|
||||
pub struct Adc<'d, T: Instance> {
|
||||
sample_time: SampleTime,
|
||||
vref_mv: u32,
|
||||
resolution: Resolution,
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
}
|
||||
@ -360,7 +351,6 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> {
|
||||
|
||||
let mut s = Self {
|
||||
sample_time: Default::default(),
|
||||
vref_mv: VREF_DEFAULT_MV,
|
||||
resolution: Resolution::default(),
|
||||
phantom: PhantomData,
|
||||
};
|
||||
@ -467,18 +457,6 @@ impl<'d, T: Instance + crate::rcc::RccPeripheral> Adc<'d, T> {
|
||||
self.resolution = resolution;
|
||||
}
|
||||
|
||||
/// Set VREF value in millivolts. This value is used for [to_millivolts()] sample conversion.
|
||||
///
|
||||
/// Use this if you have a known precise VREF (VDDA) pin reference voltage.
|
||||
pub fn set_vref_mv(&mut self, vref_mv: u32) {
|
||||
self.vref_mv = vref_mv;
|
||||
}
|
||||
|
||||
/// Convert a measurement to millivolts
|
||||
pub fn to_millivolts(&self, sample: u16) -> u16 {
|
||||
((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16
|
||||
}
|
||||
|
||||
/// Perform a single conversion.
|
||||
fn convert(&mut self) -> u16 {
|
||||
unsafe {
|
||||
|
@ -12,6 +12,7 @@ pub struct Can<'d, T: Instance> {
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Can<'d, T> {
|
||||
/// Creates a new Bxcan instance, blocking for 11 recessive bits to sync with the CAN bus.
|
||||
pub fn new(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
||||
@ -31,6 +32,28 @@ impl<'d, T: Instance> Can<'d, T> {
|
||||
can: bxcan::Can::builder(BxcanInstance(peri)).enable(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new Bxcan instance, keeping the peripheral in sleep mode.
|
||||
/// You must call [Can::enable_non_blocking] to use the peripheral.
|
||||
pub fn new_disabled(
|
||||
peri: impl Peripheral<P = T> + 'd,
|
||||
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
|
||||
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
|
||||
) -> Self {
|
||||
into_ref!(peri, rx, tx);
|
||||
|
||||
unsafe {
|
||||
rx.set_as_af(rx.af_num(), AFType::Input);
|
||||
tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
|
||||
}
|
||||
|
||||
T::enable();
|
||||
T::reset();
|
||||
|
||||
Self {
|
||||
can: bxcan::Can::builder(BxcanInstance(peri)).leave_disabled(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Drop for Can<'d, T> {
|
||||
|
@ -1,8 +1,8 @@
|
||||
use core::future::poll_fn;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use futures::future::poll_fn;
|
||||
|
||||
use crate::gpio::sealed::AFType;
|
||||
use crate::gpio::Speed;
|
||||
@ -429,7 +429,7 @@ where
|
||||
}
|
||||
});
|
||||
|
||||
let (_, result) = futures::future::join(dma_read, result).await;
|
||||
let (_, result) = embassy_futures::join::join(dma_read, result).await;
|
||||
|
||||
unsafe { Self::toggle(false) };
|
||||
|
||||
@ -537,7 +537,7 @@ where
|
||||
|
||||
unsafe { Self::toggle(true) };
|
||||
|
||||
let (_, result) = futures::future::join(dma_result, result).await;
|
||||
let (_, result) = embassy_futures::join::join(dma_result, result).await;
|
||||
|
||||
unsafe { Self::toggle(false) };
|
||||
|
||||
|
@ -29,7 +29,7 @@ use super::*;
|
||||
|
||||
pub struct State<'d, T: Instance, const TX: usize, const RX: usize>(StateStorage<Inner<'d, T, TX, RX>>);
|
||||
impl<'d, T: Instance, const TX: usize, const RX: usize> State<'d, T, TX, RX> {
|
||||
pub fn new() -> Self {
|
||||
pub const fn new() -> Self {
|
||||
Self(StateStorage::new())
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ use super::*;
|
||||
|
||||
pub struct State<'d, T: Instance, const TX: usize, const RX: usize>(StateStorage<Inner<'d, T, TX, RX>>);
|
||||
impl<'d, T: Instance, const TX: usize, const RX: usize> State<'d, T, TX, RX> {
|
||||
pub fn new() -> Self {
|
||||
pub const fn new() -> Self {
|
||||
Self(StateStorage::new())
|
||||
}
|
||||
}
|
||||
|
@ -155,7 +155,7 @@ mod eh1 {
|
||||
type Error = Infallible;
|
||||
}
|
||||
|
||||
impl<'d, T: GpioPin> embedded_hal_1::digital::blocking::InputPin for ExtiInput<'d, T> {
|
||||
impl<'d, T: GpioPin> embedded_hal_1::digital::InputPin for ExtiInput<'d, T> {
|
||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_high())
|
||||
}
|
||||
@ -165,40 +165,41 @@ mod eh1 {
|
||||
}
|
||||
}
|
||||
}
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] {
|
||||
use futures::FutureExt;
|
||||
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
|
||||
mod eha {
|
||||
use futures::FutureExt;
|
||||
|
||||
impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for ExtiInput<'d, T> {
|
||||
type WaitForHighFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
use super::*;
|
||||
|
||||
fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> {
|
||||
self.wait_for_high().map(Ok)
|
||||
}
|
||||
impl<'d, T: GpioPin> embedded_hal_async::digital::Wait for ExtiInput<'d, T> {
|
||||
type WaitForHighFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
type WaitForLowFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a> {
|
||||
self.wait_for_high().map(Ok)
|
||||
}
|
||||
|
||||
fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> {
|
||||
self.wait_for_low().map(Ok)
|
||||
}
|
||||
type WaitForLowFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
type WaitForRisingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a> {
|
||||
self.wait_for_low().map(Ok)
|
||||
}
|
||||
|
||||
fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> {
|
||||
self.wait_for_rising_edge().map(Ok)
|
||||
}
|
||||
type WaitForRisingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
type WaitForFallingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a> {
|
||||
self.wait_for_rising_edge().map(Ok)
|
||||
}
|
||||
|
||||
fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> {
|
||||
self.wait_for_falling_edge().map(Ok)
|
||||
}
|
||||
type WaitForFallingEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
type WaitForAnyEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a> {
|
||||
self.wait_for_falling_edge().map(Ok)
|
||||
}
|
||||
|
||||
fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> {
|
||||
self.wait_for_any_edge().map(Ok)
|
||||
}
|
||||
type WaitForAnyEdgeFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
|
||||
|
||||
fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a> {
|
||||
self.wait_for_any_edge().map(Ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user