Compare commits
638 Commits
Author | SHA1 | Date | |
---|---|---|---|
cf13f70ea9 | |||
6bdacb4f69 | |||
ff3baf1e90 | |||
d5dcbadbbe | |||
0ad0d27150 | |||
03d500f548 | |||
8b46343b34 | |||
a3ea01473a | |||
cf84c8bfd1 | |||
09d7950313 | |||
267cbaebe6 | |||
31fc337e2f | |||
135f350020 | |||
897663e023 | |||
2218d30c80 | |||
f5c9e3baa6 | |||
47bac9df70 | |||
3efc3eee57 | |||
bc65b8f7ec | |||
78f8e6112a | |||
996c3c1f7e | |||
171cdb94c7 | |||
1fbc150fd6 | |||
259cf6192b | |||
5528c33649 | |||
e8ff5a2baf | |||
4f8c79c911 | |||
738971938a | |||
dec7c1a28b | |||
e0727fe1f6 | |||
ba9cc102e2 | |||
32b59148a8 | |||
bd2e6b0422 | |||
85059f693b | |||
c211d51c06 | |||
7f1fd199bb | |||
5ddd2cf9a6 | |||
88f893da45 | |||
aedd41eac4 | |||
19ba7da3fd | |||
06a83c0f89 | |||
766ec77ec5 | |||
d1af696605 | |||
2386619f1f | |||
382949e1ff | |||
454828accb | |||
cf82fa687c | |||
7f258cd3c4 | |||
3a939fb511 | |||
b8e9e00b44 | |||
ff2f1049b1 | |||
be17e1b363 | |||
f3c77e59c4 | |||
30424d83ff | |||
e3be0b957a | |||
5221705495 | |||
814e096d22 | |||
5a60024af7 | |||
f482a105b8 | |||
7589b5e13e | |||
2efa73f431 | |||
270ec324b0 | |||
ca0d02933b | |||
5bc7557826 | |||
4947b13615 | |||
006260fedd | |||
e3ee24017d | |||
467b53076c | |||
27e6634c9d | |||
1f9b649f80 | |||
bef9b7a853 | |||
50a983fd9b | |||
7e5deae589 | |||
a2c440ef8c | |||
fd670a9ae5 | |||
ef69f386ab | |||
872f1ec4c2 | |||
6ccd8c051e | |||
c46418f123 | |||
19ff043acd | |||
ea99671729 | |||
8eff749823 | |||
cdcd3e26dd | |||
18c1f9dd56 | |||
ac7134ed0d | |||
ace5221080 | |||
2376b3bdfa | |||
f00e97a5f1 | |||
066dc297ed | |||
4fe344ebc0 | |||
0f2208c0af | |||
6c42885d4a | |||
39c7371621 | |||
4647df14b1 | |||
3b33cc4691 | |||
b3367be9c8 | |||
da4feb3693 | |||
05a4bb3a4a | |||
4b4c28d875 | |||
1977acdb11 | |||
0b015bd727 | |||
79acb560ec | |||
94586576a0 | |||
a3e200d011 | |||
3bccb67231 | |||
553f0158c0 | |||
b44a5fcaf6 | |||
8effff3383 | |||
05f983c607 | |||
d44383e9a7 | |||
37a773c037 | |||
d1adc93614 | |||
78a7ee7ec4 | |||
e3fe13e905 | |||
38bfa6916f | |||
50139752bc | |||
db4cd73894 | |||
8369f7614a | |||
326bc98bd2 | |||
584fc358fd | |||
b8f9341edc | |||
7ca557b917 | |||
8d8d50cc7a | |||
5948d934d3 | |||
746936f8cd | |||
8f543062aa | |||
15660cfc68 | |||
74f70dc7b4 | |||
7084570478 | |||
58719dcb58 | |||
b4eef6b1ee | |||
70a700e430 | |||
b8679c0cc8 | |||
ad861179cc | |||
d9b00c01e0 | |||
239ad5ebea | |||
46cffcc8d4 | |||
e6462f1e88 | |||
d464d1a841 | |||
980c3cf42b | |||
28eb4cd817 | |||
3de01bc223 | |||
0272deb158 | |||
c4a8b79dbc | |||
fa45dcd034 | |||
6ff91851b1 | |||
8911a4d855 | |||
056c409443 | |||
3f2abd4fd5 | |||
dc467e89a0 | |||
655ed3aa88 | |||
14ec0d27bf | |||
649f1a122a | |||
413b394d31 | |||
7ea2c3508a | |||
ec744558b2 | |||
1b9292dbcd | |||
44486c5b39 | |||
1f51367eb9 | |||
d6f42eafad | |||
dcce8945af | |||
d98c064bfe | |||
a904538555 | |||
4ef3dc5b90 | |||
bab61f9665 | |||
2765f0978f | |||
bc07539133 | |||
b4a82b7ed4 | |||
e2688dda22 | |||
d0d8585e4c | |||
729d69246a | |||
e78a6db151 | |||
f8721c3786 | |||
e519e00265 | |||
35bb20abe7 | |||
dd6a29adb2 | |||
5f9602d28b | |||
74683c706b | |||
2795e1350d | |||
aed3e5674f | |||
d941882066 | |||
3f74ff7235 | |||
9cead47212 | |||
78739d4aa9 | |||
e07e790613 | |||
ca283eed0c | |||
57edf289ea | |||
3912f5d67b | |||
c9b50e46a5 | |||
aa97fe7cbd | |||
ad07ea0290 | |||
573734008a | |||
f4a78e00a7 | |||
0d6094c8b1 | |||
b1e5b6ffe1 | |||
b6fc682117 | |||
0beb84768e | |||
b98a279367 | |||
e8a3cfaed6 | |||
0cc3e18db6 | |||
6b19c0abd1 | |||
f956d19e6e | |||
ceb0d0bf08 | |||
b3879ec223 | |||
bda99e59ec | |||
25c2a9baaa | |||
1e362c750b | |||
1a51a84313 | |||
7f72dbdaf2 | |||
e8c162ac03 | |||
1aaa19748a | |||
9e230b64a4 | |||
17b4cf8ce7 | |||
df4aa0fe25 | |||
188ee59ba6 | |||
591612db7e | |||
d673f8a865 | |||
82593bd404 | |||
a39ae12edc | |||
0ef1cb29f7 | |||
64ab23d17d | |||
18c9bcd44a | |||
e895ea2d8b | |||
b9e13cb5d1 | |||
46ff2c82aa | |||
a84ad741a4 | |||
412bcad2d1 | |||
e70c531d3d | |||
7c5f963d1f | |||
62e1e1637c | |||
3d03c18d4f | |||
2157c5a4e3 | |||
0fb677aad7 | |||
b1d0947a18 | |||
5b3f75dc72 | |||
6f2995cd4c | |||
88ada52146 | |||
d622181205 | |||
630443a4d6 | |||
035800bfbd | |||
c7803bb8f4 | |||
d496a1213c | |||
241488ef1c | |||
88b2cdd6a0 | |||
35ffdf2143 | |||
3cbc687424 | |||
4f7b831676 | |||
f20f170b1f | |||
67010d123c | |||
51708c8ed1 | |||
361fde35cf | |||
7ce3b19389 | |||
10f08445e4 | |||
f24a1b62bb | |||
bbd12c9372 | |||
6906cc9c25 | |||
cb211f88d3 | |||
3f262a2603 | |||
d94b9fe6fb | |||
b478640463 | |||
846f2fc6e4 | |||
683d5c3066 | |||
a3574e519a | |||
3e3317e8bd | |||
e7aeb9b29f | |||
7fd868ade9 | |||
6e6df22979 | |||
f7980885a5 | |||
5a1393aa0b | |||
40e4ca4751 | |||
1d810baa18 | |||
213b4c9dca | |||
889d482d2d | |||
ea0e83a7f9 | |||
f0497039ed | |||
aff77d2b65 | |||
a7c6999670 | |||
18e96898ea | |||
870dcc5970 | |||
5c5e681819 | |||
f54753beaa | |||
b24520579a | |||
31d4516516 | |||
66e62e9994 | |||
eeedaf2e76 | |||
1fc35c753b | |||
2aaf4bf96b | |||
cd92bc3145 | |||
4a156df7a1 | |||
c46e758e2c | |||
8a10948ce9 | |||
7045c53170 | |||
0621e957a0 | |||
a818f33658 | |||
3bfbf2697f | |||
7559f9e583 | |||
c8fdbe19f9 | |||
2e50bf667a | |||
ba62037642 | |||
824556c9c8 | |||
f7c0ec66d2 | |||
69bb455c60 | |||
575db17264 | |||
0aa99e66f7 | |||
3264941c1b | |||
a57d383b1d | |||
9a7fda87b0 | |||
adc810d24b | |||
75baf186f3 | |||
a6bbb130c5 | |||
d1ac6d2824 | |||
636958ff70 | |||
7f6fe93f55 | |||
e9b1904db8 | |||
02d2c06b23 | |||
debda2f52c | |||
97ca0e77bf | |||
66e399b5c6 | |||
01eb1a7339 | |||
f0d6ee69bc | |||
65f81a1f57 | |||
ecdd7c0e2f | |||
d7d79f3068 | |||
f65a96c541 | |||
eb368f77a4 | |||
57ccc1051a | |||
ac84631a2a | |||
70a91945fc | |||
32b89eeba1 | |||
063e6f96da | |||
4cec4b0548 | |||
e6c47c3718 | |||
2f7c2750d1 | |||
032b1f2d59 | |||
f76d50e837 | |||
ee5ea7aa06 | |||
251d004708 | |||
cd68f85501 | |||
b6c0ddb7df | |||
ee93bbf1d4 | |||
7b1cd42936 | |||
1cd3ae9bd5 | |||
bfcca79c1e | |||
c283e2d1b9 | |||
4a43cd3982 | |||
0dda888c95 | |||
b91d1eaca0 | |||
5a19d18b9c | |||
ed7b6056a6 | |||
322f9cb153 | |||
3ec4e6320a | |||
233aa1b53a | |||
21915a9a3f | |||
d0d0ceec6a | |||
9be61a2967 | |||
0cfa8d1bb5 | |||
cd12c9cbce | |||
eff73d6dfa | |||
3616d68aaa | |||
904f1cfc7a | |||
e62bf6a9fd | |||
7526b8edba | |||
0c97ce2fcc | |||
62d6bb6c8a | |||
c3ef98a73d | |||
6186fe0807 | |||
c4cff0b79b | |||
2e5ab7981e | |||
9d311121f2 | |||
25c3754cd2 | |||
85c6f23dcb | |||
9c6a2d9cbd | |||
b67b179933 | |||
d4ed8e5779 | |||
3e054a6f0d | |||
c6d53e7bce | |||
3bf8e4de5f | |||
9090a78df4 | |||
022d870d61 | |||
534eb960e9 | |||
7a2b8481f7 | |||
710874021a | |||
3a8e0d4a27 | |||
b217d147de | |||
68c4820dde | |||
f30fc949ff | |||
75c1a9a3aa | |||
4075ac84e1 | |||
f01609036f | |||
930b8f3495 | |||
38e7709a24 | |||
65ed19aae2 | |||
be9c05fbc9 | |||
42176b1a3a | |||
e1a0635ca3 | |||
b887b7516e | |||
c6513f93fe | |||
59f706ee2f | |||
ba7d74ac16 | |||
0324cee0ca | |||
8ac5c1a963 | |||
e7ab044935 | |||
0c4cddfafb | |||
e561e673c2 | |||
09dfcd6b66 | |||
bb8a1b7f1f | |||
b012a67df7 | |||
40f30fa4cd | |||
f197aa9419 | |||
80a740bc72 | |||
ad524373ed | |||
9badf740d8 | |||
6e901846df | |||
c0a6c78a14 | |||
58280048e3 | |||
00824af82b | |||
adf9ffb109 | |||
bc203ebe4b | |||
e042b3056d | |||
9dc927250c | |||
b856d760f4 | |||
a742a80171 | |||
a9dc887060 | |||
137e47f98d | |||
90ade1b311 | |||
a931dfd9ad | |||
eff61648fe | |||
9228a6334b | |||
3c9dfbbc9d | |||
ce0add61f8 | |||
bf6e06060b | |||
eb12114345 | |||
4b252586e5 | |||
5f6a915a32 | |||
923f1851ee | |||
bd267a6479 | |||
f3aa0cfe5a | |||
6ea5aa347d | |||
8c13126cff | |||
81da9ca621 | |||
729338875c | |||
f98c8886b2 | |||
05a9b11316 | |||
a1036e111e | |||
561126b0d6 | |||
2af97e7595 | |||
2008030a8b | |||
480328d07e | |||
05218a52e6 | |||
d2a2734752 | |||
5ad34404af | |||
a7b1e51650 | |||
70005c3956 | |||
93adbb9922 | |||
e80df91e02 | |||
5cb58754d4 | |||
5c83179071 | |||
49608714aa | |||
f116ca83e0 | |||
6e5f3f9515 | |||
5924cc8b49 | |||
0d49aaf8ca | |||
5dd9e9b3b7 | |||
d42cfda2db | |||
d1f4511cd1 | |||
fa8d5da4a5 | |||
7bc57ca3f7 | |||
7ad102695d | |||
4999de6f82 | |||
9b8bdad403 | |||
3c3fb0a529 | |||
dc4c27aead | |||
74eb519810 | |||
61dc36b1f2 | |||
55a0a15be2 | |||
fc8f96fea5 | |||
08175f191a | |||
8da987cfbb | |||
1be5f193c3 | |||
4a632e08b7 | |||
6cdee93934 | |||
5c8c1b2146 | |||
2addfc4b86 | |||
92df87781d | |||
a35d149cb1 | |||
c52320f467 | |||
2f9b59c5cf | |||
ce91fb2bfc | |||
95b3d9eb3b | |||
f1e7205055 | |||
6627824c02 | |||
d62533879c | |||
e1a8caffa4 | |||
dffdb9268b | |||
22e820b743 | |||
aa14f18d3f | |||
39f1b26a39 | |||
e70143ef8f | |||
d6654bf8b5 | |||
322a4a8401 | |||
ee5591017b | |||
b1b32f079b | |||
e8a462768e | |||
ce0d787781 | |||
fa2e63f74b | |||
23f3889167 | |||
a72a91ab06 | |||
b9ef831ff7 | |||
e538aeacc8 | |||
6b3745fc47 | |||
6200d22438 | |||
901d31e8f8 | |||
79146c4bd5 | |||
d9eae79f3e | |||
3d1b4116ec | |||
901f0257bd | |||
fec4194ae6 | |||
f866735802 | |||
20ea76c19c | |||
e1951f3ddf | |||
8b5fb254c0 | |||
624f786203 | |||
a57e48459e | |||
c604d8a8f1 | |||
be0c52bf5e | |||
44bb405683 | |||
e8587e2fba | |||
5d8817d109 | |||
c79a84a98a | |||
96edbd84fb | |||
dc400a0539 | |||
04b09a2acb | |||
481d2998ef | |||
4b695120fc | |||
bd9021ca1d | |||
6e63c8d4bf | |||
70acc093dd | |||
f7c2db245f | |||
2543bcafaf | |||
9f2fc04caa | |||
f32bfafa61 | |||
e03239e88d | |||
7d5e62d4a7 | |||
65d36f9f99 | |||
347ff18644 | |||
1e80fd81f7 | |||
52e269edbb | |||
c849620cd6 | |||
6f83acc010 | |||
2b7b7a917d | |||
f1488864eb | |||
7cf327130e | |||
02b0523199 | |||
47175198c0 | |||
4b20e6c258 | |||
83b4c01273 | |||
e313ca4ae8 | |||
ad64d7b20b | |||
5626a28af2 | |||
766ae724cb | |||
00b9f9acef | |||
d46920dce6 | |||
e1ed492577 | |||
2405aff11f | |||
a402aed3d1 | |||
c573959a95 | |||
561696dfad | |||
b1abaa380f | |||
e640933e2f | |||
183824fbdd | |||
5d0c40f61d | |||
fdfe12fe1c | |||
4bfbcd6c72 | |||
0da793e5de | |||
0dcb34fc7d | |||
feaeee1e83 | |||
a6ef314be1 | |||
daeb497045 | |||
88eb5cca71 | |||
cd128c20fa | |||
087ef918bf | |||
75cae09e79 | |||
bbe1d96045 | |||
3ddc9cd110 | |||
de2773afdd | |||
2930be5e17 | |||
09f087e53e | |||
044b837caa | |||
ad0a306ea5 | |||
b5fffbf66f | |||
537e2f3b44 | |||
80d7193b5b | |||
8315cf064e | |||
a2a26f489b | |||
f27620cc0b | |||
2911774849 | |||
6da75ea285 | |||
aa2fa29b89 | |||
c28a6bdd0b | |||
5a158b94bd | |||
c091ed2f63 | |||
45e9e51bdc | |||
7c77d2bd94 | |||
f76d94098d | |||
07233ca5b4 | |||
9fb14379c3 | |||
309c3d6b47 | |||
f1f4943ca5 | |||
1b20ba27b1 | |||
f5e96a9d60 | |||
e8b961232b | |||
85a9f5816f | |||
7bcc7e8962 | |||
b118c4cc1b | |||
f090a38dde | |||
dcd1a91966 | |||
49847ff432 | |||
b9d4b18f14 | |||
3a1ed823f8 | |||
9c647dd0e8 | |||
582ef90994 | |||
5e381d49da | |||
af59fa0a7e | |||
4907ccaa4d | |||
2c38dd17b9 | |||
d36e7abb71 | |||
e11db9fa59 | |||
f877a5889d | |||
db54edf56c | |||
4072a16af6 | |||
44a5c32ea4 | |||
b9889ad3b5 | |||
527bdc57b9 | |||
de01fe352b | |||
70662ec4ba | |||
f033089625 | |||
6d8a5c6c20 | |||
3aedbce5c2 |
14
.github/ci/build-stable.sh
vendored
14
.github/ci/build-stable.sh
vendored
@ -8,9 +8,23 @@ 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
|
||||||
|
|
||||||
|
# needed for "dumb HTTP" transport support
|
||||||
|
# used when pointing stm32-metapac to a CI-built one.
|
||||||
|
export CARGO_NET_GIT_FETCH_WITH_CLI=true
|
||||||
|
|
||||||
|
# Restore lockfiles
|
||||||
|
if [ -f /ci/cache/lockfiles.tar ]; then
|
||||||
|
echo Restoring lockfiles...
|
||||||
|
tar xf /ci/cache/lockfiles.tar
|
||||||
|
fi
|
||||||
|
|
||||||
hashtime restore /ci/cache/filetime.json || true
|
hashtime restore /ci/cache/filetime.json || true
|
||||||
hashtime save /ci/cache/filetime.json
|
hashtime save /ci/cache/filetime.json
|
||||||
|
|
||||||
sed -i 's/channel.*/channel = "stable"/g' rust-toolchain.toml
|
sed -i 's/channel.*/channel = "stable"/g' rust-toolchain.toml
|
||||||
|
|
||||||
./ci_stable.sh
|
./ci_stable.sh
|
||||||
|
|
||||||
|
# Save lockfiles
|
||||||
|
echo Saving lockfiles...
|
||||||
|
find . -type f -name Cargo.lock -exec tar -cf /ci/cache/lockfiles.tar '{}' \+
|
14
.github/ci/build.sh
vendored
14
.github/ci/build.sh
vendored
@ -14,7 +14,21 @@ if [ -f /ci/secrets/teleprobe-token.txt ]; then
|
|||||||
export TELEPROBE_CACHE=/ci/cache/teleprobe_cache.json
|
export TELEPROBE_CACHE=/ci/cache/teleprobe_cache.json
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# needed for "dumb HTTP" transport support
|
||||||
|
# used when pointing stm32-metapac to a CI-built one.
|
||||||
|
export CARGO_NET_GIT_FETCH_WITH_CLI=true
|
||||||
|
|
||||||
|
# Restore lockfiles
|
||||||
|
if [ -f /ci/cache/lockfiles.tar ]; then
|
||||||
|
echo Restoring lockfiles...
|
||||||
|
tar xf /ci/cache/lockfiles.tar
|
||||||
|
fi
|
||||||
|
|
||||||
hashtime restore /ci/cache/filetime.json || true
|
hashtime restore /ci/cache/filetime.json || true
|
||||||
hashtime save /ci/cache/filetime.json
|
hashtime save /ci/cache/filetime.json
|
||||||
|
|
||||||
./ci.sh
|
./ci.sh
|
||||||
|
|
||||||
|
# Save lockfiles
|
||||||
|
echo Saving lockfiles...
|
||||||
|
find . -type f -name Cargo.lock -exec tar -cf /ci/cache/lockfiles.tar '{}' \+
|
2
.github/ci/doc.sh
vendored
2
.github/ci/doc.sh
vendored
@ -48,8 +48,8 @@ kubectl cp webroot/static $POD:/data
|
|||||||
|
|
||||||
# build and upload stm32 last
|
# build and upload stm32 last
|
||||||
# so that it doesn't prevent other crates from getting docs updates when it breaks.
|
# so that it doesn't prevent other crates from getting docs updates when it breaks.
|
||||||
|
|
||||||
rm -rf webroot
|
rm -rf webroot
|
||||||
docserver-builder -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup
|
docserver-builder -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup
|
||||||
|
|
||||||
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 webroot/crates $POD:/data
|
kubectl cp webroot/crates $POD:/data
|
||||||
|
7
.github/ci/test.sh
vendored
7
.github/ci/test.sh
vendored
@ -8,9 +8,16 @@ 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
|
||||||
|
|
||||||
|
# needed for "dumb HTTP" transport support
|
||||||
|
# used when pointing stm32-metapac to a CI-built one.
|
||||||
|
export CARGO_NET_GIT_FETCH_WITH_CLI=true
|
||||||
|
|
||||||
hashtime restore /ci/cache/filetime.json || true
|
hashtime restore /ci/cache/filetime.json || true
|
||||||
hashtime save /ci/cache/filetime.json
|
hashtime save /ci/cache/filetime.json
|
||||||
|
|
||||||
|
MIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --manifest-path ./embassy-executor/Cargo.toml
|
||||||
|
MIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --manifest-path ./embassy-executor/Cargo.toml --features nightly
|
||||||
|
|
||||||
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-internal/Cargo.toml
|
cargo test --manifest-path ./embassy-hal-internal/Cargo.toml
|
||||||
|
@ -62,9 +62,9 @@ async fn blink(pin: AnyPin) {
|
|||||||
loop {
|
loop {
|
||||||
// Timekeeping is globally available, no need to mess with hardware timers.
|
// Timekeeping is globally available, no need to mess with hardware timers.
|
||||||
led.set_high();
|
led.set_high();
|
||||||
Timer::after(Duration::from_millis(150)).await;
|
Timer::after_millis(150).await;
|
||||||
led.set_low();
|
led.set_low();
|
||||||
Timer::after(Duration::from_millis(150)).await;
|
Timer::after_millis(150).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
137
ci.sh
137
ci.sh
@ -1,9 +1,12 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
set -euo pipefail
|
set -eo pipefail
|
||||||
|
|
||||||
export RUSTFLAGS=-Dwarnings
|
export RUSTFLAGS=-Dwarnings
|
||||||
export DEFMT_LOG=trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info
|
export DEFMT_LOG=trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info
|
||||||
|
if [[ -z "${CARGO_TARGET_DIR}" ]]; then
|
||||||
|
export CARGO_TARGET_DIR=target_ci
|
||||||
|
fi
|
||||||
|
|
||||||
TARGET=$(rustc -vV | sed -n 's|host: ||p')
|
TARGET=$(rustc -vV | sed -n 's|host: ||p')
|
||||||
|
|
||||||
@ -35,8 +38,9 @@ cargo batch \
|
|||||||
--- 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,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,proto-ipv4,igmp,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,nightly \
|
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly,dhcpv4-hostname \
|
||||||
--- 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-ieee802154 \
|
--- 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,medium-ieee802154 \
|
||||||
@ -66,46 +70,69 @@ cargo batch \
|
|||||||
--- 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-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,time \
|
||||||
--- 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,time \
|
||||||
--- 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,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti,time-driver-any,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,time-driver-any,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f410tb,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f401ve,defmt,exti,time-driver-any,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f411ce,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f405zg,defmt,exti,time-driver-any,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f413vh,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f407zg,defmt,exti,time-driver-any,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f429zi,log,exti,time-driver-any,unstable-traits,embedded-sdmmc \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f401ve,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f730i8,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f405zg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f407zg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f410tb,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l476vg,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f411ce,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l422cb,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f412zg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wb15cc,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f413vh,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l072cz,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f415zg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l041f6,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f417zg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32l151cb-a,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f423zh,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f398ve,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f427zi,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f378cc,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f429zi,log,exti,time-driver-any,unstable-traits,embedded-sdmmc,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32g0c1ve,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f437zi,log,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f217zg,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f439zi,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- 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 thumbv7em-none-eabi --features nightly,stm32f446ze,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- 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,stm32f469zi,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- 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,stm32f479zi,defmt,exti,time-driver-any,unstable-traits,embedded-sdmmc,time \
|
||||||
--- 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 thumbv7em-none-eabi --features nightly,stm32f730i8,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- 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 thumbv7em-none-eabi --features nightly,stm32h753zi,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- 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 thumbv7em-none-eabi --features nightly,stm32h735zg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- 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 thumbv7em-none-eabi --features nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h503rb,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h725re,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h562ag,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l476vg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l422cb,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wb15cc,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l072cz,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l041f6,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l073cz,defmt,exti,time-driver-any,unstable-traits,low-power,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32l151cb-a,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f398ve,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f378cc,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32g0c1ve,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f217zg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,stm32l552ze,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32wl54jc-cm0p,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wle5jb,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32g474pe,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f107vc,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f103re,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h503rb,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h562ag,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features ''\
|
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features ''\
|
||||||
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log' \
|
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log' \
|
||||||
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt' \
|
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt' \
|
||||||
@ -144,9 +171,10 @@ cargo batch \
|
|||||||
--- build --release --manifest-path examples/stm32l5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32l5 \
|
--- build --release --manifest-path examples/stm32l5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32l5 \
|
||||||
--- build --release --manifest-path examples/stm32u5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32u5 \
|
--- build --release --manifest-path examples/stm32u5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32u5 \
|
||||||
--- build --release --manifest-path examples/stm32wb/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wb \
|
--- build --release --manifest-path examples/stm32wb/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wb \
|
||||||
|
--- build --release --manifest-path examples/stm32wba/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32wba \
|
||||||
--- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wl \
|
--- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wl \
|
||||||
--- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840,skip-include --out-dir out/examples/boot/nrf \
|
--- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840,skip-include --out-dir out/examples/boot/nrf52840 \
|
||||||
--- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns,skip-include --out-dir out/examples/boot/nrf \
|
--- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns,skip-include --out-dir out/examples/boot/nrf9160 \
|
||||||
--- build --release --manifest-path examples/boot/application/rp/Cargo.toml --target thumbv6m-none-eabi --features skip-include --out-dir out/examples/boot/rp \
|
--- build --release --manifest-path examples/boot/application/rp/Cargo.toml --target thumbv6m-none-eabi --features skip-include --out-dir out/examples/boot/rp \
|
||||||
--- build --release --manifest-path examples/boot/application/stm32f3/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32f3 \
|
--- build --release --manifest-path examples/boot/application/stm32f3/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32f3 \
|
||||||
--- build --release --manifest-path examples/boot/application/stm32f7/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32f7 \
|
--- build --release --manifest-path examples/boot/application/stm32f7/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32f7 \
|
||||||
@ -162,19 +190,46 @@ cargo batch \
|
|||||||
--- build --release --manifest-path examples/wasm/Cargo.toml --target wasm32-unknown-unknown --out-dir out/examples/wasm \
|
--- build --release --manifest-path examples/wasm/Cargo.toml --target wasm32-unknown-unknown --out-dir out/examples/wasm \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103c8 --out-dir out/tests/stm32f103c8 \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103c8 --out-dir out/tests/stm32f103c8 \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi --out-dir out/tests/stm32f429zi \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi --out-dir out/tests/stm32f429zi \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f446re --out-dir out/tests/stm32f446re \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re --out-dir out/tests/stm32g491re \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re --out-dir out/tests/stm32g491re \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --out-dir out/tests/stm32g071rb \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32g071rb --out-dir out/tests/stm32g071rb \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c031c6 --out-dir out/tests/stm32c031c6 \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32c031c6 --out-dir out/tests/stm32c031c6 \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --out-dir out/tests/stm32h755zi \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --out-dir out/tests/stm32h755zi \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h753zi --out-dir out/tests/stm32h753zi \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7a3zi --out-dir out/tests/stm32h7a3zi \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/stm32wb55rg \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/stm32wb55rg \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h563zi --out-dir out/tests/stm32h563zi \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h563zi --out-dir out/tests/stm32h563zi \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/stm32u585ai \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/stm32u585ai \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u5a5zj --out-dir out/tests/stm32u5a5zj \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wba52cg --out-dir out/tests/stm32wba52cg \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l073rz --out-dir out/tests/stm32l073rz \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l152re --out-dir out/tests/stm32l152re \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4a6zg --out-dir out/tests/stm32l4a6zg \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4r5zi --out-dir out/tests/stm32l4r5zi \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze --out-dir out/tests/stm32l552ze \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f767zi --out-dir out/tests/stm32f767zi \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f207zg --out-dir out/tests/stm32f207zg \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303ze --out-dir out/tests/stm32f303ze \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l496zg --out-dir out/tests/stm32l496zg \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55jc --out-dir out/tests/stm32wl55jc \
|
||||||
--- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \
|
--- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \
|
||||||
--- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \
|
--- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \
|
||||||
--- build --release --manifest-path tests/riscv32/Cargo.toml --target riscv32imac-unknown-none-elf \
|
--- build --release --manifest-path tests/riscv32/Cargo.toml --target riscv32imac-unknown-none-elf \
|
||||||
$BUILD_EXTRA
|
$BUILD_EXTRA
|
||||||
|
|
||||||
|
|
||||||
|
rm out/tests/stm32wb55rg/wpan_mac
|
||||||
|
rm out/tests/stm32wb55rg/wpan_ble
|
||||||
|
|
||||||
|
# not in CI yet.
|
||||||
|
rm -rf out/tests/stm32f446re
|
||||||
|
|
||||||
|
# unstable, I think it's running out of RAM?
|
||||||
|
rm out/tests/stm32f207zg/eth
|
||||||
|
|
||||||
|
# doesn't work, gives "noise error", no idea why. usart_dma does pass.
|
||||||
|
rm out/tests/stm32u5a5zj/usart
|
||||||
|
|
||||||
if [[ -z "${TELEPROBE_TOKEN-}" ]]; then
|
if [[ -z "${TELEPROBE_TOKEN-}" ]]; then
|
||||||
echo No teleprobe token found, skipping running HIL tests
|
echo No teleprobe token found, skipping running HIL tests
|
||||||
exit
|
exit
|
||||||
|
61
ci_stable.sh
61
ci_stable.sh
@ -40,33 +40,38 @@ cargo batch \
|
|||||||
--- 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 \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55vy,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55vy,defmt,exti,time-driver-any,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55cc-cm4,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55cc-cm4,defmt,exti,time-driver-any,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4r9zi,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g473cc,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303vc,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585zi,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55vy,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55cc-cm4,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4r9zi,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303vc,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path examples/nrf52840/Cargo.toml --target thumbv7em-none-eabi --no-default-features --out-dir out/examples/nrf52840 --bin raw_spawn \
|
--- build --release --manifest-path examples/nrf52840/Cargo.toml --target thumbv7em-none-eabi --no-default-features --out-dir out/examples/nrf52840 --bin raw_spawn \
|
||||||
--- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --no-default-features --out-dir out/examples/stm32l0 --bin raw_spawn \
|
--- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --no-default-features --out-dir out/examples/stm32l0 --bin raw_spawn \
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![allow(incomplete_features)]
|
|
||||||
#![feature(async_fn_in_trait)]
|
#![feature(async_fn_in_trait)]
|
||||||
|
#![allow(stable_features, unknown_lints, async_fn_in_trait)]
|
||||||
|
|
||||||
use core::slice;
|
use core::slice;
|
||||||
|
|
||||||
|
@ -11,11 +11,10 @@ log = ["dep:log"]
|
|||||||
firmware-logs = []
|
firmware-logs = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-time = { version = "0.1.3", path = "../embassy-time"}
|
embassy-time = { version = "0.1.5", path = "../embassy-time"}
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync"}
|
embassy-sync = { version = "0.4.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.2.0", path = "../embassy-net-driver-channel"}
|
||||||
atomic-polyfill = "0.1.5"
|
|
||||||
|
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
log = { version = "0.4.17", optional = true }
|
log = { version = "0.4.17", optional = true }
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use embassy_futures::yield_now;
|
use embassy_futures::yield_now;
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::Timer;
|
||||||
use embedded_hal_1::digital::OutputPin;
|
use embedded_hal_1::digital::OutputPin;
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
|
|
||||||
@ -51,9 +51,9 @@ where
|
|||||||
pub async fn init(&mut self) {
|
pub async fn init(&mut self) {
|
||||||
// Reset
|
// Reset
|
||||||
self.pwr.set_low().unwrap();
|
self.pwr.set_low().unwrap();
|
||||||
Timer::after(Duration::from_millis(20)).await;
|
Timer::after_millis(20).await;
|
||||||
self.pwr.set_high().unwrap();
|
self.pwr.set_high().unwrap();
|
||||||
Timer::after(Duration::from_millis(250)).await;
|
Timer::after_millis(250).await;
|
||||||
|
|
||||||
while self
|
while self
|
||||||
.read32_swapped(REG_BUS_TEST_RO)
|
.read32_swapped(REG_BUS_TEST_RO)
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use core::cmp::{max, min};
|
use core::cmp::{max, min};
|
||||||
|
use core::iter::zip;
|
||||||
|
|
||||||
use ch::driver::LinkState;
|
|
||||||
use embassy_net_driver_channel as ch;
|
use embassy_net_driver_channel as ch;
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_net_driver_channel::driver::{HardwareAddress, LinkState};
|
||||||
|
use embassy_time::Timer;
|
||||||
|
|
||||||
pub use crate::bus::SpiBusCyw43;
|
|
||||||
use crate::consts::*;
|
use crate::consts::*;
|
||||||
use crate::events::{Event, EventSubscriber, Events};
|
use crate::events::{Event, EventSubscriber, Events};
|
||||||
use crate::fmt::Bytes;
|
use crate::fmt::Bytes;
|
||||||
@ -17,6 +17,12 @@ pub struct Error {
|
|||||||
pub status: u32,
|
pub status: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum AddMulticastAddressError {
|
||||||
|
NotMulticast,
|
||||||
|
NoFreeSlots,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Control<'a> {
|
pub struct Control<'a> {
|
||||||
state_ch: ch::StateRunner<'a>,
|
state_ch: ch::StateRunner<'a>,
|
||||||
events: &'a Events,
|
events: &'a Events,
|
||||||
@ -87,22 +93,22 @@ impl<'a> Control<'a> {
|
|||||||
self.set_iovar("country", &country_info.to_bytes()).await;
|
self.set_iovar("country", &country_info.to_bytes()).await;
|
||||||
|
|
||||||
// set country takes some time, next ioctls fail if we don't wait.
|
// set country takes some time, next ioctls fail if we don't wait.
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
|
|
||||||
// Set antenna to chip antenna
|
// Set antenna to chip antenna
|
||||||
self.ioctl_set_u32(IOCTL_CMD_ANTDIV, 0, 0).await;
|
self.ioctl_set_u32(IOCTL_CMD_ANTDIV, 0, 0).await;
|
||||||
|
|
||||||
self.set_iovar_u32("bus:txglom", 0).await;
|
self.set_iovar_u32("bus:txglom", 0).await;
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
//self.set_iovar_u32("apsta", 1).await; // this crashes, also we already did it before...??
|
//self.set_iovar_u32("apsta", 1).await; // this crashes, also we already did it before...??
|
||||||
//Timer::after(Duration::from_millis(100)).await;
|
//Timer::after_millis(100).await;
|
||||||
self.set_iovar_u32("ampdu_ba_wsize", 8).await;
|
self.set_iovar_u32("ampdu_ba_wsize", 8).await;
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
self.set_iovar_u32("ampdu_mpdu", 4).await;
|
self.set_iovar_u32("ampdu_mpdu", 4).await;
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
//self.set_iovar_u32("ampdu_rx_factor", 0).await; // this crashes
|
//self.set_iovar_u32("ampdu_rx_factor", 0).await; // this crashes
|
||||||
|
|
||||||
//Timer::after(Duration::from_millis(100)).await;
|
//Timer::after_millis(100).await;
|
||||||
|
|
||||||
// evts
|
// evts
|
||||||
let mut evts = EventMask {
|
let mut evts = EventMask {
|
||||||
@ -121,19 +127,19 @@ impl<'a> Control<'a> {
|
|||||||
|
|
||||||
self.set_iovar("bsscfg:event_msgs", &evts.to_bytes()).await;
|
self.set_iovar("bsscfg:event_msgs", &evts.to_bytes()).await;
|
||||||
|
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
|
|
||||||
// set wifi up
|
// set wifi up
|
||||||
self.up().await;
|
self.up().await;
|
||||||
|
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
|
|
||||||
self.ioctl_set_u32(110, 0, 1).await; // SET_GMODE = auto
|
self.ioctl_set_u32(110, 0, 1).await; // SET_GMODE = auto
|
||||||
self.ioctl_set_u32(142, 0, 0).await; // SET_BAND = any
|
self.ioctl_set_u32(142, 0, 0).await; // SET_BAND = any
|
||||||
|
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
|
|
||||||
self.state_ch.set_ethernet_address(mac_addr);
|
self.state_ch.set_hardware_address(HardwareAddress::Ethernet(mac_addr));
|
||||||
|
|
||||||
debug!("INIT DONE");
|
debug!("INIT DONE");
|
||||||
}
|
}
|
||||||
@ -185,7 +191,7 @@ impl<'a> Control<'a> {
|
|||||||
self.set_iovar_u32x2("bsscfg:sup_wpa2_eapver", 0, 0xFFFF_FFFF).await;
|
self.set_iovar_u32x2("bsscfg:sup_wpa2_eapver", 0, 0xFFFF_FFFF).await;
|
||||||
self.set_iovar_u32x2("bsscfg:sup_wpa_tmo", 0, 2500).await;
|
self.set_iovar_u32x2("bsscfg:sup_wpa_tmo", 0, 2500).await;
|
||||||
|
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
|
|
||||||
let mut pfi = PassphraseInfo {
|
let mut pfi = PassphraseInfo {
|
||||||
len: passphrase.len() as _,
|
len: passphrase.len() as _,
|
||||||
@ -297,7 +303,7 @@ impl<'a> Control<'a> {
|
|||||||
if security != Security::OPEN {
|
if security != Security::OPEN {
|
||||||
self.set_iovar_u32x2("bsscfg:wpa_auth", 0, 0x0084).await; // wpa_auth = WPA2_AUTH_PSK | WPA_AUTH_PSK
|
self.set_iovar_u32x2("bsscfg:wpa_auth", 0, 0x0084).await; // wpa_auth = WPA2_AUTH_PSK | WPA_AUTH_PSK
|
||||||
|
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
|
|
||||||
// Set passphrase
|
// Set passphrase
|
||||||
let mut pfi = PassphraseInfo {
|
let mut pfi = PassphraseInfo {
|
||||||
@ -317,6 +323,54 @@ impl<'a> Control<'a> {
|
|||||||
self.set_iovar_u32x2("bss", 0, 1).await; // bss = BSS_UP
|
self.set_iovar_u32x2("bss", 0, 1).await; // bss = BSS_UP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add specified address to the list of hardware addresses the device
|
||||||
|
/// listens on. The address must be a Group address (I/G bit set). Up
|
||||||
|
/// to 10 addresses are supported by the firmware. Returns the number of
|
||||||
|
/// address slots filled after adding, or an error.
|
||||||
|
pub async fn add_multicast_address(&mut self, address: [u8; 6]) -> Result<usize, AddMulticastAddressError> {
|
||||||
|
// The firmware seems to ignore non-multicast addresses, so let's
|
||||||
|
// prevent the user from adding them and wasting space.
|
||||||
|
if address[0] & 0x01 != 1 {
|
||||||
|
return Err(AddMulticastAddressError::NotMulticast);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buf = [0; 64];
|
||||||
|
self.get_iovar("mcast_list", &mut buf).await;
|
||||||
|
|
||||||
|
let n = u32::from_le_bytes(buf[..4].try_into().unwrap()) as usize;
|
||||||
|
let (used, free) = buf[4..].split_at_mut(n * 6);
|
||||||
|
|
||||||
|
if used.chunks(6).any(|a| a == address) {
|
||||||
|
return Ok(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
if free.len() < 6 {
|
||||||
|
return Err(AddMulticastAddressError::NoFreeSlots);
|
||||||
|
}
|
||||||
|
|
||||||
|
free[..6].copy_from_slice(&address);
|
||||||
|
let n = n + 1;
|
||||||
|
buf[..4].copy_from_slice(&(n as u32).to_le_bytes());
|
||||||
|
|
||||||
|
self.set_iovar_v::<80>("mcast_list", &buf).await;
|
||||||
|
Ok(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve the list of configured multicast hardware addresses.
|
||||||
|
pub async fn list_mulistcast_addresses(&mut self, result: &mut [[u8; 6]; 10]) -> usize {
|
||||||
|
let mut buf = [0; 64];
|
||||||
|
self.get_iovar("mcast_list", &mut buf).await;
|
||||||
|
|
||||||
|
let n = u32::from_le_bytes(buf[..4].try_into().unwrap()) as usize;
|
||||||
|
let used = &buf[4..][..n * 6];
|
||||||
|
|
||||||
|
for (addr, output) in zip(used.chunks(6), result.iter_mut()) {
|
||||||
|
output.copy_from_slice(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
n
|
||||||
|
}
|
||||||
|
|
||||||
async fn set_iovar_u32x2(&mut self, name: &str, val1: u32, val2: u32) {
|
async fn set_iovar_u32x2(&mut self, name: &str, val1: u32, val2: u32) {
|
||||||
let mut buf = [0; 8];
|
let mut buf = [0; 8];
|
||||||
buf[0..4].copy_from_slice(&val1.to_le_bytes());
|
buf[0..4].copy_from_slice(&val1.to_le_bytes());
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![allow(incomplete_features)]
|
#![feature(async_fn_in_trait, type_alias_impl_trait)]
|
||||||
#![feature(async_fn_in_trait, type_alias_impl_trait, concat_bytes)]
|
#![allow(stable_features, unknown_lints, async_fn_in_trait)]
|
||||||
#![deny(unused_must_use)]
|
#![deny(unused_must_use)]
|
||||||
|
|
||||||
// This mod MUST go first, so that the others see its macros.
|
// This mod MUST go first, so that the others see its macros.
|
||||||
@ -27,7 +27,7 @@ use ioctl::IoctlState;
|
|||||||
|
|
||||||
use crate::bus::Bus;
|
use crate::bus::Bus;
|
||||||
pub use crate::bus::SpiBusCyw43;
|
pub use crate::bus::SpiBusCyw43;
|
||||||
pub use crate::control::{Control, Error as ControlError, Scanner};
|
pub use crate::control::{AddMulticastAddressError, Control, Error as ControlError, Scanner};
|
||||||
pub use crate::runner::Runner;
|
pub use crate::runner::Runner;
|
||||||
pub use crate::structs::BssInfo;
|
pub use crate::structs::BssInfo;
|
||||||
|
|
||||||
|
@ -1,54 +1,48 @@
|
|||||||
macro_rules! nvram {
|
pub static NVRAM: &'static [u8] = b"
|
||||||
($($s:literal,)*) => {
|
NVRAMRev=$Rev$\x00\
|
||||||
concat_bytes!($($s, b"\x00",)* b"\x00\x00")
|
manfid=0x2d0\x00\
|
||||||
};
|
prodid=0x0727\x00\
|
||||||
}
|
vendid=0x14e4\x00\
|
||||||
|
devid=0x43e2\x00\
|
||||||
pub static NVRAM: &'static [u8] = &*nvram!(
|
boardtype=0x0887\x00\
|
||||||
b"NVRAMRev=$Rev$",
|
boardrev=0x1100\x00\
|
||||||
b"manfid=0x2d0",
|
boardnum=22\x00\
|
||||||
b"prodid=0x0727",
|
macaddr=00:A0:50:b5:59:5e\x00\
|
||||||
b"vendid=0x14e4",
|
sromrev=11\x00\
|
||||||
b"devid=0x43e2",
|
boardflags=0x00404001\x00\
|
||||||
b"boardtype=0x0887",
|
boardflags3=0x04000000\x00\
|
||||||
b"boardrev=0x1100",
|
xtalfreq=37400\x00\
|
||||||
b"boardnum=22",
|
nocrc=1\x00\
|
||||||
b"macaddr=00:A0:50:b5:59:5e",
|
ag0=255\x00\
|
||||||
b"sromrev=11",
|
aa2g=1\x00\
|
||||||
b"boardflags=0x00404001",
|
ccode=ALL\x00\
|
||||||
b"boardflags3=0x04000000",
|
pa0itssit=0x20\x00\
|
||||||
b"xtalfreq=37400",
|
extpagain2g=0\x00\
|
||||||
b"nocrc=1",
|
pa2ga0=-168,6649,-778\x00\
|
||||||
b"ag0=255",
|
AvVmid_c0=0x0,0xc8\x00\
|
||||||
b"aa2g=1",
|
cckpwroffset0=5\x00\
|
||||||
b"ccode=ALL",
|
maxp2ga0=84\x00\
|
||||||
b"pa0itssit=0x20",
|
txpwrbckof=6\x00\
|
||||||
b"extpagain2g=0",
|
cckbw202gpo=0\x00\
|
||||||
b"pa2ga0=-168,6649,-778",
|
legofdmbw202gpo=0x66111111\x00\
|
||||||
b"AvVmid_c0=0x0,0xc8",
|
mcsbw202gpo=0x77711111\x00\
|
||||||
b"cckpwroffset0=5",
|
propbw202gpo=0xdd\x00\
|
||||||
b"maxp2ga0=84",
|
ofdmdigfilttype=18\x00\
|
||||||
b"txpwrbckof=6",
|
ofdmdigfilttypebe=18\x00\
|
||||||
b"cckbw202gpo=0",
|
papdmode=1\x00\
|
||||||
b"legofdmbw202gpo=0x66111111",
|
papdvalidtest=1\x00\
|
||||||
b"mcsbw202gpo=0x77711111",
|
pacalidx2g=45\x00\
|
||||||
b"propbw202gpo=0xdd",
|
papdepsoffset=-30\x00\
|
||||||
b"ofdmdigfilttype=18",
|
papdendidx=58\x00\
|
||||||
b"ofdmdigfilttypebe=18",
|
ltecxmux=0\x00\
|
||||||
b"papdmode=1",
|
ltecxpadnum=0x0102\x00\
|
||||||
b"papdvalidtest=1",
|
ltecxfnsel=0x44\x00\
|
||||||
b"pacalidx2g=45",
|
ltecxgcigpio=0x01\x00\
|
||||||
b"papdepsoffset=-30",
|
il0macaddr=00:90:4c:c5:12:38\x00\
|
||||||
b"papdendidx=58",
|
wl0id=0x431b\x00\
|
||||||
b"ltecxmux=0",
|
deadman_to=0xffffffff\x00\
|
||||||
b"ltecxpadnum=0x0102",
|
muxenab=0x100\x00\
|
||||||
b"ltecxfnsel=0x44",
|
spurconfig=0x3\x00\
|
||||||
b"ltecxgcigpio=0x01",
|
glitch_based_crsmin=1\x00\
|
||||||
b"il0macaddr=00:90:4c:c5:12:38",
|
btc_mode=1\x00\
|
||||||
b"wl0id=0x431b",
|
\x00";
|
||||||
b"deadman_to=0xffffffff",
|
|
||||||
b"muxenab=0x100",
|
|
||||||
b"spurconfig=0x3",
|
|
||||||
b"glitch_based_crsmin=1",
|
|
||||||
b"btc_mode=1",
|
|
||||||
);
|
|
||||||
|
@ -555,14 +555,14 @@ where
|
|||||||
|
|
||||||
self.bus.bp_write8(base + AI_RESETCTRL_OFFSET, 0).await;
|
self.bus.bp_write8(base + AI_RESETCTRL_OFFSET, 0).await;
|
||||||
|
|
||||||
Timer::after(Duration::from_millis(1)).await;
|
Timer::after_millis(1).await;
|
||||||
|
|
||||||
self.bus
|
self.bus
|
||||||
.bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_CLOCK_EN)
|
.bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_CLOCK_EN)
|
||||||
.await;
|
.await;
|
||||||
let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await;
|
let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await;
|
||||||
|
|
||||||
Timer::after(Duration::from_millis(1)).await;
|
Timer::after_millis(1).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn core_is_up(&mut self, core: Core) -> bool {
|
async fn core_is_up(&mut self, core: Core) -> bool {
|
||||||
|
@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-executor = { version = "0.3.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] }
|
embassy-executor = { version = "0.3.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] }
|
||||||
embassy-time = { version = "0.1.0", path = "../../../../../embassy-time", features = ["defmt", "nightly"] }
|
embassy-time = { version = "0.1.4", path = "../../../../../embassy-time", features = ["defmt", "nightly"] }
|
||||||
embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] }
|
embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] }
|
||||||
|
|
||||||
defmt = "0.3"
|
defmt = "0.3"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
* xref:getting_started.adoc[Getting started]
|
* xref:getting_started.adoc[Getting started]
|
||||||
** xref:basic_application.adoc[Basic application]
|
** xref:basic_application.adoc[Basic application]
|
||||||
** xref:layer_by_layer.adoc[Layer by Layer]
|
* xref:layer_by_layer.adoc[Bare metal to async]
|
||||||
* xref:runtime.adoc[Executor]
|
* xref:runtime.adoc[Executor]
|
||||||
* xref:hal.adoc[HAL]
|
* xref:hal.adoc[HAL]
|
||||||
** xref:nrf.adoc[nRF]
|
** xref:nrf.adoc[nRF]
|
||||||
@ -8,3 +8,6 @@
|
|||||||
* xref:bootloader.adoc[Bootloader]
|
* xref:bootloader.adoc[Bootloader]
|
||||||
|
|
||||||
* xref:examples.adoc[Examples]
|
* xref:examples.adoc[Examples]
|
||||||
|
* xref:developer.adoc[Developer]
|
||||||
|
** xref:developer_stm32.adoc[Developer: STM32]
|
||||||
|
* xref:faq.adoc[Frequently Asked Questions]
|
||||||
|
@ -48,7 +48,7 @@ The `Spawner` is the way the main application spawns other tasks. The `Periphera
|
|||||||
include::example$basic/src/main.rs[lines="22..-1"]
|
include::example$basic/src/main.rs[lines="22..-1"]
|
||||||
----
|
----
|
||||||
|
|
||||||
What happens when the `blinker` task has been spawned and main returns? Well, the main entry point is actually just like any other task, except that you can only have one and it takes some specific type arguments. The magic lies within the `#[embassy::main]` macro. The macro does the following:
|
What happens when the `blinker` task has been spawned and main returns? Well, the main entry point is actually just like any other task, except that you can only have one and it takes some specific type arguments. The magic lies within the `#[embassy_executor::main]` macro. The macro does the following:
|
||||||
|
|
||||||
. Creates an Embassy Executor
|
. Creates an Embassy Executor
|
||||||
. Initializes the microcontroller HAL to get the `Peripherals`
|
. Initializes the microcontroller HAL to get the `Peripherals`
|
||||||
|
1
docs/modules/ROOT/pages/developer.adoc
Normal file
1
docs/modules/ROOT/pages/developer.adoc
Normal file
@ -0,0 +1 @@
|
|||||||
|
= Developer Documentation
|
79
docs/modules/ROOT/pages/developer_stm32.adoc
Normal file
79
docs/modules/ROOT/pages/developer_stm32.adoc
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
= Developer Documentation: STM32
|
||||||
|
|
||||||
|
== Understanding metapac
|
||||||
|
|
||||||
|
When a project that imports `embassy-stm32` is compiled, that project selects the feature corresponding to the chip that project is using. Based on that feature, `embassy-stm32` selects supported link:https://anysilicon.com/ip-intellectual-property-core-semiconductors/[IP] for the chip, and enables the corresponding HAL implementations. But how does `embassy-stm32` know what IP the chip contains, out of the hundreds of chips that we support? It's a long story that starts with `stm32-data-sources`.
|
||||||
|
|
||||||
|
== `stm32-data-sources`
|
||||||
|
|
||||||
|
link:https://github.com/embassy-rs/stm32-data-sources[`stm32-data-sources`] is as mostly barren repository. It has no README, no documentation, and few watchers. But it's the core of what makes `embassy-stm32` possible. The data for every chip that we support is taken in part from a corresponding XML file like link:https://github.com/embassy-rs/stm32-data-sources/blob/b8b85202e22a954d6c59d4a43d9795d34cff05cf/cubedb/mcu/STM32F051K4Ux.xml[`STM32F051K4Ux.xml`]. In that file, you'll see lines like the following:
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<IP InstanceName="I2C1" Name="I2C" Version="i2c2_v1_1_Cube"/>
|
||||||
|
<!-- snip -->
|
||||||
|
<IP ConfigFile="TIM-STM32F0xx" InstanceName="TIM1" Name="TIM1_8F0" Version="gptimer2_v2_x_Cube"/>
|
||||||
|
----
|
||||||
|
|
||||||
|
These lines indicate that this chip has an i2c, and that it's version is "v1_1". It also indicates that it has a general purpose timer that with a version of "v2_x". From this data, it's possible to determine which implementations should be included in `embassy-stm32`. But actually doing that is another matter.
|
||||||
|
|
||||||
|
|
||||||
|
== `stm32-data`
|
||||||
|
|
||||||
|
While all users of this project are familiar with `embassy-stm32`, fewer are familiar with the project that powers it: `stm32-data`. This project doesn't just aim to generate data for `embassy-stm32`, but for machine consumption in general. To acheive this, information from multiple files from the `stm32-data-sources` project are combined and parsed to assign register block implementations for each supported IP. The core of this matching resides in `chips.rs`:
|
||||||
|
|
||||||
|
[source,rust]
|
||||||
|
----
|
||||||
|
(".*:I2C:i2c2_v1_1", ("i2c", "v2", "I2C")),
|
||||||
|
// snip
|
||||||
|
(r".*TIM\d.*:gptimer.*", ("timer", "v1", "TIM_GP16")),
|
||||||
|
----
|
||||||
|
|
||||||
|
In this case, the i2c version corresponds to our "v2" and the general purpose timer version corresponds to our "v1". Therefore, the `i2c_v2.yaml` and `timer_v1.yaml` register block implementations are assigned to those IP, respectively. The result is that these lines arr generated in `STM32F051K4.json`:
|
||||||
|
|
||||||
|
[source,json]
|
||||||
|
----
|
||||||
|
{
|
||||||
|
"name": "I2C1",
|
||||||
|
"address": 1073763328,
|
||||||
|
"registers": {
|
||||||
|
"kind": "i2c",
|
||||||
|
"version": "v2",
|
||||||
|
"block": "I2C"
|
||||||
|
},
|
||||||
|
// snip
|
||||||
|
}
|
||||||
|
// snip
|
||||||
|
{
|
||||||
|
"name": "TIM1",
|
||||||
|
"address": 1073818624,
|
||||||
|
"registers": {
|
||||||
|
"kind": "timer",
|
||||||
|
"version": "v1",
|
||||||
|
"block": "TIM_ADV"
|
||||||
|
},
|
||||||
|
// snip
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
In addition to register blocks, data for pin and RCC mapping is also generated and consumed by `embassy-stm32`. `stm32-metapac-gen` is used to package and publish the data as a crate.
|
||||||
|
|
||||||
|
|
||||||
|
== `embassy-stm32`
|
||||||
|
|
||||||
|
In the `lib.rs` file located in the root of `embassy-stm32`, you'll see this line:
|
||||||
|
|
||||||
|
[source,rust]
|
||||||
|
----
|
||||||
|
#[cfg(i2c)]
|
||||||
|
pub mod i2c;
|
||||||
|
----
|
||||||
|
|
||||||
|
And in the `mod.rs` of the i2c mod, you'll see this:
|
||||||
|
|
||||||
|
[source,rust]
|
||||||
|
----
|
||||||
|
#[cfg_attr(i2c_v2, path = "v2.rs")]
|
||||||
|
----
|
||||||
|
|
||||||
|
Because i2c is supported for STM32F051K4 and its version corresponds to our "v2", the `i2c` and `i2c_v2`, configuration directives will be present, and `embassy-stm32` will include these files, respectively. This and other configuration directives and tables are generated from the data for chip, allowing `embassy-stm32` to expressively and clearly adapt logic and implementations to what is required for each chip. Compared to other projects across the embedded ecosystem, `embassy-stm32` is the only project that can re-use code across the entire stm32 lineup and remove difficult-to-implement unsafe logic to the HAL.
|
38
docs/modules/ROOT/pages/faq.adoc
Normal file
38
docs/modules/ROOT/pages/faq.adoc
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
= Frequently Asked Questions
|
||||||
|
|
||||||
|
These are a list of unsorted, commonly asked questions and answers.
|
||||||
|
|
||||||
|
Please feel free to add items to link:https://github.com/embassy-rs/embassy/edit/main/docs/modules/ROOT/pages/faq.adoc[this page], especially if someone in the chat answered a question for you!
|
||||||
|
|
||||||
|
== How to deploy to RP2040 without a debugging probe.
|
||||||
|
|
||||||
|
Install link:https://github.com/JoNil/elf2uf2-rs[elf2uf2-rs] for converting the generated elf binary into a uf2 file.
|
||||||
|
|
||||||
|
Configure the runner to use this tool, add this to `.cargo/config.toml`:
|
||||||
|
[source,toml]
|
||||||
|
----
|
||||||
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
|
runner = "elf2uf2-rs --deploy --serial --verbose"
|
||||||
|
----
|
||||||
|
|
||||||
|
The command-line parameters `--deploy` will detect your device and upload the binary, `--serial` starts a serial connection. See the documentation for more info.
|
||||||
|
|
||||||
|
== Missing main macro
|
||||||
|
|
||||||
|
If you see an error like this:
|
||||||
|
|
||||||
|
[source,rust]
|
||||||
|
----
|
||||||
|
#[embassy_executor::main]
|
||||||
|
| ^^^^ could not find `main` in `embassy_executor`
|
||||||
|
----
|
||||||
|
|
||||||
|
You are likely missing some features of the `embassy-executor` crate.
|
||||||
|
|
||||||
|
For Cortex-M targets, consider making sure that ALL of the following features are active in your `Cargo.toml` for the `embassy-executor` crate:
|
||||||
|
|
||||||
|
* `arch-cortex-m`
|
||||||
|
* `executor-thread`
|
||||||
|
* `nightly`
|
||||||
|
|
||||||
|
For Xtensa ESP32, consider using the executors and `#[main]` macro provided by your appropriate link:https://crates.io/crates/esp-hal-common[HAL crate].
|
@ -3,7 +3,7 @@
|
|||||||
So you want to try Embassy, great! To get started, there are a few tools you need to install:
|
So you want to try Embassy, great! To get started, there are a few tools you need to install:
|
||||||
|
|
||||||
* link:https://rustup.rs/[rustup] - the Rust toolchain is needed to compile Rust code.
|
* link:https://rustup.rs/[rustup] - the Rust toolchain is needed to compile Rust code.
|
||||||
* link:https://crates.io/crates/probe-run[probe-run] - to flash the firmware on your device. If you already have other tools like `OpenOCD` setup, you can use that as well.
|
* link:https://crates.io/crates/probe-rs[probe-rs] - to flash the firmware on your device. If you already have other tools like `OpenOCD` setup, you can use that as well.
|
||||||
|
|
||||||
If you don't have any supported board, don't worry: you can also run embassy on your PC using the `std` examples.
|
If you don't have any supported board, don't worry: you can also run embassy on your PC using the `std` examples.
|
||||||
|
|
||||||
@ -30,6 +30,10 @@ Embassy supports many microcontroller families, but the easiest ways to get star
|
|||||||
|
|
||||||
* link:https://www.raspberrypi.com/products/raspberry-pi-pico/[Raspberry Pi Pico]
|
* link:https://www.raspberrypi.com/products/raspberry-pi-pico/[Raspberry Pi Pico]
|
||||||
|
|
||||||
|
=== ESP32
|
||||||
|
|
||||||
|
* link:https://github.com/esp-rs/esp-rust-board[ESP32C3]
|
||||||
|
|
||||||
== Running an example
|
== Running an example
|
||||||
|
|
||||||
First you need to clone the [github repository];
|
First you need to clone the [github repository];
|
||||||
@ -38,7 +42,6 @@ First you need to clone the [github repository];
|
|||||||
----
|
----
|
||||||
git clone https://github.com/embassy-rs/embassy.git
|
git clone https://github.com/embassy-rs/embassy.git
|
||||||
cd embassy
|
cd embassy
|
||||||
git submodule update --init
|
|
||||||
----
|
----
|
||||||
|
|
||||||
You can run an example by opening a terminal and entering the following commands:
|
You can run an example by opening a terminal and entering the following commands:
|
||||||
|
@ -7,4 +7,6 @@ Embassy provides HALs for several microcontroller families:
|
|||||||
* `embassy-rp` for the Raspberry Pi RP2040 microcontrollers
|
* `embassy-rp` for the Raspberry Pi RP2040 microcontrollers
|
||||||
|
|
||||||
These HALs implement async/await functionality for most peripherals while also implementing the
|
These HALs implement async/await functionality for most peripherals while also implementing the
|
||||||
async traits in `embedded-hal-async`. You can also use these HALs with another executor.
|
async traits in `embedded-hal` and `embedded-hal-async`. You can also use these HALs with another executor.
|
||||||
|
|
||||||
|
For the ESP32 series, there is an link:https://github.com/esp-rs/esp-hal[esp-hal] which you can use.
|
||||||
|
@ -4,14 +4,9 @@ Embassy is a project to make async/await a first-class option for embedded devel
|
|||||||
|
|
||||||
== What is async?
|
== What is async?
|
||||||
|
|
||||||
Software written without async may block on I/O operations. In an std environment, such as a PC, software can handle this either by using threads or non-blocking operations.
|
When handling I/O, software must call functions that block program execution until the I/O operation completes. When running inside of an OS such as Linux, such functions generally transfer control to the kernel so that another task, known as a thread, can be executed if available, or the CPU can be put to sleep until another such task is ready to perform more work. Because an OS cannot presume that threads will behave cooperatively, threads are relatively resource-intensive, and may be forcibly interrupted they do not transfer control back to the kernel within an allotted time. But if tasks could be presumed to behave cooperatively, or at least not maliciously, it would be possible to create tasks that appear to be almost free when compared to a traditional OS thread. In Rust, these lightweight tasks, known as 'coroutines' or 'goroutines' in other languages, are implemented with async.
|
||||||
|
|
||||||
With threads, one thread blocks on an I/O operation, another is able to take its place. However, even on a PC, threads are relatively heavy, and therefore some programming languages, such as Go, have implemented a concept called coroutines or 'goroutines' that are much lighter and less-intensive than threads.
|
Async-await works by transforming each async function into an object called a future. When a future blocks on I/O the future yields, and the scheduler, called an executor, can select a different future to execute. Compared to alternatives such as an RTOS, async can yield better performance and lower power consumption because the executor doesn't have to guess when a future is ready to execute. However, program size may be higher than other alternatives, which may be a problem for certain space-constrained devices with very low memory. On the devices Embassy supports, such as stm32 and nrf, memory is generally large enough to accommodate the modestly-increased program size.
|
||||||
|
|
||||||
The other way to handle blocking I/O operations is to support polling the state of the underlying peripherals to check whether it is available to perform the requested operation. In programming languages without builtin async support,
|
|
||||||
this requires building a complex loop checking for events.
|
|
||||||
|
|
||||||
In Rust, non-blocking operations can be implemented using async-await. Async-await works by transforming each async function into an object called a future. When a future blocks on I/O the future yields, and the scheduler, called an executor, can select a different future to execute. Compared to alternatives such as an RTOS, async can yield better performance and lower power consumption because the executor doesn't have to guess when a future is ready to execute. However, program size may be higher than other alternatives, which may be a problem for certain space-constrained devices with very low memory. On the devices Embassy supports, such as stm32 and nrf, memory is generally large enough to accommodate the modestly-increased program size.
|
|
||||||
|
|
||||||
== What is Embassy?
|
== What is Embassy?
|
||||||
|
|
||||||
@ -22,13 +17,26 @@ The Embassy project consists of several crates that you can use together or inde
|
|||||||
* **Hardware Abstraction Layers** - HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed. The Embassy project maintains HALs for select hardware, but you can still use HALs from other projects with Embassy.
|
* **Hardware Abstraction Layers** - HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed. The Embassy project maintains HALs for select hardware, but you can still use HALs from other projects with Embassy.
|
||||||
** link:https://docs.embassy.dev/embassy-stm32/[embassy-stm32], for all STM32 microcontroller families.
|
** link:https://docs.embassy.dev/embassy-stm32/[embassy-stm32], for all STM32 microcontroller families.
|
||||||
** link:https://docs.embassy.dev/embassy-nrf/[embassy-nrf], for the Nordic Semiconductor nRF52, nRF53, nRF91 series.
|
** link:https://docs.embassy.dev/embassy-nrf/[embassy-nrf], for the Nordic Semiconductor nRF52, nRF53, nRF91 series.
|
||||||
|
** link:https://docs.embassy.dev/embassy-rp/[embassy-rp], for the Raspberry Pi RP2040 microcontroller.
|
||||||
|
** link:https://github.com/esp-rs[esp-rs], for the Espressif Systems ESP32 series of chips.
|
||||||
|
+
|
||||||
|
NOTE: A common question is if one can use the Embassy HALs standalone. Yes, it is possible! There are no dependency on the executor within the HALs. You can even use them without async,
|
||||||
|
as they implement both the link:https://github.com/rust-embedded/embedded-hal[Embedded HAL] blocking and async traits.
|
||||||
|
|
||||||
* **Networking** - The link:https://docs.embassy.dev/embassy-net/[embassy-net] network stack implements extensive networking functionality, including Ethernet, IP, TCP, UDP, ICMP and DHCP. Async drastically simplifies managing timeouts and serving multiple connections concurrently.
|
* **Networking** - The link:https://docs.embassy.dev/embassy-net/[embassy-net] network stack implements extensive networking functionality, including Ethernet, IP, TCP, UDP, ICMP and DHCP. Async drastically simplifies managing timeouts and serving multiple connections concurrently. Several drivers for WiFi and Ethernet chips can be found.
|
||||||
|
|
||||||
* **Bluetooth** - The link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers.
|
* **Bluetooth** - The link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers.
|
||||||
|
|
||||||
* **LoRa** - link:https://docs.embassy.dev/embassy-lora/[embassy-lora] supports LoRa networking on STM32WL wireless microcontrollers and Semtech SX127x transceivers.
|
* **LoRa** - link:https://github.com/embassy-rs/lora-phy[lora-phy] and link:https://docs.embassy.dev/embassy-lora/[embassy-lora] supports LoRa networking on a wide range of LoRa radios, fully integrated with a Rust link:https://github.com/ivajloip/rust-lorawan[LoRaWAN] implementation.
|
||||||
|
|
||||||
* **USB** - link:https://docs.embassy.dev/embassy-usb/[embassy-usb] implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own.
|
* **USB** - link:https://docs.embassy.dev/embassy-usb/[embassy-usb] implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own.
|
||||||
|
|
||||||
* **Bootloader and DFU** - link:https://github.com/embassy-rs/embassy/tree/master/embassy-boot[embassy-boot] is a lightweight bootloader supporting firmware application upgrades in a power-fail-safe way, with trial boots and rollbacks.
|
* **Bootloader and DFU** - link:https://github.com/embassy-rs/embassy/tree/master/embassy-boot[embassy-boot] is a lightweight bootloader supporting firmware application upgrades in a power-fail-safe way, with trial boots and rollbacks.
|
||||||
|
|
||||||
|
== Resources
|
||||||
|
|
||||||
|
For more reading material on async Rust and Embassy:
|
||||||
|
|
||||||
|
* link:https://tweedegolf.nl/en/blog/65/async-rust-vs-rtos-showdown[Comparsion of FreeRTOS and Embassy]
|
||||||
|
* link:https://dev.to/apollolabsbin/series/20707[Tutorials]
|
||||||
|
* link:https://blog.drogue.io/firmware-updates-part-1/[Firmware Updates with Embassy]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
= Embassy layer by layer
|
= From bare metal to async Rust
|
||||||
|
|
||||||
If you're new to Embassy, it can be overwhelming to grasp all the terminology and concepts. This guide aims to clarify the different layers in Embassy, which problem each layer solves for the application writer.
|
If you're new to Embassy, it can be overwhelming to grasp all the terminology and concepts. This guide aims to clarify the different layers in Embassy, which problem each layer solves for the application writer.
|
||||||
|
|
||||||
@ -8,8 +8,7 @@ The application we'll write is a simple 'push button, blink led' application, wh
|
|||||||
|
|
||||||
== PAC version
|
== PAC version
|
||||||
|
|
||||||
The PAC is the lowest API for accessing peripherals and registers, if you don't count reading/writing directly to memory addresses. It provides distinct types
|
The PAC is the lowest API for accessing peripherals and registers, if you don't count reading/writing directly to memory addresses. It provides distinct types to make accessing peripheral registers easier, but it does not prevent you from writing unsafe code.
|
||||||
to make accessing peripheral registers easier, but it does not prevent you from writing unsafe code.
|
|
||||||
|
|
||||||
Writing an application using the PAC directly is therefore not recommended, but if the functionality you want to use is not exposed in the upper layers, that's what you need to use.
|
Writing an application using the PAC directly is therefore not recommended, but if the functionality you want to use is not exposed in the upper layers, that's what you need to use.
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ The nRF timer driver operates at 32768 Hz by default.
|
|||||||
|
|
||||||
== Peripherals
|
== Peripherals
|
||||||
|
|
||||||
The following peripherals have a HAL implementation at present:
|
The following peripherals have a HAL implementation at present
|
||||||
|
|
||||||
* PWM
|
* PWM
|
||||||
* SPIM
|
* SPIM
|
||||||
@ -23,3 +23,7 @@ The following peripherals have a HAL implementation at present:
|
|||||||
* UARTE
|
* UARTE
|
||||||
* TWIM
|
* TWIM
|
||||||
* SAADC
|
* SAADC
|
||||||
|
|
||||||
|
== Bluetooth
|
||||||
|
|
||||||
|
For bluetooth, you can use the link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] crate.
|
||||||
|
@ -6,11 +6,11 @@ The Embassy executor is an async/await executor designed for embedded usage alon
|
|||||||
|
|
||||||
* No `alloc`, no heap needed. Task are statically allocated.
|
* No `alloc`, no heap needed. Task are statically allocated.
|
||||||
* No "fixed capacity" data structures, executor works with 1 or 1000 tasks without needing config/tuning.
|
* No "fixed capacity" data structures, executor works with 1 or 1000 tasks without needing config/tuning.
|
||||||
* Integrated timer queue: sleeping is easy, just do `Timer::after(Duration::from_secs(1)).await;`.
|
* Integrated timer queue: sleeping is easy, just do `Timer::after_secs(1).await;`.
|
||||||
* No busy-loop polling: CPU sleeps when there's no work to do, using interrupts or `WFE/SEV`.
|
* No busy-loop polling: CPU sleeps when there's no work to do, using interrupts or `WFE/SEV`.
|
||||||
* Efficient polling: a wake will only poll the woken task, not all of them.
|
* Efficient polling: a wake will only poll the woken task, not all of them.
|
||||||
* Fair: a task can't monopolize CPU time even if it's constantly being woken. All other tasks get a chance to run before a given task gets polled for the second time.
|
* Fair: a task can't monopolize CPU time even if it's constantly being woken. All other tasks get a chance to run before a given task gets polled for the second time.
|
||||||
* Creating multiple executor instances is supported, to run tasks with multiple priority levels. This allows higher-priority tasks to preempt lower-priority tasks.
|
* Creating multiple executor instances is supported, to run tasks at different priority levels. This allows higher-priority tasks to preempt lower-priority tasks.
|
||||||
|
|
||||||
== Executor
|
== Executor
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ digest = "0.10"
|
|||||||
log = { version = "0.4", optional = true }
|
log = { version = "0.4", optional = true }
|
||||||
ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true }
|
ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true }
|
||||||
embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" }
|
embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" }
|
||||||
embassy-sync = { version = "0.2.0", path = "../../embassy-sync" }
|
embassy-sync = { version = "0.4.0", path = "../../embassy-sync" }
|
||||||
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 }
|
||||||
salty = { git = "https://github.com/ycrypto/salty.git", rev = "a9f17911a5024698406b75c0fac56ab5ccf6a8c7", optional = true }
|
salty = { git = "https://github.com/ycrypto/salty.git", rev = "a9f17911a5024698406b75c0fac56ab5ccf6a8c7", optional = true }
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait))]
|
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait))]
|
||||||
|
#![cfg_attr(feature = "nightly", allow(stable_features, unknown_lints, async_fn_in_trait))]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
@ -16,7 +16,7 @@ target = "thumbv7em-none-eabi"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
|
|
||||||
embassy-sync = { path = "../../embassy-sync" }
|
embassy-sync = { version = "0.4.0", path = "../../embassy-sync" }
|
||||||
embassy-nrf = { path = "../../embassy-nrf" }
|
embassy-nrf = { path = "../../embassy-nrf" }
|
||||||
embassy-boot = { path = "../boot", default-features = false }
|
embassy-boot = { path = "../boot", default-features = false }
|
||||||
cortex-m = { version = "0.7.6" }
|
cortex-m = { version = "0.7.6" }
|
||||||
|
@ -17,7 +17,7 @@ defmt = { version = "0.3", optional = true }
|
|||||||
defmt-rtt = { version = "0.4", optional = true }
|
defmt-rtt = { version = "0.4", optional = true }
|
||||||
log = { version = "0.4", optional = true }
|
log = { version = "0.4", optional = true }
|
||||||
|
|
||||||
embassy-sync = { path = "../../embassy-sync" }
|
embassy-sync = { version = "0.4.0", path = "../../embassy-sync" }
|
||||||
embassy-rp = { path = "../../embassy-rp", default-features = false }
|
embassy-rp = { path = "../../embassy-rp", default-features = false }
|
||||||
embassy-boot = { path = "../boot", default-features = false }
|
embassy-boot = { path = "../boot", default-features = false }
|
||||||
embassy-time = { path = "../../embassy-time" }
|
embassy-time = { path = "../../embassy-time" }
|
||||||
|
@ -18,7 +18,7 @@ defmt = { version = "0.3", optional = true }
|
|||||||
defmt-rtt = { version = "0.4", optional = true }
|
defmt-rtt = { version = "0.4", optional = true }
|
||||||
log = { version = "0.4", optional = true }
|
log = { version = "0.4", optional = true }
|
||||||
|
|
||||||
embassy-sync = { path = "../../embassy-sync" }
|
embassy-sync = { version = "0.4.0", path = "../../embassy-sync" }
|
||||||
embassy-stm32 = { path = "../../embassy-stm32", default-features = false }
|
embassy-stm32 = { path = "../../embassy-stm32", default-features = false }
|
||||||
embassy-boot = { path = "../boot", default-features = false }
|
embassy-boot = { path = "../boot", default-features = false }
|
||||||
cortex-m = { version = "0.7.6" }
|
cortex-m = { version = "0.7.6" }
|
||||||
|
@ -20,8 +20,8 @@ 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.4.0", path = "../embassy-sync" }
|
||||||
embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true }
|
embassy-time = { version = "0.1.5", 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",
|
||||||
] }
|
] }
|
||||||
|
@ -11,7 +11,7 @@ pub use asynch::Partition;
|
|||||||
pub use blocking::BlockingPartition;
|
pub use blocking::BlockingPartition;
|
||||||
|
|
||||||
/// Partition error
|
/// Partition error
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum Error<T> {
|
pub enum Error<T> {
|
||||||
/// The requested flash area is outside the partition
|
/// The requested flash area is outside the partition
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections, try_blocks))]
|
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))]
|
||||||
|
#![cfg_attr(feature = "nightly", allow(stable_features, unknown_lints, async_fn_in_trait))]
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
//! Utilities to use `embedded-hal` traits with Embassy.
|
//! Utilities to use `embedded-hal` traits with Embassy.
|
||||||
@ -26,6 +27,18 @@ pub trait SetConfig {
|
|||||||
/// The configuration type used by this driver.
|
/// The configuration type used by this driver.
|
||||||
type Config;
|
type Config;
|
||||||
|
|
||||||
|
/// The error type that can occur if `set_config` fails.
|
||||||
|
type ConfigError;
|
||||||
|
|
||||||
/// Set the configuration of the driver.
|
/// Set the configuration of the driver.
|
||||||
fn set_config(&mut self, config: &Self::Config);
|
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the configuration of a peripheral driver.
|
||||||
|
pub trait GetConfig {
|
||||||
|
/// The configuration type used by this driver.
|
||||||
|
type Config;
|
||||||
|
|
||||||
|
/// Get the configuration of the driver.
|
||||||
|
fn get_config(&self) -> Self::Config;
|
||||||
}
|
}
|
||||||
|
@ -125,14 +125,14 @@ where
|
|||||||
{
|
{
|
||||||
async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), I2cDeviceError<BUS::Error>> {
|
async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), I2cDeviceError<BUS::Error>> {
|
||||||
let mut bus = self.bus.lock().await;
|
let mut bus = self.bus.lock().await;
|
||||||
bus.set_config(&self.config);
|
bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;
|
||||||
bus.read(address, buffer).await.map_err(I2cDeviceError::I2c)?;
|
bus.read(address, buffer).await.map_err(I2cDeviceError::I2c)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), I2cDeviceError<BUS::Error>> {
|
async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), I2cDeviceError<BUS::Error>> {
|
||||||
let mut bus = self.bus.lock().await;
|
let mut bus = self.bus.lock().await;
|
||||||
bus.set_config(&self.config);
|
bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;
|
||||||
bus.write(address, bytes).await.map_err(I2cDeviceError::I2c)?;
|
bus.write(address, bytes).await.map_err(I2cDeviceError::I2c)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -144,7 +144,7 @@ where
|
|||||||
rd_buffer: &mut [u8],
|
rd_buffer: &mut [u8],
|
||||||
) -> Result<(), I2cDeviceError<BUS::Error>> {
|
) -> Result<(), I2cDeviceError<BUS::Error>> {
|
||||||
let mut bus = self.bus.lock().await;
|
let mut bus = self.bus.lock().await;
|
||||||
bus.set_config(&self.config);
|
bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;
|
||||||
bus.write_read(address, wr_buffer, rd_buffer)
|
bus.write_read(address, wr_buffer, rd_buffer)
|
||||||
.await
|
.await
|
||||||
.map_err(I2cDeviceError::I2c)?;
|
.map_err(I2cDeviceError::I2c)?;
|
||||||
@ -153,7 +153,7 @@ where
|
|||||||
|
|
||||||
async fn transaction(&mut self, address: u8, operations: &mut [i2c::Operation<'_>]) -> Result<(), Self::Error> {
|
async fn transaction(&mut self, address: u8, operations: &mut [i2c::Operation<'_>]) -> Result<(), Self::Error> {
|
||||||
let mut bus = self.bus.lock().await;
|
let mut bus = self.bus.lock().await;
|
||||||
bus.set_config(&self.config);
|
bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;
|
||||||
bus.transaction(address, operations)
|
bus.transaction(address, operations)
|
||||||
.await
|
.await
|
||||||
.map_err(I2cDeviceError::I2c)?;
|
.map_err(I2cDeviceError::I2c)?;
|
||||||
|
@ -66,21 +66,29 @@ where
|
|||||||
let mut bus = self.bus.lock().await;
|
let mut bus = self.bus.lock().await;
|
||||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
||||||
|
|
||||||
let op_res: Result<(), BUS::Error> = try {
|
let op_res = 'ops: {
|
||||||
for op in operations {
|
for op in operations {
|
||||||
match op {
|
let res = match op {
|
||||||
Operation::Read(buf) => bus.read(buf).await?,
|
Operation::Read(buf) => bus.read(buf).await,
|
||||||
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"))]
|
#[cfg(not(feature = "time"))]
|
||||||
Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported),
|
Operation::DelayUs(us) => return Err(SpiDeviceError::DelayUsNotSupported),
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
Operation::DelayUs(us) => {
|
Operation::DelayUs(us) => match bus.flush().await {
|
||||||
embassy_time::Timer::after(embassy_time::Duration::from_micros(*us as _)).await
|
Err(e) => Err(e),
|
||||||
}
|
Ok(()) => {
|
||||||
|
embassy_time::Timer::after_micros(*us as _).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if let Err(e) = res {
|
||||||
|
break 'ops Err(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
};
|
};
|
||||||
|
|
||||||
// On failure, it's important to still flush and deassert CS.
|
// On failure, it's important to still flush and deassert CS.
|
||||||
@ -130,24 +138,32 @@ where
|
|||||||
{
|
{
|
||||||
async fn transaction(&mut self, operations: &mut [spi::Operation<'_, u8>]) -> Result<(), Self::Error> {
|
async fn transaction(&mut self, operations: &mut [spi::Operation<'_, u8>]) -> Result<(), Self::Error> {
|
||||||
let mut bus = self.bus.lock().await;
|
let mut bus = self.bus.lock().await;
|
||||||
bus.set_config(&self.config);
|
bus.set_config(&self.config).map_err(|_| SpiDeviceError::Config)?;
|
||||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
||||||
|
|
||||||
let op_res: Result<(), BUS::Error> = try {
|
let op_res = 'ops: {
|
||||||
for op in operations {
|
for op in operations {
|
||||||
match op {
|
let res = match op {
|
||||||
Operation::Read(buf) => bus.read(buf).await?,
|
Operation::Read(buf) => bus.read(buf).await,
|
||||||
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"))]
|
#[cfg(not(feature = "time"))]
|
||||||
Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported),
|
Operation::DelayUs(us) => return Err(SpiDeviceError::DelayUsNotSupported),
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
Operation::DelayUs(us) => {
|
Operation::DelayUs(us) => match bus.flush().await {
|
||||||
embassy_time::Timer::after(embassy_time::Duration::from_micros(*us as _)).await
|
Err(e) => Err(e),
|
||||||
}
|
Ok(()) => {
|
||||||
|
embassy_time::Timer::after_micros(*us as _).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if let Err(e) = res {
|
||||||
|
break 'ops Err(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
};
|
};
|
||||||
|
|
||||||
// On failure, it's important to still flush and deassert CS.
|
// On failure, it's important to still flush and deassert CS.
|
||||||
|
@ -148,7 +148,7 @@ where
|
|||||||
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
self.bus.lock(|bus| {
|
self.bus.lock(|bus| {
|
||||||
let mut bus = bus.borrow_mut();
|
let mut bus = bus.borrow_mut();
|
||||||
bus.set_config(&self.config);
|
bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;
|
||||||
bus.read(address, buffer).map_err(I2cDeviceError::I2c)
|
bus.read(address, buffer).map_err(I2cDeviceError::I2c)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -156,7 +156,7 @@ where
|
|||||||
fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> {
|
fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> {
|
||||||
self.bus.lock(|bus| {
|
self.bus.lock(|bus| {
|
||||||
let mut bus = bus.borrow_mut();
|
let mut bus = bus.borrow_mut();
|
||||||
bus.set_config(&self.config);
|
bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;
|
||||||
bus.write(address, bytes).map_err(I2cDeviceError::I2c)
|
bus.write(address, bytes).map_err(I2cDeviceError::I2c)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -164,7 +164,7 @@ where
|
|||||||
fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> {
|
fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
self.bus.lock(|bus| {
|
self.bus.lock(|bus| {
|
||||||
let mut bus = bus.borrow_mut();
|
let mut bus = bus.borrow_mut();
|
||||||
bus.set_config(&self.config);
|
bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;
|
||||||
bus.write_read(address, wr_buffer, rd_buffer)
|
bus.write_read(address, wr_buffer, rd_buffer)
|
||||||
.map_err(I2cDeviceError::I2c)
|
.map_err(I2cDeviceError::I2c)
|
||||||
})
|
})
|
||||||
|
@ -163,7 +163,7 @@ where
|
|||||||
fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> {
|
fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> {
|
||||||
self.bus.lock(|bus| {
|
self.bus.lock(|bus| {
|
||||||
let mut bus = bus.borrow_mut();
|
let mut bus = bus.borrow_mut();
|
||||||
bus.set_config(&self.config);
|
bus.set_config(&self.config).map_err(|_| SpiDeviceError::Config)?;
|
||||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
||||||
|
|
||||||
let op_res = operations.iter_mut().try_for_each(|op| match op {
|
let op_res = operations.iter_mut().try_for_each(|op| match op {
|
||||||
|
@ -14,6 +14,8 @@ pub mod blocking;
|
|||||||
pub enum I2cDeviceError<BUS> {
|
pub enum I2cDeviceError<BUS> {
|
||||||
/// An operation on the inner I2C bus failed.
|
/// An operation on the inner I2C bus failed.
|
||||||
I2c(BUS),
|
I2c(BUS),
|
||||||
|
/// Configuration of the inner I2C bus failed.
|
||||||
|
Config,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<BUS> i2c::Error for I2cDeviceError<BUS>
|
impl<BUS> i2c::Error for I2cDeviceError<BUS>
|
||||||
@ -23,6 +25,7 @@ where
|
|||||||
fn kind(&self) -> i2c::ErrorKind {
|
fn kind(&self) -> i2c::ErrorKind {
|
||||||
match self {
|
match self {
|
||||||
Self::I2c(e) => e.kind(),
|
Self::I2c(e) => e.kind(),
|
||||||
|
Self::Config => i2c::ErrorKind::Other,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -38,6 +41,8 @@ pub enum SpiDeviceError<BUS, CS> {
|
|||||||
Cs(CS),
|
Cs(CS),
|
||||||
/// DelayUs operations are not supported when the `time` Cargo feature is not enabled.
|
/// DelayUs operations are not supported when the `time` Cargo feature is not enabled.
|
||||||
DelayUsNotSupported,
|
DelayUsNotSupported,
|
||||||
|
/// The SPI bus could not be configured.
|
||||||
|
Config,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<BUS, CS> spi::Error for SpiDeviceError<BUS, CS>
|
impl<BUS, CS> spi::Error for SpiDeviceError<BUS, CS>
|
||||||
@ -50,6 +55,7 @@ where
|
|||||||
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,
|
Self::DelayUsNotSupported => spi::ErrorKind::Other,
|
||||||
|
Self::Config => spi::ErrorKind::Other,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,20 @@ 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.3.3 - 2023-11-15
|
||||||
|
|
||||||
|
- Add `main` macro reexport for Xtensa arch.
|
||||||
|
- Remove use of `atomic-polyfill`. The executor now has multiple implementations of its internal data structures for cases where the target supports atomics or doesn't.
|
||||||
|
|
||||||
|
## 0.3.2 - 2023-11-06
|
||||||
|
|
||||||
|
- Use `atomic-polyfill` for `riscv32`
|
||||||
|
- Removed unused dependencies (static_cell, futures-util)
|
||||||
|
|
||||||
|
## 0.3.1 - 2023-11-01
|
||||||
|
|
||||||
|
- Fix spurious "Found waker not created by the Embassy executor" error in recent nightlies.
|
||||||
|
|
||||||
## 0.3.0 - 2023-08-25
|
## 0.3.0 - 2023-08-25
|
||||||
|
|
||||||
- Replaced Pender. Implementations now must define an extern function called `__pender`.
|
- Replaced Pender. Implementations now must define an extern function called `__pender`.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "embassy-executor"
|
name = "embassy-executor"
|
||||||
version = "0.3.0"
|
version = "0.3.3"
|
||||||
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"
|
||||||
@ -27,6 +27,30 @@ default-target = "thumbv7em-none-eabi"
|
|||||||
targets = ["thumbv7em-none-eabi"]
|
targets = ["thumbv7em-none-eabi"]
|
||||||
features = ["nightly", "defmt", "arch-cortex-m", "executor-thread", "executor-interrupt"]
|
features = ["nightly", "defmt", "arch-cortex-m", "executor-thread", "executor-interrupt"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
defmt = { version = "0.3", optional = true }
|
||||||
|
log = { version = "0.4.14", optional = true }
|
||||||
|
rtos-trace = { version = "0.1.2", optional = true }
|
||||||
|
|
||||||
|
embassy-macros = { version = "0.2.1", path = "../embassy-macros" }
|
||||||
|
embassy-time = { version = "0.1.5", path = "../embassy-time", optional = true}
|
||||||
|
critical-section = "1.1"
|
||||||
|
|
||||||
|
# needed for riscv
|
||||||
|
# remove when https://github.com/rust-lang/rust/pull/114499 is merged
|
||||||
|
portable-atomic = { version = "1.5", optional = true }
|
||||||
|
|
||||||
|
# arch-cortex-m dependencies
|
||||||
|
cortex-m = { version = "0.7.6", optional = true }
|
||||||
|
|
||||||
|
# arch-wasm dependencies
|
||||||
|
wasm-bindgen = { version = "0.2.82", optional = true }
|
||||||
|
js-sys = { version = "0.3", optional = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
critical-section = { version = "1.1", features = ["std"] }
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|
||||||
# Architecture
|
# Architecture
|
||||||
@ -34,7 +58,7 @@ _arch = [] # some arch was picked
|
|||||||
arch-std = ["_arch", "critical-section/std"]
|
arch-std = ["_arch", "critical-section/std"]
|
||||||
arch-cortex-m = ["_arch", "dep:cortex-m"]
|
arch-cortex-m = ["_arch", "dep:cortex-m"]
|
||||||
arch-xtensa = ["_arch"]
|
arch-xtensa = ["_arch"]
|
||||||
arch-riscv32 = ["_arch"]
|
arch-riscv32 = ["_arch", "dep:portable-atomic"]
|
||||||
arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"]
|
arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"]
|
||||||
|
|
||||||
# Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded archs)
|
# Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded archs)
|
||||||
@ -43,30 +67,53 @@ executor-thread = []
|
|||||||
executor-interrupt = []
|
executor-interrupt = []
|
||||||
|
|
||||||
# Enable nightly-only features
|
# Enable nightly-only features
|
||||||
nightly = []
|
nightly = ["embassy-macros/nightly"]
|
||||||
|
|
||||||
turbowakers = []
|
turbowakers = []
|
||||||
|
|
||||||
integrated-timers = ["dep:embassy-time"]
|
integrated-timers = ["dep:embassy-time"]
|
||||||
|
|
||||||
# Trace interrupt invocations with rtos-trace.
|
# BEGIN AUTOGENERATED CONFIG FEATURES
|
||||||
rtos-trace-interrupt = ["rtos-trace", "embassy-macros/rtos-trace-interrupt"]
|
# Generated by gen_config.py. DO NOT EDIT.
|
||||||
|
task-arena-size-64 = []
|
||||||
|
task-arena-size-128 = []
|
||||||
|
task-arena-size-192 = []
|
||||||
|
task-arena-size-256 = []
|
||||||
|
task-arena-size-320 = []
|
||||||
|
task-arena-size-384 = []
|
||||||
|
task-arena-size-512 = []
|
||||||
|
task-arena-size-640 = []
|
||||||
|
task-arena-size-768 = []
|
||||||
|
task-arena-size-1024 = []
|
||||||
|
task-arena-size-1280 = []
|
||||||
|
task-arena-size-1536 = []
|
||||||
|
task-arena-size-2048 = []
|
||||||
|
task-arena-size-2560 = []
|
||||||
|
task-arena-size-3072 = []
|
||||||
|
task-arena-size-4096 = [] # Default
|
||||||
|
task-arena-size-5120 = []
|
||||||
|
task-arena-size-6144 = []
|
||||||
|
task-arena-size-8192 = []
|
||||||
|
task-arena-size-10240 = []
|
||||||
|
task-arena-size-12288 = []
|
||||||
|
task-arena-size-16384 = []
|
||||||
|
task-arena-size-20480 = []
|
||||||
|
task-arena-size-24576 = []
|
||||||
|
task-arena-size-32768 = []
|
||||||
|
task-arena-size-40960 = []
|
||||||
|
task-arena-size-49152 = []
|
||||||
|
task-arena-size-65536 = []
|
||||||
|
task-arena-size-81920 = []
|
||||||
|
task-arena-size-98304 = []
|
||||||
|
task-arena-size-131072 = []
|
||||||
|
task-arena-size-163840 = []
|
||||||
|
task-arena-size-196608 = []
|
||||||
|
task-arena-size-262144 = []
|
||||||
|
task-arena-size-327680 = []
|
||||||
|
task-arena-size-393216 = []
|
||||||
|
task-arena-size-524288 = []
|
||||||
|
task-arena-size-655360 = []
|
||||||
|
task-arena-size-786432 = []
|
||||||
|
task-arena-size-1048576 = []
|
||||||
|
|
||||||
[dependencies]
|
# END AUTOGENERATED CONFIG FEATURES
|
||||||
defmt = { version = "0.3", optional = true }
|
|
||||||
log = { version = "0.4.14", optional = true }
|
|
||||||
rtos-trace = { version = "0.1.2", optional = true }
|
|
||||||
|
|
||||||
futures-util = { version = "0.3.17", default-features = false }
|
|
||||||
embassy-macros = { version = "0.2.1", path = "../embassy-macros" }
|
|
||||||
embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true}
|
|
||||||
atomic-polyfill = "1.0.1"
|
|
||||||
critical-section = "1.1"
|
|
||||||
static_cell = "1.1"
|
|
||||||
|
|
||||||
# arch-cortex-m dependencies
|
|
||||||
cortex-m = { version = "0.7.6", optional = true }
|
|
||||||
|
|
||||||
# arch-wasm dependencies
|
|
||||||
wasm-bindgen = { version = "0.2.82", optional = true }
|
|
||||||
js-sys = { version = "0.3", optional = true }
|
|
||||||
|
@ -2,10 +2,36 @@
|
|||||||
|
|
||||||
An async/await executor designed for embedded usage.
|
An async/await executor designed for embedded usage.
|
||||||
|
|
||||||
- No `alloc`, no heap needed. Task futures are statically allocated.
|
- No `alloc`, no heap needed.
|
||||||
|
- With nightly Rust, task futures can be fully statically allocated.
|
||||||
- No "fixed capacity" data structures, executor works with 1 or 1000 tasks without needing config/tuning.
|
- No "fixed capacity" data structures, executor works with 1 or 1000 tasks without needing config/tuning.
|
||||||
- Integrated timer queue: sleeping is easy, just do `Timer::after(Duration::from_secs(1)).await;`.
|
- Integrated timer queue: sleeping is easy, just do `Timer::after_secs(1).await;`.
|
||||||
- No busy-loop polling: CPU sleeps when there's no work to do, using interrupts or `WFE/SEV`.
|
- No busy-loop polling: CPU sleeps when there's no work to do, using interrupts or `WFE/SEV`.
|
||||||
- Efficient polling: a wake will only poll the woken task, not all of them.
|
- Efficient polling: a wake will only poll the woken task, not all of them.
|
||||||
- Fair: a task can't monopolize CPU time even if it's constantly being woken. All other tasks get a chance to run before a given task gets polled for the second time.
|
- Fair: a task can't monopolize CPU time even if it's constantly being woken. All other tasks get a chance to run before a given task gets polled for the second time.
|
||||||
- Creating multiple executor instances is supported, to run tasks with multiple priority levels. This allows higher-priority tasks to preempt lower-priority tasks.
|
- Creating multiple executor instances is supported, to run tasks with multiple priority levels. This allows higher-priority tasks to preempt lower-priority tasks.
|
||||||
|
|
||||||
|
## Task arena
|
||||||
|
|
||||||
|
When the `nightly` Cargo feature is not enabled, `embassy-executor` allocates tasks out of an arena (a very simple bump allocator).
|
||||||
|
|
||||||
|
If the task arena gets full, the program will panic at runtime. To guarantee this doesn't happen, you must set the size to the sum of sizes of all tasks.
|
||||||
|
|
||||||
|
Tasks are allocated from the arena when spawned for the first time. If the task exists, the allocation is not released to the arena, but can be reused to spawn the task again. For multiple-instance tasks (like `#[embassy_executor::task(pool_size = 4)]`), the first spawn will allocate memory for all instances. This is done for performance and to increase predictability (for example, spawning at least 1 instance of every task at boot guarantees an immediate panic if the arena is too small, while allocating instances on-demand could delay the panic to only when the program is under load).
|
||||||
|
|
||||||
|
The arena size can be configured in two ways:
|
||||||
|
|
||||||
|
- Via Cargo features: enable a Cargo feature like `task-arena-size-8192`. Only a selection of values
|
||||||
|
is available, check `Cargo.toml` for the list.
|
||||||
|
- Via environment variables at build time: set the variable named `EMBASSY_EXECUTOR_TASK_ARENA_SIZE`. For example
|
||||||
|
`EMBASSY_EXECUTOR_TASK_ARENA_SIZE=4321 cargo build`. You can also set them in the `[env]` section of `.cargo/config.toml`.
|
||||||
|
Any value can be set, unlike with Cargo features.
|
||||||
|
|
||||||
|
Environment variables take precedence over Cargo features. If two Cargo features are enabled for the same setting
|
||||||
|
with different values, compilation fails.
|
||||||
|
|
||||||
|
## Statically allocating tasks
|
||||||
|
|
||||||
|
When using nightly Rust, enable the `nightly` Cargo feature. This will make `embassy-executor` use the `type_alias_impl_trait` feature to allocate all tasks in `static`s. Each task gets its own `static`, with the exact size to hold the task (or multiple instances of it, if using `pool_size`) calculated automatically at compile time. If tasks don't fit in RAM, this is detected at compile time by the linker. Runtime panics due to running out of memory are not possible.
|
||||||
|
|
||||||
|
The configured arena size is ignored, no arena is used at all.
|
||||||
|
@ -1,6 +1,97 @@
|
|||||||
use std::env;
|
use std::collections::HashMap;
|
||||||
|
use std::fmt::Write;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::{env, fs};
|
||||||
|
|
||||||
|
static CONFIGS: &[(&str, usize)] = &[
|
||||||
|
// BEGIN AUTOGENERATED CONFIG FEATURES
|
||||||
|
// Generated by gen_config.py. DO NOT EDIT.
|
||||||
|
("TASK_ARENA_SIZE", 4096),
|
||||||
|
// END AUTOGENERATED CONFIG FEATURES
|
||||||
|
];
|
||||||
|
|
||||||
|
struct ConfigState {
|
||||||
|
value: usize,
|
||||||
|
seen_feature: bool,
|
||||||
|
seen_env: bool,
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
let crate_name = env::var("CARGO_PKG_NAME")
|
||||||
|
.unwrap()
|
||||||
|
.to_ascii_uppercase()
|
||||||
|
.replace('-', "_");
|
||||||
|
|
||||||
|
// only rebuild if build.rs changed. Otherwise Cargo will rebuild if any
|
||||||
|
// other file changed.
|
||||||
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
|
|
||||||
|
// Rebuild if config envvar changed.
|
||||||
|
for (name, _) in CONFIGS {
|
||||||
|
println!("cargo:rerun-if-env-changed={crate_name}_{name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut configs = HashMap::new();
|
||||||
|
for (name, default) in CONFIGS {
|
||||||
|
configs.insert(
|
||||||
|
*name,
|
||||||
|
ConfigState {
|
||||||
|
value: *default,
|
||||||
|
seen_env: false,
|
||||||
|
seen_feature: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let prefix = format!("{crate_name}_");
|
||||||
|
for (var, value) in env::vars() {
|
||||||
|
if let Some(name) = var.strip_prefix(&prefix) {
|
||||||
|
let Some(cfg) = configs.get_mut(name) else {
|
||||||
|
panic!("Unknown env var {name}")
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok(value) = value.parse::<usize>() else {
|
||||||
|
panic!("Invalid value for env var {name}: {value}")
|
||||||
|
};
|
||||||
|
|
||||||
|
cfg.value = value;
|
||||||
|
cfg.seen_env = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(feature) = var.strip_prefix("CARGO_FEATURE_") {
|
||||||
|
if let Some(i) = feature.rfind('_') {
|
||||||
|
let name = &feature[..i];
|
||||||
|
let value = &feature[i + 1..];
|
||||||
|
if let Some(cfg) = configs.get_mut(name) {
|
||||||
|
let Ok(value) = value.parse::<usize>() else {
|
||||||
|
panic!("Invalid value for feature {name}: {value}")
|
||||||
|
};
|
||||||
|
|
||||||
|
// envvars take priority.
|
||||||
|
if !cfg.seen_env {
|
||||||
|
if cfg.seen_feature {
|
||||||
|
panic!("multiple values set for feature {}: {} and {}", name, cfg.value, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.value = value;
|
||||||
|
cfg.seen_feature = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut data = String::new();
|
||||||
|
|
||||||
|
for (name, cfg) in &configs {
|
||||||
|
writeln!(&mut data, "pub const {}: usize = {};", name, cfg.value).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||||
|
let out_file = out_dir.join("config.rs").to_string_lossy().to_string();
|
||||||
|
fs::write(out_file, data).unwrap();
|
||||||
|
|
||||||
|
// cortex-m targets
|
||||||
let target = env::var("TARGET").unwrap();
|
let target = env::var("TARGET").unwrap();
|
||||||
|
|
||||||
if target.starts_with("thumbv6m-") {
|
if target.starts_with("thumbv6m-") {
|
||||||
|
82
embassy-executor/gen_config.py
Normal file
82
embassy-executor/gen_config.py
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
abspath = os.path.abspath(__file__)
|
||||||
|
dname = os.path.dirname(abspath)
|
||||||
|
os.chdir(dname)
|
||||||
|
|
||||||
|
features = []
|
||||||
|
|
||||||
|
|
||||||
|
def feature(name, default, min=None, max=None, pow2=None, vals=None, factors=[]):
|
||||||
|
if vals is None:
|
||||||
|
assert min is not None
|
||||||
|
assert max is not None
|
||||||
|
|
||||||
|
vals = set()
|
||||||
|
val = min
|
||||||
|
while val <= max:
|
||||||
|
vals.add(val)
|
||||||
|
for f in factors:
|
||||||
|
if val * f <= max:
|
||||||
|
vals.add(val * f)
|
||||||
|
if (pow2 == True or (isinstance(pow2, int) and val >= pow2)) and val > 0:
|
||||||
|
val *= 2
|
||||||
|
else:
|
||||||
|
val += 1
|
||||||
|
vals.add(default)
|
||||||
|
vals = sorted(list(vals))
|
||||||
|
|
||||||
|
features.append(
|
||||||
|
{
|
||||||
|
"name": name,
|
||||||
|
"default": default,
|
||||||
|
"vals": vals,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
feature(
|
||||||
|
"task_arena_size", default=4096, min=64, max=1024 * 1024, pow2=True, factors=[3, 5]
|
||||||
|
)
|
||||||
|
|
||||||
|
# ========= Update Cargo.toml
|
||||||
|
|
||||||
|
things = ""
|
||||||
|
for f in features:
|
||||||
|
name = f["name"].replace("_", "-")
|
||||||
|
for val in f["vals"]:
|
||||||
|
things += f"{name}-{val} = []"
|
||||||
|
if val == f["default"]:
|
||||||
|
things += " # Default"
|
||||||
|
things += "\n"
|
||||||
|
things += "\n"
|
||||||
|
|
||||||
|
SEPARATOR_START = "# BEGIN AUTOGENERATED CONFIG FEATURES\n"
|
||||||
|
SEPARATOR_END = "# END AUTOGENERATED CONFIG FEATURES\n"
|
||||||
|
HELP = "# Generated by gen_config.py. DO NOT EDIT.\n"
|
||||||
|
with open("Cargo.toml", "r") as f:
|
||||||
|
data = f.read()
|
||||||
|
before, data = data.split(SEPARATOR_START, maxsplit=1)
|
||||||
|
_, after = data.split(SEPARATOR_END, maxsplit=1)
|
||||||
|
data = before + SEPARATOR_START + HELP + things + SEPARATOR_END + after
|
||||||
|
with open("Cargo.toml", "w") as f:
|
||||||
|
f.write(data)
|
||||||
|
|
||||||
|
|
||||||
|
# ========= Update build.rs
|
||||||
|
|
||||||
|
things = ""
|
||||||
|
for f in features:
|
||||||
|
name = f["name"].upper()
|
||||||
|
things += f' ("{name}", {f["default"]}),\n'
|
||||||
|
|
||||||
|
SEPARATOR_START = "// BEGIN AUTOGENERATED CONFIG FEATURES\n"
|
||||||
|
SEPARATOR_END = "// END AUTOGENERATED CONFIG FEATURES\n"
|
||||||
|
HELP = " // Generated by gen_config.py. DO NOT EDIT.\n"
|
||||||
|
with open("build.rs", "r") as f:
|
||||||
|
data = f.read()
|
||||||
|
before, data = data.split(SEPARATOR_START, maxsplit=1)
|
||||||
|
_, after = data.split(SEPARATOR_END, maxsplit=1)
|
||||||
|
data = before + SEPARATOR_START + HELP + things + " " + SEPARATOR_END + after
|
||||||
|
with open("build.rs", "w") as f:
|
||||||
|
f.write(data)
|
@ -51,7 +51,6 @@ mod thread {
|
|||||||
use core::arch::asm;
|
use core::arch::asm;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
#[cfg(feature = "nightly")]
|
|
||||||
pub use embassy_macros::main_cortex_m as main;
|
pub use embassy_macros::main_cortex_m as main;
|
||||||
|
|
||||||
use crate::{raw, Spawner};
|
use crate::{raw, Spawner};
|
||||||
@ -115,12 +114,12 @@ mod thread {
|
|||||||
pub use interrupt::*;
|
pub use interrupt::*;
|
||||||
#[cfg(feature = "executor-interrupt")]
|
#[cfg(feature = "executor-interrupt")]
|
||||||
mod interrupt {
|
mod interrupt {
|
||||||
use core::cell::UnsafeCell;
|
use core::cell::{Cell, UnsafeCell};
|
||||||
use core::mem::MaybeUninit;
|
use core::mem::MaybeUninit;
|
||||||
|
|
||||||
use atomic_polyfill::{AtomicBool, Ordering};
|
|
||||||
use cortex_m::interrupt::InterruptNumber;
|
use cortex_m::interrupt::InterruptNumber;
|
||||||
use cortex_m::peripheral::NVIC;
|
use cortex_m::peripheral::NVIC;
|
||||||
|
use critical_section::Mutex;
|
||||||
|
|
||||||
use crate::raw;
|
use crate::raw;
|
||||||
|
|
||||||
@ -146,7 +145,7 @@ mod interrupt {
|
|||||||
/// It is somewhat more complex to use, it's recommended to use the thread-mode
|
/// It is somewhat more complex to use, it's recommended to use the thread-mode
|
||||||
/// [`Executor`] instead, if it works for your use case.
|
/// [`Executor`] instead, if it works for your use case.
|
||||||
pub struct InterruptExecutor {
|
pub struct InterruptExecutor {
|
||||||
started: AtomicBool,
|
started: Mutex<Cell<bool>>,
|
||||||
executor: UnsafeCell<MaybeUninit<raw::Executor>>,
|
executor: UnsafeCell<MaybeUninit<raw::Executor>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,7 +157,7 @@ mod interrupt {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
started: AtomicBool::new(false),
|
started: Mutex::new(Cell::new(false)),
|
||||||
executor: UnsafeCell::new(MaybeUninit::uninit()),
|
executor: UnsafeCell::new(MaybeUninit::uninit()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -167,7 +166,8 @@ mod interrupt {
|
|||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// You MUST call this from the interrupt handler, and from nowhere else.
|
/// - You MUST call this from the interrupt handler, and from nowhere else.
|
||||||
|
/// - You must not call this before calling `start()`.
|
||||||
pub unsafe fn on_interrupt(&'static self) {
|
pub unsafe fn on_interrupt(&'static self) {
|
||||||
let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
|
let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
|
||||||
executor.poll();
|
executor.poll();
|
||||||
@ -196,11 +196,7 @@ mod interrupt {
|
|||||||
/// do it after.
|
/// do it after.
|
||||||
///
|
///
|
||||||
pub fn start(&'static self, irq: impl InterruptNumber) -> crate::SendSpawner {
|
pub fn start(&'static self, irq: impl InterruptNumber) -> crate::SendSpawner {
|
||||||
if self
|
if critical_section::with(|cs| self.started.borrow(cs).replace(true)) {
|
||||||
.started
|
|
||||||
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
panic!("InterruptExecutor::start() called multiple times on the same executor.");
|
panic!("InterruptExecutor::start() called multiple times on the same executor.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,10 +218,10 @@ mod interrupt {
|
|||||||
/// This returns a [`SendSpawner`] you can use to spawn tasks on this
|
/// This returns a [`SendSpawner`] you can use to spawn tasks on this
|
||||||
/// executor.
|
/// executor.
|
||||||
///
|
///
|
||||||
/// This MUST only be called on an executor that has already been spawned.
|
/// This MUST only be called on an executor that has already been started.
|
||||||
/// The function will panic otherwise.
|
/// The function will panic otherwise.
|
||||||
pub fn spawner(&'static self) -> crate::SendSpawner {
|
pub fn spawner(&'static self) -> crate::SendSpawner {
|
||||||
if !self.started.load(Ordering::Acquire) {
|
if !critical_section::with(|cs| self.started.borrow(cs).get()) {
|
||||||
panic!("InterruptExecutor::spawner() called on uninitialized executor.");
|
panic!("InterruptExecutor::spawner() called on uninitialized executor.");
|
||||||
}
|
}
|
||||||
let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
|
let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
|
||||||
|
@ -6,10 +6,9 @@ pub use thread::*;
|
|||||||
#[cfg(feature = "executor-thread")]
|
#[cfg(feature = "executor-thread")]
|
||||||
mod thread {
|
mod thread {
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::sync::atomic::{AtomicBool, Ordering};
|
|
||||||
|
|
||||||
#[cfg(feature = "nightly")]
|
|
||||||
pub use embassy_macros::main_riscv as main;
|
pub use embassy_macros::main_riscv as main;
|
||||||
|
use portable_atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
use crate::{raw, Spawner};
|
use crate::{raw, Spawner};
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ mod thread {
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::sync::{Condvar, Mutex};
|
use std::sync::{Condvar, Mutex};
|
||||||
|
|
||||||
#[cfg(feature = "nightly")]
|
|
||||||
pub use embassy_macros::main_std as main;
|
pub use embassy_macros::main_std as main;
|
||||||
|
|
||||||
use crate::{raw, Spawner};
|
use crate::{raw, Spawner};
|
||||||
|
@ -8,7 +8,6 @@ mod thread {
|
|||||||
|
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
#[cfg(feature = "nightly")]
|
|
||||||
pub use embassy_macros::main_wasm as main;
|
pub use embassy_macros::main_wasm as main;
|
||||||
use js_sys::Promise;
|
use js_sys::Promise;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
@ -73,9 +72,10 @@ mod thread {
|
|||||||
pub fn start(&'static mut self, init: impl FnOnce(Spawner)) {
|
pub fn start(&'static mut self, init: impl FnOnce(Spawner)) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let executor = &self.inner;
|
let executor = &self.inner;
|
||||||
self.ctx.closure.write(Closure::new(move |_| {
|
let future = Closure::new(move |_| {
|
||||||
executor.poll();
|
executor.poll();
|
||||||
}));
|
});
|
||||||
|
self.ctx.closure.write_in_place(|| future);
|
||||||
init(self.inner.spawner());
|
init(self.inner.spawner());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ mod thread {
|
|||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::sync::atomic::{AtomicBool, Ordering};
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
|
pub use embassy_macros::main_riscv as main;
|
||||||
|
|
||||||
use crate::{raw, Spawner};
|
use crate::{raw, Spawner};
|
||||||
|
|
||||||
/// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa
|
/// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#![cfg_attr(not(any(feature = "arch-std", feature = "arch-wasm")), no_std)]
|
#![cfg_attr(not(any(feature = "arch-std", feature = "arch-wasm")), no_std)]
|
||||||
#![cfg_attr(all(feature = "nightly", feature = "arch-xtensa"), feature(asm_experimental_arch))]
|
#![cfg_attr(feature = "arch-xtensa", feature(asm_experimental_arch))]
|
||||||
#![allow(clippy::new_without_default)]
|
#![allow(clippy::new_without_default)]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
@ -7,7 +7,6 @@
|
|||||||
// 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;
|
||||||
|
|
||||||
#[cfg(feature = "nightly")]
|
|
||||||
pub use embassy_macros::task;
|
pub use embassy_macros::task;
|
||||||
|
|
||||||
macro_rules! check_at_most_one {
|
macro_rules! check_at_most_one {
|
||||||
@ -33,6 +32,7 @@ check_at_most_one!("arch-cortex-m", "arch-riscv32", "arch-xtensa", "arch-std", "
|
|||||||
mod arch;
|
mod arch;
|
||||||
|
|
||||||
#[cfg(feature = "_arch")]
|
#[cfg(feature = "_arch")]
|
||||||
|
#[allow(unused_imports)] // don't warn if the module is empty.
|
||||||
pub use arch::*;
|
pub use arch::*;
|
||||||
|
|
||||||
pub mod raw;
|
pub mod raw;
|
||||||
@ -40,29 +40,101 @@ pub mod raw;
|
|||||||
mod spawner;
|
mod spawner;
|
||||||
pub use spawner::*;
|
pub use spawner::*;
|
||||||
|
|
||||||
|
mod config {
|
||||||
|
#![allow(unused)]
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/config.rs"));
|
||||||
|
}
|
||||||
|
|
||||||
/// Implementation details for embassy macros.
|
/// Implementation details for embassy macros.
|
||||||
/// Do not use. Used for macros and HALs only. Not covered by semver guarantees.
|
/// Do not use. Used for macros and HALs only. Not covered by semver guarantees.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
#[cfg(not(feature = "nightly"))]
|
||||||
pub mod _export {
|
pub mod _export {
|
||||||
#[cfg(feature = "rtos-trace")]
|
use core::alloc::Layout;
|
||||||
pub use rtos_trace::trace;
|
use core::cell::{Cell, UnsafeCell};
|
||||||
pub use static_cell::StaticCell;
|
use core::future::Future;
|
||||||
|
use core::mem::MaybeUninit;
|
||||||
|
use core::ptr::null_mut;
|
||||||
|
|
||||||
/// Expands the given block of code when `embassy-executor` is compiled with
|
use critical_section::{CriticalSection, Mutex};
|
||||||
/// the `rtos-trace-interrupt` feature.
|
|
||||||
#[doc(hidden)]
|
use crate::raw::TaskPool;
|
||||||
#[macro_export]
|
|
||||||
#[cfg(feature = "rtos-trace-interrupt")]
|
struct Arena<const N: usize> {
|
||||||
macro_rules! rtos_trace_interrupt {
|
buf: UnsafeCell<MaybeUninit<[u8; N]>>,
|
||||||
($($tt:tt)*) => { $($tt)* };
|
ptr: Mutex<Cell<*mut u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Does not expand the given block of code when `embassy-executor` is
|
unsafe impl<const N: usize> Sync for Arena<N> {}
|
||||||
/// compiled without the `rtos-trace-interrupt` feature.
|
unsafe impl<const N: usize> Send for Arena<N> {}
|
||||||
#[doc(hidden)]
|
|
||||||
#[macro_export]
|
impl<const N: usize> Arena<N> {
|
||||||
#[cfg(not(feature = "rtos-trace-interrupt"))]
|
const fn new() -> Self {
|
||||||
macro_rules! rtos_trace_interrupt {
|
Self {
|
||||||
($($tt:tt)*) => {};
|
buf: UnsafeCell::new(MaybeUninit::uninit()),
|
||||||
|
ptr: Mutex::new(Cell::new(null_mut())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc<T>(&'static self, cs: CriticalSection) -> &'static mut MaybeUninit<T> {
|
||||||
|
let layout = Layout::new::<T>();
|
||||||
|
|
||||||
|
let start = self.buf.get().cast::<u8>();
|
||||||
|
let end = unsafe { start.add(N) };
|
||||||
|
|
||||||
|
let mut ptr = self.ptr.borrow(cs).get();
|
||||||
|
if ptr.is_null() {
|
||||||
|
ptr = self.buf.get().cast::<u8>();
|
||||||
|
}
|
||||||
|
|
||||||
|
let bytes_left = (end as usize) - (ptr as usize);
|
||||||
|
let align_offset = (ptr as usize).next_multiple_of(layout.align()) - (ptr as usize);
|
||||||
|
|
||||||
|
if align_offset + layout.size() > bytes_left {
|
||||||
|
panic!("embassy-executor: task arena is full. You must increase the arena size, see the documentation for details: https://docs.embassy.dev/embassy-executor/");
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = unsafe { ptr.add(align_offset) };
|
||||||
|
let ptr = unsafe { ptr.add(align_offset + layout.size()) };
|
||||||
|
|
||||||
|
self.ptr.borrow(cs).set(ptr);
|
||||||
|
|
||||||
|
unsafe { &mut *(res as *mut MaybeUninit<T>) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ARENA: Arena<{ crate::config::TASK_ARENA_SIZE }> = Arena::new();
|
||||||
|
|
||||||
|
pub struct TaskPoolRef {
|
||||||
|
// type-erased `&'static mut TaskPool<F, N>`
|
||||||
|
// Needed because statics can't have generics.
|
||||||
|
ptr: Mutex<Cell<*mut ()>>,
|
||||||
|
}
|
||||||
|
unsafe impl Sync for TaskPoolRef {}
|
||||||
|
unsafe impl Send for TaskPoolRef {}
|
||||||
|
|
||||||
|
impl TaskPoolRef {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
ptr: Mutex::new(Cell::new(null_mut())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the pool for this ref, allocating it from the arena the first time.
|
||||||
|
///
|
||||||
|
/// safety: for a given TaskPoolRef instance, must always call with the exact
|
||||||
|
/// same generic params.
|
||||||
|
pub unsafe fn get<F: Future, const N: usize>(&'static self) -> &'static TaskPool<F, N> {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let ptr = self.ptr.borrow(cs);
|
||||||
|
if ptr.get().is_null() {
|
||||||
|
let pool = ARENA.alloc::<TaskPool<F, N>>(cs);
|
||||||
|
pool.write(TaskPool::new());
|
||||||
|
ptr.set(pool as *mut _ as _);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { &*(ptr.get() as *const _) }
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,15 @@
|
|||||||
//! Using this module requires respecting subtle safety contracts. If you can, prefer using the safe
|
//! Using this module requires respecting subtle safety contracts. If you can, prefer using the safe
|
||||||
//! [executor wrappers](crate::Executor) and the [`embassy_executor::task`](embassy_macros::task) macro, which are fully safe.
|
//! [executor wrappers](crate::Executor) and the [`embassy_executor::task`](embassy_macros::task) macro, which are fully safe.
|
||||||
|
|
||||||
|
#[cfg_attr(target_has_atomic = "ptr", path = "run_queue_atomics.rs")]
|
||||||
|
#[cfg_attr(not(target_has_atomic = "ptr"), path = "run_queue_critical_section.rs")]
|
||||||
mod run_queue;
|
mod run_queue;
|
||||||
|
|
||||||
|
#[cfg_attr(all(cortex_m, target_has_atomic = "8"), path = "state_atomics_arm.rs")]
|
||||||
|
#[cfg_attr(all(not(cortex_m), target_has_atomic = "8"), path = "state_atomics.rs")]
|
||||||
|
#[cfg_attr(not(target_has_atomic = "8"), path = "state_critical_section.rs")]
|
||||||
|
mod state;
|
||||||
|
|
||||||
#[cfg(feature = "integrated-timers")]
|
#[cfg(feature = "integrated-timers")]
|
||||||
mod timer_queue;
|
mod timer_queue;
|
||||||
pub(crate) mod util;
|
pub(crate) mod util;
|
||||||
@ -21,7 +29,6 @@ use core::pin::Pin;
|
|||||||
use core::ptr::NonNull;
|
use core::ptr::NonNull;
|
||||||
use core::task::{Context, Poll};
|
use core::task::{Context, Poll};
|
||||||
|
|
||||||
use atomic_polyfill::{AtomicU32, Ordering};
|
|
||||||
#[cfg(feature = "integrated-timers")]
|
#[cfg(feature = "integrated-timers")]
|
||||||
use embassy_time::driver::{self, AlarmHandle};
|
use embassy_time::driver::{self, AlarmHandle};
|
||||||
#[cfg(feature = "integrated-timers")]
|
#[cfg(feature = "integrated-timers")]
|
||||||
@ -30,21 +37,14 @@ use embassy_time::Instant;
|
|||||||
use rtos_trace::trace;
|
use rtos_trace::trace;
|
||||||
|
|
||||||
use self::run_queue::{RunQueue, RunQueueItem};
|
use self::run_queue::{RunQueue, RunQueueItem};
|
||||||
|
use self::state::State;
|
||||||
use self::util::{SyncUnsafeCell, UninitCell};
|
use self::util::{SyncUnsafeCell, UninitCell};
|
||||||
pub use self::waker::task_from_waker;
|
pub use self::waker::task_from_waker;
|
||||||
use super::SpawnToken;
|
use super::SpawnToken;
|
||||||
|
|
||||||
/// Task is spawned (has a future)
|
|
||||||
pub(crate) const STATE_SPAWNED: u32 = 1 << 0;
|
|
||||||
/// Task is in the executor run queue
|
|
||||||
pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1;
|
|
||||||
/// Task is in the executor timer queue
|
|
||||||
#[cfg(feature = "integrated-timers")]
|
|
||||||
pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2;
|
|
||||||
|
|
||||||
/// Raw task header for use in task pointers.
|
/// Raw task header for use in task pointers.
|
||||||
pub(crate) struct TaskHeader {
|
pub(crate) struct TaskHeader {
|
||||||
pub(crate) state: AtomicU32,
|
pub(crate) state: State,
|
||||||
pub(crate) run_queue_item: RunQueueItem,
|
pub(crate) run_queue_item: RunQueueItem,
|
||||||
pub(crate) executor: SyncUnsafeCell<Option<&'static SyncExecutor>>,
|
pub(crate) executor: SyncUnsafeCell<Option<&'static SyncExecutor>>,
|
||||||
poll_fn: SyncUnsafeCell<Option<unsafe fn(TaskRef)>>,
|
poll_fn: SyncUnsafeCell<Option<unsafe fn(TaskRef)>>,
|
||||||
@ -116,7 +116,7 @@ impl<F: Future + 'static> TaskStorage<F> {
|
|||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
raw: TaskHeader {
|
raw: TaskHeader {
|
||||||
state: AtomicU32::new(0),
|
state: State::new(),
|
||||||
run_queue_item: RunQueueItem::new(),
|
run_queue_item: RunQueueItem::new(),
|
||||||
executor: SyncUnsafeCell::new(None),
|
executor: SyncUnsafeCell::new(None),
|
||||||
// Note: this is lazily initialized so that a static `TaskStorage` will go in `.bss`
|
// Note: this is lazily initialized so that a static `TaskStorage` will go in `.bss`
|
||||||
@ -161,7 +161,7 @@ impl<F: Future + 'static> TaskStorage<F> {
|
|||||||
match future.poll(&mut cx) {
|
match future.poll(&mut cx) {
|
||||||
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.despawn();
|
||||||
|
|
||||||
#[cfg(feature = "integrated-timers")]
|
#[cfg(feature = "integrated-timers")]
|
||||||
this.raw.expires_at.set(Instant::MAX);
|
this.raw.expires_at.set(Instant::MAX);
|
||||||
@ -193,17 +193,13 @@ impl<F: Future + 'static> AvailableTask<F> {
|
|||||||
///
|
///
|
||||||
/// This function returns `None` if a task has already been spawned and has not finished running.
|
/// This function returns `None` if a task has already been spawned and has not finished running.
|
||||||
pub fn claim(task: &'static TaskStorage<F>) -> Option<Self> {
|
pub fn claim(task: &'static TaskStorage<F>) -> Option<Self> {
|
||||||
task.raw
|
task.raw.state.spawn().then(|| Self { task })
|
||||||
.state
|
|
||||||
.compare_exchange(0, STATE_SPAWNED | STATE_RUN_QUEUED, Ordering::AcqRel, Ordering::Acquire)
|
|
||||||
.ok()
|
|
||||||
.map(|_| Self { task })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize_impl<S>(self, future: impl FnOnce() -> F) -> SpawnToken<S> {
|
fn initialize_impl<S>(self, future: impl FnOnce() -> F) -> SpawnToken<S> {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.task.raw.poll_fn.set(Some(TaskStorage::<F>::poll));
|
self.task.raw.poll_fn.set(Some(TaskStorage::<F>::poll));
|
||||||
self.task.future.write(future());
|
self.task.future.write_in_place(future);
|
||||||
|
|
||||||
let task = TaskRef::new(self.task);
|
let task = TaskRef::new(self.task);
|
||||||
|
|
||||||
@ -394,8 +390,7 @@ impl SyncExecutor {
|
|||||||
#[cfg(feature = "integrated-timers")]
|
#[cfg(feature = "integrated-timers")]
|
||||||
task.expires_at.set(Instant::MAX);
|
task.expires_at.set(Instant::MAX);
|
||||||
|
|
||||||
let state = task.state.fetch_and(!STATE_RUN_QUEUED, Ordering::AcqRel);
|
if !task.state.run_dequeue() {
|
||||||
if state & STATE_SPAWNED == 0 {
|
|
||||||
// If task is not running, ignore it. This can happen in the following scenario:
|
// If task is not running, ignore it. This can happen in the following scenario:
|
||||||
// - Task gets dequeued, poll starts
|
// - Task gets dequeued, poll starts
|
||||||
// - While task is being polled, it gets woken. It gets placed in the queue.
|
// - While task is being polled, it gets woken. It gets placed in the queue.
|
||||||
@ -546,18 +541,7 @@ impl Executor {
|
|||||||
/// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`].
|
/// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`].
|
||||||
pub fn wake_task(task: TaskRef) {
|
pub fn wake_task(task: TaskRef) {
|
||||||
let header = task.header();
|
let header = task.header();
|
||||||
|
if header.state.run_enqueue() {
|
||||||
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.
|
// We have just marked the task as scheduled, so enqueue it.
|
||||||
unsafe {
|
unsafe {
|
||||||
let executor = header.executor.get().unwrap_unchecked();
|
let executor = header.executor.get().unwrap_unchecked();
|
||||||
@ -571,18 +555,7 @@ pub fn wake_task(task: TaskRef) {
|
|||||||
/// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`].
|
/// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`].
|
||||||
pub fn wake_task_no_pend(task: TaskRef) {
|
pub fn wake_task_no_pend(task: TaskRef) {
|
||||||
let header = task.header();
|
let header = task.header();
|
||||||
|
if header.state.run_enqueue() {
|
||||||
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.
|
// We have just marked the task as scheduled, so enqueue it.
|
||||||
unsafe {
|
unsafe {
|
||||||
let executor = header.executor.get().unwrap_unchecked();
|
let executor = header.executor.get().unwrap_unchecked();
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use core::ptr;
|
use core::ptr;
|
||||||
use core::ptr::NonNull;
|
use core::ptr::NonNull;
|
||||||
|
use core::sync::atomic::{AtomicPtr, Ordering};
|
||||||
use atomic_polyfill::{AtomicPtr, Ordering};
|
|
||||||
|
|
||||||
use super::{TaskHeader, TaskRef};
|
use super::{TaskHeader, TaskRef};
|
||||||
use crate::raw::util::SyncUnsafeCell;
|
use crate::raw::util::SyncUnsafeCell;
|
75
embassy-executor/src/raw/run_queue_critical_section.rs
Normal file
75
embassy-executor/src/raw/run_queue_critical_section.rs
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
use core::cell::Cell;
|
||||||
|
|
||||||
|
use critical_section::{CriticalSection, Mutex};
|
||||||
|
|
||||||
|
use super::TaskRef;
|
||||||
|
|
||||||
|
pub(crate) struct RunQueueItem {
|
||||||
|
next: Mutex<Cell<Option<TaskRef>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RunQueueItem {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
next: Mutex::new(Cell::new(None)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Atomic task queue using a very, very simple lock-free linked-list queue:
|
||||||
|
///
|
||||||
|
/// To enqueue a task, task.next is set to the old head, and head is atomically set to task.
|
||||||
|
///
|
||||||
|
/// Dequeuing is done in batches: the queue is emptied by atomically replacing head with
|
||||||
|
/// null. Then the batch is iterated following the next pointers until null is reached.
|
||||||
|
///
|
||||||
|
/// Note that batches will be iterated in the reverse order as they were enqueued. This is OK
|
||||||
|
/// for our purposes: it can't create fairness problems since the next batch won't run until the
|
||||||
|
/// current batch is completely processed, so even if a task enqueues itself instantly (for example
|
||||||
|
/// by waking its own waker) can't prevent other tasks from running.
|
||||||
|
pub(crate) struct RunQueue {
|
||||||
|
head: Mutex<Cell<Option<TaskRef>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RunQueue {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
head: Mutex::new(Cell::new(None)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enqueues an item. Returns true if the queue was empty.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// `item` must NOT be already enqueued in any queue.
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) unsafe fn enqueue(&self, task: TaskRef) -> bool {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let prev = self.head.borrow(cs).replace(Some(task));
|
||||||
|
task.header().run_queue_item.next.borrow(cs).set(prev);
|
||||||
|
|
||||||
|
prev.is_none()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Empty the queue, then call `on_task` for each task that was in the queue.
|
||||||
|
/// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue
|
||||||
|
/// and will be processed by the *next* call to `dequeue_all`, *not* the current one.
|
||||||
|
pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) {
|
||||||
|
// Atomically empty the queue.
|
||||||
|
let mut next = critical_section::with(|cs| self.head.borrow(cs).take());
|
||||||
|
|
||||||
|
// Iterate the linked list of tasks that were previously in the queue.
|
||||||
|
while let Some(task) = next {
|
||||||
|
// If the task re-enqueues itself, the `next` pointer will get overwritten.
|
||||||
|
// Therefore, first read the next pointer, and only then process the task.
|
||||||
|
|
||||||
|
// safety: we know if the task is enqueued, no one else will touch the `next` pointer.
|
||||||
|
let cs = unsafe { CriticalSection::new() };
|
||||||
|
next = task.header().run_queue_item.next.borrow(cs).get();
|
||||||
|
|
||||||
|
on_task(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
73
embassy-executor/src/raw/state_atomics.rs
Normal file
73
embassy-executor/src/raw/state_atomics.rs
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
use core::sync::atomic::{AtomicU32, Ordering};
|
||||||
|
|
||||||
|
/// Task is spawned (has a future)
|
||||||
|
pub(crate) const STATE_SPAWNED: u32 = 1 << 0;
|
||||||
|
/// Task is in the executor run queue
|
||||||
|
pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1;
|
||||||
|
/// Task is in the executor timer queue
|
||||||
|
#[cfg(feature = "integrated-timers")]
|
||||||
|
pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2;
|
||||||
|
|
||||||
|
pub(crate) struct State {
|
||||||
|
state: AtomicU32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
pub const fn new() -> State {
|
||||||
|
Self {
|
||||||
|
state: AtomicU32::new(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If task is idle, mark it as spawned + run_queued and return true.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn spawn(&self) -> bool {
|
||||||
|
self.state
|
||||||
|
.compare_exchange(0, STATE_SPAWNED | STATE_RUN_QUEUED, Ordering::AcqRel, Ordering::Acquire)
|
||||||
|
.is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unmark the task as spawned.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn despawn(&self) {
|
||||||
|
self.state.fetch_and(!STATE_SPAWNED, Ordering::AcqRel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark the task as run-queued if it's spawned and isn't already run-queued. Return true on success.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn run_enqueue(&self) -> bool {
|
||||||
|
self.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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unmark the task as run-queued. Return whether the task is spawned.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn run_dequeue(&self) -> bool {
|
||||||
|
let state = self.state.fetch_and(!STATE_RUN_QUEUED, Ordering::AcqRel);
|
||||||
|
state & STATE_SPAWNED != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark the task as timer-queued. Return whether it was newly queued (i.e. not queued before)
|
||||||
|
#[cfg(feature = "integrated-timers")]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn timer_enqueue(&self) -> bool {
|
||||||
|
let old_state = self.state.fetch_or(STATE_TIMER_QUEUED, Ordering::AcqRel);
|
||||||
|
old_state & STATE_TIMER_QUEUED == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unmark the task as timer-queued.
|
||||||
|
#[cfg(feature = "integrated-timers")]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn timer_dequeue(&self) {
|
||||||
|
self.state.fetch_and(!STATE_TIMER_QUEUED, Ordering::AcqRel);
|
||||||
|
}
|
||||||
|
}
|
103
embassy-executor/src/raw/state_atomics_arm.rs
Normal file
103
embassy-executor/src/raw/state_atomics_arm.rs
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
use core::arch::asm;
|
||||||
|
use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering};
|
||||||
|
|
||||||
|
// Must be kept in sync with the layout of `State`!
|
||||||
|
pub(crate) const STATE_SPAWNED: u32 = 1 << 0;
|
||||||
|
pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 8;
|
||||||
|
|
||||||
|
#[repr(C, align(4))]
|
||||||
|
pub(crate) struct State {
|
||||||
|
/// Task is spawned (has a future)
|
||||||
|
spawned: AtomicBool,
|
||||||
|
/// Task is in the executor run queue
|
||||||
|
run_queued: AtomicBool,
|
||||||
|
/// Task is in the executor timer queue
|
||||||
|
timer_queued: AtomicBool,
|
||||||
|
pad: AtomicBool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
pub const fn new() -> State {
|
||||||
|
Self {
|
||||||
|
spawned: AtomicBool::new(false),
|
||||||
|
run_queued: AtomicBool::new(false),
|
||||||
|
timer_queued: AtomicBool::new(false),
|
||||||
|
pad: AtomicBool::new(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_u32(&self) -> &AtomicU32 {
|
||||||
|
unsafe { &*(self as *const _ as *const AtomicU32) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If task is idle, mark it as spawned + run_queued and return true.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn spawn(&self) -> bool {
|
||||||
|
compiler_fence(Ordering::Release);
|
||||||
|
let r = self
|
||||||
|
.as_u32()
|
||||||
|
.compare_exchange(
|
||||||
|
0,
|
||||||
|
STATE_SPAWNED | STATE_RUN_QUEUED,
|
||||||
|
Ordering::Relaxed,
|
||||||
|
Ordering::Relaxed,
|
||||||
|
)
|
||||||
|
.is_ok();
|
||||||
|
compiler_fence(Ordering::Acquire);
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unmark the task as spawned.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn despawn(&self) {
|
||||||
|
compiler_fence(Ordering::Release);
|
||||||
|
self.spawned.store(false, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark the task as run-queued if it's spawned and isn't already run-queued. Return true on success.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn run_enqueue(&self) -> bool {
|
||||||
|
unsafe {
|
||||||
|
loop {
|
||||||
|
let state: u32;
|
||||||
|
asm!("ldrex {}, [{}]", out(reg) state, in(reg) self, options(nostack));
|
||||||
|
|
||||||
|
if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) {
|
||||||
|
asm!("clrex", options(nomem, nostack));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let outcome: usize;
|
||||||
|
let new_state = state | STATE_RUN_QUEUED;
|
||||||
|
asm!("strex {}, {}, [{}]", out(reg) outcome, in(reg) new_state, in(reg) self, options(nostack));
|
||||||
|
if outcome == 0 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unmark the task as run-queued. Return whether the task is spawned.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn run_dequeue(&self) -> bool {
|
||||||
|
compiler_fence(Ordering::Release);
|
||||||
|
|
||||||
|
let r = self.spawned.load(Ordering::Relaxed);
|
||||||
|
self.run_queued.store(false, Ordering::Relaxed);
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark the task as timer-queued. Return whether it was newly queued (i.e. not queued before)
|
||||||
|
#[cfg(feature = "integrated-timers")]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn timer_enqueue(&self) -> bool {
|
||||||
|
!self.timer_queued.swap(true, Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unmark the task as timer-queued.
|
||||||
|
#[cfg(feature = "integrated-timers")]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn timer_dequeue(&self) {
|
||||||
|
self.timer_queued.store(false, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
93
embassy-executor/src/raw/state_critical_section.rs
Normal file
93
embassy-executor/src/raw/state_critical_section.rs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
use core::cell::Cell;
|
||||||
|
|
||||||
|
use critical_section::Mutex;
|
||||||
|
|
||||||
|
/// Task is spawned (has a future)
|
||||||
|
pub(crate) const STATE_SPAWNED: u32 = 1 << 0;
|
||||||
|
/// Task is in the executor run queue
|
||||||
|
pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1;
|
||||||
|
/// Task is in the executor timer queue
|
||||||
|
#[cfg(feature = "integrated-timers")]
|
||||||
|
pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2;
|
||||||
|
|
||||||
|
pub(crate) struct State {
|
||||||
|
state: Mutex<Cell<u32>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
pub const fn new() -> State {
|
||||||
|
Self {
|
||||||
|
state: Mutex::new(Cell::new(0)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update<R>(&self, f: impl FnOnce(&mut u32) -> R) -> R {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let s = self.state.borrow(cs);
|
||||||
|
let mut val = s.get();
|
||||||
|
let r = f(&mut val);
|
||||||
|
s.set(val);
|
||||||
|
r
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If task is idle, mark it as spawned + run_queued and return true.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn spawn(&self) -> bool {
|
||||||
|
self.update(|s| {
|
||||||
|
if *s == 0 {
|
||||||
|
*s = STATE_SPAWNED | STATE_RUN_QUEUED;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unmark the task as spawned.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn despawn(&self) {
|
||||||
|
self.update(|s| *s &= !STATE_SPAWNED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark the task as run-queued if it's spawned and isn't already run-queued. Return true on success.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn run_enqueue(&self) -> bool {
|
||||||
|
self.update(|s| {
|
||||||
|
if (*s & STATE_RUN_QUEUED != 0) || (*s & STATE_SPAWNED == 0) {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
*s |= STATE_RUN_QUEUED;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unmark the task as run-queued. Return whether the task is spawned.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn run_dequeue(&self) -> bool {
|
||||||
|
self.update(|s| {
|
||||||
|
let ok = *s & STATE_SPAWNED != 0;
|
||||||
|
*s &= !STATE_RUN_QUEUED;
|
||||||
|
ok
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark the task as timer-queued. Return whether it was newly queued (i.e. not queued before)
|
||||||
|
#[cfg(feature = "integrated-timers")]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn timer_enqueue(&self) -> bool {
|
||||||
|
self.update(|s| {
|
||||||
|
let ok = *s & STATE_TIMER_QUEUED == 0;
|
||||||
|
*s |= STATE_TIMER_QUEUED;
|
||||||
|
ok
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unmark the task as timer-queued.
|
||||||
|
#[cfg(feature = "integrated-timers")]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn timer_dequeue(&self) {
|
||||||
|
self.update(|s| *s &= !STATE_TIMER_QUEUED);
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,8 @@
|
|||||||
use core::cmp::min;
|
use core::cmp::min;
|
||||||
|
|
||||||
use atomic_polyfill::Ordering;
|
|
||||||
use embassy_time::Instant;
|
use embassy_time::Instant;
|
||||||
|
|
||||||
use super::{TaskRef, STATE_TIMER_QUEUED};
|
use super::TaskRef;
|
||||||
use crate::raw::util::SyncUnsafeCell;
|
use crate::raw::util::SyncUnsafeCell;
|
||||||
|
|
||||||
pub(crate) struct TimerQueueItem {
|
pub(crate) struct TimerQueueItem {
|
||||||
@ -32,10 +31,7 @@ impl TimerQueue {
|
|||||||
pub(crate) unsafe fn update(&self, p: TaskRef) {
|
pub(crate) unsafe fn update(&self, p: TaskRef) {
|
||||||
let task = p.header();
|
let task = p.header();
|
||||||
if task.expires_at.get() != Instant::MAX {
|
if task.expires_at.get() != Instant::MAX {
|
||||||
let old_state = task.state.fetch_or(STATE_TIMER_QUEUED, Ordering::AcqRel);
|
if task.state.timer_enqueue() {
|
||||||
let is_new = old_state & STATE_TIMER_QUEUED == 0;
|
|
||||||
|
|
||||||
if is_new {
|
|
||||||
task.timer_queue_item.next.set(self.head.get());
|
task.timer_queue_item.next.set(self.head.get());
|
||||||
self.head.set(Some(p));
|
self.head.set(Some(p));
|
||||||
}
|
}
|
||||||
@ -75,7 +71,7 @@ impl TimerQueue {
|
|||||||
} else {
|
} else {
|
||||||
// Remove it
|
// Remove it
|
||||||
prev.set(task.timer_queue_item.next.get());
|
prev.set(task.timer_queue_item.next.get());
|
||||||
task.state.fetch_and(!STATE_TIMER_QUEUED, Ordering::AcqRel);
|
task.state.timer_dequeue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,9 @@ impl<T> UninitCell<T> {
|
|||||||
&mut *self.as_mut_ptr()
|
&mut *self.as_mut_ptr()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn write(&self, val: T) {
|
#[inline(never)]
|
||||||
ptr::write(self.as_mut_ptr(), val)
|
pub unsafe fn write_in_place(&self, func: impl FnOnce() -> T) {
|
||||||
|
ptr::write(self.as_mut_ptr(), func())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn drop_in_place(&self) {
|
pub unsafe fn drop_in_place(&self) {
|
||||||
|
@ -3,7 +3,7 @@ use core::task::{RawWaker, RawWakerVTable, Waker};
|
|||||||
|
|
||||||
use super::{wake_task, TaskHeader, TaskRef};
|
use super::{wake_task, TaskHeader, TaskRef};
|
||||||
|
|
||||||
const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake, drop);
|
static VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake, drop);
|
||||||
|
|
||||||
unsafe fn clone(p: *const ()) -> RawWaker {
|
unsafe fn clone(p: *const ()) -> RawWaker {
|
||||||
RawWaker::new(p, &VTABLE)
|
RawWaker::new(p, &VTABLE)
|
||||||
|
137
embassy-executor/tests/test.rs
Normal file
137
embassy-executor/tests/test.rs
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))]
|
||||||
|
|
||||||
|
use std::boxed::Box;
|
||||||
|
use std::future::poll_fn;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::task::Poll;
|
||||||
|
|
||||||
|
use embassy_executor::raw::Executor;
|
||||||
|
use embassy_executor::task;
|
||||||
|
|
||||||
|
#[export_name = "__pender"]
|
||||||
|
fn __pender(context: *mut ()) {
|
||||||
|
unsafe {
|
||||||
|
let trace = &*(context as *const Trace);
|
||||||
|
trace.push("pend");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Trace {
|
||||||
|
trace: Arc<Mutex<Vec<&'static str>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Trace {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
trace: Arc::new(Mutex::new(Vec::new())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn push(&self, value: &'static str) {
|
||||||
|
self.trace.lock().unwrap().push(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self) -> Vec<&'static str> {
|
||||||
|
self.trace.lock().unwrap().clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup() -> (&'static Executor, Trace) {
|
||||||
|
let trace = Trace::new();
|
||||||
|
let context = Box::leak(Box::new(trace.clone())) as *mut _ as *mut ();
|
||||||
|
let executor = &*Box::leak(Box::new(Executor::new(context)));
|
||||||
|
(executor, trace)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn executor_noop() {
|
||||||
|
let (executor, trace) = setup();
|
||||||
|
unsafe { executor.poll() };
|
||||||
|
assert!(trace.get().is_empty())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn executor_task() {
|
||||||
|
#[task]
|
||||||
|
async fn task1(trace: Trace) {
|
||||||
|
trace.push("poll task1")
|
||||||
|
}
|
||||||
|
|
||||||
|
let (executor, trace) = setup();
|
||||||
|
executor.spawner().spawn(task1(trace.clone())).unwrap();
|
||||||
|
|
||||||
|
unsafe { executor.poll() };
|
||||||
|
unsafe { executor.poll() };
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
trace.get(),
|
||||||
|
&[
|
||||||
|
"pend", // spawning a task pends the executor
|
||||||
|
"poll task1", // poll only once.
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn executor_task_self_wake() {
|
||||||
|
#[task]
|
||||||
|
async fn task1(trace: Trace) {
|
||||||
|
poll_fn(|cx| {
|
||||||
|
trace.push("poll task1");
|
||||||
|
cx.waker().wake_by_ref();
|
||||||
|
Poll::Pending
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
let (executor, trace) = setup();
|
||||||
|
executor.spawner().spawn(task1(trace.clone())).unwrap();
|
||||||
|
|
||||||
|
unsafe { executor.poll() };
|
||||||
|
unsafe { executor.poll() };
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
trace.get(),
|
||||||
|
&[
|
||||||
|
"pend", // spawning a task pends the executor
|
||||||
|
"poll task1", //
|
||||||
|
"pend", // task self-wakes
|
||||||
|
"poll task1", //
|
||||||
|
"pend", // task self-wakes
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn executor_task_self_wake_twice() {
|
||||||
|
#[task]
|
||||||
|
async fn task1(trace: Trace) {
|
||||||
|
poll_fn(|cx| {
|
||||||
|
trace.push("poll task1");
|
||||||
|
cx.waker().wake_by_ref();
|
||||||
|
trace.push("poll task1 wake 2");
|
||||||
|
cx.waker().wake_by_ref();
|
||||||
|
Poll::Pending
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
let (executor, trace) = setup();
|
||||||
|
executor.spawner().spawn(task1(trace.clone())).unwrap();
|
||||||
|
|
||||||
|
unsafe { executor.poll() };
|
||||||
|
unsafe { executor.poll() };
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
trace.get(),
|
||||||
|
&[
|
||||||
|
"pend", // spawning a task pends the executor
|
||||||
|
"poll task1", //
|
||||||
|
"pend", // task self-wakes
|
||||||
|
"poll task1 wake 2", // task self-wakes again, shouldn't pend
|
||||||
|
"poll task1", //
|
||||||
|
"pend", // task self-wakes
|
||||||
|
"poll task1 wake 2", // task self-wakes again, shouldn't pend
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "embassy-futures"
|
name = "embassy-futures"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "no-std, no-alloc utilities for working with futures"
|
description = "no-std, no-alloc utilities for working with futures"
|
||||||
repository = "https://github.com/embassy-rs/embassy"
|
repository = "https://github.com/embassy-rs/embassy"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# embassy-macros
|
# embassy-hal-internal
|
||||||
|
|
||||||
An [Embassy](https://embassy.dev) project.
|
An [Embassy](https://embassy.dev) project.
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ use core::sync::atomic::{compiler_fence, Ordering};
|
|||||||
|
|
||||||
use cortex_m::interrupt::InterruptNumber;
|
use cortex_m::interrupt::InterruptNumber;
|
||||||
use cortex_m::peripheral::NVIC;
|
use cortex_m::peripheral::NVIC;
|
||||||
|
use critical_section::CriticalSection;
|
||||||
|
|
||||||
/// Generate a standard `mod interrupt` for a HAL.
|
/// Generate a standard `mod interrupt` for a HAL.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
@ -91,6 +92,12 @@ macro_rules! interrupt_mod {
|
|||||||
fn set_priority(prio: crate::interrupt::Priority) {
|
fn set_priority(prio: crate::interrupt::Priority) {
|
||||||
Self::IRQ.set_priority(prio)
|
Self::IRQ.set_priority(prio)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the interrupt priority with an already-acquired critical section
|
||||||
|
#[inline]
|
||||||
|
fn set_priority_with_cs(cs: critical_section::CriticalSection, prio: crate::interrupt::Priority) {
|
||||||
|
Self::IRQ.set_priority_with_cs(cs, prio)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$(
|
$(
|
||||||
@ -195,10 +202,29 @@ pub unsafe trait InterruptExt: InterruptNumber + Copy {
|
|||||||
/// Set the interrupt priority.
|
/// Set the interrupt priority.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_priority(self, prio: Priority) {
|
fn set_priority(self, prio: Priority) {
|
||||||
critical_section::with(|_| unsafe {
|
unsafe {
|
||||||
let mut nvic: cortex_m::peripheral::NVIC = mem::transmute(());
|
let mut nvic: cortex_m::peripheral::NVIC = mem::transmute(());
|
||||||
nvic.set_priority(self, prio.into())
|
|
||||||
})
|
// On thumbv6, set_priority must do a RMW to change 8bit in a 32bit reg.
|
||||||
|
#[cfg(armv6m)]
|
||||||
|
critical_section::with(|_| nvic.set_priority(self, prio.into()));
|
||||||
|
// On thumbv7+, set_priority does an atomic 8bit write, so no CS needed.
|
||||||
|
#[cfg(not(armv6m))]
|
||||||
|
nvic.set_priority(self, prio.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the interrupt priority with an already-acquired critical section
|
||||||
|
///
|
||||||
|
/// Equivalent to `set_priority`, except you pass a `CriticalSection` to prove
|
||||||
|
/// you've already acquired a critical section. This prevents acquiring another
|
||||||
|
/// one, which saves code size.
|
||||||
|
#[inline]
|
||||||
|
fn set_priority_with_cs(self, _cs: CriticalSection, prio: Priority) {
|
||||||
|
unsafe {
|
||||||
|
let mut nvic: cortex_m::peripheral::NVIC = mem::transmute(());
|
||||||
|
nvic.set_priority(self, prio.into());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@ pub mod drop;
|
|||||||
mod macros;
|
mod macros;
|
||||||
mod peripheral;
|
mod peripheral;
|
||||||
pub mod ratio;
|
pub mod ratio;
|
||||||
pub mod ring_buffer;
|
|
||||||
pub use peripheral::{Peripheral, PeripheralRef};
|
pub use peripheral::{Peripheral, PeripheralRef};
|
||||||
|
|
||||||
#[cfg(feature = "cortex-m")]
|
#[cfg(feature = "cortex-m")]
|
||||||
|
@ -48,17 +48,23 @@ macro_rules! peripherals_struct {
|
|||||||
///Returns all the peripherals *once*
|
///Returns all the peripherals *once*
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn take() -> Self {
|
pub(crate) fn take() -> Self {
|
||||||
|
critical_section::with(Self::take_with_cs)
|
||||||
|
}
|
||||||
|
|
||||||
|
///Returns all the peripherals *once*
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn take_with_cs(_cs: critical_section::CriticalSection) -> Self {
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
static mut _EMBASSY_DEVICE_PERIPHERALS: bool = false;
|
static mut _EMBASSY_DEVICE_PERIPHERALS: bool = false;
|
||||||
|
|
||||||
critical_section::with(|_| unsafe {
|
// safety: OK because we're inside a CS.
|
||||||
|
unsafe {
|
||||||
if _EMBASSY_DEVICE_PERIPHERALS {
|
if _EMBASSY_DEVICE_PERIPHERALS {
|
||||||
panic!("init called more than once!")
|
panic!("init called more than once!")
|
||||||
}
|
}
|
||||||
_EMBASSY_DEVICE_PERIPHERALS = true;
|
_EMBASSY_DEVICE_PERIPHERALS = true;
|
||||||
Self::steal()
|
Self::steal()
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,136 +0,0 @@
|
|||||||
pub struct RingBuffer<'a> {
|
|
||||||
buf: &'a mut [u8],
|
|
||||||
start: usize,
|
|
||||||
end: usize,
|
|
||||||
empty: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RingBuffer<'a> {
|
|
||||||
pub fn new(buf: &'a mut [u8]) -> Self {
|
|
||||||
Self {
|
|
||||||
buf,
|
|
||||||
start: 0,
|
|
||||||
end: 0,
|
|
||||||
empty: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push_buf(&mut self) -> &mut [u8] {
|
|
||||||
if self.start == self.end && !self.empty {
|
|
||||||
trace!(" ringbuf: push_buf empty");
|
|
||||||
return &mut self.buf[..0];
|
|
||||||
}
|
|
||||||
|
|
||||||
let n = if self.start <= self.end {
|
|
||||||
self.buf.len() - self.end
|
|
||||||
} else {
|
|
||||||
self.start - self.end
|
|
||||||
};
|
|
||||||
|
|
||||||
trace!(" ringbuf: push_buf {:?}..{:?}", self.end, self.end + n);
|
|
||||||
&mut self.buf[self.end..self.end + n]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push(&mut self, n: usize) {
|
|
||||||
trace!(" ringbuf: push {:?}", n);
|
|
||||||
if n == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.end = self.wrap(self.end + n);
|
|
||||||
self.empty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pop_buf(&mut self) -> &mut [u8] {
|
|
||||||
if self.empty {
|
|
||||||
trace!(" ringbuf: pop_buf empty");
|
|
||||||
return &mut self.buf[..0];
|
|
||||||
}
|
|
||||||
|
|
||||||
let n = if self.end <= self.start {
|
|
||||||
self.buf.len() - self.start
|
|
||||||
} else {
|
|
||||||
self.end - self.start
|
|
||||||
};
|
|
||||||
|
|
||||||
trace!(" ringbuf: pop_buf {:?}..{:?}", self.start, self.start + n);
|
|
||||||
&mut self.buf[self.start..self.start + n]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pop(&mut self, n: usize) {
|
|
||||||
trace!(" ringbuf: pop {:?}", n);
|
|
||||||
if n == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.start = self.wrap(self.start + n);
|
|
||||||
self.empty = self.start == self.end;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_full(&self) -> bool {
|
|
||||||
self.start == self.end && !self.empty
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.empty
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
self.start = 0;
|
|
||||||
self.end = 0;
|
|
||||||
self.empty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wrap(&self, n: usize) -> usize {
|
|
||||||
assert!(n <= self.buf.len());
|
|
||||||
if n == self.buf.len() {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn push_pop() {
|
|
||||||
let mut b = [0; 4];
|
|
||||||
let mut rb = RingBuffer::new(&mut b);
|
|
||||||
let buf = rb.push_buf();
|
|
||||||
assert_eq!(4, buf.len());
|
|
||||||
buf[0] = 1;
|
|
||||||
buf[1] = 2;
|
|
||||||
buf[2] = 3;
|
|
||||||
buf[3] = 4;
|
|
||||||
rb.push(4);
|
|
||||||
|
|
||||||
let buf = rb.pop_buf();
|
|
||||||
assert_eq!(4, buf.len());
|
|
||||||
assert_eq!(1, buf[0]);
|
|
||||||
rb.pop(1);
|
|
||||||
|
|
||||||
let buf = rb.pop_buf();
|
|
||||||
assert_eq!(3, buf.len());
|
|
||||||
assert_eq!(2, buf[0]);
|
|
||||||
rb.pop(1);
|
|
||||||
|
|
||||||
let buf = rb.pop_buf();
|
|
||||||
assert_eq!(2, buf.len());
|
|
||||||
assert_eq!(3, buf[0]);
|
|
||||||
rb.pop(1);
|
|
||||||
|
|
||||||
let buf = rb.pop_buf();
|
|
||||||
assert_eq!(1, buf.len());
|
|
||||||
assert_eq!(4, buf[0]);
|
|
||||||
rb.pop(1);
|
|
||||||
|
|
||||||
let buf = rb.pop_buf();
|
|
||||||
assert_eq!(0, buf.len());
|
|
||||||
|
|
||||||
let buf = rb.push_buf();
|
|
||||||
assert_eq!(4, buf.len());
|
|
||||||
}
|
|
||||||
}
|
|
@ -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.3", path = "../embassy-time", optional = true }
|
embassy-time = { version = "0.1.5", path = "../embassy-time", optional = true }
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.4.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-async = { version = "=1.0.0-rc.1" }
|
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
||||||
embedded-hal = { version = "0.2", features = ["unproven"] }
|
embedded-hal = { version = "0.2", features = ["unproven"] }
|
||||||
|
|
||||||
futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
|
futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
|
||||||
lora-phy = { version = "1" }
|
lora-phy = { version = "2" }
|
||||||
lorawan-device = { version = "0.10.0", default-features = false, features = ["async"], optional = true }
|
lorawan-device = { version = "0.11.0", default-features = false, features = ["async"], optional = true }
|
||||||
|
|
||||||
[patch.crates-io]
|
|
||||||
lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "1323eccc1c470d4259f95f4f315d1be830d572a3"}
|
|
||||||
|
@ -67,24 +67,20 @@ where
|
|||||||
self.board_type = board_type;
|
self.board_type = board_type;
|
||||||
}
|
}
|
||||||
async fn set_nss_low(&mut self) -> Result<(), RadioError> {
|
async fn set_nss_low(&mut self) -> Result<(), RadioError> {
|
||||||
let pwr = pac::PWR;
|
pac::PWR.subghzspicr().modify(|w| w.set_nss(false));
|
||||||
pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::LOW));
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
async fn set_nss_high(&mut self) -> Result<(), RadioError> {
|
async fn set_nss_high(&mut self) -> Result<(), RadioError> {
|
||||||
let pwr = pac::PWR;
|
pac::PWR.subghzspicr().modify(|w| w.set_nss(true));
|
||||||
pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::HIGH));
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
async fn reset(&mut self, _delay: &mut impl DelayUs) -> Result<(), RadioError> {
|
async fn reset(&mut self, _delay: &mut impl DelayUs) -> Result<(), RadioError> {
|
||||||
let rcc = pac::RCC;
|
pac::RCC.csr().modify(|w| w.set_rfrst(true));
|
||||||
rcc.csr().modify(|w| w.set_rfrst(true));
|
pac::RCC.csr().modify(|w| w.set_rfrst(false));
|
||||||
rcc.csr().modify(|w| w.set_rfrst(false));
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
async fn wait_on_busy(&mut self) -> Result<(), RadioError> {
|
async fn wait_on_busy(&mut self) -> Result<(), RadioError> {
|
||||||
let pwr = pac::PWR;
|
while pac::PWR.sr2().read().rfbusys() {}
|
||||||
while pwr.sr2().read().rfbusys() == pac::pwr::vals::Rfbusys::BUSY {}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,11 +280,7 @@ where
|
|||||||
self.busy.wait_for_low().await.map_err(|_| Busy)
|
self.busy.wait_for_low().await.map_err(|_| Busy)
|
||||||
}
|
}
|
||||||
async fn await_irq(&mut self) -> Result<(), RadioError> {
|
async fn await_irq(&mut self) -> Result<(), RadioError> {
|
||||||
if self.board_type != BoardType::RpPicoWaveshareSx1262 {
|
self.dio1.wait_for_high().await.map_err(|_| DIO1)?;
|
||||||
self.dio1.wait_for_high().await.map_err(|_| DIO1)?;
|
|
||||||
} else {
|
|
||||||
self.dio1.wait_for_rising_edge().await.map_err(|_| DIO1)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![feature(async_fn_in_trait, impl_trait_projections)]
|
#![feature(async_fn_in_trait, impl_trait_projections)]
|
||||||
|
#![allow(stable_features, unknown_lints, async_fn_in_trait)]
|
||||||
//! embassy-lora holds LoRa-specific functionality.
|
//! embassy-lora holds LoRa-specific functionality.
|
||||||
|
|
||||||
pub(crate) mod fmt;
|
pub(crate) mod fmt;
|
||||||
@ -34,6 +35,6 @@ impl lorawan_device::async_device::radio::Timer for LoraTimer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn delay_ms(&mut self, millis: u64) {
|
async fn delay_ms(&mut self, millis: u64) {
|
||||||
Timer::after(Duration::from_millis(millis)).await
|
Timer::after_millis(millis).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,5 +21,4 @@ proc-macro2 = "1.0.29"
|
|||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# Enabling this cause interrupt::take! to require embassy-executor
|
nightly = []
|
||||||
rtos-trace-interrupt = []
|
|
@ -53,8 +53,7 @@ pub fn wasm() -> TokenStream {
|
|||||||
quote! {
|
quote! {
|
||||||
#[wasm_bindgen::prelude::wasm_bindgen(start)]
|
#[wasm_bindgen::prelude::wasm_bindgen(start)]
|
||||||
pub fn main() -> Result<(), wasm_bindgen::JsValue> {
|
pub fn main() -> Result<(), wasm_bindgen::JsValue> {
|
||||||
static EXECUTOR: ::embassy_executor::_export::StaticCell<::embassy_executor::Executor> = ::embassy_executor::_export::StaticCell::new();
|
let executor = ::std::boxed::Box::leak(::std::boxed::Box::new(::embassy_executor::Executor::new()));
|
||||||
let executor = EXECUTOR.init(::embassy_executor::Executor::new());
|
|
||||||
|
|
||||||
executor.start(|spawner| {
|
executor.start(|spawner| {
|
||||||
spawner.spawn(__embassy_main(spawner)).unwrap();
|
spawner.spawn(__embassy_main(spawner)).unwrap();
|
||||||
|
@ -79,6 +79,7 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result<TokenStream, TokenStre
|
|||||||
task_inner.vis = syn::Visibility::Inherited;
|
task_inner.vis = syn::Visibility::Inherited;
|
||||||
task_inner.sig.ident = task_inner_ident.clone();
|
task_inner.sig.ident = task_inner_ident.clone();
|
||||||
|
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
let mut task_outer: ItemFn = parse_quote! {
|
let mut task_outer: ItemFn = parse_quote! {
|
||||||
#visibility fn #task_ident(#fargs) -> ::embassy_executor::SpawnToken<impl Sized> {
|
#visibility fn #task_ident(#fargs) -> ::embassy_executor::SpawnToken<impl Sized> {
|
||||||
type Fut = impl ::core::future::Future + 'static;
|
type Fut = impl ::core::future::Future + 'static;
|
||||||
@ -87,6 +88,14 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result<TokenStream, TokenStre
|
|||||||
unsafe { POOL._spawn_async_fn(move || #task_inner_ident(#(#arg_names,)*)) }
|
unsafe { POOL._spawn_async_fn(move || #task_inner_ident(#(#arg_names,)*)) }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
#[cfg(not(feature = "nightly"))]
|
||||||
|
let mut task_outer: ItemFn = parse_quote! {
|
||||||
|
#visibility fn #task_ident(#fargs) -> ::embassy_executor::SpawnToken<impl Sized> {
|
||||||
|
const POOL_SIZE: usize = #pool_size;
|
||||||
|
static POOL: ::embassy_executor::_export::TaskPoolRef = ::embassy_executor::_export::TaskPoolRef::new();
|
||||||
|
unsafe { POOL.get::<_, POOL_SIZE>()._spawn_async_fn(move || #task_inner_ident(#(#arg_names,)*)) }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
task_outer.attrs.append(&mut task_inner.attrs.clone());
|
task_outer.attrs.append(&mut task_inner.attrs.clone());
|
||||||
|
|
||||||
|
@ -10,21 +10,19 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
heapless = "0.7.16"
|
heapless = "0.8"
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
log = { version = "0.4", default-features = false, optional = true }
|
log = { version = "0.4", default-features = false, optional = true }
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" }
|
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" }
|
||||||
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
||||||
embedded-hal-bus = { version = "=0.1.0-rc.1", features = ["async"] }
|
embedded-hal-bus = { version = "=0.1.0-rc.1", features = ["async"] }
|
||||||
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" }
|
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel" }
|
||||||
embassy-time = { version = "0.1.3" }
|
embassy-time = { version = "0.1.5", path = "../embassy-time" }
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||||
bitfield = "0.14.0"
|
bitfield = "0.14.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
# reenable when https://github.com/dbrgn/embedded-hal-mock/pull/86 is merged.
|
embedded-hal-mock = { version = "=0.10.0-rc.1", features = ["embedded-hal-async", "eh1"] }
|
||||||
#embedded-hal-mock = { git = "https://github.com/dbrgn/embedded-hal-mock", branch = "1-alpha", features = ["embedded-hal-async", "eh1"] }] }
|
|
||||||
embedded-hal-mock = { git = "https://github.com/newAM/embedded-hal-mock", branch = "eh1-rc.1", features = ["embedded-hal-async", "eh1"] }
|
|
||||||
crc = "3.0.1"
|
crc = "3.0.1"
|
||||||
env_logger = "0.10"
|
env_logger = "0.10"
|
||||||
critical-section = { version = "1.1.2", features = ["std"] }
|
critical-section = { version = "1.1.2", features = ["std"] }
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#![deny(clippy::pedantic)]
|
#![deny(clippy::pedantic)]
|
||||||
#![feature(async_fn_in_trait)]
|
#![feature(async_fn_in_trait)]
|
||||||
|
#![allow(stable_features, unknown_lints, async_fn_in_trait)]
|
||||||
#![cfg_attr(not(any(test, feature = "std")), no_std)]
|
#![cfg_attr(not(any(test, feature = "std")), no_std)]
|
||||||
#![allow(clippy::module_name_repetitions)]
|
#![allow(clippy::module_name_repetitions)]
|
||||||
#![allow(clippy::missing_errors_doc)]
|
#![allow(clippy::missing_errors_doc)]
|
||||||
@ -20,7 +21,7 @@ pub use crc32::ETH_FCS;
|
|||||||
use crc8::crc8;
|
use crc8::crc8;
|
||||||
use embassy_futures::select::{select, Either};
|
use embassy_futures::select::{select, Either};
|
||||||
use embassy_net_driver_channel as ch;
|
use embassy_net_driver_channel as ch;
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::Timer;
|
||||||
use embedded_hal_1::digital::OutputPin;
|
use embedded_hal_1::digital::OutputPin;
|
||||||
use embedded_hal_async::digital::Wait;
|
use embedded_hal_async::digital::Wait;
|
||||||
use embedded_hal_async::spi::{Error, Operation, SpiDevice};
|
use embedded_hal_async::spi::{Error, Operation, SpiDevice};
|
||||||
@ -609,12 +610,12 @@ pub async fn new<const N_RX: usize, const N_TX: usize, SPI: SpiDevice, INT: Wait
|
|||||||
reset.set_low().unwrap();
|
reset.set_low().unwrap();
|
||||||
|
|
||||||
// Wait t1: 20-43mS
|
// Wait t1: 20-43mS
|
||||||
Timer::after(Duration::from_millis(30)).await;
|
Timer::after_millis(30).await;
|
||||||
|
|
||||||
reset.set_high().unwrap();
|
reset.set_high().unwrap();
|
||||||
|
|
||||||
// Wait t3: 50mS
|
// Wait t3: 50mS
|
||||||
Timer::after(Duration::from_millis(50)).await;
|
Timer::after_millis(50).await;
|
||||||
|
|
||||||
// Create device
|
// Create device
|
||||||
let mut mac = ADIN1110::new(spi_dev, spi_crc, append_fcs_on_tx);
|
let mut mac = ADIN1110::new(spi_dev, spi_crc, append_fcs_on_tx);
|
||||||
|
16
embassy-net-driver-channel/CHANGELOG.md
Normal file
16
embassy-net-driver-channel/CHANGELOG.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
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/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## 0.2.0 - 2023-10-18
|
||||||
|
|
||||||
|
- Update `embassy-net-driver` to v0.2
|
||||||
|
- `Runner::new` now takes an `embassy_net_driver::HardwareAddress` parameter.
|
||||||
|
- `Runner::set_ethernet_address` is now `set_hardware_address`.
|
||||||
|
|
||||||
|
## 0.1.0 - 2023-06-29
|
||||||
|
|
||||||
|
- First release
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "embassy-net-driver-channel"
|
name = "embassy-net-driver-channel"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
description = "High-level channel-based driver for the `embassy-net` async TCP/IP network stack."
|
description = "High-level channel-based driver for the `embassy-net` async TCP/IP network stack."
|
||||||
@ -24,6 +24,6 @@ features = ["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-sync = { version = "0.2.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.4.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 = { version = "0.1.0", path = "../embassy-net-driver" }
|
embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" }
|
||||||
|
@ -7,7 +7,9 @@ The `embassy-net-driver` trait is polling-based. To implement it, you must write
|
|||||||
hand, and hook up the `Waker`s provided by `embassy-net` to the right interrupt handlers so that `embassy-net`
|
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.
|
knows when to poll your driver again to make more progress.
|
||||||
|
|
||||||
With `embassy-net-driver-channel`
|
With `embassy-net-driver-channel` you get a "channel-like" interface instead, where you can send/receive packets
|
||||||
|
to/from embassy-net. The intended usage is to spawn a "driver task" in the background that does this, passing
|
||||||
|
packets between the hardware and the channel.
|
||||||
|
|
||||||
## A note about deadlocks
|
## A note about deadlocks
|
||||||
|
|
||||||
@ -18,19 +20,19 @@ loop {
|
|||||||
// Wait for either..
|
// Wait for either..
|
||||||
match select(
|
match select(
|
||||||
// ... the chip signaling an interrupt, indicating a packet is available to receive, or
|
// ... the chip signaling an interrupt, indicating a packet is available to receive, or
|
||||||
irq_pin.wait_for_low(),
|
irq_pin.wait_for_low(),
|
||||||
// ... a TX buffer becoming available, i.e. embassy-net wants to send a packet
|
// ... a TX buffer becoming available, i.e. embassy-net wants to send a packet
|
||||||
tx_chan.tx_buf(),
|
tx_chan.tx_buf(),
|
||||||
).await {
|
).await {
|
||||||
Either::First(_) => {
|
Either::First(_) => {
|
||||||
// a packet is ready to be received!
|
// a packet is ready to be received!
|
||||||
let buf = rx_chan.rx_buf().await; // allocate a rx buf from the packet queue
|
let buf = rx_chan.rx_buf().await; // allocate a rx buf from the packet queue
|
||||||
let n = receive_packet_over_spi(buf).await;
|
let n = receive_packet_over_spi(buf).await;
|
||||||
rx_chan.rx_done(n);
|
rx_chan.rx_done(n);
|
||||||
}
|
}
|
||||||
Either::Second(buf) => {
|
Either::Second(buf) => {
|
||||||
// a packet is ready to be sent!
|
// a packet is ready to be sent!
|
||||||
send_packet_over_spi(buf).await;
|
send_packet_over_spi(buf).await;
|
||||||
tx_chan.tx_done();
|
tx_chan.tx_done();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -41,7 +43,7 @@ However, this code has a latent deadlock bug. The symptom is it can hang at `rx_
|
|||||||
|
|
||||||
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 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":
|
The fix is to make sure to always service the TX queue while you're waiting for space to become available in the RX queue. For example, select on either "tx_chan.tx_buf() available" or "INT is low AND rx_chan.rx_buf() available":
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
loop {
|
loop {
|
||||||
@ -58,12 +60,12 @@ loop {
|
|||||||
).await {
|
).await {
|
||||||
Either::First(buf) => {
|
Either::First(buf) => {
|
||||||
// a packet is ready to be received!
|
// a packet is ready to be received!
|
||||||
let n = receive_packet_over_spi(buf).await;
|
let n = receive_packet_over_spi(buf).await;
|
||||||
rx_chan.rx_done(n);
|
rx_chan.rx_done(n);
|
||||||
}
|
}
|
||||||
Either::Second(buf) => {
|
Either::Second(buf) => {
|
||||||
// a packet is ready to be sent!
|
// a packet is ready to be sent!
|
||||||
send_packet_over_spi(buf).await;
|
send_packet_over_spi(buf).await;
|
||||||
tx_chan.tx_done();
|
tx_chan.tx_done();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,12 +81,10 @@ These `embassy-net` drivers are implemented using this crate. You can look at th
|
|||||||
- [`embassy-net-wiznet`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-wiznet) for Wiznet SPI Ethernet MAC+PHY chips.
|
- [`embassy-net-wiznet`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-wiznet) for Wiznet SPI Ethernet MAC+PHY chips.
|
||||||
- [`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.
|
- [`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
|
## Interoperability
|
||||||
|
|
||||||
This crate can run on any executor.
|
This crate can run on any executor.
|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This work is licensed under either of
|
This work is licensed under either of
|
||||||
|
@ -8,9 +8,8 @@ use core::cell::RefCell;
|
|||||||
use core::mem::MaybeUninit;
|
use core::mem::MaybeUninit;
|
||||||
use core::task::{Context, Poll};
|
use core::task::{Context, Poll};
|
||||||
|
|
||||||
use driver::HardwareAddress;
|
|
||||||
pub use embassy_net_driver as driver;
|
pub use embassy_net_driver as driver;
|
||||||
use embassy_net_driver::{Capabilities, LinkState, Medium};
|
use embassy_net_driver::{Capabilities, LinkState};
|
||||||
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
||||||
use embassy_sync::blocking_mutex::Mutex;
|
use embassy_sync::blocking_mutex::Mutex;
|
||||||
use embassy_sync::waitqueue::WakerRegistration;
|
use embassy_sync::waitqueue::WakerRegistration;
|
||||||
@ -161,18 +160,10 @@ impl<'d> StateRunner<'d> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_ethernet_address(&self, address: [u8; 6]) {
|
pub fn set_hardware_address(&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.hardware_address = driver::HardwareAddress::Ethernet(address);
|
s.hardware_address = 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();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -232,11 +223,6 @@ pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>(
|
|||||||
) -> (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;
|
||||||
caps.medium = match &hardware_address {
|
|
||||||
HardwareAddress::Ethernet(_) => Medium::Ethernet,
|
|
||||||
HardwareAddress::Ieee802154(_) => Medium::Ieee802154,
|
|
||||||
HardwareAddress::Ip => Medium::Ip,
|
|
||||||
};
|
|
||||||
|
|
||||||
// safety: this is a self-referential struct, however:
|
// safety: this is a self-referential struct, however:
|
||||||
// - it can't move while the `'d` borrow is active.
|
// - it can't move while the `'d` borrow is active.
|
||||||
|
17
embassy-net-driver/CHANGELOG.md
Normal file
17
embassy-net-driver/CHANGELOG.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
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/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## 0.2.0 - 2023-10-18
|
||||||
|
|
||||||
|
- Added support for IEEE 802.15.4 mediums.
|
||||||
|
- Added `Driver::hardware_address()`, `HardwareAddress`.
|
||||||
|
- Removed `Medium` enum. The medium is deduced out of the hardware address.
|
||||||
|
- Removed `Driver::ethernet_address()`. Replacement is `hardware_address()`.
|
||||||
|
|
||||||
|
## 0.1.0 - 2023-06-29
|
||||||
|
|
||||||
|
- First release
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "embassy-net-driver"
|
name = "embassy-net-driver"
|
||||||
version = "0.1.0"
|
version = "0.2.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."
|
description = "Driver trait for the `embassy-net` async TCP/IP network stack."
|
||||||
|
@ -7,12 +7,23 @@ use core::task::Context;
|
|||||||
/// Representation of an hardware address, such as an Ethernet address or an IEEE802.15.4 address.
|
/// Representation of an hardware address, such as an Ethernet address or an IEEE802.15.4 address.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum HardwareAddress {
|
pub enum HardwareAddress {
|
||||||
/// A six-octet Ethernet address
|
/// Ethernet medium, with a A six-octet Ethernet address.
|
||||||
|
///
|
||||||
|
/// Devices of this type send and receive Ethernet frames,
|
||||||
|
/// and interfaces using it must do neighbor discovery via ARP or NDISC.
|
||||||
|
///
|
||||||
|
/// Examples of devices of this type are Ethernet, WiFi (802.11), Linux `tap`, and VPNs in tap (layer 2) mode.
|
||||||
Ethernet([u8; 6]),
|
Ethernet([u8; 6]),
|
||||||
/// An eight-octet IEEE802.15.4 address
|
/// 6LoWPAN over IEEE802.15.4, with an eight-octet address.
|
||||||
Ieee802154([u8; 8]),
|
Ieee802154([u8; 8]),
|
||||||
/// Indicates that a Driver is IP-native, and has no hardware address
|
/// Indicates that a Driver is IP-native, and has no hardware address.
|
||||||
|
///
|
||||||
|
/// Devices of this type send and receive IP frames, without an
|
||||||
|
/// Ethernet header. MAC addresses are not used, and no neighbor discovery (ARP, NDISC) is done.
|
||||||
|
///
|
||||||
|
/// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode.
|
||||||
Ip,
|
Ip,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,6 +75,10 @@ pub trait Driver {
|
|||||||
fn capabilities(&self) -> Capabilities;
|
fn capabilities(&self) -> Capabilities;
|
||||||
|
|
||||||
/// Get the device's hardware address.
|
/// Get the device's hardware address.
|
||||||
|
///
|
||||||
|
/// The returned hardware address also determines the "medium" of this driver. This indicates
|
||||||
|
/// what kind of packet the sent/received bytes are, and determines some behaviors of
|
||||||
|
/// the interface. For example, ARP/NDISC address resolution is only done for Ethernet mediums.
|
||||||
fn hardware_address(&self) -> HardwareAddress;
|
fn hardware_address(&self) -> HardwareAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,13 +139,6 @@ pub trait TxToken {
|
|||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct Capabilities {
|
pub struct Capabilities {
|
||||||
/// Medium of the device.
|
|
||||||
///
|
|
||||||
/// This indicates what kind of packet the sent/received bytes are, and determines
|
|
||||||
/// some behaviors of Interface. For example, ARP/NDISC address resolution is only done
|
|
||||||
/// for Ethernet mediums.
|
|
||||||
pub medium: Medium,
|
|
||||||
|
|
||||||
/// Maximum transmission unit.
|
/// Maximum transmission unit.
|
||||||
///
|
///
|
||||||
/// The network device is unable to send or receive frames larger than the value returned
|
/// The network device is unable to send or receive frames larger than the value returned
|
||||||
@ -161,32 +169,6 @@ pub struct Capabilities {
|
|||||||
pub checksum: ChecksumCapabilities,
|
pub checksum: ChecksumCapabilities,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type of medium of a device.
|
|
||||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
||||||
pub enum Medium {
|
|
||||||
/// Ethernet medium. Devices of this type send and receive Ethernet frames,
|
|
||||||
/// and interfaces using it must do neighbor discovery via ARP or NDISC.
|
|
||||||
///
|
|
||||||
/// Examples of devices of this type are Ethernet, WiFi (802.11), Linux `tap`, and VPNs in tap (layer 2) mode.
|
|
||||||
Ethernet,
|
|
||||||
|
|
||||||
/// IP medium. Devices of this type send and receive IP frames, without an
|
|
||||||
/// Ethernet header. MAC addresses are not used, and no neighbor discovery (ARP, NDISC) is done.
|
|
||||||
///
|
|
||||||
/// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode.
|
|
||||||
Ip,
|
|
||||||
|
|
||||||
/// IEEE 802_15_4 medium
|
|
||||||
Ieee802154,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Medium {
|
|
||||||
fn default() -> Medium {
|
|
||||||
Medium::Ethernet
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A description of checksum behavior for every supported protocol.
|
/// A description of checksum behavior for every supported protocol.
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
@ -10,8 +10,8 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
embedded-hal = { version = "1.0.0-rc.1" }
|
embedded-hal = { version = "1.0.0-rc.1" }
|
||||||
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
||||||
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
|
embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" }
|
||||||
embassy-time = { version = "0.1.3", path = "../embassy-time" }
|
embassy-time = { version = "0.1.5", path = "../embassy-time" }
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||||
|
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
|
@ -19,7 +19,7 @@ mod traits;
|
|||||||
use core::cmp;
|
use core::cmp;
|
||||||
use core::convert::TryInto;
|
use core::convert::TryInto;
|
||||||
|
|
||||||
use embassy_net_driver::{Capabilities, HardwareAddress, LinkState, Medium};
|
use embassy_net_driver::{Capabilities, HardwareAddress, LinkState};
|
||||||
use embassy_time::Duration;
|
use embassy_time::Duration;
|
||||||
use embedded_hal::digital::OutputPin;
|
use embedded_hal::digital::OutputPin;
|
||||||
use embedded_hal::spi::{Operation, SpiDevice};
|
use embedded_hal::spi::{Operation, SpiDevice};
|
||||||
@ -194,10 +194,15 @@ where
|
|||||||
self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxen());
|
self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxen());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the device's MAC address
|
||||||
|
pub fn address(&self) -> [u8; 6] {
|
||||||
|
self.mac_addr
|
||||||
|
}
|
||||||
|
|
||||||
/// Flushes the transmit buffer, ensuring all pending transmissions have completed
|
/// Flushes the transmit buffer, ensuring all pending transmissions have completed
|
||||||
/// NOTE: The returned packet *must* be `read` or `ignore`-d, otherwise this method will always
|
/// NOTE: The returned packet *must* be `read` or `ignore`-d, otherwise this method will always
|
||||||
/// return `None` on subsequent invocations
|
/// return `None` on subsequent invocations
|
||||||
pub fn receive<'a>(&mut self, buf: &'a mut [u8]) -> Option<&'a mut [u8]> {
|
pub fn receive(&mut self, buf: &mut [u8]) -> Option<usize> {
|
||||||
if self.pending_packets() == 0 {
|
if self.pending_packets() == 0 {
|
||||||
// Errata #6: we can't rely on PKTIF so we check PKTCNT
|
// Errata #6: we can't rely on PKTIF so we check PKTCNT
|
||||||
return None;
|
return None;
|
||||||
@ -241,7 +246,7 @@ where
|
|||||||
|
|
||||||
self.next_packet = next_packet;
|
self.next_packet = next_packet;
|
||||||
|
|
||||||
Some(&mut buf[..len as usize])
|
Some(len as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wait_tx_ready(&mut self) {
|
fn wait_tx_ready(&mut self) {
|
||||||
@ -642,9 +647,8 @@ where
|
|||||||
fn receive(&mut self, cx: &mut core::task::Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
|
fn receive(&mut self, cx: &mut core::task::Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
|
||||||
let rx_buf = unsafe { &mut RX_BUF };
|
let rx_buf = unsafe { &mut RX_BUF };
|
||||||
let tx_buf = unsafe { &mut TX_BUF };
|
let tx_buf = unsafe { &mut TX_BUF };
|
||||||
if let Some(pkt) = self.receive(rx_buf) {
|
if let Some(n) = self.receive(rx_buf) {
|
||||||
let n = pkt.len();
|
Some((RxToken { buf: &mut rx_buf[..n] }, TxToken { buf: tx_buf, eth: self }))
|
||||||
Some((RxToken { buf: &mut pkt[..n] }, TxToken { buf: tx_buf, eth: self }))
|
|
||||||
} else {
|
} else {
|
||||||
cx.waker().wake_by_ref();
|
cx.waker().wake_by_ref();
|
||||||
None
|
None
|
||||||
@ -667,7 +671,6 @@ where
|
|||||||
fn capabilities(&self) -> Capabilities {
|
fn capabilities(&self) -> Capabilities {
|
||||||
let mut caps = Capabilities::default();
|
let mut caps = Capabilities::default();
|
||||||
caps.max_transmission_unit = MTU;
|
caps.max_transmission_unit = MTU;
|
||||||
caps.medium = Medium::Ethernet;
|
|
||||||
caps
|
caps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,17 +7,17 @@ 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.3", path = "../embassy-time" }
|
embassy-time = { version = "0.1.5", path = "../embassy-time" }
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync"}
|
embassy-sync = { version = "0.4.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.2.0", path = "../embassy-net-driver-channel"}
|
||||||
|
|
||||||
embedded-hal = { version = "1.0.0-rc.1" }
|
embedded-hal = { version = "1.0.0-rc.1" }
|
||||||
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
||||||
|
|
||||||
noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] }
|
noproto = { git="https://github.com/embassy-rs/noproto", rev = "f5e6d1f325b6ad4e344f60452b09576e24671f62", 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.8"
|
||||||
|
|
||||||
[package.metadata.embassy_docs]
|
[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 = "https://github.com/embassy-rs/embassy/blob/embassy-net-esp-hosted-v$VERSION/embassy-net-esp-hosted/src/"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use ch::driver::LinkState;
|
|
||||||
use embassy_net_driver_channel as ch;
|
use embassy_net_driver_channel as ch;
|
||||||
|
use embassy_net_driver_channel::driver::{HardwareAddress, LinkState};
|
||||||
use heapless::String;
|
use heapless::String;
|
||||||
|
|
||||||
use crate::ioctl::Shared;
|
use crate::ioctl::Shared;
|
||||||
@ -77,7 +77,7 @@ impl<'a> Control<'a> {
|
|||||||
|
|
||||||
let mac_addr = self.get_mac_addr().await?;
|
let mac_addr = self.get_mac_addr().await?;
|
||||||
debug!("mac addr: {:02x}", mac_addr);
|
debug!("mac addr: {:02x}", mac_addr);
|
||||||
self.state_ch.set_ethernet_address(mac_addr);
|
self.state_ch.set_hardware_address(HardwareAddress::Ethernet(mac_addr));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -97,8 +97,8 @@ impl<'a> Control<'a> {
|
|||||||
|
|
||||||
pub async fn connect(&mut self, ssid: &str, password: &str) -> Result<(), Error> {
|
pub async fn connect(&mut self, ssid: &str, password: &str) -> Result<(), Error> {
|
||||||
let req = proto::CtrlMsgReqConnectAp {
|
let req = proto::CtrlMsgReqConnectAp {
|
||||||
ssid: String::from(ssid),
|
ssid: unwrap!(String::try_from(ssid)),
|
||||||
pwd: String::from(password),
|
pwd: unwrap!(String::try_from(password)),
|
||||||
bssid: String::new(),
|
bssid: String::new(),
|
||||||
listen_interval: 3,
|
listen_interval: 3,
|
||||||
is_wpa3_supported: false,
|
is_wpa3_supported: false,
|
||||||
|
@ -169,9 +169,9 @@ where
|
|||||||
pub async fn run(mut self) -> ! {
|
pub async fn run(mut self) -> ! {
|
||||||
debug!("resetting...");
|
debug!("resetting...");
|
||||||
self.reset.set_low().unwrap();
|
self.reset.set_low().unwrap();
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
self.reset.set_high().unwrap();
|
self.reset.set_high().unwrap();
|
||||||
Timer::after(Duration::from_millis(1000)).await;
|
Timer::after_millis(1000).await;
|
||||||
|
|
||||||
let mut tx_buf = [0u8; MAX_SPI_BUFFER_SIZE];
|
let mut tx_buf = [0u8; MAX_SPI_BUFFER_SIZE];
|
||||||
let mut rx_buf = [0u8; MAX_SPI_BUFFER_SIZE];
|
let mut rx_buf = [0u8; MAX_SPI_BUFFER_SIZE];
|
||||||
|
@ -15,11 +15,11 @@ log = ["dep:log", "ppproto/log"]
|
|||||||
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 }
|
||||||
|
|
||||||
embedded-io-async = { version = "0.5.0" }
|
embedded-io-async = { version = "0.6.0" }
|
||||||
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" }
|
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel" }
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||||
ppproto = { version = "0.1.2"}
|
ppproto = { version = "0.1.2"}
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.4.0", path = "../embassy-sync" }
|
||||||
|
|
||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-ppp-v$VERSION/embassy-net-ppp/src/"
|
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-ppp-v$VERSION/embassy-net-ppp/src/"
|
||||||
|
@ -11,7 +11,7 @@ use core::mem::MaybeUninit;
|
|||||||
use embassy_futures::select::{select, Either};
|
use embassy_futures::select::{select, Either};
|
||||||
use embassy_net_driver_channel as ch;
|
use embassy_net_driver_channel as ch;
|
||||||
use embassy_net_driver_channel::driver::LinkState;
|
use embassy_net_driver_channel::driver::LinkState;
|
||||||
use embedded_io_async::{BufRead, Write, WriteAllError};
|
use embedded_io_async::{BufRead, Write};
|
||||||
use ppproto::pppos::{BufferFullError, PPPoS, PPPoSAction};
|
use ppproto::pppos::{BufferFullError, PPPoS, PPPoSAction};
|
||||||
pub use ppproto::{Config, Ipv4Status};
|
pub use ppproto::{Config, Ipv4Status};
|
||||||
|
|
||||||
@ -49,23 +49,12 @@ pub enum RunError<E> {
|
|||||||
Read(E),
|
Read(E),
|
||||||
/// Writing to the serial port failed.
|
/// Writing to the serial port failed.
|
||||||
Write(E),
|
Write(E),
|
||||||
/// Writing to the serial port wrote zero bytes, indicating it can't accept more data.
|
|
||||||
WriteZero,
|
|
||||||
/// Writing to the serial got EOF.
|
/// Writing to the serial got EOF.
|
||||||
Eof,
|
Eof,
|
||||||
/// PPP protocol was terminated by the peer
|
/// PPP protocol was terminated by the peer
|
||||||
Terminated,
|
Terminated,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E> From<WriteAllError<E>> for RunError<E> {
|
|
||||||
fn from(value: WriteAllError<E>) -> Self {
|
|
||||||
match value {
|
|
||||||
WriteAllError::Other(e) => Self::Write(e),
|
|
||||||
WriteAllError::WriteZero => Self::WriteZero,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d> Runner<'d> {
|
impl<'d> Runner<'d> {
|
||||||
/// You must call this in a background task for the driver to operate.
|
/// You must call this in a background task for the driver to operate.
|
||||||
///
|
///
|
||||||
@ -125,7 +114,7 @@ impl<'d> Runner<'d> {
|
|||||||
buf[..pkt.len()].copy_from_slice(pkt);
|
buf[..pkt.len()].copy_from_slice(pkt);
|
||||||
rx_chan.rx_done(pkt.len());
|
rx_chan.rx_done(pkt.len());
|
||||||
}
|
}
|
||||||
PPPoSAction::Transmit(n) => rw.write_all(&tx_buf[..n]).await?,
|
PPPoSAction::Transmit(n) => rw.write_all(&tx_buf[..n]).await.map_err(RunError::Write)?,
|
||||||
}
|
}
|
||||||
|
|
||||||
let status = ppp.status();
|
let status = ppp.status();
|
||||||
@ -148,7 +137,7 @@ impl<'d> Runner<'d> {
|
|||||||
}
|
}
|
||||||
Either::Second(pkt) => {
|
Either::Second(pkt) => {
|
||||||
match ppp.send(pkt, &mut tx_buf) {
|
match ppp.send(pkt, &mut tx_buf) {
|
||||||
Ok(n) => rw.write_all(&tx_buf[..n]).await?,
|
Ok(n) => rw.write_all(&tx_buf[..n]).await.map_err(RunError::Write)?,
|
||||||
Err(BufferFullError) => unreachable!(),
|
Err(BufferFullError) => unreachable!(),
|
||||||
}
|
}
|
||||||
tx_chan.tx_done();
|
tx_chan.tx_done();
|
||||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
|
embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" }
|
||||||
async-io = "1.6.0"
|
async-io = "1.6.0"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
libc = "0.2.101"
|
libc = "0.2.101"
|
||||||
|
@ -10,8 +10,8 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
embedded-hal = { version = "1.0.0-rc.1" }
|
embedded-hal = { version = "1.0.0-rc.1" }
|
||||||
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
||||||
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" }
|
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel" }
|
||||||
embassy-time = { version = "0.1.3", path = "../embassy-time" }
|
embassy-time = { version = "0.1.5", path = "../embassy-time" }
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
|
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
//! [`embassy-net`](https://crates.io/crates/embassy-net) driver for WIZnet ethernet chips.
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![feature(async_fn_in_trait)]
|
#![feature(async_fn_in_trait)]
|
||||||
|
#![allow(stable_features, unknown_lints, async_fn_in_trait)]
|
||||||
|
#![doc = include_str!("../README.md")]
|
||||||
|
|
||||||
pub mod chip;
|
pub mod chip;
|
||||||
mod device;
|
mod device;
|
||||||
|
|
||||||
use embassy_futures::select::{select, Either};
|
use embassy_futures::select::{select3, Either3};
|
||||||
use embassy_net_driver_channel as ch;
|
use embassy_net_driver_channel as ch;
|
||||||
use embassy_net_driver_channel::driver::LinkState;
|
use embassy_net_driver_channel::driver::LinkState;
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::{Duration, Ticker, Timer};
|
||||||
use embedded_hal::digital::OutputPin;
|
use embedded_hal::digital::OutputPin;
|
||||||
use embedded_hal_async::digital::Wait;
|
use embedded_hal_async::digital::Wait;
|
||||||
use embedded_hal_async::spi::SpiDevice;
|
use embedded_hal_async::spi::SpiDevice;
|
||||||
@ -49,32 +50,34 @@ pub struct Runner<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> {
|
|||||||
impl<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, C, SPI, INT, RST> {
|
impl<'d, C: Chip, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, C, SPI, INT, RST> {
|
||||||
pub async fn run(mut self) -> ! {
|
pub async fn run(mut self) -> ! {
|
||||||
let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split();
|
let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split();
|
||||||
|
let mut tick = Ticker::every(Duration::from_millis(500));
|
||||||
loop {
|
loop {
|
||||||
if self.mac.is_link_up().await {
|
match select3(
|
||||||
state_chan.set_link_state(LinkState::Up);
|
async {
|
||||||
loop {
|
self.int.wait_for_low().await.ok();
|
||||||
match select(
|
rx_chan.rx_buf().await
|
||||||
async {
|
},
|
||||||
self.int.wait_for_low().await.ok();
|
tx_chan.tx_buf(),
|
||||||
rx_chan.rx_buf().await
|
tick.next(),
|
||||||
},
|
)
|
||||||
tx_chan.tx_buf(),
|
.await
|
||||||
)
|
{
|
||||||
.await
|
Either3::First(p) => {
|
||||||
{
|
if let Ok(n) = self.mac.read_frame(p).await {
|
||||||
Either::First(p) => {
|
rx_chan.rx_done(n);
|
||||||
if let Ok(n) = self.mac.read_frame(p).await {
|
}
|
||||||
rx_chan.rx_done(n);
|
}
|
||||||
}
|
Either3::Second(p) => {
|
||||||
}
|
self.mac.write_frame(p).await.ok();
|
||||||
Either::Second(p) => {
|
tx_chan.tx_done();
|
||||||
self.mac.write_frame(p).await.ok();
|
}
|
||||||
tx_chan.tx_done();
|
Either3::Third(()) => {
|
||||||
}
|
if self.mac.is_link_up().await {
|
||||||
|
state_chan.set_link_state(LinkState::Up);
|
||||||
|
} else {
|
||||||
|
state_chan.set_link_state(LinkState::Down);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
state_chan.set_link_state(LinkState::Down);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,12 +98,12 @@ pub async fn new<'a, const N_RX: usize, const N_TX: usize, C: Chip, SPI: SpiDevi
|
|||||||
// Reset the chip.
|
// Reset the chip.
|
||||||
reset.set_low().ok();
|
reset.set_low().ok();
|
||||||
// Ensure the reset is registered.
|
// Ensure the reset is registered.
|
||||||
Timer::after(Duration::from_millis(1)).await;
|
Timer::after_millis(1).await;
|
||||||
reset.set_high().ok();
|
reset.set_high().ok();
|
||||||
|
|
||||||
// Wait for PLL lock. Some chips are slower than others.
|
// Wait for PLL lock. Some chips are slower than others.
|
||||||
// Slowest is w5100s which is 100ms, so let's just wait that.
|
// Slowest is w5100s which is 100ms, so let's just wait that.
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
|
|
||||||
let mac = WiznetDevice::new(spi_dev, mac_addr).await.unwrap();
|
let mac = WiznetDevice::new(spi_dev, mac_addr).await.unwrap();
|
||||||
|
|
||||||
|
40
embassy-net/CHANGELOG.md
Normal file
40
embassy-net/CHANGELOG.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
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/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
- Avoid never resolving `TcpIo::read` when the output buffer is empty.
|
||||||
|
- Update to `smoltcp` git.
|
||||||
|
- Forward constants from `smoltcp` in DNS query results so changing DNS result size in `smoltcp` properly propagates.
|
||||||
|
|
||||||
|
## 0.2.1 - 2023-10-31
|
||||||
|
|
||||||
|
- Re-add impl_trait_projections
|
||||||
|
- Fix: Reset DHCP socket when the link up is detected
|
||||||
|
|
||||||
|
## 0.2.0 - 2023-10-18
|
||||||
|
|
||||||
|
- Re-export `smoltcp::wire::IpEndpoint`
|
||||||
|
- Add poll functions on UdpSocket
|
||||||
|
- Make dual-stack work in embassy-net
|
||||||
|
- Fix multicast support
|
||||||
|
- Allow ethernet and 802.15.4 to coexist
|
||||||
|
- Add IEEE802.15.4 address to embassy net Stack
|
||||||
|
- Use HardwareAddress in Driver
|
||||||
|
- Add async versions of smoltcp's `send` and `recv` closure based API
|
||||||
|
- add error translation to tcp errors
|
||||||
|
- Forward TCP/UDP socket capacity impls
|
||||||
|
- allow changing IP config at runtime
|
||||||
|
- allow non-'static drivers
|
||||||
|
- Remove impl_trait_projections
|
||||||
|
- update embedded-io, embedded-nal-async
|
||||||
|
- add support for dhcp hostname option
|
||||||
|
- Wake stack's task after queueing a DNS query
|
||||||
|
|
||||||
|
## 0.1.0 - 2023-06-29
|
||||||
|
|
||||||
|
- First release
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "embassy-net"
|
name = "embassy-net"
|
||||||
version = "0.1.0"
|
version = "0.2.1"
|
||||||
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"
|
description = "Async TCP/IP network stack for embedded systems"
|
||||||
@ -25,7 +25,7 @@ features = ["nightly", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "me
|
|||||||
default = []
|
default = []
|
||||||
std = []
|
std = []
|
||||||
|
|
||||||
defmt = ["dep:defmt", "smoltcp/defmt", "embassy-net-driver/defmt"]
|
defmt = ["dep:defmt", "smoltcp/defmt", "embassy-net-driver/defmt", "heapless/defmt-03"]
|
||||||
|
|
||||||
nightly = ["dep:embedded-io-async", "dep:embedded-nal-async"]
|
nightly = ["dep:embedded-io-async", "dep:embedded-nal-async"]
|
||||||
|
|
||||||
@ -33,6 +33,7 @@ udp = ["smoltcp/socket-udp"]
|
|||||||
tcp = ["smoltcp/socket-tcp"]
|
tcp = ["smoltcp/socket-tcp"]
|
||||||
dns = ["smoltcp/socket-dns", "smoltcp/proto-dns"]
|
dns = ["smoltcp/socket-dns", "smoltcp/proto-dns"]
|
||||||
dhcpv4 = ["proto-ipv4", "medium-ethernet", "smoltcp/socket-dhcpv4"]
|
dhcpv4 = ["proto-ipv4", "medium-ethernet", "smoltcp/socket-dhcpv4"]
|
||||||
|
dhcpv4-hostname = ["dhcpv4"]
|
||||||
proto-ipv4 = ["smoltcp/proto-ipv4"]
|
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"]
|
||||||
@ -45,22 +46,21 @@ igmp = ["smoltcp/proto-igmp"]
|
|||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
log = { version = "0.4.14", optional = true }
|
log = { version = "0.4.14", optional = true }
|
||||||
|
|
||||||
smoltcp = { version = "0.10.0", default-features = false, features = [
|
smoltcp = { git = "https://github.com/smoltcp-rs/smoltcp.git", rev = "b57e2f9e70e82a13f31d5ea17e55232c11cc2b2d", default-features = false, features = [
|
||||||
"socket",
|
"socket",
|
||||||
"async",
|
"async",
|
||||||
] }
|
] }
|
||||||
|
|
||||||
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
|
embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" }
|
||||||
embassy-time = { version = "0.1.3", path = "../embassy-time" }
|
embassy-time = { version = "0.1.5", path = "../embassy-time" }
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.4.0", path = "../embassy-sync" }
|
||||||
embedded-io-async = { version = "0.5.0", optional = true }
|
embedded-io-async = { version = "0.6.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.8", default-features = false }
|
||||||
as-slice = "0.2.1"
|
as-slice = "0.2.1"
|
||||||
generic-array = { version = "0.14.4", default-features = false }
|
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.5.0", optional = true }
|
embedded-nal-async = { version = "0.7", optional = true }
|
||||||
atomic-polyfill = { version = "1.0" }
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
It builds on [`smoltcp`](https://github.com/smoltcp-rs/smoltcp). It provides a higher-level and more opinionated
|
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
|
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.
|
memory management designed to work well for embedded systems, aiming for a more "Just Works" experience.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use core::task::Context;
|
use core::task::Context;
|
||||||
|
|
||||||
use embassy_net_driver::{Capabilities, Checksum, Driver, Medium, RxToken, TxToken};
|
use embassy_net_driver::{Capabilities, Checksum, Driver, RxToken, TxToken};
|
||||||
use smoltcp::phy;
|
use smoltcp::phy::{self, Medium};
|
||||||
use smoltcp::time::Instant;
|
use smoltcp::time::Instant;
|
||||||
|
|
||||||
pub(crate) struct DriverAdapter<'d, 'c, T>
|
pub(crate) struct DriverAdapter<'d, 'c, T>
|
||||||
@ -11,6 +11,7 @@ where
|
|||||||
// must be Some when actually using this to rx/tx
|
// must be Some when actually using this to rx/tx
|
||||||
pub cx: Option<&'d mut Context<'c>>,
|
pub cx: Option<&'d mut Context<'c>>,
|
||||||
pub inner: &'d mut T,
|
pub inner: &'d mut T,
|
||||||
|
pub medium: Medium,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, 'c, T> phy::Device for DriverAdapter<'d, 'c, T>
|
impl<'d, 'c, T> phy::Device for DriverAdapter<'d, 'c, T>
|
||||||
@ -46,19 +47,7 @@ where
|
|||||||
|
|
||||||
smolcaps.max_transmission_unit = caps.max_transmission_unit;
|
smolcaps.max_transmission_unit = caps.max_transmission_unit;
|
||||||
smolcaps.max_burst_size = caps.max_burst_size;
|
smolcaps.max_burst_size = caps.max_burst_size;
|
||||||
smolcaps.medium = match caps.medium {
|
smolcaps.medium = self.medium;
|
||||||
#[cfg(feature = "medium-ethernet")]
|
|
||||||
Medium::Ethernet => phy::Medium::Ethernet,
|
|
||||||
#[cfg(feature = "medium-ip")]
|
|
||||||
Medium::Ip => phy::Medium::Ip,
|
|
||||||
#[cfg(feature = "medium-ieee802154")]
|
|
||||||
Medium::Ieee802154 => phy::Medium::Ieee802154,
|
|
||||||
#[allow(unreachable_patterns)]
|
|
||||||
_ => panic!(
|
|
||||||
"Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features.",
|
|
||||||
caps.medium
|
|
||||||
),
|
|
||||||
};
|
|
||||||
smolcaps.checksum.ipv4 = convert(caps.checksum.ipv4);
|
smolcaps.checksum.ipv4 = convert(caps.checksum.ipv4);
|
||||||
smolcaps.checksum.tcp = convert(caps.checksum.tcp);
|
smolcaps.checksum.tcp = convert(caps.checksum.tcp);
|
||||||
smolcaps.checksum.udp = convert(caps.checksum.udp);
|
smolcaps.checksum.udp = convert(caps.checksum.udp);
|
||||||
|
@ -63,7 +63,11 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Make a query for a given name and return the corresponding IP addresses.
|
/// Make a query for a given name and return the corresponding IP addresses.
|
||||||
pub async fn query(&self, name: &str, qtype: DnsQueryType) -> Result<Vec<IpAddress, 1>, Error> {
|
pub async fn query(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
qtype: DnsQueryType,
|
||||||
|
) -> Result<Vec<IpAddress, { smoltcp::config::DNS_MAX_RESULT_COUNT }>, Error> {
|
||||||
self.stack.dns_query(name, qtype).await
|
self.stack.dns_query(name, qtype).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,7 +105,8 @@ where
|
|||||||
async fn get_host_by_address(
|
async fn get_host_by_address(
|
||||||
&self,
|
&self,
|
||||||
_addr: embedded_nal_async::IpAddr,
|
_addr: embedded_nal_async::IpAddr,
|
||||||
) -> Result<heapless::String<256>, Self::Error> {
|
_result: &mut [u8],
|
||||||
|
) -> Result<usize, Self::Error> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))]
|
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))]
|
||||||
|
#![cfg_attr(feature = "nightly", allow(stable_features, unknown_lints, async_fn_in_trait))]
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
|
||||||
@ -33,13 +34,14 @@ use heapless::Vec;
|
|||||||
pub use smoltcp::iface::MulticastError;
|
pub use smoltcp::iface::MulticastError;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage};
|
use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage};
|
||||||
|
use smoltcp::phy::Medium;
|
||||||
#[cfg(feature = "dhcpv4")]
|
#[cfg(feature = "dhcpv4")]
|
||||||
use smoltcp::socket::dhcpv4::{self, RetryConfig};
|
use smoltcp::socket::dhcpv4::{self, RetryConfig};
|
||||||
#[cfg(feature = "medium-ethernet")]
|
#[cfg(feature = "medium-ethernet")]
|
||||||
pub use smoltcp::wire::EthernetAddress;
|
pub use smoltcp::wire::EthernetAddress;
|
||||||
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154", feature = "medium-ip"))]
|
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154", feature = "medium-ip"))]
|
||||||
pub use smoltcp::wire::HardwareAddress;
|
pub use smoltcp::wire::HardwareAddress;
|
||||||
#[cfg(feature = "udp")]
|
#[cfg(any(feature = "udp", feature = "tcp"))]
|
||||||
pub use smoltcp::wire::IpListenEndpoint;
|
pub use smoltcp::wire::IpListenEndpoint;
|
||||||
#[cfg(feature = "medium-ieee802154")]
|
#[cfg(feature = "medium-ieee802154")]
|
||||||
pub use smoltcp::wire::{Ieee802154Address, Ieee802154Frame};
|
pub use smoltcp::wire::{Ieee802154Address, Ieee802154Frame};
|
||||||
@ -56,12 +58,22 @@ const LOCAL_PORT_MIN: u16 = 1025;
|
|||||||
const LOCAL_PORT_MAX: u16 = 65535;
|
const LOCAL_PORT_MAX: u16 = 65535;
|
||||||
#[cfg(feature = "dns")]
|
#[cfg(feature = "dns")]
|
||||||
const MAX_QUERIES: usize = 4;
|
const MAX_QUERIES: usize = 4;
|
||||||
|
#[cfg(feature = "dhcpv4-hostname")]
|
||||||
|
const MAX_HOSTNAME_LEN: usize = 32;
|
||||||
|
|
||||||
/// Memory resources needed for a network stack.
|
/// Memory resources needed for a network stack.
|
||||||
pub struct StackResources<const SOCK: usize> {
|
pub struct StackResources<const SOCK: usize> {
|
||||||
sockets: [SocketStorage<'static>; SOCK],
|
sockets: [SocketStorage<'static>; SOCK],
|
||||||
#[cfg(feature = "dns")]
|
#[cfg(feature = "dns")]
|
||||||
queries: [Option<dns::DnsQuery>; MAX_QUERIES],
|
queries: [Option<dns::DnsQuery>; MAX_QUERIES],
|
||||||
|
#[cfg(feature = "dhcpv4-hostname")]
|
||||||
|
hostname: core::cell::UnsafeCell<HostnameResources>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "dhcpv4-hostname")]
|
||||||
|
struct HostnameResources {
|
||||||
|
option: smoltcp::wire::DhcpOption<'static>,
|
||||||
|
data: [u8; MAX_HOSTNAME_LEN],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const SOCK: usize> StackResources<SOCK> {
|
impl<const SOCK: usize> StackResources<SOCK> {
|
||||||
@ -73,6 +85,11 @@ impl<const SOCK: usize> StackResources<SOCK> {
|
|||||||
sockets: [SocketStorage::EMPTY; SOCK],
|
sockets: [SocketStorage::EMPTY; SOCK],
|
||||||
#[cfg(feature = "dns")]
|
#[cfg(feature = "dns")]
|
||||||
queries: [INIT; MAX_QUERIES],
|
queries: [INIT; MAX_QUERIES],
|
||||||
|
#[cfg(feature = "dhcpv4-hostname")]
|
||||||
|
hostname: core::cell::UnsafeCell::new(HostnameResources {
|
||||||
|
option: smoltcp::wire::DhcpOption { kind: 0, data: &[] },
|
||||||
|
data: [0; MAX_HOSTNAME_LEN],
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,6 +121,7 @@ pub struct StaticConfigV6 {
|
|||||||
/// DHCP configuration.
|
/// DHCP configuration.
|
||||||
#[cfg(feature = "dhcpv4")]
|
#[cfg(feature = "dhcpv4")]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
#[non_exhaustive]
|
||||||
pub struct DhcpConfig {
|
pub struct DhcpConfig {
|
||||||
/// Maximum lease duration.
|
/// Maximum lease duration.
|
||||||
///
|
///
|
||||||
@ -120,6 +138,9 @@ pub struct DhcpConfig {
|
|||||||
pub server_port: u16,
|
pub server_port: u16,
|
||||||
/// Client port. This is almost always 68. Do not change unless you know what you're doing.
|
/// Client port. This is almost always 68. Do not change unless you know what you're doing.
|
||||||
pub client_port: u16,
|
pub client_port: u16,
|
||||||
|
/// Our hostname. This will be sent to the DHCP server as Option 12.
|
||||||
|
#[cfg(feature = "dhcpv4-hostname")]
|
||||||
|
pub hostname: Option<heapless::String<MAX_HOSTNAME_LEN>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "dhcpv4")]
|
#[cfg(feature = "dhcpv4")]
|
||||||
@ -131,6 +152,8 @@ impl Default for DhcpConfig {
|
|||||||
ignore_naks: Default::default(),
|
ignore_naks: Default::default(),
|
||||||
server_port: smoltcp::wire::DHCP_SERVER_PORT,
|
server_port: smoltcp::wire::DHCP_SERVER_PORT,
|
||||||
client_port: smoltcp::wire::DHCP_CLIENT_PORT,
|
client_port: smoltcp::wire::DHCP_CLIENT_PORT,
|
||||||
|
#[cfg(feature = "dhcpv4-hostname")]
|
||||||
|
hostname: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,7 +191,7 @@ impl Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// IPv6 configuration with dynamic addressing.
|
/// IPv4 configuration with dynamic addressing.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```rust
|
/// ```rust
|
||||||
@ -232,6 +255,8 @@ struct Inner<D: Driver> {
|
|||||||
dns_socket: SocketHandle,
|
dns_socket: SocketHandle,
|
||||||
#[cfg(feature = "dns")]
|
#[cfg(feature = "dns")]
|
||||||
dns_waker: WakerRegistration,
|
dns_waker: WakerRegistration,
|
||||||
|
#[cfg(feature = "dhcpv4-hostname")]
|
||||||
|
hostname: &'static mut core::cell::UnsafeCell<HostnameResources>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct SocketStack {
|
pub(crate) struct SocketStack {
|
||||||
@ -241,14 +266,17 @@ pub(crate) struct SocketStack {
|
|||||||
next_local_port: u16,
|
next_local_port: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_smoltcp_hardware_address(addr: driver::HardwareAddress) -> HardwareAddress {
|
fn to_smoltcp_hardware_address(addr: driver::HardwareAddress) -> (HardwareAddress, Medium) {
|
||||||
match addr {
|
match addr {
|
||||||
#[cfg(feature = "medium-ethernet")]
|
#[cfg(feature = "medium-ethernet")]
|
||||||
driver::HardwareAddress::Ethernet(eth) => HardwareAddress::Ethernet(EthernetAddress(eth)),
|
driver::HardwareAddress::Ethernet(eth) => (HardwareAddress::Ethernet(EthernetAddress(eth)), Medium::Ethernet),
|
||||||
#[cfg(feature = "medium-ieee802154")]
|
#[cfg(feature = "medium-ieee802154")]
|
||||||
driver::HardwareAddress::Ieee802154(ieee) => HardwareAddress::Ieee802154(Ieee802154Address::Extended(ieee)),
|
driver::HardwareAddress::Ieee802154(ieee) => (
|
||||||
|
HardwareAddress::Ieee802154(Ieee802154Address::Extended(ieee)),
|
||||||
|
Medium::Ieee802154,
|
||||||
|
),
|
||||||
#[cfg(feature = "medium-ip")]
|
#[cfg(feature = "medium-ip")]
|
||||||
driver::HardwareAddress::Ip => HardwareAddress::Ip,
|
driver::HardwareAddress::Ip => (HardwareAddress::Ip, Medium::Ip),
|
||||||
|
|
||||||
#[allow(unreachable_patterns)]
|
#[allow(unreachable_patterns)]
|
||||||
_ => panic!(
|
_ => panic!(
|
||||||
@ -258,7 +286,7 @@ fn to_smoltcp_hardware_address(addr: driver::HardwareAddress) -> HardwareAddress
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: Driver + 'static> Stack<D> {
|
impl<D: Driver> Stack<D> {
|
||||||
/// Create a new network stack.
|
/// Create a new network stack.
|
||||||
pub fn new<const SOCK: usize>(
|
pub fn new<const SOCK: usize>(
|
||||||
mut device: D,
|
mut device: D,
|
||||||
@ -266,7 +294,8 @@ impl<D: Driver + 'static> Stack<D> {
|
|||||||
resources: &'static mut StackResources<SOCK>,
|
resources: &'static mut StackResources<SOCK>,
|
||||||
random_seed: u64,
|
random_seed: u64,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut iface_cfg = smoltcp::iface::Config::new(to_smoltcp_hardware_address(device.hardware_address()));
|
let (hardware_addr, medium) = to_smoltcp_hardware_address(device.hardware_address());
|
||||||
|
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(
|
||||||
@ -274,6 +303,7 @@ impl<D: Driver + 'static> Stack<D> {
|
|||||||
&mut DriverAdapter {
|
&mut DriverAdapter {
|
||||||
inner: &mut device,
|
inner: &mut device,
|
||||||
cx: None,
|
cx: None,
|
||||||
|
medium,
|
||||||
},
|
},
|
||||||
instant_to_smoltcp(Instant::now()),
|
instant_to_smoltcp(Instant::now()),
|
||||||
);
|
);
|
||||||
@ -307,6 +337,8 @@ impl<D: Driver + 'static> Stack<D> {
|
|||||||
)),
|
)),
|
||||||
#[cfg(feature = "dns")]
|
#[cfg(feature = "dns")]
|
||||||
dns_waker: WakerRegistration::new(),
|
dns_waker: WakerRegistration::new(),
|
||||||
|
#[cfg(feature = "dhcpv4-hostname")]
|
||||||
|
hostname: &mut resources.hostname,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "proto-ipv4")]
|
#[cfg(feature = "proto-ipv4")]
|
||||||
@ -331,7 +363,7 @@ impl<D: Driver + 'static> Stack<D> {
|
|||||||
|
|
||||||
/// Get the hardware address of the network interface.
|
/// Get the hardware address of the network interface.
|
||||||
pub fn hardware_address(&self) -> HardwareAddress {
|
pub fn hardware_address(&self) -> HardwareAddress {
|
||||||
self.with(|_s, i| to_smoltcp_hardware_address(i.device.hardware_address()))
|
self.with(|_s, i| to_smoltcp_hardware_address(i.device.hardware_address()).0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get whether the link is up.
|
/// Get whether the link is up.
|
||||||
@ -462,7 +494,11 @@ impl<D: Driver + 'static> Stack<D> {
|
|||||||
|
|
||||||
/// Make a query for a given name and return the corresponding IP addresses.
|
/// Make a query for a given name and return the corresponding IP addresses.
|
||||||
#[cfg(feature = "dns")]
|
#[cfg(feature = "dns")]
|
||||||
pub async fn dns_query(&self, name: &str, qtype: dns::DnsQueryType) -> Result<Vec<IpAddress, 1>, dns::Error> {
|
pub async fn dns_query(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
qtype: dns::DnsQueryType,
|
||||||
|
) -> Result<Vec<IpAddress, { smoltcp::config::DNS_MAX_RESULT_COUNT }>, dns::Error> {
|
||||||
// For A and AAAA queries we try detect whether `name` is just an IP address
|
// For A and AAAA queries we try detect whether `name` is just an IP address
|
||||||
match qtype {
|
match qtype {
|
||||||
#[cfg(feature = "proto-ipv4")]
|
#[cfg(feature = "proto-ipv4")]
|
||||||
@ -484,7 +520,10 @@ impl<D: Driver + 'static> Stack<D> {
|
|||||||
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);
|
||||||
match socket.start_query(s.iface.context(), name, qtype) {
|
match socket.start_query(s.iface.context(), name, qtype) {
|
||||||
Ok(handle) => Poll::Ready(Ok(handle)),
|
Ok(handle) => {
|
||||||
|
s.waker.wake();
|
||||||
|
Poll::Ready(Ok(handle))
|
||||||
|
}
|
||||||
Err(dns::StartQueryError::NoFreeSlot) => {
|
Err(dns::StartQueryError::NoFreeSlot) => {
|
||||||
i.dns_waker.register(cx.waker());
|
i.dns_waker.register(cx.waker());
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
@ -555,7 +594,7 @@ impl<D: Driver + 'static> Stack<D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "igmp")]
|
#[cfg(feature = "igmp")]
|
||||||
impl<D: Driver + 'static> Stack<D> {
|
impl<D: Driver> Stack<D> {
|
||||||
/// Join a multicast group.
|
/// Join a multicast group.
|
||||||
pub async 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
|
where
|
||||||
@ -577,9 +616,11 @@ impl<D: Driver + 'static> Stack<D> {
|
|||||||
let addr = addr.into();
|
let addr = addr.into();
|
||||||
|
|
||||||
self.with_mut(|s, i| {
|
self.with_mut(|s, i| {
|
||||||
|
let (_hardware_addr, medium) = to_smoltcp_hardware_address(i.device.hardware_address());
|
||||||
let mut smoldev = DriverAdapter {
|
let mut smoldev = DriverAdapter {
|
||||||
cx: Some(cx),
|
cx: Some(cx),
|
||||||
inner: &mut i.device,
|
inner: &mut i.device,
|
||||||
|
medium,
|
||||||
};
|
};
|
||||||
|
|
||||||
match s
|
match s
|
||||||
@ -614,9 +655,11 @@ impl<D: Driver + 'static> Stack<D> {
|
|||||||
let addr = addr.into();
|
let addr = addr.into();
|
||||||
|
|
||||||
self.with_mut(|s, i| {
|
self.with_mut(|s, i| {
|
||||||
|
let (_hardware_addr, medium) = to_smoltcp_hardware_address(i.device.hardware_address());
|
||||||
let mut smoldev = DriverAdapter {
|
let mut smoldev = DriverAdapter {
|
||||||
cx: Some(cx),
|
cx: Some(cx),
|
||||||
inner: &mut i.device,
|
inner: &mut i.device,
|
||||||
|
medium,
|
||||||
};
|
};
|
||||||
|
|
||||||
match s
|
match s
|
||||||
@ -645,7 +688,7 @@ impl SocketStack {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: Driver + 'static> Inner<D> {
|
impl<D: Driver> Inner<D> {
|
||||||
#[cfg(feature = "proto-ipv4")]
|
#[cfg(feature = "proto-ipv4")]
|
||||||
pub fn set_config_v4(&mut self, _s: &mut SocketStack, config: ConfigV4) {
|
pub fn set_config_v4(&mut self, _s: &mut SocketStack, config: ConfigV4) {
|
||||||
// Handle static config.
|
// Handle static config.
|
||||||
@ -673,6 +716,25 @@ impl<D: Driver + 'static> Inner<D> {
|
|||||||
socket.set_max_lease_duration(c.max_lease_duration.map(crate::time::duration_to_smoltcp));
|
socket.set_max_lease_duration(c.max_lease_duration.map(crate::time::duration_to_smoltcp));
|
||||||
socket.set_ports(c.server_port, c.client_port);
|
socket.set_ports(c.server_port, c.client_port);
|
||||||
socket.set_retry_config(c.retry_config);
|
socket.set_retry_config(c.retry_config);
|
||||||
|
|
||||||
|
socket.set_outgoing_options(&[]);
|
||||||
|
#[cfg(feature = "dhcpv4-hostname")]
|
||||||
|
if let Some(h) = c.hostname {
|
||||||
|
// safety: we just did set_outgoing_options([]) so we know the socket is no longer holding a reference.
|
||||||
|
let hostname = unsafe { &mut *self.hostname.get() };
|
||||||
|
|
||||||
|
// create data
|
||||||
|
// safety: we know the buffer lives forever, new borrows the StackResources for 'static.
|
||||||
|
// also we won't modify it until next call to this function.
|
||||||
|
hostname.data[..h.len()].copy_from_slice(h.as_bytes());
|
||||||
|
let data: &[u8] = &hostname.data[..h.len()];
|
||||||
|
let data: &'static [u8] = unsafe { core::mem::transmute(data) };
|
||||||
|
|
||||||
|
// set the option.
|
||||||
|
hostname.option = smoltcp::wire::DhcpOption { data, kind: 12 };
|
||||||
|
socket.set_outgoing_options(core::slice::from_ref(&hostname.option));
|
||||||
|
}
|
||||||
|
|
||||||
socket.reset();
|
socket.reset();
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
@ -765,18 +827,28 @@ 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());
|
||||||
|
|
||||||
|
let (_hardware_addr, medium) = to_smoltcp_hardware_address(self.device.hardware_address());
|
||||||
|
|
||||||
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
|
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
|
||||||
if self.device.capabilities().medium == embassy_net_driver::Medium::Ethernet
|
|
||||||
|| self.device.capabilities().medium == embassy_net_driver::Medium::Ieee802154
|
|
||||||
{
|
{
|
||||||
s.iface
|
let do_set = match medium {
|
||||||
.set_hardware_addr(to_smoltcp_hardware_address(self.device.hardware_address()));
|
#[cfg(feature = "medium-ethernet")]
|
||||||
|
Medium::Ethernet => true,
|
||||||
|
#[cfg(feature = "medium-ieee802154")]
|
||||||
|
Medium::Ieee802154 => true,
|
||||||
|
#[allow(unreachable_patterns)]
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
if do_set {
|
||||||
|
s.iface.set_hardware_addr(_hardware_addr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let timestamp = instant_to_smoltcp(Instant::now());
|
let timestamp = instant_to_smoltcp(Instant::now());
|
||||||
let mut smoldev = DriverAdapter {
|
let mut smoldev = DriverAdapter {
|
||||||
cx: Some(cx),
|
cx: Some(cx),
|
||||||
inner: &mut self.device,
|
inner: &mut self.device,
|
||||||
|
medium,
|
||||||
};
|
};
|
||||||
s.iface.poll(timestamp, &mut smoldev, &mut s.sockets);
|
s.iface.poll(timestamp, &mut smoldev, &mut s.sockets);
|
||||||
|
|
||||||
@ -797,6 +869,9 @@ impl<D: Driver + 'static> Inner<D> {
|
|||||||
let socket = s.sockets.get_mut::<dhcpv4::Socket>(dhcp_handle);
|
let socket = s.sockets.get_mut::<dhcpv4::Socket>(dhcp_handle);
|
||||||
|
|
||||||
if self.link_up {
|
if self.link_up {
|
||||||
|
if old_link_up != self.link_up {
|
||||||
|
socket.reset();
|
||||||
|
}
|
||||||
match socket.poll() {
|
match socket.poll() {
|
||||||
None => {}
|
None => {}
|
||||||
Some(dhcpv4::Event::Deconfigured) => {
|
Some(dhcpv4::Event::Deconfigured) => {
|
||||||
|
@ -390,6 +390,13 @@ impl<'d> TcpIo<'d> {
|
|||||||
// CAUTION: smoltcp semantics around EOF are different to what you'd expect
|
// CAUTION: smoltcp semantics around EOF are different to what you'd expect
|
||||||
// from posix-like IO, so we have to tweak things here.
|
// from posix-like IO, so we have to tweak things here.
|
||||||
self.with_mut(|s, _| match s.recv_slice(buf) {
|
self.with_mut(|s, _| match s.recv_slice(buf) {
|
||||||
|
// Reading into empty buffer
|
||||||
|
Ok(0) if buf.is_empty() => {
|
||||||
|
// embedded_io_async::Read's contract is to not block if buf is empty. While
|
||||||
|
// this function is not a direct implementor of the trait method, we still don't
|
||||||
|
// want our future to never resolve.
|
||||||
|
Poll::Ready(Ok(0))
|
||||||
|
}
|
||||||
// No data ready
|
// No data ready
|
||||||
Ok(0) => {
|
Ok(0) => {
|
||||||
s.register_recv_waker(cx.waker());
|
s.register_recv_waker(cx.waker());
|
||||||
@ -579,11 +586,10 @@ mod embedded_io_impls {
|
|||||||
/// TCP client compatible with `embedded-nal-async` traits.
|
/// TCP client compatible with `embedded-nal-async` traits.
|
||||||
#[cfg(feature = "nightly")]
|
#[cfg(feature = "nightly")]
|
||||||
pub mod client {
|
pub mod client {
|
||||||
use core::cell::UnsafeCell;
|
use core::cell::{Cell, UnsafeCell};
|
||||||
use core::mem::MaybeUninit;
|
use core::mem::MaybeUninit;
|
||||||
use core::ptr::NonNull;
|
use core::ptr::NonNull;
|
||||||
|
|
||||||
use atomic_polyfill::{AtomicBool, Ordering};
|
|
||||||
use embedded_nal_async::IpAddr;
|
use embedded_nal_async::IpAddr;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -612,10 +618,7 @@ pub mod client {
|
|||||||
async fn connect<'a>(
|
async fn connect<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
remote: embedded_nal_async::SocketAddr,
|
remote: embedded_nal_async::SocketAddr,
|
||||||
) -> Result<Self::Connection<'a>, Self::Error>
|
) -> Result<Self::Connection<'a>, Self::Error> {
|
||||||
where
|
|
||||||
Self: 'a,
|
|
||||||
{
|
|
||||||
let addr: crate::IpAddress = match remote.ip() {
|
let addr: crate::IpAddress = match remote.ip() {
|
||||||
#[cfg(feature = "proto-ipv4")]
|
#[cfg(feature = "proto-ipv4")]
|
||||||
IpAddr::V4(addr) => crate::IpAddress::Ipv4(crate::Ipv4Address::from_bytes(&addr.octets())),
|
IpAddr::V4(addr) => crate::IpAddress::Ipv4(crate::Ipv4Address::from_bytes(&addr.octets())),
|
||||||
@ -702,15 +705,13 @@ pub mod client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<const N: usize, const TX_SZ: usize, const RX_SZ: usize> Sync for TcpClientState<N, TX_SZ, RX_SZ> {}
|
|
||||||
|
|
||||||
struct Pool<T, const N: usize> {
|
struct Pool<T, const N: usize> {
|
||||||
used: [AtomicBool; N],
|
used: [Cell<bool>; N],
|
||||||
data: [UnsafeCell<MaybeUninit<T>>; N],
|
data: [UnsafeCell<MaybeUninit<T>>; N],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> Pool<T, N> {
|
impl<T, const N: usize> Pool<T, N> {
|
||||||
const VALUE: AtomicBool = AtomicBool::new(false);
|
const VALUE: Cell<bool> = Cell::new(false);
|
||||||
const UNINIT: UnsafeCell<MaybeUninit<T>> = UnsafeCell::new(MaybeUninit::uninit());
|
const UNINIT: UnsafeCell<MaybeUninit<T>> = UnsafeCell::new(MaybeUninit::uninit());
|
||||||
|
|
||||||
const fn new() -> Self {
|
const fn new() -> Self {
|
||||||
@ -724,7 +725,9 @@ pub mod client {
|
|||||||
impl<T, const N: usize> Pool<T, N> {
|
impl<T, const N: usize> Pool<T, N> {
|
||||||
fn alloc(&self) -> Option<NonNull<T>> {
|
fn alloc(&self) -> Option<NonNull<T>> {
|
||||||
for n in 0..N {
|
for n in 0..N {
|
||||||
if self.used[n].swap(true, Ordering::SeqCst) == false {
|
// this can't race because Pool is not Sync.
|
||||||
|
if !self.used[n].get() {
|
||||||
|
self.used[n].set(true);
|
||||||
let p = self.data[n].get() as *mut T;
|
let p = self.data[n].get() as *mut T;
|
||||||
return Some(unsafe { NonNull::new_unchecked(p) });
|
return Some(unsafe { NonNull::new_unchecked(p) });
|
||||||
}
|
}
|
||||||
@ -738,7 +741,7 @@ pub mod client {
|
|||||||
let n = p.as_ptr().offset_from(origin);
|
let n = p.as_ptr().offset_from(origin);
|
||||||
assert!(n >= 0);
|
assert!(n >= 0);
|
||||||
assert!((n as usize) < N);
|
assert!((n as usize) < N);
|
||||||
self.used[n as usize].store(false, Ordering::SeqCst);
|
self.used[n as usize].set(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ nrf52805 = ["nrf52805-pac", "_nrf52"]
|
|||||||
nrf52810 = ["nrf52810-pac", "_nrf52"]
|
nrf52810 = ["nrf52810-pac", "_nrf52"]
|
||||||
nrf52811 = ["nrf52811-pac", "_nrf52"]
|
nrf52811 = ["nrf52811-pac", "_nrf52"]
|
||||||
nrf52820 = ["nrf52820-pac", "_nrf52"]
|
nrf52820 = ["nrf52820-pac", "_nrf52"]
|
||||||
nrf52832 = ["nrf52832-pac", "_nrf52"]
|
nrf52832 = ["nrf52832-pac", "_nrf52", "_nrf52832_anomaly_109"]
|
||||||
nrf52833 = ["nrf52833-pac", "_nrf52", "_gpio-p1"]
|
nrf52833 = ["nrf52833-pac", "_nrf52", "_gpio-p1"]
|
||||||
nrf52840 = ["nrf52840-pac", "_nrf52", "_gpio-p1"]
|
nrf52840 = ["nrf52840-pac", "_nrf52", "_gpio-p1"]
|
||||||
nrf5340-app-s = ["_nrf5340-app", "_s"]
|
nrf5340-app-s = ["_nrf5340-app", "_s"]
|
||||||
@ -90,9 +90,12 @@ _ppi = []
|
|||||||
_dppi = []
|
_dppi = []
|
||||||
_gpio-p1 = []
|
_gpio-p1 = []
|
||||||
|
|
||||||
|
# Errata workarounds
|
||||||
|
_nrf52832_anomaly_109 = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-time = { version = "0.1.3", path = "../embassy-time", optional = true }
|
embassy-time = { version = "0.1.5", path = "../embassy-time", optional = true }
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.4.0", path = "../embassy-sync" }
|
||||||
embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", 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 }
|
||||||
@ -100,14 +103,13 @@ embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optiona
|
|||||||
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-rc.1", optional = true}
|
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1", optional = true}
|
||||||
embedded-hal-async = { version = "=1.0.0-rc.1", optional = true}
|
embedded-hal-async = { version = "=1.0.0-rc.1", optional = true}
|
||||||
embedded-io = { version = "0.5.0" }
|
embedded-io = { version = "0.6.0" }
|
||||||
embedded-io-async = { version = "0.5.0", optional = true }
|
embedded-io-async = { version = "0.6.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 }
|
||||||
cortex-m-rt = ">=0.6.15,<0.8"
|
cortex-m-rt = ">=0.6.15,<0.8"
|
||||||
cortex-m = "0.7.6"
|
cortex-m = "0.7.6"
|
||||||
futures = { version = "0.3.17", default-features = false }
|
|
||||||
critical-section = "1.1"
|
critical-section = "1.1"
|
||||||
rand_core = "0.6.3"
|
rand_core = "0.6.3"
|
||||||
fixed = "1.10.0"
|
fixed = "1.10.0"
|
||||||
|
@ -12,7 +12,7 @@ use core::cmp::min;
|
|||||||
use core::future::poll_fn;
|
use core::future::poll_fn;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::slice;
|
use core::slice;
|
||||||
use core::sync::atomic::{compiler_fence, AtomicU8, AtomicUsize, Ordering};
|
use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU8, AtomicUsize, Ordering};
|
||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
|
|
||||||
use embassy_hal_internal::atomic_ring_buffer::RingBuffer;
|
use embassy_hal_internal::atomic_ring_buffer::RingBuffer;
|
||||||
@ -41,7 +41,9 @@ mod sealed {
|
|||||||
|
|
||||||
pub rx_waker: AtomicWaker,
|
pub rx_waker: AtomicWaker,
|
||||||
pub rx_buf: RingBuffer,
|
pub rx_buf: RingBuffer,
|
||||||
pub rx_bufs: AtomicU8,
|
pub rx_started: AtomicBool,
|
||||||
|
pub rx_started_count: AtomicU8,
|
||||||
|
pub rx_ended_count: AtomicU8,
|
||||||
pub rx_ppi_ch: AtomicU8,
|
pub rx_ppi_ch: AtomicU8,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -65,7 +67,9 @@ impl State {
|
|||||||
|
|
||||||
rx_waker: AtomicWaker::new(),
|
rx_waker: AtomicWaker::new(),
|
||||||
rx_buf: RingBuffer::new(),
|
rx_buf: RingBuffer::new(),
|
||||||
rx_bufs: AtomicU8::new(0),
|
rx_started: AtomicBool::new(false),
|
||||||
|
rx_started_count: AtomicU8::new(0),
|
||||||
|
rx_ended_count: AtomicU8::new(0),
|
||||||
rx_ppi_ch: AtomicU8::new(0),
|
rx_ppi_ch: AtomicU8::new(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,28 +108,20 @@ impl<U: UarteInstance> interrupt::typelevel::Handler<U::Interrupt> for Interrupt
|
|||||||
s.rx_waker.wake();
|
s.rx_waker.wake();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not RXing, start.
|
if r.events_endrx.read().bits() != 0 {
|
||||||
if s.rx_bufs.load(Ordering::Relaxed) == 0 {
|
//trace!(" irq_rx: endrx");
|
||||||
let (ptr, len) = rx.push_buf();
|
r.events_endrx.reset();
|
||||||
if len >= half_len {
|
|
||||||
//trace!(" irq_rx: starting {:?}", half_len);
|
|
||||||
s.rx_bufs.store(1, Ordering::Relaxed);
|
|
||||||
|
|
||||||
// Set up the DMA read
|
let val = s.rx_ended_count.load(Ordering::Relaxed);
|
||||||
r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) });
|
s.rx_ended_count.store(val.wrapping_add(1), Ordering::Relaxed);
|
||||||
r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(half_len as _) });
|
|
||||||
|
|
||||||
// Start UARTE Receive transaction
|
|
||||||
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
|
|
||||||
rx.push_done(half_len);
|
|
||||||
r.intenset.write(|w| w.rxstarted().set());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.events_rxstarted.read().bits() != 0 {
|
if r.events_rxstarted.read().bits() != 0 || !s.rx_started.load(Ordering::Relaxed) {
|
||||||
//trace!(" irq_rx: rxstarted");
|
//trace!(" irq_rx: rxstarted");
|
||||||
let (ptr, len) = rx.push_buf();
|
let (ptr, len) = rx.push_buf();
|
||||||
if len >= half_len {
|
if len >= half_len {
|
||||||
|
r.events_rxstarted.reset();
|
||||||
|
|
||||||
//trace!(" irq_rx: starting second {:?}", half_len);
|
//trace!(" irq_rx: starting second {:?}", half_len);
|
||||||
|
|
||||||
// Set up the DMA read
|
// Set up the DMA read
|
||||||
@ -134,11 +130,50 @@ impl<U: UarteInstance> interrupt::typelevel::Handler<U::Interrupt> for Interrupt
|
|||||||
|
|
||||||
let chn = s.rx_ppi_ch.load(Ordering::Relaxed);
|
let chn = s.rx_ppi_ch.load(Ordering::Relaxed);
|
||||||
|
|
||||||
|
// Enable endrx -> startrx PPI channel.
|
||||||
|
// From this point on, if endrx happens, startrx is automatically fired.
|
||||||
ppi::regs().chenset.write(|w| unsafe { w.bits(1 << chn) });
|
ppi::regs().chenset.write(|w| unsafe { w.bits(1 << chn) });
|
||||||
|
|
||||||
|
// It is possible that endrx happened BEFORE enabling the PPI. In this case
|
||||||
|
// the PPI channel doesn't trigger, and we'd hang. We have to detect this
|
||||||
|
// and manually start.
|
||||||
|
|
||||||
|
// check again in case endrx has happened between the last check and now.
|
||||||
|
if r.events_endrx.read().bits() != 0 {
|
||||||
|
//trace!(" irq_rx: endrx");
|
||||||
|
r.events_endrx.reset();
|
||||||
|
|
||||||
|
let val = s.rx_ended_count.load(Ordering::Relaxed);
|
||||||
|
s.rx_ended_count.store(val.wrapping_add(1), Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
let rx_ended = s.rx_ended_count.load(Ordering::Relaxed);
|
||||||
|
let rx_started = s.rx_started_count.load(Ordering::Relaxed);
|
||||||
|
|
||||||
|
// If we started the same amount of transfers as ended, the last rxend has
|
||||||
|
// already occured.
|
||||||
|
let rxend_happened = rx_started == rx_ended;
|
||||||
|
|
||||||
|
// Check if the PPI channel is still enabled. The PPI channel disables itself
|
||||||
|
// when it fires, so if it's still enabled it hasn't fired.
|
||||||
|
let ppi_ch_enabled = ppi::regs().chen.read().bits() & (1 << chn) != 0;
|
||||||
|
|
||||||
|
// if rxend happened, and the ppi channel hasn't fired yet, the rxend got missed.
|
||||||
|
// this condition also naturally matches if `!started`, needed to kickstart the DMA.
|
||||||
|
if rxend_happened && ppi_ch_enabled {
|
||||||
|
//trace!("manually starting.");
|
||||||
|
|
||||||
|
// disable the ppi ch, it's of no use anymore.
|
||||||
|
ppi::regs().chenclr.write(|w| unsafe { w.bits(1 << chn) });
|
||||||
|
|
||||||
|
// manually start
|
||||||
|
r.tasks_startrx.write(|w| unsafe { w.bits(1) });
|
||||||
|
}
|
||||||
|
|
||||||
rx.push_done(half_len);
|
rx.push_done(half_len);
|
||||||
|
|
||||||
r.events_rxstarted.reset();
|
s.rx_started_count.store(rx_started.wrapping_add(1), Ordering::Relaxed);
|
||||||
|
s.rx_started.store(true, Ordering::Relaxed);
|
||||||
} else {
|
} else {
|
||||||
//trace!(" irq_rx: rxstarted no buf");
|
//trace!(" irq_rx: rxstarted no buf");
|
||||||
r.intenclr.write(|w| w.rxstarted().clear());
|
r.intenclr.write(|w| w.rxstarted().clear());
|
||||||
@ -282,6 +317,8 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
|
|||||||
|
|
||||||
let r = U::regs();
|
let r = U::regs();
|
||||||
|
|
||||||
|
let hwfc = cts.is_some();
|
||||||
|
|
||||||
rxd.conf().write(|w| w.input().connect().drive().h0h1());
|
rxd.conf().write(|w| w.input().connect().drive().h0h1());
|
||||||
r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) });
|
r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) });
|
||||||
|
|
||||||
@ -303,7 +340,8 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
|
|||||||
// Initialize state
|
// Initialize state
|
||||||
let s = U::buffered_state();
|
let s = U::buffered_state();
|
||||||
s.tx_count.store(0, Ordering::Relaxed);
|
s.tx_count.store(0, Ordering::Relaxed);
|
||||||
s.rx_bufs.store(0, Ordering::Relaxed);
|
s.rx_started_count.store(0, Ordering::Relaxed);
|
||||||
|
s.rx_ended_count.store(0, Ordering::Relaxed);
|
||||||
let len = tx_buffer.len();
|
let len = tx_buffer.len();
|
||||||
unsafe { s.tx_buf.init(tx_buffer.as_mut_ptr(), len) };
|
unsafe { s.tx_buf.init(tx_buffer.as_mut_ptr(), len) };
|
||||||
let len = rx_buffer.len();
|
let len = rx_buffer.len();
|
||||||
@ -311,7 +349,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
|
|||||||
|
|
||||||
// Configure
|
// Configure
|
||||||
r.config.write(|w| {
|
r.config.write(|w| {
|
||||||
w.hwfc().bit(false);
|
w.hwfc().bit(hwfc);
|
||||||
w.parity().variant(config.parity);
|
w.parity().variant(config.parity);
|
||||||
w
|
w
|
||||||
});
|
});
|
||||||
@ -333,6 +371,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> {
|
|||||||
w.endtx().set();
|
w.endtx().set();
|
||||||
w.rxstarted().set();
|
w.rxstarted().set();
|
||||||
w.error().set();
|
w.error().set();
|
||||||
|
w.endrx().set();
|
||||||
w
|
w
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user