Compare commits
494 Commits
usb-serial
...
embassy-ex
Author | SHA1 | Date | |
---|---|---|---|
611e82711a | |||
baef856206 | |||
858d520882 | |||
3b43b00867 | |||
95262ad559 | |||
43e2edfbda | |||
fb49e03eda | |||
c312884692 | |||
bc156afbb2 | |||
8a9622ec2e | |||
8d71fbd032 | |||
820852be28 | |||
fcb77f3f96 | |||
32fdd4c787 | |||
6a73ab1afa | |||
6fc5c608f8 | |||
b7114fb951 | |||
d375c46590 | |||
c40b944da6 | |||
e31545860e | |||
47b8e04b1c | |||
2ab9a07b64 | |||
5d5cd23715 | |||
77844e2055 | |||
28eb3b95b0 | |||
8b837fae07 | |||
d49f40dd5c | |||
477a90b8e3 | |||
a34331ae5f | |||
b555af1c5d | |||
ae608cf2fa | |||
28618d12a1 | |||
66c1712118 | |||
7b3d7a3826 | |||
d7031fbe92 | |||
02fcb07aa9 | |||
66faba2df7 | |||
766b5fc6f6 | |||
a2fd7108ff | |||
630372b183 | |||
3c5f011245 | |||
e80db42061 | |||
7e269f6f17 | |||
30358c435e | |||
55e07712e5 | |||
293edc5772 | |||
dc5acc687f | |||
a40daa923b | |||
9dfda46e0c | |||
4d60c715e6 | |||
2c96fe917d | |||
b42a7ebd8c | |||
9b1bdc3099 | |||
bcaef1de18 | |||
33778d3772 | |||
a6b8f3d994 | |||
b166ed6b78 | |||
54d31c98fe | |||
48eac0b146 | |||
0d8a9b1e7a | |||
ef3b1f46a9 | |||
5e4f65fe1f | |||
5fcebd28f4 | |||
a1fce1b554 | |||
bbc8424a5b | |||
036bc669cd | |||
26cc0e634d | |||
1b0f4ee653 | |||
f3ad0c6ade | |||
4da9743317 | |||
dca1777a2f | |||
4a9df60a7b | |||
2c6fcdbd3f | |||
e6d4043279 | |||
5c2ba3b212 | |||
ebc173ea75 | |||
b394cc3394 | |||
6caf627262 | |||
3d68d42132 | |||
bdd59b8988 | |||
9f55228be0 | |||
83ff3cbc69 | |||
4afdce4ec5 | |||
71fcea159f | |||
036e00113e | |||
958cace36d | |||
2568c714c8 | |||
83ab8e057a | |||
0ddabf0423 | |||
b4d0f24bf9 | |||
5a2f61a031 | |||
5b4c099afc | |||
c3357f884a | |||
eff2d71f28 | |||
42b21fd7ae | |||
027801db60 | |||
ad85beb677 | |||
780569c08a | |||
ffa0c08140 | |||
c38c85ef1f | |||
6c6bd11c1a | |||
4999b045df | |||
105aa8f452 | |||
d8f02e151b | |||
3aef5999d5 | |||
3c3a1d89b5 | |||
e8d3e86591 | |||
d6c5c1772c | |||
b65406791a | |||
f3237d7a2c | |||
56b21ad429 | |||
538cf2bc24 | |||
d8420ed5a0 | |||
04ed45941a | |||
55fb1d5126 | |||
4f791799a9 | |||
d2127f6b82 | |||
1d815f4ba0 | |||
aef93246b4 | |||
6b1d802caa | |||
6f30e92c7a | |||
39c1cc9f00 | |||
73057ee241 | |||
a8a491212b | |||
2f18770e27 | |||
087e649bc2 | |||
fd9b6487e1 | |||
603c4cb4fa | |||
8bed573b88 | |||
2a004251a7 | |||
8064f4bfe0 | |||
6256a6c57c | |||
bae31ebce7 | |||
e0ce7fcde7 | |||
a9f6e30bcd | |||
0d7b005252 | |||
fcbfd224a7 | |||
eb097b9d03 | |||
7ed9e29326 | |||
bdc4aa4a3b | |||
5bb5654d84 | |||
a8d3bcbb75 | |||
ec787d3518 | |||
c7c701b3e3 | |||
e495d606ec | |||
28136579e9 | |||
cc414e63d3 | |||
fd47445d75 | |||
d39404cdda | |||
29acc46501 | |||
cffb819e61 | |||
b344c843c4 | |||
e3cc0d168c | |||
cbc8871a0b | |||
e97b14c068 | |||
5a8704b4d8 | |||
6dd2fc5941 | |||
69c0a89aa5 | |||
937a63ce28 | |||
b57ba84da5 | |||
c3ba08ffb6 | |||
c52d1d11f9 | |||
d752a3f980 | |||
973b152375 | |||
3690af9bea | |||
f81ee103bf | |||
b124222649 | |||
8d8c642845 | |||
d5f9d17b7c | |||
036e6ae30c | |||
38b5d1ee2b | |||
146c744223 | |||
0ced8400d0 | |||
2e4f89068a | |||
f9dd751b6b | |||
6b6acc256d | |||
91338adc15 | |||
1d4e1092c4 | |||
0f1ff77fcc | |||
e947aa0153 | |||
44c8db2911 | |||
93864610ce | |||
bbd2563e13 | |||
a6543cef16 | |||
2815540167 | |||
8f1ea85938 | |||
3ee3f0e21c | |||
13acca624f | |||
c54ae73d49 | |||
858ddf6777 | |||
a5f2152077 | |||
a56ef685f3 | |||
62ab0bf2e7 | |||
77e34c5e8a | |||
270d1d59a0 | |||
3c41784de8 | |||
1425dda0a7 | |||
a60d92cfbb | |||
9f898c460f | |||
2a0fe73045 | |||
8d50f8a3d3 | |||
622fcb0e10 | |||
7fc138c91e | |||
5b32db7564 | |||
bd60f003e0 | |||
4883fdd154 | |||
18b9b6c780 | |||
fbcc587eca | |||
fbe30b2453 | |||
d42dff45de | |||
8e230bf6ec | |||
603ea9f310 | |||
a56b3e9a44 | |||
0378366e29 | |||
80ce6d1fb7 | |||
ba8e5d8589 | |||
192cdc2f85 | |||
64f8a779ca | |||
5693ed1178 | |||
19c6c698b5 | |||
224fbc8125 | |||
e5b4641f9e | |||
c83552eadc | |||
4db63677f6 | |||
f4d6a23f92 | |||
2cdd593290 | |||
c675208b8a | |||
e9445ec72d | |||
899a68325c | |||
c80c232a72 | |||
83ff626c47 | |||
809d3476aa | |||
4d1d125f41 | |||
4d6b3c57b1 | |||
a3d4ae85b0 | |||
02d57afd51 | |||
28254842db | |||
3382ca1a54 | |||
07a9a4ffd8 | |||
36ff688fab | |||
3df2c71e6c | |||
ca1d4179a7 | |||
890d113b85 | |||
7555a1e302 | |||
be7fbe50d7 | |||
2c01f277c2 | |||
c333d855fc | |||
42de1c3a06 | |||
a1d3bc30fa | |||
98576c17b6 | |||
27a3d2cd0b | |||
10f5966787 | |||
48957dce87 | |||
fc901f9856 | |||
13964c7fca | |||
a1cc3f2c60 | |||
15a6e04887 | |||
6bf4717b0a | |||
f581831b86 | |||
6f02403184 | |||
d040871f7a | |||
8f23b6faa6 | |||
1d2c47273d | |||
55ac480cb0 | |||
e4ad1aa542 | |||
6b5df4523a | |||
7b34f5e866 | |||
fe1e7c4d76 | |||
34217ea797 | |||
a0515ca7ac | |||
28b419d65e | |||
7c465465c1 | |||
e95a7dc555 | |||
582006c75c | |||
c7ec45a004 | |||
88d1976e81 | |||
cd592cb055 | |||
0b63af3313 | |||
25197308e3 | |||
cf278ea1b6 | |||
4db4200c37 | |||
758862f4b1 | |||
3705b4f40d | |||
1f63fdbb15 | |||
d11a94e2a7 | |||
d6dd5ea5d3 | |||
8a146a50ec | |||
17d5e1c470 | |||
975a780efe | |||
c3774607a5 | |||
bb24cfd1e8 | |||
48b37aa2bf | |||
0bde4992ea | |||
7ec7d1bbcc | |||
0628dd997f | |||
283ec756a9 | |||
5b076cb0dd | |||
3bae533066 | |||
7b36fe049d | |||
4b3fda4f96 | |||
56ca179475 | |||
460cdc9e0f | |||
f90b170dad | |||
68792bb918 | |||
3f0c8bafb0 | |||
588c0479f5 | |||
d979841f17 | |||
dff9bd9711 | |||
eccd2ecebf | |||
ed86fc175f | |||
132327a40d | |||
a615a70eda | |||
a2501bd5c1 | |||
ff2daaff67 | |||
d5a4457b5e | |||
6d402fe393 | |||
466a391b52 | |||
a93714327e | |||
029b156563 | |||
55a5e9b3a5 | |||
d8c7c3fc4b | |||
f192f44018 | |||
b81c14f442 | |||
f54e1cea90 | |||
fbddfcbfb7 | |||
67b14e6e7a | |||
6f4172fbc1 | |||
c6e2f4a90b | |||
91c1d17f16 | |||
29f3d5b68d | |||
4aca7c8811 | |||
8a811cfcf7 | |||
bf4493dbdf | |||
c1bf5aee24 | |||
735d676a72 | |||
37c103b5f3 | |||
05c524a7db | |||
758a2c528f | |||
69b4e898b3 | |||
b0da6318f3 | |||
972cdd4265 | |||
f9aebfce01 | |||
151557fec3 | |||
7d68ca1f3b | |||
4b63829110 | |||
e196387e69 | |||
f8d608093f | |||
ffeb40ff43 | |||
47305c2bf2 | |||
c421b7f5f0 | |||
d137286981 | |||
864202a23a | |||
a77fb0f630 | |||
a42ac86f1b | |||
c6cd69887c | |||
d1711036db | |||
8313b7315a | |||
d7ecf6f593 | |||
082147939d | |||
67c4d165c7 | |||
fb3e6a2b40 | |||
8ee2f50b8c | |||
46a4600952 | |||
27992d9c07 | |||
a0dc87d64e | |||
1255d8a8ce | |||
7d3eb6463a | |||
ab7fcf1d5b | |||
2c5146f19f | |||
3341b53eb4 | |||
70c05c62e4 | |||
eb57bb298f | |||
953c745ed8 | |||
ce73c29246 | |||
9c4df46c46 | |||
2a035a24a6 | |||
10c0174903 | |||
d26a247a32 | |||
a101d9078d | |||
b2f843a4ce | |||
40d25da793 | |||
b956d5d06c | |||
0845eab8a0 | |||
582c721aec | |||
0c4180cdd0 | |||
d9824dfd64 | |||
a088c4bee6 | |||
8359d8c020 | |||
1869fe02ba | |||
e3e8d82933 | |||
a96f30edf4 | |||
af15b49bfe | |||
60b2f075dc | |||
0c49e6747c | |||
99b4ea7c1d | |||
c9b9be5b81 | |||
2e6b813225 | |||
aceba1c03f | |||
8141d53d94 | |||
21a8653195 | |||
d372df7ddb | |||
6e13f5b387 | |||
c07854fed8 | |||
8c4997c5fc | |||
3252eaa060 | |||
348019e37f | |||
b9eb3dfad7 | |||
71513ccb39 | |||
cdb3fb059f | |||
93caf97a04 | |||
bca2c54948 | |||
81cbb0fc32 | |||
c69f2929c0 | |||
4d23ea554b | |||
d6fde756a8 | |||
ba43444292 | |||
6eac49186d | |||
2432cece38 | |||
fef338f5c2 | |||
24e186e684 | |||
4feabb13bf | |||
3f19879f41 | |||
e90f47aba3 | |||
2aa2b843ce | |||
fa2cda81db | |||
837d3bcdbb | |||
9f50f34547 | |||
f0b17675d8 | |||
6eb46c419c | |||
96f1525ffe | |||
01101e3df0 | |||
b95e5a4ea6 | |||
f7ec579c18 | |||
4f0aca481f | |||
ce889900d6 | |||
8a0a7c81b6 | |||
e892014b65 | |||
8cbe5b8e20 | |||
5666c56903 | |||
d5898c11eb | |||
daedfbbd87 | |||
bf7e24e9d7 | |||
02f367f733 | |||
f2e7a23148 | |||
27a89019ad | |||
2eb7a67c70 | |||
59f829c6cc | |||
91c31d5e43 | |||
9b5d7ec061 | |||
ed493be869 | |||
f5ca687e9b | |||
9c81d63155 | |||
60c54107ce | |||
56dd22f0ac | |||
afec1b439b | |||
219ef5b37a | |||
a2d1e7f02c | |||
5e6e18b310 | |||
80407aa930 | |||
a575e40a35 | |||
28fb492c40 | |||
e7bc84dda8 | |||
8cafaa1f3c | |||
df944edeef | |||
388d3e273d | |||
915f79c974 | |||
ea04a0277b | |||
71afa40a69 | |||
89fbb02979 | |||
76a334bd7c | |||
f47a148f51 | |||
5ecf9ec7bc | |||
78736328a0 | |||
8d0095c618 | |||
fdb3c3d6ff | |||
56ab6d9f14 | |||
88052480b1 | |||
218b102b28 | |||
fe7b72948a | |||
e0747e937f | |||
320e2cf35b | |||
f8ee33abb9 | |||
78a2ca8a0e | |||
f5d084552d | |||
d896f80405 | |||
2900ab79e7 | |||
14eae9ca06 | |||
64154fec8c | |||
ed97e61dbe | |||
029713eca0 | |||
3d26573c6b | |||
0963b5f92c | |||
530f192acc | |||
a46f33b214 |
41
.gitattributes
vendored
Normal file
41
.gitattributes
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
* text=auto
|
||||||
|
|
||||||
|
*.adoc text
|
||||||
|
*.html text
|
||||||
|
*.in text
|
||||||
|
*.json text
|
||||||
|
*.md text
|
||||||
|
*.proto text
|
||||||
|
*.py text
|
||||||
|
*.rs text
|
||||||
|
*.service text
|
||||||
|
*.sh text
|
||||||
|
*.toml text
|
||||||
|
*.txt text
|
||||||
|
*.x text
|
||||||
|
*.yml text
|
||||||
|
|
||||||
|
*.raw binary
|
||||||
|
*.bin binary
|
||||||
|
*.png binary
|
||||||
|
*.jpg binary
|
||||||
|
*.jpeg binary
|
||||||
|
*.gif binary
|
||||||
|
*.ico binary
|
||||||
|
*.mov binary
|
||||||
|
*.mp4 binary
|
||||||
|
*.mp3 binary
|
||||||
|
*.flv binary
|
||||||
|
*.fla binary
|
||||||
|
*.swf binary
|
||||||
|
*.gz binary
|
||||||
|
*.zip binary
|
||||||
|
*.7z binary
|
||||||
|
*.ttf binary
|
||||||
|
*.eot binary
|
||||||
|
*.woff binary
|
||||||
|
*.pyc binary
|
||||||
|
*.pdf binary
|
||||||
|
*.ez binary
|
||||||
|
*.bz2 binary
|
||||||
|
*.swp binary
|
17
.github/ci/crlf.sh
vendored
Executable file
17
.github/ci/crlf.sh
vendored
Executable file
@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
## on push branch~=gh-readonly-queue/main/.*
|
||||||
|
## on pull_request
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
FILES_WITH_CRLF=$(find ! -path "./.git/*" -not -type d | xargs file -N | (grep " CRLF " || true))
|
||||||
|
|
||||||
|
if [ -z "$FILES_WITH_CRLF" ]; then
|
||||||
|
echo -e "No files with CRLF endings found."
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
NR_FILES=$(echo "$FILES_WITH_CRLF" | wc -l)
|
||||||
|
echo -e "ERROR: Found ${NR_FILES} files with CRLF endings."
|
||||||
|
echo "$FILES_WITH_CRLF"
|
||||||
|
exit "$NR_FILES"
|
||||||
|
fi
|
52
.github/ci/doc.sh
vendored
52
.github/ci/doc.sh
vendored
@ -6,7 +6,7 @@ set -euo pipefail
|
|||||||
export RUSTUP_HOME=/ci/cache/rustup
|
export RUSTUP_HOME=/ci/cache/rustup
|
||||||
export CARGO_HOME=/ci/cache/cargo
|
export CARGO_HOME=/ci/cache/cargo
|
||||||
export CARGO_TARGET_DIR=/ci/cache/target
|
export CARGO_TARGET_DIR=/ci/cache/target
|
||||||
export BUILDER_THREADS=6
|
export BUILDER_THREADS=4
|
||||||
export BUILDER_COMPRESS=true
|
export BUILDER_COMPRESS=true
|
||||||
|
|
||||||
# force rustup to download the toolchain before starting building.
|
# force rustup to download the toolchain before starting building.
|
||||||
@ -15,30 +15,32 @@ export BUILDER_COMPRESS=true
|
|||||||
# which makes rustup very sad
|
# which makes rustup very sad
|
||||||
rustc --version > /dev/null
|
rustc --version > /dev/null
|
||||||
|
|
||||||
docserver-builder -i ./embassy-stm32 -o crates/embassy-stm32/git.zup
|
docserver-builder -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup
|
||||||
docserver-builder -i ./embassy-boot/boot -o crates/embassy-boot/git.zup
|
docserver-builder -i ./embassy-boot/boot -o webroot/crates/embassy-boot/git.zup
|
||||||
docserver-builder -i ./embassy-boot/nrf -o crates/embassy-boot-nrf/git.zup
|
docserver-builder -i ./embassy-boot/nrf -o webroot/crates/embassy-boot-nrf/git.zup
|
||||||
docserver-builder -i ./embassy-boot/rp -o crates/embassy-boot-rp/git.zup
|
docserver-builder -i ./embassy-boot/rp -o webroot/crates/embassy-boot-rp/git.zup
|
||||||
docserver-builder -i ./embassy-boot/stm32 -o crates/embassy-boot-stm32/git.zup
|
docserver-builder -i ./embassy-boot/stm32 -o webroot/crates/embassy-boot-stm32/git.zup
|
||||||
docserver-builder -i ./embassy-embedded-hal -o crates/embassy-embedded-hal/git.zup
|
docserver-builder -i ./embassy-embedded-hal -o webroot/crates/embassy-embedded-hal/git.zup
|
||||||
docserver-builder -i ./embassy-executor -o crates/embassy-executor/git.zup
|
docserver-builder -i ./embassy-executor -o webroot/crates/embassy-executor/git.zup
|
||||||
docserver-builder -i ./embassy-futures -o crates/embassy-futures/git.zup
|
docserver-builder -i ./embassy-futures -o webroot/crates/embassy-futures/git.zup
|
||||||
docserver-builder -i ./embassy-lora -o crates/embassy-lora/git.zup
|
docserver-builder -i ./embassy-lora -o webroot/crates/embassy-lora/git.zup
|
||||||
docserver-builder -i ./embassy-net -o crates/embassy-net/git.zup
|
docserver-builder -i ./embassy-net -o webroot/crates/embassy-net/git.zup
|
||||||
docserver-builder -i ./embassy-net-driver -o crates/embassy-net-driver/git.zup
|
docserver-builder -i ./embassy-net-driver -o webroot/crates/embassy-net-driver/git.zup
|
||||||
docserver-builder -i ./embassy-net-driver-channel -o crates/embassy-net-driver-channel/git.zup
|
docserver-builder -i ./embassy-net-driver-channel -o webroot/crates/embassy-net-driver-channel/git.zup
|
||||||
docserver-builder -i ./embassy-nrf -o crates/embassy-nrf/git.zup
|
docserver-builder -i ./embassy-nrf -o webroot/crates/embassy-nrf/git.zup
|
||||||
docserver-builder -i ./embassy-rp -o crates/embassy-rp/git.zup
|
docserver-builder -i ./embassy-rp -o webroot/crates/embassy-rp/git.zup
|
||||||
docserver-builder -i ./embassy-sync -o crates/embassy-sync/git.zup
|
docserver-builder -i ./embassy-sync -o webroot/crates/embassy-sync/git.zup
|
||||||
docserver-builder -i ./embassy-time -o crates/embassy-time/git.zup
|
docserver-builder -i ./embassy-time -o webroot/crates/embassy-time/git.zup
|
||||||
docserver-builder -i ./embassy-usb -o crates/embassy-usb/git.zup
|
docserver-builder -i ./embassy-usb -o webroot/crates/embassy-usb/git.zup
|
||||||
docserver-builder -i ./embassy-usb-driver -o crates/embassy-usb-driver/git.zup
|
docserver-builder -i ./embassy-usb-driver -o webroot/crates/embassy-usb-driver/git.zup
|
||||||
docserver-builder -i ./embassy-usb-logger -o crates/embassy-usb-logger/git.zup
|
docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/git.zup
|
||||||
docserver-builder -i ./cyw43 -o crates/cyw43/git.zup
|
docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup
|
||||||
docserver-builder -i ./cyw43-pio -o crates/cyw43-pio/git.zup
|
docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup
|
||||||
docserver-builder -i ./embassy-net-w5500 -o crates/embassy-net-w5500/git.zup
|
docserver-builder -i ./embassy-net-w5500 -o webroot/crates/embassy-net-w5500/git.zup
|
||||||
docserver-builder -i ./embassy-stm32-wpan -o crates/embassy-stm32-wpan/git.zup
|
docserver-builder -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup
|
||||||
|
docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static
|
||||||
|
|
||||||
export KUBECONFIG=/ci/secrets/kubeconfig.yml
|
export KUBECONFIG=/ci/secrets/kubeconfig.yml
|
||||||
POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name})
|
POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name})
|
||||||
kubectl cp crates $POD:/data
|
kubectl cp webroot/crates $POD:/data
|
||||||
|
kubectl cp webroot/static $POD:/data
|
2
.github/ci/test.sh
vendored
2
.github/ci/test.sh
vendored
@ -13,7 +13,7 @@ hashtime save /ci/cache/filetime.json
|
|||||||
|
|
||||||
cargo test --manifest-path ./embassy-sync/Cargo.toml
|
cargo test --manifest-path ./embassy-sync/Cargo.toml
|
||||||
cargo test --manifest-path ./embassy-embedded-hal/Cargo.toml
|
cargo test --manifest-path ./embassy-embedded-hal/Cargo.toml
|
||||||
cargo test --manifest-path ./embassy-hal-common/Cargo.toml
|
cargo test --manifest-path ./embassy-hal-internal/Cargo.toml
|
||||||
cargo test --manifest-path ./embassy-time/Cargo.toml --features generic-queue
|
cargo test --manifest-path ./embassy-time/Cargo.toml --features generic-queue
|
||||||
|
|
||||||
cargo test --manifest-path ./embassy-boot/boot/Cargo.toml
|
cargo test --manifest-path ./embassy-boot/boot/Cargo.toml
|
||||||
|
8
.vscode/settings.json
vendored
8
.vscode/settings.json
vendored
@ -6,16 +6,16 @@
|
|||||||
"rust-analyzer.check.allTargets": false,
|
"rust-analyzer.check.allTargets": false,
|
||||||
"rust-analyzer.check.noDefaultFeatures": true,
|
"rust-analyzer.check.noDefaultFeatures": true,
|
||||||
"rust-analyzer.cargo.noDefaultFeatures": true,
|
"rust-analyzer.cargo.noDefaultFeatures": true,
|
||||||
"rust-analyzer.cargo.target": "thumbv7em-none-eabi",
|
"rust-analyzer.cargo.target": "thumbv7m-none-eabi",
|
||||||
//"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf",
|
//"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf",
|
||||||
"rust-analyzer.cargo.features": [
|
"rust-analyzer.cargo.features": [
|
||||||
"nightly",
|
///"nightly",
|
||||||
],
|
],
|
||||||
"rust-analyzer.linkedProjects": [
|
"rust-analyzer.linkedProjects": [
|
||||||
// Declare for the target you wish to develop
|
// Declare for the target you wish to develop
|
||||||
// "embassy-executor/Cargo.toml",
|
// "embassy-executor/Cargo.toml",
|
||||||
// "embassy-sync/Cargo.toml",
|
// "embassy-sync/Cargo.toml",
|
||||||
"examples/nrf52840/Cargo.toml",
|
"examples/stm32wl/Cargo.toml",
|
||||||
// "examples/nrf5340/Cargo.toml",
|
// "examples/nrf5340/Cargo.toml",
|
||||||
// "examples/nrf-rtos-trace/Cargo.toml",
|
// "examples/nrf-rtos-trace/Cargo.toml",
|
||||||
// "examples/rp/Cargo.toml",
|
// "examples/rp/Cargo.toml",
|
||||||
@ -25,6 +25,7 @@
|
|||||||
// "examples/stm32f1/Cargo.toml",
|
// "examples/stm32f1/Cargo.toml",
|
||||||
// "examples/stm32f2/Cargo.toml",
|
// "examples/stm32f2/Cargo.toml",
|
||||||
// "examples/stm32f3/Cargo.toml",
|
// "examples/stm32f3/Cargo.toml",
|
||||||
|
// "examples/stm32f334/Cargo.toml",
|
||||||
// "examples/stm32f4/Cargo.toml",
|
// "examples/stm32f4/Cargo.toml",
|
||||||
// "examples/stm32f7/Cargo.toml",
|
// "examples/stm32f7/Cargo.toml",
|
||||||
// "examples/stm32g0/Cargo.toml",
|
// "examples/stm32g0/Cargo.toml",
|
||||||
@ -40,4 +41,5 @@
|
|||||||
// "examples/stm32wl/Cargo.toml",
|
// "examples/stm32wl/Cargo.toml",
|
||||||
// "examples/wasm/Cargo.toml",
|
// "examples/wasm/Cargo.toml",
|
||||||
],
|
],
|
||||||
|
"rust-analyzer.showUnlinkedFileNotification": false,
|
||||||
}
|
}
|
15
README.md
15
README.md
@ -33,6 +33,7 @@ The <a href="https://docs.embassy.dev/embassy-net/">embassy-net</a> network stac
|
|||||||
|
|
||||||
- **Bluetooth** -
|
- **Bluetooth** -
|
||||||
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.
|
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.
|
||||||
|
The <a href="https://github.com/embassy-rs/embassy/tree/main/embassy-stm32-wpan">embassy-stm32-wpan</a> crate provides Bluetooth Low Energy 5.x support for stm32wb microcontrollers.
|
||||||
|
|
||||||
- **LoRa** -
|
- **LoRa** -
|
||||||
<a href="https://docs.embassy.dev/embassy-lora/">embassy-lora</a> supports LoRa networking.
|
<a href="https://docs.embassy.dev/embassy-lora/">embassy-lora</a> supports LoRa networking.
|
||||||
@ -99,10 +100,10 @@ Examples are found in the `examples/` folder seperated by the chip manufacturer
|
|||||||
|
|
||||||
### Running examples
|
### Running examples
|
||||||
|
|
||||||
- Install `probe-rs-cli` with defmt support.
|
- Install `probe-rs`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo install probe-rs-cli
|
cargo install probe-rs --features cli
|
||||||
```
|
```
|
||||||
|
|
||||||
- Change directory to the sample's base directory. For example:
|
- Change directory to the sample's base directory. For example:
|
||||||
@ -111,6 +112,12 @@ cargo install probe-rs-cli
|
|||||||
cd examples/nrf52840
|
cd examples/nrf52840
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- Ensure `Cargo.toml` sets the right feature for the name of the chip you are programming.
|
||||||
|
If this name is incorrect, the example may fail to run or immediately crash
|
||||||
|
after being programmed.
|
||||||
|
|
||||||
|
- Ensure `.cargo/config.toml` contains the name of the chip you are programming.
|
||||||
|
|
||||||
- Run the example
|
- Run the example
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
@ -119,6 +126,8 @@ For example:
|
|||||||
cargo run --release --bin blinky
|
cargo run --release --bin blinky
|
||||||
```
|
```
|
||||||
|
|
||||||
|
For more help getting started, see [Getting Started][1] and [Running the Examples][2].
|
||||||
|
|
||||||
## Developing Embassy with Rust Analyzer based editors
|
## Developing Embassy with Rust Analyzer based editors
|
||||||
|
|
||||||
The [Rust Analyzer](https://rust-analyzer.github.io/) is used by [Visual Studio Code](https://code.visualstudio.com/)
|
The [Rust Analyzer](https://rust-analyzer.github.io/) is used by [Visual Studio Code](https://code.visualstudio.com/)
|
||||||
@ -151,3 +160,5 @@ This work is licensed under either of
|
|||||||
|
|
||||||
at your option.
|
at your option.
|
||||||
|
|
||||||
|
[1]: https://github.com/embassy-rs/embassy/wiki/Getting-Started
|
||||||
|
[2]: https://github.com/embassy-rs/embassy/wiki/Running-the-Examples
|
||||||
|
20
ci.sh
20
ci.sh
@ -5,10 +5,6 @@ set -euo pipefail
|
|||||||
export RUSTFLAGS=-Dwarnings
|
export RUSTFLAGS=-Dwarnings
|
||||||
export DEFMT_LOG=trace,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info
|
export DEFMT_LOG=trace,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info
|
||||||
|
|
||||||
# needed by wifi examples
|
|
||||||
export WIFI_NETWORK=x
|
|
||||||
export WIFI_PASSWORD=x
|
|
||||||
|
|
||||||
TARGET=$(rustc -vV | sed -n 's|host: ||p')
|
TARGET=$(rustc -vV | sed -n 's|host: ||p')
|
||||||
|
|
||||||
BUILD_EXTRA=""
|
BUILD_EXTRA=""
|
||||||
@ -24,20 +20,19 @@ cargo batch \
|
|||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,defmt \
|
--- 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-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
|
||||||
--- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
|
--- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
|
||||||
--- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits,defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \
|
--- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \
|
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly \
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly \
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits,nightly \
|
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,unstable-traits \
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ieee802154 \
|
||||||
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,medium-ieee802154 \
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,nightly \
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,nightly \
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,unstable-traits,nightly \
|
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet \
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet \
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet,unstable-traits \
|
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet,nightly \
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet,nightly \
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet,unstable-traits,nightly \
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,nightly \
|
||||||
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,nightly \
|
||||||
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,medium-ieee802154,nightly \
|
||||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52805,gpiote,time-driver-rtc1 \
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52805,gpiote,time-driver-rtc1 \
|
||||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52810,gpiote,time-driver-rtc1 \
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52810,gpiote,time-driver-rtc1 \
|
||||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52811,gpiote,time-driver-rtc1 \
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52811,gpiote,time-driver-rtc1 \
|
||||||
@ -57,6 +52,7 @@ cargo batch \
|
|||||||
--- 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,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly \
|
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly \
|
||||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,intrinsics \
|
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,intrinsics \
|
||||||
|
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,qspi-as-gpio \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time-driver-any \
|
||||||
@ -90,6 +86,7 @@ cargo batch \
|
|||||||
--- 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 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 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,stm32wle5jb,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wle5jb,defmt,exti,time-driver-any,unstable-traits \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32g474pe,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,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,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-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,unstable-traits \
|
||||||
@ -119,6 +116,7 @@ cargo batch \
|
|||||||
--- build --release --manifest-path examples/stm32f1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f1 \
|
--- build --release --manifest-path examples/stm32f1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f1 \
|
||||||
--- build --release --manifest-path examples/stm32f2/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f2 \
|
--- build --release --manifest-path examples/stm32f2/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f2 \
|
||||||
--- build --release --manifest-path examples/stm32f3/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f3 \
|
--- build --release --manifest-path examples/stm32f3/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f3 \
|
||||||
|
--- build --release --manifest-path examples/stm32f334/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f334 \
|
||||||
--- build --release --manifest-path examples/stm32f4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32f4 \
|
--- build --release --manifest-path examples/stm32f4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32f4 \
|
||||||
--- build --release --manifest-path examples/stm32f7/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f7 \
|
--- build --release --manifest-path examples/stm32f7/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f7 \
|
||||||
--- build --release --manifest-path examples/stm32c0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32c0 \
|
--- build --release --manifest-path examples/stm32c0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32c0 \
|
||||||
|
@ -16,9 +16,7 @@ cargo batch \
|
|||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \
|
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \
|
||||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,unstable-traits \
|
|
||||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \
|
||||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time-driver-rtc1 \
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time-driver-rtc1 \
|
||||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time-driver-rtc1 \
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time-driver-rtc1 \
|
||||||
@ -36,6 +34,7 @@ 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,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 --features unstable-traits,log \
|
||||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi \
|
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi \
|
||||||
|
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features qspi-as-gpio \
|
||||||
--- 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 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 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 stm32u585zi,defmt,exti,time-driver-any,unstable-traits \
|
||||||
|
Binary file not shown.
Binary file not shown.
@ -3,3 +3,7 @@
|
|||||||
Firmware obtained from https://github.com/Infineon/wifi-host-driver/tree/master/WiFi_Host_Driver/resources/firmware/COMPONENT_43439
|
Firmware obtained from https://github.com/Infineon/wifi-host-driver/tree/master/WiFi_Host_Driver/resources/firmware/COMPONENT_43439
|
||||||
|
|
||||||
Licensed under the [Infineon Permissive Binary License](./LICENSE-permissive-binary-license-1.0.txt)
|
Licensed under the [Infineon Permissive Binary License](./LICENSE-permissive-binary-license-1.0.txt)
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
* 2023-07-28: synced with `ad3bad0` - Update 43439 fw from 7.95.55 ot 7.95.62
|
||||||
|
@ -8,7 +8,6 @@ use cyw43::SpiBusCyw43;
|
|||||||
use embassy_rp::dma::Channel;
|
use embassy_rp::dma::Channel;
|
||||||
use embassy_rp::gpio::{Drive, Level, Output, Pin, Pull, SlewRate};
|
use embassy_rp::gpio::{Drive, Level, Output, Pin, Pull, SlewRate};
|
||||||
use embassy_rp::pio::{Common, Config, Direction, Instance, Irq, PioPin, ShiftDirection, StateMachine};
|
use embassy_rp::pio::{Common, Config, Direction, Instance, Irq, PioPin, ShiftDirection, StateMachine};
|
||||||
use embassy_rp::relocate::RelocatedProgram;
|
|
||||||
use embassy_rp::{pio_instr_util, Peripheral, PeripheralRef};
|
use embassy_rp::{pio_instr_util, Peripheral, PeripheralRef};
|
||||||
use fixed::FixedU32;
|
use fixed::FixedU32;
|
||||||
use pio_proc::pio_asm;
|
use pio_proc::pio_asm;
|
||||||
@ -88,8 +87,6 @@ where
|
|||||||
".wrap"
|
".wrap"
|
||||||
);
|
);
|
||||||
|
|
||||||
let relocated = RelocatedProgram::new(&program.program);
|
|
||||||
|
|
||||||
let mut pin_io: embassy_rp::pio::Pin<PIO> = common.make_pio_pin(dio);
|
let mut pin_io: embassy_rp::pio::Pin<PIO> = common.make_pio_pin(dio);
|
||||||
pin_io.set_pull(Pull::None);
|
pin_io.set_pull(Pull::None);
|
||||||
pin_io.set_schmitt(true);
|
pin_io.set_schmitt(true);
|
||||||
@ -102,7 +99,8 @@ where
|
|||||||
pin_clk.set_slew_rate(SlewRate::Fast);
|
pin_clk.set_slew_rate(SlewRate::Fast);
|
||||||
|
|
||||||
let mut cfg = Config::default();
|
let mut cfg = Config::default();
|
||||||
cfg.use_program(&common.load_program(&relocated), &[&pin_clk]);
|
let loaded_program = common.load_program(&program.program);
|
||||||
|
cfg.use_program(&loaded_program, &[&pin_clk]);
|
||||||
cfg.set_out_pins(&[&pin_io]);
|
cfg.set_out_pins(&[&pin_io]);
|
||||||
cfg.set_in_pins(&[&pin_io]);
|
cfg.set_in_pins(&[&pin_io]);
|
||||||
cfg.set_set_pins(&[&pin_io]);
|
cfg.set_set_pins(&[&pin_io]);
|
||||||
@ -142,7 +140,7 @@ where
|
|||||||
sm,
|
sm,
|
||||||
irq,
|
irq,
|
||||||
dma: dma.into_ref(),
|
dma: dma.into_ref(),
|
||||||
wrap_target: relocated.wrap().target,
|
wrap_target: loaded_program.wrap.target,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ log = ["dep:log"]
|
|||||||
firmware-logs = []
|
firmware-logs = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-time = { version = "0.1.0", path = "../embassy-time"}
|
embassy-time = { version = "0.1.2", path = "../embassy-time"}
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync"}
|
embassy-sync = { version = "0.2.0", path = "../embassy-sync"}
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
|
||||||
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
|
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
|
||||||
@ -24,7 +24,7 @@ cortex-m = "0.7.6"
|
|||||||
cortex-m-rt = "0.7.0"
|
cortex-m-rt = "0.7.0"
|
||||||
futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] }
|
futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] }
|
||||||
|
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.10" }
|
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.11" }
|
||||||
num_enum = { version = "0.5.7", default-features = false }
|
num_enum = { version = "0.5.7", default-features = false }
|
||||||
|
|
||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# cyw43
|
# cyw43
|
||||||
|
|
||||||
WIP driver for the CYW43439 wifi chip, used in the Raspberry Pi Pico W. Implementation based on [Infineon/wifi-host-driver](https://github.com/Infineon/wifi-host-driver).
|
Rust driver for the CYW43439 wifi chip, used in the Raspberry Pi Pico W. Implementation based on [Infineon/wifi-host-driver](https://github.com/Infineon/wifi-host-driver).
|
||||||
|
|
||||||
## Current status
|
## Current status
|
||||||
|
|
||||||
@ -19,18 +19,18 @@ Working:
|
|||||||
TODO:
|
TODO:
|
||||||
|
|
||||||
- Setting a custom MAC address.
|
- Setting a custom MAC address.
|
||||||
- Bus sleep (unclear what the benefit is. Is it needed for IRQs? or is it just power consumption optimization?)
|
- Bus sleep (for power consumption optimization)
|
||||||
|
|
||||||
## Running the examples
|
## Running the examples
|
||||||
|
|
||||||
- `cargo install probe-rs-cli`
|
- `cargo install probe-rs --features cli`
|
||||||
- `cd examples/rpi-pico-w`
|
- `cd examples/rp`
|
||||||
### Example 1: Scan the wifi stations
|
### Example 1: Scan the wifi stations
|
||||||
- `cargo run --release --bin wifi_scan`
|
- `cargo run --release --bin wifi_scan`
|
||||||
### Example 2: Create an access point (IP and credentials in the code)
|
### Example 2: Create an access point (IP and credentials in the code)
|
||||||
- `cargo run --release --bin tcp_server_ap`
|
- `cargo run --release --bin wifi_ap_tcp_server`
|
||||||
### Example 3: Connect to an existing network and create a server
|
### Example 3: Connect to an existing network and create a server
|
||||||
- `WIFI_NETWORK=MyWifiNetwork WIFI_PASSWORD=MyWifiPassword cargo run --release`
|
- `cargo run --release --bin wifi_tcp_server`
|
||||||
|
|
||||||
After a few seconds, you should see that DHCP picks up an IP address like this
|
After a few seconds, you should see that DHCP picks up an IP address like this
|
||||||
```
|
```
|
||||||
|
@ -216,7 +216,7 @@ where
|
|||||||
PWR: OutputPin,
|
PWR: OutputPin,
|
||||||
SPI: SpiBusCyw43,
|
SPI: SpiBusCyw43,
|
||||||
{
|
{
|
||||||
let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]);
|
let (ch_runner, device) = ch::new(&mut state.ch, ch::driver::HardwareAddress::Ethernet([0; 6]));
|
||||||
let state_ch = ch_runner.state_runner();
|
let state_ch = ch_runner.state_runner();
|
||||||
|
|
||||||
let mut runner = Runner::new(ch_runner, Bus::new(pwr, spi), &state.ioctl_state, &state.events);
|
let mut runner = Runner::new(ch_runner, Bus::new(pwr, spi), &state.ioctl_state, &state.events);
|
||||||
|
@ -345,7 +345,9 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn rx(&mut self, packet: &mut [u8]) {
|
fn rx(&mut self, packet: &mut [u8]) {
|
||||||
let Some((sdpcm_header, payload)) = SdpcmHeader::parse(packet) else { return };
|
let Some((sdpcm_header, payload)) = SdpcmHeader::parse(packet) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
self.update_credit(&sdpcm_header);
|
self.update_credit(&sdpcm_header);
|
||||||
|
|
||||||
@ -353,7 +355,9 @@ where
|
|||||||
|
|
||||||
match channel {
|
match channel {
|
||||||
CHANNEL_TYPE_CONTROL => {
|
CHANNEL_TYPE_CONTROL => {
|
||||||
let Some((cdc_header, response)) = CdcHeader::parse(payload) else { return; };
|
let Some((cdc_header, response)) = CdcHeader::parse(payload) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
trace!(" {:?}", cdc_header);
|
trace!(" {:?}", cdc_header);
|
||||||
|
|
||||||
if cdc_header.id == self.ioctl_id {
|
if cdc_header.id == self.ioctl_id {
|
||||||
@ -417,8 +421,12 @@ where
|
|||||||
let status = event_packet.msg.status;
|
let status = event_packet.msg.status;
|
||||||
let event_payload = match evt_type {
|
let event_payload = match evt_type {
|
||||||
Event::ESCAN_RESULT if status == EStatus::PARTIAL => {
|
Event::ESCAN_RESULT if status == EStatus::PARTIAL => {
|
||||||
let Some((_, bss_info)) = ScanResults::parse(evt_data) else { return };
|
let Some((_, bss_info)) = ScanResults::parse(evt_data) else {
|
||||||
let Some(bss_info) = BssInfo::parse(bss_info) else { return };
|
return;
|
||||||
|
};
|
||||||
|
let Some(bss_info) = BssInfo::parse(bss_info) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
events::Payload::BssInfo(*bss_info)
|
events::Payload::BssInfo(*bss_info)
|
||||||
}
|
}
|
||||||
Event::ESCAN_RESULT => events::Payload::None,
|
Event::ESCAN_RESULT => events::Payload::None,
|
||||||
@ -439,7 +447,9 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
CHANNEL_TYPE_DATA => {
|
CHANNEL_TYPE_DATA => {
|
||||||
let Some((_, packet)) = BdcHeader::parse(payload) else { return };
|
let Some((_, packet)) = BdcHeader::parse(payload) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
trace!("rx pkt {:02x}", Bytes(&packet[..packet.len().min(48)]));
|
trace!("rx pkt {:02x}", Bytes(&packet[..packet.len().min(48)]));
|
||||||
|
|
||||||
match self.ch.try_rx_buf() {
|
match self.ch.try_rx_buf() {
|
||||||
|
@ -10,9 +10,9 @@ use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAG
|
|||||||
|
|
||||||
/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
|
/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
|
||||||
/// 'mess up' the internal bootloader state
|
/// 'mess up' the internal bootloader state
|
||||||
pub struct FirmwareUpdater<DFU: NorFlash, STATE: NorFlash> {
|
pub struct FirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> {
|
||||||
dfu: DFU,
|
dfu: DFU,
|
||||||
state: STATE,
|
state: FirmwareState<'d, STATE>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "none")]
|
#[cfg(target_os = "none")]
|
||||||
@ -47,22 +47,12 @@ impl<'a, FLASH: NorFlash>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> {
|
impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
|
||||||
/// Create a firmware updater instance with partition ranges for the update and state partitions.
|
/// Create a firmware updater instance with partition ranges for the update and state partitions.
|
||||||
pub fn new(config: FirmwareUpdaterConfig<DFU, STATE>) -> Self {
|
pub fn new(config: FirmwareUpdaterConfig<DFU, STATE>, aligned: &'d mut [u8]) -> Self {
|
||||||
Self {
|
Self {
|
||||||
dfu: config.dfu,
|
dfu: config.dfu,
|
||||||
state: config.state,
|
state: FirmwareState::new(config.state, aligned),
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure we are running a booted firmware to avoid reverting to a bad state.
|
|
||||||
async fn verify_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
|
|
||||||
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
|
|
||||||
if self.get_state(aligned).await? == State::Boot {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(FirmwareUpdaterError::BadState)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,14 +61,8 @@ impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> {
|
|||||||
/// This is useful to check if the bootloader has just done a swap, in order
|
/// This is useful to check if the bootloader has just done a swap, in order
|
||||||
/// to do verifications and self-tests of the new image before calling
|
/// to do verifications and self-tests of the new image before calling
|
||||||
/// `mark_booted`.
|
/// `mark_booted`.
|
||||||
pub async fn get_state(&mut self, aligned: &mut [u8]) -> Result<State, FirmwareUpdaterError> {
|
pub async fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> {
|
||||||
self.state.read(0, aligned).await?;
|
self.state.get_state().await
|
||||||
|
|
||||||
if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
|
|
||||||
Ok(State::Swap)
|
|
||||||
} else {
|
|
||||||
Ok(State::Boot)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify the DFU given a public key. If there is an error then DO NOT
|
/// Verify the DFU given a public key. If there is an error then DO NOT
|
||||||
@ -92,23 +76,16 @@ impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> {
|
|||||||
///
|
///
|
||||||
/// If no signature feature is set then this method will always return a
|
/// If no signature feature is set then this method will always return a
|
||||||
/// signature error.
|
/// signature error.
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The `_aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from
|
|
||||||
/// and written to.
|
|
||||||
#[cfg(feature = "_verify")]
|
#[cfg(feature = "_verify")]
|
||||||
pub async fn verify_and_mark_updated(
|
pub async fn verify_and_mark_updated(
|
||||||
&mut self,
|
&mut self,
|
||||||
_public_key: &[u8],
|
_public_key: &[u8],
|
||||||
_signature: &[u8],
|
_signature: &[u8],
|
||||||
_update_len: u32,
|
_update_len: u32,
|
||||||
_aligned: &mut [u8],
|
|
||||||
) -> Result<(), FirmwareUpdaterError> {
|
) -> Result<(), FirmwareUpdaterError> {
|
||||||
assert_eq!(_aligned.len(), STATE::WRITE_SIZE);
|
|
||||||
assert!(_update_len <= self.dfu.capacity() as u32);
|
assert!(_update_len <= self.dfu.capacity() as u32);
|
||||||
|
|
||||||
self.verify_booted(_aligned).await?;
|
self.state.verify_booted().await?;
|
||||||
|
|
||||||
#[cfg(feature = "ed25519-dalek")]
|
#[cfg(feature = "ed25519-dalek")]
|
||||||
{
|
{
|
||||||
@ -121,8 +98,9 @@ impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> {
|
|||||||
let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?;
|
let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?;
|
||||||
let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
|
let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
|
||||||
|
|
||||||
|
let mut chunk_buf = [0; 2];
|
||||||
let mut message = [0; 64];
|
let mut message = [0; 64];
|
||||||
self.hash::<Sha512>(_update_len, _aligned, &mut message).await?;
|
self.hash::<Sha512>(_update_len, &mut chunk_buf, &mut message).await?;
|
||||||
|
|
||||||
public_key.verify(&message, &signature).map_err(into_signature_error)?
|
public_key.verify(&message, &signature).map_err(into_signature_error)?
|
||||||
}
|
}
|
||||||
@ -143,7 +121,8 @@ impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> {
|
|||||||
let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
|
let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
|
||||||
|
|
||||||
let mut message = [0; 64];
|
let mut message = [0; 64];
|
||||||
self.hash::<Sha512>(_update_len, _aligned, &mut message).await?;
|
let mut chunk_buf = [0; 2];
|
||||||
|
self.hash::<Sha512>(_update_len, &mut chunk_buf, &mut message).await?;
|
||||||
|
|
||||||
let r = public_key.verify(&message, &signature);
|
let r = public_key.verify(&message, &signature);
|
||||||
trace!(
|
trace!(
|
||||||
@ -156,7 +135,7 @@ impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> {
|
|||||||
r.map_err(into_signature_error)?
|
r.map_err(into_signature_error)?
|
||||||
}
|
}
|
||||||
|
|
||||||
self.set_magic(_aligned, SWAP_MAGIC).await
|
self.state.mark_updated().await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify the update in DFU with any digest.
|
/// Verify the update in DFU with any digest.
|
||||||
@ -177,49 +156,14 @@ impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Mark to trigger firmware swap on next boot.
|
/// Mark to trigger firmware swap on next boot.
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
|
|
||||||
#[cfg(not(feature = "_verify"))]
|
#[cfg(not(feature = "_verify"))]
|
||||||
pub async fn mark_updated(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
|
pub async fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
|
self.state.mark_updated().await
|
||||||
self.set_magic(aligned, SWAP_MAGIC).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark firmware boot successful and stop rollback on reset.
|
/// Mark firmware boot successful and stop rollback on reset.
|
||||||
///
|
pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
/// # Safety
|
self.state.mark_booted().await
|
||||||
///
|
|
||||||
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
|
|
||||||
pub async fn mark_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
|
|
||||||
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
|
|
||||||
self.set_magic(aligned, BOOT_MAGIC).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn set_magic(&mut self, aligned: &mut [u8], magic: u8) -> Result<(), FirmwareUpdaterError> {
|
|
||||||
self.state.read(0, aligned).await?;
|
|
||||||
|
|
||||||
if aligned.iter().any(|&b| b != magic) {
|
|
||||||
// Read progress validity
|
|
||||||
self.state.read(STATE::WRITE_SIZE as u32, aligned).await?;
|
|
||||||
|
|
||||||
if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
|
|
||||||
// The current progress validity marker is invalid
|
|
||||||
} else {
|
|
||||||
// Invalidate progress
|
|
||||||
aligned.fill(!STATE_ERASE_VALUE);
|
|
||||||
self.state.write(STATE::WRITE_SIZE as u32, aligned).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear magic and progress
|
|
||||||
self.state.erase(0, self.state.capacity() as u32).await?;
|
|
||||||
|
|
||||||
// Set magic
|
|
||||||
aligned.fill(magic);
|
|
||||||
self.state.write(0, aligned).await?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write data to a flash page.
|
/// Write data to a flash page.
|
||||||
@ -229,16 +173,10 @@ impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> {
|
|||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// Failing to meet alignment and size requirements may result in a panic.
|
/// Failing to meet alignment and size requirements may result in a panic.
|
||||||
pub async fn write_firmware(
|
pub async fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> {
|
||||||
&mut self,
|
|
||||||
aligned: &mut [u8],
|
|
||||||
offset: usize,
|
|
||||||
data: &[u8],
|
|
||||||
) -> Result<(), FirmwareUpdaterError> {
|
|
||||||
assert!(data.len() >= DFU::ERASE_SIZE);
|
assert!(data.len() >= DFU::ERASE_SIZE);
|
||||||
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
|
|
||||||
|
|
||||||
self.verify_booted(aligned).await?;
|
self.state.verify_booted().await?;
|
||||||
|
|
||||||
self.dfu.erase(offset as u32, (offset + data.len()) as u32).await?;
|
self.dfu.erase(offset as u32, (offset + data.len()) as u32).await?;
|
||||||
|
|
||||||
@ -252,20 +190,94 @@ impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> {
|
|||||||
///
|
///
|
||||||
/// Using this instead of `write_firmware` allows for an optimized API in
|
/// Using this instead of `write_firmware` allows for an optimized API in
|
||||||
/// exchange for added complexity.
|
/// exchange for added complexity.
|
||||||
///
|
pub async fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> {
|
||||||
/// # Safety
|
self.state.verify_booted().await?;
|
||||||
///
|
|
||||||
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
|
|
||||||
pub async fn prepare_update(&mut self, aligned: &mut [u8]) -> Result<&mut DFU, FirmwareUpdaterError> {
|
|
||||||
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
|
|
||||||
self.verify_booted(aligned).await?;
|
|
||||||
|
|
||||||
self.dfu.erase(0, self.dfu.capacity() as u32).await?;
|
self.dfu.erase(0, self.dfu.capacity() as u32).await?;
|
||||||
|
|
||||||
Ok(&mut self.dfu)
|
Ok(&mut self.dfu)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Manages the state partition of the firmware update.
|
||||||
|
///
|
||||||
|
/// Can be used standalone for more fine grained control, or as part of the updater.
|
||||||
|
pub struct FirmwareState<'d, STATE> {
|
||||||
|
state: STATE,
|
||||||
|
aligned: &'d mut [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> {
|
||||||
|
/// Create a firmware state instance with a buffer for magic content and state partition.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from
|
||||||
|
/// and written to.
|
||||||
|
pub fn new(state: STATE, aligned: &'d mut [u8]) -> Self {
|
||||||
|
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
|
||||||
|
Self { state, aligned }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we are running a booted firmware to avoid reverting to a bad state.
|
||||||
|
async fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
|
if self.get_state().await? == State::Boot {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(FirmwareUpdaterError::BadState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtain the current state.
|
||||||
|
///
|
||||||
|
/// This is useful to check if the bootloader has just done a swap, in order
|
||||||
|
/// to do verifications and self-tests of the new image before calling
|
||||||
|
/// `mark_booted`.
|
||||||
|
pub async fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> {
|
||||||
|
self.state.read(0, &mut self.aligned).await?;
|
||||||
|
|
||||||
|
if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) {
|
||||||
|
Ok(State::Swap)
|
||||||
|
} else {
|
||||||
|
Ok(State::Boot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark to trigger firmware swap on next boot.
|
||||||
|
pub async fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
|
self.set_magic(SWAP_MAGIC).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark firmware boot successful and stop rollback on reset.
|
||||||
|
pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
|
self.set_magic(BOOT_MAGIC).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_magic(&mut self, magic: u8) -> Result<(), FirmwareUpdaterError> {
|
||||||
|
self.state.read(0, &mut self.aligned).await?;
|
||||||
|
|
||||||
|
if self.aligned.iter().any(|&b| b != magic) {
|
||||||
|
// Read progress validity
|
||||||
|
self.state.read(STATE::WRITE_SIZE as u32, &mut self.aligned).await?;
|
||||||
|
|
||||||
|
if self.aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
|
||||||
|
// The current progress validity marker is invalid
|
||||||
|
} else {
|
||||||
|
// Invalidate progress
|
||||||
|
self.aligned.fill(!STATE_ERASE_VALUE);
|
||||||
|
self.state.write(STATE::WRITE_SIZE as u32, &self.aligned).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear magic and progress
|
||||||
|
self.state.erase(0, self.state.capacity() as u32).await?;
|
||||||
|
|
||||||
|
// Set magic
|
||||||
|
self.aligned.fill(magic);
|
||||||
|
self.state.write(0, &self.aligned).await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use embassy_embedded_hal::flash::partition::Partition;
|
use embassy_embedded_hal::flash::partition::Partition;
|
||||||
@ -288,8 +300,8 @@ mod tests {
|
|||||||
let mut to_write = [0; 4096];
|
let mut to_write = [0; 4096];
|
||||||
to_write[..7].copy_from_slice(update.as_slice());
|
to_write[..7].copy_from_slice(update.as_slice());
|
||||||
|
|
||||||
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state });
|
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
|
||||||
block_on(updater.write_firmware(&mut aligned, 0, to_write.as_slice())).unwrap();
|
block_on(updater.write_firmware(0, to_write.as_slice())).unwrap();
|
||||||
let mut chunk_buf = [0; 2];
|
let mut chunk_buf = [0; 2];
|
||||||
let mut hash = [0; 20];
|
let mut hash = [0; 20];
|
||||||
block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();
|
block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();
|
||||||
|
@ -10,9 +10,9 @@ use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAG
|
|||||||
|
|
||||||
/// Blocking FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
|
/// Blocking FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
|
||||||
/// 'mess up' the internal bootloader state
|
/// 'mess up' the internal bootloader state
|
||||||
pub struct BlockingFirmwareUpdater<DFU: NorFlash, STATE: NorFlash> {
|
pub struct BlockingFirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> {
|
||||||
dfu: DFU,
|
dfu: DFU,
|
||||||
state: STATE,
|
state: BlockingFirmwareState<'d, STATE>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "none")]
|
#[cfg(target_os = "none")]
|
||||||
@ -49,22 +49,17 @@ impl<'a, FLASH: NorFlash>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> {
|
impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> {
|
||||||
/// Create a firmware updater instance with partition ranges for the update and state partitions.
|
/// Create a firmware updater instance with partition ranges for the update and state partitions.
|
||||||
pub fn new(config: FirmwareUpdaterConfig<DFU, STATE>) -> Self {
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from
|
||||||
|
/// and written to.
|
||||||
|
pub fn new(config: FirmwareUpdaterConfig<DFU, STATE>, aligned: &'d mut [u8]) -> Self {
|
||||||
Self {
|
Self {
|
||||||
dfu: config.dfu,
|
dfu: config.dfu,
|
||||||
state: config.state,
|
state: BlockingFirmwareState::new(config.state, aligned),
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure we are running a booted firmware to avoid reverting to a bad state.
|
|
||||||
fn verify_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
|
|
||||||
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
|
|
||||||
if self.get_state(aligned)? == State::Boot {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(FirmwareUpdaterError::BadState)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,14 +68,8 @@ impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> {
|
|||||||
/// This is useful to check if the bootloader has just done a swap, in order
|
/// This is useful to check if the bootloader has just done a swap, in order
|
||||||
/// to do verifications and self-tests of the new image before calling
|
/// to do verifications and self-tests of the new image before calling
|
||||||
/// `mark_booted`.
|
/// `mark_booted`.
|
||||||
pub fn get_state(&mut self, aligned: &mut [u8]) -> Result<State, FirmwareUpdaterError> {
|
pub fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> {
|
||||||
self.state.read(0, aligned)?;
|
self.state.get_state()
|
||||||
|
|
||||||
if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
|
|
||||||
Ok(State::Swap)
|
|
||||||
} else {
|
|
||||||
Ok(State::Boot)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify the DFU given a public key. If there is an error then DO NOT
|
/// Verify the DFU given a public key. If there is an error then DO NOT
|
||||||
@ -94,23 +83,16 @@ impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> {
|
|||||||
///
|
///
|
||||||
/// If no signature feature is set then this method will always return a
|
/// If no signature feature is set then this method will always return a
|
||||||
/// signature error.
|
/// signature error.
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The `_aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from
|
|
||||||
/// and written to.
|
|
||||||
#[cfg(feature = "_verify")]
|
#[cfg(feature = "_verify")]
|
||||||
pub fn verify_and_mark_updated(
|
pub fn verify_and_mark_updated(
|
||||||
&mut self,
|
&mut self,
|
||||||
_public_key: &[u8],
|
_public_key: &[u8],
|
||||||
_signature: &[u8],
|
_signature: &[u8],
|
||||||
_update_len: u32,
|
_update_len: u32,
|
||||||
_aligned: &mut [u8],
|
|
||||||
) -> Result<(), FirmwareUpdaterError> {
|
) -> Result<(), FirmwareUpdaterError> {
|
||||||
assert_eq!(_aligned.len(), STATE::WRITE_SIZE);
|
|
||||||
assert!(_update_len <= self.dfu.capacity() as u32);
|
assert!(_update_len <= self.dfu.capacity() as u32);
|
||||||
|
|
||||||
self.verify_booted(_aligned)?;
|
self.state.verify_booted()?;
|
||||||
|
|
||||||
#[cfg(feature = "ed25519-dalek")]
|
#[cfg(feature = "ed25519-dalek")]
|
||||||
{
|
{
|
||||||
@ -124,7 +106,8 @@ impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> {
|
|||||||
let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
|
let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
|
||||||
|
|
||||||
let mut message = [0; 64];
|
let mut message = [0; 64];
|
||||||
self.hash::<Sha512>(_update_len, _aligned, &mut message)?;
|
let mut chunk_buf = [0; 2];
|
||||||
|
self.hash::<Sha512>(_update_len, &mut chunk_buf, &mut message)?;
|
||||||
|
|
||||||
public_key.verify(&message, &signature).map_err(into_signature_error)?
|
public_key.verify(&message, &signature).map_err(into_signature_error)?
|
||||||
}
|
}
|
||||||
@ -145,7 +128,8 @@ impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> {
|
|||||||
let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
|
let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
|
||||||
|
|
||||||
let mut message = [0; 64];
|
let mut message = [0; 64];
|
||||||
self.hash::<Sha512>(_update_len, _aligned, &mut message)?;
|
let mut chunk_buf = [0; 2];
|
||||||
|
self.hash::<Sha512>(_update_len, &mut chunk_buf, &mut message)?;
|
||||||
|
|
||||||
let r = public_key.verify(&message, &signature);
|
let r = public_key.verify(&message, &signature);
|
||||||
trace!(
|
trace!(
|
||||||
@ -158,7 +142,7 @@ impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> {
|
|||||||
r.map_err(into_signature_error)?
|
r.map_err(into_signature_error)?
|
||||||
}
|
}
|
||||||
|
|
||||||
self.set_magic(_aligned, SWAP_MAGIC)
|
self.state.mark_updated()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify the update in DFU with any digest.
|
/// Verify the update in DFU with any digest.
|
||||||
@ -179,49 +163,14 @@ impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Mark to trigger firmware swap on next boot.
|
/// Mark to trigger firmware swap on next boot.
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
|
|
||||||
#[cfg(not(feature = "_verify"))]
|
#[cfg(not(feature = "_verify"))]
|
||||||
pub fn mark_updated(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
|
pub fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
|
self.state.mark_updated()
|
||||||
self.set_magic(aligned, SWAP_MAGIC)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark firmware boot successful and stop rollback on reset.
|
/// Mark firmware boot successful and stop rollback on reset.
|
||||||
///
|
pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
/// # Safety
|
self.state.mark_booted()
|
||||||
///
|
|
||||||
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
|
|
||||||
pub fn mark_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
|
|
||||||
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
|
|
||||||
self.set_magic(aligned, BOOT_MAGIC)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_magic(&mut self, aligned: &mut [u8], magic: u8) -> Result<(), FirmwareUpdaterError> {
|
|
||||||
self.state.read(0, aligned)?;
|
|
||||||
|
|
||||||
if aligned.iter().any(|&b| b != magic) {
|
|
||||||
// Read progress validity
|
|
||||||
self.state.read(STATE::WRITE_SIZE as u32, aligned)?;
|
|
||||||
|
|
||||||
if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
|
|
||||||
// The current progress validity marker is invalid
|
|
||||||
} else {
|
|
||||||
// Invalidate progress
|
|
||||||
aligned.fill(!STATE_ERASE_VALUE);
|
|
||||||
self.state.write(STATE::WRITE_SIZE as u32, aligned)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear magic and progress
|
|
||||||
self.state.erase(0, self.state.capacity() as u32)?;
|
|
||||||
|
|
||||||
// Set magic
|
|
||||||
aligned.fill(magic);
|
|
||||||
self.state.write(0, aligned)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write data to a flash page.
|
/// Write data to a flash page.
|
||||||
@ -231,15 +180,9 @@ impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> {
|
|||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// Failing to meet alignment and size requirements may result in a panic.
|
/// Failing to meet alignment and size requirements may result in a panic.
|
||||||
pub fn write_firmware(
|
pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> {
|
||||||
&mut self,
|
|
||||||
aligned: &mut [u8],
|
|
||||||
offset: usize,
|
|
||||||
data: &[u8],
|
|
||||||
) -> Result<(), FirmwareUpdaterError> {
|
|
||||||
assert!(data.len() >= DFU::ERASE_SIZE);
|
assert!(data.len() >= DFU::ERASE_SIZE);
|
||||||
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
|
self.state.verify_booted()?;
|
||||||
self.verify_booted(aligned)?;
|
|
||||||
|
|
||||||
self.dfu.erase(offset as u32, (offset + data.len()) as u32)?;
|
self.dfu.erase(offset as u32, (offset + data.len()) as u32)?;
|
||||||
|
|
||||||
@ -253,19 +196,94 @@ impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> {
|
|||||||
///
|
///
|
||||||
/// Using this instead of `write_firmware` allows for an optimized API in
|
/// Using this instead of `write_firmware` allows for an optimized API in
|
||||||
/// exchange for added complexity.
|
/// exchange for added complexity.
|
||||||
///
|
pub fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> {
|
||||||
/// # Safety
|
self.state.verify_booted()?;
|
||||||
///
|
|
||||||
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
|
|
||||||
pub fn prepare_update(&mut self, aligned: &mut [u8]) -> Result<&mut DFU, FirmwareUpdaterError> {
|
|
||||||
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
|
|
||||||
self.verify_booted(aligned)?;
|
|
||||||
self.dfu.erase(0, self.dfu.capacity() as u32)?;
|
self.dfu.erase(0, self.dfu.capacity() as u32)?;
|
||||||
|
|
||||||
Ok(&mut self.dfu)
|
Ok(&mut self.dfu)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Manages the state partition of the firmware update.
|
||||||
|
///
|
||||||
|
/// Can be used standalone for more fine grained control, or as part of the updater.
|
||||||
|
pub struct BlockingFirmwareState<'d, STATE> {
|
||||||
|
state: STATE,
|
||||||
|
aligned: &'d mut [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> {
|
||||||
|
/// Create a firmware state instance with a buffer for magic content and state partition.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from
|
||||||
|
/// and written to.
|
||||||
|
pub fn new(state: STATE, aligned: &'d mut [u8]) -> Self {
|
||||||
|
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
|
||||||
|
Self { state, aligned }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we are running a booted firmware to avoid reverting to a bad state.
|
||||||
|
fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
|
if self.get_state()? == State::Boot {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(FirmwareUpdaterError::BadState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtain the current state.
|
||||||
|
///
|
||||||
|
/// This is useful to check if the bootloader has just done a swap, in order
|
||||||
|
/// to do verifications and self-tests of the new image before calling
|
||||||
|
/// `mark_booted`.
|
||||||
|
pub fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> {
|
||||||
|
self.state.read(0, &mut self.aligned)?;
|
||||||
|
|
||||||
|
if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) {
|
||||||
|
Ok(State::Swap)
|
||||||
|
} else {
|
||||||
|
Ok(State::Boot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark to trigger firmware swap on next boot.
|
||||||
|
pub fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
|
self.set_magic(SWAP_MAGIC)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark firmware boot successful and stop rollback on reset.
|
||||||
|
pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
|
self.set_magic(BOOT_MAGIC)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_magic(&mut self, magic: u8) -> Result<(), FirmwareUpdaterError> {
|
||||||
|
self.state.read(0, &mut self.aligned)?;
|
||||||
|
|
||||||
|
if self.aligned.iter().any(|&b| b != magic) {
|
||||||
|
// Read progress validity
|
||||||
|
self.state.read(STATE::WRITE_SIZE as u32, &mut self.aligned)?;
|
||||||
|
|
||||||
|
if self.aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
|
||||||
|
// The current progress validity marker is invalid
|
||||||
|
} else {
|
||||||
|
// Invalidate progress
|
||||||
|
self.aligned.fill(!STATE_ERASE_VALUE);
|
||||||
|
self.state.write(STATE::WRITE_SIZE as u32, &self.aligned)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear magic and progress
|
||||||
|
self.state.erase(0, self.state.capacity() as u32)?;
|
||||||
|
|
||||||
|
// Set magic
|
||||||
|
self.aligned.fill(magic);
|
||||||
|
self.state.write(0, &self.aligned)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
@ -283,14 +301,14 @@ mod tests {
|
|||||||
let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 4096, 8>::default()));
|
let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 4096, 8>::default()));
|
||||||
let state = BlockingPartition::new(&flash, 0, 4096);
|
let state = BlockingPartition::new(&flash, 0, 4096);
|
||||||
let dfu = BlockingPartition::new(&flash, 65536, 65536);
|
let dfu = BlockingPartition::new(&flash, 65536, 65536);
|
||||||
|
let mut aligned = [0; 8];
|
||||||
|
|
||||||
let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
|
let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
|
||||||
let mut to_write = [0; 4096];
|
let mut to_write = [0; 4096];
|
||||||
to_write[..7].copy_from_slice(update.as_slice());
|
to_write[..7].copy_from_slice(update.as_slice());
|
||||||
|
|
||||||
let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state });
|
let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
|
||||||
let mut aligned = [0; 8];
|
updater.write_firmware(0, to_write.as_slice()).unwrap();
|
||||||
updater.write_firmware(&mut aligned, 0, to_write.as_slice()).unwrap();
|
|
||||||
let mut chunk_buf = [0; 2];
|
let mut chunk_buf = [0; 2];
|
||||||
let mut hash = [0; 20];
|
let mut hash = [0; 20];
|
||||||
updater
|
updater
|
||||||
|
@ -3,8 +3,8 @@ mod asynch;
|
|||||||
mod blocking;
|
mod blocking;
|
||||||
|
|
||||||
#[cfg(feature = "nightly")]
|
#[cfg(feature = "nightly")]
|
||||||
pub use asynch::FirmwareUpdater;
|
pub use asynch::{FirmwareState, FirmwareUpdater};
|
||||||
pub use blocking::BlockingFirmwareUpdater;
|
pub use blocking::{BlockingFirmwareState, BlockingFirmwareUpdater};
|
||||||
use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind};
|
use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind};
|
||||||
|
|
||||||
/// Firmware updater flash configuration holding the two flashes used by the updater
|
/// Firmware updater flash configuration holding the two flashes used by the updater
|
||||||
|
@ -16,9 +16,11 @@ mod test_flash;
|
|||||||
// TODO: Use the value provided by NorFlash when available
|
// TODO: Use the value provided by NorFlash when available
|
||||||
pub(crate) const STATE_ERASE_VALUE: u8 = 0xFF;
|
pub(crate) const STATE_ERASE_VALUE: u8 = 0xFF;
|
||||||
pub use boot_loader::{BootError, BootLoader, BootLoaderConfig};
|
pub use boot_loader::{BootError, BootLoader, BootLoaderConfig};
|
||||||
|
pub use firmware_updater::{
|
||||||
|
BlockingFirmwareState, BlockingFirmwareUpdater, FirmwareUpdaterConfig, FirmwareUpdaterError,
|
||||||
|
};
|
||||||
#[cfg(feature = "nightly")]
|
#[cfg(feature = "nightly")]
|
||||||
pub use firmware_updater::FirmwareUpdater;
|
pub use firmware_updater::{FirmwareState, FirmwareUpdater};
|
||||||
pub use firmware_updater::{BlockingFirmwareUpdater, FirmwareUpdaterConfig, FirmwareUpdaterError};
|
|
||||||
|
|
||||||
pub(crate) const BOOT_MAGIC: u8 = 0xD0;
|
pub(crate) const BOOT_MAGIC: u8 = 0xD0;
|
||||||
pub(crate) const SWAP_MAGIC: u8 = 0xF0;
|
pub(crate) const SWAP_MAGIC: u8 = 0xF0;
|
||||||
@ -118,15 +120,18 @@ mod tests {
|
|||||||
block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap();
|
block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap();
|
||||||
block_on(flash.active().write(0, &ORIGINAL)).unwrap();
|
block_on(flash.active().write(0, &ORIGINAL)).unwrap();
|
||||||
|
|
||||||
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
|
let mut updater = FirmwareUpdater::new(
|
||||||
|
FirmwareUpdaterConfig {
|
||||||
dfu: flash.dfu(),
|
dfu: flash.dfu(),
|
||||||
state: flash.state(),
|
state: flash.state(),
|
||||||
});
|
},
|
||||||
block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap();
|
&mut aligned,
|
||||||
block_on(updater.mark_updated(&mut aligned)).unwrap();
|
);
|
||||||
|
block_on(updater.write_firmware(0, &UPDATE)).unwrap();
|
||||||
|
block_on(updater.mark_updated()).unwrap();
|
||||||
|
|
||||||
// Writing after marking updated is not allowed until marked as booted.
|
// Writing after marking updated is not allowed until marked as booted.
|
||||||
let res: Result<(), FirmwareUpdaterError> = block_on(updater.write_firmware(&mut aligned, 0, &UPDATE));
|
let res: Result<(), FirmwareUpdaterError> = block_on(updater.write_firmware(0, &UPDATE));
|
||||||
assert!(matches!(res, Err::<(), _>(FirmwareUpdaterError::BadState)));
|
assert!(matches!(res, Err::<(), _>(FirmwareUpdaterError::BadState)));
|
||||||
|
|
||||||
let flash = flash.into_blocking();
|
let flash = flash.into_blocking();
|
||||||
@ -158,11 +163,14 @@ mod tests {
|
|||||||
|
|
||||||
// Mark as booted
|
// Mark as booted
|
||||||
let flash = flash.into_async();
|
let flash = flash.into_async();
|
||||||
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
|
let mut updater = FirmwareUpdater::new(
|
||||||
|
FirmwareUpdaterConfig {
|
||||||
dfu: flash.dfu(),
|
dfu: flash.dfu(),
|
||||||
state: flash.state(),
|
state: flash.state(),
|
||||||
});
|
},
|
||||||
block_on(updater.mark_booted(&mut aligned)).unwrap();
|
&mut aligned,
|
||||||
|
);
|
||||||
|
block_on(updater.mark_booted()).unwrap();
|
||||||
|
|
||||||
let flash = flash.into_blocking();
|
let flash = flash.into_blocking();
|
||||||
let mut bootloader = BootLoader::new(BootLoaderConfig {
|
let mut bootloader = BootLoader::new(BootLoaderConfig {
|
||||||
@ -190,12 +198,15 @@ mod tests {
|
|||||||
block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap();
|
block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap();
|
||||||
block_on(flash.active().write(0, &ORIGINAL)).unwrap();
|
block_on(flash.active().write(0, &ORIGINAL)).unwrap();
|
||||||
|
|
||||||
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
|
let mut updater = FirmwareUpdater::new(
|
||||||
|
FirmwareUpdaterConfig {
|
||||||
dfu: flash.dfu(),
|
dfu: flash.dfu(),
|
||||||
state: flash.state(),
|
state: flash.state(),
|
||||||
});
|
},
|
||||||
block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap();
|
&mut aligned,
|
||||||
block_on(updater.mark_updated(&mut aligned)).unwrap();
|
);
|
||||||
|
block_on(updater.write_firmware(0, &UPDATE)).unwrap();
|
||||||
|
block_on(updater.mark_updated()).unwrap();
|
||||||
|
|
||||||
let flash = flash.into_blocking();
|
let flash = flash.into_blocking();
|
||||||
let mut bootloader = BootLoader::new(BootLoaderConfig {
|
let mut bootloader = BootLoader::new(BootLoaderConfig {
|
||||||
@ -232,12 +243,15 @@ mod tests {
|
|||||||
block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap();
|
block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap();
|
||||||
block_on(flash.active().write(0, &ORIGINAL)).unwrap();
|
block_on(flash.active().write(0, &ORIGINAL)).unwrap();
|
||||||
|
|
||||||
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
|
let mut updater = FirmwareUpdater::new(
|
||||||
|
FirmwareUpdaterConfig {
|
||||||
dfu: flash.dfu(),
|
dfu: flash.dfu(),
|
||||||
state: flash.state(),
|
state: flash.state(),
|
||||||
});
|
},
|
||||||
block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap();
|
&mut aligned,
|
||||||
block_on(updater.mark_updated(&mut aligned)).unwrap();
|
);
|
||||||
|
block_on(updater.write_firmware(0, &UPDATE)).unwrap();
|
||||||
|
block_on(updater.mark_updated()).unwrap();
|
||||||
|
|
||||||
let flash = flash.into_blocking();
|
let flash = flash.into_blocking();
|
||||||
let mut bootloader = BootLoader::new(BootLoaderConfig {
|
let mut bootloader = BootLoader::new(BootLoaderConfig {
|
||||||
@ -293,18 +307,19 @@ mod tests {
|
|||||||
|
|
||||||
// On with the test
|
// On with the test
|
||||||
let flash = flash.into_async();
|
let flash = flash.into_async();
|
||||||
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
|
let mut aligned = [0; 4];
|
||||||
|
let mut updater = FirmwareUpdater::new(
|
||||||
|
FirmwareUpdaterConfig {
|
||||||
dfu: flash.dfu(),
|
dfu: flash.dfu(),
|
||||||
state: flash.state(),
|
state: flash.state(),
|
||||||
});
|
},
|
||||||
|
&mut aligned,
|
||||||
let mut aligned = [0; 4];
|
);
|
||||||
|
|
||||||
assert!(block_on(updater.verify_and_mark_updated(
|
assert!(block_on(updater.verify_and_mark_updated(
|
||||||
&public_key.to_bytes(),
|
&public_key.to_bytes(),
|
||||||
&signature.to_bytes(),
|
&signature.to_bytes(),
|
||||||
firmware_len as u32,
|
firmware_len as u32,
|
||||||
&mut aligned,
|
|
||||||
))
|
))
|
||||||
.is_ok());
|
.is_ok());
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ An adaptation of `embassy-boot` for nRF.
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
* Load applications with our without the softdevice.
|
* Load applications with or without the softdevice.
|
||||||
* Configure bootloader partitions based on linker script.
|
* Configure bootloader partitions based on linker script.
|
||||||
* Using watchdog timer to detect application failure.
|
* Using watchdog timer to detect application failure.
|
||||||
|
|
||||||
|
@ -3,10 +3,12 @@
|
|||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
mod fmt;
|
mod fmt;
|
||||||
|
|
||||||
|
pub use embassy_boot::{
|
||||||
|
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig,
|
||||||
|
};
|
||||||
#[cfg(feature = "nightly")]
|
#[cfg(feature = "nightly")]
|
||||||
pub use embassy_boot::FirmwareUpdater;
|
pub use embassy_boot::{FirmwareState, FirmwareUpdater};
|
||||||
pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig};
|
use embassy_nrf::nvmc::PAGE_SIZE;
|
||||||
use embassy_nrf::nvmc::{Nvmc, PAGE_SIZE};
|
|
||||||
use embassy_nrf::peripherals::WDT;
|
use embassy_nrf::peripherals::WDT;
|
||||||
use embassy_nrf::wdt;
|
use embassy_nrf::wdt;
|
||||||
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
|
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
|
||||||
@ -104,15 +106,15 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A flash implementation that wraps NVMC and will pet a watchdog when touching flash.
|
/// A flash implementation that wraps any flash and will pet a watchdog when touching flash.
|
||||||
pub struct WatchdogFlash<'d> {
|
pub struct WatchdogFlash<FLASH> {
|
||||||
flash: Nvmc<'d>,
|
flash: FLASH,
|
||||||
wdt: wdt::WatchdogHandle,
|
wdt: wdt::WatchdogHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> WatchdogFlash<'d> {
|
impl<FLASH> WatchdogFlash<FLASH> {
|
||||||
/// Start a new watchdog with a given flash and WDT peripheral and a timeout
|
/// Start a new watchdog with a given flash and WDT peripheral and a timeout
|
||||||
pub fn start(flash: Nvmc<'d>, wdt: WDT, config: wdt::Config) -> Self {
|
pub fn start(flash: FLASH, wdt: WDT, config: wdt::Config) -> Self {
|
||||||
let (_wdt, [wdt]) = match wdt::Watchdog::try_new(wdt, config) {
|
let (_wdt, [wdt]) = match wdt::Watchdog::try_new(wdt, config) {
|
||||||
Ok(x) => x,
|
Ok(x) => x,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
@ -127,13 +129,13 @@ impl<'d> WatchdogFlash<'d> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> ErrorType for WatchdogFlash<'d> {
|
impl<FLASH: ErrorType> ErrorType for WatchdogFlash<FLASH> {
|
||||||
type Error = <Nvmc<'d> as ErrorType>::Error;
|
type Error = FLASH::Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> NorFlash for WatchdogFlash<'d> {
|
impl<FLASH: NorFlash> NorFlash for WatchdogFlash<FLASH> {
|
||||||
const WRITE_SIZE: usize = <Nvmc<'d> as NorFlash>::WRITE_SIZE;
|
const WRITE_SIZE: usize = FLASH::WRITE_SIZE;
|
||||||
const ERASE_SIZE: usize = <Nvmc<'d> as NorFlash>::ERASE_SIZE;
|
const ERASE_SIZE: usize = FLASH::ERASE_SIZE;
|
||||||
|
|
||||||
fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
|
fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
|
||||||
self.wdt.pet();
|
self.wdt.pet();
|
||||||
@ -145,8 +147,8 @@ impl<'d> NorFlash for WatchdogFlash<'d> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> ReadNorFlash for WatchdogFlash<'d> {
|
impl<FLASH: ReadNorFlash> ReadNorFlash for WatchdogFlash<FLASH> {
|
||||||
const READ_SIZE: usize = <Nvmc<'d> as ReadNorFlash>::READ_SIZE;
|
const READ_SIZE: usize = FLASH::READ_SIZE;
|
||||||
fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> {
|
fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
self.wdt.pet();
|
self.wdt.pet();
|
||||||
self.flash.read(offset, data)
|
self.flash.read(offset, data)
|
||||||
|
@ -3,10 +3,12 @@
|
|||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
mod fmt;
|
mod fmt;
|
||||||
|
|
||||||
|
pub use embassy_boot::{
|
||||||
|
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State,
|
||||||
|
};
|
||||||
#[cfg(feature = "nightly")]
|
#[cfg(feature = "nightly")]
|
||||||
pub use embassy_boot::FirmwareUpdater;
|
pub use embassy_boot::{FirmwareState, FirmwareUpdater};
|
||||||
pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State};
|
use embassy_rp::flash::{Blocking, Flash, ERASE_SIZE};
|
||||||
use embassy_rp::flash::{Flash, ERASE_SIZE};
|
|
||||||
use embassy_rp::peripherals::{FLASH, WATCHDOG};
|
use embassy_rp::peripherals::{FLASH, WATCHDOG};
|
||||||
use embassy_rp::watchdog::Watchdog;
|
use embassy_rp::watchdog::Watchdog;
|
||||||
use embassy_time::Duration;
|
use embassy_time::Duration;
|
||||||
@ -58,14 +60,14 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>
|
|||||||
|
|
||||||
/// A flash implementation that will feed a watchdog when touching flash.
|
/// A flash implementation that will feed a watchdog when touching flash.
|
||||||
pub struct WatchdogFlash<'d, const SIZE: usize> {
|
pub struct WatchdogFlash<'d, const SIZE: usize> {
|
||||||
flash: Flash<'d, FLASH, SIZE>,
|
flash: Flash<'d, FLASH, Blocking, SIZE>,
|
||||||
watchdog: Watchdog,
|
watchdog: Watchdog,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, const SIZE: usize> WatchdogFlash<'d, SIZE> {
|
impl<'d, const SIZE: usize> WatchdogFlash<'d, SIZE> {
|
||||||
/// Start a new watchdog with a given flash and watchdog peripheral and a timeout
|
/// Start a new watchdog with a given flash and watchdog peripheral and a timeout
|
||||||
pub fn start(flash: FLASH, watchdog: WATCHDOG, timeout: Duration) -> Self {
|
pub fn start(flash: FLASH, watchdog: WATCHDOG, timeout: Duration) -> Self {
|
||||||
let flash: Flash<'_, FLASH, SIZE> = Flash::new(flash);
|
let flash = Flash::<_, Blocking, SIZE>::new(flash);
|
||||||
let mut watchdog = Watchdog::new(watchdog);
|
let mut watchdog = Watchdog::new(watchdog);
|
||||||
watchdog.start(timeout);
|
watchdog.start(timeout);
|
||||||
Self { flash, watchdog }
|
Self { flash, watchdog }
|
||||||
@ -73,12 +75,12 @@ impl<'d, const SIZE: usize> WatchdogFlash<'d, SIZE> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, const SIZE: usize> ErrorType for WatchdogFlash<'d, SIZE> {
|
impl<'d, const SIZE: usize> ErrorType for WatchdogFlash<'d, SIZE> {
|
||||||
type Error = <Flash<'d, FLASH, SIZE> as ErrorType>::Error;
|
type Error = <Flash<'d, FLASH, Blocking, SIZE> as ErrorType>::Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, const SIZE: usize> NorFlash for WatchdogFlash<'d, SIZE> {
|
impl<'d, const SIZE: usize> NorFlash for WatchdogFlash<'d, SIZE> {
|
||||||
const WRITE_SIZE: usize = <Flash<'d, FLASH, SIZE> as NorFlash>::WRITE_SIZE;
|
const WRITE_SIZE: usize = <Flash<'d, FLASH, Blocking, SIZE> as NorFlash>::WRITE_SIZE;
|
||||||
const ERASE_SIZE: usize = <Flash<'d, FLASH, SIZE> as NorFlash>::ERASE_SIZE;
|
const ERASE_SIZE: usize = <Flash<'d, FLASH, Blocking, SIZE> as NorFlash>::ERASE_SIZE;
|
||||||
|
|
||||||
fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
|
fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
|
||||||
self.watchdog.feed();
|
self.watchdog.feed();
|
||||||
@ -91,7 +93,7 @@ impl<'d, const SIZE: usize> NorFlash for WatchdogFlash<'d, SIZE> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, const SIZE: usize> ReadNorFlash for WatchdogFlash<'d, SIZE> {
|
impl<'d, const SIZE: usize> ReadNorFlash for WatchdogFlash<'d, SIZE> {
|
||||||
const READ_SIZE: usize = <Flash<'d, FLASH, SIZE> as ReadNorFlash>::READ_SIZE;
|
const READ_SIZE: usize = <Flash<'d, FLASH, Blocking, SIZE> as ReadNorFlash>::READ_SIZE;
|
||||||
fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> {
|
fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
self.watchdog.feed();
|
self.watchdog.feed();
|
||||||
self.flash.read(offset, data)
|
self.flash.read(offset, data)
|
||||||
|
@ -3,9 +3,11 @@
|
|||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
mod fmt;
|
mod fmt;
|
||||||
|
|
||||||
|
pub use embassy_boot::{
|
||||||
|
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State,
|
||||||
|
};
|
||||||
#[cfg(feature = "nightly")]
|
#[cfg(feature = "nightly")]
|
||||||
pub use embassy_boot::FirmwareUpdater;
|
pub use embassy_boot::{FirmwareState, FirmwareUpdater};
|
||||||
pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State};
|
|
||||||
use embedded_storage::nor_flash::NorFlash;
|
use embedded_storage::nor_flash::NorFlash;
|
||||||
|
|
||||||
/// A bootloader for STM32 devices.
|
/// A bootloader for STM32 devices.
|
||||||
|
@ -15,15 +15,18 @@ target = "x86_64-unknown-linux-gnu"
|
|||||||
std = []
|
std = []
|
||||||
# Enable nightly-only features
|
# Enable nightly-only features
|
||||||
nightly = ["embassy-futures", "embedded-hal-async", "embedded-storage-async"]
|
nightly = ["embassy-futures", "embedded-hal-async", "embedded-storage-async"]
|
||||||
|
time = ["dep:embassy-time"]
|
||||||
|
default = ["time"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures", optional = true }
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures", optional = true }
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
||||||
|
embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true }
|
||||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [
|
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [
|
||||||
"unproven",
|
"unproven",
|
||||||
] }
|
] }
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" }
|
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" }
|
||||||
embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true }
|
embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true }
|
||||||
embedded-storage = "0.3.0"
|
embedded-storage = "0.3.0"
|
||||||
embedded-storage-async = { version = "0.4.0", optional = true }
|
embedded-storage-async = { version = "0.4.0", optional = true }
|
||||||
nb = "1.0.0"
|
nb = "1.0.0"
|
||||||
|
@ -74,7 +74,21 @@ where
|
|||||||
E: embedded_hal_1::spi::Error + 'static,
|
E: embedded_hal_1::spi::Error + 'static,
|
||||||
T: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>,
|
T: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>,
|
||||||
{
|
{
|
||||||
async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> {
|
async fn flush(&mut self) -> Result<(), Self::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> {
|
||||||
|
self.wrapped.write(data)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
|
self.wrapped.transfer(data)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
|
||||||
// Ensure we write the expected bytes
|
// Ensure we write the expected bytes
|
||||||
for i in 0..core::cmp::min(read.len(), write.len()) {
|
for i in 0..core::cmp::min(read.len(), write.len()) {
|
||||||
read[i] = write[i].clone();
|
read[i] = write[i].clone();
|
||||||
@ -83,38 +97,7 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn transfer_in_place<'a>(&'a mut self, _: &'a mut [u8]) -> Result<(), Self::Error> {
|
async fn transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, E> embedded_hal_async::spi::SpiBusFlush for BlockingAsync<T>
|
|
||||||
where
|
|
||||||
E: embedded_hal_1::spi::Error + 'static,
|
|
||||||
T: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>,
|
|
||||||
{
|
|
||||||
async fn flush(&mut self) -> Result<(), Self::Error> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, E> embedded_hal_async::spi::SpiBusWrite<u8> for BlockingAsync<T>
|
|
||||||
where
|
|
||||||
E: embedded_hal_1::spi::Error + 'static,
|
|
||||||
T: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>,
|
|
||||||
{
|
|
||||||
async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> {
|
|
||||||
self.wrapped.write(data)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, E> embedded_hal_async::spi::SpiBusRead<u8> for BlockingAsync<T>
|
|
||||||
where
|
|
||||||
E: embedded_hal_1::spi::Error + 'static,
|
|
||||||
T: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>,
|
|
||||||
{
|
|
||||||
async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
|
|
||||||
self.wrapped.transfer(data)?;
|
self.wrapped.transfer(data)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -69,51 +69,36 @@ where
|
|||||||
type Error = T::Error;
|
type Error = T::Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> embedded_hal_async::spi::SpiBus<u8> for YieldingAsync<T>
|
impl<T, Word: 'static + Copy> embedded_hal_async::spi::SpiBus<Word> for YieldingAsync<T>
|
||||||
where
|
where
|
||||||
T: embedded_hal_async::spi::SpiBus,
|
T: embedded_hal_async::spi::SpiBus<Word>,
|
||||||
{
|
|
||||||
async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> {
|
|
||||||
self.wrapped.transfer(read, write).await?;
|
|
||||||
yield_now().await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Result<(), Self::Error> {
|
|
||||||
self.wrapped.transfer_in_place(words).await?;
|
|
||||||
yield_now().await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> embedded_hal_async::spi::SpiBusFlush for YieldingAsync<T>
|
|
||||||
where
|
|
||||||
T: embedded_hal_async::spi::SpiBusFlush,
|
|
||||||
{
|
{
|
||||||
async fn flush(&mut self) -> Result<(), Self::Error> {
|
async fn flush(&mut self) -> Result<(), Self::Error> {
|
||||||
self.wrapped.flush().await?;
|
self.wrapped.flush().await?;
|
||||||
yield_now().await;
|
yield_now().await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> embedded_hal_async::spi::SpiBusWrite<u8> for YieldingAsync<T>
|
async fn write(&mut self, data: &[Word]) -> Result<(), Self::Error> {
|
||||||
where
|
|
||||||
T: embedded_hal_async::spi::SpiBusWrite<u8>,
|
|
||||||
{
|
|
||||||
async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> {
|
|
||||||
self.wrapped.write(data).await?;
|
self.wrapped.write(data).await?;
|
||||||
yield_now().await;
|
yield_now().await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn read(&mut self, data: &mut [Word]) -> Result<(), Self::Error> {
|
||||||
|
self.wrapped.read(data).await?;
|
||||||
|
yield_now().await;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> embedded_hal_async::spi::SpiBusRead<u8> for YieldingAsync<T>
|
async fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> {
|
||||||
where
|
self.wrapped.transfer(read, write).await?;
|
||||||
T: embedded_hal_async::spi::SpiBusRead<u8>,
|
yield_now().await;
|
||||||
{
|
Ok(())
|
||||||
async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
|
}
|
||||||
self.wrapped.read(data).await?;
|
|
||||||
|
async fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
|
||||||
|
self.wrapped.transfer_in_place(words).await?;
|
||||||
yield_now().await;
|
yield_now().await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -56,62 +56,6 @@ where
|
|||||||
type Error = SpiDeviceError<BUS::Error, CS::Error>;
|
type Error = SpiDeviceError<BUS::Error, CS::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M, BUS, CS> spi::SpiDeviceRead for SpiDevice<'_, M, BUS, CS>
|
|
||||||
where
|
|
||||||
M: RawMutex,
|
|
||||||
BUS: spi::SpiBusRead,
|
|
||||||
CS: OutputPin,
|
|
||||||
{
|
|
||||||
async fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> {
|
|
||||||
let mut bus = self.bus.lock().await;
|
|
||||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
|
||||||
|
|
||||||
let op_res: Result<(), BUS::Error> = try {
|
|
||||||
for buf in operations {
|
|
||||||
bus.read(buf).await?;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// On failure, it's important to still flush and deassert CS.
|
|
||||||
let flush_res = bus.flush().await;
|
|
||||||
let cs_res = self.cs.set_high();
|
|
||||||
|
|
||||||
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
|
|
||||||
flush_res.map_err(SpiDeviceError::Spi)?;
|
|
||||||
cs_res.map_err(SpiDeviceError::Cs)?;
|
|
||||||
|
|
||||||
Ok(op_res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<M, BUS, CS> spi::SpiDeviceWrite for SpiDevice<'_, M, BUS, CS>
|
|
||||||
where
|
|
||||||
M: RawMutex,
|
|
||||||
BUS: spi::SpiBusWrite,
|
|
||||||
CS: OutputPin,
|
|
||||||
{
|
|
||||||
async fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> {
|
|
||||||
let mut bus = self.bus.lock().await;
|
|
||||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
|
||||||
|
|
||||||
let op_res: Result<(), BUS::Error> = try {
|
|
||||||
for buf in operations {
|
|
||||||
bus.write(buf).await?;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// On failure, it's important to still flush and deassert CS.
|
|
||||||
let flush_res = bus.flush().await;
|
|
||||||
let cs_res = self.cs.set_high();
|
|
||||||
|
|
||||||
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
|
|
||||||
flush_res.map_err(SpiDeviceError::Spi)?;
|
|
||||||
cs_res.map_err(SpiDeviceError::Cs)?;
|
|
||||||
|
|
||||||
Ok(op_res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<M, BUS, CS> spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
|
impl<M, BUS, CS> spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
|
||||||
where
|
where
|
||||||
M: RawMutex,
|
M: RawMutex,
|
||||||
@ -129,6 +73,12 @@ where
|
|||||||
Operation::Write(buf) => bus.write(buf).await?,
|
Operation::Write(buf) => bus.write(buf).await?,
|
||||||
Operation::Transfer(read, write) => bus.transfer(read, write).await?,
|
Operation::Transfer(read, write) => bus.transfer(read, write).await?,
|
||||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?,
|
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?,
|
||||||
|
#[cfg(not(feature = "time"))]
|
||||||
|
Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported),
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
Operation::DelayUs(us) => {
|
||||||
|
embassy_time::Timer::after(embassy_time::Duration::from_micros(*us as _)).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -172,64 +122,6 @@ where
|
|||||||
type Error = SpiDeviceError<BUS::Error, CS::Error>;
|
type Error = SpiDeviceError<BUS::Error, CS::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M, BUS, CS> spi::SpiDeviceWrite for SpiDeviceWithConfig<'_, M, BUS, CS>
|
|
||||||
where
|
|
||||||
M: RawMutex,
|
|
||||||
BUS: spi::SpiBusWrite + SetConfig,
|
|
||||||
CS: OutputPin,
|
|
||||||
{
|
|
||||||
async fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> {
|
|
||||||
let mut bus = self.bus.lock().await;
|
|
||||||
bus.set_config(&self.config);
|
|
||||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
|
||||||
|
|
||||||
let op_res: Result<(), BUS::Error> = try {
|
|
||||||
for buf in operations {
|
|
||||||
bus.write(buf).await?;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// On failure, it's important to still flush and deassert CS.
|
|
||||||
let flush_res = bus.flush().await;
|
|
||||||
let cs_res = self.cs.set_high();
|
|
||||||
|
|
||||||
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
|
|
||||||
flush_res.map_err(SpiDeviceError::Spi)?;
|
|
||||||
cs_res.map_err(SpiDeviceError::Cs)?;
|
|
||||||
|
|
||||||
Ok(op_res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<M, BUS, CS> spi::SpiDeviceRead for SpiDeviceWithConfig<'_, M, BUS, CS>
|
|
||||||
where
|
|
||||||
M: RawMutex,
|
|
||||||
BUS: spi::SpiBusRead + SetConfig,
|
|
||||||
CS: OutputPin,
|
|
||||||
{
|
|
||||||
async fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> {
|
|
||||||
let mut bus = self.bus.lock().await;
|
|
||||||
bus.set_config(&self.config);
|
|
||||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
|
||||||
|
|
||||||
let op_res: Result<(), BUS::Error> = try {
|
|
||||||
for buf in operations {
|
|
||||||
bus.read(buf).await?;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// On failure, it's important to still flush and deassert CS.
|
|
||||||
let flush_res = bus.flush().await;
|
|
||||||
let cs_res = self.cs.set_high();
|
|
||||||
|
|
||||||
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
|
|
||||||
flush_res.map_err(SpiDeviceError::Spi)?;
|
|
||||||
cs_res.map_err(SpiDeviceError::Cs)?;
|
|
||||||
|
|
||||||
Ok(op_res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<M, BUS, CS> spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
|
impl<M, BUS, CS> spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
|
||||||
where
|
where
|
||||||
M: RawMutex,
|
M: RawMutex,
|
||||||
@ -248,6 +140,12 @@ where
|
|||||||
Operation::Write(buf) => bus.write(buf).await?,
|
Operation::Write(buf) => bus.write(buf).await?,
|
||||||
Operation::Transfer(read, write) => bus.transfer(read, write).await?,
|
Operation::Transfer(read, write) => bus.transfer(read, write).await?,
|
||||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?,
|
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?,
|
||||||
|
#[cfg(not(feature = "time"))]
|
||||||
|
Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported),
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
Operation::DelayUs(us) => {
|
||||||
|
embassy_time::Timer::after(embassy_time::Duration::from_micros(*us as _)).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -22,7 +22,7 @@ use core::cell::RefCell;
|
|||||||
use embassy_sync::blocking_mutex::raw::RawMutex;
|
use embassy_sync::blocking_mutex::raw::RawMutex;
|
||||||
use embassy_sync::blocking_mutex::Mutex;
|
use embassy_sync::blocking_mutex::Mutex;
|
||||||
use embedded_hal_1::digital::OutputPin;
|
use embedded_hal_1::digital::OutputPin;
|
||||||
use embedded_hal_1::spi::{self, Operation, SpiBus, SpiBusRead, SpiBusWrite};
|
use embedded_hal_1::spi::{self, Operation, SpiBus};
|
||||||
|
|
||||||
use crate::shared_bus::SpiDeviceError;
|
use crate::shared_bus::SpiDeviceError;
|
||||||
use crate::SetConfig;
|
use crate::SetConfig;
|
||||||
@ -48,58 +48,6 @@ where
|
|||||||
type Error = SpiDeviceError<BUS::Error, CS::Error>;
|
type Error = SpiDeviceError<BUS::Error, CS::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceRead for SpiDevice<'_, M, BUS, CS>
|
|
||||||
where
|
|
||||||
M: RawMutex,
|
|
||||||
BUS: SpiBusRead,
|
|
||||||
CS: OutputPin,
|
|
||||||
{
|
|
||||||
fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> {
|
|
||||||
self.bus.lock(|bus| {
|
|
||||||
let mut bus = bus.borrow_mut();
|
|
||||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
|
||||||
|
|
||||||
let op_res = operations.iter_mut().try_for_each(|buf| bus.read(buf));
|
|
||||||
|
|
||||||
// On failure, it's important to still flush and deassert CS.
|
|
||||||
let flush_res = bus.flush();
|
|
||||||
let cs_res = self.cs.set_high();
|
|
||||||
|
|
||||||
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
|
|
||||||
flush_res.map_err(SpiDeviceError::Spi)?;
|
|
||||||
cs_res.map_err(SpiDeviceError::Cs)?;
|
|
||||||
|
|
||||||
Ok(op_res)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceWrite for SpiDevice<'_, M, BUS, CS>
|
|
||||||
where
|
|
||||||
M: RawMutex,
|
|
||||||
BUS: SpiBusWrite,
|
|
||||||
CS: OutputPin,
|
|
||||||
{
|
|
||||||
fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> {
|
|
||||||
self.bus.lock(|bus| {
|
|
||||||
let mut bus = bus.borrow_mut();
|
|
||||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
|
||||||
|
|
||||||
let op_res = operations.iter().try_for_each(|buf| bus.write(buf));
|
|
||||||
|
|
||||||
// On failure, it's important to still flush and deassert CS.
|
|
||||||
let flush_res = bus.flush();
|
|
||||||
let cs_res = self.cs.set_high();
|
|
||||||
|
|
||||||
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
|
|
||||||
flush_res.map_err(SpiDeviceError::Spi)?;
|
|
||||||
cs_res.map_err(SpiDeviceError::Cs)?;
|
|
||||||
|
|
||||||
Ok(op_res)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
|
impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
|
||||||
where
|
where
|
||||||
M: RawMutex,
|
M: RawMutex,
|
||||||
@ -116,6 +64,13 @@ where
|
|||||||
Operation::Write(buf) => bus.write(buf),
|
Operation::Write(buf) => bus.write(buf),
|
||||||
Operation::Transfer(read, write) => bus.transfer(read, write),
|
Operation::Transfer(read, write) => bus.transfer(read, write),
|
||||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
|
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
|
||||||
|
#[cfg(not(feature = "time"))]
|
||||||
|
Operation::DelayUs(_) => Err(SpiDeviceError::DelayUsNotSupported),
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
Operation::DelayUs(us) => {
|
||||||
|
embassy_time::block_for(embassy_time::Duration::from_micros(*us as _));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// On failure, it's important to still flush and deassert CS.
|
// On failure, it's important to still flush and deassert CS.
|
||||||
@ -199,58 +154,6 @@ where
|
|||||||
type Error = SpiDeviceError<BUS::Error, CS::Error>;
|
type Error = SpiDeviceError<BUS::Error, CS::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceRead for SpiDeviceWithConfig<'_, M, BUS, CS>
|
|
||||||
where
|
|
||||||
M: RawMutex,
|
|
||||||
BUS: SpiBusRead + SetConfig,
|
|
||||||
CS: OutputPin,
|
|
||||||
{
|
|
||||||
fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> {
|
|
||||||
self.bus.lock(|bus| {
|
|
||||||
let mut bus = bus.borrow_mut();
|
|
||||||
bus.set_config(&self.config);
|
|
||||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
|
||||||
|
|
||||||
let op_res = operations.iter_mut().try_for_each(|buf| bus.read(buf));
|
|
||||||
|
|
||||||
// On failure, it's important to still flush and deassert CS.
|
|
||||||
let flush_res = bus.flush();
|
|
||||||
let cs_res = self.cs.set_high();
|
|
||||||
|
|
||||||
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
|
|
||||||
flush_res.map_err(SpiDeviceError::Spi)?;
|
|
||||||
cs_res.map_err(SpiDeviceError::Cs)?;
|
|
||||||
Ok(op_res)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceWrite for SpiDeviceWithConfig<'_, M, BUS, CS>
|
|
||||||
where
|
|
||||||
M: RawMutex,
|
|
||||||
BUS: SpiBusWrite + SetConfig,
|
|
||||||
CS: OutputPin,
|
|
||||||
{
|
|
||||||
fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> {
|
|
||||||
self.bus.lock(|bus| {
|
|
||||||
let mut bus = bus.borrow_mut();
|
|
||||||
bus.set_config(&self.config);
|
|
||||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
|
||||||
|
|
||||||
let op_res = operations.iter().try_for_each(|buf| bus.write(buf));
|
|
||||||
|
|
||||||
// On failure, it's important to still flush and deassert CS.
|
|
||||||
let flush_res = bus.flush();
|
|
||||||
let cs_res = self.cs.set_high();
|
|
||||||
|
|
||||||
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
|
|
||||||
flush_res.map_err(SpiDeviceError::Spi)?;
|
|
||||||
cs_res.map_err(SpiDeviceError::Cs)?;
|
|
||||||
Ok(op_res)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
|
impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
|
||||||
where
|
where
|
||||||
M: RawMutex,
|
M: RawMutex,
|
||||||
@ -268,6 +171,13 @@ where
|
|||||||
Operation::Write(buf) => bus.write(buf),
|
Operation::Write(buf) => bus.write(buf),
|
||||||
Operation::Transfer(read, write) => bus.transfer(read, write),
|
Operation::Transfer(read, write) => bus.transfer(read, write),
|
||||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
|
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
|
||||||
|
#[cfg(not(feature = "time"))]
|
||||||
|
Operation::DelayUs(_) => Err(SpiDeviceError::DelayUsNotSupported),
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
Operation::DelayUs(us) => {
|
||||||
|
embassy_time::block_for(embassy_time::Duration::from_micros(*us as _));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// On failure, it's important to still flush and deassert CS.
|
// On failure, it's important to still flush and deassert CS.
|
||||||
|
@ -30,11 +30,14 @@ where
|
|||||||
/// Error returned by SPI device implementations in this crate.
|
/// Error returned by SPI device implementations in this crate.
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum SpiDeviceError<BUS, CS> {
|
pub enum SpiDeviceError<BUS, CS> {
|
||||||
/// An operation on the inner SPI bus failed.
|
/// An operation on the inner SPI bus failed.
|
||||||
Spi(BUS),
|
Spi(BUS),
|
||||||
/// Setting the value of the Chip Select (CS) pin failed.
|
/// Setting the value of the Chip Select (CS) pin failed.
|
||||||
Cs(CS),
|
Cs(CS),
|
||||||
|
/// DelayUs operations are not supported when the `time` Cargo feature is not enabled.
|
||||||
|
DelayUsNotSupported,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<BUS, CS> spi::Error for SpiDeviceError<BUS, CS>
|
impl<BUS, CS> spi::Error for SpiDeviceError<BUS, CS>
|
||||||
@ -46,6 +49,7 @@ where
|
|||||||
match self {
|
match self {
|
||||||
Self::Spi(e) => e.kind(),
|
Self::Spi(e) => e.kind(),
|
||||||
Self::Cs(_) => spi::ErrorKind::Other,
|
Self::Cs(_) => spi::ErrorKind::Other,
|
||||||
|
Self::DelayUsNotSupported => spi::ErrorKind::Other,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## 0.2.1 - 2023-08-10
|
||||||
|
|
||||||
|
- Avoid calling `pend()` when waking expired timers
|
||||||
|
- Properly reset finished task state with `integrated-timers` enabled
|
||||||
|
- Introduce `InterruptExecutor::spawner()`
|
||||||
|
- Fix incorrect critical section in Xtensa executor
|
||||||
|
|
||||||
## 0.2.0 - 2023-04-27
|
## 0.2.0 - 2023-04-27
|
||||||
|
|
||||||
- Replace unnecessary atomics in runqueue
|
- Replace unnecessary atomics in runqueue
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "embassy-executor"
|
name = "embassy-executor"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
description = "async/await executor designed for embedded usage"
|
description = "async/await executor designed for embedded usage"
|
||||||
@ -62,7 +62,7 @@ rtos-trace = { version = "0.1.2", optional = true }
|
|||||||
|
|
||||||
futures-util = { version = "0.3.17", default-features = false }
|
futures-util = { version = "0.3.17", default-features = false }
|
||||||
embassy-macros = { version = "0.2.0", path = "../embassy-macros" }
|
embassy-macros = { version = "0.2.0", path = "../embassy-macros" }
|
||||||
embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true}
|
embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true}
|
||||||
atomic-polyfill = "1.0.1"
|
atomic-polyfill = "1.0.1"
|
||||||
critical-section = "1.1"
|
critical-section = "1.1"
|
||||||
static_cell = "1.1"
|
static_cell = "1.1"
|
||||||
|
@ -165,6 +165,9 @@ impl<F: Future + 'static> TaskStorage<F> {
|
|||||||
Poll::Ready(_) => {
|
Poll::Ready(_) => {
|
||||||
this.future.drop_in_place();
|
this.future.drop_in_place();
|
||||||
this.raw.state.fetch_and(!STATE_SPAWNED, Ordering::AcqRel);
|
this.raw.state.fetch_and(!STATE_SPAWNED, Ordering::AcqRel);
|
||||||
|
|
||||||
|
#[cfg(feature = "integrated-timers")]
|
||||||
|
this.raw.expires_at.set(Instant::MAX);
|
||||||
}
|
}
|
||||||
Poll::Pending => {}
|
Poll::Pending => {}
|
||||||
}
|
}
|
||||||
@ -406,7 +409,7 @@ impl SyncExecutor {
|
|||||||
#[allow(clippy::never_loop)]
|
#[allow(clippy::never_loop)]
|
||||||
loop {
|
loop {
|
||||||
#[cfg(feature = "integrated-timers")]
|
#[cfg(feature = "integrated-timers")]
|
||||||
self.timer_queue.dequeue_expired(Instant::now(), |task| wake_task(task));
|
self.timer_queue.dequeue_expired(Instant::now(), wake_task_no_pend);
|
||||||
|
|
||||||
self.run_queue.dequeue_all(|p| {
|
self.run_queue.dequeue_all(|p| {
|
||||||
let task = p.header();
|
let task = p.header();
|
||||||
@ -570,6 +573,31 @@ pub fn wake_task(task: TaskRef) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Wake a task by `TaskRef` without calling pend.
|
||||||
|
///
|
||||||
|
/// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`].
|
||||||
|
pub fn wake_task_no_pend(task: TaskRef) {
|
||||||
|
let header = task.header();
|
||||||
|
|
||||||
|
let res = header.state.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| {
|
||||||
|
// If already scheduled, or if not started,
|
||||||
|
if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
// Mark it as scheduled
|
||||||
|
Some(state | STATE_RUN_QUEUED)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if res.is_ok() {
|
||||||
|
// We have just marked the task as scheduled, so enqueue it.
|
||||||
|
unsafe {
|
||||||
|
let executor = header.executor.get().unwrap_unchecked();
|
||||||
|
executor.run_queue.enqueue(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "integrated-timers")]
|
#[cfg(feature = "integrated-timers")]
|
||||||
struct TimerQueue;
|
struct TimerQueue;
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "embassy-hal-common"
|
name = "embassy-hal-internal"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
16
embassy-hal-internal/README.md
Normal file
16
embassy-hal-internal/README.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# embassy-macros
|
||||||
|
|
||||||
|
An [Embassy](https://embassy.dev) project.
|
||||||
|
|
||||||
|
Internal implementation details for Embassy HALs. DO NOT USE DIRECTLY. Embassy HALs (`embassy-nrf`, `embassy-stm32`, `embassy-rp`) already reexport
|
||||||
|
everything you need to use them effectively.
|
||||||
|
|
||||||
|
## 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.
|
@ -1,5 +1,6 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![allow(clippy::new_without_default)]
|
#![allow(clippy::new_without_default)]
|
||||||
|
#![doc = include_str!("../README.md")]
|
||||||
|
|
||||||
// This mod MUST go first, so that the others see its macros.
|
// This mod MUST go first, so that the others see its macros.
|
||||||
pub(crate) mod fmt;
|
pub(crate) mod fmt;
|
@ -116,6 +116,7 @@ macro_rules! impl_peripheral {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn clone_unchecked(&self) -> Self::P {
|
unsafe fn clone_unchecked(&self) -> Self::P {
|
||||||
|
#[allow(clippy::needless_update)]
|
||||||
$type { ..*self }
|
$type { ..*self }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -161,7 +161,7 @@ pub trait Peripheral: Sized {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'b, T: Deref> Peripheral for T
|
impl<'b, T: DerefMut> Peripheral for T
|
||||||
where
|
where
|
||||||
T::Target: Peripheral,
|
T::Target: Peripheral,
|
||||||
{
|
{
|
@ -12,7 +12,7 @@ target = "thumbv7em-none-eabi"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
stm32wl = ["dep:embassy-stm32"]
|
stm32wl = ["dep:embassy-stm32"]
|
||||||
time = []
|
time = ["embassy-time", "lorawan-device"]
|
||||||
defmt = ["dep:defmt", "lorawan-device/defmt"]
|
defmt = ["dep:defmt", "lorawan-device/defmt"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
@ -20,15 +20,12 @@ defmt = ["dep:defmt", "lorawan-device/defmt"]
|
|||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
log = { version = "0.4.14", optional = true }
|
log = { version = "0.4.14", optional = true }
|
||||||
|
|
||||||
embassy-time = { version = "0.1.0", path = "../embassy-time" }
|
embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true }
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
||||||
embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true }
|
embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true }
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" }
|
embedded-hal-async = { version = "=0.2.0-alpha.2" }
|
||||||
embedded-hal-async = { version = "=0.2.0-alpha.1" }
|
|
||||||
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"] }
|
embedded-hal = { version = "0.2", features = ["unproven"] }
|
||||||
bit_field = { version = "0.10" }
|
|
||||||
|
|
||||||
|
futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
|
||||||
lora-phy = { version = "1" }
|
lora-phy = { version = "1" }
|
||||||
lorawan-device = { version = "0.10.0", default-features = false, features = ["async"] }
|
lorawan-device = { version = "0.10.0", default-features = false, features = ["async"], optional = true }
|
||||||
|
@ -2,6 +2,14 @@
|
|||||||
name = "embassy-net-driver-channel"
|
name = "embassy-net-driver-channel"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
description = "High-level channel-based driver for the `embassy-net` async TCP/IP network stack."
|
||||||
|
repository = "https://github.com/embassy-rs/embassy"
|
||||||
|
categories = [
|
||||||
|
"embedded",
|
||||||
|
"no-std",
|
||||||
|
"asynchronous",
|
||||||
|
]
|
||||||
|
|
||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-channel-v$VERSION/embassy-net-driver-channel/src/"
|
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-channel-v$VERSION/embassy-net-driver-channel/src/"
|
||||||
@ -9,6 +17,9 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-d
|
|||||||
features = ["defmt"]
|
features = ["defmt"]
|
||||||
target = "thumbv7em-none-eabi"
|
target = "thumbv7em-none-eabi"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
features = ["defmt"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
log = { version = "0.4.14", optional = true }
|
log = { version = "0.4.14", optional = true }
|
||||||
|
96
embassy-net-driver-channel/README.md
Normal file
96
embassy-net-driver-channel/README.md
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
# embassy-net-driver-channel
|
||||||
|
|
||||||
|
This crate provides a toolkit for implementing [`embassy-net`](https://crates.io/crates/embassy-net) drivers in a
|
||||||
|
higher level way than implementing the [`embassy-net-driver`](https://crates.io/crates/embassy-net-driver) trait directly.
|
||||||
|
|
||||||
|
The `embassy-net-driver` trait is polling-based. To implement it, you must write the packet receive/transmit state machines by
|
||||||
|
hand, and hook up the `Waker`s provided by `embassy-net` to the right interrupt handlers so that `embassy-net`
|
||||||
|
knows when to poll your driver again to make more progress.
|
||||||
|
|
||||||
|
With `embassy-net-driver-channel`
|
||||||
|
|
||||||
|
## A note about deadlocks
|
||||||
|
|
||||||
|
When implementing a driver using this crate, it might be tempting to write it in the most straightforward way:
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
loop {
|
||||||
|
// Wait for either..
|
||||||
|
match select(
|
||||||
|
// ... the chip signaling an interrupt, indicating a packet is available to receive, or
|
||||||
|
irq_pin.wait_for_low(),
|
||||||
|
// ... a TX buffer becoming available, i.e. embassy-net wants to send a packet
|
||||||
|
tx_chan.tx_buf(),
|
||||||
|
).await {
|
||||||
|
Either::First(_) => {
|
||||||
|
// a packet is ready to be received!
|
||||||
|
let buf = rx_chan.rx_buf().await; // allocate a rx buf from the packet queue
|
||||||
|
let n = receive_packet_over_spi(buf).await;
|
||||||
|
rx_chan.rx_done(n);
|
||||||
|
}
|
||||||
|
Either::Second(buf) => {
|
||||||
|
// a packet is ready to be sent!
|
||||||
|
send_packet_over_spi(buf).await;
|
||||||
|
tx_chan.tx_done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
However, this code has a latent deadlock bug. The symptom is it can hang at `rx_chan.rx_buf().await` under load.
|
||||||
|
|
||||||
|
The reason is that, under load, both the TX and RX queues can get full at the same time. When this happens, the `embassy-net` task stalls trying to send because the TX queue is full, therefore it stops processing packets in the RX queue. Your driver task also stalls because the RX queue is full, therefore it stops processing packets in the TX queue.
|
||||||
|
|
||||||
|
The fix is to make sure to always service the TX queue while you're waiting for space to become available in the TX queue. For example, select on either "tx_chan.tx_buf() available" or "INT is low AND rx_chan.rx_buf() available":
|
||||||
|
|
||||||
|
```rust,ignore
|
||||||
|
loop {
|
||||||
|
// Wait for either..
|
||||||
|
match select(
|
||||||
|
async {
|
||||||
|
// ... the chip signaling an interrupt, indicating a packet is available to receive
|
||||||
|
irq_pin.wait_for_low().await;
|
||||||
|
// *AND* the buffer is ready...
|
||||||
|
rx_chan.rx_buf().await
|
||||||
|
},
|
||||||
|
// ... or a TX buffer becoming available, i.e. embassy-net wants to send a packet
|
||||||
|
tx_chan.tx_buf(),
|
||||||
|
).await {
|
||||||
|
Either::First(buf) => {
|
||||||
|
// a packet is ready to be received!
|
||||||
|
let n = receive_packet_over_spi(buf).await;
|
||||||
|
rx_chan.rx_done(n);
|
||||||
|
}
|
||||||
|
Either::Second(buf) => {
|
||||||
|
// a packet is ready to be sent!
|
||||||
|
send_packet_over_spi(buf).await;
|
||||||
|
tx_chan.tx_done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
These `embassy-net` drivers are implemented using this crate. You can look at them for inspiration.
|
||||||
|
|
||||||
|
- [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W
|
||||||
|
- [`embassy-usb`](https://github.com/embassy-rs/embassy/tree/main/embassy-usb) for Ethernet-over-USB (CDC NCM) support.
|
||||||
|
- [`embassy-net-w5500`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-w5500) for Wiznet W5500 SPI Ethernet MAC+PHY chip.
|
||||||
|
- [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU.
|
||||||
|
|
||||||
|
|
||||||
|
## Interoperability
|
||||||
|
|
||||||
|
This crate can run on any executor.
|
||||||
|
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This work is licensed under either of
|
||||||
|
|
||||||
|
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
at your option.
|
@ -1,4 +1,5 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
#![doc = include_str!("../README.md")]
|
||||||
|
|
||||||
// must go first!
|
// must go first!
|
||||||
mod fmt;
|
mod fmt;
|
||||||
@ -41,7 +42,7 @@ struct StateInner<'d, const MTU: usize> {
|
|||||||
struct Shared {
|
struct Shared {
|
||||||
link_state: LinkState,
|
link_state: LinkState,
|
||||||
waker: WakerRegistration,
|
waker: WakerRegistration,
|
||||||
ethernet_address: [u8; 6],
|
hardware_address: driver::HardwareAddress,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Runner<'d, const MTU: usize> {
|
pub struct Runner<'d, const MTU: usize> {
|
||||||
@ -84,10 +85,10 @@ impl<'d, const MTU: usize> Runner<'d, MTU> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_ethernet_address(&mut self, address: [u8; 6]) {
|
pub fn set_hardware_address(&mut self, address: driver::HardwareAddress) {
|
||||||
self.shared.lock(|s| {
|
self.shared.lock(|s| {
|
||||||
let s = &mut *s.borrow_mut();
|
let s = &mut *s.borrow_mut();
|
||||||
s.ethernet_address = address;
|
s.hardware_address = address;
|
||||||
s.waker.wake();
|
s.waker.wake();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -149,7 +150,15 @@ impl<'d> StateRunner<'d> {
|
|||||||
pub fn set_ethernet_address(&self, address: [u8; 6]) {
|
pub fn set_ethernet_address(&self, address: [u8; 6]) {
|
||||||
self.shared.lock(|s| {
|
self.shared.lock(|s| {
|
||||||
let s = &mut *s.borrow_mut();
|
let s = &mut *s.borrow_mut();
|
||||||
s.ethernet_address = address;
|
s.hardware_address = driver::HardwareAddress::Ethernet(address);
|
||||||
|
s.waker.wake();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_ieee802154_address(&self, address: [u8; 8]) {
|
||||||
|
self.shared.lock(|s| {
|
||||||
|
let s = &mut *s.borrow_mut();
|
||||||
|
s.hardware_address = driver::HardwareAddress::Ieee802154(address);
|
||||||
s.waker.wake();
|
s.waker.wake();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -205,7 +214,7 @@ impl<'d, const MTU: usize> TxRunner<'d, MTU> {
|
|||||||
|
|
||||||
pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>(
|
pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>(
|
||||||
state: &'d mut State<MTU, N_RX, N_TX>,
|
state: &'d mut State<MTU, N_RX, N_TX>,
|
||||||
ethernet_address: [u8; 6],
|
hardware_address: driver::HardwareAddress,
|
||||||
) -> (Runner<'d, MTU>, Device<'d, MTU>) {
|
) -> (Runner<'d, MTU>, Device<'d, MTU>) {
|
||||||
let mut caps = Capabilities::default();
|
let mut caps = Capabilities::default();
|
||||||
caps.max_transmission_unit = MTU;
|
caps.max_transmission_unit = MTU;
|
||||||
@ -221,7 +230,7 @@ pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>(
|
|||||||
tx: zerocopy_channel::Channel::new(&mut state.tx[..]),
|
tx: zerocopy_channel::Channel::new(&mut state.tx[..]),
|
||||||
shared: Mutex::new(RefCell::new(Shared {
|
shared: Mutex::new(RefCell::new(Shared {
|
||||||
link_state: LinkState::Down,
|
link_state: LinkState::Down,
|
||||||
ethernet_address,
|
hardware_address,
|
||||||
waker: WakerRegistration::new(),
|
waker: WakerRegistration::new(),
|
||||||
})),
|
})),
|
||||||
});
|
});
|
||||||
@ -288,8 +297,8 @@ impl<'d, const MTU: usize> embassy_net_driver::Driver for Device<'d, MTU> {
|
|||||||
self.caps.clone()
|
self.caps.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ethernet_address(&self) -> [u8; 6] {
|
fn hardware_address(&self) -> driver::HardwareAddress {
|
||||||
self.shared.lock(|s| s.borrow().ethernet_address)
|
self.shared.lock(|s| s.borrow().hardware_address)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn link_state(&mut self, cx: &mut Context) -> LinkState {
|
fn link_state(&mut self, cx: &mut Context) -> LinkState {
|
||||||
|
@ -3,7 +3,13 @@ name = "embassy-net-driver"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
description = "Driver trait for the `embassy-net` async TCP/IP network stack."
|
||||||
|
repository = "https://github.com/embassy-rs/embassy"
|
||||||
|
categories = [
|
||||||
|
"embedded",
|
||||||
|
"no-std",
|
||||||
|
"asynchronous",
|
||||||
|
]
|
||||||
|
|
||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-v$VERSION/embassy-net-driver/src/"
|
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-v$VERSION/embassy-net-driver/src/"
|
||||||
@ -11,5 +17,8 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-d
|
|||||||
features = ["defmt"]
|
features = ["defmt"]
|
||||||
target = "thumbv7em-none-eabi"
|
target = "thumbv7em-none-eabi"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
features = ["defmt"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
@ -1,5 +1,21 @@
|
|||||||
# embassy-net-driver
|
# embassy-net-driver
|
||||||
|
|
||||||
|
This crate contains the driver trait necessary for adding [`embassy-net`](https://crates.io/crates/embassy-net) support
|
||||||
|
for a new hardware platform.
|
||||||
|
|
||||||
|
If you want to *use* `embassy-net` with already made drivers, you should depend on the main `embassy-net` crate, not on this crate.
|
||||||
|
|
||||||
|
If you are writing a driver, you should depend only on this crate, not on the main `embassy-net` crate.
|
||||||
|
This will allow your driver to continue working for newer `embassy-net` major versions, without needing an update,
|
||||||
|
if the driver trait has not had breaking changes.
|
||||||
|
|
||||||
|
See also [`embassy-net-driver-channel`](https://crates.io/crates/embassy-net-driver-channel), which provides a higer-level API
|
||||||
|
to construct a driver that processes packets in its own background task and communicates with the `embassy-net` task via
|
||||||
|
packet queues for RX and TX.
|
||||||
|
|
||||||
|
## Interoperability
|
||||||
|
|
||||||
|
This crate can run on any executor.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
@ -4,6 +4,18 @@
|
|||||||
|
|
||||||
use core::task::Context;
|
use core::task::Context;
|
||||||
|
|
||||||
|
/// Representation of an hardware address, such as an Ethernet address or an IEEE802.15.4 address.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum HardwareAddress {
|
||||||
|
/// A six-octet Ethernet address
|
||||||
|
Ethernet([u8; 6]),
|
||||||
|
/// An eight-octet IEEE802.15.4 address
|
||||||
|
Ieee802154([u8; 8]),
|
||||||
|
/// Indicates that a Driver is IP-native, and has no hardware address
|
||||||
|
Ip,
|
||||||
|
}
|
||||||
|
|
||||||
/// Main `embassy-net` driver API.
|
/// Main `embassy-net` driver API.
|
||||||
///
|
///
|
||||||
/// This is essentially an interface for sending and receiving raw network frames.
|
/// This is essentially an interface for sending and receiving raw network frames.
|
||||||
@ -51,8 +63,8 @@ pub trait Driver {
|
|||||||
/// Get a description of device capabilities.
|
/// Get a description of device capabilities.
|
||||||
fn capabilities(&self) -> Capabilities;
|
fn capabilities(&self) -> Capabilities;
|
||||||
|
|
||||||
/// Get the device's Ethernet address.
|
/// Get the device's hardware address.
|
||||||
fn ethernet_address(&self) -> [u8; 6];
|
fn hardware_address(&self) -> HardwareAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + Driver> Driver for &mut T {
|
impl<T: ?Sized + Driver> Driver for &mut T {
|
||||||
@ -75,8 +87,8 @@ impl<T: ?Sized + Driver> Driver for &mut T {
|
|||||||
fn link_state(&mut self, cx: &mut Context) -> LinkState {
|
fn link_state(&mut self, cx: &mut Context) -> LinkState {
|
||||||
T::link_state(self, cx)
|
T::link_state(self, cx)
|
||||||
}
|
}
|
||||||
fn ethernet_address(&self) -> [u8; 6] {
|
fn hardware_address(&self) -> HardwareAddress {
|
||||||
T::ethernet_address(self)
|
T::hardware_address(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,6 +176,9 @@ pub enum Medium {
|
|||||||
///
|
///
|
||||||
/// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode.
|
/// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode.
|
||||||
Ip,
|
Ip,
|
||||||
|
|
||||||
|
/// IEEE 802_15_4 medium
|
||||||
|
Ieee802154,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Medium {
|
impl Default for Medium {
|
||||||
|
@ -7,14 +7,20 @@ edition = "2021"
|
|||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
log = { version = "0.4.14", optional = true }
|
log = { version = "0.4.14", optional = true }
|
||||||
|
|
||||||
embassy-time = { version = "0.1.0", path = "../embassy-time" }
|
embassy-time = { version = "0.1.2", path = "../embassy-time" }
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync"}
|
embassy-sync = { version = "0.2.0", path = "../embassy-sync"}
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
|
||||||
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
|
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
|
||||||
|
|
||||||
embedded-hal = { version = "1.0.0-alpha.10" }
|
embedded-hal = { version = "1.0.0-alpha.11" }
|
||||||
embedded-hal-async = { version = "=0.2.0-alpha.1" }
|
embedded-hal-async = { version = "=0.2.0-alpha.2" }
|
||||||
|
|
||||||
noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] }
|
noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] }
|
||||||
#noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] }
|
#noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] }
|
||||||
heapless = "0.7.16"
|
heapless = "0.7.16"
|
||||||
|
|
||||||
|
[package.metadata.embassy_docs]
|
||||||
|
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-esp-hosted-v$VERSION/embassy-net-esp-hosted/src/"
|
||||||
|
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-esp-hosted/src/"
|
||||||
|
target = "thumbv7em-none-eabi"
|
||||||
|
features = ["defmt"]
|
@ -1,5 +1,4 @@
|
|||||||
use ch::driver::LinkState;
|
use ch::driver::LinkState;
|
||||||
use defmt::Debug2Format;
|
|
||||||
use embassy_net_driver_channel as ch;
|
use embassy_net_driver_channel as ch;
|
||||||
use heapless::String;
|
use heapless::String;
|
||||||
|
|
||||||
@ -54,8 +53,9 @@ impl<'a> Control<'a> {
|
|||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
let resp = self.ioctl(req).await;
|
let resp = self.ioctl(req).await;
|
||||||
let proto::CtrlMsgPayload::RespConnectAp(resp) = resp.payload.unwrap() else { panic!("unexpected resp") };
|
let proto::CtrlMsgPayload::RespConnectAp(resp) = resp.payload.unwrap() else {
|
||||||
debug!("======= {:?}", Debug2Format(&resp));
|
panic!("unexpected resp")
|
||||||
|
};
|
||||||
assert_eq!(resp.resp, 0);
|
assert_eq!(resp.resp, 0);
|
||||||
self.state_ch.set_link_state(LinkState::Up);
|
self.state_ch.set_link_state(LinkState::Up);
|
||||||
}
|
}
|
||||||
@ -71,7 +71,9 @@ impl<'a> Control<'a> {
|
|||||||
)),
|
)),
|
||||||
};
|
};
|
||||||
let resp = self.ioctl(req).await;
|
let resp = self.ioctl(req).await;
|
||||||
let proto::CtrlMsgPayload::RespGetMacAddress(resp) = resp.payload.unwrap() else { panic!("unexpected resp") };
|
let proto::CtrlMsgPayload::RespGetMacAddress(resp) = resp.payload.unwrap() else {
|
||||||
|
panic!("unexpected resp")
|
||||||
|
};
|
||||||
assert_eq!(resp.resp, 0);
|
assert_eq!(resp.resp, 0);
|
||||||
|
|
||||||
// WHY IS THIS A STRING? WHYYYY
|
// WHY IS THIS A STRING? WHYYYY
|
||||||
@ -100,7 +102,9 @@ impl<'a> Control<'a> {
|
|||||||
payload: Some(proto::CtrlMsgPayload::ReqSetWifiMode(proto::CtrlMsgReqSetMode { mode })),
|
payload: Some(proto::CtrlMsgPayload::ReqSetWifiMode(proto::CtrlMsgReqSetMode { mode })),
|
||||||
};
|
};
|
||||||
let resp = self.ioctl(req).await;
|
let resp = self.ioctl(req).await;
|
||||||
let proto::CtrlMsgPayload::RespSetWifiMode(resp) = resp.payload.unwrap() else { panic!("unexpected resp") };
|
let proto::CtrlMsgPayload::RespSetWifiMode(resp) = resp.payload.unwrap() else {
|
||||||
|
panic!("unexpected resp")
|
||||||
|
};
|
||||||
assert_eq!(resp.resp, 0);
|
assert_eq!(resp.resp, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ where
|
|||||||
IN: InputPin + Wait,
|
IN: InputPin + Wait,
|
||||||
OUT: OutputPin,
|
OUT: OutputPin,
|
||||||
{
|
{
|
||||||
let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]);
|
let (ch_runner, device) = ch::new(&mut state.ch, ch::driver::HardwareAddress::Ethernet([0; 6]));
|
||||||
let state_ch = ch_runner.state_runner();
|
let state_ch = ch_runner.state_runner();
|
||||||
|
|
||||||
let mut runner = Runner {
|
let mut runner = Runner {
|
||||||
@ -311,14 +311,14 @@ where
|
|||||||
fn handle_event(&self, data: &[u8]) {
|
fn handle_event(&self, data: &[u8]) {
|
||||||
let Ok(event) = noproto::read::<CtrlMsg>(data) else {
|
let Ok(event) = noproto::read::<CtrlMsg>(data) else {
|
||||||
warn!("failed to parse event");
|
warn!("failed to parse event");
|
||||||
return
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("event: {:?}", &event);
|
debug!("event: {:?}", &event);
|
||||||
|
|
||||||
let Some(payload) = &event.payload else {
|
let Some(payload) = &event.payload else {
|
||||||
warn!("event without payload?");
|
warn!("event without payload?");
|
||||||
return
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
match payload {
|
match payload {
|
||||||
|
19
embassy-net-tuntap/Cargo.toml
Normal file
19
embassy-net-tuntap/Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
[package]
|
||||||
|
name = "embassy-net-tuntap"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "embassy-net driver for Linux TUN/TAP interfaces."
|
||||||
|
keywords = ["embedded", "tuntap", "embassy-net", "embedded-hal-async", "ethernet", "async"]
|
||||||
|
categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"]
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
|
||||||
|
async-io = "1.6.0"
|
||||||
|
log = "0.4.14"
|
||||||
|
libc = "0.2.101"
|
||||||
|
|
||||||
|
[package.metadata.embassy_docs]
|
||||||
|
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-tuntap-v$VERSION/embassy-net-tuntap/src/"
|
||||||
|
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-tuntap/src/"
|
||||||
|
target = "thumbv7em-none-eabi"
|
17
embassy-net-tuntap/README.md
Normal file
17
embassy-net-tuntap/README.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# `embassy-net` integration for Linux TUN/TAP interfaces.
|
||||||
|
|
||||||
|
[`embassy-net`](https://crates.io/crates/embassy-net) integration for for Linux TUN (IP medium) and TAP (Ethernet medium) interfaces.
|
||||||
|
|
||||||
|
## Interoperability
|
||||||
|
|
||||||
|
This crate can run on any executor.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This work is licensed under either of
|
||||||
|
|
||||||
|
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
at your option.
|
@ -4,7 +4,7 @@ use std::os::unix::io::{AsRawFd, RawFd};
|
|||||||
use std::task::Context;
|
use std::task::Context;
|
||||||
|
|
||||||
use async_io::Async;
|
use async_io::Async;
|
||||||
use embassy_net_driver::{self, Capabilities, Driver, LinkState};
|
use embassy_net_driver::{self, Capabilities, Driver, HardwareAddress, LinkState};
|
||||||
use log::*;
|
use log::*;
|
||||||
|
|
||||||
pub const SIOCGIFMTU: libc::c_ulong = 0x8921;
|
pub const SIOCGIFMTU: libc::c_ulong = 0x8921;
|
||||||
@ -19,6 +19,7 @@ const ETHERNET_HEADER_LEN: usize = 14;
|
|||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
struct ifreq {
|
struct ifreq {
|
||||||
ifr_name: [libc::c_char; libc::IF_NAMESIZE],
|
ifr_name: [libc::c_char; libc::IF_NAMESIZE],
|
||||||
ifr_data: libc::c_int, /* ifr_ifindex or ifr_mtu */
|
ifr_data: libc::c_int, /* ifr_ifindex or ifr_mtu */
|
||||||
@ -180,8 +181,8 @@ impl Driver for TunTapDevice {
|
|||||||
LinkState::Up
|
LinkState::Up
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ethernet_address(&self) -> [u8; 6] {
|
fn hardware_address(&self) -> HardwareAddress {
|
||||||
[0x02, 0x03, 0x04, 0x05, 0x06, 0x07]
|
HardwareAddress::Ethernet([0x02, 0x03, 0x04, 0x05, 0x06, 0x07])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,11 +8,11 @@ license = "MIT OR Apache-2.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embedded-hal = { version = "1.0.0-alpha.10" }
|
embedded-hal = { version = "1.0.0-alpha.11" }
|
||||||
embedded-hal-async = { version = "=0.2.0-alpha.1" }
|
embedded-hal-async = { version = "=0.2.0-alpha.2" }
|
||||||
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" }
|
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" }
|
||||||
embassy-time = { version = "0.1.0" }
|
embassy-time = { version = "0.1.2", path = "../embassy-time" }
|
||||||
embassy-futures = { version = "0.1.0" }
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
|
|
||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
|
//! [`embassy-net`](https://crates.io/crates/embassy-net) driver for the WIZnet W5500 ethernet chip.
|
||||||
#![no_std]
|
#![no_std]
|
||||||
/// [`embassy-net`](crates.io/crates/embassy-net) driver for the WIZnet W5500 ethernet chip.
|
|
||||||
mod device;
|
mod device;
|
||||||
mod socket;
|
mod socket;
|
||||||
mod spi;
|
mod spi;
|
||||||
@ -77,7 +78,7 @@ impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Obtain a driver for using the W5500 with [`embassy-net`](crates.io/crates/embassy-net).
|
/// Obtain a driver for using the W5500 with [`embassy-net`](https://crates.io/crates/embassy-net).
|
||||||
pub async fn new<'a, const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT: Wait, RST: OutputPin>(
|
pub async fn new<'a, const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT: Wait, RST: OutputPin>(
|
||||||
mac_addr: [u8; 6],
|
mac_addr: [u8; 6],
|
||||||
state: &'a mut State<N_RX, N_TX>,
|
state: &'a mut State<N_RX, N_TX>,
|
||||||
@ -95,7 +96,7 @@ pub async fn new<'a, const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT:
|
|||||||
|
|
||||||
let mac = W5500::new(spi_dev, mac_addr).await.unwrap();
|
let mac = W5500::new(spi_dev, mac_addr).await.unwrap();
|
||||||
|
|
||||||
let (runner, device) = ch::new(&mut state.ch_state, mac_addr);
|
let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ethernet(mac_addr));
|
||||||
(
|
(
|
||||||
device,
|
device,
|
||||||
Runner {
|
Runner {
|
||||||
|
@ -22,7 +22,11 @@ impl<SPI: SpiDevice> SpiInterface<SPI> {
|
|||||||
let address_phase = address.to_be_bytes();
|
let address_phase = address.to_be_bytes();
|
||||||
let control_phase = [(block as u8) << 3 | 0b0000_0100];
|
let control_phase = [(block as u8) << 3 | 0b0000_0100];
|
||||||
let data_phase = data;
|
let data_phase = data;
|
||||||
let operations = &[&address_phase[..], &control_phase, &data_phase];
|
let operations = &mut [
|
||||||
self.0.write_transaction(operations).await
|
Operation::Write(&address_phase[..]),
|
||||||
|
Operation::Write(&control_phase),
|
||||||
|
Operation::Write(&data_phase),
|
||||||
|
];
|
||||||
|
self.0.transaction(operations).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,16 +3,22 @@ name = "embassy-net"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
description = "Async TCP/IP network stack for embedded systems"
|
||||||
|
repository = "https://github.com/embassy-rs/embassy"
|
||||||
|
categories = [
|
||||||
|
"embedded",
|
||||||
|
"no-std",
|
||||||
|
"asynchronous",
|
||||||
|
]
|
||||||
|
|
||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/"
|
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/"
|
||||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/src/"
|
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/src/"
|
||||||
features = ["nightly", "unstable-traits", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "igmp"]
|
features = ["nightly", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp"]
|
||||||
target = "thumbv7em-none-eabi"
|
target = "thumbv7em-none-eabi"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = ["nightly", "unstable-traits", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "igmp"]
|
features = ["nightly", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "medium-ieee802154", "igmp"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
@ -20,8 +26,7 @@ std = []
|
|||||||
|
|
||||||
defmt = ["dep:defmt", "smoltcp/defmt", "embassy-net-driver/defmt"]
|
defmt = ["dep:defmt", "smoltcp/defmt", "embassy-net-driver/defmt"]
|
||||||
|
|
||||||
nightly = ["dep:embedded-io", "embedded-io?/async", "dep:embedded-nal-async"]
|
nightly = ["dep:embedded-io-async", "dep:embedded-nal-async"]
|
||||||
unstable-traits = []
|
|
||||||
|
|
||||||
udp = ["smoltcp/socket-udp"]
|
udp = ["smoltcp/socket-udp"]
|
||||||
tcp = ["smoltcp/socket-tcp"]
|
tcp = ["smoltcp/socket-tcp"]
|
||||||
@ -31,6 +36,7 @@ proto-ipv4 = ["smoltcp/proto-ipv4"]
|
|||||||
proto-ipv6 = ["smoltcp/proto-ipv6"]
|
proto-ipv6 = ["smoltcp/proto-ipv6"]
|
||||||
medium-ethernet = ["smoltcp/medium-ethernet"]
|
medium-ethernet = ["smoltcp/medium-ethernet"]
|
||||||
medium-ip = ["smoltcp/medium-ip"]
|
medium-ip = ["smoltcp/medium-ip"]
|
||||||
|
medium-ieee802154 = ["smoltcp/medium-ieee802154"]
|
||||||
igmp = ["smoltcp/proto-igmp"]
|
igmp = ["smoltcp/proto-igmp"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
@ -44,10 +50,9 @@ smoltcp = { version = "0.10.0", default-features = false, features = [
|
|||||||
] }
|
] }
|
||||||
|
|
||||||
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
|
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
|
||||||
embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" }
|
embassy-time = { version = "0.1.2", path = "../embassy-time" }
|
||||||
embassy-time = { version = "0.1.0", path = "../embassy-time" }
|
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
||||||
embedded-io = { version = "0.4.0", optional = true }
|
embedded-io-async = { version = "0.5.0", optional = true }
|
||||||
|
|
||||||
managed = { version = "0.8.0", default-features = false, features = [ "map" ] }
|
managed = { version = "0.8.0", default-features = false, features = [ "map" ] }
|
||||||
heapless = { version = "0.7.5", default-features = false }
|
heapless = { version = "0.7.5", default-features = false }
|
||||||
@ -56,5 +61,5 @@ generic-array = { version = "0.14.4", default-features = false }
|
|||||||
stable_deref_trait = { version = "1.2.0", default-features = false }
|
stable_deref_trait = { version = "1.2.0", default-features = false }
|
||||||
futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
|
futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
|
||||||
atomic-pool = "1.0"
|
atomic-pool = "1.0"
|
||||||
embedded-nal-async = { version = "0.4.0", optional = true }
|
embedded-nal-async = { version = "0.5.0", optional = true }
|
||||||
atomic-polyfill = { version = "1.0" }
|
atomic-polyfill = { version = "1.0" }
|
||||||
|
@ -1,30 +1,56 @@
|
|||||||
# embassy-net
|
# embassy-net
|
||||||
|
|
||||||
embassy-net contains an async network API based on smoltcp and embassy, designed
|
`embassy-net` is a no-std no-alloc async network stack, designed for embedded systems.
|
||||||
for embedded systems.
|
|
||||||
|
|
||||||
## Running the example
|
It builds on [`smoltcp`](https://github.com/smoltcp-rs/smoltcp). It provides a higher-level and more opinionated
|
||||||
|
API. It glues together the components provided by `smoltcp`, handling the low-level details with defaults and
|
||||||
|
memory management designed to work well for embedded systems, aiiming for a more "Just Works" experience.
|
||||||
|
|
||||||
First, create the tap0 interface. You only need to do this once.
|
## Features
|
||||||
|
|
||||||
```sh
|
- IPv4, IPv6
|
||||||
sudo ip tuntap add name tap0 mode tap user $USER
|
- Ethernet and bare-IP mediums.
|
||||||
sudo ip link set tap0 up
|
- TCP, UDP, DNS, DHCPv4, IGMPv4
|
||||||
sudo ip addr add 192.168.69.100/24 dev tap0
|
- TCP sockets implement the `embedded-io` async traits.
|
||||||
sudo ip -6 addr add fe80::100/64 dev tap0
|
|
||||||
sudo ip -6 addr add fdaa::100/64 dev tap0
|
|
||||||
sudo ip -6 route add fe80::/64 dev tap0
|
|
||||||
sudo ip -6 route add fdaa::/64 dev tap0
|
|
||||||
```
|
|
||||||
|
|
||||||
Second, have something listening there. For example `nc -l 8000`
|
See the [`smoltcp`](https://github.com/smoltcp-rs/smoltcp) README for a detailed list of implemented and
|
||||||
|
unimplemented features of the network protocols.
|
||||||
|
|
||||||
Then run the example located in the `examples` folder:
|
## Hardware support
|
||||||
|
|
||||||
```sh
|
- [`esp-wifi`](https://github.com/esp-rs/esp-wifi) for WiFi support on bare-metal ESP32 chips. Maintained by Espressif.
|
||||||
cd $EMBASSY_ROOT/examples/std/
|
- [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W
|
||||||
cargo run --bin net -- --static-ip
|
- [`embassy-usb`](https://github.com/embassy-rs/embassy/tree/main/embassy-usb) for Ethernet-over-USB (CDC NCM) support.
|
||||||
```
|
- [`embassy-stm32`](https://github.com/embassy-rs/embassy/tree/main/embassy-stm32) for the builtin Ethernet MAC in all STM32 chips (STM32F1, STM32F2, STM32F4, STM32F7, STM32H7, STM32H5).
|
||||||
|
- [`embassy-net-w5500`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-w5500) for Wiznet W5500 SPI Ethernet MAC+PHY chip.
|
||||||
|
- [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
- For usage with Embassy HALs and network chip drivers, search [here](https://github.com/embassy-rs/embassy/tree/main/examples) for `eth` or `wifi`.
|
||||||
|
- The [`esp-wifi` repo](https://github.com/esp-rs/esp-wifi) has examples for use on bare-metal ESP32 chips.
|
||||||
|
- For usage on `std` platforms, see [the `std` examples](https://github.com/embassy-rs/embassy/tree/main/examples/std/src/bin)
|
||||||
|
|
||||||
|
## Adding support for new hardware
|
||||||
|
|
||||||
|
To add `embassy-net` support for new hardware (i.e. a new Ethernet or WiFi chip, or
|
||||||
|
an Ethernet/WiFi MCU peripheral), you have to implement the [`embassy-net-driver`](https://crates.io/crates/embassy-net-driver)
|
||||||
|
traits.
|
||||||
|
|
||||||
|
Alternatively, [`embassy-net-driver-channel`](https://crates.io/crates/embassy-net-driver-channel) provides a higer-level API
|
||||||
|
to construct a driver that processes packets in its own background task and communicates with the `embassy-net` task via
|
||||||
|
packet queues for RX and TX.
|
||||||
|
|
||||||
|
Drivers should depend only on `embassy-net-driver` or `embassy-net-driver-channel`. Never on the main `embassy-net` crate.
|
||||||
|
This allows existing drivers to continue working for newer `embassy-net` major versions, without needing an update, if the driver
|
||||||
|
trait has not had breaking changes.
|
||||||
|
|
||||||
|
## Interoperability
|
||||||
|
|
||||||
|
This crate can run on any executor.
|
||||||
|
|
||||||
|
[`embassy-time`](https://crates.io/crates/embassy-time) is used for timekeeping and timeouts. You must
|
||||||
|
link an `embassy-time` driver in your project to use this crate.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
@ -51,6 +51,8 @@ where
|
|||||||
Medium::Ethernet => phy::Medium::Ethernet,
|
Medium::Ethernet => phy::Medium::Ethernet,
|
||||||
#[cfg(feature = "medium-ip")]
|
#[cfg(feature = "medium-ip")]
|
||||||
Medium::Ip => phy::Medium::Ip,
|
Medium::Ip => phy::Medium::Ip,
|
||||||
|
#[cfg(feature = "medium-ieee802154")]
|
||||||
|
Medium::Ieee802154 => phy::Medium::Ieee802154,
|
||||||
#[allow(unreachable_patterns)]
|
#[allow(unreachable_patterns)]
|
||||||
_ => panic!(
|
_ => panic!(
|
||||||
"Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.",
|
"Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.",
|
||||||
|
@ -68,7 +68,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
|
#[cfg(feature = "nightly")]
|
||||||
impl<'a, D> embedded_nal_async::Dns for DnsSocket<'a, D>
|
impl<'a, D> embedded_nal_async::Dns for DnsSocket<'a, D>
|
||||||
where
|
where
|
||||||
D: Driver + 'static,
|
D: Driver + 'static,
|
||||||
|
@ -24,17 +24,23 @@ use embassy_net_driver::{Driver, LinkState, Medium};
|
|||||||
use embassy_sync::waitqueue::WakerRegistration;
|
use embassy_sync::waitqueue::WakerRegistration;
|
||||||
use embassy_time::{Instant, Timer};
|
use embassy_time::{Instant, Timer};
|
||||||
use futures::pin_mut;
|
use futures::pin_mut;
|
||||||
|
#[allow(unused_imports)]
|
||||||
use heapless::Vec;
|
use heapless::Vec;
|
||||||
#[cfg(feature = "igmp")]
|
#[cfg(feature = "igmp")]
|
||||||
pub use smoltcp::iface::MulticastError;
|
pub use smoltcp::iface::MulticastError;
|
||||||
|
#[allow(unused_imports)]
|
||||||
use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage};
|
use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage};
|
||||||
#[cfg(feature = "dhcpv4")]
|
#[cfg(feature = "dhcpv4")]
|
||||||
use smoltcp::socket::dhcpv4::{self, RetryConfig};
|
use smoltcp::socket::dhcpv4::{self, RetryConfig};
|
||||||
|
#[cfg(feature = "medium-ethernet")]
|
||||||
|
pub use smoltcp::wire::EthernetAddress;
|
||||||
|
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154", feature = "medium-ip"))]
|
||||||
|
pub use smoltcp::wire::HardwareAddress;
|
||||||
#[cfg(feature = "udp")]
|
#[cfg(feature = "udp")]
|
||||||
pub use smoltcp::wire::IpListenEndpoint;
|
pub use smoltcp::wire::IpListenEndpoint;
|
||||||
#[cfg(feature = "medium-ethernet")]
|
#[cfg(feature = "medium-ieee802154")]
|
||||||
pub use smoltcp::wire::{EthernetAddress, HardwareAddress};
|
pub use smoltcp::wire::{Ieee802154Address, Ieee802154Frame};
|
||||||
pub use smoltcp::wire::{IpAddress, IpCidr};
|
pub use smoltcp::wire::{IpAddress, IpCidr, IpEndpoint};
|
||||||
#[cfg(feature = "proto-ipv4")]
|
#[cfg(feature = "proto-ipv4")]
|
||||||
pub use smoltcp::wire::{Ipv4Address, Ipv4Cidr};
|
pub use smoltcp::wire::{Ipv4Address, Ipv4Cidr};
|
||||||
#[cfg(feature = "proto-ipv6")]
|
#[cfg(feature = "proto-ipv6")]
|
||||||
@ -57,7 +63,7 @@ pub struct StackResources<const SOCK: usize> {
|
|||||||
|
|
||||||
impl<const SOCK: usize> StackResources<SOCK> {
|
impl<const SOCK: usize> StackResources<SOCK> {
|
||||||
/// Create a new set of stack resources.
|
/// Create a new set of stack resources.
|
||||||
pub fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
#[cfg(feature = "dns")]
|
#[cfg(feature = "dns")]
|
||||||
const INIT: Option<dns::DnsQuery> = None;
|
const INIT: Option<dns::DnsQuery> = None;
|
||||||
Self {
|
Self {
|
||||||
@ -224,6 +230,20 @@ pub(crate) struct SocketStack {
|
|||||||
next_local_port: u16,
|
next_local_port: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_smoltcp_hardware_address(addr: driver::HardwareAddress) -> HardwareAddress {
|
||||||
|
match addr {
|
||||||
|
#[cfg(feature = "medium-ethernet")]
|
||||||
|
driver::HardwareAddress::Ethernet(eth) => HardwareAddress::Ethernet(EthernetAddress(eth)),
|
||||||
|
#[cfg(feature = "medium-ieee802154")]
|
||||||
|
driver::HardwareAddress::Ieee802154(ieee) => HardwareAddress::Ieee802154(Ieee802154Address::Extended(ieee)),
|
||||||
|
#[cfg(feature = "medium-ip")]
|
||||||
|
driver::HardwareAddress::Ip => HardwareAddress::Ip,
|
||||||
|
|
||||||
|
#[allow(unreachable_patterns)]
|
||||||
|
_ => panic!("Unsupported address {:?}. Make sure to enable medium-ethernet or medium-ieee802154 in embassy-net's Cargo features.", addr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<D: Driver + 'static> Stack<D> {
|
impl<D: Driver + 'static> Stack<D> {
|
||||||
/// Create a new network stack.
|
/// Create a new network stack.
|
||||||
pub fn new<const SOCK: usize>(
|
pub fn new<const SOCK: usize>(
|
||||||
@ -232,21 +252,7 @@ impl<D: Driver + 'static> Stack<D> {
|
|||||||
resources: &'static mut StackResources<SOCK>,
|
resources: &'static mut StackResources<SOCK>,
|
||||||
random_seed: u64,
|
random_seed: u64,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
#[cfg(feature = "medium-ethernet")]
|
let mut iface_cfg = smoltcp::iface::Config::new(to_smoltcp_hardware_address(device.hardware_address()));
|
||||||
let medium = device.capabilities().medium;
|
|
||||||
|
|
||||||
let hardware_addr = match medium {
|
|
||||||
#[cfg(feature = "medium-ethernet")]
|
|
||||||
Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress(device.ethernet_address())),
|
|
||||||
#[cfg(feature = "medium-ip")]
|
|
||||||
Medium::Ip => HardwareAddress::Ip,
|
|
||||||
#[allow(unreachable_patterns)]
|
|
||||||
_ => panic!(
|
|
||||||
"Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.",
|
|
||||||
medium
|
|
||||||
),
|
|
||||||
};
|
|
||||||
let mut iface_cfg = smoltcp::iface::Config::new(hardware_addr);
|
|
||||||
iface_cfg.random_seed = random_seed;
|
iface_cfg.random_seed = random_seed;
|
||||||
|
|
||||||
let iface = Interface::new(
|
let iface = Interface::new(
|
||||||
@ -262,6 +268,7 @@ impl<D: Driver + 'static> Stack<D> {
|
|||||||
|
|
||||||
let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN;
|
let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN;
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "medium-ieee802154", allow(unused_mut))]
|
||||||
let mut socket = SocketStack {
|
let mut socket = SocketStack {
|
||||||
sockets,
|
sockets,
|
||||||
iface,
|
iface,
|
||||||
@ -269,6 +276,7 @@ impl<D: Driver + 'static> Stack<D> {
|
|||||||
next_local_port,
|
next_local_port,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "medium-ieee802154", allow(unused_mut))]
|
||||||
let mut inner = Inner {
|
let mut inner = Inner {
|
||||||
device,
|
device,
|
||||||
link_up: false,
|
link_up: false,
|
||||||
@ -287,6 +295,9 @@ impl<D: Driver + 'static> Stack<D> {
|
|||||||
dns_waker: WakerRegistration::new(),
|
dns_waker: WakerRegistration::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "medium-ieee802154")]
|
||||||
|
let _ = config;
|
||||||
|
|
||||||
#[cfg(feature = "proto-ipv4")]
|
#[cfg(feature = "proto-ipv4")]
|
||||||
match config.ipv4 {
|
match config.ipv4 {
|
||||||
ConfigV4::Static(config) => {
|
ConfigV4::Static(config) => {
|
||||||
@ -323,9 +334,9 @@ impl<D: Driver + 'static> Stack<D> {
|
|||||||
f(&mut *self.socket.borrow_mut(), &mut *self.inner.borrow_mut())
|
f(&mut *self.socket.borrow_mut(), &mut *self.inner.borrow_mut())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the MAC address of the network interface.
|
/// Get the hardware address of the network interface.
|
||||||
pub fn ethernet_address(&self) -> [u8; 6] {
|
pub fn hardware_address(&self) -> HardwareAddress {
|
||||||
self.with(|_s, i| i.device.ethernet_address())
|
self.with(|_s, i| to_smoltcp_hardware_address(i.device.hardware_address()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get whether the link is up.
|
/// Get whether the link is up.
|
||||||
@ -419,7 +430,29 @@ impl<D: Driver + 'static> Stack<D> {
|
|||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
use embassy_hal_common::drop::OnDrop;
|
#[must_use = "to delay the drop handler invocation to the end of the scope"]
|
||||||
|
struct OnDrop<F: FnOnce()> {
|
||||||
|
f: core::mem::MaybeUninit<F>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: FnOnce()> OnDrop<F> {
|
||||||
|
fn new(f: F) -> Self {
|
||||||
|
Self {
|
||||||
|
f: core::mem::MaybeUninit::new(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn defuse(self) {
|
||||||
|
core::mem::forget(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: FnOnce()> Drop for OnDrop<F> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { self.f.as_ptr().read()() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let drop = OnDrop::new(|| {
|
let drop = OnDrop::new(|| {
|
||||||
self.with_mut(|s, i| {
|
self.with_mut(|s, i| {
|
||||||
let socket = s.sockets.get_mut::<dns::Socket>(i.dns_socket);
|
let socket = s.sockets.get_mut::<dns::Socket>(i.dns_socket);
|
||||||
@ -457,30 +490,78 @@ impl<D: Driver + 'static> Stack<D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "igmp")]
|
#[cfg(feature = "igmp")]
|
||||||
impl<D: Driver + smoltcp::phy::Device + 'static> Stack<D> {
|
impl<D: Driver + 'static> Stack<D> {
|
||||||
/// Join a multicast group.
|
/// Join a multicast group.
|
||||||
pub fn join_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError>
|
pub async fn join_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError>
|
||||||
|
where
|
||||||
|
T: Into<IpAddress>,
|
||||||
|
{
|
||||||
|
let addr = addr.into();
|
||||||
|
|
||||||
|
poll_fn(move |cx| self.poll_join_multicast_group(addr, cx)).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Join a multicast group.
|
||||||
|
///
|
||||||
|
/// When the send queue is full, this method will return `Poll::Pending`
|
||||||
|
/// and register the current task to be notified when the queue has space available.
|
||||||
|
pub fn poll_join_multicast_group<T>(&self, addr: T, cx: &mut Context<'_>) -> Poll<Result<bool, MulticastError>>
|
||||||
where
|
where
|
||||||
T: Into<IpAddress>,
|
T: Into<IpAddress>,
|
||||||
{
|
{
|
||||||
let addr = addr.into();
|
let addr = addr.into();
|
||||||
|
|
||||||
self.with_mut(|s, i| {
|
self.with_mut(|s, i| {
|
||||||
s.iface
|
let mut smoldev = DriverAdapter {
|
||||||
.join_multicast_group(&mut i.device, addr, instant_to_smoltcp(Instant::now()))
|
cx: Some(cx),
|
||||||
|
inner: &mut i.device,
|
||||||
|
};
|
||||||
|
|
||||||
|
match s
|
||||||
|
.iface
|
||||||
|
.join_multicast_group(&mut smoldev, addr, instant_to_smoltcp(Instant::now()))
|
||||||
|
{
|
||||||
|
Ok(announce_sent) => Poll::Ready(Ok(announce_sent)),
|
||||||
|
Err(MulticastError::Exhausted) => Poll::Pending,
|
||||||
|
Err(other) => Poll::Ready(Err(other)),
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Leave a multicast group.
|
/// Leave a multicast group.
|
||||||
pub fn leave_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError>
|
pub async fn leave_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError>
|
||||||
|
where
|
||||||
|
T: Into<IpAddress>,
|
||||||
|
{
|
||||||
|
let addr = addr.into();
|
||||||
|
|
||||||
|
poll_fn(move |cx| self.poll_leave_multicast_group(addr, cx)).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Leave a multicast group.
|
||||||
|
///
|
||||||
|
/// When the send queue is full, this method will return `Poll::Pending`
|
||||||
|
/// and register the current task to be notified when the queue has space available.
|
||||||
|
pub fn poll_leave_multicast_group<T>(&self, addr: T, cx: &mut Context<'_>) -> Poll<Result<bool, MulticastError>>
|
||||||
where
|
where
|
||||||
T: Into<IpAddress>,
|
T: Into<IpAddress>,
|
||||||
{
|
{
|
||||||
let addr = addr.into();
|
let addr = addr.into();
|
||||||
|
|
||||||
self.with_mut(|s, i| {
|
self.with_mut(|s, i| {
|
||||||
s.iface
|
let mut smoldev = DriverAdapter {
|
||||||
.leave_multicast_group(&mut i.device, addr, instant_to_smoltcp(Instant::now()))
|
cx: Some(cx),
|
||||||
|
inner: &mut i.device,
|
||||||
|
};
|
||||||
|
|
||||||
|
match s
|
||||||
|
.iface
|
||||||
|
.leave_multicast_group(&mut smoldev, addr, instant_to_smoltcp(Instant::now()))
|
||||||
|
{
|
||||||
|
Ok(leave_sent) => Poll::Ready(Ok(leave_sent)),
|
||||||
|
Err(MulticastError::Exhausted) => Poll::Pending,
|
||||||
|
Err(other) => Poll::Ready(Err(other)),
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -502,22 +583,26 @@ impl SocketStack {
|
|||||||
impl<D: Driver + 'static> Inner<D> {
|
impl<D: Driver + 'static> Inner<D> {
|
||||||
#[cfg(feature = "proto-ipv4")]
|
#[cfg(feature = "proto-ipv4")]
|
||||||
fn apply_config_v4(&mut self, s: &mut SocketStack, config: StaticConfigV4) {
|
fn apply_config_v4(&mut self, s: &mut SocketStack, config: StaticConfigV4) {
|
||||||
#[cfg(feature = "medium-ethernet")]
|
|
||||||
let medium = self.device.capabilities().medium;
|
|
||||||
|
|
||||||
debug!("Acquired IP configuration:");
|
debug!("Acquired IP configuration:");
|
||||||
|
|
||||||
debug!(" IP address: {}", config.address);
|
debug!(" IP address: {}", config.address);
|
||||||
s.iface.update_ip_addrs(|addrs| {
|
s.iface.update_ip_addrs(|addrs| {
|
||||||
if addrs.is_empty() {
|
if let Some((index, _)) = addrs
|
||||||
addrs.push(IpCidr::Ipv4(config.address)).unwrap();
|
.iter()
|
||||||
} else {
|
.enumerate()
|
||||||
addrs[0] = IpCidr::Ipv4(config.address);
|
.find(|(_, &addr)| matches!(addr, IpCidr::Ipv4(_)))
|
||||||
|
{
|
||||||
|
addrs.remove(index);
|
||||||
}
|
}
|
||||||
|
addrs.push(IpCidr::Ipv4(config.address)).unwrap();
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(feature = "medium-ethernet")]
|
#[cfg(feature = "medium-ip")]
|
||||||
if medium == Medium::Ethernet {
|
let skip_gateway = self.device.capabilities().medium != Medium::Ip;
|
||||||
|
#[cfg(not(feature = "medium-ip"))]
|
||||||
|
let skip_gateway = false;
|
||||||
|
|
||||||
|
if !skip_gateway {
|
||||||
if let Some(gateway) = config.gateway {
|
if let Some(gateway) = config.gateway {
|
||||||
debug!(" Default gateway: {}", gateway);
|
debug!(" Default gateway: {}", gateway);
|
||||||
s.iface.routes_mut().add_default_ipv4_route(gateway).unwrap();
|
s.iface.routes_mut().add_default_ipv4_route(gateway).unwrap();
|
||||||
@ -548,11 +633,14 @@ impl<D: Driver + 'static> Inner<D> {
|
|||||||
|
|
||||||
debug!(" IP address: {}", config.address);
|
debug!(" IP address: {}", config.address);
|
||||||
s.iface.update_ip_addrs(|addrs| {
|
s.iface.update_ip_addrs(|addrs| {
|
||||||
if addrs.is_empty() {
|
if let Some((index, _)) = addrs
|
||||||
addrs.push(IpCidr::Ipv6(config.address)).unwrap();
|
.iter()
|
||||||
} else {
|
.enumerate()
|
||||||
addrs[0] = IpCidr::Ipv6(config.address);
|
.find(|(_, &addr)| matches!(addr, IpCidr::Ipv6(_)))
|
||||||
|
{
|
||||||
|
addrs.remove(index);
|
||||||
}
|
}
|
||||||
|
addrs.push(IpCidr::Ipv6(config.address)).unwrap();
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(feature = "medium-ethernet")]
|
#[cfg(feature = "medium-ethernet")]
|
||||||
@ -620,13 +708,21 @@ impl<D: Driver + 'static> Inner<D> {
|
|||||||
socket.set_retry_config(config.retry_config);
|
socket.set_retry_config(config.retry_config);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)] // used only with dhcp
|
#[cfg(feature = "dhcpv4")]
|
||||||
fn unapply_config(&mut self, s: &mut SocketStack) {
|
fn unapply_config_v4(&mut self, s: &mut SocketStack) {
|
||||||
#[cfg(feature = "medium-ethernet")]
|
#[cfg(feature = "medium-ethernet")]
|
||||||
let medium = self.device.capabilities().medium;
|
let medium = self.device.capabilities().medium;
|
||||||
|
|
||||||
debug!("Lost IP configuration");
|
debug!("Lost IP configuration");
|
||||||
s.iface.update_ip_addrs(|ip_addrs| ip_addrs.clear());
|
s.iface.update_ip_addrs(|ip_addrs| {
|
||||||
|
#[cfg(feature = "proto-ipv4")]
|
||||||
|
if let Some((index, _)) = ip_addrs
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.find(|(_, &addr)| matches!(addr, IpCidr::Ipv4(_)))
|
||||||
|
{
|
||||||
|
ip_addrs.remove(index);
|
||||||
|
}
|
||||||
|
});
|
||||||
#[cfg(feature = "medium-ethernet")]
|
#[cfg(feature = "medium-ethernet")]
|
||||||
if medium == Medium::Ethernet {
|
if medium == Medium::Ethernet {
|
||||||
#[cfg(feature = "proto-ipv4")]
|
#[cfg(feature = "proto-ipv4")]
|
||||||
@ -643,11 +739,12 @@ impl<D: Driver + 'static> Inner<D> {
|
|||||||
fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) {
|
fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) {
|
||||||
s.waker.register(cx.waker());
|
s.waker.register(cx.waker());
|
||||||
|
|
||||||
#[cfg(feature = "medium-ethernet")]
|
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
|
||||||
if self.device.capabilities().medium == Medium::Ethernet {
|
if self.device.capabilities().medium == Medium::Ethernet
|
||||||
s.iface.set_hardware_addr(HardwareAddress::Ethernet(EthernetAddress(
|
|| self.device.capabilities().medium == Medium::Ieee802154
|
||||||
self.device.ethernet_address(),
|
{
|
||||||
)));
|
s.iface
|
||||||
|
.set_hardware_addr(to_smoltcp_hardware_address(self.device.hardware_address()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let timestamp = instant_to_smoltcp(Instant::now());
|
let timestamp = instant_to_smoltcp(Instant::now());
|
||||||
@ -673,7 +770,7 @@ impl<D: Driver + 'static> Inner<D> {
|
|||||||
if self.link_up {
|
if self.link_up {
|
||||||
match socket.poll() {
|
match socket.poll() {
|
||||||
None => {}
|
None => {}
|
||||||
Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s),
|
Some(dhcpv4::Event::Deconfigured) => self.unapply_config_v4(s),
|
||||||
Some(dhcpv4::Event::Configured(config)) => {
|
Some(dhcpv4::Event::Configured(config)) => {
|
||||||
let config = StaticConfigV4 {
|
let config = StaticConfigV4 {
|
||||||
address: config.address,
|
address: config.address,
|
||||||
@ -685,7 +782,7 @@ impl<D: Driver + 'static> Inner<D> {
|
|||||||
}
|
}
|
||||||
} else if old_link_up {
|
} else if old_link_up {
|
||||||
socket.reset();
|
socket.reset();
|
||||||
self.unapply_config(s);
|
self.unapply_config_v4(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//if old_link_up || self.link_up {
|
//if old_link_up || self.link_up {
|
||||||
|
@ -382,29 +382,29 @@ impl<'d> TcpIo<'d> {
|
|||||||
mod embedded_io_impls {
|
mod embedded_io_impls {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
impl embedded_io::Error for ConnectError {
|
impl embedded_io_async::Error for ConnectError {
|
||||||
fn kind(&self) -> embedded_io::ErrorKind {
|
fn kind(&self) -> embedded_io_async::ErrorKind {
|
||||||
embedded_io::ErrorKind::Other
|
embedded_io_async::ErrorKind::Other
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl embedded_io::Error for Error {
|
impl embedded_io_async::Error for Error {
|
||||||
fn kind(&self) -> embedded_io::ErrorKind {
|
fn kind(&self) -> embedded_io_async::ErrorKind {
|
||||||
embedded_io::ErrorKind::Other
|
embedded_io_async::ErrorKind::Other
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> embedded_io::Io for TcpSocket<'d> {
|
impl<'d> embedded_io_async::ErrorType for TcpSocket<'d> {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> embedded_io::asynch::Read for TcpSocket<'d> {
|
impl<'d> embedded_io_async::Read for TcpSocket<'d> {
|
||||||
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||||
self.io.read(buf).await
|
self.io.read(buf).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> embedded_io::asynch::Write for TcpSocket<'d> {
|
impl<'d> embedded_io_async::Write for TcpSocket<'d> {
|
||||||
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||||
self.io.write(buf).await
|
self.io.write(buf).await
|
||||||
}
|
}
|
||||||
@ -414,21 +414,21 @@ mod embedded_io_impls {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> embedded_io::Io for TcpReader<'d> {
|
impl<'d> embedded_io_async::ErrorType for TcpReader<'d> {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> embedded_io::asynch::Read for TcpReader<'d> {
|
impl<'d> embedded_io_async::Read for TcpReader<'d> {
|
||||||
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||||
self.io.read(buf).await
|
self.io.read(buf).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> embedded_io::Io for TcpWriter<'d> {
|
impl<'d> embedded_io_async::ErrorType for TcpWriter<'d> {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> embedded_io::asynch::Write for TcpWriter<'d> {
|
impl<'d> embedded_io_async::Write for TcpWriter<'d> {
|
||||||
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||||
self.io.write(buf).await
|
self.io.write(buf).await
|
||||||
}
|
}
|
||||||
@ -440,7 +440,7 @@ mod embedded_io_impls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// TCP client compatible with `embedded-nal-async` traits.
|
/// TCP client compatible with `embedded-nal-async` traits.
|
||||||
#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
|
#[cfg(feature = "nightly")]
|
||||||
pub mod client {
|
pub mod client {
|
||||||
use core::cell::UnsafeCell;
|
use core::cell::UnsafeCell;
|
||||||
use core::mem::MaybeUninit;
|
use core::mem::MaybeUninit;
|
||||||
@ -527,13 +527,13 @@ pub mod client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::Io
|
impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io_async::ErrorType
|
||||||
for TcpConnection<'d, N, TX_SZ, RX_SZ>
|
for TcpConnection<'d, N, TX_SZ, RX_SZ>
|
||||||
{
|
{
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::asynch::Read
|
impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io_async::Read
|
||||||
for TcpConnection<'d, N, TX_SZ, RX_SZ>
|
for TcpConnection<'d, N, TX_SZ, RX_SZ>
|
||||||
{
|
{
|
||||||
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||||
@ -541,7 +541,7 @@ pub mod client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io::asynch::Write
|
impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_io_async::Write
|
||||||
for TcpConnection<'d, N, TX_SZ, RX_SZ>
|
for TcpConnection<'d, N, TX_SZ, RX_SZ>
|
||||||
{
|
{
|
||||||
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
use core::future::poll_fn;
|
use core::future::poll_fn;
|
||||||
use core::mem;
|
use core::mem;
|
||||||
use core::task::Poll;
|
use core::task::{Context, Poll};
|
||||||
|
|
||||||
use embassy_net_driver::Driver;
|
use embassy_net_driver::Driver;
|
||||||
use smoltcp::iface::{Interface, SocketHandle};
|
use smoltcp::iface::{Interface, SocketHandle};
|
||||||
@ -102,7 +102,17 @@ impl<'a> UdpSocket<'a> {
|
|||||||
///
|
///
|
||||||
/// Returns the number of bytes received and the remote endpoint.
|
/// Returns the number of bytes received and the remote endpoint.
|
||||||
pub async fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, IpEndpoint), Error> {
|
pub async fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, IpEndpoint), Error> {
|
||||||
poll_fn(move |cx| {
|
poll_fn(move |cx| self.poll_recv_from(buf, cx)).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Receive a datagram.
|
||||||
|
///
|
||||||
|
/// When no datagram is available, this method will return `Poll::Pending` and
|
||||||
|
/// register the current task to be notified when a datagram is received.
|
||||||
|
///
|
||||||
|
/// When a datagram is received, this method will return `Poll::Ready` with the
|
||||||
|
/// number of bytes received and the remote endpoint.
|
||||||
|
pub fn poll_recv_from(&self, buf: &mut [u8], cx: &mut Context<'_>) -> Poll<Result<(usize, IpEndpoint), Error>> {
|
||||||
self.with_mut(|s, _| match s.recv_slice(buf) {
|
self.with_mut(|s, _| match s.recv_slice(buf) {
|
||||||
Ok((n, meta)) => Poll::Ready(Ok((n, meta.endpoint))),
|
Ok((n, meta)) => Poll::Ready(Ok((n, meta.endpoint))),
|
||||||
// No data ready
|
// No data ready
|
||||||
@ -111,17 +121,33 @@ impl<'a> UdpSocket<'a> {
|
|||||||
Poll::Pending
|
Poll::Pending
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send a datagram to the specified remote endpoint.
|
/// Send a datagram to the specified remote endpoint.
|
||||||
|
///
|
||||||
|
/// This method will wait until the datagram has been sent.
|
||||||
|
///
|
||||||
|
/// When the remote endpoint is not reachable, this method will return `Err(Error::NoRoute)`
|
||||||
pub async fn send_to<T>(&self, buf: &[u8], remote_endpoint: T) -> Result<(), Error>
|
pub async fn send_to<T>(&self, buf: &[u8], remote_endpoint: T) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
T: Into<IpEndpoint>,
|
T: Into<IpEndpoint>,
|
||||||
{
|
{
|
||||||
let remote_endpoint = remote_endpoint.into();
|
let remote_endpoint: IpEndpoint = remote_endpoint.into();
|
||||||
poll_fn(move |cx| {
|
poll_fn(move |cx| self.poll_send_to(buf, remote_endpoint, cx)).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a datagram to the specified remote endpoint.
|
||||||
|
///
|
||||||
|
/// When the datagram has been sent, this method will return `Poll::Ready(Ok())`.
|
||||||
|
///
|
||||||
|
/// When the socket's send buffer is full, this method will return `Poll::Pending`
|
||||||
|
/// and register the current task to be notified when the buffer has space available.
|
||||||
|
///
|
||||||
|
/// When the remote endpoint is not reachable, this method will return `Poll::Ready(Err(Error::NoRoute))`.
|
||||||
|
pub fn poll_send_to<T>(&self, buf: &[u8], remote_endpoint: T, cx: &mut Context<'_>) -> Poll<Result<(), Error>>
|
||||||
|
where
|
||||||
|
T: Into<IpEndpoint>,
|
||||||
|
{
|
||||||
self.with_mut(|s, _| match s.send_slice(buf, remote_endpoint) {
|
self.with_mut(|s, _| match s.send_slice(buf, remote_endpoint) {
|
||||||
// Entire datagram has been sent
|
// Entire datagram has been sent
|
||||||
Ok(()) => Poll::Ready(Ok(())),
|
Ok(()) => Poll::Ready(Ok(())),
|
||||||
@ -131,8 +157,6 @@ impl<'a> UdpSocket<'a> {
|
|||||||
}
|
}
|
||||||
Err(udp::SendError::Unaddressable) => Poll::Ready(Err(Error::NoRoute)),
|
Err(udp::SendError::Unaddressable) => Poll::Ready(Err(Error::NoRoute)),
|
||||||
})
|
})
|
||||||
})
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the local endpoint of the socket.
|
/// Returns the local endpoint of the socket.
|
||||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
|||||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-nrf-v$VERSION/embassy-nrf/src/"
|
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-nrf-v$VERSION/embassy-nrf/src/"
|
||||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-nrf/src/"
|
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-nrf/src/"
|
||||||
|
|
||||||
features = ["nightly", "defmt", "unstable-pac", "unstable-traits", "gpiote", "time-driver-rtc1"]
|
features = ["nightly", "time", "defmt", "unstable-pac", "unstable-traits", "gpiote", "time-driver-rtc1"]
|
||||||
flavors = [
|
flavors = [
|
||||||
{ regex_feature = "nrf52.*", target = "thumbv7em-none-eabihf" },
|
{ regex_feature = "nrf52.*", target = "thumbv7em-none-eabihf" },
|
||||||
{ regex_feature = "nrf53.*", target = "thumbv8m.main-none-eabihf" },
|
{ regex_feature = "nrf53.*", target = "thumbv8m.main-none-eabihf" },
|
||||||
@ -32,10 +32,10 @@ rt = [
|
|||||||
|
|
||||||
time = ["dep:embassy-time"]
|
time = ["dep:embassy-time"]
|
||||||
|
|
||||||
defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-usb-driver?/defmt", "embedded-io?/defmt", "embassy-embedded-hal/defmt"]
|
defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-usb-driver?/defmt", "embassy-embedded-hal/defmt"]
|
||||||
|
|
||||||
# Enable nightly-only features
|
# Enable nightly-only features
|
||||||
nightly = ["embedded-hal-1", "embedded-hal-async", "dep:embassy-usb-driver", "embedded-storage-async", "dep:embedded-io", "embassy-embedded-hal/nightly"]
|
nightly = ["embedded-hal-1", "embedded-hal-async", "dep:embassy-usb-driver", "embedded-storage-async", "dep:embedded-io-async", "embassy-embedded-hal/nightly"]
|
||||||
|
|
||||||
# Reexport the PAC for the currently enabled chip at `embassy_nrf::pac`.
|
# 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.
|
# This is unstable because semver-minor (non-breaking) releases of embassy-nrf may major-bump (breaking) the PAC version.
|
||||||
@ -91,16 +91,17 @@ _dppi = []
|
|||||||
_gpio-p1 = []
|
_gpio-p1 = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true }
|
embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true }
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
||||||
embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common", features = ["cortex-m", "prio-bits-3"] }
|
embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] }
|
||||||
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
|
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
|
||||||
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true }
|
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true }
|
||||||
|
|
||||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10", optional = true}
|
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11", optional = true}
|
||||||
embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true}
|
embedded-hal-async = { version = "=0.2.0-alpha.2", optional = true}
|
||||||
embedded-io = { version = "0.4.0", features = ["async"], optional = true }
|
embedded-io = { version = "0.5.0" }
|
||||||
|
embedded-io-async = { version = "0.5.0", optional = true }
|
||||||
|
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
log = { version = "0.4.14", optional = true }
|
log = { version = "0.4.14", optional = true }
|
||||||
|
@ -15,8 +15,8 @@ use core::slice;
|
|||||||
use core::sync::atomic::{compiler_fence, AtomicU8, AtomicUsize, Ordering};
|
use core::sync::atomic::{compiler_fence, AtomicU8, AtomicUsize, Ordering};
|
||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
|
|
||||||
use embassy_hal_common::atomic_ring_buffer::RingBuffer;
|
use embassy_hal_internal::atomic_ring_buffer::RingBuffer;
|
||||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
// Re-export SVD variants to allow user to directly set values
|
// 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};
|
pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity};
|
||||||
@ -572,37 +572,37 @@ impl<'u, 'd, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'u, 'd, U, T> {
|
|||||||
mod _embedded_io {
|
mod _embedded_io {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
impl embedded_io::Error for Error {
|
impl embedded_io_async::Error for Error {
|
||||||
fn kind(&self) -> embedded_io::ErrorKind {
|
fn kind(&self) -> embedded_io_async::ErrorKind {
|
||||||
match *self {}
|
match *self {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarte<'d, U, T> {
|
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io_async::ErrorType for BufferedUarte<'d, U, T> {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteRx<'u, 'd, U, T> {
|
impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io_async::ErrorType for BufferedUarteRx<'u, 'd, U, T> {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io::Io for BufferedUarteTx<'u, 'd, U, T> {
|
impl<'u, 'd, U: UarteInstance, T: TimerInstance> embedded_io_async::ErrorType for BufferedUarteTx<'u, 'd, U, T> {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarte<'d, U, T> {
|
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io_async::Read for BufferedUarte<'d, U, T> {
|
||||||
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||||
self.inner_read(buf).await
|
self.inner_read(buf).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Read for BufferedUarteRx<'u, 'd, U, T> {
|
impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io_async::Read for BufferedUarteRx<'u, 'd, U, T> {
|
||||||
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||||
self.inner.inner_read(buf).await
|
self.inner.inner_read(buf).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarte<'d, U, T> {
|
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io_async::BufRead for BufferedUarte<'d, U, T> {
|
||||||
async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
|
async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
|
||||||
self.inner_fill_buf().await
|
self.inner_fill_buf().await
|
||||||
}
|
}
|
||||||
@ -612,7 +612,7 @@ mod _embedded_io {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::BufRead for BufferedUarteRx<'u, 'd, U, T> {
|
impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io_async::BufRead for BufferedUarteRx<'u, 'd, U, T> {
|
||||||
async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
|
async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> {
|
||||||
self.inner.inner_fill_buf().await
|
self.inner.inner_fill_buf().await
|
||||||
}
|
}
|
||||||
@ -622,7 +622,7 @@ mod _embedded_io {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarte<'d, U, T> {
|
impl<'d, U: UarteInstance, T: TimerInstance> embedded_io_async::Write for BufferedUarte<'d, U, T> {
|
||||||
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||||
self.inner_write(buf).await
|
self.inner_write(buf).await
|
||||||
}
|
}
|
||||||
@ -632,7 +632,7 @@ mod _embedded_io {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io::asynch::Write for BufferedUarteTx<'u, 'd, U, T> {
|
impl<'u, 'd: 'u, U: UarteInstance, T: TimerInstance> embedded_io_async::Write for BufferedUarteTx<'u, 'd, U, T> {
|
||||||
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||||
self.inner.inner_write(buf).await
|
self.inner.inner_write(buf).await
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 192 * 1024;
|
|||||||
|
|
||||||
pub const RESET_PIN: u32 = 21;
|
pub const RESET_PIN: u32 = 21;
|
||||||
|
|
||||||
embassy_hal_common::peripherals! {
|
embassy_hal_internal::peripherals! {
|
||||||
// RTC
|
// RTC
|
||||||
RTC0,
|
RTC0,
|
||||||
RTC1,
|
RTC1,
|
||||||
@ -208,7 +208,7 @@ impl_ppi_channel!(PPI_CH31, 31 => static);
|
|||||||
impl_saadc_input!(P0_04, ANALOG_INPUT2);
|
impl_saadc_input!(P0_04, ANALOG_INPUT2);
|
||||||
impl_saadc_input!(P0_05, ANALOG_INPUT3);
|
impl_saadc_input!(P0_05, ANALOG_INPUT3);
|
||||||
|
|
||||||
embassy_hal_common::interrupt_mod!(
|
embassy_hal_internal::interrupt_mod!(
|
||||||
POWER_CLOCK,
|
POWER_CLOCK,
|
||||||
RADIO,
|
RADIO,
|
||||||
UARTE0_UART0,
|
UARTE0_UART0,
|
||||||
|
@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 192 * 1024;
|
|||||||
|
|
||||||
pub const RESET_PIN: u32 = 21;
|
pub const RESET_PIN: u32 = 21;
|
||||||
|
|
||||||
embassy_hal_common::peripherals! {
|
embassy_hal_internal::peripherals! {
|
||||||
// RTC
|
// RTC
|
||||||
RTC0,
|
RTC0,
|
||||||
RTC1,
|
RTC1,
|
||||||
@ -234,7 +234,7 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5);
|
|||||||
impl_saadc_input!(P0_30, ANALOG_INPUT6);
|
impl_saadc_input!(P0_30, ANALOG_INPUT6);
|
||||||
impl_saadc_input!(P0_31, ANALOG_INPUT7);
|
impl_saadc_input!(P0_31, ANALOG_INPUT7);
|
||||||
|
|
||||||
embassy_hal_common::interrupt_mod!(
|
embassy_hal_internal::interrupt_mod!(
|
||||||
POWER_CLOCK,
|
POWER_CLOCK,
|
||||||
RADIO,
|
RADIO,
|
||||||
UARTE0_UART0,
|
UARTE0_UART0,
|
||||||
|
@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 192 * 1024;
|
|||||||
|
|
||||||
pub const RESET_PIN: u32 = 21;
|
pub const RESET_PIN: u32 = 21;
|
||||||
|
|
||||||
embassy_hal_common::peripherals! {
|
embassy_hal_internal::peripherals! {
|
||||||
// RTC
|
// RTC
|
||||||
RTC0,
|
RTC0,
|
||||||
RTC1,
|
RTC1,
|
||||||
@ -236,7 +236,7 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5);
|
|||||||
impl_saadc_input!(P0_30, ANALOG_INPUT6);
|
impl_saadc_input!(P0_30, ANALOG_INPUT6);
|
||||||
impl_saadc_input!(P0_31, ANALOG_INPUT7);
|
impl_saadc_input!(P0_31, ANALOG_INPUT7);
|
||||||
|
|
||||||
embassy_hal_common::interrupt_mod!(
|
embassy_hal_internal::interrupt_mod!(
|
||||||
POWER_CLOCK,
|
POWER_CLOCK,
|
||||||
RADIO,
|
RADIO,
|
||||||
UARTE0_UART0,
|
UARTE0_UART0,
|
||||||
|
@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 256 * 1024;
|
|||||||
|
|
||||||
pub const RESET_PIN: u32 = 18;
|
pub const RESET_PIN: u32 = 18;
|
||||||
|
|
||||||
embassy_hal_common::peripherals! {
|
embassy_hal_internal::peripherals! {
|
||||||
// USB
|
// USB
|
||||||
USBD,
|
USBD,
|
||||||
|
|
||||||
@ -224,7 +224,7 @@ impl_ppi_channel!(PPI_CH29, 29 => static);
|
|||||||
impl_ppi_channel!(PPI_CH30, 30 => static);
|
impl_ppi_channel!(PPI_CH30, 30 => static);
|
||||||
impl_ppi_channel!(PPI_CH31, 31 => static);
|
impl_ppi_channel!(PPI_CH31, 31 => static);
|
||||||
|
|
||||||
embassy_hal_common::interrupt_mod!(
|
embassy_hal_internal::interrupt_mod!(
|
||||||
POWER_CLOCK,
|
POWER_CLOCK,
|
||||||
RADIO,
|
RADIO,
|
||||||
UARTE0_UART0,
|
UARTE0_UART0,
|
||||||
|
@ -12,7 +12,7 @@ pub const FLASH_SIZE: usize = 512 * 1024;
|
|||||||
|
|
||||||
pub const RESET_PIN: u32 = 21;
|
pub const RESET_PIN: u32 = 21;
|
||||||
|
|
||||||
embassy_hal_common::peripherals! {
|
embassy_hal_internal::peripherals! {
|
||||||
// RTC
|
// RTC
|
||||||
RTC0,
|
RTC0,
|
||||||
RTC1,
|
RTC1,
|
||||||
@ -263,7 +263,7 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7);
|
|||||||
|
|
||||||
impl_i2s!(I2S, I2S, I2S);
|
impl_i2s!(I2S, I2S, I2S);
|
||||||
|
|
||||||
embassy_hal_common::interrupt_mod!(
|
embassy_hal_internal::interrupt_mod!(
|
||||||
POWER_CLOCK,
|
POWER_CLOCK,
|
||||||
RADIO,
|
RADIO,
|
||||||
UARTE0_UART0,
|
UARTE0_UART0,
|
||||||
|
@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 512 * 1024;
|
|||||||
|
|
||||||
pub const RESET_PIN: u32 = 18;
|
pub const RESET_PIN: u32 = 18;
|
||||||
|
|
||||||
embassy_hal_common::peripherals! {
|
embassy_hal_internal::peripherals! {
|
||||||
// USB
|
// USB
|
||||||
USBD,
|
USBD,
|
||||||
|
|
||||||
@ -306,7 +306,7 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7);
|
|||||||
|
|
||||||
impl_i2s!(I2S, I2S, I2S);
|
impl_i2s!(I2S, I2S, I2S);
|
||||||
|
|
||||||
embassy_hal_common::interrupt_mod!(
|
embassy_hal_internal::interrupt_mod!(
|
||||||
POWER_CLOCK,
|
POWER_CLOCK,
|
||||||
RADIO,
|
RADIO,
|
||||||
UARTE0_UART0,
|
UARTE0_UART0,
|
||||||
|
@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 1024 * 1024;
|
|||||||
|
|
||||||
pub const RESET_PIN: u32 = 18;
|
pub const RESET_PIN: u32 = 18;
|
||||||
|
|
||||||
embassy_hal_common::peripherals! {
|
embassy_hal_internal::peripherals! {
|
||||||
// USB
|
// USB
|
||||||
USBD,
|
USBD,
|
||||||
|
|
||||||
@ -311,7 +311,7 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7);
|
|||||||
|
|
||||||
impl_i2s!(I2S, I2S, I2S);
|
impl_i2s!(I2S, I2S, I2S);
|
||||||
|
|
||||||
embassy_hal_common::interrupt_mod!(
|
embassy_hal_internal::interrupt_mod!(
|
||||||
POWER_CLOCK,
|
POWER_CLOCK,
|
||||||
RADIO,
|
RADIO,
|
||||||
UARTE0_UART0,
|
UARTE0_UART0,
|
||||||
|
@ -218,7 +218,7 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 1024;
|
|||||||
|
|
||||||
pub const FLASH_SIZE: usize = 1024 * 1024;
|
pub const FLASH_SIZE: usize = 1024 * 1024;
|
||||||
|
|
||||||
embassy_hal_common::peripherals! {
|
embassy_hal_internal::peripherals! {
|
||||||
// USB
|
// USB
|
||||||
USBD,
|
USBD,
|
||||||
|
|
||||||
@ -506,7 +506,7 @@ impl_saadc_input!(P0_18, ANALOG_INPUT5);
|
|||||||
impl_saadc_input!(P0_19, ANALOG_INPUT6);
|
impl_saadc_input!(P0_19, ANALOG_INPUT6);
|
||||||
impl_saadc_input!(P0_20, ANALOG_INPUT7);
|
impl_saadc_input!(P0_20, ANALOG_INPUT7);
|
||||||
|
|
||||||
embassy_hal_common::interrupt_mod!(
|
embassy_hal_internal::interrupt_mod!(
|
||||||
FPU,
|
FPU,
|
||||||
CACHE,
|
CACHE,
|
||||||
SPU,
|
SPU,
|
||||||
|
@ -109,7 +109,7 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 1024;
|
|||||||
|
|
||||||
pub const FLASH_SIZE: usize = 256 * 1024;
|
pub const FLASH_SIZE: usize = 256 * 1024;
|
||||||
|
|
||||||
embassy_hal_common::peripherals! {
|
embassy_hal_internal::peripherals! {
|
||||||
// RTC
|
// RTC
|
||||||
RTC0,
|
RTC0,
|
||||||
RTC1,
|
RTC1,
|
||||||
@ -342,7 +342,7 @@ impl_ppi_channel!(PPI_CH29, 29 => configurable);
|
|||||||
impl_ppi_channel!(PPI_CH30, 30 => configurable);
|
impl_ppi_channel!(PPI_CH30, 30 => configurable);
|
||||||
impl_ppi_channel!(PPI_CH31, 31 => configurable);
|
impl_ppi_channel!(PPI_CH31, 31 => configurable);
|
||||||
|
|
||||||
embassy_hal_common::interrupt_mod!(
|
embassy_hal_internal::interrupt_mod!(
|
||||||
CLOCK_POWER,
|
CLOCK_POWER,
|
||||||
RADIO,
|
RADIO,
|
||||||
RNG,
|
RNG,
|
||||||
|
@ -169,7 +169,7 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 1024;
|
|||||||
|
|
||||||
pub const FLASH_SIZE: usize = 1024 * 1024;
|
pub const FLASH_SIZE: usize = 1024 * 1024;
|
||||||
|
|
||||||
embassy_hal_common::peripherals! {
|
embassy_hal_internal::peripherals! {
|
||||||
// RTC
|
// RTC
|
||||||
RTC0,
|
RTC0,
|
||||||
RTC1,
|
RTC1,
|
||||||
@ -368,7 +368,7 @@ impl_saadc_input!(P0_18, ANALOG_INPUT5);
|
|||||||
impl_saadc_input!(P0_19, ANALOG_INPUT6);
|
impl_saadc_input!(P0_19, ANALOG_INPUT6);
|
||||||
impl_saadc_input!(P0_20, ANALOG_INPUT7);
|
impl_saadc_input!(P0_20, ANALOG_INPUT7);
|
||||||
|
|
||||||
embassy_hal_common::interrupt_mod!(
|
embassy_hal_internal::interrupt_mod!(
|
||||||
SPU,
|
SPU,
|
||||||
CLOCK_POWER,
|
CLOCK_POWER,
|
||||||
UARTE0_SPIM0_SPIS0_TWIM0_TWIS0,
|
UARTE0_SPIM0_SPIS0_TWIM0_TWIS0,
|
||||||
|
@ -5,7 +5,7 @@ use core::convert::Infallible;
|
|||||||
use core::hint::unreachable_unchecked;
|
use core::hint::unreachable_unchecked;
|
||||||
|
|
||||||
use cfg_if::cfg_if;
|
use cfg_if::cfg_if;
|
||||||
use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef};
|
use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef};
|
||||||
|
|
||||||
use self::sealed::Pin as _;
|
use self::sealed::Pin as _;
|
||||||
use crate::pac::p0 as gpio;
|
use crate::pac::p0 as gpio;
|
||||||
|
@ -4,7 +4,7 @@ use core::convert::Infallible;
|
|||||||
use core::future::{poll_fn, Future};
|
use core::future::{poll_fn, Future};
|
||||||
use core::task::{Context, Poll};
|
use core::task::{Context, Poll};
|
||||||
|
|
||||||
use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef};
|
use embassy_hal_internal::{impl_peripheral, into_ref, Peripheral, PeripheralRef};
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
|
||||||
use crate::gpio::sealed::Pin as _;
|
use crate::gpio::sealed::Pin as _;
|
||||||
@ -221,7 +221,7 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the IN event, for use with PPI.
|
/// Returns the IN event, for use with PPI.
|
||||||
pub fn event_in(&self) -> Event {
|
pub fn event_in(&self) -> Event<'d> {
|
||||||
let g = regs();
|
let g = regs();
|
||||||
Event::from_reg(&g.events_in[self.ch.number()])
|
Event::from_reg(&g.events_in[self.ch.number()])
|
||||||
}
|
}
|
||||||
@ -292,21 +292,21 @@ impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the OUT task, for use with PPI.
|
/// Returns the OUT task, for use with PPI.
|
||||||
pub fn task_out(&self) -> Task {
|
pub fn task_out(&self) -> Task<'d> {
|
||||||
let g = regs();
|
let g = regs();
|
||||||
Task::from_reg(&g.tasks_out[self.ch.number()])
|
Task::from_reg(&g.tasks_out[self.ch.number()])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the CLR task, for use with PPI.
|
/// Returns the CLR task, for use with PPI.
|
||||||
#[cfg(not(feature = "nrf51"))]
|
#[cfg(not(feature = "nrf51"))]
|
||||||
pub fn task_clr(&self) -> Task {
|
pub fn task_clr(&self) -> Task<'d> {
|
||||||
let g = regs();
|
let g = regs();
|
||||||
Task::from_reg(&g.tasks_clr[self.ch.number()])
|
Task::from_reg(&g.tasks_clr[self.ch.number()])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the SET task, for use with PPI.
|
/// Returns the SET task, for use with PPI.
|
||||||
#[cfg(not(feature = "nrf51"))]
|
#[cfg(not(feature = "nrf51"))]
|
||||||
pub fn task_set(&self) -> Task {
|
pub fn task_set(&self) -> Task<'d> {
|
||||||
let g = regs();
|
let g = regs();
|
||||||
Task::from_reg(&g.tasks_set[self.ch.number()])
|
Task::from_reg(&g.tasks_set[self.ch.number()])
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,8 @@ use core::ops::{Deref, DerefMut};
|
|||||||
use core::sync::atomic::{compiler_fence, Ordering};
|
use core::sync::atomic::{compiler_fence, Ordering};
|
||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
|
|
||||||
use embassy_hal_common::drop::OnDrop;
|
use embassy_hal_internal::drop::OnDrop;
|
||||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
|
|
||||||
use crate::gpio::{AnyPin, Pin as GpioPin};
|
use crate::gpio::{AnyPin, Pin as GpioPin};
|
||||||
use crate::interrupt::typelevel::Interrupt;
|
use crate::interrupt::typelevel::Interrupt;
|
||||||
|
@ -98,7 +98,7 @@ mod chip;
|
|||||||
/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)
|
/// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`)
|
||||||
/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to
|
/// and implements the right [`Binding`]s for it. You can pass this struct to drivers to
|
||||||
/// prove at compile-time that the right interrupts have been bound.
|
/// prove at compile-time that the right interrupts have been bound.
|
||||||
// developer note: this macro can't be in `embassy-hal-common` due to the use of `$crate`.
|
// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! bind_interrupts {
|
macro_rules! bind_interrupts {
|
||||||
($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => {
|
($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => {
|
||||||
@ -127,7 +127,7 @@ pub use chip::pac;
|
|||||||
#[cfg(not(feature = "unstable-pac"))]
|
#[cfg(not(feature = "unstable-pac"))]
|
||||||
pub(crate) use chip::pac;
|
pub(crate) use chip::pac;
|
||||||
pub use chip::{peripherals, Peripherals, EASY_DMA_SIZE};
|
pub use chip::{peripherals, Peripherals, EASY_DMA_SIZE};
|
||||||
pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
|
pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
|
||||||
|
|
||||||
pub use crate::chip::interrupt;
|
pub use crate::chip::interrupt;
|
||||||
pub use crate::pac::NVIC_PRIO_BITS;
|
pub use crate::pac::NVIC_PRIO_BITS;
|
||||||
@ -410,13 +410,13 @@ pub fn init(config: config::Config) -> Peripherals {
|
|||||||
warn!(
|
warn!(
|
||||||
"You have requested enabling chip reset functionality on the reset pin, by not enabling the Cargo feature `reset-pin-as-gpio`.\n\
|
"You have requested enabling chip reset functionality on the reset pin, by not enabling the Cargo feature `reset-pin-as-gpio`.\n\
|
||||||
However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\
|
However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\
|
||||||
To fix this, erase UICR manually, for example using `probe-rs-cli erase` or `nrfjprog --eraseuicr`."
|
To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`."
|
||||||
);
|
);
|
||||||
#[cfg(feature = "reset-pin-as-gpio")]
|
#[cfg(feature = "reset-pin-as-gpio")]
|
||||||
warn!(
|
warn!(
|
||||||
"You have requested using the reset pin as GPIO, by enabling the Cargo feature `reset-pin-as-gpio`.\n\
|
"You have requested using the reset pin as GPIO, by enabling the Cargo feature `reset-pin-as-gpio`.\n\
|
||||||
However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\
|
However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\
|
||||||
To fix this, erase UICR manually, for example using `probe-rs-cli erase` or `nrfjprog --eraseuicr`."
|
To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -432,7 +432,7 @@ pub fn init(config: config::Config) -> Peripherals {
|
|||||||
warn!(
|
warn!(
|
||||||
"You have requested to use P0.09 and P0.10 pins for NFC, by not enabling the Cargo feature `nfc-pins-as-gpio`.\n\
|
"You have requested to use P0.09 and P0.10 pins for NFC, by not enabling the Cargo feature `nfc-pins-as-gpio`.\n\
|
||||||
However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\
|
However, UICR is already programmed to some other setting, and can't be changed without erasing it.\n\
|
||||||
To fix this, erase UICR manually, for example using `probe-rs-cli erase` or `nrfjprog --eraseuicr`."
|
To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use core::{ptr, slice};
|
use core::{ptr, slice};
|
||||||
|
|
||||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
use embedded_storage::nor_flash::{
|
use embedded_storage::nor_flash::{
|
||||||
ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash,
|
ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash,
|
||||||
};
|
};
|
||||||
|
@ -6,14 +6,24 @@ use core::marker::PhantomData;
|
|||||||
use core::sync::atomic::{compiler_fence, Ordering};
|
use core::sync::atomic::{compiler_fence, Ordering};
|
||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
|
|
||||||
use embassy_hal_common::drop::OnDrop;
|
use embassy_hal_internal::drop::OnDrop;
|
||||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
|
use fixed::types::I7F1;
|
||||||
use futures::future::poll_fn;
|
use futures::future::poll_fn;
|
||||||
|
|
||||||
use crate::chip::EASY_DMA_SIZE;
|
use crate::chip::EASY_DMA_SIZE;
|
||||||
use crate::gpio::sealed::Pin;
|
use crate::gpio::sealed::Pin;
|
||||||
use crate::gpio::{AnyPin, Pin as GpioPin};
|
use crate::gpio::{AnyPin, Pin as GpioPin};
|
||||||
use crate::interrupt::typelevel::Interrupt;
|
use crate::interrupt::typelevel::Interrupt;
|
||||||
|
use crate::pac::pdm::mode::{EDGE_A, OPERATION_A};
|
||||||
|
pub use crate::pac::pdm::pdmclkctrl::FREQ_A as Frequency;
|
||||||
|
#[cfg(any(
|
||||||
|
feature = "nrf52840",
|
||||||
|
feature = "nrf52833",
|
||||||
|
feature = "_nrf5340-app",
|
||||||
|
feature = "_nrf9160",
|
||||||
|
))]
|
||||||
|
pub use crate::pac::pdm::ratio::RATIO_A as Ratio;
|
||||||
use crate::{interrupt, Peripheral};
|
use crate::{interrupt, Peripheral};
|
||||||
|
|
||||||
/// Interrupt handler.
|
/// Interrupt handler.
|
||||||
@ -23,7 +33,20 @@ pub struct InterruptHandler<T: Instance> {
|
|||||||
|
|
||||||
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
|
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
|
||||||
unsafe fn on_interrupt() {
|
unsafe fn on_interrupt() {
|
||||||
T::regs().intenclr.write(|w| w.end().clear());
|
let r = T::regs();
|
||||||
|
|
||||||
|
if r.events_end.read().bits() != 0 {
|
||||||
|
r.intenclr.write(|w| w.end().clear());
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.events_started.read().bits() != 0 {
|
||||||
|
r.intenclr.write(|w| w.started().clear());
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.events_stopped.read().bits() != 0 {
|
||||||
|
r.intenclr.write(|w| w.stopped().clear());
|
||||||
|
}
|
||||||
|
|
||||||
T::state().waker.wake();
|
T::state().waker.wake();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -44,10 +67,24 @@ pub enum Error {
|
|||||||
BufferZeroLength,
|
BufferZeroLength,
|
||||||
/// PDM is not running
|
/// PDM is not running
|
||||||
NotRunning,
|
NotRunning,
|
||||||
|
/// PDM is already running
|
||||||
|
AlreadyRunning,
|
||||||
}
|
}
|
||||||
|
|
||||||
static DUMMY_BUFFER: [i16; 1] = [0; 1];
|
static DUMMY_BUFFER: [i16; 1] = [0; 1];
|
||||||
|
|
||||||
|
/// The state of a continuously running sampler. While it reflects
|
||||||
|
/// the progress of a sampler, it also signals what should be done
|
||||||
|
/// next. For example, if the sampler has stopped then the Pdm implementation
|
||||||
|
/// can then tear down its infrastructure.
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub enum SamplerState {
|
||||||
|
/// The sampler processed the samples and is ready for more.
|
||||||
|
Sampled,
|
||||||
|
/// The sampler is done processing samples.
|
||||||
|
Stopped,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> Pdm<'d, T> {
|
impl<'d, T: Instance> Pdm<'d, T> {
|
||||||
/// Create PDM driver
|
/// Create PDM driver
|
||||||
pub fn new(
|
pub fn new(
|
||||||
@ -79,18 +116,24 @@ impl<'d, T: Instance> Pdm<'d, T> {
|
|||||||
r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) });
|
r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) });
|
||||||
|
|
||||||
// configure
|
// configure
|
||||||
// use default for
|
r.pdmclkctrl.write(|w| w.freq().variant(config.frequency));
|
||||||
// - gain right
|
#[cfg(any(
|
||||||
// - gain left
|
feature = "nrf52840",
|
||||||
// - clk
|
feature = "nrf52833",
|
||||||
// - ratio
|
feature = "_nrf5340-app",
|
||||||
|
feature = "_nrf9160",
|
||||||
|
))]
|
||||||
|
r.ratio.write(|w| w.ratio().variant(config.ratio));
|
||||||
r.mode.write(|w| {
|
r.mode.write(|w| {
|
||||||
w.edge().bit(config.edge == Edge::LeftRising);
|
w.operation().variant(config.operation_mode.into());
|
||||||
w.operation().bit(config.operation_mode == OperationMode::Mono);
|
w.edge().variant(config.edge.into());
|
||||||
w
|
w
|
||||||
});
|
});
|
||||||
r.gainl.write(|w| w.gainl().default_gain());
|
|
||||||
r.gainr.write(|w| w.gainr().default_gain());
|
Self::_set_gain(r, config.gain_left, config.gain_right);
|
||||||
|
|
||||||
|
// Disable all events interrupts
|
||||||
|
r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) });
|
||||||
|
|
||||||
// IRQ
|
// IRQ
|
||||||
T::Interrupt::unpend();
|
T::Interrupt::unpend();
|
||||||
@ -101,6 +144,25 @@ impl<'d, T: Instance> Pdm<'d, T> {
|
|||||||
Self { _peri: pdm }
|
Self { _peri: pdm }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn _set_gain(r: &crate::pac::pdm::RegisterBlock, gain_left: I7F1, gain_right: I7F1) {
|
||||||
|
let gain_left = gain_left
|
||||||
|
.saturating_add(I7F1::from_bits(40))
|
||||||
|
.saturating_to_num::<u8>()
|
||||||
|
.clamp(0, 0x50);
|
||||||
|
let gain_right = gain_right
|
||||||
|
.saturating_add(I7F1::from_bits(40))
|
||||||
|
.saturating_to_num::<u8>()
|
||||||
|
.clamp(0, 0x50);
|
||||||
|
|
||||||
|
r.gainl.write(|w| unsafe { w.gainl().bits(gain_left) });
|
||||||
|
r.gainr.write(|w| unsafe { w.gainr().bits(gain_right) });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adjust the gain of the PDM microphone on the fly
|
||||||
|
pub fn set_gain(&mut self, gain_left: I7F1, gain_right: I7F1) {
|
||||||
|
Self::_set_gain(T::regs(), gain_left, gain_right)
|
||||||
|
}
|
||||||
|
|
||||||
/// Start sampling microphon data into a dummy buffer
|
/// Start sampling microphon data into a dummy buffer
|
||||||
/// Usefull to start the microphon and keep it active between recording samples
|
/// Usefull to start the microphon and keep it active between recording samples
|
||||||
pub async fn start(&mut self) {
|
pub async fn start(&mut self) {
|
||||||
@ -198,6 +260,108 @@ impl<'d, T: Instance> Pdm<'d, T> {
|
|||||||
|
|
||||||
compiler_fence(Ordering::SeqCst);
|
compiler_fence(Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Continuous sampling with double buffers.
|
||||||
|
///
|
||||||
|
/// A sampler closure is provided that receives the buffer of samples, noting
|
||||||
|
/// that the size of this buffer can be less than the original buffer's size.
|
||||||
|
/// A command is return from the closure that indicates whether the sampling
|
||||||
|
/// should continue or stop.
|
||||||
|
///
|
||||||
|
/// NOTE: The time spent within the callback supplied should not exceed the time
|
||||||
|
/// taken to acquire the samples into a single buffer. You should measure the
|
||||||
|
/// time taken by the callback and set the sample buffer size accordingly.
|
||||||
|
/// Exceeding this time can lead to samples becoming dropped.
|
||||||
|
pub async fn run_task_sampler<S, const N: usize>(
|
||||||
|
&mut self,
|
||||||
|
bufs: &mut [[i16; N]; 2],
|
||||||
|
mut sampler: S,
|
||||||
|
) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
S: FnMut(&[i16; N]) -> SamplerState,
|
||||||
|
{
|
||||||
|
let r = T::regs();
|
||||||
|
|
||||||
|
if r.events_started.read().bits() != 0 {
|
||||||
|
return Err(Error::AlreadyRunning);
|
||||||
|
}
|
||||||
|
|
||||||
|
r.sample
|
||||||
|
.ptr
|
||||||
|
.write(|w| unsafe { w.sampleptr().bits(bufs[0].as_mut_ptr() as u32) });
|
||||||
|
r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(N as _) });
|
||||||
|
|
||||||
|
// Reset and enable the events
|
||||||
|
r.events_end.reset();
|
||||||
|
r.events_started.reset();
|
||||||
|
r.events_stopped.reset();
|
||||||
|
r.intenset.write(|w| {
|
||||||
|
w.end().set();
|
||||||
|
w.started().set();
|
||||||
|
w.stopped().set();
|
||||||
|
w
|
||||||
|
});
|
||||||
|
|
||||||
|
// Don't reorder the start event before the previous writes. Hopefully self
|
||||||
|
// wouldn't happen anyway.
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
|
||||||
|
r.tasks_start.write(|w| unsafe { w.bits(1) });
|
||||||
|
|
||||||
|
let mut current_buffer = 0;
|
||||||
|
|
||||||
|
let mut done = false;
|
||||||
|
|
||||||
|
let drop = OnDrop::new(|| {
|
||||||
|
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||||
|
// N.B. It would be better if this were async, but Drop only support sync code.
|
||||||
|
while r.events_stopped.read().bits() != 0 {}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for events and complete when the sampler indicates it has had enough.
|
||||||
|
poll_fn(|cx| {
|
||||||
|
let r = T::regs();
|
||||||
|
|
||||||
|
T::state().waker.register(cx.waker());
|
||||||
|
|
||||||
|
if r.events_end.read().bits() != 0 {
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
|
||||||
|
r.events_end.reset();
|
||||||
|
r.intenset.write(|w| w.end().set());
|
||||||
|
|
||||||
|
if !done {
|
||||||
|
// Discard the last buffer after the user requested a stop.
|
||||||
|
if sampler(&bufs[current_buffer]) == SamplerState::Sampled {
|
||||||
|
let next_buffer = 1 - current_buffer;
|
||||||
|
current_buffer = next_buffer;
|
||||||
|
} else {
|
||||||
|
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
||||||
|
done = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.events_started.read().bits() != 0 {
|
||||||
|
r.events_started.reset();
|
||||||
|
r.intenset.write(|w| w.started().set());
|
||||||
|
|
||||||
|
let next_buffer = 1 - current_buffer;
|
||||||
|
r.sample
|
||||||
|
.ptr
|
||||||
|
.write(|w| unsafe { w.sampleptr().bits(bufs[next_buffer].as_mut_ptr() as u32) });
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.events_stopped.read().bits() != 0 {
|
||||||
|
return Poll::Ready(());
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
drop.defuse();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PDM microphone driver Config
|
/// PDM microphone driver Config
|
||||||
@ -206,6 +370,20 @@ pub struct Config {
|
|||||||
pub operation_mode: OperationMode,
|
pub operation_mode: OperationMode,
|
||||||
/// On which edge the left channel should be samples
|
/// On which edge the left channel should be samples
|
||||||
pub edge: Edge,
|
pub edge: Edge,
|
||||||
|
/// Clock frequency
|
||||||
|
pub frequency: Frequency,
|
||||||
|
/// Clock ratio
|
||||||
|
#[cfg(any(
|
||||||
|
feature = "nrf52840",
|
||||||
|
feature = "nrf52833",
|
||||||
|
feature = "_nrf5340-app",
|
||||||
|
feature = "_nrf9160",
|
||||||
|
))]
|
||||||
|
pub ratio: Ratio,
|
||||||
|
/// Gain left in dB
|
||||||
|
pub gain_left: I7F1,
|
||||||
|
/// Gain right in dB
|
||||||
|
pub gain_right: I7F1,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
@ -213,6 +391,16 @@ impl Default for Config {
|
|||||||
Self {
|
Self {
|
||||||
operation_mode: OperationMode::Mono,
|
operation_mode: OperationMode::Mono,
|
||||||
edge: Edge::LeftFalling,
|
edge: Edge::LeftFalling,
|
||||||
|
frequency: Frequency::DEFAULT,
|
||||||
|
#[cfg(any(
|
||||||
|
feature = "nrf52840",
|
||||||
|
feature = "nrf52833",
|
||||||
|
feature = "_nrf5340-app",
|
||||||
|
feature = "_nrf9160",
|
||||||
|
))]
|
||||||
|
ratio: Ratio::RATIO80,
|
||||||
|
gain_left: I7F1::ZERO,
|
||||||
|
gain_right: I7F1::ZERO,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -226,6 +414,15 @@ pub enum OperationMode {
|
|||||||
Stereo,
|
Stereo,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<OperationMode> for OPERATION_A {
|
||||||
|
fn from(mode: OperationMode) -> Self {
|
||||||
|
match mode {
|
||||||
|
OperationMode::Mono => OPERATION_A::MONO,
|
||||||
|
OperationMode::Stereo => OPERATION_A::STEREO,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// PDM edge polarity
|
/// PDM edge polarity
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub enum Edge {
|
pub enum Edge {
|
||||||
@ -235,6 +432,15 @@ pub enum Edge {
|
|||||||
LeftFalling,
|
LeftFalling,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Edge> for EDGE_A {
|
||||||
|
fn from(edge: Edge) -> Self {
|
||||||
|
match edge {
|
||||||
|
Edge::LeftRising => EDGE_A::LEFT_RISING,
|
||||||
|
Edge::LeftFalling => EDGE_A::LEFT_FALLING,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> Drop for Pdm<'d, T> {
|
impl<'d, T: Instance> Drop for Pdm<'d, T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use embassy_hal_common::into_ref;
|
use embassy_hal_internal::into_ref;
|
||||||
|
|
||||||
use super::{Channel, ConfigurableChannel, Event, Ppi, Task};
|
use super::{Channel, ConfigurableChannel, Event, Ppi, Task};
|
||||||
use crate::{pac, Peripheral};
|
use crate::{pac, Peripheral};
|
||||||
@ -12,14 +12,14 @@ pub(crate) fn regs() -> &'static pac::dppic::RegisterBlock {
|
|||||||
|
|
||||||
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
|
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
|
||||||
/// Configure PPI channel to trigger `task` on `event`.
|
/// Configure PPI channel to trigger `task` on `event`.
|
||||||
pub fn new_one_to_one(ch: impl Peripheral<P = C> + 'd, event: Event, task: Task) -> Self {
|
pub fn new_one_to_one(ch: impl Peripheral<P = C> + 'd, event: Event<'d>, task: Task<'d>) -> Self {
|
||||||
Ppi::new_many_to_many(ch, [event], [task])
|
Ppi::new_many_to_many(ch, [event], [task])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {
|
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {
|
||||||
/// Configure PPI channel to trigger both `task1` and `task2` on `event`.
|
/// Configure PPI channel to trigger both `task1` and `task2` on `event`.
|
||||||
pub fn new_one_to_two(ch: impl Peripheral<P = C> + 'd, event: Event, task1: Task, task2: Task) -> Self {
|
pub fn new_one_to_two(ch: impl Peripheral<P = C> + 'd, event: Event<'d>, task1: Task<'d>, task2: Task<'d>) -> Self {
|
||||||
Ppi::new_many_to_many(ch, [event], [task1, task2])
|
Ppi::new_many_to_many(ch, [event], [task1, task2])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -30,8 +30,8 @@ impl<'d, C: ConfigurableChannel, const EVENT_COUNT: usize, const TASK_COUNT: usi
|
|||||||
/// Configure a DPPI channel to trigger all `tasks` when any of the `events` fires.
|
/// Configure a DPPI channel to trigger all `tasks` when any of the `events` fires.
|
||||||
pub fn new_many_to_many(
|
pub fn new_many_to_many(
|
||||||
ch: impl Peripheral<P = C> + 'd,
|
ch: impl Peripheral<P = C> + 'd,
|
||||||
events: [Event; EVENT_COUNT],
|
events: [Event<'d>; EVENT_COUNT],
|
||||||
tasks: [Task; TASK_COUNT],
|
tasks: [Task<'d>; TASK_COUNT],
|
||||||
) -> Self {
|
) -> Self {
|
||||||
into_ref!(ch);
|
into_ref!(ch);
|
||||||
|
|
||||||
|
@ -15,9 +15,10 @@
|
|||||||
//! many tasks and events, but any single task or event can only be coupled with one channel.
|
//! many tasks and events, but any single task or event can only be coupled with one channel.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
|
use core::marker::PhantomData;
|
||||||
use core::ptr::NonNull;
|
use core::ptr::NonNull;
|
||||||
|
|
||||||
use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef};
|
use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef};
|
||||||
|
|
||||||
use crate::{peripherals, Peripheral};
|
use crate::{peripherals, Peripheral};
|
||||||
|
|
||||||
@ -30,9 +31,9 @@ pub(crate) use _version::*;
|
|||||||
pub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> {
|
pub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> {
|
||||||
ch: PeripheralRef<'d, C>,
|
ch: PeripheralRef<'d, C>,
|
||||||
#[cfg(feature = "_dppi")]
|
#[cfg(feature = "_dppi")]
|
||||||
events: [Event; EVENT_COUNT],
|
events: [Event<'d>; EVENT_COUNT],
|
||||||
#[cfg(feature = "_dppi")]
|
#[cfg(feature = "_dppi")]
|
||||||
tasks: [Task; TASK_COUNT],
|
tasks: [Task<'d>; TASK_COUNT],
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PPI channel group driver.
|
/// PPI channel group driver.
|
||||||
@ -95,7 +96,7 @@ impl<'d, G: Group> PpiGroup<'d, G> {
|
|||||||
/// Get a reference to the "enable all" task.
|
/// Get a reference to the "enable all" task.
|
||||||
///
|
///
|
||||||
/// When triggered, it will enable all the channels in this group.
|
/// When triggered, it will enable all the channels in this group.
|
||||||
pub fn task_enable_all(&self) -> Task {
|
pub fn task_enable_all(&self) -> Task<'d> {
|
||||||
let n = self.g.number();
|
let n = self.g.number();
|
||||||
Task::from_reg(®s().tasks_chg[n].en)
|
Task::from_reg(®s().tasks_chg[n].en)
|
||||||
}
|
}
|
||||||
@ -103,7 +104,7 @@ impl<'d, G: Group> PpiGroup<'d, G> {
|
|||||||
/// Get a reference to the "disable all" task.
|
/// Get a reference to the "disable all" task.
|
||||||
///
|
///
|
||||||
/// When triggered, it will disable all the channels in this group.
|
/// When triggered, it will disable all the channels in this group.
|
||||||
pub fn task_disable_all(&self) -> Task {
|
pub fn task_disable_all(&self) -> Task<'d> {
|
||||||
let n = self.g.number();
|
let n = self.g.number();
|
||||||
Task::from_reg(®s().tasks_chg[n].dis)
|
Task::from_reg(®s().tasks_chg[n].dis)
|
||||||
}
|
}
|
||||||
@ -125,20 +126,28 @@ const REGISTER_DPPI_CONFIG_OFFSET: usize = 0x80 / core::mem::size_of::<u32>();
|
|||||||
/// When a task is subscribed to a PPI channel, it will run when the channel is triggered by
|
/// When a task is subscribed to a PPI channel, it will run when the channel is triggered by
|
||||||
/// a published event.
|
/// a published event.
|
||||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
pub struct Task(NonNull<u32>);
|
pub struct Task<'d>(NonNull<u32>, PhantomData<&'d ()>);
|
||||||
|
|
||||||
impl Task {
|
impl<'d> Task<'d> {
|
||||||
/// Create a new `Task` from a task register pointer
|
/// Create a new `Task` from a task register pointer
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// `ptr` must be a pointer to a valid `TASKS_*` register from an nRF peripheral.
|
/// `ptr` must be a pointer to a valid `TASKS_*` register from an nRF peripheral.
|
||||||
pub unsafe fn new_unchecked(ptr: NonNull<u32>) -> Self {
|
pub unsafe fn new_unchecked(ptr: NonNull<u32>) -> Self {
|
||||||
Self(ptr)
|
Self(ptr, PhantomData)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Triggers this task.
|
||||||
|
pub fn trigger(&mut self) {
|
||||||
|
unsafe { self.0.as_ptr().write_volatile(1) };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_reg<T>(reg: &T) -> Self {
|
pub(crate) fn from_reg<T>(reg: &T) -> Self {
|
||||||
Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
|
Self(
|
||||||
|
unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) },
|
||||||
|
PhantomData,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Address of subscription register for this task.
|
/// Address of subscription register for this task.
|
||||||
@ -151,26 +160,39 @@ impl Task {
|
|||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// NonNull is not send, but this event is only allowed to point at registers and those exist in any context on the same core.
|
/// NonNull is not send, but this event is only allowed to point at registers and those exist in any context on the same core.
|
||||||
unsafe impl Send for Task {}
|
unsafe impl Send for Task<'_> {}
|
||||||
|
|
||||||
/// Represents an event that a peripheral can publish.
|
/// Represents an event that a peripheral can publish.
|
||||||
///
|
///
|
||||||
/// An event can be set to publish on a PPI channel when the event happens.
|
/// An event can be set to publish on a PPI channel when the event happens.
|
||||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
pub struct Event(NonNull<u32>);
|
pub struct Event<'d>(NonNull<u32>, PhantomData<&'d ()>);
|
||||||
|
|
||||||
impl Event {
|
impl<'d> Event<'d> {
|
||||||
/// Create a new `Event` from an event register pointer
|
/// Create a new `Event` from an event register pointer
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// `ptr` must be a pointer to a valid `EVENTS_*` register from an nRF peripheral.
|
/// `ptr` must be a pointer to a valid `EVENTS_*` register from an nRF peripheral.
|
||||||
pub unsafe fn new_unchecked(ptr: NonNull<u32>) -> Self {
|
pub unsafe fn new_unchecked(ptr: NonNull<u32>) -> Self {
|
||||||
Self(ptr)
|
Self(ptr, PhantomData)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_reg<T>(reg: &T) -> Self {
|
pub(crate) fn from_reg<T>(reg: &'d T) -> Self {
|
||||||
Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) })
|
Self(
|
||||||
|
unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) },
|
||||||
|
PhantomData,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Describes whether this Event is currently in a triggered state.
|
||||||
|
pub fn is_triggered(&self) -> bool {
|
||||||
|
unsafe { self.0.as_ptr().read_volatile() == 1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear the current register's triggered state, reverting it to 0.
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
unsafe { self.0.as_ptr().write_volatile(0) };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Address of publish register for this event.
|
/// Address of publish register for this event.
|
||||||
@ -183,7 +205,7 @@ impl Event {
|
|||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// NonNull is not send, but this event is only allowed to point at registers and those exist in any context on the same core.
|
/// NonNull is not send, but this event is only allowed to point at registers and those exist in any context on the same core.
|
||||||
unsafe impl Send for Event {}
|
unsafe impl Send for Event<'_> {}
|
||||||
|
|
||||||
// ======================
|
// ======================
|
||||||
// traits
|
// traits
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
use embassy_hal_common::into_ref;
|
use embassy_hal_internal::into_ref;
|
||||||
|
|
||||||
use super::{Channel, ConfigurableChannel, Event, Ppi, StaticChannel, Task};
|
use super::{Channel, ConfigurableChannel, Event, Ppi, StaticChannel, Task};
|
||||||
use crate::{pac, Peripheral};
|
use crate::{pac, Peripheral};
|
||||||
|
|
||||||
impl Task {
|
impl<'d> Task<'d> {
|
||||||
fn reg_val(&self) -> u32 {
|
fn reg_val(&self) -> u32 {
|
||||||
self.0.as_ptr() as _
|
self.0.as_ptr() as _
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Event {
|
impl<'d> Event<'d> {
|
||||||
fn reg_val(&self) -> u32 {
|
fn reg_val(&self) -> u32 {
|
||||||
self.0.as_ptr() as _
|
self.0.as_ptr() as _
|
||||||
}
|
}
|
||||||
@ -34,7 +34,7 @@ impl<'d, C: StaticChannel> Ppi<'d, C, 0, 1> {
|
|||||||
|
|
||||||
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
|
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
|
||||||
/// Configure PPI channel to trigger `task` on `event`.
|
/// Configure PPI channel to trigger `task` on `event`.
|
||||||
pub fn new_one_to_one(ch: impl Peripheral<P = C> + 'd, event: Event, task: Task) -> Self {
|
pub fn new_one_to_one(ch: impl Peripheral<P = C> + 'd, event: Event<'d>, task: Task<'d>) -> Self {
|
||||||
into_ref!(ch);
|
into_ref!(ch);
|
||||||
|
|
||||||
let r = regs();
|
let r = regs();
|
||||||
@ -49,7 +49,7 @@ impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> {
|
|||||||
#[cfg(not(feature = "nrf51"))] // Not for nrf51 because of the fork task
|
#[cfg(not(feature = "nrf51"))] // Not for nrf51 because of the fork task
|
||||||
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {
|
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {
|
||||||
/// Configure PPI channel to trigger both `task1` and `task2` on `event`.
|
/// Configure PPI channel to trigger both `task1` and `task2` on `event`.
|
||||||
pub fn new_one_to_two(ch: impl Peripheral<P = C> + 'd, event: Event, task1: Task, task2: Task) -> Self {
|
pub fn new_one_to_two(ch: impl Peripheral<P = C> + 'd, event: Event<'d>, task1: Task<'d>, task2: Task<'d>) -> Self {
|
||||||
into_ref!(ch);
|
into_ref!(ch);
|
||||||
|
|
||||||
let r = regs();
|
let r = regs();
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
use core::sync::atomic::{compiler_fence, Ordering};
|
use core::sync::atomic::{compiler_fence, Ordering};
|
||||||
|
|
||||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
|
|
||||||
use crate::gpio::sealed::Pin as _;
|
use crate::gpio::sealed::Pin as _;
|
||||||
use crate::gpio::{AnyPin, Pin as GpioPin, PselBits};
|
use crate::gpio::{AnyPin, Pin as GpioPin, PselBits};
|
||||||
@ -181,7 +181,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
|
|||||||
|
|
||||||
/// Returns reference to `Stopped` event endpoint for PPI.
|
/// Returns reference to `Stopped` event endpoint for PPI.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn event_stopped(&self) -> Event {
|
pub fn event_stopped(&self) -> Event<'d> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
Event::from_reg(&r.events_stopped)
|
Event::from_reg(&r.events_stopped)
|
||||||
@ -189,7 +189,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
|
|||||||
|
|
||||||
/// Returns reference to `LoopsDone` event endpoint for PPI.
|
/// Returns reference to `LoopsDone` event endpoint for PPI.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn event_loops_done(&self) -> Event {
|
pub fn event_loops_done(&self) -> Event<'d> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
Event::from_reg(&r.events_loopsdone)
|
Event::from_reg(&r.events_loopsdone)
|
||||||
@ -197,7 +197,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
|
|||||||
|
|
||||||
/// Returns reference to `PwmPeriodEnd` event endpoint for PPI.
|
/// Returns reference to `PwmPeriodEnd` event endpoint for PPI.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn event_pwm_period_end(&self) -> Event {
|
pub fn event_pwm_period_end(&self) -> Event<'d> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
Event::from_reg(&r.events_pwmperiodend)
|
Event::from_reg(&r.events_pwmperiodend)
|
||||||
@ -205,7 +205,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
|
|||||||
|
|
||||||
/// Returns reference to `Seq0 End` event endpoint for PPI.
|
/// Returns reference to `Seq0 End` event endpoint for PPI.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn event_seq_end(&self) -> Event {
|
pub fn event_seq_end(&self) -> Event<'d> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
Event::from_reg(&r.events_seqend[0])
|
Event::from_reg(&r.events_seqend[0])
|
||||||
@ -213,7 +213,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
|
|||||||
|
|
||||||
/// Returns reference to `Seq1 End` event endpoint for PPI.
|
/// Returns reference to `Seq1 End` event endpoint for PPI.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn event_seq1_end(&self) -> Event {
|
pub fn event_seq1_end(&self) -> Event<'d> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
Event::from_reg(&r.events_seqend[1])
|
Event::from_reg(&r.events_seqend[1])
|
||||||
@ -221,7 +221,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
|
|||||||
|
|
||||||
/// Returns reference to `Seq0 Started` event endpoint for PPI.
|
/// Returns reference to `Seq0 Started` event endpoint for PPI.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn event_seq0_started(&self) -> Event {
|
pub fn event_seq0_started(&self) -> Event<'d> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
Event::from_reg(&r.events_seqstarted[0])
|
Event::from_reg(&r.events_seqstarted[0])
|
||||||
@ -229,7 +229,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
|
|||||||
|
|
||||||
/// Returns reference to `Seq1 Started` event endpoint for PPI.
|
/// Returns reference to `Seq1 Started` event endpoint for PPI.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn event_seq1_started(&self) -> Event {
|
pub fn event_seq1_started(&self) -> Event<'d> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
Event::from_reg(&r.events_seqstarted[1])
|
Event::from_reg(&r.events_seqstarted[1])
|
||||||
@ -240,7 +240,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
|
|||||||
///
|
///
|
||||||
/// Interacting with the sequence while it runs puts it in an unknown state
|
/// Interacting with the sequence while it runs puts it in an unknown state
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn task_start_seq0(&self) -> Task {
|
pub unsafe fn task_start_seq0(&self) -> Task<'d> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
Task::from_reg(&r.tasks_seqstart[0])
|
Task::from_reg(&r.tasks_seqstart[0])
|
||||||
@ -251,7 +251,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
|
|||||||
///
|
///
|
||||||
/// Interacting with the sequence while it runs puts it in an unknown state
|
/// Interacting with the sequence while it runs puts it in an unknown state
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn task_start_seq1(&self) -> Task {
|
pub unsafe fn task_start_seq1(&self) -> Task<'d> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
Task::from_reg(&r.tasks_seqstart[1])
|
Task::from_reg(&r.tasks_seqstart[1])
|
||||||
@ -262,7 +262,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
|
|||||||
///
|
///
|
||||||
/// Interacting with the sequence while it runs puts it in an unknown state
|
/// Interacting with the sequence while it runs puts it in an unknown state
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn task_next_step(&self) -> Task {
|
pub unsafe fn task_next_step(&self) -> Task<'d> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
Task::from_reg(&r.tasks_nextstep)
|
Task::from_reg(&r.tasks_nextstep)
|
||||||
@ -273,7 +273,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
|
|||||||
///
|
///
|
||||||
/// Interacting with the sequence while it runs puts it in an unknown state
|
/// Interacting with the sequence while it runs puts it in an unknown state
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn task_stop(&self) -> Task {
|
pub unsafe fn task_stop(&self) -> Task<'d> {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
Task::from_reg(&r.tasks_stop)
|
Task::from_reg(&r.tasks_stop)
|
||||||
|
@ -6,7 +6,7 @@ use core::future::poll_fn;
|
|||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
|
|
||||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
|
|
||||||
use crate::gpio::sealed::Pin as _;
|
use crate::gpio::sealed::Pin as _;
|
||||||
use crate::gpio::{AnyPin, Pin as GpioPin};
|
use crate::gpio::{AnyPin, Pin as GpioPin};
|
||||||
|
@ -7,8 +7,8 @@ use core::marker::PhantomData;
|
|||||||
use core::ptr;
|
use core::ptr;
|
||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
|
|
||||||
use embassy_hal_common::drop::OnDrop;
|
use embassy_hal_internal::drop::OnDrop;
|
||||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
|
use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
|
||||||
|
|
||||||
use crate::gpio::{self, Pin as GpioPin};
|
use crate::gpio::{self, Pin as GpioPin};
|
||||||
|
@ -8,8 +8,8 @@ use core::ptr;
|
|||||||
use core::sync::atomic::{AtomicPtr, Ordering};
|
use core::sync::atomic::{AtomicPtr, Ordering};
|
||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
|
|
||||||
use embassy_hal_common::drop::OnDrop;
|
use embassy_hal_internal::drop::OnDrop;
|
||||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
|
||||||
use crate::interrupt::typelevel::Interrupt;
|
use crate::interrupt::typelevel::Interrupt;
|
||||||
|
@ -6,8 +6,8 @@ use core::future::poll_fn;
|
|||||||
use core::sync::atomic::{compiler_fence, Ordering};
|
use core::sync::atomic::{compiler_fence, Ordering};
|
||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
|
|
||||||
use embassy_hal_common::drop::OnDrop;
|
use embassy_hal_internal::drop::OnDrop;
|
||||||
use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef};
|
use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef};
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
use pac::{saadc, SAADC};
|
use pac::{saadc, SAADC};
|
||||||
use saadc::ch::config::{GAIN_A, REFSEL_A, RESP_A, TACQ_A};
|
use saadc::ch::config::{GAIN_A, REFSEL_A, RESP_A, TACQ_A};
|
||||||
@ -320,7 +320,9 @@ impl<'d, const N: usize> Saadc<'d, N> {
|
|||||||
timer.cc(0).write(sample_counter);
|
timer.cc(0).write(sample_counter);
|
||||||
timer.cc(0).short_compare_clear();
|
timer.cc(0).short_compare_clear();
|
||||||
|
|
||||||
let mut sample_ppi = Ppi::new_one_to_one(ppi_ch2, timer.cc(0).event_compare(), Task::from_reg(&r.tasks_sample));
|
let timer_cc = timer.cc(0);
|
||||||
|
|
||||||
|
let mut sample_ppi = Ppi::new_one_to_one(ppi_ch2, timer_cc.event_compare(), Task::from_reg(&r.tasks_sample));
|
||||||
|
|
||||||
timer.start();
|
timer.start();
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ use core::sync::atomic::{compiler_fence, Ordering};
|
|||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
|
|
||||||
use embassy_embedded_hal::SetConfig;
|
use embassy_embedded_hal::SetConfig;
|
||||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
|
pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
|
||||||
pub use pac::spim0::frequency::FREQUENCY_A as Frequency;
|
pub use pac::spim0::frequency::FREQUENCY_A as Frequency;
|
||||||
|
|
||||||
@ -468,25 +468,19 @@ mod eh1 {
|
|||||||
type Error = Error;
|
type Error = Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> embedded_hal_1::spi::SpiBusFlush for Spim<'d, T> {
|
impl<'d, T: Instance> embedded_hal_1::spi::SpiBus<u8> for Spim<'d, T> {
|
||||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance> embedded_hal_1::spi::SpiBusRead<u8> for Spim<'d, T> {
|
|
||||||
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
self.blocking_transfer(words, &[])
|
self.blocking_transfer(words, &[])
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance> embedded_hal_1::spi::SpiBusWrite<u8> for Spim<'d, T> {
|
|
||||||
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
|
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
|
||||||
self.blocking_write(words)
|
self.blocking_write(words)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance> embedded_hal_1::spi::SpiBus<u8> for Spim<'d, T> {
|
|
||||||
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
|
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
|
||||||
self.blocking_transfer(read, write)
|
self.blocking_transfer(read, write)
|
||||||
}
|
}
|
||||||
@ -502,25 +496,19 @@ mod eha {
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusFlush for Spim<'d, T> {
|
impl<'d, T: Instance> embedded_hal_async::spi::SpiBus<u8> for Spim<'d, T> {
|
||||||
async fn flush(&mut self) -> Result<(), Error> {
|
async fn flush(&mut self) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusRead<u8> for Spim<'d, T> {
|
|
||||||
async fn read(&mut self, words: &mut [u8]) -> Result<(), Error> {
|
async fn read(&mut self, words: &mut [u8]) -> Result<(), Error> {
|
||||||
self.read(words).await
|
self.read(words).await
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance> embedded_hal_async::spi::SpiBusWrite<u8> for Spim<'d, T> {
|
|
||||||
async fn write(&mut self, data: &[u8]) -> Result<(), Error> {
|
async fn write(&mut self, data: &[u8]) -> Result<(), Error> {
|
||||||
self.write(data).await
|
self.write(data).await
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: Instance> embedded_hal_async::spi::SpiBus<u8> for Spim<'d, T> {
|
|
||||||
async fn transfer(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> {
|
async fn transfer(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(), Error> {
|
||||||
self.transfer(rx, tx).await
|
self.transfer(rx, tx).await
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ use core::sync::atomic::{compiler_fence, Ordering};
|
|||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
|
|
||||||
use embassy_embedded_hal::SetConfig;
|
use embassy_embedded_hal::SetConfig;
|
||||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
|
pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
|
||||||
|
|
||||||
use crate::chip::FORCE_COPY_BUFFER_SIZE;
|
use crate::chip::FORCE_COPY_BUFFER_SIZE;
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
use core::future::poll_fn;
|
use core::future::poll_fn;
|
||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
|
|
||||||
use embassy_hal_common::drop::OnDrop;
|
use embassy_hal_internal::drop::OnDrop;
|
||||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
use fixed::types::I30F2;
|
use fixed::types::I30F2;
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
#![macro_use]
|
#![macro_use]
|
||||||
|
|
||||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
|
|
||||||
use crate::ppi::{Event, Task};
|
use crate::ppi::{Event, Task};
|
||||||
use crate::{pac, Peripheral};
|
use crate::{pac, Peripheral};
|
||||||
@ -168,21 +168,21 @@ impl<'d, T: Instance> Timer<'d, T> {
|
|||||||
/// Returns the START task, for use with PPI.
|
/// Returns the START task, for use with PPI.
|
||||||
///
|
///
|
||||||
/// When triggered, this task starts the timer.
|
/// When triggered, this task starts the timer.
|
||||||
pub fn task_start(&self) -> Task {
|
pub fn task_start(&self) -> Task<'d> {
|
||||||
Task::from_reg(&T::regs().tasks_start)
|
Task::from_reg(&T::regs().tasks_start)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the STOP task, for use with PPI.
|
/// Returns the STOP task, for use with PPI.
|
||||||
///
|
///
|
||||||
/// When triggered, this task stops the timer.
|
/// When triggered, this task stops the timer.
|
||||||
pub fn task_stop(&self) -> Task {
|
pub fn task_stop(&self) -> Task<'d> {
|
||||||
Task::from_reg(&T::regs().tasks_stop)
|
Task::from_reg(&T::regs().tasks_stop)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the CLEAR task, for use with PPI.
|
/// Returns the CLEAR task, for use with PPI.
|
||||||
///
|
///
|
||||||
/// When triggered, this task resets the timer's counter to 0.
|
/// When triggered, this task resets the timer's counter to 0.
|
||||||
pub fn task_clear(&self) -> Task {
|
pub fn task_clear(&self) -> Task<'d> {
|
||||||
Task::from_reg(&T::regs().tasks_clear)
|
Task::from_reg(&T::regs().tasks_clear)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +190,7 @@ impl<'d, T: Instance> Timer<'d, T> {
|
|||||||
///
|
///
|
||||||
/// When triggered, this task increments the timer's counter by 1.
|
/// When triggered, this task increments the timer's counter by 1.
|
||||||
/// Only works in counter mode.
|
/// Only works in counter mode.
|
||||||
pub fn task_count(&self) -> Task {
|
pub fn task_count(&self) -> Task<'d> {
|
||||||
Task::from_reg(&T::regs().tasks_count)
|
Task::from_reg(&T::regs().tasks_count)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,14 +258,14 @@ impl<'d, T: Instance> Cc<'d, T> {
|
|||||||
/// Returns this CC register's CAPTURE task, for use with PPI.
|
/// Returns this CC register's CAPTURE task, for use with PPI.
|
||||||
///
|
///
|
||||||
/// When triggered, this task will capture the current value of the timer's counter in this register.
|
/// When triggered, this task will capture the current value of the timer's counter in this register.
|
||||||
pub fn task_capture(&self) -> Task {
|
pub fn task_capture(&self) -> Task<'d> {
|
||||||
Task::from_reg(&T::regs().tasks_capture)
|
Task::from_reg(&T::regs().tasks_capture)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns this CC register's COMPARE event, for use with PPI.
|
/// Returns this CC register's COMPARE event, for use with PPI.
|
||||||
///
|
///
|
||||||
/// This event will fire when the timer's counter reaches the value in this CC register.
|
/// This event will fire when the timer's counter reaches the value in this CC register.
|
||||||
pub fn event_compare(&self) -> Event {
|
pub fn event_compare(&self) -> Event<'d> {
|
||||||
Event::from_reg(&T::regs().events_compare[self.n])
|
Event::from_reg(&T::regs().events_compare[self.n])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ use core::sync::atomic::Ordering::SeqCst;
|
|||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
|
|
||||||
use embassy_embedded_hal::SetConfig;
|
use embassy_embedded_hal::SetConfig;
|
||||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
use embassy_time::{Duration, Instant};
|
use embassy_time::{Duration, Instant};
|
||||||
|
@ -8,7 +8,7 @@ use core::sync::atomic::compiler_fence;
|
|||||||
use core::sync::atomic::Ordering::SeqCst;
|
use core::sync::atomic::Ordering::SeqCst;
|
||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
|
|
||||||
use embassy_hal_common::{into_ref, PeripheralRef};
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
use embassy_time::{Duration, Instant};
|
use embassy_time::{Duration, Instant};
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user