Compare commits
853 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7612ada45 | ||
|
|
d581195df7 | ||
|
|
040c0fd353 | ||
|
|
021676e871 | ||
|
|
848029b152 | ||
|
|
2b80e4d123 | ||
|
|
0ef00206d5 | ||
|
|
409641dd7e | ||
|
|
9f553b1b0e | ||
|
|
8a7f8361a6 | ||
|
|
7d0b39dbd2 | ||
|
|
246e7f1c8b | ||
|
|
6b4312fb90 | ||
|
|
5a64d9ba8f | ||
|
|
f1276f7d1b | ||
|
|
bb406cec6b | ||
|
|
66d2effee2 | ||
|
|
b3401bff59 | ||
|
|
2a4e58a230 | ||
|
|
5cd0d6f6bf | ||
|
|
39da5337ac | ||
|
|
7319458dd2 | ||
|
|
05b5bac027 | ||
|
|
6494354a91 | ||
|
|
713cd491b6 | ||
|
|
337b3cc6b7 | ||
|
|
dc2b968491 | ||
|
|
19eb7728bb | ||
|
|
551386216e | ||
|
|
1f929af377 | ||
|
|
06a15807e3 | ||
|
|
0f384992cf | ||
|
|
36095e447e | ||
|
|
c2de8a1859 | ||
|
|
b9ebeb1809 | ||
|
|
8b16365c2d | ||
|
|
62c72947dd | ||
|
|
286219707c | ||
|
|
d66e153686 | ||
|
|
7f8af8a651 | ||
|
|
2e6a95ac99 | ||
|
|
5c30c925ca | ||
|
|
f83ab23f04 | ||
|
|
f1c372f250 | ||
|
|
151e66c3b3 | ||
|
|
a3b3547212 | ||
|
|
1fff464b66 | ||
|
|
036d08e09f | ||
|
|
cde6169d6d | ||
|
|
d4386adfc7 | ||
|
|
97598968eb | ||
|
|
b8f15f0a8b | ||
|
|
f2b958b501 | ||
|
|
f7c1bdbfc5 | ||
|
|
78c63a7a0f | ||
|
|
af8eaea1e3 | ||
|
|
1684ba10f0 | ||
|
|
2ca1545b50 | ||
|
|
f990957f21 | ||
|
|
d9f1e9a53f | ||
|
|
85d30e9816 | ||
|
|
a12e7fece1 | ||
|
|
bc0bedd628 | ||
|
|
3a03dd88c7 | ||
|
|
cc4e527eaf | ||
|
|
d66281609b | ||
|
|
62994578c2 | ||
|
|
35d3ed301c | ||
|
|
273fdc0928 | ||
|
|
3cc8d611bd | ||
|
|
f6126502ae | ||
|
|
d2f15d69d7 | ||
|
|
5135965116 | ||
|
|
6b01f7993b | ||
|
|
d86a079ea9 | ||
|
|
9458fd3ed4 | ||
|
|
2d4ccd735f | ||
|
|
b4379b8518 | ||
|
|
b26b31f1ce | ||
|
|
dfa96820f0 | ||
|
|
033c4b84c6 | ||
|
|
50387290d9 | ||
|
|
dcded33d3d | ||
|
|
d54f8440a5 | ||
|
|
91d7f23982 | ||
|
|
a189eff517 | ||
|
|
da59a4eb36 | ||
|
|
94e7ffbcef | ||
|
|
a6a83d3bb5 | ||
|
|
891a12e13f | ||
|
|
9f3476b006 | ||
|
|
2d3fdeb876 | ||
|
|
2d87bb0002 | ||
|
|
b6117d5040 | ||
|
|
92910bf1cb | ||
|
|
1a2bee6f1f | ||
|
|
aeda6ac00a | ||
|
|
cfb83b153d | ||
|
|
2512658653 | ||
|
|
b50a075449 | ||
|
|
7095576e6a | ||
|
|
eec75c8f82 | ||
|
|
aed0fac0eb | ||
|
|
b06c7a470c | ||
|
|
6a008bf597 | ||
|
|
a00643f22d | ||
|
|
ef98e2b24f | ||
|
|
8b36a43c07 | ||
|
|
3adb0b288e | ||
|
|
fe482bdd05 | ||
|
|
09511c750a | ||
|
|
51215093ca | ||
|
|
45395d4cc9 | ||
|
|
79ca9d07aa | ||
|
|
e98674e8fa | ||
|
|
c1f0c134c3 | ||
|
|
b5775667b4 | ||
|
|
60a2c76005 | ||
|
|
9c1d99d9b4 | ||
|
|
3a4a7632b1 | ||
|
|
f81b5f6c7f | ||
|
|
973671c3cb | ||
|
|
f9203dc523 | ||
|
|
457ed44802 | ||
|
|
02ddad47c0 | ||
|
|
a824963943 | ||
|
|
2fc10d684c | ||
|
|
8600c4009f | ||
|
|
3c1aba5088 | ||
|
|
bdfec3781f | ||
|
|
73a6e32971 | ||
|
|
432622ca9f | ||
|
|
70fe8fb077 | ||
|
|
7821968df8 | ||
|
|
26fd1a40d9 | ||
|
|
9c06c69600 | ||
|
|
4f6d51fefa | ||
|
|
2472e36232 | ||
|
|
c6404fe569 | ||
|
|
d68550c485 | ||
|
|
52c65dd149 | ||
|
|
64f4fea403 | ||
|
|
38dde2ebbd | ||
|
|
959631ee55 | ||
|
|
014a04faad | ||
|
|
f3346a80b9 | ||
|
|
c19c7fcf01 | ||
|
|
92b91257e9 | ||
|
|
b11fc0fce8 | ||
|
|
099e0eacdf | ||
|
|
b538e48ea4 | ||
|
|
7da4444a7e | ||
|
|
8cbc249e2e | ||
|
|
30276e1609 | ||
|
|
456bcfb7c6 | ||
|
|
0b452f7933 | ||
|
|
5d9cc1a588 | ||
|
|
5d120f7a70 | ||
|
|
6cb5d9643f | ||
|
|
fbc57542a8 | ||
|
|
321ca2f131 | ||
|
|
cf10f07a9e | ||
|
|
57fc5df858 | ||
|
|
33659380dd | ||
|
|
4af44b1498 | ||
|
|
2c14e595db | ||
|
|
e10ae2ddff | ||
|
|
7402ad61ed | ||
|
|
3c4b7f0f66 | ||
|
|
50d8187e39 | ||
|
|
6d6f6a7baa | ||
|
|
31d714a156 | ||
|
|
ac4679a1ae | ||
|
|
d6f63d67a9 | ||
|
|
d5e6ba5ceb | ||
|
|
4233bddf23 | ||
|
|
639853ede9 | ||
|
|
ac819fb42f | ||
|
|
8782429e9f | ||
|
|
0da6eec089 | ||
|
|
feac6f17c3 | ||
|
|
255f5e7c73 | ||
|
|
4c5be2c907 | ||
|
|
ccb3a1ba40 | ||
|
|
8860aba9b2 | ||
|
|
eabb6fb1c6 | ||
|
|
665fb0e278 | ||
|
|
ed7960ce8b | ||
|
|
ca9ee23b5e | ||
|
|
1e6820d1a7 | ||
|
|
0c86740418 | ||
|
|
177db100fb | ||
|
|
40c0a6944e | ||
|
|
c717f04d4d | ||
|
|
f9ba299f2e | ||
|
|
05f2ee5a72 | ||
|
|
b953f178b8 | ||
|
|
111ae93cc5 | ||
|
|
fc4eaa1ebf | ||
|
|
a5c4aa1cea | ||
|
|
7d717c6acf | ||
|
|
f9c7d06a6e | ||
|
|
e5b923b678 | ||
|
|
6fa2823e10 | ||
|
|
25d8390d68 | ||
|
|
448ed9a001 | ||
|
|
9d4b8fdbc6 | ||
|
|
05a1ebead3 | ||
|
|
416c1481ae | ||
|
|
4546f861c8 | ||
|
|
a8fedf0130 | ||
|
|
3e42b76993 | ||
|
|
f07e25c970 | ||
|
|
b7224ef4c9 | ||
|
|
8a23dbe1b6 | ||
|
|
4e257f74aa | ||
|
|
f3c5286028 | ||
|
|
6bf03f63a9 | ||
|
|
681f4ef0aa | ||
|
|
561b582a80 | ||
|
|
19b08efc1d | ||
|
|
a7d8b2009d | ||
|
|
2ac47868f7 | ||
|
|
f03527f6fc | ||
|
|
c163b0586a | ||
|
|
1de2483609 | ||
|
|
5f6ca8d252 | ||
|
|
4c1c6131df | ||
|
|
71c4fc5a24 | ||
|
|
5486cdb5e3 | ||
|
|
e367c83bde | ||
|
|
1afc9eef89 | ||
|
|
54d4407042 | ||
|
|
632bbc6461 | ||
|
|
aec83bdd31 | ||
|
|
56cd5a288f | ||
|
|
c574834ae3 | ||
|
|
62ee60d43f | ||
|
|
a4bc0536a3 | ||
|
|
d914a0301f | ||
|
|
a754e411b1 | ||
|
|
f2aa3f9863 | ||
|
|
d32a7336bb | ||
|
|
326e006335 | ||
|
|
f0d0dd92c4 | ||
|
|
358d884643 | ||
|
|
9d51d1befe | ||
|
|
88c4dba9e0 | ||
|
|
472d5f93e0 | ||
|
|
6e2606b367 | ||
|
|
99a579973a | ||
|
|
39c0baf2c7 | ||
|
|
060aa7262c | ||
|
|
67bc37fb93 | ||
|
|
f0a361e448 | ||
|
|
da9c0b0579 | ||
|
|
a15cfe7f21 | ||
|
|
ef7842fab4 | ||
|
|
0dc8dcf8e2 | ||
|
|
7aa8145213 | ||
|
|
e22103d867 | ||
|
|
d08dc3dcb7 | ||
|
|
f29d539a78 | ||
|
|
5af3d9ddf2 | ||
|
|
d655fe665f | ||
|
|
ba8daafb0b | ||
|
|
0142703112 | ||
|
|
c26600f943 | ||
|
|
f93efa73a9 | ||
|
|
be9dc0e0b7 | ||
|
|
dc88cb13e8 | ||
|
|
b27482df3c | ||
|
|
7ca1b4376f | ||
|
|
f26eef646a | ||
|
|
81f93698b0 | ||
|
|
3a9abca148 | ||
|
|
08030d5541 | ||
|
|
ca5e8560bf | ||
|
|
038d07f5b6 | ||
|
|
16f277915b | ||
|
|
754d8aa585 | ||
|
|
af3f892381 | ||
|
|
ba96bac776 | ||
|
|
f4540a5508 | ||
|
|
897a67808c | ||
|
|
3e16c5c623 | ||
|
|
e5bc63916f | ||
|
|
efe58e94a2 | ||
|
|
62e991d749 | ||
|
|
d3f03042f3 | ||
|
|
a4fe5e6e63 | ||
|
|
00ad9b5eed | ||
|
|
18da679d8a | ||
|
|
5a993fed38 | ||
|
|
c7a2368845 | ||
|
|
3e9a506c00 | ||
|
|
e033162ffd | ||
|
|
30aef580e3 | ||
|
|
8789ca3c3d | ||
|
|
a3304c6cff | ||
|
|
f5b8e4b914 | ||
|
|
8e9f6b5015 | ||
|
|
ed51cd8c35 | ||
|
|
f1bedbe3dc | ||
|
|
487de0ff22 | ||
|
|
fcc6375ba5 | ||
|
|
b8a4d5f9ff | ||
|
|
11c73caf89 | ||
|
|
fa66a43949 | ||
|
|
0981c1b619 | ||
|
|
4422ed3ccf | ||
|
|
f50c6fc071 | ||
|
|
66f6737697 | ||
|
|
00d892b214 | ||
|
|
c481c49888 | ||
|
|
4377ec083f | ||
|
|
fd57488ce3 | ||
|
|
6d96810c56 | ||
|
|
9321a34dbe | ||
|
|
feaf66847c | ||
|
|
3bb49b049a | ||
|
|
5076d1ee8f | ||
|
|
fc924bf929 | ||
|
|
04f43d28c3 | ||
|
|
cf9050d5d7 | ||
|
|
826754c482 | ||
|
|
4534ee13ae | ||
|
|
117327e206 | ||
|
|
26fc8871db | ||
|
|
02d221ee52 | ||
|
|
f2c473d3bc | ||
|
|
d971d65c98 | ||
|
|
987f00bb1d | ||
|
|
794cdb0af4 | ||
|
|
89d9de67eb | ||
|
|
3ee8fa61ee | ||
|
|
37fa662fe7 | ||
|
|
d4e463b3ff | ||
|
|
3d0a1998fa | ||
|
|
370f54119f | ||
|
|
5324bce815 | ||
|
|
d5afeeddc8 | ||
|
|
5de267ab09 | ||
|
|
ba63beb3b2 | ||
|
|
25eff6a255 | ||
|
|
d9d771706f | ||
|
|
46be3b19b5 | ||
|
|
f765a6b094 | ||
|
|
c0a9934e62 | ||
|
|
b1ca30e404 | ||
|
|
d9532bbba7 | ||
|
|
344ee133bc | ||
|
|
fc92beebe6 | ||
|
|
5ae76d727e | ||
|
|
a787a13441 | ||
|
|
9de459c663 | ||
|
|
cc4c6e1a8e | ||
|
|
45806dfba0 | ||
|
|
65396dda1f | ||
|
|
87c2741305 | ||
|
|
f496579c3b | ||
|
|
d44affcbf5 | ||
|
|
2971c08ed4 | ||
|
|
c9c7aa9253 | ||
|
|
831df5a83c | ||
|
|
dfb40c0416 | ||
|
|
b5dd5aed17 | ||
|
|
f48415e717 | ||
|
|
2a74addee0 | ||
|
|
351a64937b | ||
|
|
562c891ef9 | ||
|
|
515e67092b | ||
|
|
b5152dcae5 | ||
|
|
f8e2341116 | ||
|
|
e7dab298f5 | ||
|
|
24c545db4a | ||
|
|
7a733a75db | ||
|
|
f2e0211c1b | ||
|
|
127df3c311 | ||
|
|
f374d6a102 | ||
|
|
312746fb14 | ||
|
|
9f5a57d819 | ||
|
|
dafad9b487 | ||
|
|
7332f5834a | ||
|
|
208339ddeb | ||
|
|
6f40dc2cca | ||
|
|
e1c27f1b22 | ||
|
|
ddf4ff7e9d | ||
|
|
be82a6521a | ||
|
|
3ea95bd0d8 | ||
|
|
6295c5f9da | ||
|
|
05941ccc37 | ||
|
|
82a9abfff8 | ||
|
|
d71434adfb | ||
|
|
b3d2aaddd0 | ||
|
|
a3f734028a | ||
|
|
93f1012663 | ||
|
|
e6da89627b | ||
|
|
598212a020 | ||
|
|
d6813a4bf0 | ||
|
|
b5f0246129 | ||
|
|
492e35aa74 | ||
|
|
17daa464ba | ||
|
|
fba475ee40 | ||
|
|
70126c8149 | ||
|
|
f11c18a6b9 | ||
|
|
a5ab73959e | ||
|
|
4f97befdde | ||
|
|
a2af2ac65e | ||
|
|
4d9ea52eae | ||
|
|
b6aceb1de8 | ||
|
|
5370afb1eb | ||
|
|
42a0417fba | ||
|
|
80c0a81718 | ||
|
|
fb694110d2 | ||
|
|
e898e89f60 | ||
|
|
39109a4a05 | ||
|
|
99bf346898 | ||
|
|
7688504289 | ||
|
|
9bec6a1806 | ||
|
|
b7b916f03a | ||
|
|
49e14b7ccb | ||
|
|
0cf7abfc47 | ||
|
|
647e34f401 | ||
|
|
9c3b17166f | ||
|
|
5420ce0959 | ||
|
|
9465244e0d | ||
|
|
08d406ee2b | ||
|
|
c21287a5ef | ||
|
|
671003ace6 | ||
|
|
580222758e | ||
|
|
447411fb58 | ||
|
|
d60bafbf05 | ||
|
|
b620e35ebe | ||
|
|
11f90b9e8b | ||
|
|
6b4079be3a | ||
|
|
44567f6687 | ||
|
|
c5e342a7a7 | ||
|
|
2dc285a947 | ||
|
|
8e6411bd31 | ||
|
|
fce510f50a | ||
|
|
f9ab55c38f | ||
|
|
533288d351 | ||
|
|
897c92e389 | ||
|
|
aacc001e5d | ||
|
|
28b5cd0158 | ||
|
|
5917275108 | ||
|
|
bac6bfd1b4 | ||
|
|
99c238d19d | ||
|
|
6fb636be89 | ||
|
|
84a060013d | ||
|
|
61bb240166 | ||
|
|
16abd3d318 | ||
|
|
6abbc72e11 | ||
|
|
c7a7760b51 | ||
|
|
fbee4e5e4f | ||
|
|
5dcde78ccc | ||
|
|
65c5dc1781 | ||
|
|
8aa1a88a23 | ||
|
|
1003ce0c0f | ||
|
|
a41bc4cb3d | ||
|
|
e1697310f6 | ||
|
|
d0f98b6c1f | ||
|
|
341db16874 | ||
|
|
5f0dc148ed | ||
|
|
b4ccb359ef | ||
|
|
0e333f7aa2 | ||
|
|
d610ee83db | ||
|
|
19db509403 | ||
|
|
f829c8f2ac | ||
|
|
69cf454a5a | ||
|
|
dc6c53ee47 | ||
|
|
7dfaca49ba | ||
|
|
a33ebe1ed6 | ||
|
|
686037be4d | ||
|
|
41f9925e2c | ||
|
|
05582d3ca9 | ||
|
|
70491b9e37 | ||
|
|
ec130877b7 | ||
|
|
6a38053c15 | ||
|
|
42bbdaeb4f | ||
|
|
e708dbc777 | ||
|
|
d1acacb757 | ||
|
|
59728c523f | ||
|
|
f95ab0def5 | ||
|
|
6e706c5f11 | ||
|
|
eb1b295116 | ||
|
|
f6e69b06d0 | ||
|
|
6127f5df11 | ||
|
|
a10f86dbaa | ||
|
|
361a6c58b7 | ||
|
|
f1ec4a2ac9 | ||
|
|
9e87792fb9 | ||
|
|
460eaa254a | ||
|
|
945f8b3a19 | ||
|
|
c83d1de974 | ||
|
|
1173aac945 | ||
|
|
6b6e628940 | ||
|
|
c53ba38c43 | ||
|
|
352879abce | ||
|
|
a6e14067ae | ||
|
|
64a7d49a29 | ||
|
|
23f76b0bea | ||
|
|
a3a3ef12e4 | ||
|
|
888e9425a4 | ||
|
|
63a2d407ee | ||
|
|
1371b11f32 | ||
|
|
2e8937a90f | ||
|
|
cb0016aa43 | ||
|
|
1574e53b0e | ||
|
|
bb6e710327 | ||
|
|
6ac56a25dc | ||
|
|
3473dda774 | ||
|
|
e58b4d8d89 | ||
|
|
54ee364130 | ||
|
|
56d8d58443 | ||
|
|
94db708f49 | ||
|
|
3049f2804a | ||
|
|
c5ea0e9149 | ||
|
|
0c29c4381c | ||
|
|
40d5481edd | ||
|
|
311f0127de | ||
|
|
81f3776aa7 | ||
|
|
dd2e475f00 | ||
|
|
67bb6c54bb | ||
|
|
56806313bd | ||
|
|
36ed7bdf14 | ||
|
|
237804efb6 | ||
|
|
2744a5dd86 | ||
|
|
c6207c0f59 | ||
|
|
45db1b55ec | ||
|
|
3215d93f53 | ||
|
|
c7218fba2b | ||
|
|
7f251b457a | ||
|
|
4ef91c5941 | ||
|
|
38f1d28585 | ||
|
|
3299348ebc | ||
|
|
e8b0a376eb | ||
|
|
5d6354ccbd | ||
|
|
426c8fb0bd | ||
|
|
8be7f81e34 | ||
|
|
d5bff95a1b | ||
|
|
c090191afb | ||
|
|
1424f2a43d | ||
|
|
bc7c53f668 | ||
|
|
917756a7bf | ||
|
|
776f34cff1 | ||
|
|
02c99786f9 | ||
|
|
0eb30c5286 | ||
|
|
32be53d618 | ||
|
|
2c8bb07be2 | ||
|
|
884db5aa32 | ||
|
|
e33b060734 | ||
|
|
14baad4625 | ||
|
|
37299237cb | ||
|
|
04cad71926 | ||
|
|
31a135c0f1 | ||
|
|
6fd9443c13 | ||
|
|
af1febfb8c | ||
|
|
eddcb7354f | ||
|
|
77b6cb8615 | ||
|
|
7ea471c1ac | ||
|
|
10c900a53f | ||
|
|
1631f22083 | ||
|
|
7e981a5376 | ||
|
|
45c8107ecd | ||
|
|
0cae79b4cc | ||
|
|
0363169084 | ||
|
|
bc74e2bb61 | ||
|
|
7e9f9c7528 | ||
|
|
dcc6c896de | ||
|
|
bb8660d3c5 | ||
|
|
6c3ccb6043 | ||
|
|
a2883ac318 | ||
|
|
2bef914e7c | ||
|
|
eb9bfd52b1 | ||
|
|
a5be31fadc | ||
|
|
8e0a2012dc | ||
|
|
d5b6c850f9 | ||
|
|
e75d43d3f0 | ||
|
|
eb54168228 | ||
|
|
dfe628c53d | ||
|
|
253b013c2a | ||
|
|
ea34196d8d | ||
|
|
967c478846 | ||
|
|
bb4321838e | ||
|
|
dc4ddcb3fe | ||
|
|
163f45076e | ||
|
|
c4ad9d37ab | ||
|
|
921ecc4d56 | ||
|
|
786682c0b2 | ||
|
|
c62bd8b332 | ||
|
|
cc7077624c | ||
|
|
4f9dc960c6 | ||
|
|
cd0272ee81 | ||
|
|
f177b945b7 | ||
|
|
63ef16fae6 | ||
|
|
cc08da2ad8 | ||
|
|
fc826caf18 | ||
|
|
bdd3e93de2 | ||
|
|
40810a5c1d | ||
|
|
3af45a2ed0 | ||
|
|
60b9d5c704 | ||
|
|
8cb921f73b | ||
|
|
347ef5188b | ||
|
|
4ca2398bc1 | ||
|
|
d0cd890e51 | ||
|
|
565faf723b | ||
|
|
891d476de1 | ||
|
|
7753551671 | ||
|
|
e46e80b8c1 | ||
|
|
c9925b7f6d | ||
|
|
0b5eb1f00e | ||
|
|
9d5fb31c4b | ||
|
|
d4ec063cec | ||
|
|
fd467c2f07 | ||
|
|
c864c8a982 | ||
|
|
9691141fed | ||
|
|
eee20de116 | ||
|
|
13d6b517d8 | ||
|
|
bb806d35e0 | ||
|
|
2c03266061 | ||
|
|
d34a872230 | ||
|
|
1630868d06 | ||
|
|
2d90fa3021 | ||
|
|
0d418fe837 | ||
|
|
152c0180df | ||
|
|
bd711517f3 | ||
|
|
65b956d6a8 | ||
|
|
05076697ec | ||
|
|
fb90fe780c | ||
|
|
e7f8f50e82 | ||
|
|
d2a93894f5 | ||
|
|
1122df15e2 | ||
|
|
a33159a021 | ||
|
|
4c5e493b1b | ||
|
|
b8af24071e | ||
|
|
dc8e0a6496 | ||
|
|
fd4676d434 | ||
|
|
e680900f50 | ||
|
|
a20054c90c | ||
|
|
f125c20cf2 | ||
|
|
8a47e4254f | ||
|
|
e50f663842 | ||
|
|
ebcd26fd51 | ||
|
|
48e3e912f3 | ||
|
|
cb91828e5e | ||
|
|
40c327eeae | ||
|
|
2fd1594637 | ||
|
|
4d9441ef99 | ||
|
|
4dbe5d35a6 | ||
|
|
83dfb5b803 | ||
|
|
79d617da38 | ||
|
|
db39d0197a | ||
|
|
06bf505093 | ||
|
|
8aee84f842 | ||
|
|
7c25750e3a | ||
|
|
0ad4ee0c91 | ||
|
|
e67d489737 | ||
|
|
e446d78410 | ||
|
|
60e4b882ef | ||
|
|
3b9a4938cb | ||
|
|
06315b8c8c | ||
|
|
83398a7ca0 | ||
|
|
28d26e9f45 | ||
|
|
b98dbe10d2 | ||
|
|
a0a02ce2a8 | ||
|
|
49ebc23a6d | ||
|
|
bd4b044748 | ||
|
|
cc28c3eb09 | ||
|
|
5facb759ae | ||
|
|
ec8308058b | ||
|
|
76ed67ad35 | ||
|
|
60d39e9f33 | ||
|
|
c6ffbb6c9d | ||
|
|
45926a1142 | ||
|
|
1eb22a27a6 | ||
|
|
683a0e072c | ||
|
|
9acf72df33 | ||
|
|
ef290a9a78 | ||
|
|
9f1cf17c99 | ||
|
|
9edd098da5 | ||
|
|
8a1df42309 | ||
|
|
3c05759556 | ||
|
|
2faa2654cb | ||
|
|
68628fc008 | ||
|
|
58f40e95e5 | ||
|
|
865823cace | ||
|
|
2c1e66e9b3 | ||
|
|
df2b7bd847 | ||
|
|
8567d35cdc | ||
|
|
c22a64e37f | ||
|
|
0c95668e7a | ||
|
|
209a82bdc7 | ||
|
|
5a8c76ebdc | ||
|
|
f32565b4af | ||
|
|
edd03717d2 | ||
|
|
68a4fb29ed | ||
|
|
fd1c7b4fc7 | ||
|
|
ee4424961e | ||
|
|
f70ef1a593 | ||
|
|
d5d3f1f46b | ||
|
|
157ac17be1 | ||
|
|
840c395a11 | ||
|
|
db8a5912b7 | ||
|
|
c77b0614d0 | ||
|
|
03548807fc | ||
|
|
b2b43049c1 | ||
|
|
03b3245eeb | ||
|
|
fa99a1ad43 | ||
|
|
c77f48388a | ||
|
|
373735f96a | ||
|
|
ad1176865e | ||
|
|
086b6059b9 | ||
|
|
0023473068 | ||
|
|
e5b46b5024 | ||
|
|
94bca218c9 | ||
|
|
e0610f7997 | ||
|
|
dd8083dbdc | ||
|
|
56a7553b2d | ||
|
|
f5dfca7f27 | ||
|
|
c4383196fa | ||
|
|
4d2ab5bc8f | ||
|
|
866933bf6f | ||
|
|
5f79be6c9b | ||
|
|
4d44f4179e | ||
|
|
7e5895c5fd | ||
|
|
20c77891d8 | ||
|
|
3dfea214d4 | ||
|
|
822bef12c7 | ||
|
|
39a75bae90 | ||
|
|
763bd638e7 | ||
|
|
8f2ee88491 | ||
|
|
3d9f117beb | ||
|
|
c4d1eb492e | ||
|
|
6f91367d6c | ||
|
|
111dcda103 | ||
|
|
71db9fa55c | ||
|
|
54f4f0e90d | ||
|
|
29d9cfbe31 | ||
|
|
0fa3214971 | ||
|
|
803fbb4d94 | ||
|
|
000617b876 | ||
|
|
17492f6b62 | ||
|
|
3c2dccd51c | ||
|
|
2a750dfedc | ||
|
|
e368be0565 | ||
|
|
48ced47874 | ||
|
|
89a3d56b15 | ||
|
|
1a5ca65eed | ||
|
|
586744070b | ||
|
|
66cab5dfb4 | ||
|
|
6b742968b1 | ||
|
|
46c8527123 | ||
|
|
381ce9530c | ||
|
|
634191203f | ||
|
|
da3375bbe4 | ||
|
|
5d61074c85 | ||
|
|
a22b817ee5 | ||
|
|
dfc6d86a58 | ||
|
|
f6925480f2 | ||
|
|
c73a2d91ae | ||
|
|
86f4f50b3e | ||
|
|
02c2825b2c | ||
|
|
1fdfc9270e | ||
|
|
41aa556764 | ||
|
|
52e5b94bd5 | ||
|
|
1e6165e04b | ||
|
|
315b8cc0ff | ||
|
|
fd9f7895f6 | ||
|
|
b3bc28efef | ||
|
|
d26b1bd504 | ||
|
|
2437d82554 | ||
|
|
43f284034c | ||
|
|
215573a720 | ||
|
|
bf2cca90be | ||
|
|
bd9808827f | ||
|
|
839a3ba62b | ||
|
|
000ed9affc | ||
|
|
b8ca1323d0 | ||
|
|
969f9a15f6 | ||
|
|
13c81177c5 | ||
|
|
ba73154c36 | ||
|
|
efcf7d4074 | ||
|
|
256d7198f9 | ||
|
|
81a40703bd | ||
|
|
55864b1ba7 | ||
|
|
e4c985e8dd | ||
|
|
4aca01a7b6 | ||
|
|
21d833d2a3 | ||
|
|
392ae7dee6 | ||
|
|
c8bd107218 | ||
|
|
99b0c774ac | ||
|
|
2440c240ce | ||
|
|
dc6f8378e2 | ||
|
|
32587d7569 | ||
|
|
b8b041b51a | ||
|
|
c2279a28d3 | ||
|
|
f0d6dc4bee | ||
|
|
9b048f4c99 | ||
|
|
bd1e1fab1a | ||
|
|
6f01b5aba3 | ||
|
|
5037447a0a | ||
|
|
9f035d40a9 | ||
|
|
7d5372f57d | ||
|
|
d5abc3fc85 | ||
|
|
cf143972b6 | ||
|
|
344cc95e65 | ||
|
|
b9ba657dc4 | ||
|
|
4077734c3c | ||
|
|
65dca0025b | ||
|
|
f18e8ae33c | ||
|
|
9b64e648e5 | ||
|
|
7792d67b47 | ||
|
|
f060582f0b | ||
|
|
fd4f5592a4 | ||
|
|
d761ec4def | ||
|
|
ace679f13b | ||
|
|
8da8425907 | ||
|
|
3efdbe9420 | ||
|
|
d50290e9dd | ||
|
|
1b2dab1dff | ||
|
|
d5e4995777 | ||
|
|
bc74a446a4 | ||
|
|
93d31e76d8 | ||
|
|
25f509ce74 | ||
|
|
0cb5e4e82d | ||
|
|
4e5020f83f | ||
|
|
baea915935 | ||
|
|
1444b62777 | ||
|
|
2cc7a8ccbd | ||
|
|
33c7c32c53 | ||
|
|
4a0a19e253 | ||
|
|
e98cf71b67 | ||
|
|
a61ffef909 | ||
|
|
1f155cf301 | ||
|
|
e5b393fe4d | ||
|
|
6d9048a873 | ||
|
|
1846543873 | ||
|
|
3a9c248f11 | ||
|
|
b402692c72 | ||
|
|
3079d9cc49 | ||
|
|
1983a74cb1 | ||
|
|
de19bb0446 | ||
|
|
7947cecd93 | ||
|
|
7983923b16 | ||
|
|
1d3fa8e93b | ||
|
|
6046528e0b | ||
|
|
2b4c408333 | ||
|
|
1f129744fd | ||
|
|
c283563542 | ||
|
|
cc9ccb3f83 | ||
|
|
465a87659e |
@ -1,2 +1,4 @@
|
||||
[alias]
|
||||
xtask = "run --package xtask --"
|
||||
xfmt = "xtask fmt-packages"
|
||||
qa = "xtask run-example qa-test"
|
||||
31
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
31
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ["bug", "status:needs-attention"]
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Bug description
|
||||
|
||||
<!-- A clear and concise description of what the bug is. -->
|
||||
|
||||
## To Reproduce
|
||||
|
||||
<!-- Steps to reproduce the behavior. -->
|
||||
1. ...
|
||||
2. ...
|
||||
|
||||
<!-- Please share the minimal repro of the issue where the bug can be reproduced. -->
|
||||
|
||||
<!-- Make sure you are able to reproduce the bug in the `main` branch, too. -->
|
||||
|
||||
## Expected behavior
|
||||
|
||||
<!-- A clear and concise description of what you expected to happen. Attach screenshots if needed. -->
|
||||
|
||||
## Environment
|
||||
|
||||
- Target device: [e.g. ESP32-S3] <!-- Use `espflash board-info` to get the target device iformation. -->
|
||||
- Crate name and version: [e.g. esp-hal 0.20.0]
|
||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Ask questions in Matrix channel (Recommended)
|
||||
url: https://matrix.to/#/#esp-rs:matrix.org
|
||||
about: Ask any questions directly in our Matrix channel.
|
||||
- name: Ask questions in GitHub Discussions
|
||||
url: https://github.com/esp-rs/esp-hal/discussions/new
|
||||
about: Post your questions and engage in discussions via GitHub.
|
||||
24
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
24
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ["enhancement", "status:needs-attention"]
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Motivations
|
||||
|
||||
<!-- If your feature request is related to a problem, please describe it. -->
|
||||
|
||||
## Solution
|
||||
|
||||
<!-- Describe the solution you'd like. -->
|
||||
|
||||
## Alternatives
|
||||
|
||||
<!-- Describe any alternative solutions or features you've considered. -->
|
||||
|
||||
## Additional context
|
||||
|
||||
<!-- Add any other context or screenshots about the feature request here. -->
|
||||
30
.github/PULL_REQUEST_TEMPLATE.md
vendored
30
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,18 +1,22 @@
|
||||
## Thank you!
|
||||
## Thank you for your contribution!
|
||||
|
||||
Thank you for your contribution.
|
||||
Please make sure that your submission includes the following:
|
||||
We appreciate the time and effort you've put into this pull request.
|
||||
To help us review it efficiently, please ensure you've gone through the following checklist:
|
||||
|
||||
### Must
|
||||
### Submission Checklist 📝
|
||||
- [ ] I have updated existing examples or added new ones (if applicable).
|
||||
- [ ] I have used `cargo xtask fmt-packages` command to ensure that all changed code is formatted correctly.
|
||||
- [ ] My changes were added to the [`CHANGELOG.md`](https://github.com/esp-rs/esp-hal/blob/main/esp-hal/CHANGELOG.md) in the **_proper_** section.
|
||||
- [ ] I have added necessary changes to user code to the [Migration Guide](https://github.com/esp-rs/esp-hal/blob/main/esp-hal/MIGRATING-0.21.md).
|
||||
- [ ] My changes are in accordance to the [esp-rs API guidelines](https://github.com/esp-rs/esp-hal/blob/main/documentation/API-GUIDELINES.md)
|
||||
|
||||
- [ ] The code compiles without `errors` or `warnings`.
|
||||
- [ ] All examples work.
|
||||
- [ ] `cargo fmt` was run.
|
||||
- [ ] Your changes were added to the `CHANGELOG.md` in the proper section.
|
||||
- [ ] You updated existing examples or added examples (if applicable).
|
||||
- [ ] Added examples are checked in CI
|
||||
#### Extra:
|
||||
- [ ] I have read the [CONTRIBUTING.md guide](https://github.com/esp-rs/esp-hal/blob/main/documentation/CONTRIBUTING.md) and followed its instructions.
|
||||
|
||||
### Nice to have
|
||||
### Pull Request Details 📖
|
||||
|
||||
- [ ] You add a description of your work to this PR.
|
||||
- [ ] You added proper docs for your newly added features and code.
|
||||
#### Description
|
||||
Please provide a clear and concise description of your changes, including the motivation behind these changes. The context is crucial for the reviewers.
|
||||
|
||||
#### Testing
|
||||
Describe how you tested your changes.
|
||||
|
||||
68
.github/actions/check-esp-hal/action.yml
vendored
Normal file
68
.github/actions/check-esp-hal/action.yml
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
name: Build and Check
|
||||
description: Build and check the esp-hal and esp-lp-hal pacakges for a specified device
|
||||
inputs:
|
||||
device:
|
||||
description: "Device SOC"
|
||||
required: true
|
||||
target:
|
||||
description: "Target"
|
||||
required: true
|
||||
toolchain:
|
||||
description: "Toolchain channel"
|
||||
required: true
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Set up cargo environment
|
||||
shell: bash
|
||||
run: |
|
||||
# Convert the target triple from kebab-case to SCREAMING_SNAKE_CASE:
|
||||
big_target=$(echo "${{ matrix.device.target }}" | tr [:lower:] [:upper:] | tr '-' '_')
|
||||
# Set the *target specific* RUSTFLAGS for the current device:
|
||||
echo "CARGO_TARGET_${big_target}_RUSTFLAGS=-Dwarnings" >> $GITHUB_ENV
|
||||
# Linting toolchain (stable cant build documentation)
|
||||
if [ "${{ inputs.toolchain }}" == "nightly" ]; then
|
||||
echo "LINTING_TOOLCHAIN=+nightly" >> $GITHUB_ENV
|
||||
else
|
||||
echo "LINTING_TOOLCHAIN=+esp" >> $GITHUB_ENV
|
||||
fi
|
||||
# Clippy and docs checks
|
||||
- name: Clippy
|
||||
shell: bash
|
||||
run: cargo $LINTING_TOOLCHAIN xtask lint-packages --chips ${{ inputs.device }}
|
||||
- name: Check doc-tests
|
||||
shell: bash
|
||||
run: cargo $LINTING_TOOLCHAIN xtask run-doc-test esp-hal ${{ inputs.device }}
|
||||
- name: Check documentation
|
||||
shell: bash
|
||||
run: cargo $LINTING_TOOLCHAIN xtask build-documentation --packages esp-hal --chips ${{ inputs.device }}
|
||||
# Build all supported examples for the low-power core first (if present):
|
||||
- name: Build prerequisite examples (esp-lp-hal)
|
||||
shell: bash
|
||||
if: contains(fromJson('["esp32c6", "esp32s2", "esp32s3"]'), inputs.device)
|
||||
run: cargo +${{ inputs.toolchain }} xtask build-examples esp-lp-hal ${{ inputs.device }}
|
||||
- name: Check esp-lp-hal documentation
|
||||
shell: bash
|
||||
if: contains(fromJson('["esp32c6", "esp32s2", "esp32s3"]'), inputs.device)
|
||||
run: cargo $LINTING_TOOLCHAIN xtask build-documentation --packages esp-lp-hal --chips ${{ inputs.device }}
|
||||
# Make sure we're able to build the HAL without the default features
|
||||
# enabled:
|
||||
- name: Build (no features)
|
||||
shell: bash
|
||||
run: |
|
||||
cargo xtask build-package \
|
||||
--no-default-features \
|
||||
--toolchain=${{ inputs.toolchain }} \
|
||||
--features=${{ inputs.device }} \
|
||||
--target=${{ inputs.target }} \
|
||||
esp-hal
|
||||
- name: Build (examples)
|
||||
env:
|
||||
CI: 1
|
||||
shell: bash
|
||||
run: cargo +${{ inputs.toolchain }} xtask build-examples esp-hal ${{ inputs.device }} --debug
|
||||
- name: Build (qa-test)
|
||||
env:
|
||||
CI: 1
|
||||
shell: bash
|
||||
run: cargo +${{ inputs.toolchain }} xtask build-examples qa-test ${{ inputs.device }} --debug
|
||||
25
.github/scripts/build_docs.sh
vendored
25
.github/scripts/build_docs.sh
vendored
@ -1,25 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Extract the package version from `esp-hal` (using arcane methods):
|
||||
PKG_VERSION=$(
|
||||
cargo metadata --format-version=1 --no-deps --manifest-path=esp-hal/Cargo.toml \
|
||||
| jq -r '.packages[] | select(.name=="esp-hal") | .version'
|
||||
)
|
||||
|
||||
# Build the documentation for each supported cheap, namespacing by
|
||||
# package version and chip:
|
||||
CHIPS=("esp32" "esp32c2" "esp32c3" "esp32c6" "esp32h2" "esp32p4" "esp32s2" "esp32s3")
|
||||
|
||||
for CHIP in "${CHIPS[@]}"; do
|
||||
cargo xtask build-documentation \
|
||||
--output-path="docs/esp-hal/$PKG_VERSION"/"$CHIP"/ \
|
||||
esp-hal \
|
||||
"$CHIP"
|
||||
done
|
||||
|
||||
# Copy any additional resources (such as the index and our logo)
|
||||
# to the location of the built documentation as well:
|
||||
cp resources/esp-rs.svg docs/
|
||||
cp resources/index.html docs/
|
||||
164
.github/workflows/changelog.yml
vendored
164
.github/workflows/changelog.yml
vendored
@ -2,8 +2,12 @@ name: Changelog check
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
# We will not track changes for the `xtask` package.
|
||||
# We will not track changes for the following packages/directories.
|
||||
paths-ignore:
|
||||
- "/examples/"
|
||||
- "/extras/"
|
||||
- "/hil-tests/"
|
||||
- "/resources/"
|
||||
- "/xtask/"
|
||||
# Run on labeled/unlabeled in addition to defaults to detect
|
||||
# adding/removing skip-changelog labels.
|
||||
@ -15,10 +19,162 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- uses: dangoslen/changelog-enforcer@v3
|
||||
- name: Check which package is modified
|
||||
uses: dorny/paths-filter@v3
|
||||
id: changes
|
||||
with:
|
||||
filters: |
|
||||
esp-alloc:
|
||||
- 'esp-alloc/**'
|
||||
esp-backtrace:
|
||||
- 'esp-backtrace/**'
|
||||
esp-build:
|
||||
- 'esp-build/**'
|
||||
esp-config:
|
||||
- 'esp-config/**'
|
||||
esp-hal:
|
||||
- 'esp-hal/**'
|
||||
esp-hal-embassy:
|
||||
- 'esp-hal-embassy/**'
|
||||
esp-hal-procmacros:
|
||||
- 'esp-hal-procmacros/**'
|
||||
esp-ieee802154:
|
||||
- 'esp-ieee802154/**'
|
||||
esp-lp-hal:
|
||||
- 'esp-lp-hal/**'
|
||||
esp-metadata:
|
||||
- 'esp-metadata/**'
|
||||
esp-println:
|
||||
- 'esp-println/**'
|
||||
esp-riscv-rt:
|
||||
- 'esp-riscv-rt/**'
|
||||
esp-storage:
|
||||
- 'esp-storage/**'
|
||||
esp-wifi:
|
||||
- 'esp-wifi/**'
|
||||
xtensa-lx:
|
||||
- 'xtensa-lx/**'
|
||||
xtensa-lx-rt:
|
||||
- 'xtensa-lx-rt/**'
|
||||
|
||||
- name: Check that changelog updated (esp-alloc)
|
||||
if: steps.changes.outputs.esp-alloc == 'true'
|
||||
uses: dangoslen/changelog-enforcer@v3
|
||||
with:
|
||||
changeLogPath: esp-alloc/CHANGELOG.md
|
||||
skipLabels: "skip-changelog"
|
||||
missingUpdateErrorMessage: "Please add a changelog entry in the esp-alloc/CHANGELOG.md file."
|
||||
|
||||
- name: Check that changelog updated (esp-backtrace)
|
||||
if: steps.changes.outputs.esp-backtrace == 'true'
|
||||
uses: dangoslen/changelog-enforcer@v3
|
||||
with:
|
||||
changeLogPath: esp-backtrace/CHANGELOG.md
|
||||
skipLabels: "skip-changelog"
|
||||
missingUpdateErrorMessage: "Please add a changelog entry in the esp-backtrace/CHANGELOG.md file."
|
||||
|
||||
- name: Check that changelog updated (esp-build)
|
||||
if: steps.changes.outputs.esp-build == 'true'
|
||||
uses: dangoslen/changelog-enforcer@v3
|
||||
with:
|
||||
changeLogPath: esp-build/CHANGELOG.md
|
||||
skipLabels: "skip-changelog"
|
||||
missingUpdateErrorMessage: "Please add a changelog entry in the esp-build/CHANGELOG.md file."
|
||||
|
||||
- name: Check that changelog updated (esp-config)
|
||||
if: steps.changes.outputs.esp-config == 'true'
|
||||
uses: dangoslen/changelog-enforcer@v3
|
||||
with:
|
||||
changeLogPath: esp-config/CHANGELOG.md
|
||||
skipLabels: "skip-changelog"
|
||||
missingUpdateErrorMessage: "Please add a changelog entry in the esp-config/CHANGELOG.md file."
|
||||
|
||||
- name: Check that changelog updated (esp-hal)
|
||||
if: steps.changes.outputs.esp-hal == 'true'
|
||||
uses: dangoslen/changelog-enforcer@v3
|
||||
with:
|
||||
changeLogPath: esp-hal/CHANGELOG.md
|
||||
skipLabels: "skip-changelog"
|
||||
missingUpdateErrorMessage: "Please add a changelog entry in the CHANGELOG.md file."
|
||||
missingUpdateErrorMessage: "Please add a changelog entry in the esp-hal/CHANGELOG.md file."
|
||||
|
||||
- name: Check that changelog updated (esp-hal-embassy)
|
||||
if: steps.changes.outputs.esp-hal-embassy == 'true'
|
||||
uses: dangoslen/changelog-enforcer@v3
|
||||
with:
|
||||
changeLogPath: esp-hal-embassy/CHANGELOG.md
|
||||
skipLabels: "skip-changelog"
|
||||
missingUpdateErrorMessage: "Please add a changelog entry in the esp-hal-embassy/CHANGELOG.md file."
|
||||
|
||||
- name: Check that changelog updated (esp-hal-procmacros)
|
||||
if: steps.changes.outputs.esp-hal-procmacros == 'true'
|
||||
uses: dangoslen/changelog-enforcer@v3
|
||||
with:
|
||||
changeLogPath: esp-hal-procmacros/CHANGELOG.md
|
||||
skipLabels: "skip-changelog"
|
||||
missingUpdateErrorMessage: "Please add a changelog entry in the esp-hal-procmacros/CHANGELOG.md file."
|
||||
|
||||
- name: Check that changelog updated (esp-ieee802154)
|
||||
if: steps.changes.outputs.esp-ieee802154 == 'true'
|
||||
uses: dangoslen/changelog-enforcer@v3
|
||||
with:
|
||||
changeLogPath: esp-ieee802154/CHANGELOG.md
|
||||
skipLabels: "skip-changelog"
|
||||
missingUpdateErrorMessage: "Please add a changelog entry in the esp-ieee802154/CHANGELOG.md file."
|
||||
|
||||
- name: Check that changelog updated (esp-lp-hal)
|
||||
if: steps.changes.outputs.esp-lp-hal == 'true'
|
||||
uses: dangoslen/changelog-enforcer@v3
|
||||
with:
|
||||
changeLogPath: esp-lp-hal/CHANGELOG.md
|
||||
skipLabels: "skip-changelog"
|
||||
missingUpdateErrorMessage: "Please add a changelog entry in the esp-lp-hal/CHANGELOG.md file."
|
||||
|
||||
- name: Check that changelog updated (esp-println)
|
||||
if: steps.changes.outputs.esp-println == 'true'
|
||||
uses: dangoslen/changelog-enforcer@v3
|
||||
with:
|
||||
changeLogPath: esp-println/CHANGELOG.md
|
||||
skipLabels: "skip-changelog"
|
||||
missingUpdateErrorMessage: "Please add a changelog entry in the esp-println/CHANGELOG.md file."
|
||||
|
||||
- name: Check that changelog updated (esp-riscv-rt)
|
||||
if: steps.changes.outputs.esp-riscv-rt == 'true'
|
||||
uses: dangoslen/changelog-enforcer@v3
|
||||
with:
|
||||
changeLogPath: esp-riscv-rt/CHANGELOG.md
|
||||
skipLabels: "skip-changelog"
|
||||
missingUpdateErrorMessage: "Please add a changelog entry in the esp-riscv-rt/CHANGELOG.md file."
|
||||
|
||||
- name: Check that changelog updated (esp-storage)
|
||||
if: steps.changes.outputs.esp-storage == 'true'
|
||||
uses: dangoslen/changelog-enforcer@v3
|
||||
with:
|
||||
changeLogPath: esp-storage/CHANGELOG.md
|
||||
skipLabels: "skip-changelog"
|
||||
missingUpdateErrorMessage: "Please add a changelog entry in the esp-storage/CHANGELOG.md file."
|
||||
|
||||
- name: Check that changelog updated (esp-wifi)
|
||||
if: steps.changes.outputs.esp-wifi == 'true'
|
||||
uses: dangoslen/changelog-enforcer@v3
|
||||
with:
|
||||
changeLogPath: esp-wifi/CHANGELOG.md
|
||||
skipLabels: "skip-changelog"
|
||||
missingUpdateErrorMessage: "Please add a changelog entry in the esp-wifi/CHANGELOG.md file."
|
||||
|
||||
- name: Check that changelog updated (xtensa-lx)
|
||||
if: steps.changes.outputs.xtensa-lx == 'true'
|
||||
uses: dangoslen/changelog-enforcer@v3
|
||||
with:
|
||||
changeLogPath: xtensa-lx/CHANGELOG.md
|
||||
skipLabels: "skip-changelog"
|
||||
missingUpdateErrorMessage: "Please add a changelog entry in the xtensa-lx/CHANGELOG.md file."
|
||||
|
||||
- name: Check that changelog updated (xtensa-lx-rt)
|
||||
if: steps.changes.outputs.xtensa-lx-rt == 'true'
|
||||
uses: dangoslen/changelog-enforcer@v3
|
||||
with:
|
||||
changeLogPath: xtensa-lx-rt/CHANGELOG.md
|
||||
skipLabels: "skip-changelog"
|
||||
missingUpdateErrorMessage: "Please add a changelog entry in the xtensa-lx-rt/CHANGELOG.md file."
|
||||
|
||||
290
.github/workflows/ci.yml
vendored
290
.github/workflows/ci.yml
vendored
@ -7,30 +7,25 @@
|
||||
# 1.) In the 'esp-hal' job, add the name of the chip to the `matrix.soc` array.
|
||||
# 1a.) If the device has a low-power core (which is supported in
|
||||
# `esp-lp-hal`), then update the `if` condition to build prerequisites.
|
||||
# 2.) In the 'msrv-riscv' job, add checks as needed for the new chip.
|
||||
# 3.) In the 'clippy-riscv' job, add checks as needed for the new chip.
|
||||
# 3.) In the 'rustfmt' job, add checks as needed for the new chip.
|
||||
# 2.) In the 'msrv' job, add checks as needed for the new chip.
|
||||
|
||||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "**/CHANGELOG.md"
|
||||
- "**/README.md"
|
||||
push:
|
||||
branches-ignore:
|
||||
- "gh-readonly-queue/**"
|
||||
paths-ignore:
|
||||
- "**/CHANGELOG.md"
|
||||
- "**/README.md"
|
||||
- "main"
|
||||
merge_group:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
MSRV: "1.76.0"
|
||||
MSRV: "1.83.0"
|
||||
RUSTDOCFLAGS: -Dwarnings
|
||||
DEFMT_LOG: trace
|
||||
|
||||
# Cancel any currently running workflows from the same PR, branch, or
|
||||
# tag when a new workflow is triggered.
|
||||
@ -45,230 +40,130 @@ jobs:
|
||||
# Build Packages
|
||||
|
||||
esp-hal:
|
||||
name: esp-hal (${{ matrix.device.soc }})
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
SSID: SSID
|
||||
PASSWORD: PASSWORD
|
||||
STATIC_IP: 1.1.1.1
|
||||
GATEWAY_IP: 1.1.1.1
|
||||
HOST_IP: 1.1.1.1
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
soc: [
|
||||
device: [
|
||||
# RISC-V devices:
|
||||
"esp32c2",
|
||||
"esp32c3",
|
||||
"esp32c6",
|
||||
"esp32h2",
|
||||
"esp32p4",
|
||||
{ soc: "esp32c2", target: "riscv32imc-unknown-none-elf", toolchain: "stable" },
|
||||
{ soc: "esp32c3", target: "riscv32imc-unknown-none-elf", toolchain: "stable" },
|
||||
{ soc: "esp32c6", target: "riscv32imac-unknown-none-elf", toolchain: "stable" },
|
||||
{ soc: "esp32h2", target: "riscv32imac-unknown-none-elf", toolchain: "stable" },
|
||||
# Xtensa devices:
|
||||
"esp32",
|
||||
"esp32s2",
|
||||
"esp32s3",
|
||||
{ soc: "esp32", target: "xtensa-esp32-none-elf", toolchain: "esp" },
|
||||
{ soc: "esp32s2", target: "xtensa-esp32s2-none-elf", toolchain: "esp" },
|
||||
{ soc: "esp32s3", target: "xtensa-esp32s3-none-elf", toolchain: "esp" },
|
||||
]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# Install the Rust toolchain for RISC-V devices:
|
||||
- if: ${{ !contains(fromJson('["esp32", "esp32s2", "esp32s3"]'), matrix.soc) }}
|
||||
uses: dtolnay/rust-toolchain@v1
|
||||
with:
|
||||
target: riscv32imc-unknown-none-elf,riscv32imac-unknown-none-elf,riscv32imafc-unknown-none-elf
|
||||
toolchain: nightly
|
||||
components: rust-src
|
||||
# Install the Rust toolchain for Xtensa devices:
|
||||
- if: contains(fromJson('["esp32", "esp32s2", "esp32s3"]'), matrix.soc)
|
||||
uses: esp-rs/xtensa-toolchain@v1.5
|
||||
- uses: esp-rs/xtensa-toolchain@v1.5
|
||||
with:
|
||||
buildtargets: ${{ matrix.soc }}
|
||||
default: true
|
||||
ldproxy: false
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
# Build all supported examples for the low-power core first (if present):
|
||||
- if: contains(fromJson('["esp32c6", "esp32s2", "esp32s3"]'), matrix.soc)
|
||||
name: Build prerequisites (esp-lp-hal)
|
||||
run: cargo xtask build-examples esp-lp-hal ${{ matrix.soc }}
|
||||
|
||||
# Build all supported examples for the specified device:
|
||||
- name: Build examples
|
||||
run: cargo xtask build-examples esp-hal ${{ matrix.soc }}
|
||||
# Ensure we can build the documentation for the specified device:
|
||||
- name: Build documentation
|
||||
run: cargo xtask build-documentation esp-hal ${{ matrix.soc }}
|
||||
|
||||
esp-lp-hal:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
soc: ["esp32c6", "esp32s2", "esp32s3"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# Install the Rust toolchain for RISC-V devices:
|
||||
version: 1.83.0.1
|
||||
# Install the Rust stable toolchain for RISC-V devices:
|
||||
- uses: dtolnay/rust-toolchain@v1
|
||||
with:
|
||||
target: riscv32imc-unknown-none-elf,riscv32imac-unknown-none-elf
|
||||
toolchain: nightly
|
||||
toolchain: stable
|
||||
components: rust-src
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
# Build all supported examples for the specified device:
|
||||
- name: Build examples
|
||||
run: cargo xtask build-examples esp-lp-hal ${{ matrix.soc }}
|
||||
# Ensure we can build the documentation for the specified device:
|
||||
- name: Build documentation
|
||||
run: cargo xtask build-documentation esp-lp-hal ${{ matrix.soc }}
|
||||
- name: Build and Check
|
||||
uses: ./.github/actions/check-esp-hal
|
||||
with:
|
||||
device: ${{ matrix.device.soc }}
|
||||
target: ${{ matrix.device.target }}
|
||||
toolchain: ${{ matrix.device.toolchain }}
|
||||
|
||||
esp-riscv-rt:
|
||||
extras:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@v1
|
||||
with:
|
||||
target: riscv32imc-unknown-none-elf,riscv32imac-unknown-none-elf,riscv32imafc-unknown-none-elf
|
||||
toolchain: nightly
|
||||
components: rust-src
|
||||
toolchain: stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
# Build for all RISC-V targets (no features):
|
||||
- name: Build esp-riscv-rt (riscv32imc, no features)
|
||||
run: cd esp-riscv-rt/ && cargo build -Zbuild-std=core --target=riscv32imc-unknown-none-elf
|
||||
- name: Build esp-riscv-rt (riscv32imac, no features)
|
||||
run: cd esp-riscv-rt/ && cargo build -Zbuild-std=core --target=riscv32imac-unknown-none-elf
|
||||
- name: Build esp-riscv-rt (riscv32imafc, no features)
|
||||
run: cd esp-riscv-rt/ && cargo build -Zbuild-std=core --target=riscv32imafc-unknown-none-elf
|
||||
# Build for all RISC-V targets (all features):
|
||||
- name: Build esp-riscv-rt (riscv32imc, all features)
|
||||
run: cd esp-riscv-rt/ && cargo build -Zbuild-std=core --target=riscv32imc-unknown-none-elf --features=ci
|
||||
- name: Build esp-riscv-rt (riscv32imac, all features)
|
||||
run: cd esp-riscv-rt/ && cargo build -Zbuild-std=core --target=riscv32imac-unknown-none-elf --features=ci
|
||||
- name: Build esp-riscv-rt (riscv32imafc, all features)
|
||||
run: cd esp-riscv-rt/ && cargo build -Zbuild-std=core --target=riscv32imafc-unknown-none-elf --features=ci
|
||||
# Ensure documentation can be built
|
||||
- name: rustdoc
|
||||
run: cd esp-riscv-rt/ && cargo doc
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get update && sudo apt-get -y install musl-tools libudev-dev pkg-config
|
||||
# Build the extra crates
|
||||
- name: Build the bench-server
|
||||
run: cd extras/bench-server && cargo build
|
||||
- name: Build esp-wifishark
|
||||
run: cd extras/esp-wifishark && cargo build
|
||||
- name: Build ieee802154-sniffer
|
||||
run: cd extras/ieee802154-sniffer && cargo build
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# MSRV
|
||||
|
||||
msrv-riscv:
|
||||
msrv:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
RUSTC_BOOTSTRAP: 1
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@v1
|
||||
with:
|
||||
target: riscv32imc-unknown-none-elf,riscv32imac-unknown-none-elf
|
||||
toolchain: ${{ env.MSRV }}
|
||||
components: rust-src
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
# TODO:
|
||||
# - Enable additional (all?) features for MSRV check
|
||||
|
||||
# Verify the MSRV for all RISC-V chips.
|
||||
- name: msrv (esp-hal)
|
||||
run: |
|
||||
cargo xtask build-package --features=esp32c2 --target=riscv32imc-unknown-none-elf esp-hal
|
||||
cargo xtask build-package --features=esp32c3 --target=riscv32imc-unknown-none-elf esp-hal
|
||||
cargo xtask build-package --features=esp32c6 --target=riscv32imac-unknown-none-elf esp-hal
|
||||
cargo xtask build-package --features=esp32h2 --target=riscv32imac-unknown-none-elf esp-hal
|
||||
cargo xtask build-package --features=esp32p4 --target=riscv32imafc-unknown-none-elf esp-hal
|
||||
- name: msrv (esp-lp-hal)
|
||||
run: |
|
||||
cargo xtask build-package --features=esp32c6 --target=riscv32imac-unknown-none-elf esp-lp-hal
|
||||
cargo xtask build-package --features=esp32s2 --target=riscv32imc-unknown-none-elf esp-lp-hal
|
||||
cargo xtask build-package --features=esp32s3 --target=riscv32imc-unknown-none-elf esp-lp-hal
|
||||
|
||||
msrv-xtensa:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
RUSTC_BOOTSTRAP: 1
|
||||
|
||||
# TODO:
|
||||
# - Enable additional (all?) features for MSRV check
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: esp-rs/xtensa-toolchain@v1.5
|
||||
with:
|
||||
ldproxy: false
|
||||
version: ${{ env.MSRV }}
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
# Verify the MSRV for all Xtensa chips:
|
||||
- name: msrv (esp-hal)
|
||||
run: |
|
||||
cargo xtask build-package --toolchain=esp --features=esp32 --target=xtensa-esp32-none-elf esp-hal
|
||||
cargo xtask build-package --toolchain=esp --features=esp32s2 --target=xtensa-esp32s2-none-elf esp-hal
|
||||
cargo xtask build-package --toolchain=esp --features=esp32s3 --target=xtensa-esp32s3-none-elf esp-hal
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Lint
|
||||
|
||||
clippy-riscv:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
targets: riscv32imc-unknown-none-elf,riscv32imac-unknown-none-elf,riscv32imafc-unknown-none-elf
|
||||
components: clippy,rust-src
|
||||
target: riscv32imc-unknown-none-elf,riscv32imac-unknown-none-elf
|
||||
toolchain: ${{ env.MSRV }}
|
||||
components: rust-src
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
# Run 'cargo clippy' on all packages targeting RISC-V:
|
||||
## esp-hal:
|
||||
- name: clippy (esp-hal, esp32c2)
|
||||
run: cd esp-hal && cargo clippy --features=esp32c2 --target=riscv32imc-unknown-none-elf -- -D warnings
|
||||
- name: clippy (esp-hal, esp32c3)
|
||||
run: cd esp-hal && cargo clippy --features=esp32c3 --target=riscv32imc-unknown-none-elf -- -D warnings
|
||||
- name: clippy (esp-hal, esp32c6)
|
||||
run: cd esp-hal && cargo clippy --features=esp32c6 --target=riscv32imac-unknown-none-elf -- -D warnings
|
||||
- name: clippy (esp-hal, esp32h2)
|
||||
run: cd esp-hal && cargo clippy --features=esp32h2 --target=riscv32imac-unknown-none-elf -- -D warnings
|
||||
# TODO: Re-enable when able
|
||||
# - name: clippy (esp-hal, esp32p4)
|
||||
# run: cd esp-hal && cargo clippy --features=esp32p4 --target=riscv32imafc-unknown-none-elf -- -D warnings
|
||||
## esp-hal-smartled:
|
||||
- name: clippy (esp-hal-smartled)
|
||||
run: cd esp-hal-smartled && cargo clippy --features=esp32c6 --target=riscv32imac-unknown-none-elf -- -D warnings
|
||||
## esp-lp-hal:
|
||||
- name: clippy (esp-lp-hal, esp32c6)
|
||||
run: cd esp-lp-hal && cargo clippy --features=esp32c6 --target=riscv32imac-unknown-none-elf -- -D warnings
|
||||
- name: clippy (esp-lp-hal, esp32s2)
|
||||
run: cd esp-lp-hal && cargo clippy --features=esp32s2 --target=riscv32imc-unknown-none-elf -- -D warnings
|
||||
- name: clippy (esp-lp-hal, esp32s3)
|
||||
run: cd esp-lp-hal && cargo clippy --features=esp32s3 --target=riscv32imc-unknown-none-elf -- -D warnings
|
||||
# esp-riscv-rt:
|
||||
- name: clippy (esp-riscv-rt)
|
||||
run: cd esp-riscv-rt && cargo clippy --target=riscv32imc-unknown-none-elf -- -D warnings
|
||||
# Verify the MSRV for all RISC-V chips.
|
||||
- name: msrv RISCV (esp-hal)
|
||||
run: |
|
||||
cargo xtask build-package --features=esp32c2,ci --target=riscv32imc-unknown-none-elf esp-hal
|
||||
cargo xtask build-package --features=esp32c3,ci --target=riscv32imc-unknown-none-elf esp-hal
|
||||
cargo xtask build-package --features=esp32c6,ci --target=riscv32imac-unknown-none-elf esp-hal
|
||||
cargo xtask build-package --features=esp32h2,ci --target=riscv32imac-unknown-none-elf esp-hal
|
||||
|
||||
clippy-xtensa:
|
||||
runs-on: ubuntu-latest
|
||||
- name: msrv RISCV (esp-wifi)
|
||||
run: |
|
||||
cargo xtask build-package --features=esp32c2,wifi,ble,esp-hal/unstable --target=riscv32imc-unknown-none-elf esp-wifi
|
||||
cargo xtask build-package --features=esp32c3,wifi,ble,esp-hal/unstable --target=riscv32imc-unknown-none-elf esp-wifi
|
||||
cargo xtask build-package --features=esp32c6,wifi,ble,esp-hal/unstable --target=riscv32imac-unknown-none-elf esp-wifi
|
||||
cargo xtask build-package --features=esp32h2,ble,esp-hal/unstable --target=riscv32imac-unknown-none-elf esp-wifi
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: esp-rs/xtensa-toolchain@v1.5
|
||||
with:
|
||||
default: true
|
||||
ldproxy: false
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
# Verify the MSRV for all Xtensa chips:
|
||||
- name: msrv Xtensa (esp-hal)
|
||||
run: |
|
||||
cargo xtask build-package --toolchain=esp --features=esp32,ci --target=xtensa-esp32-none-elf esp-hal
|
||||
cargo xtask build-package --toolchain=esp --features=esp32s2,ci --target=xtensa-esp32s2-none-elf esp-hal
|
||||
cargo xtask build-package --toolchain=esp --features=esp32s3,ci --target=xtensa-esp32s3-none-elf esp-hal
|
||||
|
||||
# Run 'cargo clippy' on all packages targeting Xtensa:
|
||||
- name: clippy (esp-hal, esp32)
|
||||
run: cd esp-hal && cargo clippy -Zbuild-std=core --features=esp32 --target=xtensa-esp32-none-elf -- -D warnings
|
||||
- name: clippy (esp-hal, esp32s2)
|
||||
run: cd esp-hal && cargo clippy -Zbuild-std=core --features=esp32s2 --target=xtensa-esp32s2-none-elf -- -D warnings
|
||||
- name: clippy (esp-hal, esp32s3)
|
||||
run: cd esp-hal && cargo clippy -Zbuild-std=core --features=esp32s3 --target=xtensa-esp32s3-none-elf -- -D warnings
|
||||
- name: msrv Xtensa (esp-wifi)
|
||||
run: |
|
||||
cargo xtask build-package --toolchain=esp --features=esp32,wifi,ble,esp-hal/unstable --target=xtensa-esp32-none-elf esp-wifi
|
||||
cargo xtask build-package --toolchain=esp --features=esp32s2,wifi,esp-hal/unstable --target=xtensa-esp32s2-none-elf esp-wifi
|
||||
cargo xtask build-package --toolchain=esp --features=esp32s3,wifi,ble,esp-hal/unstable --target=xtensa-esp32s3-none-elf esp-wifi
|
||||
|
||||
- name: msrv (esp-lp-hal)
|
||||
run: |
|
||||
cargo xtask build-package --features=esp32c6 --target=riscv32imac-unknown-none-elf esp-lp-hal
|
||||
cargo xtask build-package --features=esp32s2 --target=riscv32imc-unknown-none-elf esp-lp-hal
|
||||
cargo xtask build-package --features=esp32s3 --target=riscv32imc-unknown-none-elf esp-lp-hal
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Format
|
||||
|
||||
rustfmt:
|
||||
runs-on: ubuntu-latest
|
||||
@ -284,15 +179,20 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
# Check the formatting of all packages:
|
||||
- name: rustfmt (esp-hal)
|
||||
run: cargo fmt --all --manifest-path=esp-hal/Cargo.toml -- --check
|
||||
- name: rustfmt (esp-hal-procmacros)
|
||||
run: cargo fmt --all --manifest-path=esp-hal-procmacros/Cargo.toml -- --check
|
||||
- name: rustfmt (esp-hal-smartled)
|
||||
run: cargo fmt --all --manifest-path=esp-hal-smartled/Cargo.toml -- --check
|
||||
- name: rustfmt (esp-lp-hal)
|
||||
run: cargo fmt --all --manifest-path=esp-lp-hal/Cargo.toml -- --check
|
||||
- name: rustfmt (esp-riscv-rt)
|
||||
run: cargo fmt --all --manifest-path=esp-riscv-rt/Cargo.toml -- --check
|
||||
- name: rustfmt (examples)
|
||||
run: cargo fmt --all --manifest-path=examples/Cargo.toml -- --check
|
||||
- run: cargo xtask fmt-packages --check
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# host tests
|
||||
|
||||
host-tests:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
# Check the formatting of all packages:
|
||||
- run: cd esp-config && cargo test --features build
|
||||
|
||||
53
.github/workflows/ci_nightly.yml
vendored
Normal file
53
.github/workflows/ci_nightly.yml
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
name: CI - nightly
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
RUSTDOCFLAGS: -Dwarnings
|
||||
DEFMT_LOG: trace
|
||||
|
||||
jobs:
|
||||
|
||||
esp-hal-nightly:
|
||||
name: esp-hal | nightly (${{ matrix.device.soc }})
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
SSID: SSID
|
||||
PASSWORD: PASSWORD
|
||||
STATIC_IP: 1.1.1.1
|
||||
GATEWAY_IP: 1.1.1.1
|
||||
HOST_IP: 1.1.1.1
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
device: [
|
||||
# RISC-V devices:
|
||||
{ soc: "esp32c2", target: "riscv32imc-unknown-none-elf" },
|
||||
{ soc: "esp32c3", target: "riscv32imc-unknown-none-elf" },
|
||||
{ soc: "esp32c6", target: "riscv32imac-unknown-none-elf" },
|
||||
{ soc: "esp32h2", target: "riscv32imac-unknown-none-elf" },
|
||||
]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# Install the Rust nightly toolchain for RISC-V devices:
|
||||
- uses: dtolnay/rust-toolchain@v1
|
||||
with:
|
||||
target: riscv32imc-unknown-none-elf,riscv32imac-unknown-none-elf
|
||||
toolchain: nightly
|
||||
components: rust-src, clippy, rustfmt
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Build and Check
|
||||
uses: ./.github/actions/check-esp-hal
|
||||
with:
|
||||
device: ${{ matrix.device.soc }}
|
||||
target: ${{ matrix.device.target }}
|
||||
toolchain: nightly
|
||||
57
.github/workflows/documentation.yml
vendored
57
.github/workflows/documentation.yml
vendored
@ -2,12 +2,33 @@ name: Documentation
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
esp-hal:
|
||||
description: "esp-hal tag"
|
||||
required: true
|
||||
esp-wifi:
|
||||
description: "esp-wifi tag"
|
||||
required: true
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
packages: '[
|
||||
{ "name": "esp-hal", "tag": "${{ github.event.inputs.esp-hal }}" },
|
||||
{ "name": "esp-wifi", "tag": "esp-wifi-${{ github.event.inputs.esp-wifi }}" }
|
||||
]'
|
||||
steps:
|
||||
- run: echo "Setup complete!"
|
||||
build:
|
||||
needs: setup
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
packages: ${{ fromJson(needs.setup.outputs.packages) }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@ -15,22 +36,50 @@ jobs:
|
||||
with:
|
||||
default: true
|
||||
ldproxy: false
|
||||
version: 1.83.0.1
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: esp-rs/esp-hal
|
||||
ref: ${{ matrix.packages.tag }}
|
||||
|
||||
- name: Build documentation
|
||||
run: ./.github/scripts/build_docs.sh
|
||||
run: cargo xtask build-documentation --packages=${{ matrix.packages.name }}
|
||||
|
||||
# https://github.com/actions/deploy-pages/issues/303#issuecomment-1951207879
|
||||
- name: Remove problematic '.lock' files
|
||||
run: find docs -name ".lock" -exec rm -f {} \;
|
||||
|
||||
- name: Upload docs for ${{ matrix.packages.name }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.packages.name }}
|
||||
path: "docs/${{ matrix.packages.name }}"
|
||||
|
||||
assemble:
|
||||
needs: [setup, build]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Prepare
|
||||
run: mkdir docs
|
||||
- name: Download all docs
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: "docs/"
|
||||
|
||||
- name: Create index.html
|
||||
run: "cargo xtask build-documentation-index --packages=$(echo '${{ needs.setup.outputs.packages }}' | jq -r '[.[].name] | join(\",\")')"
|
||||
|
||||
- name: Upload Pages artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: "docs"
|
||||
path: "docs/"
|
||||
|
||||
deploy:
|
||||
# Add a dependency to the build job:
|
||||
needs: build
|
||||
# Add a dependency to the assemble job:
|
||||
needs: assemble
|
||||
|
||||
# Grant GITHUB_TOKEN the permissions required to make a Pages deployment:
|
||||
permissions:
|
||||
|
||||
186
.github/workflows/hil.yml
vendored
Normal file
186
.github/workflows/hil.yml
vendored
Normal file
@ -0,0 +1,186 @@
|
||||
name: HIL
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
merge_group:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
repository:
|
||||
description: "Owner and repository to test"
|
||||
required: true
|
||||
default: 'esp-rs/esp-hal'
|
||||
branch:
|
||||
description: "Branch, tag or SHA to checkout."
|
||||
required: true
|
||||
default: "main"
|
||||
|
||||
# Cancel any currently running workflows from the same PR, branch, or
|
||||
# tag when a new workflow is triggered.
|
||||
#
|
||||
# https://stackoverflow.com/a/66336834
|
||||
concurrency:
|
||||
cancel-in-progress: true
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
jobs:
|
||||
build-xtasks:
|
||||
name: Build xtasks | ${{ matrix.host.arch }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
host:
|
||||
- arch: armv7
|
||||
rust-target: armv7-unknown-linux-gnueabihf
|
||||
- arch: aarch64
|
||||
rust-target: aarch64-unknown-linux-gnu
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
if: github.event_name != 'workflow_dispatch'
|
||||
- uses: actions/checkout@v4
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
with:
|
||||
repository: ${{ github.event.inputs.repository }}
|
||||
ref: ${{ github.event.inputs.branch }}
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
components: rust-src
|
||||
|
||||
- name: Install cross
|
||||
run: cargo install cross
|
||||
|
||||
- name: Build xtasks
|
||||
run: cross build --release --target ${{ matrix.host.rust-target }} -p xtask
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: xtask-${{ matrix.host.arch }}
|
||||
path: target/${{ matrix.host.rust-target }}/release/xtask
|
||||
|
||||
build-tests:
|
||||
name: Build HIL Tests | ${{ matrix.target.soc }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target:
|
||||
# RISC-V devices:
|
||||
- soc: esp32c2
|
||||
rust-target: riscv32imc-unknown-none-elf
|
||||
- soc: esp32c3
|
||||
rust-target: riscv32imc-unknown-none-elf
|
||||
- soc: esp32c6
|
||||
rust-target: riscv32imac-unknown-none-elf
|
||||
- soc: esp32h2
|
||||
rust-target: riscv32imac-unknown-none-elf
|
||||
# # Xtensa devices:
|
||||
- soc: esp32
|
||||
rust-target: xtensa-esp32-none-elf
|
||||
- soc: esp32s2
|
||||
rust-target: xtensa-esp32s2-none-elf
|
||||
- soc: esp32s3
|
||||
rust-target: xtensa-esp32s3-none-elf
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
if: github.event_name != 'workflow_dispatch'
|
||||
- uses: actions/checkout@v4
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
with:
|
||||
repository: ${{ github.event.inputs.repository }}
|
||||
ref: ${{ github.event.inputs.branch }}
|
||||
|
||||
# Install the Rust toolchain for RISC-V devices:
|
||||
- if: ${{ !contains(fromJson('["esp32", "esp32s2", "esp32s3"]'), matrix.target.soc) }}
|
||||
uses: dtolnay/rust-toolchain@v1
|
||||
with:
|
||||
target: ${{ matrix.target.rust-target }}
|
||||
toolchain: stable
|
||||
components: rust-src
|
||||
# Install the Rust toolchain for Xtensa devices:
|
||||
- if: contains(fromJson('["esp32", "esp32s2", "esp32s3"]'), matrix.target.soc)
|
||||
uses: esp-rs/xtensa-toolchain@v1.5
|
||||
with:
|
||||
buildtargets: ${{ matrix.target.soc }}
|
||||
default: true
|
||||
ldproxy: false
|
||||
version: 1.83.0.1
|
||||
|
||||
- name: Build tests
|
||||
run: cargo xtask build-tests ${{ matrix.target.soc }}
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: tests-${{ matrix.target.soc }}
|
||||
path: /home/runner/work/esp-hal/esp-hal/target/tests/${{ matrix.target.soc }}
|
||||
if-no-files-found: error
|
||||
overwrite: true
|
||||
|
||||
hil:
|
||||
name: Run HIL Tests | ${{ matrix.target.soc }}
|
||||
needs: [build-tests, build-xtasks]
|
||||
runs-on:
|
||||
labels: [self-hosted, "${{ matrix.target.runner }}"]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target:
|
||||
# RISC-V devices:
|
||||
- soc: esp32c2
|
||||
runner: esp32c2-jtag
|
||||
host: aarch64
|
||||
- soc: esp32c3
|
||||
runner: esp32c3-usb
|
||||
host: armv7
|
||||
- soc: esp32c6
|
||||
runner: esp32c6-usb
|
||||
host: armv7
|
||||
- soc: esp32h2
|
||||
runner: esp32h2-usb
|
||||
host: armv7
|
||||
# Xtensa devices:
|
||||
- soc: esp32
|
||||
runner: esp32-jtag
|
||||
host: aarch64
|
||||
- soc: esp32s2
|
||||
runner: esp32s2-jtag
|
||||
host: armv7
|
||||
- soc: esp32s3
|
||||
runner: esp32s3-usb
|
||||
host: armv7
|
||||
steps:
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: tests-${{ matrix.target.soc }}
|
||||
path: tests-${{ matrix.target.soc }}
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: xtask-${{ matrix.target.host }}
|
||||
|
||||
- name: Run Tests
|
||||
id: run-tests
|
||||
run: |
|
||||
[ -f ~/setup.sh ] && source ~/setup.sh
|
||||
|
||||
export PATH=$PATH:/home/espressif/.cargo/bin
|
||||
chmod +x xtask
|
||||
./xtask run-elfs ${{ matrix.target.soc }} tests-${{ matrix.target.soc }}
|
||||
|
||||
- name: Clean up
|
||||
if: always()
|
||||
run: |
|
||||
rm -rf tests-${{ matrix.target.soc }}
|
||||
rm -f xtask
|
||||
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
@ -1,3 +0,0 @@
|
||||
{
|
||||
"recommendations": ["rust-lang.rust-analyzer", "tamasfe.even-better-toml"]
|
||||
}
|
||||
101
CONTRIBUTING.md
101
CONTRIBUTING.md
@ -1,101 +0,0 @@
|
||||
# Welcome to the `esp-hal` contributing guide
|
||||
|
||||
Thank you for investing your time in contributing to our project!
|
||||
|
||||
In this guide you will get an overview of the contribution workflow from opening an issue, creating a PR, reviewing, and merging the PR.
|
||||
|
||||
Use the table of contents icon (<img src="resources/table-of-contents.png" width="24" height="24" />) in the top right corner of this document to get to a specific section of this guide quickly.
|
||||
|
||||
## New Contributor Guide
|
||||
|
||||
To get an overview of the project, please read the [README]. Here are some resources to help you get started with open source contributions:
|
||||
|
||||
- [Finding ways to contribute to open source on GitHub]
|
||||
- [Set up Git]
|
||||
- [GitHub flow]
|
||||
- [Collaborating with pull requests]
|
||||
|
||||
[README]: README.md
|
||||
[Finding ways to contribute to open source on GitHub]: https://docs.github.com/en/get-started/exploring-projects-on-github/finding-ways-to-contribute-to-open-source-on-github
|
||||
[Set up Git]: https://docs.github.com/en/get-started/quickstart/set-up-git
|
||||
[GitHub flow]: https://docs.github.com/en/get-started/quickstart/github-flow
|
||||
[Collaborating with pull requests]: https://docs.github.com/en/github/collaborating-with-pull-requests
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Issues
|
||||
|
||||
#### Create a New Issue
|
||||
|
||||
If you spot a problem with the docs, [search if an issue already exists]. If a related issue doesn't exist, you can open a new issue using the [issue form].
|
||||
|
||||
[search if an issue already exists]: https://docs.github.com/en/github/searching-for-information-on-github/searching-on-github/searching-issues-and-pull-requests#search-by-the-title-body-or-comments
|
||||
[issue form]: https://github.com/esp-rs/esp-hal/issues/new/
|
||||
|
||||
#### Solve an Issue
|
||||
|
||||
Scan through our [existing issues] to find one that interests you. You can narrow down the search using labels as filters. If you find an issue to work on, you are welcome to open a PR with a fix.
|
||||
|
||||
It's recommended that you comment in the relevant issue, mentioning that you are actively working on it, however this is not a requirement.
|
||||
|
||||
If somebody is already assigned to an issue, this does not necessarily mean they are actively working on it; don't be afraid to comment in these issues asking if you can take over the work if you're interested.
|
||||
|
||||
[existing issues]: https://github.com/esp-rs/esp-hal/issues
|
||||
|
||||
### Make Changes
|
||||
|
||||
1. Fork the repository.
|
||||
- Using GitHub Desktop:
|
||||
- [Getting started with GitHub Desktop] will guide you through setting up Desktop.
|
||||
- Once Desktop is set up, you can use it to [fork the repo!]
|
||||
- Using the command line:
|
||||
- [Fork the repo] so that you can make your changes without affecting the original project until you're ready to merge them.
|
||||
2. Install or update to the latest version of Rust. See [rustup.rs] for more information.
|
||||
3. Create a working branch and start with your changes!
|
||||
|
||||
[Getting started with GitHub Desktop]: https://docs.github.com/en/desktop/installing-and-configuring-github-desktop/getting-started-with-github-desktop
|
||||
[fork the repo!]: https://docs.github.com/en/desktop/contributing-and-collaborating-using-github-desktop/cloning-and-forking-repositories-from-github-desktop
|
||||
[Fork the repo]: https://docs.github.com/en/github/getting-started-with-github/fork-a-repo#fork-an-example-repository
|
||||
[rustup.rs]: https://rustup.rs/
|
||||
|
||||
### Commit Your Update
|
||||
|
||||
Commit the changes once you are happy with them. Don't forget to self-review to speed up the review process.
|
||||
|
||||
We ask that you ensure all source code files has been properly formatted with `rustfmt`, and that you have linted your changes by running `cargo clippy`. These tools can be installed by running the following commands:
|
||||
|
||||
```shell
|
||||
rustup component add rustfmt
|
||||
rustup component add clippy
|
||||
```
|
||||
|
||||
We _strongly_ recommend that you use the supplied `pre-commit` Git hook, which will ensure that all source code has been formatted correctly prior to committing. See the [Git documentation] for more information on hooks.
|
||||
|
||||
The `pre-commit` hook can be installed by running the following command in a terminal, from the root of the repository:
|
||||
|
||||
```shell
|
||||
cp pre-commit .git/hooks/pre-commit
|
||||
```
|
||||
|
||||
[Git documentation]: https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks
|
||||
|
||||
### Pull Request
|
||||
|
||||
When you're finished with the changes, create a pull request, also known as a PR.
|
||||
|
||||
- Fill the pull request template so that we can review your PR. This template helps reviewers understand your changes as well as the purpose of your pull request.
|
||||
- Don't forget to [link PR to issue] if you are solving one.
|
||||
- Enable the checkbox to [allow maintainer edits] so the branch can be updated for a merge. Once you submit your PR, a Docs team member will review your proposal. We may ask questions or request additional information.
|
||||
- We may ask for changes to be made before a PR can be merged, either using [suggested changes] or pull request comments. You can apply suggested changes directly through the UI. You can make any other changes in your fork, then commit them to your branch.
|
||||
- As you update your PR and apply changes, mark each conversation as [resolved].
|
||||
- If you run into any merge issues, checkout this [git tutorial] to help you resolve merge conflicts and other issues.
|
||||
|
||||
[link PR to issue]: https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue
|
||||
[allow maintainer edits]: https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/allowing-changes-to-a-pull-request-branch-created-from-a-fork
|
||||
[suggested changes]: https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/incorporating-feedback-in-your-pull-request
|
||||
[resolved]: https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/commenting-on-a-pull-request#resolving-conversations
|
||||
[git tutorial]: https://github.com/skills/resolve-merge-conflicts
|
||||
|
||||
### Your PR is Merged!
|
||||
|
||||
Congratulations! The esp-rs team thanks you for your contributions!
|
||||
19
Cargo.toml
19
Cargo.toml
@ -2,10 +2,27 @@
|
||||
resolver = "2"
|
||||
members = ["xtask"]
|
||||
exclude = [
|
||||
"esp-alloc",
|
||||
"esp-backtrace",
|
||||
"esp-build",
|
||||
"esp-config",
|
||||
"esp-hal",
|
||||
"esp-hal-embassy",
|
||||
"esp-hal-procmacros",
|
||||
"esp-hal-smartled",
|
||||
"esp-ieee802154",
|
||||
"esp-lp-hal",
|
||||
"esp-metadata",
|
||||
"esp-println",
|
||||
"esp-riscv-rt",
|
||||
"esp-wifi",
|
||||
"esp-storage",
|
||||
"examples",
|
||||
"extras/bench-server",
|
||||
"extras/esp-wifishark",
|
||||
"extras/ieee802154-sniffer",
|
||||
"hil-test",
|
||||
"qa-test",
|
||||
"xtensa-lx",
|
||||
"xtensa-lx-rt",
|
||||
"xtensa-lx-rt/procmacros",
|
||||
]
|
||||
|
||||
117
README.md
117
README.md
@ -1,20 +1,29 @@
|
||||
# esp-hal
|
||||
|
||||

|
||||

|
||||
[](https://matrix.to/#/#esp-rs:matrix.org)
|
||||

|
||||

|
||||

|
||||
[](https://matrix.to/#/#esp-rs:matrix.org)
|
||||
|
||||
**H**ardware **A**bstraction **L**ayer crates for the **ESP32**, **ESP32-C2/C3/C6**, **ESP32-H2**, **ESP32-P4**, and **ESP32-S2/S3** from Espressif. Additionally provides support for programming the low-power RISC-V cores found on the **ESP32-C6** and **ESP32-S2/S3**.
|
||||
Bare-metal (`no_std`) hardware abstraction layer for Espressif devices. Currently supports, to varying degrees, the following devices:
|
||||
|
||||
These HALs are `no_std`; if you are looking for `std` support, please use [esp-idf-hal] instead.
|
||||
- ESP32 Series: _ESP32_
|
||||
- ESP32-C Series: _ESP32-C2, ESP32-C3, ESP32-C6_
|
||||
- ESP32-H Series: _ESP32-H2_
|
||||
- ESP32-S Series: _ESP32-S2, ESP32-S3_
|
||||
|
||||
Additionally provides limited support for programming the low-power RISC-V cores found on the _ESP32-C6_, _ESP32-S2_, and _ESP32-S3_ via the [esp-lp-hal] package.
|
||||
|
||||
These packages are all `no_std`; if you are looking for `std` support, please use [esp-idf-svc] instead.
|
||||
|
||||
If you have any questions, comments, or concerns, please [open an issue], [start a new discussion], or join us on [Matrix]. For additional information regarding any of the crates in this repository, please refer to the relevant crate's README.
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> This project is still in the relatively early stages of development, and as such there should be no expectation of API stability. A significant number of peripherals currently have drivers implemented but have varying levels of functionality. For most basic tasks, this should be usable already, however some more advanced or uncommon features may not yet be implemented.
|
||||
> This repository includes crates that are at various stages of maturity and stability. While many functionalities have already been implemented and are usable for most tasks, certain advanced or less common features may still be under development. Each crate may offer different levels of functionality and guarantees.
|
||||
|
||||
[esp-idf-hal]: https://github.com/esp-rs/esp-idf-hal
|
||||
[esp-lp-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp-lp-hal
|
||||
[esp-idf-svc]: https://github.com/esp-rs/esp-idf-svc
|
||||
[open an issue]: https://github.com/esp-rs/esp-hal/issues/new
|
||||
[start a new discussion]: https://github.com/esp-rs/esp-hal/discussions/new
|
||||
[matrix]: https://matrix.to/#/#esp-rs:matrix.org
|
||||
@ -23,10 +32,10 @@ If you have any questions, comments, or concerns, please [open an issue], [start
|
||||
|
||||
For information relating to the development of Rust applications on ESP devices, please first read [The Rust on ESP Book].
|
||||
|
||||
For information about the HAL and how to use it in your own projects, please refer to the documentation on [docs.rs] for the relevant chip.
|
||||
For information about the HAL and how to use it in your own projects, please refer to the [documentation].
|
||||
|
||||
[The Rust on ESP Book]: https://esp-rs.github.io/book/
|
||||
[docs.rs]: https://docs.rs
|
||||
[documentation]: https://docs.esp-rs.org/esp-hal/
|
||||
|
||||
## Resources
|
||||
|
||||
@ -38,92 +47,14 @@ For information about the HAL and how to use it in your own projects, please ref
|
||||
|
||||
## Crates
|
||||
|
||||
### [esp-hal]
|
||||
This repository is home to a number of different packages; for more information regarding a particular package, please refer to its `README.md` and/or documentation.
|
||||
|
||||
Implements number of the traits defined in [embedded-hal](https://github.com/rust-embedded/embedded-hal) for various ESP devices. Full list of currently supported devices with basic information is in the table below.
|
||||
## Contributing
|
||||
|
||||
| Feature | Technical Reference Manual | Target | MSRV |
|
||||
| :-----------: | :------------------------: | :-----------------------------: | :--------: |
|
||||
| `esp32` | [ESP32] | `xtensa-esp32-none-elf` | ![esp] |
|
||||
| `esp32c2` | [ESP32-C2] | `riscv32imc-unknown-none-elf` | ![stable] |
|
||||
| `esp32c3` | [ESP32-C3] | `riscv32imc-unknown-none-elf` | ![stable] |
|
||||
| `esp32c6` | [ESP32-C6] | `riscv32imac-unknown-none-elf` | ![stable] |
|
||||
| `esp32h2` | [ESP32-H2] | `riscv32imac-unknown-none-elf` | ![stable] |
|
||||
| `esp32p4` | [ESP32-P4] | `riscv32imafc-unknown-none-elf` | ![stable] |
|
||||
| `esp32s2` | [ESP32-S2] | `xtensa-esp32s2-none-elf` | ![esp] |
|
||||
| `esp32s3` | [ESP32-S3] | `xtensa-esp32s3-none-elf` | ![esp] |
|
||||
We have a number of living documents to aid contributing to the project, please give these a read before modifying code:
|
||||
|
||||
[esp-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp-hal
|
||||
[esp]: https://img.shields.io/badge/rustc-esp%201.74+-red.svg
|
||||
[stable]: https://img.shields.io/badge/rustc-stable%201.76+-red.svg
|
||||
[esp32]: https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf
|
||||
[esp32-c2]: https://www.espressif.com/sites/default/files/documentation/esp8684_technical_reference_manual_en.pdf
|
||||
[esp32-c3]: https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf
|
||||
[esp32-c6]: https://www.espressif.com/sites/default/files/documentation/esp32-c6_technical_reference_manual_en.pdf
|
||||
[esp32-h2]: https://www.espressif.com/sites/default/files/documentation/esp32-h2_technical_reference_manual_en.pdf
|
||||
[esp32-p4]: https://www.espressif.com/sites/default/files/documentation/esp32-p4_technical_reference_manual_en.pdf
|
||||
[esp32-s2]: https://www.espressif.com/sites/default/files/documentation/esp32-s2_technical_reference_manual_en.pdf
|
||||
[esp32-s3]: https://www.espressif.com/sites/default/files/documentation/esp32-s3_technical_reference_manual_en.pdf
|
||||
|
||||
### [esp-lp-hal]
|
||||
|
||||
Implements a number of the traits defined in [embedded-hal](https://github.com/rust-embedded/embedded-hal) for the low-power (lp) RISC-V coprocessors found on the ESP32-C6, ESP32-S2, and ESP32-S3 from Espressif. The main idea is to have code running on lp core and putting the main core into sleep, making it more power-efficient.
|
||||
|
||||
|
||||
| Crate | Documentation | Targets |
|
||||
| :----------: | :-----------------------: | :-----------------------------------------------------------: |
|
||||
| [esp-lp-hal] | N/A (_Not yet published_) | `riscv32imc-unknown-none-elf`, `riscv32imac-unknown-none-elf` |
|
||||
|
||||
|
||||
[esp-lp-hal]: https://github.com/esp-rs/esp-hal/tree/main/esp-lp-hal
|
||||
|
||||
### [esp-hal-procmacros]
|
||||
|
||||
Procedural macros for use with the esp-hal family of HAL packages.
|
||||
|
||||
[esp-hal-procmacros]: https://github.com/esp-rs/esp-hal/tree/main/esp-hal-procmacros
|
||||
|
||||
### [esp-riscv-rt]
|
||||
|
||||
Minimal runtime / startup for RISC-V CPUs from Espressif.
|
||||
|
||||
[esp-riscv-rt]: https://github.com/esp-rs/esp-hal/tree/main/esp-riscv-rt
|
||||
|
||||
## Ancillary Crates
|
||||
|
||||
There are a number of other crates within the [esp-rs organization] which can be used in conjunction with `esp-hal`:
|
||||
|
||||
| Crate | Description |
|
||||
| :--------------: | :----------------------------------------------------------------------------: |
|
||||
| [esp-alloc] | A simple `no_std` heap allocator |
|
||||
| [esp-backtrace] | Backtrace support for bare-metal applications |
|
||||
| [esp-ieee802154] | Low-level IEEE802.15.4 driver for the ESP32-C6 and ESP32-H2 |
|
||||
| [esp-openthread] | A bare-metal Thread implementation using `esp-ieee802154` |
|
||||
| [esp-println] | Provides `print!` and `println!` implementations |
|
||||
| [esp-storage] | Implementation of [embedded-storage] traits to access unencrypted flash memory |
|
||||
| [esp-wifi] | `no_std` Wi-Fi/BLE/ESP-NOW support |
|
||||
|
||||
[esp-rs organization]: https://github.com/esp-rs
|
||||
[esp-alloc]: https://github.com/esp-rs/esp-alloc
|
||||
[esp-backtrace]: https://github.com/esp-rs/esp-backtrace
|
||||
[esp-ieee802154]: https://github.com/esp-rs/esp-ieee802154
|
||||
[esp-openthread]: https://github.com/esp-rs/esp-openthread
|
||||
[esp-println]: https://github.com/esp-rs/esp-println
|
||||
[esp-storage]: https://github.com/esp-rs/esp-storage
|
||||
[embedded-storage]: https://github.com/rust-embedded-community/embedded-storage
|
||||
[esp-wifi]: https://github.com/esp-rs/esp-wifi
|
||||
|
||||
## Git Hooks
|
||||
|
||||
We provide a simple `pre-commit` hook to verify the formatting of each package prior to committing changes. We _strongly_ encourage use of this git hook.
|
||||
|
||||
The hook can be enabled by copying it in to the `.git/hooks/` directory:
|
||||
|
||||
```bash
|
||||
cp pre-commit .git/hooks/pre-commit
|
||||
```
|
||||
|
||||
When using this hook, you can choose to ignore its failure on a per-commit basis by committing with the `--no-verify` flag; however, you will need to be sure that all packages are formatted when submitting a pull request.
|
||||
- [API-GUIDELINES](https://github.com/esp-rs/esp-hal/blob/main/documentation/API-GUIDELINES.md)
|
||||
- [CONTRIBUTING-GUIDE](https://github.com/esp-rs/esp-hal/blob/main/documentation/CONTRIBUTING.md)
|
||||
|
||||
## License
|
||||
|
||||
@ -134,7 +65,7 @@ Licensed under either of:
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
### Contribution notice
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in
|
||||
the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without
|
||||
|
||||
144
documentation/API-GUIDELINES.md
Normal file
144
documentation/API-GUIDELINES.md
Normal file
@ -0,0 +1,144 @@
|
||||
# `esp-rs` API Guidelines
|
||||
|
||||
## About
|
||||
|
||||
This is a living document - make sure to check the latest version of this document.
|
||||
|
||||
> [!NOTE]
|
||||
> Not all of the currently existing code follows this guideline, yet.
|
||||
|
||||
In general, the [Rust API Guidelines](https://rust-lang.github.io/api-guidelines) apply to all projects in the ESP-RS GitHub organization where possible.
|
||||
- Especially for public API but if possible also for internal APIs.
|
||||
|
||||
## Amendments to the Rust API Guidelines
|
||||
|
||||
- `C-RW-VALUE` and `C-SERDE` do not apply.
|
||||
- `C-COMMON-TRAITS`:
|
||||
The set of traits to implement depend on the type and use case. In esp-hal, we can highlight a few such use cases and provide recommendations what should be implemented. If nothing here applies, use your best judgement.
|
||||
- Driver structures: `Debug`
|
||||
- Driver configuration: `Default`, `Debug`, `PartialEq/Eq`, `Clone/Copy`, `Hash`
|
||||
- `Clone/Copy` depends on the size and contents of the structure. They should generally be implemented, unless there is a good reason not to.
|
||||
- The `Default` configuration needs to make sense for a particular driver, and applying the default configuration must not fail.
|
||||
- Error types: `Debug`, `PartialEq/Eq`, `Clone/Copy`, `Hash`, `Error`, `Display`
|
||||
|
||||
## Construction and Destruction of Drivers
|
||||
|
||||
- Drivers must take peripherals via the `PeripheralRef` pattern - they don't consume peripherals directly.
|
||||
- If a driver requires pins, those pins should be configured using `fn with_signal_name(self, pin: impl Peripheral<P = impl PeripheralInput> + 'd) -> Self` or `fn with_signal_name(self, pin: impl Peripheral<P = impl PeripheralOutput> + 'd) -> Self`
|
||||
- If a driver supports multiple peripheral instances (for example, I2C0 is one such instance):
|
||||
- The driver should not be generic over the peripheral instance.
|
||||
- The author must to use `crate::any_peripheral` to define the "any" peripheral instance type.
|
||||
- The driver must implement a `new` constructor that automatically converts the peripheral instance into the any type.
|
||||
- If a driver is configurable, configuration options should be implemented as a `Config` struct in the same module where the driver is located.
|
||||
- The driver's constructor should take the config struct by value, and it should return `Result<Self, ConfigError>`.
|
||||
- The `ConfigError` enum should be separate from other `Error` enums used by the driver.
|
||||
- The driver should implement `fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError>`.
|
||||
- In case the driver's configuration is infallible (all possible combinations of options are supported by the hardware), the `ConfigError` should be implemented as an empty `enum`.
|
||||
- Configuration structs should derive `procmacros::BuilderLite` in order to automatically implement the Builder Lite pattern for them.
|
||||
- If a driver implements both blocking and async operations, or only implements blocking operations, but may support asynchronous ones in the future, the driver's type signature must include a `crate::Mode` type parameter.
|
||||
- By default, constructors must configure the driver for blocking mode. The driver must implement `into_async` (and a matching `into_blocking`) function that reconfigures the driver.
|
||||
- `into_async` must configure the driver and/or the associated DMA channels. This most often means enabling an interrupt handler.
|
||||
- `into_blocking` must undo the configuration done by `into_async`.
|
||||
- The asynchronous driver implementation must also expose the blocking methods (except for interrupt related functions).
|
||||
- Drivers must have a `Drop` implementation resetting the peripheral to idle state. There are some exceptions to this:
|
||||
- GPIO where common usage is to "set and drop" so they can't be changed
|
||||
- Where we don't want to disable the peripheral as it's used internally, for example SYSTIMER is used by `time::now()` API. See `KEEP_ENABLED` in src/system.rs
|
||||
- A driver doesn't need to do anything special for deinitialization and has a `PeripheralGuard` field which implements the disabling and resetting of the peripheral.
|
||||
- Consider using a builder-like pattern for driver construction.
|
||||
|
||||
## Interoperability
|
||||
|
||||
- Don't use `log::XXX!` macros directly - use the wrappers in `fmt.rs` (e.g. just `info!` instead of `log::info!` or importing `log::*`)!
|
||||
- Consider implementing common ecosystem traits, like the ones in `embedded-hal` or `embassy-embedded-hal`.
|
||||
- Where the guidelines suggest implementing `Debug`, `defmt::Format` should also be implemented.
|
||||
- The `defmt::Format` implementation needs to be gated behind the `defmt` feature.
|
||||
- see [this example](https://github.com/esp-rs/esp-hal/blob/df2b7bd8472cc1d18db0d9441156575570f59bb3/esp-hal/src/spi/mod.rs#L15)
|
||||
- e.g. `#[cfg_attr(feature = "defmt", derive(defmt::Format))]`
|
||||
- Implementations of common, but unstable traits (e.g. `embassy_embedded_hal::SetConfig`) need to be gated with the `unstable` feature.
|
||||
|
||||
## API Surface
|
||||
|
||||
- API documentation must be provided for every new driver and API.
|
||||
- Private details should not leak into the public API, and should be made private where technically possible.
|
||||
- Implementation details that _need_ to be public should be marked with `#[doc(hidden)]` and a comment as to why it needs to be public.
|
||||
- For the time being, this includes any `Instance` traits, and `State` or `Info` structs as well.
|
||||
- Functions which technically need to be public but shouldn't be callable by the user need to be sealed.
|
||||
- see [this example in Rust's core library](https://github.com/rust-lang/rust/blob/044a28a4091f2e1a5883f7fa990223f8b200a2cd/library/core/src/error.rs#L89-L100)
|
||||
- Any public traits, that **must not** be implemented downstream need to be `Sealed`.
|
||||
- Prefer compile-time checks over runtime checks where possible, prefer a fallible API over panics.
|
||||
- Follow naming conventions in order to be consistent across drivers - take inspiration from existing drivers.
|
||||
- Design APIs in a way that they are easy to use.
|
||||
- Driver API decisions should be assessed individually, don't _not_ just follow embedded-hal or other ecosystem trait crates. Expose the capabilities of the hardware. (Ecosystem traits are implemented on top of the inherent API)
|
||||
- Avoid type states and extraneous generics whenever possible
|
||||
- These often lead to usability problems, and tend to just complicate things needlessly - sometimes it can be a good tradeoff to make a type not ZST
|
||||
- Common cases of useless type info is storing pin information - this is usually not required after configuring the pins and will bloat the complexity of the type massively. When following the `PeripheralRef` pattern it's not needed in order to keep users from re-using the pin while in use
|
||||
- Avoiding `&mut self` when `&self` is safe to use. `&self` is generally easier to use as an API. Typical applications of this are where the methods just do writes to registers which don't have side effects.
|
||||
- Maintain order consistency in the API, such as in the case of pairs like RX/TX.
|
||||
- If your driver provides a way to listen for interrupts, the interrupts should be listed in a `derive(EnumSetType)` enum as opposed to one function per interrupt flag.
|
||||
- If a driver only implements a subset of a peripheral's capabilities, it should be placed in the `peripheral::subcategory` module.
|
||||
- For example, if a driver implements the slave-mode I2C driver, it should be placed into `i2c::slave`.
|
||||
- This helps us reducing the need of introducing breaking changes if we implement additional functionalities.
|
||||
- Avoid abbreviations and contractions in the API, where possible.
|
||||
- Saving a few characters may introduce ambiguity, e.g `SpiTransDone`, is it `Transmit` or `Transfer`?
|
||||
- Common abbreviations, that are well understood such as `Dma` are perfectly fine.
|
||||
|
||||
## Maintainability
|
||||
|
||||
- Avoid excessive use of macros unless there is no other option; modification of the PAC crates should be considered before resorting to macros.
|
||||
- Every line of code is a liability. Take some time to see if your implementation can be simplified before opening a PR.
|
||||
- If you are porting code from ESP-IDF (or anything else), please include a link WITH the commit hash in it, and please highlight the relevant line(s) of code
|
||||
- If necessary provide further context as comments (consider linking to code, PRs, TRM - make sure to use permanent links, e.g. include the hash when linking to a Git repository, include the revision, page number etc. when linking to TRMs)
|
||||
- Prefer line comments (//) to block comments (/* ... */)
|
||||
- Generally, follow common "good practices" and idiomatic Rust style
|
||||
- All `Future` objects (public or private) must be marked with ``#[must_use = "futures do nothing unless you `.await` or poll them"]``.
|
||||
- Prefer `cfg_if!` (or, if the branches just pick between separate values of the same variable, `cfg!()`) over multiple exclusive `#[cfg]` attributes. `cfg_if!`/`cfg!()` visually divide the options, often results in simpler conditions and simplifies adding new branches in the future.
|
||||
|
||||
## Driver implementation
|
||||
|
||||
- If a common `Instance` trait is used for multiple peripherals, those traits should not have any logic implemented in them.
|
||||
- The `Instance` traits should only be used to access information about a peripheral instance.
|
||||
- The internal implementation of the driver should be non-generic over the peripheral instance. This helps the compiler produce smaller code.
|
||||
- The author is encouraged to return a static shared reference to an `Info` and a `State` structure from the `Instance` trait.
|
||||
- The `Info` struct should describe the peripheral. Do not use any interior mutability.
|
||||
- The `State` struct should contain counters, wakers and other, mutable state. As this is accessed via a shared reference, interior mutability and atomic variables are preferred.
|
||||
|
||||
## Modules Documentation
|
||||
|
||||
Modules should have the following documentation format:
|
||||
```rust
|
||||
//! # Peripheral Name (Peripheral Acronym)
|
||||
//!
|
||||
//! ## Overview
|
||||
//! Small description of the peripheral, see ESP-IDF docs or TRM
|
||||
//!
|
||||
//! ## Configuration
|
||||
//! Explain how can the peripheral be configured, and which parameters can be configured
|
||||
//!
|
||||
//! ## Usage
|
||||
//! Explain if we implement any external traits
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! ### Name of the Example
|
||||
//! Small description of the example if needed
|
||||
//! ```rust, no_run
|
||||
//! ...
|
||||
//! ```
|
||||
//!
|
||||
//! ## Implementation State
|
||||
//! List unsupported features
|
||||
```
|
||||
- If any of the headers is empty, remove it
|
||||
- When possible, use ESP-IDF docs and TRM as references and include links if possible.
|
||||
- In case of referencing an ESP-IDF link make it chip-specific, for example:
|
||||
```
|
||||
#, "/api-reference/peripherals/etm.html)")]
|
||||
```
|
||||
- In case of referencing a TRM chapter, use the `crate::trm_markdown_link!()` macro. If you are referring to a particular chapter, you may use `crate::trm_markdown_link!("#chapter_anchor")`.
|
||||
- Documentation examples must be short
|
||||
- But must also provide value beyond what the rustdoc generated docs show
|
||||
- Showing a snippet of a slightly more complex interaction, for example inverting the signals for a driver
|
||||
- Showing construction if it is more complex, or requires some non-obvious precursor steps. Think about this for drivers that take a generic instance to construct, rustdoc doesn't do a good job of showing what concrete things can be passed into a constructor.
|
||||
- For more complex scenarios, create an example.
|
||||
- Use rustdoc syntax for linking to other documentation items instead of markdown links where possible
|
||||
- https://doc.rust-lang.org/rustdoc/write-documentation/linking-to-items-by-name.html
|
||||
143
documentation/CONTRIBUTING.md
Normal file
143
documentation/CONTRIBUTING.md
Normal file
@ -0,0 +1,143 @@
|
||||
|
||||
# Welcome to the `esp-hal` Contributing Guide
|
||||
|
||||
Thank you for considering contributing to our project! Your efforts help make `esp-hal` a better ecosystem for everyone.
|
||||
|
||||
This guide outlines the contribution workflow, from reporting issues and submitting pull requests, to the review process and eventual merger of contributions.
|
||||
|
||||
## Quick Navigation
|
||||
* [New Contributor Guide]
|
||||
* [Getting Started]
|
||||
* [Issues: Reporting and Resolving]
|
||||
* [Making Changes: Fork, Edit, and Pull Request]
|
||||
* [Testing Your Contributions]
|
||||
* [Commit Your Updates]
|
||||
* [Pull Request: From Submission to Merge]
|
||||
* [Your PR is merged!]
|
||||
|
||||
[New Contributor Guide]: #new-contributor-guide
|
||||
[Getting Started]: #getting-started
|
||||
[Issues: Reporting and Resolving]: #issues-reporting-and-resolving
|
||||
[Making Changes: Fork, Edit, and Pull Request]: #making-changes-fork-edit-and-pull-request
|
||||
[Testing Your Contributions]: #testing-your-contributions
|
||||
[Commit your updates]: #commit-your-updates
|
||||
[Pull Request: From Submission to Merge]: #pull-request-from-submission-to-merge
|
||||
[Your PR is merged!]: #your-pr-is-merged
|
||||
|
||||
## New Contributor Guide
|
||||
|
||||
Welcome aboard! If you're new to `esp-hal` or open-source contribution, here are some resources to get you started:
|
||||
|
||||
* [Understanding the Project]: A high-level overview of `esp-hal`.
|
||||
* Intro to Open Source Contribution: [GitHub's Guide]
|
||||
* [Setting Up Git]
|
||||
* Workflow Insights: [GitHub Flow]
|
||||
* Collaborating via [Pull Requests]
|
||||
|
||||
Before adding or changing code, review the [esp-rs API guidelines].
|
||||
|
||||
[Understanding the Project]: README.md
|
||||
[GitHub's Guide]: https://docs.github.com/en/get-started/exploring-projects-on-github/finding-ways-to-contribute-to-open-source-on-github
|
||||
[Setting Up Git]: https://docs.github.com/en/get-started/quickstart/set-up-git
|
||||
[GitHub Flow]: https://docs.github.com/en/get-started/quickstart/github-flow
|
||||
[Pull Requests]: https://docs.github.com/en/github/collaborating-with-pull-requests
|
||||
[esp-rs API guidelines]: ./documentation/API-GUIDELINES.md
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Issues: Reporting and Resolving
|
||||
|
||||
#### Reporting a New Issue
|
||||
|
||||
Encountered a problem or have an idea? First, [check existing issues] to avoid duplicates. If your concern is new, use our [issue form] to submit it.
|
||||
|
||||
[check existing issues]: https://github.com/esp-rs/esp-hal/issues
|
||||
[issue form]: https://github.com/esp-rs/esp-hal/issues/new/
|
||||
|
||||
#### Working on an Issue
|
||||
|
||||
Browse [existing issues] to find one that resonates with you. Use labels for easier filtering. If you decide to tackle an issue, it's courteous (but not mandatory) to let others know by commenting.
|
||||
|
||||
[existing issues]: https://github.com/esp-rs/esp-hal/issues
|
||||
|
||||
#### Making Changes: Fork, Edit, and Pull Request
|
||||
|
||||
1. **Fork**: Start by [forking the repository]. This keeps the main project safe while you make your changes.
|
||||
2. **Setup**: Ensure you have the latest Rust toolchain via [rustup.rs].
|
||||
3. **Branch**: Create a branch in your fork for your changes. Keep your changes focused and limited to a single issue or feature.
|
||||
|
||||
[forking the repository]: https://docs.github.com/en/github/getting-started-with-github/fork-a-repo
|
||||
[rustup.rs]: https://rustup.rs/
|
||||
|
||||
#### What You Should Do:
|
||||
|
||||
* **API changes**: If your contribution changes the API, please adapt the driver (including module level documentation) and examples accordingly and update the [HIL] (Hardware-in-the-Loop) tests.
|
||||
* **Run Related Examples**: After making changes, run any affected examples to ensure they build successfully and perform as expected.
|
||||
* **Manual Testing**: For hardware-related changes, manually test your changes on the actual devices when possible. If not, please note it in the corresponding issue, and someone from our team will assist with testing. This is crucial because hardware behavior can sometimes differ from what's simulated or expected.
|
||||
* **HIL Tests**: Ensure that any changes to the API or hardware interaction logic are reflected in the HIL tests located in the `hil-test` directory. This helps verify the real-world applicability of your changes.
|
||||
|
||||
By taking these extra steps to test your contributions, you help maintain the high quality and reliability of `esp-hal`, ensuring it remains a robust platform for everyone.
|
||||
|
||||
[HIL]: https://github.com/esp-rs/esp-hal/tree/main/hil-test
|
||||
|
||||
### Testing Your Contributions
|
||||
|
||||
Ensuring the quality and reliability of `esp-hal` is a shared responsibility, and testing plays a critical role in this process. Our GitHub CI automatically checks the buildability of all examples and drivers within the project. However, automated tests can't catch everything, especially when it comes to the nuanced behavior of hardware interactions. So make sure that the example affected by your change works as expected.
|
||||
|
||||
Further steps that can (or should) be taken in testing:
|
||||
|
||||
* Using [xtask], build examples for the specified chip.
|
||||
* Build the documentation and run the doctests if they have been modified using the `build-documentation` and `run-doc-test` commands in [xtask].
|
||||
* Run the [HIL] tests locally if changes have been made to them.
|
||||
|
||||
[xtask]: https://github.com/esp-rs/esp-hal/tree/main/xtask
|
||||
|
||||
### Commit Your Updates
|
||||
|
||||
Commit your changes once you're satisfied. Review your own work to streamline the review process later. Use `rustfmt` and `cargo clippy` to ensure your code adheres to Rust's conventions.
|
||||
|
||||
```shell
|
||||
rustup component add rustfmt
|
||||
rustup component add clippy
|
||||
```
|
||||
|
||||
We _strongly_ recommend that you format your code before committing to ensure consistency throughout the project.
|
||||
To format all packages in the workspace, run the following command in a terminal from the root of the repository:
|
||||
|
||||
```shell
|
||||
cargo xtask fmt-packages
|
||||
```
|
||||
|
||||
We also recommend using the `lint-packages` subcommand, which uses `cargo clippy` and will lint the entire driver in order to catch common mistakes in the code.
|
||||
|
||||
```shell
|
||||
cargo xtask lint-packages
|
||||
```
|
||||
|
||||
This will use `rustfmt` to ensure that all source code is formatted correctly prior to committing.
|
||||
|
||||
## Pull Request: From Submission to Merge
|
||||
|
||||
* Fill the pull request template so that we can review your PR. This template helps reviewers understand your changes as well as the purpose of your pull request.
|
||||
* [Link your PR] to any relevant issues it addresses.
|
||||
* [Allow edits from maintainers] so the branch can be updated for a merge. Once you submit your PR, a Docs team member will review your proposal. We may ask questions or request additional information.
|
||||
* Make sure you add an entry with your changes to the [Changelog]. Also make sure that it is in the appropriate section of the document.
|
||||
* Make sure you add your changes to the current [migration guide].
|
||||
* We may ask for changes to be made before a PR can be merged, either using [suggested changes] or pull request comments. You can apply suggested changes directly through the UI. You can make any other changes in your fork, then commit them to your branch.
|
||||
* As you update your PR and apply changes, mark each conversation as [resolved].
|
||||
* Resolve merge conflicts if they arise, using resources like [this git tutorial] for help.
|
||||
|
||||
[Link your PR]: https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue
|
||||
[Allow edits from maintainers]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-forkmember
|
||||
[Changelog]: esp-hal/CHANGELOG.md
|
||||
[migration guide]: esp-hal/MIGRATING-0.20.md
|
||||
[suggested changes]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/incorporating-feedback-in-your-pull-request
|
||||
[resolved]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/commenting-on-a-pull-request#resolving-conversations
|
||||
[this git tutorial]: https://github.com/skills/resolve-merge-conflicts
|
||||
|
||||
|
||||
## Your PR is Merged!
|
||||
|
||||
Congratulations! The `esp-rs` team thanks you for your contributions!
|
||||
|
||||
Contributing to open source extends beyond just code! Each contribution, regardless of size, plays a significant role. We appreciate your involvement in this collective endeavor.
|
||||
1
esp-alloc/.clippy.toml
Normal file
1
esp-alloc/.clippy.toml
Normal file
@ -0,0 +1 @@
|
||||
avoid-breaking-exported-api = false
|
||||
36
esp-alloc/CHANGELOG.md
Normal file
36
esp-alloc/CHANGELOG.md
Normal file
@ -0,0 +1,36 @@
|
||||
# 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]
|
||||
|
||||
### Added
|
||||
|
||||
- `esp_alloc::HEAP.stats()` can now be used to get heap usage informations (#2137)
|
||||
|
||||
### Changed
|
||||
|
||||
### Fixed
|
||||
|
||||
### Removed
|
||||
|
||||
## 0.5.0 - 2024-10-10
|
||||
|
||||
### Changed
|
||||
|
||||
- a global allocator is created in esp-alloc, now you need to add individual memory regions (up to 3) to the allocator (#2099)
|
||||
|
||||
## 0.4.0 - 2024-06-04
|
||||
|
||||
## 0.3.0 - 2023-04-25
|
||||
|
||||
## 0.2.1 - 2023-04-21
|
||||
|
||||
## 0.2.0 - 2023-02-22
|
||||
|
||||
## 0.1.0 - 2022-07-25
|
||||
|
||||
[Unreleased]: https://github.com/esp-rs/esp-hal/commits/main/esp-alloc?since=2024-10-10
|
||||
37
esp-alloc/Cargo.toml
Normal file
37
esp-alloc/Cargo.toml
Normal file
@ -0,0 +1,37 @@
|
||||
[package]
|
||||
name = "esp-alloc"
|
||||
version = "0.5.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.68"
|
||||
description = "A heap allocator for Espressif devices"
|
||||
keywords = ["allocator", "embedded", "embedded-hal", "esp32", "espressif", "memory"]
|
||||
categories = ["embedded", "memory-management", "no-std"]
|
||||
repository = "https://github.com/esp-rs/esp-hal"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
default-target = "riscv32imc-unknown-none-elf"
|
||||
features = ["nightly"]
|
||||
|
||||
[dependencies]
|
||||
defmt = { version = "0.3.8", optional = true }
|
||||
cfg-if = "1.0.0"
|
||||
critical-section = "1.1.3"
|
||||
enumset = "1.1.5"
|
||||
linked_list_allocator = { version = "0.10.5", default-features = false, features = ["const_mut_refs"] }
|
||||
document-features = "0.2.10"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
nightly = []
|
||||
|
||||
## Implement `defmt::Format` on certain types.
|
||||
defmt = ["dep:defmt"]
|
||||
|
||||
## Enable this feature if you want to keep stats about the internal heap usage such as:
|
||||
## - Max memory usage since initialization of the heap
|
||||
## - Total allocated memory since initialization of the heap
|
||||
## - Total freed memory since initialization of the heap
|
||||
##
|
||||
## ⚠️ Note: Enabling this feature will require extra computation every time alloc/dealloc is called.
|
||||
internal-heap-stats = []
|
||||
26
esp-alloc/README.md
Normal file
26
esp-alloc/README.md
Normal file
@ -0,0 +1,26 @@
|
||||
# esp-alloc
|
||||
|
||||
[](https://crates.io/crates/esp-alloc)
|
||||
[](https://docs.rs/esp-alloc)
|
||||

|
||||

|
||||
[](https://matrix.to/#/#esp-rs:matrix.org)
|
||||
|
||||
A simple `no_std` heap allocator for RISC-V and Xtensa processors from Espressif. Supports all currently available ESP32 devices.
|
||||
|
||||
**NOTE:** using this as your global allocator requires using Rust 1.68 or greater, or the `nightly` release channel.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of:
|
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
- MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in
|
||||
the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without
|
||||
any additional terms or conditions.
|
||||
589
esp-alloc/src/lib.rs
Normal file
589
esp-alloc/src/lib.rs
Normal file
@ -0,0 +1,589 @@
|
||||
//! A `no_std` heap allocator for RISC-V and Xtensa processors from
|
||||
//! Espressif. Supports all currently available ESP32 devices.
|
||||
//!
|
||||
//! **NOTE:** using this as your global allocator requires using Rust 1.68 or
|
||||
//! greater, or the `nightly` release channel.
|
||||
//!
|
||||
//! # Using this as your Global Allocator
|
||||
//!
|
||||
//! ```rust
|
||||
//! use esp_alloc as _;
|
||||
//!
|
||||
//! fn init_heap() {
|
||||
//! const HEAP_SIZE: usize = 32 * 1024;
|
||||
//! static mut HEAP: MaybeUninit<[u8; HEAP_SIZE]> = MaybeUninit::uninit();
|
||||
//!
|
||||
//! unsafe {
|
||||
//! esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new(
|
||||
//! HEAP.as_mut_ptr() as *mut u8,
|
||||
//! HEAP_SIZE,
|
||||
//! esp_alloc::MemoryCapability::Internal.into(),
|
||||
//! ));
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! # Using this with the nightly `allocator_api`-feature
|
||||
//! Sometimes you want to have more control over allocations.
|
||||
//!
|
||||
//! For that, it's convenient to use the nightly `allocator_api`-feature,
|
||||
//! which allows you to specify an allocator for single allocations.
|
||||
//!
|
||||
//! **NOTE:** To use this, you have to enable the crate's `nightly` feature
|
||||
//! flag.
|
||||
//!
|
||||
//! Create and initialize an allocator to use in single allocations:
|
||||
//! ```rust
|
||||
//! static PSRAM_ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty();
|
||||
//!
|
||||
//! fn init_psram_heap() {
|
||||
//! unsafe {
|
||||
//! PSRAM_ALLOCATOR.add_region(esp_alloc::HeapRegion::new(
|
||||
//! psram::psram_vaddr_start() as *mut u8,
|
||||
//! psram::PSRAM_BYTES,
|
||||
//! esp_alloc::MemoryCapability::Internal.into(),
|
||||
//! ));
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! And then use it in an allocation:
|
||||
//! ```rust
|
||||
//! let large_buffer: Vec<u8, _> = Vec::with_capacity_in(1048576, &PSRAM_ALLOCATOR);
|
||||
//! ```
|
||||
//!
|
||||
//! You can also get stats about the heap usage at anytime with:
|
||||
//! ```rust
|
||||
//! let stats: HeapStats = esp_alloc::HEAP.stats();
|
||||
//! // HeapStats implements the Display and defmt::Format traits, so you can pretty-print the heap stats.
|
||||
//! println!("{}", stats);
|
||||
//! ```
|
||||
//!
|
||||
//! ```txt
|
||||
//! HEAP INFO
|
||||
//! Size: 131068
|
||||
//! Current usage: 46148
|
||||
//! Max usage: 46148
|
||||
//! Total freed: 0
|
||||
//! Total allocated: 46148
|
||||
//! Memory Layout:
|
||||
//! Internal | ████████████░░░░░░░░░░░░░░░░░░░░░░░ | Used: 35% (Used 46148 of 131068, free: 84920)
|
||||
//! Unused | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ |
|
||||
//! Unused | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ |
|
||||
//! ```
|
||||
//! ## Feature Flags
|
||||
#![doc = document_features::document_features!()]
|
||||
#![no_std]
|
||||
#![cfg_attr(feature = "nightly", feature(allocator_api))]
|
||||
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
|
||||
|
||||
mod macros;
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
use core::alloc::{AllocError, Allocator};
|
||||
use core::{
|
||||
alloc::{GlobalAlloc, Layout},
|
||||
cell::RefCell,
|
||||
fmt::Display,
|
||||
ptr::{self, NonNull},
|
||||
};
|
||||
|
||||
use critical_section::Mutex;
|
||||
use enumset::{EnumSet, EnumSetType};
|
||||
use linked_list_allocator::Heap;
|
||||
|
||||
/// The global allocator instance
|
||||
#[global_allocator]
|
||||
pub static HEAP: EspHeap = EspHeap::empty();
|
||||
|
||||
const NON_REGION: Option<HeapRegion> = None;
|
||||
|
||||
const BAR_WIDTH: usize = 35;
|
||||
|
||||
fn write_bar(f: &mut core::fmt::Formatter<'_>, usage_percent: usize) -> core::fmt::Result {
|
||||
let used_blocks = BAR_WIDTH * usage_percent / 100;
|
||||
(0..used_blocks).try_for_each(|_| write!(f, "█"))?;
|
||||
(used_blocks..BAR_WIDTH).try_for_each(|_| write!(f, "░"))
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
fn write_bar_defmt(fmt: defmt::Formatter, usage_percent: usize) {
|
||||
let used_blocks = BAR_WIDTH * usage_percent / 100;
|
||||
(0..used_blocks).for_each(|_| defmt::write!(fmt, "█"));
|
||||
(used_blocks..BAR_WIDTH).for_each(|_| defmt::write!(fmt, "░"));
|
||||
}
|
||||
|
||||
#[derive(EnumSetType, Debug)]
|
||||
/// Describes the properties of a memory region
|
||||
pub enum MemoryCapability {
|
||||
/// Memory must be internal; specifically it should not disappear when
|
||||
/// flash/spiram cache is switched off
|
||||
Internal,
|
||||
/// Memory must be in SPI RAM
|
||||
External,
|
||||
}
|
||||
|
||||
/// Stats for a heap region
|
||||
#[derive(Debug)]
|
||||
pub struct RegionStats {
|
||||
/// Total usable size of the heap region in bytes.
|
||||
size: usize,
|
||||
|
||||
/// Currently used size of the heap region in bytes.
|
||||
used: usize,
|
||||
|
||||
/// Free size of the heap region in bytes.
|
||||
free: usize,
|
||||
|
||||
/// Capabilities of the memory region.
|
||||
capabilities: EnumSet<MemoryCapability>,
|
||||
}
|
||||
|
||||
impl Display for RegionStats {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
let usage_percent = self.used * 100 / self.size;
|
||||
|
||||
// Display Memory type
|
||||
if self.capabilities.contains(MemoryCapability::Internal) {
|
||||
write!(f, "Internal")?;
|
||||
} else if self.capabilities.contains(MemoryCapability::External) {
|
||||
write!(f, "External")?;
|
||||
} else {
|
||||
write!(f, "Unknown")?;
|
||||
}
|
||||
|
||||
write!(f, " | ")?;
|
||||
|
||||
write_bar(f, usage_percent)?;
|
||||
|
||||
write!(
|
||||
f,
|
||||
" | Used: {}% (Used {} of {}, free: {})",
|
||||
usage_percent, self.used, self.size, self.free
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
impl defmt::Format for RegionStats {
|
||||
fn format(&self, fmt: defmt::Formatter) {
|
||||
let usage_percent = self.used * 100 / self.size;
|
||||
|
||||
if self.capabilities.contains(MemoryCapability::Internal) {
|
||||
defmt::write!(fmt, "Internal");
|
||||
} else if self.capabilities.contains(MemoryCapability::External) {
|
||||
defmt::write!(fmt, "External");
|
||||
} else {
|
||||
defmt::write!(fmt, "Unknown");
|
||||
}
|
||||
|
||||
defmt::write!(fmt, " | ");
|
||||
|
||||
write_bar_defmt(fmt, usage_percent);
|
||||
|
||||
defmt::write!(
|
||||
fmt,
|
||||
" | Used: {}% (Used {} of {}, free: {})",
|
||||
usage_percent,
|
||||
self.used,
|
||||
self.size,
|
||||
self.free
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A memory region to be used as heap memory
|
||||
pub struct HeapRegion {
|
||||
heap: Heap,
|
||||
capabilities: EnumSet<MemoryCapability>,
|
||||
}
|
||||
|
||||
impl HeapRegion {
|
||||
/// Create a new [HeapRegion] with the given capabilities
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - The supplied memory region must be available for the entire program
|
||||
/// (`'static`).
|
||||
/// - The supplied memory region must be exclusively available to the heap
|
||||
/// only, no aliasing.
|
||||
/// - `size > 0`.
|
||||
pub unsafe fn new(
|
||||
heap_bottom: *mut u8,
|
||||
size: usize,
|
||||
capabilities: EnumSet<MemoryCapability>,
|
||||
) -> Self {
|
||||
let mut heap = Heap::empty();
|
||||
heap.init(heap_bottom, size);
|
||||
|
||||
Self { heap, capabilities }
|
||||
}
|
||||
|
||||
/// Return stats for the current memory region
|
||||
pub fn stats(&self) -> RegionStats {
|
||||
RegionStats {
|
||||
size: self.heap.size(),
|
||||
used: self.heap.used(),
|
||||
free: self.heap.free(),
|
||||
capabilities: self.capabilities,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Stats for a heap allocator
|
||||
///
|
||||
/// Enable the "internal-heap-stats" feature if you want collect additional heap
|
||||
/// informations at the cost of extra cpu time during every alloc/dealloc.
|
||||
#[derive(Debug)]
|
||||
pub struct HeapStats {
|
||||
/// Granular stats for all the configured memory regions.
|
||||
region_stats: [Option<RegionStats>; 3],
|
||||
|
||||
/// Total size of all combined heap regions in bytes.
|
||||
size: usize,
|
||||
|
||||
/// Current usage of the heap across all configured regions in bytes.
|
||||
current_usage: usize,
|
||||
|
||||
/// Estimation of the max used heap in bytes.
|
||||
#[cfg(feature = "internal-heap-stats")]
|
||||
max_usage: usize,
|
||||
|
||||
/// Estimation of the total allocated bytes since initialization.
|
||||
#[cfg(feature = "internal-heap-stats")]
|
||||
total_allocated: usize,
|
||||
|
||||
/// Estimation of the total freed bytes since initialization.
|
||||
#[cfg(feature = "internal-heap-stats")]
|
||||
total_freed: usize,
|
||||
}
|
||||
|
||||
impl Display for HeapStats {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
writeln!(f, "HEAP INFO")?;
|
||||
writeln!(f, "Size: {}", self.size)?;
|
||||
writeln!(f, "Current usage: {}", self.current_usage)?;
|
||||
#[cfg(feature = "internal-heap-stats")]
|
||||
{
|
||||
writeln!(f, "Max usage: {}", self.max_usage)?;
|
||||
writeln!(f, "Total freed: {}", self.total_freed)?;
|
||||
writeln!(f, "Total allocated: {}", self.total_allocated)?;
|
||||
}
|
||||
writeln!(f, "Memory Layout: ")?;
|
||||
for region in self.region_stats.iter() {
|
||||
if let Some(region) = region.as_ref() {
|
||||
region.fmt(f)?;
|
||||
writeln!(f)?;
|
||||
} else {
|
||||
// Display unused memory regions
|
||||
write!(f, "Unused | ")?;
|
||||
write_bar(f, 0)?;
|
||||
writeln!(f, " |")?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
impl defmt::Format for HeapStats {
|
||||
fn format(&self, fmt: defmt::Formatter) {
|
||||
defmt::write!(fmt, "HEAP INFO\n");
|
||||
defmt::write!(fmt, "Size: {}\n", self.size);
|
||||
defmt::write!(fmt, "Current usage: {}\n", self.current_usage);
|
||||
#[cfg(feature = "internal-heap-stats")]
|
||||
{
|
||||
defmt::write!(fmt, "Max usage: {}\n", self.max_usage);
|
||||
defmt::write!(fmt, "Total freed: {}\n", self.total_freed);
|
||||
defmt::write!(fmt, "Total allocated: {}\n", self.total_allocated);
|
||||
}
|
||||
defmt::write!(fmt, "Memory Layout:\n");
|
||||
for region in self.region_stats.iter() {
|
||||
if let Some(region) = region.as_ref() {
|
||||
defmt::write!(fmt, "{}\n", region);
|
||||
} else {
|
||||
defmt::write!(fmt, "Unused | ");
|
||||
write_bar_defmt(fmt, 0);
|
||||
defmt::write!(fmt, " |\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal stats to keep track across multiple regions.
|
||||
#[cfg(feature = "internal-heap-stats")]
|
||||
struct InternalHeapStats {
|
||||
max_usage: usize,
|
||||
total_allocated: usize,
|
||||
total_freed: usize,
|
||||
}
|
||||
|
||||
/// A memory allocator
|
||||
///
|
||||
/// In addition to what Rust's memory allocator can do it allows to allocate
|
||||
/// memory in regions satisfying specific needs.
|
||||
pub struct EspHeap {
|
||||
heap: Mutex<RefCell<[Option<HeapRegion>; 3]>>,
|
||||
#[cfg(feature = "internal-heap-stats")]
|
||||
internal_heap_stats: Mutex<RefCell<InternalHeapStats>>,
|
||||
}
|
||||
|
||||
impl EspHeap {
|
||||
/// Crate a new UNINITIALIZED heap allocator
|
||||
pub const fn empty() -> Self {
|
||||
EspHeap {
|
||||
heap: Mutex::new(RefCell::new([NON_REGION; 3])),
|
||||
#[cfg(feature = "internal-heap-stats")]
|
||||
internal_heap_stats: Mutex::new(RefCell::new(InternalHeapStats {
|
||||
max_usage: 0,
|
||||
total_allocated: 0,
|
||||
total_freed: 0,
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a memory region to the heap
|
||||
///
|
||||
/// `heap_bottom` is a pointer to the location of the bottom of the heap.
|
||||
///
|
||||
/// `size` is the size of the heap in bytes.
|
||||
///
|
||||
/// You can add up to three regions per allocator.
|
||||
///
|
||||
/// Note that:
|
||||
///
|
||||
/// - Memory is allocated from the first suitable memory region first
|
||||
///
|
||||
/// - The heap grows "upwards", towards larger addresses. Thus `end_addr`
|
||||
/// must be larger than `start_addr`
|
||||
///
|
||||
/// - The size of the heap is `(end_addr as usize) - (start_addr as usize)`.
|
||||
/// The allocator won't use the byte at `end_addr`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - The supplied memory region must be available for the entire program (a
|
||||
/// `'static` lifetime).
|
||||
/// - The supplied memory region must be exclusively available to the heap
|
||||
/// only, no aliasing.
|
||||
/// - `size > 0`.
|
||||
pub unsafe fn add_region(&self, region: HeapRegion) {
|
||||
critical_section::with(|cs| {
|
||||
let mut regions = self.heap.borrow_ref_mut(cs);
|
||||
let free = regions
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|v| v.1.is_none())
|
||||
.map(|v| v.0);
|
||||
|
||||
if let Some(free) = free {
|
||||
regions[free] = Some(region);
|
||||
} else {
|
||||
panic!(
|
||||
"Exceeded the maximum of {} heap memory regions",
|
||||
regions.len()
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns an estimate of the amount of bytes in use in all memory regions.
|
||||
pub fn used(&self) -> usize {
|
||||
critical_section::with(|cs| {
|
||||
let regions = self.heap.borrow_ref(cs);
|
||||
let mut used = 0;
|
||||
for region in regions.iter() {
|
||||
if let Some(region) = region.as_ref() {
|
||||
used += region.heap.used();
|
||||
}
|
||||
}
|
||||
used
|
||||
})
|
||||
}
|
||||
|
||||
/// Return usage stats for the [Heap].
|
||||
///
|
||||
/// Note:
|
||||
/// [HeapStats] directly implements [Display], so this function can be
|
||||
/// called from within `println!()` to pretty-print the usage of the
|
||||
/// heap.
|
||||
pub fn stats(&self) -> HeapStats {
|
||||
const EMPTY_REGION_STAT: Option<RegionStats> = None;
|
||||
let mut region_stats: [Option<RegionStats>; 3] = [EMPTY_REGION_STAT; 3];
|
||||
|
||||
critical_section::with(|cs| {
|
||||
let mut used = 0;
|
||||
let mut free = 0;
|
||||
let regions = self.heap.borrow_ref(cs);
|
||||
for (id, region) in regions.iter().enumerate() {
|
||||
if let Some(region) = region.as_ref() {
|
||||
let stats = region.stats();
|
||||
free += stats.free;
|
||||
used += stats.used;
|
||||
region_stats[id] = Some(region.stats());
|
||||
}
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "internal-heap-stats")] {
|
||||
let internal_heap_stats = self.internal_heap_stats.borrow_ref(cs);
|
||||
HeapStats {
|
||||
region_stats,
|
||||
size: free + used,
|
||||
current_usage: used,
|
||||
max_usage: internal_heap_stats.max_usage,
|
||||
total_allocated: internal_heap_stats.total_allocated,
|
||||
total_freed: internal_heap_stats.total_freed,
|
||||
}
|
||||
} else {
|
||||
HeapStats {
|
||||
region_stats,
|
||||
size: free + used,
|
||||
current_usage: used,
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns an estimate of the amount of bytes available.
|
||||
pub fn free(&self) -> usize {
|
||||
self.free_caps(EnumSet::empty())
|
||||
}
|
||||
|
||||
/// The free heap satisfying the given requirements
|
||||
pub fn free_caps(&self, capabilities: EnumSet<MemoryCapability>) -> usize {
|
||||
critical_section::with(|cs| {
|
||||
let regions = self.heap.borrow_ref(cs);
|
||||
let mut free = 0;
|
||||
for region in regions.iter().filter(|region| {
|
||||
if region.is_some() {
|
||||
region
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.capabilities
|
||||
.is_superset(capabilities)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}) {
|
||||
if let Some(region) = region.as_ref() {
|
||||
free += region.heap.free();
|
||||
}
|
||||
}
|
||||
free
|
||||
})
|
||||
}
|
||||
|
||||
/// Allocate memory in a region satisfying the given requirements.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is unsafe because undefined behavior can result
|
||||
/// if the caller does not ensure that `layout` has non-zero size.
|
||||
///
|
||||
/// The allocated block of memory may or may not be initialized.
|
||||
pub unsafe fn alloc_caps(
|
||||
&self,
|
||||
capabilities: EnumSet<MemoryCapability>,
|
||||
layout: Layout,
|
||||
) -> *mut u8 {
|
||||
critical_section::with(|cs| {
|
||||
#[cfg(feature = "internal-heap-stats")]
|
||||
let before = self.used();
|
||||
let mut regions = self.heap.borrow_ref_mut(cs);
|
||||
let mut iter = (*regions).iter_mut().filter(|region| {
|
||||
if region.is_some() {
|
||||
region
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.capabilities
|
||||
.is_superset(capabilities)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
let res = loop {
|
||||
if let Some(Some(region)) = iter.next() {
|
||||
let res = region.heap.allocate_first_fit(layout);
|
||||
if let Ok(res) = res {
|
||||
break Some(res);
|
||||
}
|
||||
} else {
|
||||
break None;
|
||||
}
|
||||
};
|
||||
|
||||
res.map_or(ptr::null_mut(), |allocation| {
|
||||
#[cfg(feature = "internal-heap-stats")]
|
||||
{
|
||||
let mut internal_heap_stats = self.internal_heap_stats.borrow_ref_mut(cs);
|
||||
drop(regions);
|
||||
// We need to call used because [linked_list_allocator::Heap] does internal size
|
||||
// alignment so we cannot use the size provided by the layout.
|
||||
let used = self.used();
|
||||
|
||||
internal_heap_stats.total_allocated += used - before;
|
||||
internal_heap_stats.max_usage =
|
||||
core::cmp::max(internal_heap_stats.max_usage, used);
|
||||
}
|
||||
|
||||
allocation.as_ptr()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl GlobalAlloc for EspHeap {
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
self.alloc_caps(EnumSet::empty(), layout)
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
||||
if ptr.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
critical_section::with(|cs| {
|
||||
#[cfg(feature = "internal-heap-stats")]
|
||||
let before = self.used();
|
||||
let mut regions = self.heap.borrow_ref_mut(cs);
|
||||
let mut iter = (*regions).iter_mut();
|
||||
|
||||
while let Some(Some(region)) = iter.next() {
|
||||
if region.heap.bottom() <= ptr && region.heap.top() >= ptr {
|
||||
region.heap.deallocate(NonNull::new_unchecked(ptr), layout);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "internal-heap-stats")]
|
||||
{
|
||||
let mut internal_heap_stats = self.internal_heap_stats.borrow_ref_mut(cs);
|
||||
drop(regions);
|
||||
// We need to call `used()` because [linked_list_allocator::Heap] does internal
|
||||
// size alignment so we cannot use the size provided by the
|
||||
// layout.
|
||||
internal_heap_stats.total_freed += before - self.used();
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
unsafe impl Allocator for EspHeap {
|
||||
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
|
||||
let raw_ptr = unsafe { self.alloc(layout) };
|
||||
|
||||
if raw_ptr.is_null() {
|
||||
return Err(AllocError);
|
||||
}
|
||||
|
||||
let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
|
||||
Ok(NonNull::slice_from_raw_parts(ptr, layout.size()))
|
||||
}
|
||||
|
||||
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
|
||||
self.dealloc(ptr.as_ptr(), layout);
|
||||
}
|
||||
}
|
||||
43
esp-alloc/src/macros.rs
Normal file
43
esp-alloc/src/macros.rs
Normal file
@ -0,0 +1,43 @@
|
||||
//! Macros provided for convenience
|
||||
|
||||
/// Initialize a global heap allocator providing a heap of the given size in
|
||||
/// bytes
|
||||
#[macro_export]
|
||||
macro_rules! heap_allocator {
|
||||
($size:expr) => {{
|
||||
static mut HEAP: core::mem::MaybeUninit<[u8; $size]> = core::mem::MaybeUninit::uninit();
|
||||
|
||||
unsafe {
|
||||
$crate::HEAP.add_region($crate::HeapRegion::new(
|
||||
HEAP.as_mut_ptr() as *mut u8,
|
||||
$size,
|
||||
$crate::MemoryCapability::Internal.into(),
|
||||
));
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
/// Initialize a global heap allocator backed by PSRAM
|
||||
///
|
||||
/// You need a SoC which supports PSRAM
|
||||
/// and activate the feature to enable it. You need to pass the PSRAM peripheral
|
||||
/// and the psram module path.
|
||||
///
|
||||
/// # Usage
|
||||
/// ```rust, no_run
|
||||
/// esp_alloc::psram_allocator!(peripherals.PSRAM, hal::psram);
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! psram_allocator {
|
||||
($peripheral:expr, $psram_module:path) => {{
|
||||
use $psram_module as _psram;
|
||||
let (start, size) = _psram::psram_raw_parts(&$peripheral);
|
||||
unsafe {
|
||||
$crate::HEAP.add_region($crate::HeapRegion::new(
|
||||
start,
|
||||
size,
|
||||
$crate::MemoryCapability::External.into(),
|
||||
));
|
||||
}
|
||||
}};
|
||||
}
|
||||
63
esp-backtrace/CHANGELOG.md
Normal file
63
esp-backtrace/CHANGELOG.md
Normal file
@ -0,0 +1,63 @@
|
||||
# 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]
|
||||
|
||||
### Added
|
||||
|
||||
### Changed
|
||||
|
||||
### Fixed
|
||||
|
||||
### Removed
|
||||
|
||||
## 0.14.2 - 2024-10-10
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix build when not using `panic-handler` (#2257)
|
||||
|
||||
## 0.14.1 - 2024-09-06
|
||||
|
||||
### Added
|
||||
|
||||
### Changed
|
||||
|
||||
- Print a more helpful message in case of a `Cp0Disabled` exception (#2061)
|
||||
|
||||
### Fixed
|
||||
|
||||
### Removed
|
||||
|
||||
## 0.14.0 - 2024-08-29
|
||||
|
||||
### Added
|
||||
|
||||
- Add custom-pre-backtrace feature (#1822)
|
||||
|
||||
### Changed
|
||||
|
||||
- Improve panic message printing (#1823)
|
||||
|
||||
## 0.13.0 - 2024-07-16
|
||||
|
||||
No changes - published to avoid conflicts with `esp-println`
|
||||
|
||||
## 0.12.2 - 2024-07-15
|
||||
|
||||
### Changed
|
||||
|
||||
- Remove build script check for `nightly-2024-06-12` (#1788)
|
||||
|
||||
## 0.12.1 - 2024-06-19
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix compilation for nightly after 2024-06-12. (#1681)
|
||||
- Only prints float registers on targets which have them. (#1690)
|
||||
|
||||
[Unreleased]: https://github.com/esp-rs/esp-hal/commits/main/esp-backtrace?since=2024-10-10
|
||||
55
esp-backtrace/Cargo.toml
Normal file
55
esp-backtrace/Cargo.toml
Normal file
@ -0,0 +1,55 @@
|
||||
[package]
|
||||
name = "esp-backtrace"
|
||||
version = "0.14.2"
|
||||
edition = "2021"
|
||||
rust-version = "1.76.0"
|
||||
description = "Bare-metal backtrace support for Espressif devices"
|
||||
keywords = ["backtrace", "embedded", "esp32", "espressif"]
|
||||
categories = ["embedded", "hardware-support", "no-std"]
|
||||
repository = "https://github.com/esp-rs/esp-hal"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
default-target = "riscv32imc-unknown-none-elf"
|
||||
features = ["esp32c3", "panic-handler", "exception-handler", "println", "esp-println/uart"]
|
||||
|
||||
[dependencies]
|
||||
defmt = { version = "0.3.8", optional = true }
|
||||
esp-println = { version = "0.12.0", optional = true, default-features = false, path = "../esp-println" }
|
||||
semihosting = { version = "0.1.15", optional = true }
|
||||
|
||||
[build-dependencies]
|
||||
esp-build = { version = "0.1.0", path = "../esp-build" }
|
||||
|
||||
[features]
|
||||
default = ["colors"]
|
||||
|
||||
# You must enable exactly one of the below features to support the correct chip:
|
||||
esp32 = ["esp-println?/esp32", "semihosting?/openocd-semihosting", "print-float-registers"]
|
||||
esp32c2 = ["esp-println?/esp32c2"]
|
||||
esp32c3 = ["esp-println?/esp32c3"]
|
||||
esp32c6 = ["esp-println?/esp32c6"]
|
||||
esp32h2 = ["esp-println?/esp32h2"]
|
||||
esp32p4 = ["esp-println?/esp32p4"]
|
||||
esp32s2 = ["esp-println?/esp32s2", "semihosting?/openocd-semihosting"]
|
||||
esp32s3 = ["esp-println?/esp32s3", "semihosting?/openocd-semihosting", "print-float-registers"]
|
||||
|
||||
# Use esp-println
|
||||
println = ["dep:esp-println"]
|
||||
|
||||
# Use defmt
|
||||
defmt = ["dep:defmt"]
|
||||
|
||||
print-float-registers = [] # TODO support esp32p4
|
||||
|
||||
# You may optionally enable one or more of the below features to provide
|
||||
# additional functionality:
|
||||
colors = []
|
||||
custom-halt = []
|
||||
custom-pre-backtrace = []
|
||||
exception-handler = []
|
||||
halt-cores = []
|
||||
panic-handler = []
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = "allow"
|
||||
60
esp-backtrace/README.md
Normal file
60
esp-backtrace/README.md
Normal file
@ -0,0 +1,60 @@
|
||||
# esp-backtrace - backtrace for ESP32 bare-metal
|
||||
|
||||
[](https://crates.io/crates/esp-backtrace)
|
||||
[](https://docs.rs/esp-backtrace)
|
||||

|
||||

|
||||
[](https://matrix.to/#/#esp-rs:matrix.org)
|
||||
|
||||
Supports the ESP32, ESP32-C2/C3/C6, ESP32-H2, ESP32-P4, and ESP32-S2/S3. Optional exception and panic handlers are included, both of which can be enabled via their respective features.
|
||||
|
||||
Please note that when targeting a RISC-V device, you **need** to force frame pointers (i.e. `"-C", "force-frame-pointers",` in your `.cargo/config.toml`); this is **not** required for Xtensa.
|
||||
|
||||
You can get an array of backtrace addresses (currently limited to 10) via `arch::backtrace()` if
|
||||
you want to create a backtrace yourself (i.e. not using the panic or exception handler).
|
||||
|
||||
When using the panic and/or exception handler make sure to include `use esp_backtrace as _;`.
|
||||
|
||||
## Features
|
||||
|
||||
| Feature | Description |
|
||||
|----------------------|--------------------------------------------------------------------------------------------------------------------|
|
||||
| esp32 | Target ESP32 |
|
||||
| esp32c2 | Target ESP32-C2 |
|
||||
| esp32c3 | Target ESP32-C3 |
|
||||
| esp32c6 | Target ESP32-C6 |
|
||||
| esp32h2 | Target ESP32-H2 |
|
||||
| esp32p4 | Target ESP32-P4 |
|
||||
| esp32s2 | Target ESP32-S2 |
|
||||
| esp32s3 | Target ESP32-S3 |
|
||||
| panic-handler | Include a panic handler, will add `esp-println` as a dependency |
|
||||
| exception-handler | Include an exception handler, will add `esp-println` as a dependency |
|
||||
| println | Use `esp-println` to print messages |
|
||||
| defmt | Use `defmt` logging to print messages\* (check [example](https://github.com/playfulFence/backtrace-defmt-example)) |
|
||||
| colors | Print messages in red\* |
|
||||
| halt-cores | Halt both CPUs on ESP32 / ESP32-S3 instead of doing a `loop {}` in case of a panic or exception |
|
||||
| semihosting | Call `semihosting::process::abort()` on panic. |
|
||||
| custom-halt | Invoke the extern function `custom_halt()` instead of doing a `loop {}` in case of a panic or exception |
|
||||
| custom-pre-backtrace | Invoke the extern function `custom_pre_backtrace()` before handling a panic or exception |
|
||||
|
||||
\* _only used for panic and exception handlers_
|
||||
|
||||
### `defmt` Feature
|
||||
|
||||
Please note that `defmt` does _not_ provide MSRV guarantees with releases, and as such we are not able to make any MSRV guarantees when this feature is enabled. For more information refer to the MSRV section of `defmt`'s README:
|
||||
https://github.com/knurling-rs/defmt?tab=readme-ov-file#msrv
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of:
|
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
- MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in
|
||||
the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without
|
||||
any additional terms or conditions.
|
||||
15
esp-backtrace/build.rs
Normal file
15
esp-backtrace/build.rs
Normal file
@ -0,0 +1,15 @@
|
||||
use esp_build::assert_unique_used_features;
|
||||
|
||||
fn main() {
|
||||
// Ensure that only a single chip is specified:
|
||||
assert_unique_used_features!(
|
||||
"esp32", "esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32p4", "esp32s2", "esp32s3"
|
||||
);
|
||||
|
||||
// Ensure that exactly a backend is selected:
|
||||
assert_unique_used_features!("defmt", "println");
|
||||
|
||||
if cfg!(feature = "custom-halt") && cfg!(feature = "halt-cores") {
|
||||
panic!("Only one of `custom-halt` and `halt-cores` can be enabled");
|
||||
}
|
||||
}
|
||||
341
esp-backtrace/src/lib.rs
Normal file
341
esp-backtrace/src/lib.rs
Normal file
@ -0,0 +1,341 @@
|
||||
#![allow(rustdoc::bare_urls, unused_macros)]
|
||||
#![cfg_attr(target_arch = "xtensa", feature(asm_experimental_arch))]
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
|
||||
#![no_std]
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
use defmt as _;
|
||||
#[cfg(feature = "println")]
|
||||
use esp_println as _;
|
||||
|
||||
const MAX_BACKTRACE_ADDRESSES: usize = 10;
|
||||
|
||||
#[cfg(feature = "colors")]
|
||||
const RESET: &str = "\u{001B}[0m";
|
||||
#[cfg(feature = "colors")]
|
||||
const RED: &str = "\u{001B}[31m";
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
macro_rules! println {
|
||||
("") => {
|
||||
// Do nothing if the string is just a space
|
||||
};
|
||||
($($arg:tt)*) => {
|
||||
defmt::error!($($arg)*);
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "println", not(feature = "defmt")))]
|
||||
macro_rules! println {
|
||||
($($arg:tt)*) => {
|
||||
esp_println::println!($($arg)*);
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused, unused_variables)]
|
||||
fn set_color_code(code: &str) {
|
||||
#[cfg(feature = "println")]
|
||||
{
|
||||
println!("{}", code);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "riscv32", path = "riscv.rs")]
|
||||
#[cfg_attr(target_arch = "xtensa", path = "xtensa.rs")]
|
||||
pub mod arch;
|
||||
|
||||
#[cfg(feature = "panic-handler")]
|
||||
#[panic_handler]
|
||||
fn panic_handler(info: &core::panic::PanicInfo) -> ! {
|
||||
pre_backtrace();
|
||||
|
||||
#[cfg(feature = "colors")]
|
||||
set_color_code(RED);
|
||||
|
||||
println!("");
|
||||
println!("====================== PANIC ======================");
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
println!("{}", info);
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
println!("{}", defmt::Display2Format(info));
|
||||
|
||||
println!("");
|
||||
println!("Backtrace:");
|
||||
println!("");
|
||||
|
||||
let backtrace = crate::arch::backtrace();
|
||||
#[cfg(target_arch = "riscv32")]
|
||||
if backtrace.iter().filter(|e| e.is_some()).count() == 0 {
|
||||
println!("No backtrace available - make sure to force frame-pointers. (see https://crates.io/crates/esp-backtrace)");
|
||||
}
|
||||
for addr in backtrace.into_iter().flatten() {
|
||||
#[cfg(all(feature = "colors", feature = "println"))]
|
||||
println!("{}0x{:x}", RED, addr - crate::arch::RA_OFFSET);
|
||||
|
||||
#[cfg(not(all(feature = "colors", feature = "println")))]
|
||||
println!("0x{:x}", addr - crate::arch::RA_OFFSET);
|
||||
}
|
||||
|
||||
#[cfg(feature = "colors")]
|
||||
set_color_code(RESET);
|
||||
|
||||
#[cfg(feature = "semihosting")]
|
||||
semihosting::process::abort();
|
||||
|
||||
#[cfg(not(feature = "semihosting"))]
|
||||
halt();
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "exception-handler", target_arch = "xtensa"))]
|
||||
#[no_mangle]
|
||||
#[link_section = ".rwtext"]
|
||||
unsafe fn __user_exception(cause: arch::ExceptionCause, context: arch::Context) {
|
||||
pre_backtrace();
|
||||
|
||||
#[cfg(feature = "colors")]
|
||||
set_color_code(RED);
|
||||
|
||||
// Unfortunately, a different formatter string is used
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
esp_println::println!("\n\nException occurred '{}'", cause);
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
defmt::error!("\n\nException occurred '{}'", cause);
|
||||
|
||||
println!("{:?}", context);
|
||||
|
||||
let backtrace = crate::arch::backtrace_internal(context.A1, 0);
|
||||
for e in backtrace {
|
||||
if let Some(addr) = e {
|
||||
println!("0x{:x}", addr);
|
||||
}
|
||||
}
|
||||
println!("");
|
||||
println!("");
|
||||
println!("");
|
||||
|
||||
#[cfg(feature = "colors")]
|
||||
set_color_code(RESET);
|
||||
|
||||
#[cfg(feature = "semihosting")]
|
||||
semihosting::process::abort();
|
||||
|
||||
#[cfg(not(feature = "semihosting"))]
|
||||
halt();
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "exception-handler", target_arch = "riscv32"))]
|
||||
#[export_name = "ExceptionHandler"]
|
||||
fn exception_handler(context: &arch::TrapFrame) -> ! {
|
||||
pre_backtrace();
|
||||
|
||||
let mepc = context.pc;
|
||||
let code = context.mcause & 0xff;
|
||||
let mtval = context.mtval;
|
||||
|
||||
#[cfg(feature = "colors")]
|
||||
set_color_code(RED);
|
||||
|
||||
if code == 14 {
|
||||
println!("");
|
||||
println!(
|
||||
"Stack overflow detected at 0x{:x} called by 0x{:x}",
|
||||
mepc, context.ra
|
||||
);
|
||||
println!("");
|
||||
} else {
|
||||
let code = match code {
|
||||
0 => "Instruction address misaligned",
|
||||
1 => "Instruction access fault",
|
||||
2 => "Illegal instruction",
|
||||
3 => "Breakpoint",
|
||||
4 => "Load address misaligned",
|
||||
5 => "Load access fault",
|
||||
6 => "Store/AMO address misaligned",
|
||||
7 => "Store/AMO access fault",
|
||||
8 => "Environment call from U-mode",
|
||||
9 => "Environment call from S-mode",
|
||||
10 => "Reserved",
|
||||
11 => "Environment call from M-mode",
|
||||
12 => "Instruction page fault",
|
||||
13 => "Load page fault",
|
||||
14 => "Reserved",
|
||||
15 => "Store/AMO page fault",
|
||||
_ => "UNKNOWN",
|
||||
};
|
||||
|
||||
println!(
|
||||
"Exception '{}' mepc=0x{:08x}, mtval=0x{:08x}",
|
||||
code, mepc, mtval
|
||||
);
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
println!("{:x?}", context);
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
println!("{:?}", context);
|
||||
|
||||
let backtrace = crate::arch::backtrace_internal(context.s0 as u32, 0);
|
||||
if backtrace.iter().filter(|e| e.is_some()).count() == 0 {
|
||||
println!("No backtrace available - make sure to force frame-pointers. (see https://crates.io/crates/esp-backtrace)");
|
||||
}
|
||||
for addr in backtrace.into_iter().flatten() {
|
||||
#[cfg(all(feature = "colors", feature = "println"))]
|
||||
println!("{}0x{:x}", RED, addr - crate::arch::RA_OFFSET);
|
||||
|
||||
#[cfg(not(all(feature = "colors", feature = "println")))]
|
||||
println!("0x{:x}", addr - crate::arch::RA_OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
println!("");
|
||||
println!("");
|
||||
println!("");
|
||||
|
||||
#[cfg(feature = "colors")]
|
||||
set_color_code(RESET);
|
||||
|
||||
#[cfg(feature = "semihosting")]
|
||||
semihosting::process::abort();
|
||||
|
||||
#[cfg(not(feature = "semihosting"))]
|
||||
halt();
|
||||
}
|
||||
|
||||
// Ensure that the address is in DRAM and that it is 16-byte aligned.
|
||||
//
|
||||
// Based loosely on the `esp_stack_ptr_in_dram` function from
|
||||
// `components/esp_hw_support/include/esp_memory_utils.h` in ESP-IDF.
|
||||
//
|
||||
// Address ranges can be found in `components/soc/$CHIP/include/soc/soc.h` as
|
||||
// `SOC_DRAM_LOW` and `SOC_DRAM_HIGH`.
|
||||
fn is_valid_ram_address(address: u32) -> bool {
|
||||
if (address & 0xF) != 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
#[cfg(feature = "esp32")]
|
||||
if !(0x3FFA_E000..=0x4000_0000).contains(&address) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#[cfg(feature = "esp32c2")]
|
||||
if !(0x3FCA_0000..=0x3FCE_0000).contains(&address) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#[cfg(feature = "esp32c3")]
|
||||
if !(0x3FC8_0000..=0x3FCE_0000).contains(&address) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#[cfg(feature = "esp32c6")]
|
||||
if !(0x4080_0000..=0x4088_0000).contains(&address) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#[cfg(feature = "esp32h2")]
|
||||
if !(0x4080_0000..=0x4085_0000).contains(&address) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#[cfg(feature = "esp32p4")]
|
||||
if !(0x4FF0_0000..=0x4FFC_0000).contains(&address) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#[cfg(feature = "esp32s2")]
|
||||
if !(0x3FFB_0000..=0x4000_0000).contains(&address) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#[cfg(feature = "esp32s3")]
|
||||
if !(0x3FC8_8000..=0x3FD0_0000).contains(&address) {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
any(
|
||||
not(any(feature = "esp32", feature = "esp32p4", feature = "esp32s3")),
|
||||
not(feature = "halt-cores")
|
||||
),
|
||||
not(feature = "custom-halt")
|
||||
))]
|
||||
#[allow(unused)]
|
||||
fn halt() -> ! {
|
||||
loop {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "custom-halt")]
|
||||
fn halt() -> ! {
|
||||
extern "Rust" {
|
||||
fn custom_halt() -> !;
|
||||
}
|
||||
unsafe { custom_halt() }
|
||||
}
|
||||
|
||||
// TODO: Enable `halt` function for `esp32p4` feature once implemented
|
||||
#[cfg(all(any(feature = "esp32", feature = "esp32s3"), feature = "halt-cores"))]
|
||||
#[allow(unused)]
|
||||
fn halt() -> ! {
|
||||
#[cfg(feature = "esp32")]
|
||||
mod registers {
|
||||
pub(crate) const OPTIONS0: u32 = 0x3ff48000;
|
||||
pub(crate) const SW_CPU_STALL: u32 = 0x3ff480ac;
|
||||
}
|
||||
|
||||
#[cfg(feature = "esp32p4")]
|
||||
mod registers {
|
||||
pub(crate) const SW_CPU_STALL: u32 = 0x50115200;
|
||||
}
|
||||
|
||||
#[cfg(feature = "esp32s3")]
|
||||
mod registers {
|
||||
pub(crate) const OPTIONS0: u32 = 0x60008000;
|
||||
pub(crate) const SW_CPU_STALL: u32 = 0x600080bc;
|
||||
}
|
||||
|
||||
let sw_cpu_stall = registers::SW_CPU_STALL as *mut u32;
|
||||
|
||||
#[cfg(feature = "esp32p4")]
|
||||
unsafe {}
|
||||
|
||||
#[cfg(not(feature = "esp32p4"))]
|
||||
unsafe {
|
||||
// We need to write the value "0x86" to stall a particular core. The write
|
||||
// location is split into two separate bit fields named "c0" and "c1", and the
|
||||
// two fields are located in different registers. Each core has its own pair of
|
||||
// "c0" and "c1" bit fields.
|
||||
|
||||
let options0 = registers::OPTIONS0 as *mut u32;
|
||||
|
||||
options0.write_volatile(options0.read_volatile() & !(0b1111) | 0b1010);
|
||||
|
||||
sw_cpu_stall.write_volatile(
|
||||
sw_cpu_stall.read_volatile() & !(0b111111 << 20) & !(0b111111 << 26)
|
||||
| (0x21 << 20)
|
||||
| (0x21 << 26),
|
||||
);
|
||||
}
|
||||
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "custom-pre-backtrace"))]
|
||||
#[allow(unused)]
|
||||
fn pre_backtrace() {}
|
||||
|
||||
#[cfg(feature = "custom-pre-backtrace")]
|
||||
fn pre_backtrace() {
|
||||
extern "Rust" {
|
||||
fn custom_pre_backtrace();
|
||||
}
|
||||
unsafe { custom_pre_backtrace() }
|
||||
}
|
||||
219
esp-backtrace/src/riscv.rs
Normal file
219
esp-backtrace/src/riscv.rs
Normal file
@ -0,0 +1,219 @@
|
||||
use core::arch::asm;
|
||||
|
||||
use crate::MAX_BACKTRACE_ADDRESSES;
|
||||
|
||||
// subtract 4 from the return address
|
||||
// the return address is the address following the JALR
|
||||
// we get better results (especially if the caller was the last instruction in
|
||||
// the calling function) if we report the address of the JALR itself
|
||||
// even if it was a C.JALR we should get good results using RA - 4
|
||||
#[allow(unused)]
|
||||
pub(super) const RA_OFFSET: usize = 4;
|
||||
|
||||
/// Registers saved in trap handler
|
||||
#[doc(hidden)]
|
||||
#[derive(Default, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[repr(C)]
|
||||
#[cfg(feature = "exception-handler")]
|
||||
pub(crate) struct TrapFrame {
|
||||
/// Return address, stores the address to return to after a function call or
|
||||
/// interrupt.
|
||||
pub ra: usize,
|
||||
/// Temporary register t0, used for intermediate values.
|
||||
pub t0: usize,
|
||||
/// Temporary register t1, used for intermediate values.
|
||||
pub t1: usize,
|
||||
/// Temporary register t2, used for intermediate values.
|
||||
pub t2: usize,
|
||||
/// Temporary register t3, used for intermediate values.
|
||||
pub t3: usize,
|
||||
/// Temporary register t4, used for intermediate values.
|
||||
pub t4: usize,
|
||||
/// Temporary register t5, used for intermediate values.
|
||||
pub t5: usize,
|
||||
/// Temporary register t6, used for intermediate values.
|
||||
pub t6: usize,
|
||||
/// Argument register a0, typically used to pass the first argument to a
|
||||
/// function.
|
||||
pub a0: usize,
|
||||
/// Argument register a1, typically used to pass the second argument to a
|
||||
/// function.
|
||||
pub a1: usize,
|
||||
/// Argument register a2, typically used to pass the third argument to a
|
||||
/// function.
|
||||
pub a2: usize,
|
||||
/// Argument register a3, typically used to pass the fourth argument to a
|
||||
/// function.
|
||||
pub a3: usize,
|
||||
/// Argument register a4, typically used to pass the fifth argument to a
|
||||
/// function.
|
||||
pub a4: usize,
|
||||
/// Argument register a5, typically used to pass the sixth argument to a
|
||||
/// function.
|
||||
pub a5: usize,
|
||||
/// Argument register a6, typically used to pass the seventh argument to a
|
||||
/// function.
|
||||
pub a6: usize,
|
||||
/// Argument register a7, typically used to pass the eighth argument to a
|
||||
/// function.
|
||||
pub a7: usize,
|
||||
/// Saved register s0, used to hold values across function calls.
|
||||
pub s0: usize,
|
||||
/// Saved register s1, used to hold values across function calls.
|
||||
pub s1: usize,
|
||||
/// Saved register s2, used to hold values across function calls.
|
||||
pub s2: usize,
|
||||
/// Saved register s3, used to hold values across function calls.
|
||||
pub s3: usize,
|
||||
/// Saved register s4, used to hold values across function calls.
|
||||
pub s4: usize,
|
||||
/// Saved register s5, used to hold values across function calls.
|
||||
pub s5: usize,
|
||||
/// Saved register s6, used to hold values across function calls.
|
||||
pub s6: usize,
|
||||
/// Saved register s7, used to hold values across function calls.
|
||||
pub s7: usize,
|
||||
/// Saved register s8, used to hold values across function calls.
|
||||
pub s8: usize,
|
||||
/// Saved register s9, used to hold values across function calls.
|
||||
pub s9: usize,
|
||||
/// Saved register s10, used to hold values across function calls.
|
||||
pub s10: usize,
|
||||
/// Saved register s11, used to hold values across function calls.
|
||||
pub s11: usize,
|
||||
/// Global pointer register, holds the address of the global data area.
|
||||
pub gp: usize,
|
||||
/// Thread pointer register, holds the address of the thread-local storage
|
||||
/// area.
|
||||
pub tp: usize,
|
||||
/// Stack pointer register, holds the address of the top of the stack.
|
||||
pub sp: usize,
|
||||
/// Program counter, stores the address of the next instruction to be
|
||||
/// executed.
|
||||
pub pc: usize,
|
||||
/// Machine status register, holds the current status of the processor,
|
||||
/// including interrupt enable bits and privilege mode.
|
||||
pub mstatus: usize,
|
||||
/// Machine cause register, contains the reason for the trap (e.g.,
|
||||
/// exception or interrupt number).
|
||||
pub mcause: usize,
|
||||
/// Machine trap value register, contains additional information about the
|
||||
/// trap (e.g., faulting address).
|
||||
pub mtval: usize,
|
||||
}
|
||||
|
||||
#[cfg(feature = "exception-handler")]
|
||||
impl core::fmt::Debug for TrapFrame {
|
||||
fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
|
||||
write!(
|
||||
fmt,
|
||||
"TrapFrame
|
||||
PC=0x{:08x} RA/x1=0x{:08x} SP/x2=0x{:08x} GP/x3=0x{:08x} TP/x4=0x{:08x}
|
||||
T0/x5=0x{:08x} T1/x6=0x{:08x} T2/x7=0x{:08x} S0/FP/x8=0x{:08x} S1/x9=0x{:08x}
|
||||
A0/x10=0x{:08x} A1/x11=0x{:08x} A2/x12=0x{:08x} A3/x13=0x{:08x} A4/x14=0x{:08x}
|
||||
A5/x15=0x{:08x} A6/x16=0x{:08x} A7/x17=0x{:08x} S2/x18=0x{:08x} S3/x19=0x{:08x}
|
||||
S4/x20=0x{:08x} S5/x21=0x{:08x} S6/x22=0x{:08x} S7/x23=0x{:08x} S8/x24=0x{:08x}
|
||||
S9/x25=0x{:08x} S10/x26=0x{:08x} S11/x27=0x{:08x} T3/x28=0x{:08x} T4/x29=0x{:08x}
|
||||
T5/x30=0x{:08x} T6/x31=0x{:08x}
|
||||
|
||||
MSTATUS=0x{:08x}
|
||||
MCAUSE=0x{:08x}
|
||||
MTVAL=0x{:08x}
|
||||
",
|
||||
self.pc,
|
||||
self.ra,
|
||||
self.gp,
|
||||
self.sp,
|
||||
self.tp,
|
||||
self.t0,
|
||||
self.t1,
|
||||
self.t2,
|
||||
self.s0,
|
||||
self.s1,
|
||||
self.a0,
|
||||
self.a1,
|
||||
self.a2,
|
||||
self.a3,
|
||||
self.a4,
|
||||
self.a5,
|
||||
self.a6,
|
||||
self.a7,
|
||||
self.s2,
|
||||
self.s3,
|
||||
self.s4,
|
||||
self.s5,
|
||||
self.s6,
|
||||
self.s7,
|
||||
self.s8,
|
||||
self.s9,
|
||||
self.s10,
|
||||
self.s11,
|
||||
self.t3,
|
||||
self.t4,
|
||||
self.t5,
|
||||
self.t6,
|
||||
self.mstatus,
|
||||
self.mcause,
|
||||
self.mtval,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get an array of backtrace addresses.
|
||||
///
|
||||
/// This needs `force-frame-pointers` enabled.
|
||||
pub fn backtrace() -> [Option<usize>; MAX_BACKTRACE_ADDRESSES] {
|
||||
let fp = unsafe {
|
||||
let mut _tmp: u32;
|
||||
asm!("mv {0}, x8", out(reg) _tmp);
|
||||
_tmp
|
||||
};
|
||||
|
||||
backtrace_internal(fp, 2)
|
||||
}
|
||||
|
||||
pub(crate) fn backtrace_internal(
|
||||
fp: u32,
|
||||
suppress: i32,
|
||||
) -> [Option<usize>; MAX_BACKTRACE_ADDRESSES] {
|
||||
let mut result = [None; 10];
|
||||
let mut index = 0;
|
||||
|
||||
let mut fp = fp;
|
||||
let mut suppress = suppress;
|
||||
let mut old_address = 0;
|
||||
loop {
|
||||
unsafe {
|
||||
let address = (fp as *const u32).offset(-1).read_volatile(); // RA/PC
|
||||
fp = (fp as *const u32).offset(-2).read_volatile(); // next FP
|
||||
|
||||
if old_address == address {
|
||||
break;
|
||||
}
|
||||
|
||||
old_address = address;
|
||||
|
||||
if address == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
if !crate::is_valid_ram_address(fp) {
|
||||
break;
|
||||
}
|
||||
|
||||
if suppress == 0 {
|
||||
result[index] = Some(address as usize);
|
||||
index += 1;
|
||||
|
||||
if index >= MAX_BACKTRACE_ADDRESSES {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
suppress -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
424
esp-backtrace/src/xtensa.rs
Normal file
424
esp-backtrace/src/xtensa.rs
Normal file
@ -0,0 +1,424 @@
|
||||
use core::{arch::asm, fmt::Display};
|
||||
|
||||
use crate::MAX_BACKTRACE_ADDRESSES;
|
||||
|
||||
// subtract 3 from the return address
|
||||
// the return address is the address following the callxN
|
||||
// we get better results (especially if the caller was the last function in the
|
||||
// calling function) if we report the address of callxN itself
|
||||
#[allow(unused)]
|
||||
pub(super) const RA_OFFSET: usize = 3;
|
||||
|
||||
/// Exception Cause
|
||||
#[doc(hidden)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[repr(C)]
|
||||
pub enum ExceptionCause {
|
||||
/// Illegal Instruction
|
||||
IllegalInstruction = 0,
|
||||
/// System Call (Syscall Instruction)
|
||||
Syscall = 1,
|
||||
/// Instruction Fetch Error
|
||||
InstrFetchError = 2,
|
||||
/// Load Store Error
|
||||
LoadStoreError = 3,
|
||||
/// Level 1 Interrupt
|
||||
LevelOneInterrupt = 4,
|
||||
/// Stack Extension Assist (movsp Instruction) For Alloca
|
||||
Alloca = 5,
|
||||
/// Integer Divide By Zero
|
||||
DivideByZero = 6,
|
||||
/// Use Of Failed Speculative Access (Not Implemented)
|
||||
NextPCValueIllegal = 7,
|
||||
/// Privileged Instruction
|
||||
PrivilegedInstruction = 8,
|
||||
/// Unaligned Load Or Store
|
||||
UnalignedLoadOrStore = 9,
|
||||
/// Reserved
|
||||
ExternalRegisterPrivilegeError = 10,
|
||||
/// Reserved
|
||||
ExclusiveError = 11,
|
||||
/// Pif Data Error On Instruction Fetch (Rb-200x And Later)
|
||||
InstrDataError = 12,
|
||||
/// Pif Data Error On Load Or Store (Rb-200x And Later)
|
||||
LoadStoreDataError = 13,
|
||||
/// Pif Address Error On Instruction Fetch (Rb-200x And Later)
|
||||
InstrAddrError = 14,
|
||||
/// Pif Address Error On Load Or Store (Rb-200x And Later)
|
||||
LoadStoreAddrError = 15,
|
||||
/// Itlb Miss (No Itlb Entry Matches, Hw Refill Also Missed)
|
||||
ItlbMiss = 16,
|
||||
/// Itlb Multihit (Multiple Itlb Entries Match)
|
||||
ItlbMultiHit = 17,
|
||||
/// Ring Privilege Violation On Instruction Fetch
|
||||
InstrRing = 18,
|
||||
/// Size Restriction On Ifetch (Not Implemented)
|
||||
Reserved19 = 19,
|
||||
/// Cache Attribute Does Not Allow Instruction Fetch
|
||||
InstrProhibited = 20,
|
||||
/// Reserved
|
||||
Reserved21 = 21,
|
||||
/// Reserved
|
||||
Reserved22 = 22,
|
||||
/// Reserved
|
||||
Reserved23 = 23,
|
||||
/// Dtlb Miss (No Dtlb Entry Matches, Hw Refill Also Missed)
|
||||
DtlbMiss = 24,
|
||||
/// Dtlb Multihit (Multiple Dtlb Entries Match)
|
||||
DtlbMultiHit = 25,
|
||||
/// Ring Privilege Violation On Load Or Store
|
||||
LoadStoreRing = 26,
|
||||
/// Size Restriction On Load/Store (Not Implemented)
|
||||
Reserved27 = 27,
|
||||
/// Cache Attribute Does Not Allow Load
|
||||
LoadProhibited = 28,
|
||||
/// Cache Attribute Does Not Allow Store
|
||||
StoreProhibited = 29,
|
||||
/// Reserved
|
||||
Reserved30 = 30,
|
||||
/// Reserved
|
||||
Reserved31 = 31,
|
||||
/// Access To Coprocessor 0 When Disabled
|
||||
Cp0Disabled = 32,
|
||||
/// Access To Coprocessor 1 When Disabled
|
||||
Cp1Disabled = 33,
|
||||
/// Access To Coprocessor 2 When Disabled
|
||||
Cp2Disabled = 34,
|
||||
/// Access To Coprocessor 3 When Disabled
|
||||
Cp3Disabled = 35,
|
||||
/// Access To Coprocessor 4 When Disabled
|
||||
Cp4Disabled = 36,
|
||||
/// Access To Coprocessor 5 When Disabled
|
||||
Cp5Disabled = 37,
|
||||
/// Access To Coprocessor 6 When Disabled
|
||||
Cp6Disabled = 38,
|
||||
/// Access To Coprocessor 7 When Disabled
|
||||
Cp7Disabled = 39,
|
||||
/// None
|
||||
None = 255,
|
||||
}
|
||||
|
||||
impl Display for ExceptionCause {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
if *self == Self::Cp0Disabled {
|
||||
write!(f, "Cp0Disabled (Access to the floating point coprocessor is not allowed. You may want to enable the `float-save-restore` feature of the `xtensa-lx-rt` crate.)")
|
||||
} else {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[repr(C)]
|
||||
pub struct Context {
|
||||
/// Program counter, stores the address of the next instruction to be
|
||||
/// executed.
|
||||
pub PC: u32,
|
||||
/// Processor status, holds various status flags for the CPU.
|
||||
pub PS: u32,
|
||||
/// General-purpose register A0 used for data storage and manipulation.
|
||||
pub A0: u32,
|
||||
/// General-purpose register A1 used for data storage and manipulation.
|
||||
pub A1: u32,
|
||||
/// General-purpose register A2 used for data storage and manipulation.
|
||||
pub A2: u32,
|
||||
/// General-purpose register A3 used for data storage and manipulation.
|
||||
pub A3: u32,
|
||||
/// General-purpose register A4 used for data storage and manipulation.
|
||||
pub A4: u32,
|
||||
/// General-purpose register A5 used for data storage and manipulation.
|
||||
pub A5: u32,
|
||||
/// General-purpose register A6 used for data storage and manipulation.
|
||||
pub A6: u32,
|
||||
/// General-purpose register A7 used for data storage and manipulation.
|
||||
pub A7: u32,
|
||||
/// General-purpose register A8 used for data storage and manipulation.
|
||||
pub A8: u32,
|
||||
/// General-purpose register A9 used for data storage and manipulation.
|
||||
pub A9: u32,
|
||||
/// General-purpose register A10 used for data storage and manipulation.
|
||||
pub A10: u32,
|
||||
/// General-purpose register A11 used for data storage and manipulation.
|
||||
pub A11: u32,
|
||||
/// General-purpose register A12 used for data storage and manipulation.
|
||||
pub A12: u32,
|
||||
/// General-purpose register A13 used for data storage and manipulation.
|
||||
pub A13: u32,
|
||||
/// General-purpose register A14 used for data storage and manipulation.
|
||||
pub A14: u32,
|
||||
/// General-purpose register A15 used for data storage and manipulation.
|
||||
pub A15: u32,
|
||||
/// Shift amount register, used for shift and rotate instructions.
|
||||
pub SAR: u32,
|
||||
/// Exception cause, indicates the reason for the last exception.
|
||||
pub EXCCAUSE: u32,
|
||||
/// Exception address, holds the address related to the exception.
|
||||
pub EXCVADDR: u32,
|
||||
/// Loop start address, used in loop instructions.
|
||||
pub LBEG: u32,
|
||||
/// Loop end address, used in loop instructions.
|
||||
pub LEND: u32,
|
||||
/// Loop counter, used to count iterations in loop instructions.
|
||||
pub LCOUNT: u32,
|
||||
/// Thread pointer, used for thread-local storage.
|
||||
pub THREADPTR: u32,
|
||||
/// Compare register, used for certain compare instructions.
|
||||
pub SCOMPARE1: u32,
|
||||
/// Break register, used for breakpoint-related operations.
|
||||
pub BR: u32,
|
||||
/// Accumulator low register, used for extended arithmetic operations.
|
||||
pub ACCLO: u32,
|
||||
/// Accumulator high register, used for extended arithmetic operations.
|
||||
pub ACCHI: u32,
|
||||
/// Additional register M0 used for special operations.
|
||||
pub M0: u32,
|
||||
/// Additional register M1 used for special operations.
|
||||
pub M1: u32,
|
||||
/// Additional register M2 used for special operations.
|
||||
pub M2: u32,
|
||||
/// Additional register M3 used for special operations.
|
||||
pub M3: u32,
|
||||
/// 64-bit floating-point register (low part), available if the
|
||||
/// `print-float-registers` feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F64R_LO: u32,
|
||||
/// 64-bit floating-point register (high part), available if the
|
||||
/// `print-float-registers` feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F64R_HI: u32,
|
||||
/// Floating-point status register, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F64S: u32,
|
||||
/// Floating-point control register, available if the
|
||||
/// `print-float-registers` feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub FCR: u32,
|
||||
/// Floating-point status register, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub FSR: u32,
|
||||
/// Floating-point register F0, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F0: u32,
|
||||
/// Floating-point register F1, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F1: u32,
|
||||
/// Floating-point register F2, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F2: u32,
|
||||
/// Floating-point register F3, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F3: u32,
|
||||
/// Floating-point register F4, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F4: u32,
|
||||
/// Floating-point register F5, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F5: u32,
|
||||
/// Floating-point register F6, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F6: u32,
|
||||
/// Floating-point register F7, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F7: u32,
|
||||
/// Floating-point register F8, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F8: u32,
|
||||
/// Floating-point register F9, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F9: u32,
|
||||
/// Floating-point register F10, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F10: u32,
|
||||
/// Floating-point register F11, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F11: u32,
|
||||
/// Floating-point register F12, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F12: u32,
|
||||
/// Floating-point register F13, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F13: u32,
|
||||
/// Floating-point register F14, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F14: u32,
|
||||
/// Floating-point register F15, available if the `print-float-registers`
|
||||
/// feature is enabled.
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
pub F15: u32,
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for Context {
|
||||
fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
|
||||
write!(
|
||||
fmt,
|
||||
"Context
|
||||
PC=0x{:08x} PS=0x{:08x}
|
||||
A0=0x{:08x} A1=0x{:08x} A2=0x{:08x} A3=0x{:08x} A4=0x{:08x}
|
||||
A5=0x{:08x} A6=0x{:08x} A7=0x{:08x} A8=0x{:08x} A9=0x{:08x}
|
||||
A10=0x{:08x} A11=0x{:08x} A12=0x{:08x} A13=0x{:08x} A14=0x{:08x}
|
||||
A15=0x{:08x}
|
||||
SAR={:08x}
|
||||
EXCCAUSE=0x{:08x} EXCVADDR=0x{:08x}
|
||||
LBEG=0x{:08x} LEND=0x{:08x} LCOUNT=0x{:08x}
|
||||
THREADPTR=0x{:08x}
|
||||
SCOMPARE1=0x{:08x}
|
||||
BR=0x{:08x}
|
||||
ACCLO=0x{:08x} ACCHI=0x{:08x}
|
||||
M0=0x{:08x} M1=0x{:08x} M2=0x{:08x} M3=0x{:08x}
|
||||
",
|
||||
self.PC,
|
||||
self.PS,
|
||||
self.A0,
|
||||
self.A1,
|
||||
self.A2,
|
||||
self.A3,
|
||||
self.A4,
|
||||
self.A5,
|
||||
self.A6,
|
||||
self.A7,
|
||||
self.A8,
|
||||
self.A9,
|
||||
self.A10,
|
||||
self.A11,
|
||||
self.A12,
|
||||
self.A13,
|
||||
self.A14,
|
||||
self.A15,
|
||||
self.SAR,
|
||||
self.EXCCAUSE,
|
||||
self.EXCVADDR,
|
||||
self.LBEG,
|
||||
self.LEND,
|
||||
self.LCOUNT,
|
||||
self.THREADPTR,
|
||||
self.SCOMPARE1,
|
||||
self.BR,
|
||||
self.ACCLO,
|
||||
self.ACCHI,
|
||||
self.M0,
|
||||
self.M1,
|
||||
self.M2,
|
||||
self.M3,
|
||||
)?;
|
||||
#[cfg(feature = "print-float-registers")]
|
||||
write!(
|
||||
fmt,
|
||||
"F64R_LO=0x{:08x} F64R_HI=0x{:08x} F64S=0x{:08x}
|
||||
FCR=0x{:08x} FSR=0x{:08x}
|
||||
F0=0x{:08x} F1=0x{:08x} F2=0x{:08x} F3=0x{:08x} F4=0x{:08x}
|
||||
F5=0x{:08x} F6=0x{:08x} F7=0x{:08x} F8=0x{:08x} F9=0x{:08x}
|
||||
F10=0x{:08x} F11=0x{:08x} F12=0x{:08x} F13=0x{:08x} F14=0x{:08x}
|
||||
F15=0x{:08x}",
|
||||
self.F64R_LO,
|
||||
self.F64R_HI,
|
||||
self.F64S,
|
||||
self.FCR,
|
||||
self.FSR,
|
||||
self.F0,
|
||||
self.F1,
|
||||
self.F2,
|
||||
self.F3,
|
||||
self.F4,
|
||||
self.F5,
|
||||
self.F6,
|
||||
self.F7,
|
||||
self.F8,
|
||||
self.F9,
|
||||
self.F10,
|
||||
self.F11,
|
||||
self.F12,
|
||||
self.F13,
|
||||
self.F14,
|
||||
self.F15,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Get an array of backtrace addresses.
|
||||
pub fn backtrace() -> [Option<usize>; MAX_BACKTRACE_ADDRESSES] {
|
||||
let sp = unsafe {
|
||||
let mut _tmp: u32;
|
||||
asm!("mov {0}, a1", out(reg) _tmp);
|
||||
_tmp
|
||||
};
|
||||
|
||||
backtrace_internal(sp, 1)
|
||||
}
|
||||
|
||||
pub(crate) fn sanitize_address(address: u32) -> u32 {
|
||||
(address & 0x3fff_ffff) | 0x4000_0000
|
||||
}
|
||||
|
||||
pub(crate) fn backtrace_internal(
|
||||
sp: u32,
|
||||
suppress: i32,
|
||||
) -> [Option<usize>; MAX_BACKTRACE_ADDRESSES] {
|
||||
let mut result = [None; 10];
|
||||
let mut index = 0;
|
||||
|
||||
let mut fp = sp;
|
||||
let mut suppress = suppress;
|
||||
let mut old_address = 0;
|
||||
|
||||
loop {
|
||||
unsafe {
|
||||
let address = sanitize_address((fp as *const u32).offset(-4).read_volatile()); // RA/PC
|
||||
fp = (fp as *const u32).offset(-3).read_volatile(); // next FP
|
||||
|
||||
if old_address == address {
|
||||
break;
|
||||
}
|
||||
|
||||
old_address = address;
|
||||
|
||||
// the address is 0 but we sanitized the address - then 0 becomes 0x40000000
|
||||
if address == 0x40000000 {
|
||||
break;
|
||||
}
|
||||
|
||||
if !crate::is_valid_ram_address(fp) {
|
||||
break;
|
||||
}
|
||||
|
||||
if fp == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
if suppress == 0 {
|
||||
result[index] = Some(address as usize);
|
||||
index += 1;
|
||||
|
||||
if index >= MAX_BACKTRACE_ADDRESSES {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
suppress -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
24
esp-build/CHANGELOG.md
Normal file
24
esp-build/CHANGELOG.md
Normal file
@ -0,0 +1,24 @@
|
||||
# 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.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
### Fixed
|
||||
|
||||
### Changed
|
||||
|
||||
- Use `panic` instead of `process::exit` in esp-build (#2402 )
|
||||
|
||||
### Removed
|
||||
|
||||
## [0.1.0] - 2024-04-17
|
||||
|
||||
- Initial release
|
||||
|
||||
[Unreleased]: https://github.com/esp-rs/esp-hal/commits/main/esp-build?since=2024-04-17
|
||||
16
esp-build/Cargo.toml
Normal file
16
esp-build/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "esp-build"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.60.0"
|
||||
description = "Build utilities for esp-hal"
|
||||
repository = "https://github.com/esp-rs/esp-hal"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
quote = "1.0.37"
|
||||
syn = { version = "2.0.79", features = ["fold", "full"] }
|
||||
termcolor = "1.4.1"
|
||||
33
esp-build/README.md
Normal file
33
esp-build/README.md
Normal file
@ -0,0 +1,33 @@
|
||||
# esp-build
|
||||
|
||||
[](https://crates.io/crates/esp-build)
|
||||
[](https://docs.rs/esp-build)
|
||||

|
||||

|
||||
[](https://matrix.to/#/#esp-rs:matrix.org)
|
||||
|
||||
Build utilities for use with `esp-hal` and other related packages, intended for use in [build scripts]. This package is still quite minimal, but provides:
|
||||
|
||||
[build scripts]: https://doc.rust-lang.org/cargo/reference/build-scripts.html
|
||||
|
||||
## [Documentation](https://docs.rs/crate/esp-build)
|
||||
|
||||
## Minimum Supported Rust Version (MSRV)
|
||||
|
||||
This crate is guaranteed to compile on stable Rust 1.60 and up. It _might_
|
||||
compile with older versions but that may change in any new patch release.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of:
|
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
- MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in
|
||||
the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without
|
||||
any additional terms or conditions.
|
||||
229
esp-build/src/lib.rs
Normal file
229
esp-build/src/lib.rs
Normal file
@ -0,0 +1,229 @@
|
||||
//! Build utilities for esp-hal.
|
||||
|
||||
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
|
||||
|
||||
use std::io::Write as _;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use syn::{parse_macro_input, punctuated::Punctuated, LitStr, Token};
|
||||
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
|
||||
|
||||
/// Print a build error and terminate the process.
|
||||
///
|
||||
/// It should be noted that the error will be printed BEFORE the main function
|
||||
/// is called, and as such this should NOT be thought analogous to `println!` or
|
||||
/// similar utilities.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// esp_build::error! {"
|
||||
/// ERROR: something really bad has happened!
|
||||
/// "}
|
||||
/// // Process exits with exit code 1
|
||||
/// ```
|
||||
#[proc_macro]
|
||||
pub fn error(input: TokenStream) -> TokenStream {
|
||||
do_alert(Color::Red, input);
|
||||
panic!("Build failed");
|
||||
}
|
||||
|
||||
/// Print a build warning.
|
||||
///
|
||||
/// It should be noted that the warning will be printed BEFORE the main function
|
||||
/// is called, and as such this should NOT be thought analogous to `println!` or
|
||||
/// similar utilities.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// esp_build::warning! {"
|
||||
/// WARNING: something unpleasant has happened!
|
||||
/// "};
|
||||
/// ```
|
||||
#[proc_macro]
|
||||
pub fn warning(input: TokenStream) -> TokenStream {
|
||||
do_alert(Color::Yellow, input)
|
||||
}
|
||||
|
||||
/// Given some features, assert that **at most** one of the features is enabled.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```rust
|
||||
/// assert_unique_features!("foo", "bar", "baz");
|
||||
/// ```
|
||||
#[proc_macro]
|
||||
pub fn assert_unique_features(input: TokenStream) -> TokenStream {
|
||||
let features = parse_macro_input!(input with Punctuated<LitStr, Token![,]>::parse_terminated)
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let unique = impl_unique_features(&features, "exactly zero or one");
|
||||
|
||||
quote::quote! {
|
||||
#unique
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Given some features, assert that **at least** one of the features is
|
||||
/// enabled.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```rust
|
||||
/// assert_used_features!("foo", "bar", "baz");
|
||||
/// ```
|
||||
#[proc_macro]
|
||||
pub fn assert_used_features(input: TokenStream) -> TokenStream {
|
||||
let features = parse_macro_input!(input with Punctuated<LitStr, Token![,]>::parse_terminated)
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let used = impl_used_features(&features, "at least one");
|
||||
|
||||
quote::quote! {
|
||||
#used
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Given some features, assert that **exactly** one of the features is enabled.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```rust
|
||||
/// assert_unique_used_features!("foo", "bar", "baz");
|
||||
/// ```
|
||||
#[proc_macro]
|
||||
pub fn assert_unique_used_features(input: TokenStream) -> TokenStream {
|
||||
let features = parse_macro_input!(input with Punctuated<LitStr, Token![,]>::parse_terminated)
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let unique = impl_unique_features(&features, "exactly one");
|
||||
let used = impl_used_features(&features, "exactly one");
|
||||
|
||||
quote::quote! {
|
||||
#unique
|
||||
#used
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helper Functions
|
||||
|
||||
fn impl_unique_features(features: &[LitStr], expectation: &str) -> impl ToTokens {
|
||||
let pairs = unique_pairs(features);
|
||||
let unique_cfgs = pairs
|
||||
.iter()
|
||||
.map(|(a, b)| quote::quote! { all(feature = #a, feature = #b) });
|
||||
|
||||
let message = format!(
|
||||
r#"
|
||||
ERROR: expected {expectation} enabled feature from feature group:
|
||||
{:?}
|
||||
"#,
|
||||
features.iter().map(|lit| lit.value()).collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
quote::quote! {
|
||||
#[cfg(any(#(#unique_cfgs),*))]
|
||||
::esp_build::error! { #message }
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_used_features(features: &[LitStr], expectation: &str) -> impl ToTokens {
|
||||
let message = format!(
|
||||
r#"
|
||||
ERROR: expected {expectation} enabled feature from feature group:
|
||||
{:?}
|
||||
"#,
|
||||
features.iter().map(|lit| lit.value()).collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
quote::quote! {
|
||||
#[cfg(not(any(#(feature = #features),*)))]
|
||||
::esp_build::error! { #message }
|
||||
}
|
||||
}
|
||||
|
||||
// Adapted from:
|
||||
// https://github.com/dtolnay/build-alert/blob/49d060e/src/lib.rs#L54-L93
|
||||
fn do_alert(color: Color, input: TokenStream) -> TokenStream {
|
||||
let message = parse_macro_input!(input as LitStr).value();
|
||||
|
||||
let stderr = &mut StandardStream::stderr(ColorChoice::Auto);
|
||||
let color_spec = ColorSpec::new().set_fg(Some(color)).clone();
|
||||
|
||||
let mut has_nonspace = false;
|
||||
|
||||
for mut line in message.lines() {
|
||||
if !has_nonspace {
|
||||
let (maybe_heading, rest) = split_heading(line);
|
||||
|
||||
if let Some(heading) = maybe_heading {
|
||||
stderr.set_color(color_spec.clone().set_bold(true)).ok();
|
||||
write!(stderr, "\n{}", heading).ok();
|
||||
has_nonspace = true;
|
||||
}
|
||||
|
||||
line = rest;
|
||||
}
|
||||
|
||||
if line.is_empty() {
|
||||
writeln!(stderr).ok();
|
||||
} else {
|
||||
stderr.set_color(&color_spec).ok();
|
||||
writeln!(stderr, "{}", line).ok();
|
||||
|
||||
has_nonspace = has_nonspace || line.contains(|ch: char| ch != ' ');
|
||||
}
|
||||
}
|
||||
|
||||
stderr.reset().ok();
|
||||
writeln!(stderr).ok();
|
||||
|
||||
TokenStream::new()
|
||||
}
|
||||
|
||||
// Adapted from:
|
||||
// https://github.com/dtolnay/build-alert/blob/49d060e/src/lib.rs#L95-L114
|
||||
fn split_heading(s: &str) -> (Option<&str>, &str) {
|
||||
let mut end = 0;
|
||||
while end < s.len() && s[end..].starts_with(|ch: char| ch.is_ascii_uppercase()) {
|
||||
end += 1;
|
||||
}
|
||||
|
||||
if end >= 3 && (end == s.len() || s[end..].starts_with(':')) {
|
||||
let (heading, rest) = s.split_at(end);
|
||||
(Some(heading), rest)
|
||||
} else {
|
||||
(None, s)
|
||||
}
|
||||
}
|
||||
|
||||
fn unique_pairs(features: &[LitStr]) -> Vec<(&LitStr, &LitStr)> {
|
||||
let mut pairs = Vec::new();
|
||||
|
||||
let mut i = 0;
|
||||
let mut j = 0;
|
||||
|
||||
while i < features.len() {
|
||||
let a = &features[i];
|
||||
let b = &features[j];
|
||||
|
||||
if a.value() != b.value() {
|
||||
pairs.push((a, b));
|
||||
}
|
||||
|
||||
j += 1;
|
||||
|
||||
if j >= features.len() {
|
||||
i += 1;
|
||||
j = i;
|
||||
}
|
||||
}
|
||||
|
||||
pairs
|
||||
}
|
||||
32
esp-config/CHANGELOG.md
Normal file
32
esp-config/CHANGELOG.md
Normal file
@ -0,0 +1,32 @@
|
||||
# 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.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
### Fixed
|
||||
|
||||
- Users no longer have to manually import `esp_config_int_parse`. (#2630)
|
||||
|
||||
### Changed
|
||||
|
||||
- Crate prefixes and configuration keys are now separated by `_CONFIG_` (#2848)
|
||||
|
||||
### Removed
|
||||
|
||||
## 0.2.0 - 2024-11-20
|
||||
|
||||
### Added
|
||||
|
||||
- Add configuration validation (#2475)
|
||||
|
||||
## 0.1.0 - 2024-10-10
|
||||
|
||||
- Initial release
|
||||
|
||||
[Unreleased]: https://github.com/esp-rs/esp-hal/commits/main/esp-config?since=2024-11-20
|
||||
18
esp-config/Cargo.toml
Normal file
18
esp-config/Cargo.toml
Normal file
@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "esp-config"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.79.0"
|
||||
description = "Configure projects using esp-hal and related packages"
|
||||
repository = "https://github.com/esp-rs/esp-hal"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
document-features = "0.2.10"
|
||||
|
||||
[dev-dependencies]
|
||||
temp-env = "0.3.6"
|
||||
|
||||
[features]
|
||||
## Enable the generation and parsing of a config
|
||||
build = []
|
||||
65
esp-config/README.md
Normal file
65
esp-config/README.md
Normal file
@ -0,0 +1,65 @@
|
||||
# esp-config
|
||||
|
||||
[](https://crates.io/crates/esp-config)
|
||||
[](https://docs.rs/esp-config)
|
||||

|
||||

|
||||
[](https://matrix.to/#/#esp-rs:matrix.org)
|
||||
|
||||
## [Documentation](https://docs.rs/crate/esp-config)
|
||||
|
||||
## Usage
|
||||
|
||||
`esp-config` takes a prefix (usually the crate name) and a set of configuration keys and default values to produce a configuration system that supports:
|
||||
|
||||
- Emitting rustc cfg's for boolean keys
|
||||
- Emitting environment variables for numbers
|
||||
- Along with decimal parsing, it supports Hex, Octal and Binary with the respective `0x`, `0o` and `0b` prefixes.
|
||||
- Emitting environment variables string values
|
||||
|
||||
### Viewing the configuration
|
||||
|
||||
The possible configuration values are output as a markdown table in the crates `OUT_DIR` with the format `{prefix}_config_table.md`, this can then be included into the crates top level documentation. Here is an example of the output:
|
||||
|
||||
|
||||
| Name | Description | Default value |
|
||||
|------|-------------|---------------|
|
||||
|**ESP_HAL_PLACE_SPI_DRIVER_IN_RAM**|Places the SPI driver in RAM for better performance|false|
|
||||
|
||||
### Setting configuration options
|
||||
|
||||
For any available configuration option, the environment variable or cfg is _always_ set based on the default value specified in the table. Users can override this by setting environment variables locally in their shell _or_ the preferred option is to utilize cargo's [`env` section](https://doc.rust-lang.org/cargo/reference/config.html#env).
|
||||
|
||||
It's important to note that due to a [bug in cargo](https://github.com/rust-lang/cargo/issues/10358), any modifications to the environment, local or otherwise will only get picked up on a full clean build of the project.
|
||||
|
||||
To see the final selected configuration another table is output to the `OUT_DIR` with the format `{prefix}_selected_config.md`.
|
||||
|
||||
### Capturing configuration values in the downstream crate
|
||||
|
||||
For all supported data types, there are helper macros that emit `const` code for parsing the configuration values.
|
||||
|
||||
- Numbers - `esp_config_int!(integer_type, "ENV")`
|
||||
- Strings - `esp_config_str!("ENV")`
|
||||
- Bool - `esp_config_bool!("ENV")`
|
||||
|
||||
In addition to environment variables, for boolean types rust `cfg`'s are emitted in snake case _without_ the prefix.
|
||||
|
||||
## Minimum Supported Rust Version (MSRV)
|
||||
|
||||
This crate is guaranteed to compile on stable Rust 1.79 and up. It _might_
|
||||
compile with older versions but that may change in any new patch release.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of:
|
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
- MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in
|
||||
the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without
|
||||
any additional terms or conditions.
|
||||
699
esp-config/src/generate.rs
Normal file
699
esp-config/src/generate.rs
Normal file
@ -0,0 +1,699 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
env,
|
||||
fmt::{self, Write as _},
|
||||
fs,
|
||||
ops::Range,
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
const DOC_TABLE_HEADER: &str = r#"
|
||||
| Name | Description | Default value |
|
||||
|------|-------------|---------------|
|
||||
"#;
|
||||
|
||||
const SELECTED_TABLE_HEADER: &str = r#"
|
||||
| Name | Selected value |
|
||||
|------|----------------|
|
||||
"#;
|
||||
|
||||
/// Configuration errors.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
/// Parse errors.
|
||||
Parse(String),
|
||||
/// Validation errors.
|
||||
Validation(String),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// Convenience function for creating parse errors.
|
||||
pub fn parse<S>(message: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
Self::Parse(message.into())
|
||||
}
|
||||
|
||||
/// Convenience function for creating validation errors.
|
||||
pub fn validation<S>(message: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
Self::Validation(message.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Error::Parse(message) => write!(f, "{message}"),
|
||||
Error::Validation(message) => write!(f, "{message}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Supported configuration value types.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Value {
|
||||
/// Booleans.
|
||||
Bool(bool),
|
||||
/// Integers.
|
||||
Integer(i128),
|
||||
/// Strings.
|
||||
String(String),
|
||||
}
|
||||
|
||||
// TODO: Do we want to handle negative values for non-decimal values?
|
||||
impl Value {
|
||||
fn parse_in_place(&mut self, s: &str) -> Result<(), Error> {
|
||||
*self = match self {
|
||||
Value::Bool(_) => match s {
|
||||
"true" => Value::Bool(true),
|
||||
"false" => Value::Bool(false),
|
||||
_ => {
|
||||
return Err(Error::parse(format!(
|
||||
"Expected 'true' or 'false', found: '{s}'"
|
||||
)))
|
||||
}
|
||||
},
|
||||
Value::Integer(_) => {
|
||||
let inner = match s.as_bytes() {
|
||||
[b'0', b'x', ..] => i128::from_str_radix(&s[2..], 16),
|
||||
[b'0', b'o', ..] => i128::from_str_radix(&s[2..], 8),
|
||||
[b'0', b'b', ..] => i128::from_str_radix(&s[2..], 2),
|
||||
_ => i128::from_str_radix(&s, 10),
|
||||
}
|
||||
.map_err(|_| Error::parse(format!("Expected valid intger value, found: '{s}'")))?;
|
||||
|
||||
Value::Integer(inner)
|
||||
}
|
||||
Value::String(_) => Value::String(s.into()),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Convert the value to a [bool].
|
||||
pub fn as_bool(&self) -> bool {
|
||||
match self {
|
||||
Value::Bool(value) => *value,
|
||||
_ => panic!("attempted to convert non-bool value to a bool"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the value to an [i128].
|
||||
pub fn as_integer(&self) -> i128 {
|
||||
match self {
|
||||
Value::Integer(value) => *value,
|
||||
_ => panic!("attempted to convert non-integer value to an integer"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the value to a [String].
|
||||
pub fn as_string(&self) -> String {
|
||||
match self {
|
||||
Value::String(value) => value.to_owned(),
|
||||
_ => panic!("attempted to convert non-string value to a string"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Is the value a bool?
|
||||
pub fn is_bool(&self) -> bool {
|
||||
matches!(self, Value::Bool(_))
|
||||
}
|
||||
|
||||
/// Is the value an integer?
|
||||
pub fn is_integer(&self) -> bool {
|
||||
matches!(self, Value::Integer(_))
|
||||
}
|
||||
|
||||
/// Is the value a string?
|
||||
pub fn is_string(&self) -> bool {
|
||||
matches!(self, Value::String(_))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Value {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Value::Bool(b) => write!(f, "{b}"),
|
||||
Value::Integer(i) => write!(f, "{i}"),
|
||||
Value::String(s) => write!(f, "{s}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration value validation functions.
|
||||
pub enum Validator {
|
||||
/// Only allow negative integers, i.e. any values less than 0.
|
||||
NegativeInteger,
|
||||
/// Only allow non-negative integers, i.e. any values greater than or equal
|
||||
/// to 0.
|
||||
NonNegativeInteger,
|
||||
/// Only allow positive integers, i.e. any values greater than to 0.
|
||||
PositiveInteger,
|
||||
/// Ensure that an integer value falls within the specified range.
|
||||
IntegerInRange(Range<i128>),
|
||||
/// A custom validation function to run against any supported value type.
|
||||
Custom(Box<dyn Fn(&Value) -> Result<(), Error>>),
|
||||
}
|
||||
|
||||
impl Validator {
|
||||
fn validate(&self, value: &Value) -> Result<(), Error> {
|
||||
match self {
|
||||
Validator::NegativeInteger => negative_integer(value)?,
|
||||
Validator::NonNegativeInteger => non_negative_integer(value)?,
|
||||
Validator::PositiveInteger => positive_integer(value)?,
|
||||
Validator::IntegerInRange(range) => integer_in_range(range, value)?,
|
||||
Validator::Custom(validator_fn) => validator_fn(value)?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn negative_integer(value: &Value) -> Result<(), Error> {
|
||||
if !value.is_integer() {
|
||||
return Err(Error::validation(
|
||||
"Validator::NegativeInteger can only be used with integer values",
|
||||
));
|
||||
} else if value.as_integer() >= 0 {
|
||||
return Err(Error::validation(format!(
|
||||
"Expected negative integer, found '{}'",
|
||||
value.as_integer()
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn non_negative_integer(value: &Value) -> Result<(), Error> {
|
||||
if !value.is_integer() {
|
||||
return Err(Error::validation(
|
||||
"Validator::NonNegativeInteger can only be used with integer values",
|
||||
));
|
||||
} else if value.as_integer() < 0 {
|
||||
return Err(Error::validation(format!(
|
||||
"Expected non-negative integer, found '{}'",
|
||||
value.as_integer()
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn positive_integer(value: &Value) -> Result<(), Error> {
|
||||
if !value.is_integer() {
|
||||
return Err(Error::validation(
|
||||
"Validator::PositiveInteger can only be used with integer values",
|
||||
));
|
||||
} else if value.as_integer() <= 0 {
|
||||
return Err(Error::validation(format!(
|
||||
"Expected positive integer, found '{}'",
|
||||
value.as_integer()
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn integer_in_range(range: &Range<i128>, value: &Value) -> Result<(), Error> {
|
||||
if !value.is_integer() || !range.contains(&value.as_integer()) {
|
||||
Err(Error::validation(format!(
|
||||
"Value '{}' does not fall within range '{:?}'",
|
||||
value, range
|
||||
)))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate and parse config from a prefix, and an array tuples containing the
|
||||
/// name, description, default value, and an optional validator.
|
||||
///
|
||||
/// This function will parse any `SCREAMING_SNAKE_CASE` environment variables
|
||||
/// that match the given prefix. It will then attempt to parse the [`Value`] and
|
||||
/// run any validators which have been specified.
|
||||
///
|
||||
/// Once the config has been parsed, this function will emit `snake_case` cfg's
|
||||
/// _without_ the prefix which can be used in the dependant crate. After that,
|
||||
/// it will create a markdown table in the `OUT_DIR` under the name
|
||||
/// `{prefix}_config_table.md` where prefix has also been converted to
|
||||
/// `snake_case`. This can be included in crate documentation to outline the
|
||||
/// available configuration options for the crate.
|
||||
///
|
||||
/// Passing a value of true for the `emit_md_tables` argument will create and
|
||||
/// write markdown files of the available configuration and selected
|
||||
/// configuration which can be included in documentation.
|
||||
///
|
||||
/// Unknown keys with the supplied prefix will cause this function to panic.
|
||||
pub fn generate_config(
|
||||
crate_name: &str,
|
||||
config: &[(&str, &str, Value, Option<Validator>)],
|
||||
emit_md_tables: bool,
|
||||
) -> HashMap<String, Value> {
|
||||
// Only rebuild if `build.rs` changed. Otherwise, Cargo will rebuild if any
|
||||
// other file changed.
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
|
||||
#[cfg(not(test))]
|
||||
env_change_work_around();
|
||||
|
||||
let mut doc_table = String::from(DOC_TABLE_HEADER);
|
||||
let mut selected_config = String::from(SELECTED_TABLE_HEADER);
|
||||
|
||||
// Ensure that the prefix is `SCREAMING_SNAKE_CASE`:
|
||||
let prefix = format!("{}_CONFIG_", screaming_snake_case(crate_name));
|
||||
|
||||
// Build a lookup table for any provided validators; we must prefix the
|
||||
// name of the config and transform it to SCREAMING_SNAKE_CASE so that
|
||||
// it matches the keys in the hash table produced by `create_config`.
|
||||
let config_validators = config
|
||||
.iter()
|
||||
.flat_map(|(name, _description, _default, validator)| {
|
||||
if let Some(validator) = validator {
|
||||
let name = format!("{prefix}{}", screaming_snake_case(name));
|
||||
Some((name, validator))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
let mut configs = create_config(&prefix, config, &mut doc_table);
|
||||
capture_from_env(&prefix, &mut configs);
|
||||
|
||||
for (name, value) in configs.iter() {
|
||||
if let Some(validator) = config_validators.get(name) {
|
||||
validator.validate(value).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
emit_configuration(&prefix, &configs, &mut selected_config);
|
||||
|
||||
if emit_md_tables {
|
||||
let file_name = snake_case(crate_name);
|
||||
write_config_tables(&file_name, doc_table, selected_config);
|
||||
}
|
||||
|
||||
configs
|
||||
}
|
||||
|
||||
// A work-around for https://github.com/rust-lang/cargo/issues/10358
|
||||
// This can be removed when https://github.com/rust-lang/cargo/pull/14058 is merged.
|
||||
// Unlikely to work on projects in workspaces
|
||||
#[cfg(not(test))]
|
||||
fn env_change_work_around() {
|
||||
let mut out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||
|
||||
// We clean out_dir by removing all trailing directories, until it ends with
|
||||
// target
|
||||
while !out_dir.ends_with("target") {
|
||||
if !out_dir.pop() {
|
||||
return; // We ran out of directories...
|
||||
}
|
||||
}
|
||||
out_dir.pop();
|
||||
|
||||
let dotcargo = out_dir.join(".cargo/");
|
||||
if dotcargo.exists() {
|
||||
if dotcargo.join("config.toml").exists() {
|
||||
println!(
|
||||
"cargo:rerun-if-changed={}",
|
||||
dotcargo.join("config.toml").display()
|
||||
);
|
||||
}
|
||||
if dotcargo.join("config").exists() {
|
||||
println!(
|
||||
"cargo:rerun-if-changed={}",
|
||||
dotcargo.join("config").display()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_config(
|
||||
prefix: &str,
|
||||
config: &[(&str, &str, Value, Option<Validator>)],
|
||||
doc_table: &mut String,
|
||||
) -> HashMap<String, Value> {
|
||||
let mut configs = HashMap::new();
|
||||
|
||||
for (name, description, default, _validator) in config {
|
||||
let name = format!("{prefix}{}", screaming_snake_case(name));
|
||||
configs.insert(name.clone(), default.clone());
|
||||
|
||||
// Write documentation table line:
|
||||
let default = default.to_string();
|
||||
writeln!(doc_table, "|**{name}**|{description}|{default}|").unwrap();
|
||||
|
||||
// Rebuild if config environment variable changed:
|
||||
println!("cargo:rerun-if-env-changed={name}");
|
||||
}
|
||||
|
||||
configs
|
||||
}
|
||||
|
||||
fn capture_from_env(prefix: &str, configs: &mut HashMap<String, Value>) {
|
||||
let mut unknown = Vec::new();
|
||||
let mut failed = Vec::new();
|
||||
|
||||
// Try and capture input from the environment:
|
||||
for (var, value) in env::vars() {
|
||||
if var.starts_with(prefix) {
|
||||
let Some(cfg) = configs.get_mut(&var) else {
|
||||
unknown.push(var);
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Err(e) = cfg.parse_in_place(&value) {
|
||||
failed.push(format!("{var}: {e}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !failed.is_empty() {
|
||||
panic!("Invalid configuration options detected: {:?}", failed);
|
||||
}
|
||||
|
||||
if !unknown.is_empty() {
|
||||
panic!("Unknown configuration options detected: {:?}", unknown);
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_configuration(
|
||||
prefix: &str,
|
||||
configs: &HashMap<String, Value>,
|
||||
selected_config: &mut String,
|
||||
) {
|
||||
for (name, value) in configs.iter() {
|
||||
let cfg_name = snake_case(name.trim_start_matches(prefix));
|
||||
println!("cargo:rustc-check-cfg=cfg({cfg_name})");
|
||||
|
||||
if let Value::Bool(true) = value {
|
||||
println!("cargo:rustc-cfg={cfg_name}");
|
||||
}
|
||||
|
||||
let value = value.to_string();
|
||||
|
||||
// Values that haven't been seen will be output here with the default value:
|
||||
println!("cargo:rustc-env={}={}", name, value);
|
||||
writeln!(selected_config, "|**{name}**|{value}|").unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn write_config_tables(prefix: &str, doc_table: String, selected_config: String) {
|
||||
let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||
|
||||
let out_file = out_dir
|
||||
.join(format!("{prefix}_config_table.md"))
|
||||
.display()
|
||||
.to_string();
|
||||
fs::write(out_file, doc_table).unwrap();
|
||||
|
||||
let out_file = out_dir
|
||||
.join(format!("{prefix}_selected_config.md"))
|
||||
.display()
|
||||
.to_string();
|
||||
fs::write(out_file, selected_config).unwrap();
|
||||
}
|
||||
|
||||
fn snake_case(name: &str) -> String {
|
||||
let mut name = name.replace("-", "_");
|
||||
name.make_ascii_lowercase();
|
||||
|
||||
name
|
||||
}
|
||||
|
||||
fn screaming_snake_case(name: &str) -> String {
|
||||
let mut name = name.replace("-", "_");
|
||||
name.make_ascii_uppercase();
|
||||
|
||||
name
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn value_number_formats() {
|
||||
const INPUTS: &[&str] = &["0xAA", "0o252", "0b0000000010101010", "170"];
|
||||
let mut v = Value::Integer(0);
|
||||
|
||||
for input in INPUTS {
|
||||
v.parse_in_place(input).unwrap();
|
||||
// no matter the input format, the output format should be decimal
|
||||
assert_eq!(format!("{v}"), "170");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn value_bool_inputs() {
|
||||
let mut v = Value::Bool(false);
|
||||
|
||||
v.parse_in_place("true").unwrap();
|
||||
assert_eq!(format!("{v}"), "true");
|
||||
|
||||
v.parse_in_place("false").unwrap();
|
||||
assert_eq!(format!("{v}"), "false");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn env_override() {
|
||||
temp_env::with_vars(
|
||||
[
|
||||
("ESP_TEST_CONFIG_NUMBER", Some("0xaa")),
|
||||
("ESP_TEST_CONFIG_NUMBER_SIGNED", Some("-999")),
|
||||
("ESP_TEST_CONFIG_STRING", Some("Hello world!")),
|
||||
("ESP_TEST_CONFIG_BOOL", Some("true")),
|
||||
],
|
||||
|| {
|
||||
let configs = generate_config(
|
||||
"esp-test",
|
||||
&[
|
||||
("number", "NA", Value::Integer(999), None),
|
||||
("number_signed", "NA", Value::Integer(-777), None),
|
||||
("string", "NA", Value::String("Demo".to_owned()), None),
|
||||
("bool", "NA", Value::Bool(false), None),
|
||||
("number_default", "NA", Value::Integer(999), None),
|
||||
(
|
||||
"string_default",
|
||||
"NA",
|
||||
Value::String("Demo".to_owned()),
|
||||
None,
|
||||
),
|
||||
("bool_default", "NA", Value::Bool(false), None),
|
||||
],
|
||||
false,
|
||||
);
|
||||
|
||||
// some values have changed
|
||||
assert_eq!(
|
||||
match configs.get("ESP_TEST_CONFIG_NUMBER").unwrap() {
|
||||
Value::Integer(num) => *num,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
0xaa
|
||||
);
|
||||
assert_eq!(
|
||||
match configs.get("ESP_TEST_CONFIG_NUMBER_SIGNED").unwrap() {
|
||||
Value::Integer(num) => *num,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
-999
|
||||
);
|
||||
assert_eq!(
|
||||
match configs.get("ESP_TEST_CONFIG_STRING").unwrap() {
|
||||
Value::String(val) => val,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
"Hello world!"
|
||||
);
|
||||
assert_eq!(
|
||||
match configs.get("ESP_TEST_CONFIG_BOOL").unwrap() {
|
||||
Value::Bool(val) => *val,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
// the rest are the defaults
|
||||
assert_eq!(
|
||||
match configs.get("ESP_TEST_CONFIG_NUMBER_DEFAULT").unwrap() {
|
||||
Value::Integer(num) => *num,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
999
|
||||
);
|
||||
assert_eq!(
|
||||
match configs.get("ESP_TEST_CONFIG_STRING_DEFAULT").unwrap() {
|
||||
Value::String(val) => val,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
"Demo"
|
||||
);
|
||||
assert_eq!(
|
||||
match configs.get("ESP_TEST_CONFIG_BOOL_DEFAULT").unwrap() {
|
||||
Value::Bool(val) => *val,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
false
|
||||
);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builtin_validation_passes() {
|
||||
temp_env::with_vars(
|
||||
[
|
||||
("ESP_TEST_CONFIG_POSITIVE_NUMBER", Some("7")),
|
||||
("ESP_TEST_CONFIG_NEGATIVE_NUMBER", Some("-1")),
|
||||
("ESP_TEST_CONFIG_NON_NEGATIVE_NUMBER", Some("0")),
|
||||
("ESP_TEST_CONFIG_RANGE", Some("9")),
|
||||
],
|
||||
|| {
|
||||
generate_config(
|
||||
"esp-test",
|
||||
&[
|
||||
(
|
||||
"positive_number",
|
||||
"NA",
|
||||
Value::Integer(-1),
|
||||
Some(Validator::PositiveInteger),
|
||||
),
|
||||
(
|
||||
"negative_number",
|
||||
"NA",
|
||||
Value::Integer(1),
|
||||
Some(Validator::NegativeInteger),
|
||||
),
|
||||
(
|
||||
"non_negative_number",
|
||||
"NA",
|
||||
Value::Integer(-1),
|
||||
Some(Validator::NonNegativeInteger),
|
||||
),
|
||||
(
|
||||
"range",
|
||||
"NA",
|
||||
Value::Integer(0),
|
||||
Some(Validator::IntegerInRange(5..10)),
|
||||
),
|
||||
],
|
||||
false,
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_validation_passes() {
|
||||
temp_env::with_vars([("ESP_TEST_CONFIG_NUMBER", Some("13"))], || {
|
||||
generate_config(
|
||||
"esp-test",
|
||||
&[(
|
||||
"number",
|
||||
"NA",
|
||||
Value::Integer(-1),
|
||||
Some(Validator::Custom(Box::new(|value| {
|
||||
let range = 10..20;
|
||||
if !value.is_integer() || !range.contains(&value.as_integer()) {
|
||||
Err(Error::validation("value does not fall within range"))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}))),
|
||||
)],
|
||||
false,
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn builtin_validation_bails() {
|
||||
temp_env::with_vars([("ESP_TEST_CONFIG_POSITIVE_NUMBER", Some("-99"))], || {
|
||||
generate_config(
|
||||
"esp-test",
|
||||
&[(
|
||||
"positive_number",
|
||||
"NA",
|
||||
Value::Integer(-1),
|
||||
Some(Validator::PositiveInteger),
|
||||
)],
|
||||
false,
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn custom_validation_bails() {
|
||||
temp_env::with_vars([("ESP_TEST_CONFIG_NUMBER", Some("37"))], || {
|
||||
generate_config(
|
||||
"esp-test",
|
||||
&[(
|
||||
"number",
|
||||
"NA",
|
||||
Value::Integer(-1),
|
||||
Some(Validator::Custom(Box::new(|value| {
|
||||
let range = 10..20;
|
||||
if !value.is_integer() || !range.contains(&value.as_integer()) {
|
||||
Err(Error::validation("value does not fall within range"))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}))),
|
||||
)],
|
||||
false,
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn env_unknown_bails() {
|
||||
temp_env::with_vars(
|
||||
[
|
||||
("ESP_TEST_CONFIG_NUMBER", Some("0xaa")),
|
||||
("ESP_TEST_CONFIG_RANDOM_VARIABLE", Some("")),
|
||||
],
|
||||
|| {
|
||||
generate_config(
|
||||
"esp-test",
|
||||
&[("number", "NA", Value::Integer(999), None)],
|
||||
false,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn env_invalid_values_bails() {
|
||||
temp_env::with_vars([("ESP_TEST_CONFIG_NUMBER", Some("Hello world"))], || {
|
||||
generate_config(
|
||||
"esp-test",
|
||||
&[("number", "NA", Value::Integer(999), None)],
|
||||
false,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn env_unknown_prefix_is_ignored() {
|
||||
temp_env::with_vars(
|
||||
[("ESP_TEST_OTHER_CONFIG_NUMBER", Some("Hello world"))],
|
||||
|| {
|
||||
generate_config(
|
||||
"esp-test",
|
||||
&[("number", "NA", Value::Integer(999), None)],
|
||||
false,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
110
esp-config/src/lib.rs
Normal file
110
esp-config/src/lib.rs
Normal file
@ -0,0 +1,110 @@
|
||||
#![doc = include_str!("../README.md")]
|
||||
//! ## Feature Flags
|
||||
#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
|
||||
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
|
||||
#![cfg_attr(not(feature = "build"), no_std)]
|
||||
#![deny(missing_docs, rust_2018_idioms)]
|
||||
|
||||
#[cfg(feature = "build")]
|
||||
mod generate;
|
||||
#[cfg(feature = "build")]
|
||||
pub use generate::{generate_config, Error, Validator, Value};
|
||||
|
||||
/// Parse the value of an environment variable as a [bool] at compile time.
|
||||
#[macro_export]
|
||||
macro_rules! esp_config_bool {
|
||||
( $var:expr ) => {
|
||||
match env!($var).as_bytes() {
|
||||
b"true" => true,
|
||||
b"false" => false,
|
||||
_ => ::core::panic!("boolean value must be either 'true' or 'false'"),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: From 1.82 on, we can use `<$ty>::from_str_radix(env!($var), 10)`
|
||||
/// Parse the value of an environment variable as an integer at compile time.
|
||||
#[macro_export]
|
||||
macro_rules! esp_config_int {
|
||||
( $ty:ty, $var:expr ) => {
|
||||
const {
|
||||
const BYTES: &[u8] = env!($var).as_bytes();
|
||||
$crate::esp_config_int_parse!($ty, BYTES)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Get the string value of an environment variable at compile time.
|
||||
#[macro_export]
|
||||
macro_rules! esp_config_str {
|
||||
( $var:expr ) => {
|
||||
env!($var)
|
||||
};
|
||||
}
|
||||
|
||||
/// Parse a string like "777" into an integer, which _can_ be used in a `const`
|
||||
/// context
|
||||
#[doc(hidden)] // To avoid confusion with `esp_config_int`, hide this in the docs
|
||||
#[macro_export]
|
||||
macro_rules! esp_config_int_parse {
|
||||
( $ty:ty, $bytes:expr ) => {{
|
||||
let mut bytes = $bytes;
|
||||
let mut val: $ty = 0;
|
||||
let mut sign_seen = false;
|
||||
let mut is_negative = false;
|
||||
|
||||
while let [byte, rest @ ..] = bytes {
|
||||
match *byte {
|
||||
b'0'..=b'9' => {
|
||||
val = val * 10 + (*byte - b'0') as $ty;
|
||||
}
|
||||
b'-' | b'+' if !sign_seen => {
|
||||
is_negative = *byte == b'-';
|
||||
sign_seen = true;
|
||||
}
|
||||
_ => ::core::panic!("invalid character encountered while parsing integer"),
|
||||
}
|
||||
|
||||
bytes = rest;
|
||||
}
|
||||
|
||||
if is_negative {
|
||||
let original = val;
|
||||
// Subtract the value twice to get a negative:
|
||||
val -= original;
|
||||
val -= original;
|
||||
}
|
||||
|
||||
val
|
||||
}};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
// We can only test success in the const context
|
||||
const _: () = {
|
||||
core::assert!(esp_config_int_parse!(i64, "-77777".as_bytes()) == -77777);
|
||||
core::assert!(esp_config_int_parse!(isize, "-7777".as_bytes()) == -7777);
|
||||
core::assert!(esp_config_int_parse!(i32, "-999".as_bytes()) == -999);
|
||||
core::assert!(esp_config_int_parse!(i16, "-99".as_bytes()) == -99);
|
||||
core::assert!(esp_config_int_parse!(i8, "-9".as_bytes()) == -9);
|
||||
|
||||
core::assert!(esp_config_int_parse!(u64, "77777".as_bytes()) == 77777);
|
||||
core::assert!(esp_config_int_parse!(usize, "7777".as_bytes()) == 7777);
|
||||
core::assert!(esp_config_int_parse!(u32, "999".as_bytes()) == 999);
|
||||
core::assert!(esp_config_int_parse!(u16, "99".as_bytes()) == 99);
|
||||
core::assert!(esp_config_int_parse!(u8, "9".as_bytes()) == 9);
|
||||
};
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_expect_positive() {
|
||||
esp_config_int_parse!(u8, "-5".as_bytes());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_invalid_digit() {
|
||||
esp_config_int_parse!(u32, "a".as_bytes());
|
||||
}
|
||||
}
|
||||
1
esp-hal-embassy/.clippy.toml
Normal file
1
esp-hal-embassy/.clippy.toml
Normal file
@ -0,0 +1 @@
|
||||
avoid-breaking-exported-api = false
|
||||
74
esp-hal-embassy/CHANGELOG.md
Normal file
74
esp-hal-embassy/CHANGELOG.md
Normal file
@ -0,0 +1,74 @@
|
||||
# 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]
|
||||
|
||||
|
||||
### Added
|
||||
|
||||
### Changed
|
||||
|
||||
- Bump MSRV to 1.83 (#2615)
|
||||
|
||||
- Config: Crate prefixes and configuration keys are now separated by `_CONFIG_` (#2848)
|
||||
|
||||
### Fixed
|
||||
|
||||
### Removed
|
||||
|
||||
## 0.5.0 - 2024-11-20
|
||||
|
||||
### Added
|
||||
|
||||
- `ESP_HAL_EMBASSY_LOW_POWER_WAIT` configuration option. (#2329)
|
||||
|
||||
### Changed
|
||||
|
||||
- Reduce memory footprint by 4 bytes on multi-core MCUs.
|
||||
- The time driver no longer uses cross-core critical sections. (#2559)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Alarm interrupts are now handled on the core that allocated them. (For executors created on the second core after calling `esp_hal_embassy::init`) (#2451)
|
||||
|
||||
### Removed
|
||||
|
||||
## 0.4.0 - 2024-10-10
|
||||
|
||||
### Changed
|
||||
|
||||
- MSRV bump to 1.79 (#2156)
|
||||
|
||||
### Removed
|
||||
|
||||
- Removed the `clocks` parameter from `esp_hal_embassy::init`. (#1999)
|
||||
|
||||
## 0.3.0 - 2024-08-29
|
||||
|
||||
### Added
|
||||
|
||||
- This package now re-exports the `esp_hal_procmacros::main` macro (#1828)
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated to latest release (`0.6.0`) for `embassy-executor` (#1942)
|
||||
- Changed `init` to accept timers of multiple types (#1957, #2012)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed a bug where the timeout was huge whenever the timestamp at the time of scheduling was already in the past (#1875)
|
||||
- Fixed interrupt executors looping endlessly when `integrated-timers` is used. (#1936)
|
||||
|
||||
## 0.2.0 - 2024-07-15
|
||||
|
||||
### Changed
|
||||
|
||||
- Removed the TIMG and SYSTIMER time drivers, replaced by a generic time driver taking `OneShotTimer<ErasedTimer>` (#1753)
|
||||
|
||||
## 0.1.0 - 2024-06-04
|
||||
|
||||
[Unreleased]: https://github.com/esp-rs/esp-hal/commits/main/esp-hal-embassy?since=2024-11-20
|
||||
54
esp-hal-embassy/Cargo.toml
Normal file
54
esp-hal-embassy/Cargo.toml
Normal file
@ -0,0 +1,54 @@
|
||||
[package]
|
||||
name = "esp-hal-embassy"
|
||||
version = "0.5.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.83.0"
|
||||
description = "Embassy support for esp-hal"
|
||||
keywords = ["async", "embedded", "esp32", "espressif"]
|
||||
categories = ["asynchronous", "embedded", "hardware-support", "no-std"]
|
||||
repository = "https://github.com/esp-rs/esp-hal"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
default-target = "riscv32imac-unknown-none-elf"
|
||||
features = ["esp32c6"]
|
||||
|
||||
[dependencies]
|
||||
critical-section = "1.2.0"
|
||||
defmt = { version = "0.3.8", optional = true }
|
||||
document-features = "0.2.10"
|
||||
embassy-executor = { version = "0.6.3", optional = true }
|
||||
embassy-time-driver = { version = "0.1.0", features = [ "tick-hz-1_000_000" ] }
|
||||
esp-hal = { version = "0.22.0", path = "../esp-hal" }
|
||||
log = { version = "0.4.22", optional = true }
|
||||
macros = { version = "0.15.0", features = ["embassy"], package = "esp-hal-procmacros", path = "../esp-hal-procmacros" }
|
||||
portable-atomic = "1.9.0"
|
||||
static_cell = "2.1.0"
|
||||
|
||||
[build-dependencies]
|
||||
esp-build = { version = "0.1.0", path = "../esp-build" }
|
||||
esp-config = { version = "0.2.0", path = "../esp-config", features = ["build"] }
|
||||
esp-metadata = { version = "0.4.0", path = "../esp-metadata" }
|
||||
|
||||
[features]
|
||||
default = ["executors"]
|
||||
|
||||
esp32 = ["esp-hal/esp32"]
|
||||
esp32c2 = ["esp-hal/esp32c2"]
|
||||
esp32c3 = ["esp-hal/esp32c3"]
|
||||
esp32c6 = ["esp-hal/esp32c6"]
|
||||
esp32h2 = ["esp-hal/esp32h2"]
|
||||
esp32s2 = ["esp-hal/esp32s2"]
|
||||
esp32s3 = ["esp-hal/esp32s3"]
|
||||
|
||||
## Implement `defmt::Format` on certain types.
|
||||
defmt = ["dep:defmt", "embassy-executor?/defmt", "esp-hal/defmt"]
|
||||
## Enable logging via the log crate
|
||||
log = ["dep:log"]
|
||||
## Provide `Executor` and `InterruptExecutor`
|
||||
executors = ["dep:embassy-executor", "esp-hal/__esp_hal_embassy"]
|
||||
## Use the executor-integrated `embassy-time` timer queue.
|
||||
integrated-timers = ["embassy-executor?/integrated-timers"]
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = "allow"
|
||||
63
esp-hal-embassy/MIGRATING-0.3.md
Normal file
63
esp-hal-embassy/MIGRATING-0.3.md
Normal file
@ -0,0 +1,63 @@
|
||||
# Migration Guide from 0.3.x to v0.4.x
|
||||
|
||||
## Initialization
|
||||
|
||||
You no longer have to set up clocks and pass them to `esp_hal_embassy::init`.
|
||||
|
||||
```diff
|
||||
use esp_hal::{
|
||||
- clock::ClockControl,
|
||||
- peripherals::Peripherals,
|
||||
prelude::*,
|
||||
- system::SystemControl,
|
||||
};
|
||||
|
||||
#[esp_hal_embassy::main]
|
||||
async fn main(_spawner: Spawner) -> ! {
|
||||
- let peripherals = Peripherals::take();
|
||||
- let system = SystemControl::new(peripherals.SYSTEM);
|
||||
- let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
|
||||
+ let peripherals = esp_hal::init(esp_hal::Config::default());
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
- esp_hal_embassy::init(&clocks, timg0);
|
||||
+ esp_hal_embassy::init(timg0.timer0);
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
You have to specify a timer instance (that may be a `TimerGroup` timer unit
|
||||
or a `SystemTimer` alarm) or an array of `AnyTimer`s when calling `init`.
|
||||
An example of how you can set multiple timers (for example when using
|
||||
multiple executors):
|
||||
|
||||
```rust
|
||||
use esp_hal::{
|
||||
prelude::*,
|
||||
timer::{
|
||||
AnyTimer,
|
||||
systimer::SystemTimer
|
||||
}
|
||||
};
|
||||
|
||||
#[esp_hal_embassy::main]
|
||||
async fn main(_spawner: Spawner) -> ! {
|
||||
let peripherals = esp_hal::init(esp_hal::Config::default());
|
||||
|
||||
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
let timer0: AnyTimer = timg0.timer0.into();
|
||||
let timer1: AnyTimer = timg0.timer1.into();
|
||||
|
||||
// You can use either a TimerGroup timer, a SystemTimer alarm,
|
||||
// or you can mix and match them as well.
|
||||
let systimer = SystemTimer::new(peripherals.SYSTIMER).split::<Target>();
|
||||
let timer2: AnyTimer = systimer.alarm0;
|
||||
|
||||
esp_hal_embassy::init([timer0, timer1, timer2]);
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Note that you only have to convert into `AnyTimer` if you want to use multiple timers.
|
||||
3
esp-hal-embassy/MIGRATING-0.4.md
Normal file
3
esp-hal-embassy/MIGRATING-0.4.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Migration Guide from 0.4.x to v0.5.x
|
||||
|
||||
You don't have to make changes to your code to update esp-hal-embassy.
|
||||
15
esp-hal-embassy/MIGRATING-0.5.md
Normal file
15
esp-hal-embassy/MIGRATING-0.5.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Migration Guide from 0.5.x to v0.6.x
|
||||
|
||||
## Crate configuration changes
|
||||
|
||||
To prevent ambiguity between configurations, we had to change the naming format of configuration
|
||||
keys. Before, we used `{prefix}_{key}`, which meant that esp-hal and esp-hal-* configuration keys
|
||||
were impossible to tell apart. To fix this issue, we are changing the separator from one underscore
|
||||
character to `_CONFIG_`. This also means that users will have to change their `config.toml`
|
||||
configurations to match the new format.
|
||||
|
||||
```diff
|
||||
[env]
|
||||
-ESP_HAL_EMBASSY_LOW_POWER_WAIT="false"
|
||||
+ESP_HAL_EMBASSY_CONFIG_LOW_POWER_WAIT="false"
|
||||
```
|
||||
37
esp-hal-embassy/README.md
Normal file
37
esp-hal-embassy/README.md
Normal file
@ -0,0 +1,37 @@
|
||||
# esp-hal-embassy
|
||||
|
||||
[](https://crates.io/crates/esp-hal-embassy)
|
||||
[](https://docs.rs/esp-hal-embassy)
|
||||

|
||||

|
||||
[](https://matrix.to/#/#esp-rs:matrix.org)
|
||||
|
||||
[Embassy] support for `esp-hal`.
|
||||
|
||||
Note that this crate currently requires you to enable the `unstable` feature on `esp-hal`.
|
||||
|
||||
[embassy]: https://github.com/embassy-rs/embassy
|
||||
|
||||
## [Documentation]
|
||||
|
||||
[documentation]: https://docs.rs/esp-hal-embassy/
|
||||
|
||||
## Minimum Supported Rust Version (MSRV)
|
||||
|
||||
This crate is guaranteed to compile on stable Rust 1.83 and up. It _might_
|
||||
compile with older versions but that may change in any new patch release.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of:
|
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
- MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in
|
||||
the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without
|
||||
any additional terms or conditions.
|
||||
54
esp-hal-embassy/build.rs
Normal file
54
esp-hal-embassy/build.rs
Normal file
@ -0,0 +1,54 @@
|
||||
use std::{error::Error, str::FromStr};
|
||||
|
||||
use esp_build::assert_unique_used_features;
|
||||
use esp_config::{generate_config, Value};
|
||||
use esp_metadata::{Chip, Config};
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
// NOTE: update when adding new device support!
|
||||
// Ensure that exactly one chip has been specified:
|
||||
assert_unique_used_features!(
|
||||
"esp32", "esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32s2", "esp32s3"
|
||||
);
|
||||
|
||||
// NOTE: update when adding new device support!
|
||||
// Determine the name of the configured device:
|
||||
let device_name = if cfg!(feature = "esp32") {
|
||||
"esp32"
|
||||
} else if cfg!(feature = "esp32c2") {
|
||||
"esp32c2"
|
||||
} else if cfg!(feature = "esp32c3") {
|
||||
"esp32c3"
|
||||
} else if cfg!(feature = "esp32c6") {
|
||||
"esp32c6"
|
||||
} else if cfg!(feature = "esp32h2") {
|
||||
"esp32h2"
|
||||
} else if cfg!(feature = "esp32s2") {
|
||||
"esp32s2"
|
||||
} else if cfg!(feature = "esp32s3") {
|
||||
"esp32s3"
|
||||
} else {
|
||||
unreachable!() // We've confirmed exactly one known device was selected
|
||||
};
|
||||
|
||||
// Load the configuration file for the configured device:
|
||||
let chip = Chip::from_str(device_name)?;
|
||||
let config = Config::for_chip(&chip);
|
||||
|
||||
// Define all necessary configuration symbols for the configured device:
|
||||
config.define_symbols();
|
||||
|
||||
// emit config
|
||||
generate_config(
|
||||
"esp_hal_embassy",
|
||||
&[(
|
||||
"low-power-wait",
|
||||
"Enables the lower-power wait if no tasks are ready to run on the thread-mode executor. This allows the MCU to use less power if the workload allows. Recommended for battery-powered systems. May impact analog performance.",
|
||||
Value::Bool(true),
|
||||
None
|
||||
)],
|
||||
true,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
137
esp-hal-embassy/src/executor/interrupt.rs
Normal file
137
esp-hal-embassy/src/executor/interrupt.rs
Normal file
@ -0,0 +1,137 @@
|
||||
//! Interrupt-mode executor.
|
||||
|
||||
use core::{cell::UnsafeCell, mem::MaybeUninit};
|
||||
|
||||
use embassy_executor::{raw, SendSpawner};
|
||||
use esp_hal::{
|
||||
interrupt::{self, software::SoftwareInterrupt, InterruptHandler},
|
||||
Cpu,
|
||||
};
|
||||
use portable_atomic::{AtomicUsize, Ordering};
|
||||
|
||||
const COUNT: usize = 3 + cfg!(not(multi_core)) as usize;
|
||||
static mut EXECUTORS: [CallbackContext; COUNT] = [const { CallbackContext::new() }; COUNT];
|
||||
|
||||
/// Interrupt mode executor.
|
||||
///
|
||||
/// This executor runs tasks in interrupt mode. The interrupt handler is set up
|
||||
/// to poll tasks, and when a task is woken the interrupt is pended from
|
||||
/// software.
|
||||
pub struct InterruptExecutor<const SWI: u8> {
|
||||
core: AtomicUsize,
|
||||
executor: UnsafeCell<MaybeUninit<raw::Executor>>,
|
||||
interrupt: SoftwareInterrupt<SWI>,
|
||||
}
|
||||
|
||||
unsafe impl<const SWI: u8> Send for InterruptExecutor<SWI> {}
|
||||
unsafe impl<const SWI: u8> Sync for InterruptExecutor<SWI> {}
|
||||
|
||||
struct CallbackContext {
|
||||
raw_executor: UnsafeCell<*mut raw::Executor>,
|
||||
}
|
||||
|
||||
impl CallbackContext {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
raw_executor: UnsafeCell::new(core::ptr::null_mut()),
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self) -> *mut raw::Executor {
|
||||
unsafe { *self.raw_executor.get() }
|
||||
}
|
||||
|
||||
fn set(&self, executor: *mut raw::Executor) {
|
||||
unsafe { self.raw_executor.get().write(executor) };
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn handle_interrupt<const NUM: u8>() {
|
||||
let swi = unsafe { SoftwareInterrupt::<NUM>::steal() };
|
||||
swi.reset();
|
||||
|
||||
unsafe {
|
||||
let executor = unwrap!(EXECUTORS[NUM as usize].get().as_mut());
|
||||
executor.poll();
|
||||
}
|
||||
}
|
||||
|
||||
impl<const SWI: u8> InterruptExecutor<SWI> {
|
||||
/// Create a new `InterruptExecutor`.
|
||||
/// This takes the software interrupt to be used internally.
|
||||
#[inline]
|
||||
pub const fn new(interrupt: SoftwareInterrupt<SWI>) -> Self {
|
||||
Self {
|
||||
core: AtomicUsize::new(usize::MAX),
|
||||
executor: UnsafeCell::new(MaybeUninit::uninit()),
|
||||
interrupt,
|
||||
}
|
||||
}
|
||||
|
||||
/// Start the executor at the given priority level.
|
||||
///
|
||||
/// This initializes the executor, enables the interrupt, and returns.
|
||||
/// The executor keeps running in the background through the interrupt.
|
||||
///
|
||||
/// This returns a [`SendSpawner`] you can use to spawn tasks on it. A
|
||||
/// [`SendSpawner`] is returned instead of a
|
||||
/// [`Spawner`](embassy_executor::Spawner) because the
|
||||
/// executor effectively runs in a different "thread" (the interrupt),
|
||||
/// so spawning tasks on it is effectively sending them.
|
||||
///
|
||||
/// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor,
|
||||
/// use [`Spawner::for_current_executor`](embassy_executor::Spawner::for_current_executor)
|
||||
/// from a task running in it.
|
||||
pub fn start(&'static mut self, priority: interrupt::Priority) -> SendSpawner {
|
||||
if self
|
||||
.core
|
||||
.compare_exchange(
|
||||
usize::MAX,
|
||||
Cpu::current() as usize,
|
||||
Ordering::Acquire,
|
||||
Ordering::Relaxed,
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
panic!("InterruptExecutor::start() called multiple times on the same executor.");
|
||||
}
|
||||
|
||||
unsafe {
|
||||
(*self.executor.get())
|
||||
.as_mut_ptr()
|
||||
.write(raw::Executor::new((SWI as usize) as *mut ()));
|
||||
|
||||
EXECUTORS[SWI as usize].set((*self.executor.get()).as_mut_ptr());
|
||||
}
|
||||
|
||||
let swi_handler = match SWI {
|
||||
0 => handle_interrupt::<0>,
|
||||
1 => handle_interrupt::<1>,
|
||||
2 => handle_interrupt::<2>,
|
||||
#[cfg(not(multi_core))]
|
||||
3 => handle_interrupt::<3>,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
self.interrupt
|
||||
.set_interrupt_handler(InterruptHandler::new(swi_handler, priority));
|
||||
|
||||
let executor = unsafe { (*self.executor.get()).assume_init_ref() };
|
||||
executor.spawner().make_send()
|
||||
}
|
||||
|
||||
/// Get a SendSpawner for this executor
|
||||
///
|
||||
/// This returns a [`SendSpawner`] you can use to spawn tasks on this
|
||||
/// executor.
|
||||
///
|
||||
/// This MUST only be called on an executor that has already been started.
|
||||
/// The function will panic otherwise.
|
||||
pub fn spawner(&'static self) -> SendSpawner {
|
||||
if self.core.load(Ordering::Acquire) == usize::MAX {
|
||||
panic!("InterruptExecutor::spawner() called on uninitialized executor.");
|
||||
}
|
||||
let executor = unsafe { (*self.executor.get()).assume_init_ref() };
|
||||
executor.spawner().make_send()
|
||||
}
|
||||
}
|
||||
24
esp-hal-embassy/src/executor/mod.rs
Normal file
24
esp-hal-embassy/src/executor/mod.rs
Normal file
@ -0,0 +1,24 @@
|
||||
pub use self::{interrupt::*, thread::*};
|
||||
|
||||
mod interrupt;
|
||||
mod thread;
|
||||
|
||||
#[export_name = "__pender"]
|
||||
fn __pender(context: *mut ()) {
|
||||
use esp_hal::interrupt::software::SoftwareInterrupt;
|
||||
|
||||
match context as usize {
|
||||
// For interrupt executors, the context value is the
|
||||
// software interrupt number
|
||||
0 => unsafe { SoftwareInterrupt::<0>::steal().raise() },
|
||||
1 => unsafe { SoftwareInterrupt::<1>::steal().raise() },
|
||||
2 => unsafe { SoftwareInterrupt::<2>::steal().raise() },
|
||||
#[cfg(not(multi_core))]
|
||||
3 => unsafe { SoftwareInterrupt::<3>::steal().raise() },
|
||||
// THREAD_MODE_CONTEXT + core ID
|
||||
16 => thread::pend_thread_mode(0),
|
||||
#[cfg(multi_core)]
|
||||
17 => thread::pend_thread_mode(1),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
176
esp-hal-embassy/src/executor/thread.rs
Normal file
176
esp-hal-embassy/src/executor/thread.rs
Normal file
@ -0,0 +1,176 @@
|
||||
//! Multicore-aware thread-mode embassy executor.
|
||||
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embassy_executor::{raw, Spawner};
|
||||
use esp_hal::Cpu;
|
||||
#[cfg(multi_core)]
|
||||
use esp_hal::{interrupt::software::SoftwareInterrupt, macros::handler};
|
||||
#[cfg(low_power_wait)]
|
||||
use portable_atomic::{AtomicBool, Ordering};
|
||||
|
||||
pub(crate) const THREAD_MODE_CONTEXT: usize = 16;
|
||||
|
||||
/// global atomic used to keep track of whether there is work to do since sev()
|
||||
/// is not available on either Xtensa or RISC-V
|
||||
static SIGNAL_WORK_THREAD_MODE: [AtomicBool; Cpu::COUNT] =
|
||||
[const { AtomicBool::new(false) }; Cpu::COUNT];
|
||||
|
||||
#[cfg(all(multi_core, low_power_wait))]
|
||||
#[handler]
|
||||
fn software3_interrupt() {
|
||||
// This interrupt is fired when the thread-mode executor's core needs to be
|
||||
// woken. It doesn't matter which core handles this interrupt first, the
|
||||
// point is just to wake up the core that is currently executing
|
||||
// `waiti`.
|
||||
unsafe { SoftwareInterrupt::<3>::steal().reset() };
|
||||
}
|
||||
|
||||
pub(crate) fn pend_thread_mode(_core: usize) {
|
||||
#[cfg(low_power_wait)]
|
||||
{
|
||||
// Signal that there is work to be done.
|
||||
SIGNAL_WORK_THREAD_MODE[_core].store(true, Ordering::Relaxed);
|
||||
|
||||
// If we are pending a task on the current core, we're done. Otherwise, we
|
||||
// need to make sure the other core wakes up.
|
||||
#[cfg(multi_core)]
|
||||
if _core != Cpu::current() as usize {
|
||||
// We need to clear the interrupt from software. We don't actually
|
||||
// need it to trigger and run the interrupt handler, we just need to
|
||||
// kick waiti to return.
|
||||
unsafe { SoftwareInterrupt::<3>::steal().raise() };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Thread mode executor.
|
||||
///
|
||||
/// This is the simplest and most common kind of executor. It runs on thread
|
||||
/// mode (at the lowest priority level).
|
||||
#[cfg_attr(multi_core, doc = "")]
|
||||
#[cfg_attr(
|
||||
multi_core,
|
||||
doc = "This executor is safe to use on multiple cores. You need to
|
||||
create one instance per core. The executors don't steal tasks from each other."
|
||||
)]
|
||||
pub struct Executor {
|
||||
inner: raw::Executor,
|
||||
not_send: PhantomData<*mut ()>,
|
||||
}
|
||||
|
||||
impl Executor {
|
||||
/// Create a new Executor.
|
||||
#[cfg_attr(
|
||||
multi_core,
|
||||
doc = r#"
|
||||
|
||||
This will use software-interrupt 3 which isn't available for anything else to wake the other core(s)."#
|
||||
)]
|
||||
pub fn new() -> Self {
|
||||
#[cfg(all(multi_core, low_power_wait))]
|
||||
unsafe {
|
||||
SoftwareInterrupt::<3>::steal().set_interrupt_handler(software3_interrupt);
|
||||
}
|
||||
|
||||
Self {
|
||||
inner: raw::Executor::new((THREAD_MODE_CONTEXT + Cpu::current() as usize) as *mut ()),
|
||||
not_send: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Run the executor.
|
||||
///
|
||||
/// The `init` closure is called with a [`Spawner`] that spawns tasks on
|
||||
/// this executor. Use it to spawn the initial task(s). After `init`
|
||||
/// returns, the executor starts running the tasks.
|
||||
///
|
||||
/// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is
|
||||
/// `Copy`), for example by passing it as an argument to the initial
|
||||
/// tasks.
|
||||
///
|
||||
/// This function requires `&'static mut self`. This means you have to store
|
||||
/// the Executor instance in a place where it'll live forever and grants
|
||||
/// you mutable access. There's a few ways to do this:
|
||||
///
|
||||
/// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
|
||||
/// - a `static mut` (unsafe, not recommended)
|
||||
/// - a local variable in a function you know never returns (like `fn main()
|
||||
/// -> !`), upgrading its lifetime with `transmute`. (unsafe)
|
||||
///
|
||||
/// This function never returns.
|
||||
pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
|
||||
init(self.inner.spawner());
|
||||
|
||||
#[cfg(low_power_wait)]
|
||||
let cpu = Cpu::current() as usize;
|
||||
|
||||
loop {
|
||||
unsafe { self.inner.poll() };
|
||||
|
||||
#[cfg(low_power_wait)]
|
||||
Self::wait_impl(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(xtensa, low_power_wait))]
|
||||
fn wait_impl(cpu: usize) {
|
||||
// Manual critical section implementation that only masks interrupts handlers.
|
||||
// We must not acquire the cross-core on dual-core systems because that would
|
||||
// prevent the other core from doing useful work while this core is sleeping.
|
||||
let token: critical_section::RawRestoreState;
|
||||
unsafe { core::arch::asm!("rsil {0}, 5", out(reg) token) };
|
||||
|
||||
// we do not care about race conditions between the load and store operations,
|
||||
// interrupts will only set this value to true.
|
||||
// Acquire makes no sense but at this time it's slightly faster than Relaxed.
|
||||
if SIGNAL_WORK_THREAD_MODE[cpu].load(Ordering::Acquire) {
|
||||
// if there is work to do, exit critical section and loop back to polling
|
||||
unsafe {
|
||||
core::arch::asm!(
|
||||
"wsr.ps {0}",
|
||||
"rsync",
|
||||
in(reg) token
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// `waiti` sets the `PS.INTLEVEL` when slipping into sleep because critical
|
||||
// sections in Xtensa are implemented via increasing `PS.INTLEVEL`.
|
||||
// The critical section ends here. Take care not add code after
|
||||
// `waiti` if it needs to be inside the CS.
|
||||
// Do not lower INTLEVEL below the current value.
|
||||
match token & 0x0F {
|
||||
0 => unsafe { core::arch::asm!("waiti 0") },
|
||||
1 => unsafe { core::arch::asm!("waiti 1") },
|
||||
2 => unsafe { core::arch::asm!("waiti 2") },
|
||||
3 => unsafe { core::arch::asm!("waiti 3") },
|
||||
4 => unsafe { core::arch::asm!("waiti 4") },
|
||||
_ => unsafe { core::arch::asm!("waiti 5") },
|
||||
}
|
||||
}
|
||||
// If this races and some waker sets the signal, we'll reset it, but still poll.
|
||||
SIGNAL_WORK_THREAD_MODE[cpu].store(false, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
#[cfg(all(riscv, low_power_wait))]
|
||||
fn wait_impl(cpu: usize) {
|
||||
// we do not care about race conditions between the load and store operations,
|
||||
// interrupts will only set this value to true.
|
||||
critical_section::with(|_| {
|
||||
// if there is work to do, loop back to polling
|
||||
if !SIGNAL_WORK_THREAD_MODE[cpu].load(Ordering::Relaxed) {
|
||||
// if not, wait for interrupt
|
||||
unsafe { core::arch::asm!("wfi") };
|
||||
}
|
||||
});
|
||||
// if an interrupt occurred while waiting, it will be serviced here
|
||||
// If this races and some waker sets the signal, we'll reset it, but still poll.
|
||||
SIGNAL_WORK_THREAD_MODE[cpu].store(false, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Executor {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
226
esp-hal-embassy/src/fmt.rs
Normal file
226
esp-hal-embassy/src/fmt.rs
Normal file
@ -0,0 +1,226 @@
|
||||
#![macro_use]
|
||||
#![allow(unused_macros)]
|
||||
|
||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||
|
||||
macro_rules! assert {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::assert!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::assert!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! assert_eq {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::assert_eq!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::assert_eq!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! assert_ne {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::assert_ne!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::assert_ne!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug_assert {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::debug_assert!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug_assert!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug_assert_eq {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::debug_assert_eq!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug_assert_eq!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug_assert_ne {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::debug_assert_ne!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug_assert_ne!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! todo {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::todo!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::todo!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! unreachable {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::unreachable!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::unreachable!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! panic {
|
||||
($($x:tt)*) => {
|
||||
{
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
::core::panic!($($x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::panic!($($x)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! trace {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::trace!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::trace!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! debug {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::debug!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::debug!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! info {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::info!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::info!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! warn {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::warn!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::warn!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! error {
|
||||
($s:literal $(, $x:expr)* $(,)?) => {
|
||||
{
|
||||
#[cfg(feature = "log")]
|
||||
::log::error!($s $(, $x)*);
|
||||
#[cfg(feature = "defmt")]
|
||||
::defmt::error!($s $(, $x)*);
|
||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||
let _ = ($( & $x ),*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
macro_rules! unwrap {
|
||||
($($x:tt)*) => {
|
||||
::defmt::unwrap!($($x)*)
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "defmt"))]
|
||||
macro_rules! unwrap {
|
||||
($arg:expr) => {
|
||||
match $crate::fmt::Try::into_result($arg) {
|
||||
::core::result::Result::Ok(t) => t,
|
||||
::core::result::Result::Err(e) => {
|
||||
::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
|
||||
}
|
||||
}
|
||||
};
|
||||
($arg:expr, $($msg:expr),+ $(,)? ) => {
|
||||
match $crate::fmt::Try::into_result($arg) {
|
||||
::core::result::Result::Ok(t) => t,
|
||||
::core::result::Result::Err(e) => {
|
||||
::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct NoneError;
|
||||
|
||||
pub trait Try {
|
||||
type Ok;
|
||||
type Error;
|
||||
#[allow(unused)]
|
||||
fn into_result(self) -> Result<Self::Ok, Self::Error>;
|
||||
}
|
||||
|
||||
impl<T> Try for Option<T> {
|
||||
type Ok = T;
|
||||
type Error = NoneError;
|
||||
|
||||
#[inline]
|
||||
fn into_result(self) -> Result<T, NoneError> {
|
||||
self.ok_or(NoneError)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> Try for Result<T, E> {
|
||||
type Ok = T;
|
||||
type Error = E;
|
||||
|
||||
#[inline]
|
||||
fn into_result(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
162
esp-hal-embassy/src/lib.rs
Normal file
162
esp-hal-embassy/src/lib.rs
Normal file
@ -0,0 +1,162 @@
|
||||
//! Embassy support for [esp-hal].
|
||||
//!
|
||||
//! [Embassy] is a modern asynchronous framework intended for use with embedded
|
||||
//! systems. This package provides support for building applications using
|
||||
//! Embassy with [esp-hal].
|
||||
//!
|
||||
//! Note that this crate currently requires you to enable the `unstable` feature
|
||||
//! on `esp-hal`.
|
||||
//!
|
||||
//! [esp-hal]: https://github.com/esp-rs/esp-hal
|
||||
//! [embassy]: https://github.com/embassy-rs/embassy
|
||||
//!
|
||||
//! ## Executors
|
||||
//!
|
||||
//! Two types of executors are provided:
|
||||
//!
|
||||
//! - [Executor]: A thread-mode executor
|
||||
//! - [InterruptExecutor]: An interrupt-mode executor
|
||||
//!
|
||||
//! [InterruptExecutor] can be used to achieve preemptive multitasking in
|
||||
//! asynchronous applications, which is typically something reserved for more
|
||||
//! traditional RTOS. More information can be found in the [Embassy
|
||||
//! documentation].
|
||||
//!
|
||||
//! [embassy documentation]: https://embassy.dev/book/
|
||||
//!
|
||||
//! ## Initialization
|
||||
//!
|
||||
//! Embassy **must** be initialized by calling the [init] function. This
|
||||
//! initialization must be performed *prior* to spawning any tasks.
|
||||
//!
|
||||
//! ## Feature Flags
|
||||
#![doc = document_features::document_features!()]
|
||||
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
|
||||
#![deny(missing_docs)]
|
||||
#![cfg_attr(xtensa, feature(asm_experimental_arch))]
|
||||
#![no_std]
|
||||
|
||||
// MUST be the first module
|
||||
mod fmt;
|
||||
|
||||
#[cfg(not(feature = "esp32"))]
|
||||
use esp_hal::timer::systimer::Alarm;
|
||||
use esp_hal::timer::{timg::Timer as TimgTimer, AnyTimer};
|
||||
pub use macros::main;
|
||||
|
||||
#[cfg(feature = "executors")]
|
||||
pub use self::executor::{Executor, InterruptExecutor};
|
||||
use self::time_driver::{EmbassyTimer, Timer};
|
||||
|
||||
#[cfg(feature = "executors")]
|
||||
mod executor;
|
||||
mod time_driver;
|
||||
|
||||
macro_rules! mk_static {
|
||||
($t:ty,$val:expr) => {{
|
||||
static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new();
|
||||
#[deny(unused_attributes)]
|
||||
let x = STATIC_CELL.uninit().write(($val));
|
||||
x
|
||||
}};
|
||||
}
|
||||
|
||||
/// A trait to allow better UX for initializing the timers.
|
||||
///
|
||||
/// This trait is meant to be used only for the `init` function.
|
||||
/// Calling `timers()` multiple times may panic.
|
||||
pub trait TimerCollection {
|
||||
/// Returns the timers as a slice.
|
||||
fn timers(self) -> &'static mut [Timer];
|
||||
}
|
||||
|
||||
/// Helper trait to reduce boilerplate.
|
||||
///
|
||||
/// We can't blanket-implement for `Into<AnyTimer>` because of possible
|
||||
/// conflicting implementations.
|
||||
trait IntoAnyTimer: Into<AnyTimer> {}
|
||||
|
||||
impl IntoAnyTimer for AnyTimer {}
|
||||
|
||||
impl IntoAnyTimer for TimgTimer where Self: Into<AnyTimer> {}
|
||||
|
||||
#[cfg(not(feature = "esp32"))]
|
||||
impl IntoAnyTimer for Alarm where Self: Into<AnyTimer> {}
|
||||
|
||||
impl<T> TimerCollection for T
|
||||
where
|
||||
T: IntoAnyTimer,
|
||||
{
|
||||
fn timers(self) -> &'static mut [Timer] {
|
||||
Timer::new(self.into()).timers()
|
||||
}
|
||||
}
|
||||
|
||||
impl TimerCollection for Timer {
|
||||
fn timers(self) -> &'static mut [Timer] {
|
||||
let timers = mk_static!([Timer; 1], [self]);
|
||||
timers.timers()
|
||||
}
|
||||
}
|
||||
|
||||
impl TimerCollection for &'static mut [Timer] {
|
||||
fn timers(self) -> &'static mut [Timer] {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> TimerCollection for &'static mut [Timer; N] {
|
||||
fn timers(self) -> &'static mut [Timer] {
|
||||
self.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_array {
|
||||
($n:literal) => {
|
||||
impl<T> TimerCollection for [T; $n]
|
||||
where
|
||||
T: IntoAnyTimer,
|
||||
{
|
||||
fn timers(self) -> &'static mut [Timer] {
|
||||
mk_static!([Timer; $n], self.map(|t| Timer::new(t.into())))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_array!(2);
|
||||
impl_array!(3);
|
||||
impl_array!(4);
|
||||
|
||||
/// Initialize embassy.
|
||||
///
|
||||
/// Call this as soon as possible, before the first timer-related operation.
|
||||
///
|
||||
/// The time driver can be one of a number of different options:
|
||||
///
|
||||
/// - A timg `Timer` instance
|
||||
/// - A systimer `Alarm` instance
|
||||
/// - An `AnyTimer` instance
|
||||
/// - A `OneShotTimer` instance
|
||||
/// - A mutable static slice of `OneShotTimer` instances
|
||||
/// - A mutable static array of `OneShotTimer` instances
|
||||
/// - A 2, 3, 4 element array of `AnyTimer` instances
|
||||
///
|
||||
/// Note that if you use the `integrated-timers` feature,
|
||||
/// you need to pass as many timers as you start executors.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust, no_run
|
||||
#[doc = esp_hal::before_snippet!()]
|
||||
/// use esp_hal::timer::timg::TimerGroup;
|
||||
///
|
||||
/// let timg0 = TimerGroup::new(peripherals.TIMG0);
|
||||
/// esp_hal_embassy::init(timg0.timer0);
|
||||
///
|
||||
/// // ... now you can spawn embassy tasks or use `Timer::after` etc.
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn init(time_driver: impl TimerCollection) {
|
||||
EmbassyTimer::init(time_driver.timers())
|
||||
}
|
||||
253
esp-hal-embassy/src/time_driver.rs
Normal file
253
esp-hal-embassy/src/time_driver.rs
Normal file
@ -0,0 +1,253 @@
|
||||
use core::cell::Cell;
|
||||
|
||||
use embassy_time_driver::{AlarmHandle, Driver};
|
||||
use esp_hal::{
|
||||
interrupt::{InterruptHandler, Priority},
|
||||
sync::Locked,
|
||||
time::{now, ExtU64},
|
||||
timer::OneShotTimer,
|
||||
Blocking,
|
||||
};
|
||||
|
||||
pub type Timer = OneShotTimer<'static, Blocking>;
|
||||
|
||||
enum AlarmState {
|
||||
Created(extern "C" fn()),
|
||||
Allocated(extern "C" fn()),
|
||||
Initialized(&'static mut Timer),
|
||||
}
|
||||
impl AlarmState {
|
||||
fn initialize(timer: &'static mut Timer, interrupt_handler: extern "C" fn()) -> AlarmState {
|
||||
// If the driver is initialized, bind the interrupt handler to the
|
||||
// timer. This ensures that alarms allocated after init are correctly
|
||||
// bound to the core that created the executor.
|
||||
timer.set_interrupt_handler(InterruptHandler::new(interrupt_handler, Priority::max()));
|
||||
timer.enable_interrupt(true);
|
||||
AlarmState::Initialized(timer)
|
||||
}
|
||||
}
|
||||
|
||||
struct AlarmInner {
|
||||
pub callback: Cell<(*const (), *mut ())>,
|
||||
pub state: AlarmState,
|
||||
}
|
||||
|
||||
struct Alarm {
|
||||
pub inner: Locked<AlarmInner>,
|
||||
}
|
||||
|
||||
unsafe impl Send for Alarm {}
|
||||
|
||||
impl Alarm {
|
||||
pub const fn new(handler: extern "C" fn()) -> Self {
|
||||
Self {
|
||||
inner: Locked::new(AlarmInner {
|
||||
callback: Cell::new((core::ptr::null(), core::ptr::null_mut())),
|
||||
state: AlarmState::Created(handler),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct EmbassyTimer {
|
||||
alarms: [Alarm; MAX_SUPPORTED_ALARM_COUNT],
|
||||
available_timers: Locked<Option<&'static mut [Timer]>>,
|
||||
}
|
||||
|
||||
/// Repeats the `Alarm::new` constructor for each alarm, creating an interrupt
|
||||
/// handler for each of them.
|
||||
macro_rules! alarms {
|
||||
($($idx:literal),*) => {
|
||||
[$(
|
||||
Alarm::new({
|
||||
// Not #[handler] so we don't have to store the priority - which is constant.
|
||||
extern "C" fn handler() {
|
||||
DRIVER.on_interrupt($idx);
|
||||
}
|
||||
handler
|
||||
})
|
||||
),*]
|
||||
};
|
||||
}
|
||||
|
||||
const MAX_SUPPORTED_ALARM_COUNT: usize = 7;
|
||||
embassy_time_driver::time_driver_impl!(static DRIVER: EmbassyTimer = EmbassyTimer {
|
||||
alarms: alarms!(0, 1, 2, 3, 4, 5, 6),
|
||||
available_timers: Locked::new(None),
|
||||
});
|
||||
|
||||
impl EmbassyTimer {
|
||||
pub(super) fn init(mut timers: &'static mut [Timer]) {
|
||||
assert!(
|
||||
timers.len() <= MAX_SUPPORTED_ALARM_COUNT,
|
||||
"Maximum {} timers can be used.",
|
||||
MAX_SUPPORTED_ALARM_COUNT
|
||||
);
|
||||
|
||||
// Reset timers
|
||||
timers.iter_mut().for_each(|timer| {
|
||||
timer.enable_interrupt(false);
|
||||
timer.stop();
|
||||
});
|
||||
|
||||
// Initialize already allocated timers
|
||||
for alarm in DRIVER.alarms.iter() {
|
||||
timers = alarm.inner.with(move |alarm| {
|
||||
if let AlarmState::Allocated(interrupt_handler) = alarm.state {
|
||||
// Pluck off a timer
|
||||
|
||||
let Some((timer, remaining_timers)) = timers.split_first_mut() else {
|
||||
not_enough_timers();
|
||||
};
|
||||
|
||||
alarm.state = AlarmState::initialize(timer, interrupt_handler);
|
||||
|
||||
remaining_timers
|
||||
} else {
|
||||
timers
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Store the available timers
|
||||
DRIVER
|
||||
.available_timers
|
||||
.with(|available_timers| *available_timers = Some(timers));
|
||||
}
|
||||
|
||||
fn on_interrupt(&self, id: usize) {
|
||||
let (cb, ctx) = self.alarms[id].inner.with(|alarm| {
|
||||
if let AlarmState::Initialized(timer) = &mut alarm.state {
|
||||
timer.clear_interrupt();
|
||||
alarm.callback.get()
|
||||
} else {
|
||||
unsafe {
|
||||
// SAFETY: `on_interrupt` is registered right when the alarm is initialized.
|
||||
core::hint::unreachable_unchecked()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let cb: fn(*mut ()) = unsafe {
|
||||
// Safety:
|
||||
// - we can ignore the possibility of `f` being unset (null) because of the
|
||||
// safety contract of `allocate_alarm`.
|
||||
// - other than that we only store valid function pointers into alarm.callback
|
||||
core::mem::transmute(cb)
|
||||
};
|
||||
|
||||
cb(ctx);
|
||||
}
|
||||
|
||||
/// Returns `true` if the timer was armed, `false` if the timestamp is in
|
||||
/// the past.
|
||||
fn arm(timer: &mut Timer, timestamp: u64) -> bool {
|
||||
let now = now().duration_since_epoch();
|
||||
let ts = timestamp.micros();
|
||||
|
||||
if ts > now {
|
||||
let timeout = ts - now;
|
||||
unwrap!(timer.schedule(timeout));
|
||||
true
|
||||
} else {
|
||||
// If the timestamp is past, we return `false` to ask embassy to poll again
|
||||
// immediately.
|
||||
timer.stop();
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Driver for EmbassyTimer {
|
||||
fn now(&self) -> u64 {
|
||||
now().ticks()
|
||||
}
|
||||
|
||||
unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
|
||||
for (i, alarm) in self.alarms.iter().enumerate() {
|
||||
let handle = alarm.inner.with(|alarm| {
|
||||
let AlarmState::Created(interrupt_handler) = alarm.state else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let timer = self.available_timers.with(|available_timers| {
|
||||
// `allocate_alarm` may be called before `esp_hal_embassy::init()`. If
|
||||
// `timers` is `None`, we return `None` to signal that the alarm cannot be
|
||||
// initialized yet. These alarms will be initialized when `init` is called.
|
||||
if let Some(timers) = available_timers.take() {
|
||||
// If the driver is initialized, we can allocate a timer.
|
||||
// If this fails, we can't do anything about it.
|
||||
let Some((timer, rest)) = timers.split_first_mut() else {
|
||||
not_enough_timers();
|
||||
};
|
||||
*available_timers = Some(rest);
|
||||
Some(timer)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
alarm.state = match timer {
|
||||
Some(timer) => AlarmState::initialize(timer, interrupt_handler),
|
||||
|
||||
None => {
|
||||
// No timers are available yet, mark the alarm as allocated.
|
||||
AlarmState::Allocated(interrupt_handler)
|
||||
}
|
||||
};
|
||||
|
||||
Some(AlarmHandle::new(i as u8))
|
||||
});
|
||||
|
||||
if handle.is_some() {
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
|
||||
let n = alarm.id() as usize;
|
||||
|
||||
self.alarms[n].inner.with(|alarm| {
|
||||
alarm.callback.set((callback as *const (), ctx));
|
||||
})
|
||||
}
|
||||
|
||||
fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
|
||||
let alarm = &self.alarms[alarm.id() as usize];
|
||||
|
||||
// If `embassy-executor/integrated-timers` is enabled and there are no pending
|
||||
// timers, embassy still calls `set_alarm` with `u64::MAX`. By returning
|
||||
// `true` we signal that no re-polling is necessary.
|
||||
if timestamp == u64::MAX {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The hardware fires the alarm even if timestamp is lower than the current
|
||||
// time. In this case the interrupt handler will pend a wake-up when we exit the
|
||||
// critical section.
|
||||
//
|
||||
// This is correct behavior. See https://docs.rs/embassy-time-driver/0.1.0/embassy_time_driver/trait.Driver.html#tymethod.set_alarm
|
||||
// (... the driver should return true and arrange to call the alarm callback as
|
||||
// soon as possible, but not synchronously.)
|
||||
|
||||
alarm.inner.with(|alarm| {
|
||||
if let AlarmState::Initialized(timer) = &mut alarm.state {
|
||||
Self::arm(timer, timestamp)
|
||||
} else {
|
||||
panic!("set_alarm called before esp_hal_embassy::init()")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[track_caller]
|
||||
fn not_enough_timers() -> ! {
|
||||
// This is wrapped in a separate function because rustfmt does not like
|
||||
// extremely long strings. Also, if log is used, this avoids storing the string
|
||||
// twice.
|
||||
panic!("There are not enough timers to allocate a new alarm. Call esp_hal_embassy::init() with the correct number of timers, or consider using one of the embassy-timer/generic-queue-X features.");
|
||||
}
|
||||
60
esp-hal-procmacros/CHANGELOG.md
Normal file
60
esp-hal-procmacros/CHANGELOG.md
Normal file
@ -0,0 +1,60 @@
|
||||
# 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.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Added the `BuilderLite` derive macro which implements the Builder Lite pattern for a struct (#2614)
|
||||
|
||||
### Fixed
|
||||
|
||||
### Changed
|
||||
|
||||
- Functions marked with `#[handler]` can now be referenced in `const` context. (#2559)
|
||||
|
||||
### Removed
|
||||
|
||||
- Removed the `enum-dispatch`, `interrupt`, and `ram` features (#2594)
|
||||
|
||||
## [0.15.0] - 2024-11-20
|
||||
|
||||
### Changed
|
||||
|
||||
- Remove `get_` prefix from functions (#2528)
|
||||
|
||||
## [0.14.0] - 2024-10-10
|
||||
|
||||
## [0.13.0] - 2024-08-29
|
||||
|
||||
## [0.12.0] - 2024-07-15
|
||||
|
||||
## [0.11.0] - 2024-06-04
|
||||
|
||||
## [0.10.0] - 2024-04-18
|
||||
|
||||
## [0.9.0] - 2024-03-18
|
||||
|
||||
## [0.8.0] - 2023-12-12
|
||||
|
||||
## [0.7.0] - 2023-10-31
|
||||
|
||||
## [0.6.1] - 2023-09-05
|
||||
|
||||
## [0.6.0] - 2023-07-04
|
||||
|
||||
## [0.5.0] - 2023-03-27
|
||||
|
||||
## [0.4.0] - 2023-02-21
|
||||
|
||||
## [0.2.0] - 2023-01-26
|
||||
|
||||
## [0.1.0] - 2022-08-25
|
||||
|
||||
- Initial release
|
||||
|
||||
[Unreleased]: https://github.com/esp-rs/esp-hal/commits/main/esp-hal-procmacros?since=2024-11-20
|
||||
@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "esp-hal-procmacros"
|
||||
version = "0.9.0"
|
||||
version = "0.15.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.76.0"
|
||||
rust-version = "1.83.0"
|
||||
description = "Procedural macros for esp-hal"
|
||||
repository = "https://github.com/esp-rs/esp-hal"
|
||||
license = "MIT OR Apache-2.0"
|
||||
@ -14,34 +14,28 @@ features = ["embassy", "has-ulp-core", "interrupt", "ram", "is-ulp-core"]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
darling = "0.20.8"
|
||||
document-features = "0.2.8"
|
||||
darling = "0.20.10"
|
||||
document-features = "0.2.10"
|
||||
litrs = "0.4.1"
|
||||
object = { version = "0.33.0", optional = true }
|
||||
proc-macro-crate = "3.1.0"
|
||||
proc-macro-error = "1.0.4"
|
||||
proc-macro2 = "1.0.78"
|
||||
quote = "1.0.35"
|
||||
syn = { version = "2.0.52", features = ["extra-traits", "full"] }
|
||||
object = { version = "0.36.5", default-features = false, features = ["read_core", "elf"], optional = true }
|
||||
proc-macro-crate = "3.2.0"
|
||||
proc-macro-error2 = "2.0.1"
|
||||
proc-macro2 = "1.0.92"
|
||||
quote = "1.0.37"
|
||||
syn = { version = "2.0.89", features = ["extra-traits", "full"] }
|
||||
|
||||
[features]
|
||||
## Provide a `#[main]` procmacro to mark the entry point for Embassy applications.
|
||||
embassy = []
|
||||
## Provide enum dispatch helpers.
|
||||
enum-dispatch = []
|
||||
## Provide an `#[interrupt]` procmacro for defining interrupt service routines.
|
||||
interrupt = []
|
||||
## Provide a `#[ram]` procmacro to place functions in RAM instead of flash.
|
||||
ram = []
|
||||
## Indicates the target devices has RTC slow memory available.
|
||||
rtc_slow = []
|
||||
## Indicates the target device has RTC slow memory available.
|
||||
rtc-slow = []
|
||||
|
||||
#! ### Low-power Core Feature Flags
|
||||
## Indicate that the SoC contains an LP core.
|
||||
has-lp-core = ["dep:object"]
|
||||
## Indicate that the SoC contains a ULP core.
|
||||
has-ulp-core = ["dep:object"]
|
||||
## Provide an `#[entry]` macro for running applications on the ESP32-C6/P4's
|
||||
## Provide an `#[entry]` macro for running applications on the ESP32-C6's
|
||||
## LP core.
|
||||
is-lp-core = []
|
||||
## Provide an `#[entry]` macro for running applications on the ESP32-S2/S3's
|
||||
|
||||
@ -2,21 +2,21 @@
|
||||
|
||||
[](https://crates.io/crates/esp-hal-procmacros)
|
||||
[](https://docs.rs/esp-hal-procmacros)
|
||||

|
||||

|
||||
[](https://matrix.to/#/#esp-rs:matrix.org)
|
||||
|
||||
Procedural macros for use with the `esp-hal` family of HAL packages.
|
||||
|
||||
Provides macros for:
|
||||
|
||||
- Placing statics and functions into RAM
|
||||
- Marking interrupt handlers
|
||||
- Automatically creating an `embassy` executor instance and spawning the defined entry point
|
||||
Procedural macros for use with `esp-hal` and other related packages.
|
||||
|
||||
## [Documentation]
|
||||
|
||||
[documentation]: https://docs.rs/esp-hal-procmacros/
|
||||
|
||||
## Minimum Supported Rust Version (MSRV)
|
||||
|
||||
This crate is guaranteed to compile on stable Rust 1.76 and up. It _might_
|
||||
compile with older versions but that may change in any new patch release.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of:
|
||||
|
||||
@ -22,8 +22,7 @@ pub(crate) mod main {
|
||||
use std::{cell::RefCell, fmt::Display, thread};
|
||||
|
||||
use darling::{export::NestedMeta, FromMeta};
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use proc_macro_crate::{crate_name, FoundCrate};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::{ReturnType, Type};
|
||||
|
||||
@ -35,8 +34,7 @@ pub(crate) mod main {
|
||||
f: syn::ItemFn,
|
||||
main: TokenStream,
|
||||
) -> Result<TokenStream, TokenStream> {
|
||||
#[allow(unused_variables)]
|
||||
let args = Args::from_list(args).map_err(|e| e.write_errors())?;
|
||||
let _args = Args::from_list(args).map_err(|e| e.write_errors())?;
|
||||
|
||||
let fargs = f.sig.inputs.clone();
|
||||
|
||||
@ -48,13 +46,13 @@ pub(crate) mod main {
|
||||
if !f.sig.generics.params.is_empty() {
|
||||
ctxt.error_spanned_by(&f.sig, "main function must not be generic");
|
||||
}
|
||||
if !f.sig.generics.where_clause.is_none() {
|
||||
if f.sig.generics.where_clause.is_some() {
|
||||
ctxt.error_spanned_by(&f.sig, "main function must not have `where` clauses");
|
||||
}
|
||||
if !f.sig.abi.is_none() {
|
||||
if f.sig.abi.is_some() {
|
||||
ctxt.error_spanned_by(&f.sig, "main function must not have an ABI qualifier");
|
||||
}
|
||||
if !f.sig.variadic.is_none() {
|
||||
if f.sig.variadic.is_some() {
|
||||
ctxt.error_spanned_by(&f.sig, "main function must not be variadic");
|
||||
}
|
||||
match &f.sig.output {
|
||||
@ -79,11 +77,13 @@ pub(crate) mod main {
|
||||
let out = &f.sig.output;
|
||||
|
||||
let result = quote! {
|
||||
#[doc(hidden)]
|
||||
#[::embassy_executor::task()]
|
||||
async fn __embassy_main(#fargs) #out {
|
||||
#f_body
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
unsafe fn __make_static<T>(t: &mut T) -> &'static mut T {
|
||||
::core::mem::transmute(t)
|
||||
}
|
||||
@ -131,12 +131,6 @@ pub(crate) mod main {
|
||||
.push(syn::Error::new_spanned(obj.into_token_stream(), msg));
|
||||
}
|
||||
|
||||
/// Add one of Syn's parse errors.
|
||||
#[allow(unused)]
|
||||
pub fn syn_error(&self, err: syn::Error) {
|
||||
self.errors.borrow_mut().as_mut().unwrap().push(err);
|
||||
}
|
||||
|
||||
/// Consume this object, producing a formatted error string if there are
|
||||
/// errors.
|
||||
pub fn check(self) -> Result<(), TokenStream> {
|
||||
@ -162,23 +156,10 @@ pub(crate) mod main {
|
||||
}
|
||||
|
||||
pub fn main() -> TokenStream {
|
||||
let hal_crate = if cfg!(any(feature = "is-lp-core", feature = "is-ulp-core")) {
|
||||
crate_name("esp-lp-hal")
|
||||
} else {
|
||||
crate_name("esp-hal")
|
||||
};
|
||||
|
||||
let executor = if let Ok(FoundCrate::Name(ref name)) = hal_crate {
|
||||
let ident = Ident::new(&name, Span::call_site().into());
|
||||
quote!( #ident::embassy::executor::Executor )
|
||||
} else {
|
||||
quote!(crate::embassy::executor::Executor)
|
||||
};
|
||||
|
||||
quote! {
|
||||
#[entry]
|
||||
#[esp_hal::entry]
|
||||
fn main() -> ! {
|
||||
let mut executor = #executor::new();
|
||||
let mut executor = ::esp_hal_embassy::Executor::new();
|
||||
let executor = unsafe { __make_static(&mut executor) };
|
||||
executor.run(|spawner| {
|
||||
spawner.must_spawn(__embassy_main(spawner));
|
||||
|
||||
@ -1,80 +0,0 @@
|
||||
use proc_macro2::{Group, TokenStream, TokenTree};
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{
|
||||
parse::{Parse, ParseStream, Result},
|
||||
Ident,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct MakeGpioEnumDispatchMacro {
|
||||
pub name: String,
|
||||
pub filter: Vec<String>,
|
||||
pub elements: Vec<(String, usize)>,
|
||||
}
|
||||
|
||||
impl Parse for MakeGpioEnumDispatchMacro {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let name = input.parse::<Ident>()?.to_string();
|
||||
let filter = input
|
||||
.parse::<Group>()?
|
||||
.stream()
|
||||
.into_iter()
|
||||
.map(|v| match v {
|
||||
TokenTree::Group(_) => String::new(),
|
||||
TokenTree::Ident(ident) => ident.to_string(),
|
||||
TokenTree::Punct(_) => String::new(),
|
||||
TokenTree::Literal(_) => String::new(),
|
||||
})
|
||||
.filter(|p| !p.is_empty())
|
||||
.collect();
|
||||
|
||||
let mut elements = vec![];
|
||||
|
||||
let mut stream = input.parse::<Group>()?.stream().into_iter();
|
||||
let mut element_name = String::new();
|
||||
loop {
|
||||
match stream.next() {
|
||||
Some(v) => match v {
|
||||
TokenTree::Ident(ident) => {
|
||||
element_name = ident.to_string();
|
||||
}
|
||||
TokenTree::Literal(lit) => {
|
||||
let index = lit.to_string().parse().unwrap();
|
||||
elements.push((element_name.clone(), index));
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(MakeGpioEnumDispatchMacro {
|
||||
name,
|
||||
filter,
|
||||
elements,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn build_match_arms(input: MakeGpioEnumDispatchMacro) -> Vec<TokenStream> {
|
||||
let mut arms = Vec::new();
|
||||
for (gpio_type, num) in input.elements {
|
||||
let enum_name = format_ident!("ErasedPin");
|
||||
let variant_name = format_ident!("Gpio{}", num);
|
||||
|
||||
if input.filter.contains(&gpio_type) {
|
||||
arms.push({
|
||||
quote! { #enum_name::#variant_name($target) => $body }
|
||||
});
|
||||
} else {
|
||||
arms.push({
|
||||
quote! {
|
||||
#[allow(unused)]
|
||||
#enum_name::#variant_name($target) => { panic!("Unsupported") }
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
arms
|
||||
}
|
||||
@ -24,7 +24,7 @@ pub(crate) fn check_attr_whitelist(
|
||||
|
||||
'o: for attr in attrs {
|
||||
for val in whitelist {
|
||||
if eq(&attr, &val) {
|
||||
if eq(attr, val) {
|
||||
continue 'o;
|
||||
}
|
||||
}
|
||||
@ -35,27 +35,12 @@ pub(crate) fn check_attr_whitelist(
|
||||
}
|
||||
};
|
||||
|
||||
return Err(Error::new(attr.span(), &err_str).to_compile_error().into());
|
||||
return Err(Error::new(attr.span(), err_str).to_compile_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn extract_cfgs(attrs: Vec<Attribute>) -> (Vec<Attribute>, Vec<Attribute>) {
|
||||
let mut cfgs = vec![];
|
||||
let mut not_cfgs = vec![];
|
||||
|
||||
for attr in attrs {
|
||||
if eq(&attr, "cfg") {
|
||||
cfgs.push(attr);
|
||||
} else {
|
||||
not_cfgs.push(attr);
|
||||
}
|
||||
}
|
||||
|
||||
(cfgs, not_cfgs)
|
||||
}
|
||||
|
||||
/// Returns `true` if `attr.path` matches `name`
|
||||
fn eq(attr: &Attribute, name: &str) -> bool {
|
||||
attr.style == AttrStyle::Outer && attr.path().is_ident(name)
|
||||
|
||||
@ -16,105 +16,138 @@
|
||||
//! optimized memory usage and precise handling of hardware interrupts.
|
||||
//!
|
||||
//! Key Components:
|
||||
//! - [interrupt](attr.interrupt.html) - Attribute macro for marking interrupt
|
||||
//! handlers. Interrupt handlers are used to handle specific hardware
|
||||
//! interrupts generated by peripherals.<br> The macro allows users to
|
||||
//! specify the interrupt name explicitly or use the function name to match
|
||||
//! the interrupt.
|
||||
//! - [main](attr.main.html) - Creates a new `executor`` instance and declares
|
||||
//! - [`interrupt`](attr.interrupt.html) - Attribute macro for marking
|
||||
//! interrupt handlers. Interrupt handlers are used to handle specific
|
||||
//! hardware interrupts generated by peripherals.
|
||||
//!
|
||||
//! The macro allows users to specify the interrupt name explicitly or use
|
||||
//! the function name to match the interrupt.
|
||||
//! - [`main`](attr.main.html) - Creates a new `executor` instance and declares
|
||||
//! an application entry point spawning the corresponding function body as an
|
||||
//! async task.
|
||||
//! - [ram](attr.ram.html) - Attribute macro for placing statics and functions
|
||||
//! into specific memory sections, such as SRAM or RTC RAM (slow or fast)
|
||||
//! with different initialization options. Supported options are:
|
||||
//! - `rtc_fast` - Use RTC fast RAM
|
||||
//! - `rtc_slow` - Use RTC slow RAM (not all targets support slow RTC RAM)
|
||||
//! - `uninitialized` - Skip initialization of the memory
|
||||
//! - `zeroed` - Initialize the memory to zero
|
||||
//! - [`ram`](attr.ram.html) - Attribute macro for placing statics and
|
||||
//! functions into specific memory sections, such as SRAM or RTC RAM (slow or
|
||||
//! fast) with different initialization options. See its documentation for
|
||||
//! details.
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! #### `interrupt` macro
|
||||
//!
|
||||
//! Requires the `interrupt` feature to be enabled.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! #[interrupt]
|
||||
//! fn INTR_NAME() {
|
||||
//! // Interrupt handling code here
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! #### `main` macro
|
||||
//!
|
||||
//! Requires the `embassy` feature to be enabled.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! ```rust, no_run
|
||||
//! #[main]
|
||||
//! async fn main(spawner: Spawner) {
|
||||
//! // Your application's entry point
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! #### `ram` macro
|
||||
//!
|
||||
//! Requires the `ram` feature to be enabled.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! #[ram(rtc_fast)]
|
||||
//! static mut SOME_INITED_DATA: [u8; 2] = [0xaa, 0xbb];
|
||||
//!
|
||||
//! #[ram(rtc_fast, uninitialized)]
|
||||
//! static mut SOME_UNINITED_DATA: [u8; 2] = [0; 2];
|
||||
//!
|
||||
//! #[ram(rtc_fast, zeroed)]
|
||||
//! static mut SOME_ZEROED_DATA: [u8; 8] = [0; 8];
|
||||
//! ```
|
||||
//!
|
||||
//! ## Feature Flags
|
||||
#![doc = document_features::document_features!()]
|
||||
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
|
||||
|
||||
#[allow(unused)]
|
||||
use proc_macro::TokenStream;
|
||||
use darling::{ast::NestedMeta, Error, FromMeta};
|
||||
use proc_macro::{Span, TokenStream};
|
||||
use proc_macro2::Ident;
|
||||
use proc_macro_crate::{crate_name, FoundCrate};
|
||||
use proc_macro_error2::abort;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{
|
||||
parse,
|
||||
parse::Error as ParseError,
|
||||
spanned::Spanned,
|
||||
Data,
|
||||
DataStruct,
|
||||
GenericArgument,
|
||||
Item,
|
||||
ItemFn,
|
||||
Path,
|
||||
PathArguments,
|
||||
PathSegment,
|
||||
ReturnType,
|
||||
Type,
|
||||
};
|
||||
|
||||
use self::interrupt::{check_attr_whitelist, WhiteListCaller};
|
||||
|
||||
#[cfg(feature = "embassy")]
|
||||
mod embassy;
|
||||
#[cfg(feature = "enum-dispatch")]
|
||||
mod enum_dispatch;
|
||||
#[cfg(feature = "interrupt")]
|
||||
mod interrupt;
|
||||
#[cfg(any(feature = "is-lp-core", feature = "is-ulp-core"))]
|
||||
#[cfg(any(
|
||||
feature = "is-lp-core",
|
||||
feature = "is-ulp-core",
|
||||
feature = "has-lp-core",
|
||||
feature = "has-ulp-core"
|
||||
))]
|
||||
mod lp_core;
|
||||
|
||||
#[cfg(feature = "ram")]
|
||||
#[derive(Debug, Default, darling::FromMeta)]
|
||||
#[darling(default)]
|
||||
struct RamArgs {
|
||||
rtc_fast: bool,
|
||||
rtc_slow: bool,
|
||||
uninitialized: bool,
|
||||
persistent: bool,
|
||||
zeroed: bool,
|
||||
}
|
||||
|
||||
/// This attribute allows placing statics and functions into ram.
|
||||
/// Sets which segment of RAM to use for a function or static and how it should
|
||||
/// be initialized.
|
||||
///
|
||||
/// Options that can be specified are rtc_slow or rtc_fast to use the
|
||||
/// RTC slow or RTC fast ram instead of the normal SRAM.
|
||||
/// Requires the `ram` feature.
|
||||
///
|
||||
/// The uninitialized option will skip initialization of the memory
|
||||
/// (e.g. to persist it across resets or deep sleep mode for the RTC RAM)
|
||||
/// # Options
|
||||
///
|
||||
/// Not all targets support RTC slow ram.
|
||||
#[cfg(feature = "ram")]
|
||||
/// - `rtc_fast`: Use RTC fast RAM.
|
||||
/// - `rtc_slow`: Use RTC slow RAM. **Note**: not available on all targets.
|
||||
/// - `persistent`: Persist the contents of the `static` across resets. See [the
|
||||
/// section below](#persistent) for details.
|
||||
/// - `zeroed`: Initialize the memory of the `static` to zero. The initializer
|
||||
/// expression will be discarded. Types used must implement
|
||||
/// [`bytemuck::Zeroable`].
|
||||
///
|
||||
/// Using both `rtc_fast` and `rtc_slow` or `persistent` and `zeroed` together
|
||||
/// is an error.
|
||||
///
|
||||
/// ## `persistent`
|
||||
///
|
||||
/// Initialize the memory to zero after the initial boot. Thereafter,
|
||||
/// initialization is skipped to allow communication across `software_reset()`,
|
||||
/// deep sleep, watchdog timeouts, etc.
|
||||
///
|
||||
/// Types used must implement [`bytemuck::AnyBitPattern`].
|
||||
///
|
||||
/// ### Warnings
|
||||
///
|
||||
/// - A system-level or lesser reset occurring before the ram has been zeroed
|
||||
/// *could* skip initialization and start the application with the static
|
||||
/// filled with random bytes.
|
||||
/// - There is no way to keep some kinds of resets from happening while updating
|
||||
/// a persistent static—not even a critical section.
|
||||
///
|
||||
/// If these are issues for your application, consider adding a checksum
|
||||
/// alongside the data.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust, no_run
|
||||
/// #[ram(rtc_fast)]
|
||||
/// static mut SOME_INITED_DATA: [u8; 2] = [0xaa, 0xbb];
|
||||
///
|
||||
/// #[ram(rtc_fast, persistent)]
|
||||
/// static mut SOME_PERSISTENT_DATA: [u8; 2] = [0; 2];
|
||||
///
|
||||
/// #[ram(rtc_fast, zeroed)]
|
||||
/// static mut SOME_ZEROED_DATA: [u8; 8] = [0; 8];
|
||||
/// ```
|
||||
///
|
||||
/// See the `ram` example in the esp-hal repository for a full usage example.
|
||||
///
|
||||
/// [`bytemuck::AnyBitPattern`]: https://docs.rs/bytemuck/1.9.0/bytemuck/trait.AnyBitPattern.html
|
||||
/// [`bytemuck::Zeroable`]: https://docs.rs/bytemuck/1.9.0/bytemuck/trait.Zeroable.html
|
||||
#[proc_macro_attribute]
|
||||
#[proc_macro_error::proc_macro_error]
|
||||
#[proc_macro_error2::proc_macro_error]
|
||||
pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
use darling::{ast::NestedMeta, Error, FromMeta};
|
||||
use proc_macro::Span;
|
||||
use proc_macro_error::abort;
|
||||
use syn::{parse, Item};
|
||||
|
||||
let attr_args = match NestedMeta::parse_meta_list(args.into()) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
@ -125,7 +158,7 @@ pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let RamArgs {
|
||||
rtc_fast,
|
||||
rtc_slow,
|
||||
uninitialized,
|
||||
persistent,
|
||||
zeroed,
|
||||
} = match FromMeta::from_list(&attr_args) {
|
||||
Ok(v) => v,
|
||||
@ -136,7 +169,7 @@ pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
|
||||
let item: Item = parse(input).expect("failed to parse input");
|
||||
|
||||
#[cfg(not(feature = "rtc_slow"))]
|
||||
#[cfg(not(feature = "rtc-slow"))]
|
||||
if rtc_slow {
|
||||
abort!(
|
||||
Span::call_site(),
|
||||
@ -145,7 +178,7 @@ pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
}
|
||||
|
||||
let is_fn = matches!(item, Item::Fn(_));
|
||||
let section_name = match (is_fn, rtc_fast, rtc_slow, uninitialized, zeroed) {
|
||||
let section_name = match (is_fn, rtc_fast, rtc_slow, persistent, zeroed) {
|
||||
(true, false, false, false, false) => Ok(".rwtext"),
|
||||
(true, true, false, false, false) => Ok(".rtc_fast.text"),
|
||||
(true, false, true, false, false) => Ok(".rtc_slow.text"),
|
||||
@ -153,11 +186,11 @@ pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
(false, false, false, false, false) => Ok(".data"),
|
||||
|
||||
(false, true, false, false, false) => Ok(".rtc_fast.data"),
|
||||
(false, true, false, true, false) => Ok(".rtc_fast.noinit"),
|
||||
(false, true, false, true, false) => Ok(".rtc_fast.persistent"),
|
||||
(false, true, false, false, true) => Ok(".rtc_fast.bss"),
|
||||
|
||||
(false, false, true, false, false) => Ok(".rtc_slow.data"),
|
||||
(false, false, true, true, false) => Ok(".rtc_slow.noinit"),
|
||||
(false, false, true, true, false) => Ok(".rtc_slow.persistent"),
|
||||
(false, false, true, false, true) => Ok(".rtc_slow.bss"),
|
||||
|
||||
_ => Err(()),
|
||||
@ -176,94 +209,89 @@ pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
}
|
||||
};
|
||||
|
||||
let trait_check = if zeroed {
|
||||
Some("zeroable")
|
||||
} else if persistent {
|
||||
Some("persistable")
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let trait_check = trait_check.map(|name| {
|
||||
use proc_macro_crate::{crate_name, FoundCrate};
|
||||
|
||||
let hal = Ident::new(
|
||||
if let Ok(FoundCrate::Name(ref name)) = crate_name("esp-hal") {
|
||||
name
|
||||
} else {
|
||||
"crate"
|
||||
},
|
||||
Span::call_site().into(),
|
||||
);
|
||||
|
||||
let assertion = quote::format_ident!("assert_is_{name}");
|
||||
let Item::Static(ref item) = item else {
|
||||
abort!(item, "Expected a `static`");
|
||||
};
|
||||
let ty = &item.ty;
|
||||
quote::quote! {
|
||||
const _: () = #hal::__macro_implementation::#assertion::<#ty>();
|
||||
}
|
||||
});
|
||||
|
||||
let output = quote::quote! {
|
||||
#section
|
||||
#item
|
||||
#trait_check
|
||||
};
|
||||
|
||||
output.into()
|
||||
}
|
||||
|
||||
/// Marks a function as an interrupt handler
|
||||
/// Mark a function as an interrupt handler.
|
||||
///
|
||||
/// Used to handle on of the [interrupts](enum.Interrupt.html).
|
||||
/// Optionally a priority can be specified, e.g. `#[handler(priority =
|
||||
/// esp_hal::interrupt::Priority::Priority2)]`.
|
||||
///
|
||||
/// When specified between braces (`#[interrupt(example)]`) that interrupt will
|
||||
/// be used and the function can have an arbitrary name. Otherwise the name of
|
||||
/// the function must be the name of the interrupt.
|
||||
///
|
||||
/// Example usage:
|
||||
///
|
||||
/// ```rust
|
||||
/// #[interrupt]
|
||||
/// fn GPIO() {
|
||||
/// // code
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The interrupt context can also be supplied by adding a argument to the
|
||||
/// interrupt function for example, on Xtensa based chips:
|
||||
///
|
||||
/// ```rust
|
||||
/// fn GPIO(context: &mut xtensa_lx_rt::exception::Context) {
|
||||
/// // code
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(feature = "interrupt")]
|
||||
/// If no priority is given, `Priority::min()` is assumed
|
||||
#[proc_macro_attribute]
|
||||
pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
use std::iter;
|
||||
#[proc_macro_error2::proc_macro_error]
|
||||
pub fn handler(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
#[derive(Debug, FromMeta)]
|
||||
struct MacroArgs {
|
||||
priority: Option<syn::Expr>,
|
||||
}
|
||||
|
||||
use darling::{ast::NestedMeta, Error};
|
||||
use proc_macro::Span;
|
||||
use proc_macro2::Ident;
|
||||
use proc_macro_crate::{crate_name, FoundCrate};
|
||||
use proc_macro_error::abort;
|
||||
use quote::quote;
|
||||
use syn::{
|
||||
parse::Error as ParseError,
|
||||
spanned::Spanned,
|
||||
ItemFn,
|
||||
Meta,
|
||||
ReturnType,
|
||||
Type,
|
||||
Visibility,
|
||||
};
|
||||
|
||||
use self::interrupt::{check_attr_whitelist, extract_cfgs, WhiteListCaller};
|
||||
|
||||
let mut f: ItemFn = syn::parse(input).expect("`#[interrupt]` must be applied to a function");
|
||||
let mut f: ItemFn = syn::parse(input).expect("`#[handler]` must be applied to a function");
|
||||
let original_span = f.span();
|
||||
|
||||
let attr_args = match NestedMeta::parse_meta_list(args.into()) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
return TokenStream::from(Error::from(e).write_errors());
|
||||
return TokenStream::from(darling::Error::from(e).write_errors());
|
||||
}
|
||||
};
|
||||
|
||||
if attr_args.len() > 1 {
|
||||
abort!(
|
||||
Span::call_site(),
|
||||
"This attribute accepts zero or 1 arguments"
|
||||
)
|
||||
let args = match MacroArgs::from_list(&attr_args) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
return TokenStream::from(e.write_errors());
|
||||
}
|
||||
};
|
||||
|
||||
let ident = f.sig.ident.clone();
|
||||
let mut ident_s = &ident.clone();
|
||||
|
||||
if attr_args.len() == 1 {
|
||||
if let NestedMeta::Meta(Meta::Path(x)) = &attr_args[0] {
|
||||
ident_s = x.get_ident().unwrap();
|
||||
let root = Ident::new(
|
||||
if let Ok(FoundCrate::Name(ref name)) = crate_name("esp-hal") {
|
||||
name
|
||||
} else {
|
||||
abort!(
|
||||
Span::call_site(),
|
||||
format!(
|
||||
"This attribute accepts a string attribute {:?}",
|
||||
attr_args[0]
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
"crate"
|
||||
},
|
||||
Span::call_site().into(),
|
||||
);
|
||||
|
||||
let priority = if let Some(priority) = args.priority {
|
||||
quote::quote!( #priority )
|
||||
} else {
|
||||
quote::quote! { #root::interrupt::Priority::min() }
|
||||
};
|
||||
|
||||
// XXX should we blacklist other attributes?
|
||||
|
||||
@ -272,7 +300,6 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
}
|
||||
|
||||
let valid_signature = f.sig.constness.is_none()
|
||||
&& f.vis == Visibility::Inherited
|
||||
&& f.sig.abi.is_none()
|
||||
&& f.sig.generics.params.is_empty()
|
||||
&& f.sig.generics.where_clause.is_none()
|
||||
@ -290,393 +317,49 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
if !valid_signature {
|
||||
return ParseError::new(
|
||||
f.span(),
|
||||
"`#[interrupt]` handlers must have signature `[unsafe] fn([&mut Context]) [-> !]`",
|
||||
"`#[handler]` handlers must have signature `[unsafe] fn([&mut Context]) [-> !]`",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
f.sig.abi = syn::parse_quote_spanned!(original_span => extern "C");
|
||||
let orig = f.sig.ident;
|
||||
let vis = f.vis.clone();
|
||||
f.sig.ident = Ident::new(
|
||||
&format!("__esp_hal_internal_{}", f.sig.ident),
|
||||
&format!("__esp_hal_internal_{}", orig),
|
||||
proc_macro2::Span::call_site(),
|
||||
);
|
||||
let new = f.sig.ident.clone();
|
||||
|
||||
let hal_crate = if cfg!(any(feature = "is-lp-core", feature = "is-ulp-core")) {
|
||||
crate_name("esp-lp-hal")
|
||||
} else {
|
||||
crate_name("esp-hal")
|
||||
};
|
||||
|
||||
let interrupt_in_hal_crate = if let Ok(FoundCrate::Name(ref name)) = hal_crate {
|
||||
let ident = Ident::new(&name, Span::call_site().into());
|
||||
quote!( #ident::peripherals::Interrupt::#ident_s )
|
||||
} else {
|
||||
quote!( crate::peripherals::Interrupt::#ident_s )
|
||||
};
|
||||
|
||||
f.block.stmts.extend(iter::once(
|
||||
syn::parse2(quote! {{
|
||||
// Check that this interrupt actually exists
|
||||
#interrupt_in_hal_crate;
|
||||
}})
|
||||
.unwrap(),
|
||||
));
|
||||
|
||||
let tramp_ident = Ident::new(
|
||||
&format!("{}_trampoline", f.sig.ident),
|
||||
proc_macro2::Span::call_site(),
|
||||
);
|
||||
let ident = &f.sig.ident;
|
||||
|
||||
let (ref cfgs, ref attrs) = extract_cfgs(f.attrs.clone());
|
||||
|
||||
let export_name = ident_s.to_string();
|
||||
|
||||
let trap_frame_in_hal_crate = if let Ok(FoundCrate::Name(ref name)) = hal_crate {
|
||||
let ident = Ident::new(&name, Span::call_site().into());
|
||||
quote!( #ident::trapframe::TrapFrame )
|
||||
} else {
|
||||
quote!(crate::trapframe::TrapFrame)
|
||||
};
|
||||
|
||||
let context_call =
|
||||
(f.sig.inputs.len() == 1).then(|| Ident::new("context", proc_macro2::Span::call_site()));
|
||||
|
||||
quote!(
|
||||
#(#cfgs)*
|
||||
#(#attrs)*
|
||||
#[doc(hidden)]
|
||||
#[export_name = #export_name]
|
||||
pub unsafe extern "C" fn #tramp_ident(context: &mut #trap_frame_in_hal_crate) {
|
||||
#ident(
|
||||
#context_call
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
quote::quote_spanned!(original_span =>
|
||||
#f
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
#vis const #orig: #root::interrupt::InterruptHandler = #root::interrupt::InterruptHandler::new(#new, #priority);
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Create an enum for erased GPIO pins, using the enum-dispatch pattern
|
||||
///
|
||||
/// Only used internally
|
||||
#[cfg(feature = "enum-dispatch")]
|
||||
#[proc_macro]
|
||||
pub fn make_gpio_enum_dispatch_macro(input: TokenStream) -> TokenStream {
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
use self::enum_dispatch::{build_match_arms, MakeGpioEnumDispatchMacro};
|
||||
|
||||
let input = syn::parse_macro_input!(input as MakeGpioEnumDispatchMacro);
|
||||
|
||||
let macro_name = format_ident!("{}", input.name);
|
||||
let arms = build_match_arms(input);
|
||||
|
||||
quote! {
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! #macro_name {
|
||||
($m:ident, $target:ident, $body:block) => {
|
||||
match $m {
|
||||
#(#arms)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) use #macro_name;
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Load code to be run on the LP/ULP core.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```no_run
|
||||
/// ```rust, no_run
|
||||
/// let lp_core_code = load_lp_code!("path.elf");
|
||||
/// lp_core_code.run(&mut lp_core, lp_core::LpCoreWakeupSource::HpCpu, lp_pin);
|
||||
/// ````
|
||||
#[cfg(any(feature = "has-lp-core", feature = "has-ulp-core"))]
|
||||
#[proc_macro]
|
||||
pub fn load_lp_code(input: TokenStream) -> TokenStream {
|
||||
use std::{fs, path::Path};
|
||||
|
||||
use litrs::StringLit;
|
||||
use object::{File, Object, ObjectSection, ObjectSymbol, Section, SectionKind};
|
||||
use parse::Error;
|
||||
use proc_macro::Span;
|
||||
use proc_macro_crate::{crate_name, FoundCrate};
|
||||
use quote::quote;
|
||||
use syn::{parse, Ident};
|
||||
|
||||
let hal_crate = if cfg!(any(feature = "is-lp-core", feature = "is-ulp-core")) {
|
||||
crate_name("esp-lp-hal")
|
||||
} else {
|
||||
crate_name("esp-hal")
|
||||
};
|
||||
|
||||
let hal_crate = if let Ok(FoundCrate::Name(ref name)) = hal_crate {
|
||||
let ident = Ident::new(&name, Span::call_site().into());
|
||||
quote!( #ident )
|
||||
} else {
|
||||
quote!(crate)
|
||||
};
|
||||
|
||||
let first_token = match input.into_iter().next() {
|
||||
Some(token) => token,
|
||||
None => {
|
||||
return Error::new(
|
||||
Span::call_site().into(),
|
||||
"You need to give the path to an ELF file",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
};
|
||||
let arg = match StringLit::try_from(&first_token) {
|
||||
Ok(arg) => arg,
|
||||
Err(_) => {
|
||||
return Error::new(
|
||||
Span::call_site().into(),
|
||||
"You need to give the path to an ELF file",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
};
|
||||
let elf_file = arg.value();
|
||||
|
||||
if !Path::new(elf_file).exists() {
|
||||
return Error::new(Span::call_site().into(), "File not found")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
let bin_data = fs::read(elf_file).unwrap();
|
||||
let obj_file = File::parse(&*bin_data).unwrap();
|
||||
let sections = obj_file.sections();
|
||||
|
||||
let mut sections: Vec<Section> = sections
|
||||
.into_iter()
|
||||
.filter(|section| match section.kind() {
|
||||
SectionKind::Text
|
||||
| SectionKind::ReadOnlyData
|
||||
| SectionKind::Data
|
||||
| SectionKind::UninitializedData => true,
|
||||
_ => false,
|
||||
})
|
||||
.collect();
|
||||
sections.sort_by(|a, b| a.address().partial_cmp(&b.address()).unwrap());
|
||||
|
||||
let mut binary: Vec<u8> = Vec::new();
|
||||
let mut last_address = if cfg!(feature = "has-lp-core") {
|
||||
0x5000_0000
|
||||
} else {
|
||||
0x0
|
||||
};
|
||||
|
||||
for section in sections {
|
||||
if section.address() > last_address {
|
||||
for _ in 0..(section.address() - last_address) {
|
||||
binary.push(0);
|
||||
}
|
||||
}
|
||||
|
||||
binary.extend_from_slice(section.data().unwrap());
|
||||
last_address = section.address() + section.size();
|
||||
}
|
||||
|
||||
let magic_symbol = obj_file
|
||||
.symbols()
|
||||
.find(|s| s.name().unwrap().starts_with("__ULP_MAGIC_"));
|
||||
|
||||
if let None = magic_symbol {
|
||||
return Error::new(
|
||||
Span::call_site().into(),
|
||||
"Given file doesn't seem to be an LP/ULP core application.",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
let magic_symbol = magic_symbol.unwrap().name().unwrap();
|
||||
|
||||
let magic_symbol = magic_symbol.trim_start_matches("__ULP_MAGIC_");
|
||||
let args: Vec<proc_macro2::TokenStream> = magic_symbol
|
||||
.split("$")
|
||||
.into_iter()
|
||||
.map(|t| {
|
||||
let t = t.replace("GpioPin", "LowPowerPin");
|
||||
t.parse().unwrap()
|
||||
})
|
||||
.filter(|v: &proc_macro2::TokenStream| !v.is_empty())
|
||||
.collect();
|
||||
|
||||
#[cfg(feature = "has-lp-core")]
|
||||
let imports = quote! {
|
||||
use #hal_crate::lp_core::LpCore;
|
||||
use #hal_crate::lp_core::LpCoreWakeupSource;
|
||||
use #hal_crate::gpio::lp_gpio::LowPowerPin;
|
||||
use #hal_crate::gpio::*;
|
||||
use #hal_crate::uart::lp_uart::LpUart;
|
||||
use #hal_crate::i2c::lp_i2c::LpI2c;
|
||||
};
|
||||
#[cfg(feature = "has-ulp-core")]
|
||||
let imports = quote! {
|
||||
use #hal_crate::ulp_core::UlpCore as LpCore;
|
||||
use #hal_crate::ulp_core::UlpCoreWakeupSource as LpCoreWakeupSource;
|
||||
use #hal_crate::gpio::*;
|
||||
};
|
||||
|
||||
#[cfg(feature = "has-lp-core")]
|
||||
let rtc_code_start = quote! { _rtc_fast_data_start };
|
||||
#[cfg(feature = "has-ulp-core")]
|
||||
let rtc_code_start = quote! { _rtc_slow_data_start };
|
||||
|
||||
quote! {
|
||||
{
|
||||
#imports
|
||||
|
||||
struct LpCoreCode {}
|
||||
|
||||
static LP_CODE: &[u8] = &[#(#binary),*];
|
||||
|
||||
extern "C" {
|
||||
static #rtc_code_start: u32;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
core::ptr::copy_nonoverlapping(LP_CODE as *const _ as *const u8, &#rtc_code_start as *const u32 as *mut u8, LP_CODE.len());
|
||||
}
|
||||
|
||||
impl LpCoreCode {
|
||||
pub fn run(
|
||||
&self,
|
||||
lp_core: &mut LpCore,
|
||||
wakeup_source: LpCoreWakeupSource,
|
||||
#(_: #args),*
|
||||
) {
|
||||
lp_core.run(wakeup_source);
|
||||
}
|
||||
}
|
||||
|
||||
LpCoreCode {}
|
||||
}
|
||||
}
|
||||
.into()
|
||||
lp_core::load_lp_code(input)
|
||||
}
|
||||
|
||||
/// Marks the entry function of a LP core / ULP program.
|
||||
#[cfg(any(feature = "is-lp-core", feature = "is-ulp-core"))]
|
||||
#[proc_macro_error::proc_macro_error]
|
||||
#[proc_macro_attribute]
|
||||
#[proc_macro_error2::proc_macro_error]
|
||||
pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
use proc_macro2::{Ident, Span};
|
||||
use proc_macro_crate::{crate_name, FoundCrate};
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{
|
||||
parse::{self, Error},
|
||||
parse_macro_input,
|
||||
spanned::Spanned,
|
||||
FnArg,
|
||||
ItemFn,
|
||||
};
|
||||
|
||||
use self::lp_core::{extract_pin, get_simplename, make_magic_symbol_name};
|
||||
|
||||
let found_crate = crate_name("esp-lp-hal").expect("esp-lp-hal is present in `Cargo.toml`");
|
||||
let hal_crate = match found_crate {
|
||||
FoundCrate::Itself => quote!(esp_lp_hal),
|
||||
FoundCrate::Name(name) => {
|
||||
let ident = Ident::new(&name, Span::call_site());
|
||||
quote!( #ident::Something )
|
||||
}
|
||||
};
|
||||
|
||||
if !args.is_empty() {
|
||||
return Error::new(Span::call_site(), "This attribute accepts no arguments")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
let f = parse_macro_input!(input as ItemFn);
|
||||
|
||||
let mut argument_types = Vec::new();
|
||||
let mut create_peripheral = Vec::new();
|
||||
|
||||
let mut used_pins: Vec<u8> = Vec::new();
|
||||
|
||||
for (num, arg) in f.sig.inputs.iter().enumerate() {
|
||||
let param_name = format_ident!("param{}", num);
|
||||
match arg {
|
||||
FnArg::Receiver(_) => {
|
||||
return Error::new(arg.span(), "invalid argument")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
FnArg::Typed(t) => {
|
||||
match get_simplename(&t.ty).as_str() {
|
||||
"GpioPin" => {
|
||||
let pin = extract_pin(&t.ty);
|
||||
if used_pins.contains(&pin) {
|
||||
return Error::new(arg.span(), "duplicate pin")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
used_pins.push(pin);
|
||||
create_peripheral.push(quote!(
|
||||
let mut #param_name = unsafe { the_hal::gpio::conjure().unwrap() };
|
||||
));
|
||||
}
|
||||
"LpUart" => {
|
||||
create_peripheral.push(quote!(
|
||||
let mut #param_name = unsafe { the_hal::uart::conjure() };
|
||||
));
|
||||
}
|
||||
"LpI2c" => {
|
||||
create_peripheral.push(quote!(
|
||||
let mut #param_name = unsafe { the_hal::i2c::conjure() };
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
return Error::new(arg.span(), "invalid argument to main")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
}
|
||||
argument_types.push(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let magic_symbol_name = make_magic_symbol_name(&argument_types);
|
||||
|
||||
let param_names: Vec<Ident> = argument_types
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(num, _)| format_ident!("param{}", num))
|
||||
.collect();
|
||||
|
||||
quote!(
|
||||
#[allow(non_snake_case)]
|
||||
#[export_name = "main"]
|
||||
pub fn __risc_v_rt__main() -> ! {
|
||||
#[export_name = #magic_symbol_name]
|
||||
static ULP_MAGIC: [u32; 0] = [0u32; 0];
|
||||
|
||||
unsafe { ULP_MAGIC.as_ptr().read_volatile(); }
|
||||
|
||||
use #hal_crate as the_hal;
|
||||
#(
|
||||
#create_peripheral;
|
||||
)*
|
||||
|
||||
main(#(#param_names),*);
|
||||
}
|
||||
|
||||
#f
|
||||
)
|
||||
.into()
|
||||
lp_core::entry(args, input)
|
||||
}
|
||||
|
||||
/// Creates a new `executor` instance and declares an application entry point
|
||||
@ -713,3 +396,129 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
|
||||
run(&args.meta, f, main()).unwrap_or_else(|x| x).into()
|
||||
}
|
||||
|
||||
/// Automatically implement the [Builder Lite] pattern for a struct.
|
||||
///
|
||||
/// This will create an `impl` which contains methods for each field of a
|
||||
/// struct, allowing users to easily set the values. The generated methods will
|
||||
/// be the field name prefixed with `with_`, and calls to these methods can be
|
||||
/// chained as needed.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust, no_run
|
||||
/// #[derive(Default)]
|
||||
/// enum MyEnum {
|
||||
/// #[default]
|
||||
/// A,
|
||||
/// B,
|
||||
/// }
|
||||
///
|
||||
/// #[derive(Default, BuilderLite)]
|
||||
/// #[non_exhaustive]
|
||||
/// struct MyStruct {
|
||||
/// enum_field: MyEnum,
|
||||
/// bool_field: bool,
|
||||
/// option_field: Option<i32>,
|
||||
/// }
|
||||
///
|
||||
/// MyStruct::default()
|
||||
/// .with_enum_field(MyEnum::B)
|
||||
/// .with_bool_field(true)
|
||||
/// .with_option_field(-5);
|
||||
/// ```
|
||||
///
|
||||
/// [Builder Lite]: https://matklad.github.io/2022/05/29/builder-lite.html
|
||||
#[proc_macro_derive(BuilderLite)]
|
||||
pub fn builder_lite_derive(item: TokenStream) -> TokenStream {
|
||||
let input = syn::parse_macro_input!(item as syn::DeriveInput);
|
||||
|
||||
let span = input.span();
|
||||
let ident = input.ident;
|
||||
|
||||
let mut fns = Vec::new();
|
||||
if let Data::Struct(DataStruct { fields, .. }) = &input.data {
|
||||
for field in fields {
|
||||
let field_ident = field.ident.as_ref().unwrap();
|
||||
let field_type = &field.ty;
|
||||
|
||||
let function_ident = format_ident!("with_{}", field_ident);
|
||||
|
||||
let maybe_path_type = extract_type_path(field_type)
|
||||
.and_then(|path| extract_option_segment(path))
|
||||
.and_then(|path_seg| match path_seg.arguments {
|
||||
PathArguments::AngleBracketed(ref params) => params.args.first(),
|
||||
_ => None,
|
||||
})
|
||||
.and_then(|generic_arg| match *generic_arg {
|
||||
GenericArgument::Type(ref ty) => Some(ty),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
let (field_type, field_assigns) = if let Some(inner_type) = maybe_path_type {
|
||||
(inner_type, quote! { Some(#field_ident) })
|
||||
} else {
|
||||
(field_type, quote! { #field_ident })
|
||||
};
|
||||
|
||||
fns.push(quote! {
|
||||
#[doc = concat!(" Assign the given value to the `", stringify!(#field_ident) ,"` field.")]
|
||||
#[must_use]
|
||||
pub fn #function_ident(mut self, #field_ident: #field_type) -> Self {
|
||||
self.#field_ident = #field_assigns;
|
||||
self
|
||||
}
|
||||
});
|
||||
|
||||
if maybe_path_type.is_some() {
|
||||
let function_ident = format_ident!("with_{}_none", field_ident);
|
||||
fns.push(quote! {
|
||||
#[doc = concat!(" Set the value of `", stringify!(#field_ident), "` to `None`.")]
|
||||
#[must_use]
|
||||
pub fn #function_ident(mut self) -> Self {
|
||||
self.#field_ident = None;
|
||||
self
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return ParseError::new(
|
||||
span,
|
||||
"#[derive(Builder)] is only defined for structs, not for enums or unions!",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
let implementation = quote! {
|
||||
#[automatically_derived]
|
||||
impl #ident {
|
||||
#(#fns)*
|
||||
}
|
||||
};
|
||||
|
||||
implementation.into()
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/56264023
|
||||
fn extract_type_path(ty: &Type) -> Option<&Path> {
|
||||
match *ty {
|
||||
Type::Path(ref typepath) if typepath.qself.is_none() => Some(&typepath.path),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/56264023
|
||||
fn extract_option_segment(path: &Path) -> Option<&PathSegment> {
|
||||
let idents_of_path = path.segments.iter().fold(String::new(), |mut acc, v| {
|
||||
acc.push_str(&v.ident.to_string());
|
||||
acc.push('|');
|
||||
acc
|
||||
});
|
||||
|
||||
vec!["Option|", "std|option|Option|", "core|option|Option|"]
|
||||
.into_iter()
|
||||
.find(|s| idents_of_path == *s)
|
||||
.and_then(|_| path.segments.last())
|
||||
}
|
||||
|
||||
@ -1,23 +1,41 @@
|
||||
#[allow(unused)]
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{GenericArgument, PatType, PathArguments, Type};
|
||||
|
||||
#[cfg(any(feature = "is-lp-core", feature = "is-ulp-core"))]
|
||||
pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
use proc_macro2::{Ident, Span};
|
||||
use proc_macro_crate::{crate_name, FoundCrate};
|
||||
use quote::format_ident;
|
||||
use syn::{
|
||||
parse::Error,
|
||||
parse_macro_input,
|
||||
spanned::Spanned,
|
||||
FnArg,
|
||||
GenericArgument,
|
||||
ItemFn,
|
||||
PatType,
|
||||
PathArguments,
|
||||
Type,
|
||||
};
|
||||
|
||||
pub(crate) fn make_magic_symbol_name(args: &Vec<&PatType>) -> String {
|
||||
let mut res = String::from("__ULP_MAGIC_");
|
||||
for &a in args {
|
||||
let t = &a.ty;
|
||||
let quoted = to_string(&t);
|
||||
let quoted = to_string(t);
|
||||
res.push_str("ed);
|
||||
res.push_str("$");
|
||||
res.push('$');
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub(crate) fn get_simplename(t: &Type) -> String {
|
||||
String::from(match t {
|
||||
Type::Path(p) => String::from(&p.path.segments.last().unwrap().ident.to_string()),
|
||||
pub(crate) fn simplename(t: &Type) -> String {
|
||||
match t {
|
||||
Type::Path(p) => p.path.segments.last().unwrap().ident.to_string(),
|
||||
_ => String::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn extract_pin(ty: &Type) -> u8 {
|
||||
@ -31,7 +49,7 @@ pub(crate) fn extract_pin(ty: &Type) -> u8 {
|
||||
res = extract_pin(t);
|
||||
}
|
||||
GenericArgument::Const(c) => {
|
||||
res = ("e! { #c }.to_string()).parse().unwrap();
|
||||
res = quote! { #c }.to_string().parse().unwrap();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
@ -50,22 +68,308 @@ fn to_string(ty: &Type) -> String {
|
||||
res.push_str(&segment.ident.to_string());
|
||||
|
||||
if let PathArguments::AngleBracketed(g) = &segment.arguments {
|
||||
res.push_str("<");
|
||||
res.push('<');
|
||||
let mut pushed = false;
|
||||
for arg in &g.args {
|
||||
if pushed {
|
||||
res.push(',');
|
||||
}
|
||||
|
||||
match arg {
|
||||
GenericArgument::Type(t) => {
|
||||
pushed = true;
|
||||
res.push_str(&to_string(t));
|
||||
}
|
||||
GenericArgument::Const(c) => {
|
||||
res.push_str(",");
|
||||
pushed = true;
|
||||
res.push_str("e! { #c }.to_string());
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
res.push_str(">");
|
||||
res.push('>');
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
let found_crate = crate_name("esp-lp-hal").expect("esp-lp-hal is present in `Cargo.toml`");
|
||||
let hal_crate = match found_crate {
|
||||
FoundCrate::Itself => quote!(esp_lp_hal),
|
||||
FoundCrate::Name(name) => {
|
||||
let ident = Ident::new(&name, Span::call_site());
|
||||
quote!( #ident )
|
||||
}
|
||||
};
|
||||
|
||||
if !args.is_empty() {
|
||||
return Error::new(Span::call_site(), "This attribute accepts no arguments")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
let f = parse_macro_input!(input as ItemFn);
|
||||
|
||||
let mut argument_types = Vec::new();
|
||||
let mut create_peripheral = Vec::new();
|
||||
|
||||
let mut used_pins: Vec<u8> = Vec::new();
|
||||
|
||||
for (num, arg) in f.sig.inputs.iter().enumerate() {
|
||||
let param_name = format_ident!("param{}", num);
|
||||
match arg {
|
||||
FnArg::Receiver(_) => {
|
||||
return Error::new(arg.span(), "invalid argument")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
FnArg::Typed(t) => {
|
||||
match simplename(&t.ty).as_str() {
|
||||
"Output" => {
|
||||
let pin = extract_pin(&t.ty);
|
||||
if used_pins.contains(&pin) {
|
||||
return Error::new(arg.span(), "duplicate pin")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
used_pins.push(pin);
|
||||
create_peripheral.push(quote!(
|
||||
let mut #param_name = unsafe { the_hal::gpio::conjure_output().unwrap() };
|
||||
));
|
||||
}
|
||||
"Input" => {
|
||||
let pin = extract_pin(&t.ty);
|
||||
if used_pins.contains(&pin) {
|
||||
return Error::new(arg.span(), "duplicate pin")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
used_pins.push(pin);
|
||||
create_peripheral.push(quote!(
|
||||
let mut #param_name = unsafe { the_hal::gpio::conjure_input().unwrap() };
|
||||
));
|
||||
}
|
||||
"LpUart" => {
|
||||
create_peripheral.push(quote!(
|
||||
let mut #param_name = unsafe { the_hal::uart::conjure() };
|
||||
));
|
||||
}
|
||||
"LpI2c" => {
|
||||
create_peripheral.push(quote!(
|
||||
let mut #param_name = unsafe { the_hal::i2c::conjure() };
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
return Error::new(arg.span(), "invalid argument to main")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
}
|
||||
argument_types.push(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let magic_symbol_name = make_magic_symbol_name(&argument_types);
|
||||
|
||||
let param_names: Vec<Ident> = argument_types
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(num, _)| format_ident!("param{}", num))
|
||||
.collect();
|
||||
|
||||
quote!(
|
||||
#[allow(non_snake_case)]
|
||||
#[export_name = "main"]
|
||||
pub fn __risc_v_rt__main() -> ! {
|
||||
#[export_name = #magic_symbol_name]
|
||||
static ULP_MAGIC: [u32; 0] = [0u32; 0];
|
||||
|
||||
unsafe { ULP_MAGIC.as_ptr().read_volatile(); }
|
||||
|
||||
use #hal_crate as the_hal;
|
||||
#(
|
||||
#create_peripheral;
|
||||
)*
|
||||
|
||||
main(#(#param_names),*);
|
||||
}
|
||||
|
||||
#f
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "has-lp-core", feature = "has-ulp-core"))]
|
||||
pub fn load_lp_code(input: TokenStream) -> TokenStream {
|
||||
use std::{fs, path::Path};
|
||||
|
||||
use litrs::StringLit;
|
||||
use object::{File, Object, ObjectSection, ObjectSymbol, Section, SectionKind};
|
||||
use parse::Error;
|
||||
use proc_macro::Span;
|
||||
use proc_macro_crate::{crate_name, FoundCrate};
|
||||
use syn::{parse, Ident};
|
||||
|
||||
let hal_crate = if cfg!(any(feature = "is-lp-core", feature = "is-ulp-core")) {
|
||||
crate_name("esp-lp-hal")
|
||||
} else {
|
||||
crate_name("esp-hal")
|
||||
};
|
||||
|
||||
let hal_crate = if let Ok(FoundCrate::Name(ref name)) = hal_crate {
|
||||
let ident = Ident::new(name, Span::call_site().into());
|
||||
quote!( #ident )
|
||||
} else {
|
||||
quote!(crate)
|
||||
};
|
||||
|
||||
let first_token = match input.into_iter().next() {
|
||||
Some(token) => token,
|
||||
None => {
|
||||
return Error::new(
|
||||
Span::call_site().into(),
|
||||
"You need to give the path to an ELF file",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
};
|
||||
let arg = match StringLit::try_from(&first_token) {
|
||||
Ok(arg) => arg,
|
||||
Err(_) => {
|
||||
return Error::new(
|
||||
Span::call_site().into(),
|
||||
"You need to give the path to an ELF file",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
};
|
||||
let elf_file = arg.value();
|
||||
|
||||
if !Path::new(elf_file).exists() {
|
||||
return Error::new(Span::call_site().into(), "File not found")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
let bin_data = fs::read(elf_file).unwrap();
|
||||
let obj_file = File::parse(&*bin_data).unwrap();
|
||||
let sections = obj_file.sections();
|
||||
|
||||
let mut sections: Vec<Section> = sections
|
||||
.into_iter()
|
||||
.filter(|section| {
|
||||
matches!(
|
||||
section.kind(),
|
||||
SectionKind::Text
|
||||
| SectionKind::ReadOnlyData
|
||||
| SectionKind::Data
|
||||
| SectionKind::UninitializedData
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
sections.sort_by(|a, b| a.address().partial_cmp(&b.address()).unwrap());
|
||||
|
||||
let mut binary: Vec<u8> = Vec::new();
|
||||
let mut last_address = if cfg!(feature = "has-lp-core") {
|
||||
0x5000_0000
|
||||
} else {
|
||||
0x0
|
||||
};
|
||||
|
||||
for section in sections {
|
||||
if section.address() > last_address {
|
||||
let fill = section.address() - last_address;
|
||||
binary.extend(std::iter::repeat(0).take(fill as usize));
|
||||
}
|
||||
|
||||
binary.extend_from_slice(section.data().unwrap());
|
||||
last_address = section.address() + section.size();
|
||||
}
|
||||
|
||||
let magic_symbol = obj_file
|
||||
.symbols()
|
||||
.find(|s| s.name().unwrap().starts_with("__ULP_MAGIC_"));
|
||||
|
||||
let magic_symbol = if let Some(magic_symbol) = magic_symbol {
|
||||
magic_symbol.name().unwrap()
|
||||
} else {
|
||||
return Error::new(
|
||||
Span::call_site().into(),
|
||||
"Given file doesn't seem to be an LP/ULP core application.",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
};
|
||||
|
||||
let magic_symbol = magic_symbol.trim_start_matches("__ULP_MAGIC_");
|
||||
let args: Vec<proc_macro2::TokenStream> = magic_symbol
|
||||
.split("$")
|
||||
.map(|t| {
|
||||
let t = if t.contains("OutputOpenDrain") {
|
||||
t.replace("OutputOpenDrain", "LowPowerOutputOpenDrain")
|
||||
} else {
|
||||
t.replace("Output", "LowPowerOutput")
|
||||
};
|
||||
let t = t.replace("Input", "LowPowerInput");
|
||||
t.parse().unwrap()
|
||||
})
|
||||
.filter(|v: &proc_macro2::TokenStream| !v.is_empty())
|
||||
.collect();
|
||||
|
||||
#[cfg(feature = "has-lp-core")]
|
||||
let imports = quote! {
|
||||
use #hal_crate::lp_core::LpCore;
|
||||
use #hal_crate::lp_core::LpCoreWakeupSource;
|
||||
use #hal_crate::gpio::lp_io::LowPowerOutput;
|
||||
use #hal_crate::gpio::*;
|
||||
use #hal_crate::uart::lp_uart::LpUart;
|
||||
use #hal_crate::i2c::lp_i2c::LpI2c;
|
||||
};
|
||||
#[cfg(feature = "has-ulp-core")]
|
||||
let imports = quote! {
|
||||
use #hal_crate::ulp_core::UlpCore as LpCore;
|
||||
use #hal_crate::ulp_core::UlpCoreWakeupSource as LpCoreWakeupSource;
|
||||
use #hal_crate::gpio::*;
|
||||
};
|
||||
|
||||
#[cfg(feature = "has-lp-core")]
|
||||
let rtc_code_start = quote! { _rtc_fast_data_start };
|
||||
#[cfg(feature = "has-ulp-core")]
|
||||
let rtc_code_start = quote! { _rtc_slow_data_start };
|
||||
|
||||
quote! {
|
||||
{
|
||||
#imports
|
||||
|
||||
struct LpCoreCode {}
|
||||
|
||||
static LP_CODE: &[u8] = &[#(#binary),*];
|
||||
|
||||
extern "C" {
|
||||
static #rtc_code_start: u32;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
core::ptr::copy_nonoverlapping(LP_CODE as *const _ as *const u8, &#rtc_code_start as *const u32 as *mut u8, LP_CODE.len());
|
||||
}
|
||||
|
||||
impl LpCoreCode {
|
||||
pub fn run(
|
||||
&self,
|
||||
lp_core: &mut LpCore,
|
||||
wakeup_source: LpCoreWakeupSource,
|
||||
#(_: #args),*
|
||||
) {
|
||||
lp_core.run(wakeup_source);
|
||||
}
|
||||
}
|
||||
|
||||
LpCoreCode {}
|
||||
}
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
@ -1,37 +0,0 @@
|
||||
[package]
|
||||
name = "esp-hal-smartled"
|
||||
version = "0.9.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.76.0"
|
||||
description = "RMT adapter for smartleds"
|
||||
repository = "https://github.com/esp-rs/esp-hal"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["esp32c3"]
|
||||
targets = ["riscv32imc-unknown-none-elf"]
|
||||
|
||||
[dependencies]
|
||||
defmt = { version = "0.3.6", optional = true }
|
||||
document-features = "0.2.8"
|
||||
esp-hal = { version = "0.16.0", path = "../esp-hal" }
|
||||
fugit = "0.3.7"
|
||||
smart-leds-trait = "0.3.0"
|
||||
|
||||
[features]
|
||||
## Implement `defmt::Format` on certain types.
|
||||
defmt = ["dep:defmt", "esp-hal/defmt"]
|
||||
|
||||
#! ### Chip Support Feature Flags
|
||||
## Target the ESP32.
|
||||
esp32 = ["esp-hal/esp32"]
|
||||
## Target the ESP32-C3.
|
||||
esp32c3 = ["esp-hal/esp32c3"]
|
||||
## Target the ESP32-C6.
|
||||
esp32c6 = ["esp-hal/esp32c6"]
|
||||
## Target the ESP32-H2.
|
||||
esp32h2 = ["esp-hal/esp32h2"]
|
||||
## Target the ESP32-S2.
|
||||
esp32s2 = ["esp-hal/esp32s2"]
|
||||
## Target the ESP32-S3.
|
||||
esp32s3 = ["esp-hal/esp32s3"]
|
||||
@ -1,34 +0,0 @@
|
||||
# esp-hal-smartled
|
||||
|
||||
[](https://crates.io/crates/esp-hal-smartled)
|
||||
[](https://docs.rs/esp-hal-smartled)
|
||||

|
||||
[](https://matrix.to/#/#esp-rs:matrix.org)
|
||||
|
||||
This adapter allows for the use of an RMT output channel to easily interact with RGB LEDs and use the convenience functions of the [`smart-leds`](https://crates.io/crates/smart-leds) crate.
|
||||
|
||||
## [Documentation]
|
||||
|
||||
[documentation]: https://docs.rs/esp-hal-smartled/
|
||||
|
||||
## Usage
|
||||
|
||||
### `defmt` Feature
|
||||
|
||||
Please note that `defmt` does _not_ provide MSRV guarantees with releases, and as such we are not able to make any MSRV guarantees when this feature is enabled. For more information refer to the MSRV section of `defmt`'s README:
|
||||
https://github.com/knurling-rs/defmt?tab=readme-ov-file#msrv
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of:
|
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
- MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in
|
||||
the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without
|
||||
any additional terms or conditions.
|
||||
@ -1,202 +0,0 @@
|
||||
//! This adapter allows for the use of an RMT output channel to easily interact
|
||||
//! with RGB LEDs and use the convenience functions of the
|
||||
//! [`smart-leds`](https://crates.io/crates/smart-leds) crate.
|
||||
//!
|
||||
//! This is a simple implementation where every LED is adressed in an
|
||||
//! individual RMT operation. This is working perfectly fine in blocking mode,
|
||||
//! but in case this is used in combination with interrupts that might disturb
|
||||
//! the sequential sending, an alternative implementation (addressing the LEDs
|
||||
//! in a sequence in a single RMT send operation) might be required!
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
//! let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||
//! let rmt = Rmt::new(peripherals.RMT, 80u32.MHz(), &clocks).unwrap();
|
||||
//!
|
||||
//! let rmt_buffer = smartLedBuffer!(1);
|
||||
//! let mut led = SmartLedsAdapter::new(rmt.channel0, io.pins.gpio2, rmt_buffer);
|
||||
//! ```
|
||||
//!
|
||||
//! ## Feature Flags
|
||||
#![doc = document_features::document_features!()]
|
||||
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
|
||||
#![deny(missing_docs)]
|
||||
#![no_std]
|
||||
|
||||
use core::{fmt::Debug, slice::IterMut};
|
||||
|
||||
use esp_hal::{
|
||||
clock::Clocks,
|
||||
gpio::OutputPin,
|
||||
peripheral::Peripheral,
|
||||
rmt::{Error as RmtError, PulseCode, TxChannel, TxChannelConfig, TxChannelCreator},
|
||||
};
|
||||
use smart_leds_trait::{SmartLedsWrite, RGB8};
|
||||
|
||||
const SK68XX_CODE_PERIOD: u32 = 1200;
|
||||
const SK68XX_T0H_NS: u32 = 320;
|
||||
const SK68XX_T0L_NS: u32 = SK68XX_CODE_PERIOD - SK68XX_T0H_NS;
|
||||
const SK68XX_T1H_NS: u32 = 640;
|
||||
const SK68XX_T1L_NS: u32 = SK68XX_CODE_PERIOD - SK68XX_T1H_NS;
|
||||
|
||||
/// All types of errors that can happen during the conversion and transmission
|
||||
/// of LED commands
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum LedAdapterError {
|
||||
/// Raised in the event that the provided data container is not large enough
|
||||
BufferSizeExceeded,
|
||||
/// Raised if something goes wrong in the transmission,
|
||||
TransmissionError(RmtError),
|
||||
}
|
||||
|
||||
/// Macro to allocate a buffer sized for a specific number of LEDs to be
|
||||
/// addressed.
|
||||
///
|
||||
/// Attempting to use more LEDs that the buffer is configured for will result in
|
||||
/// an `LedAdapterError:BufferSizeExceeded` error.
|
||||
#[macro_export]
|
||||
macro_rules! smartLedBuffer {
|
||||
( $buffer_size: literal ) => {
|
||||
// The size we're assigning here is calculated as following
|
||||
// (
|
||||
// Nr. of LEDs
|
||||
// * channels (r,g,b -> 3)
|
||||
// * pulses per channel 8)
|
||||
// ) + 1 additional pulse for the end delimiter
|
||||
[0u32; $buffer_size * 24 + 1]
|
||||
};
|
||||
}
|
||||
|
||||
/// Adapter taking an RMT channel and a specific pin and providing RGB LED
|
||||
/// interaction functionality using the `smart-leds` crate
|
||||
pub struct SmartLedsAdapter<TX, const BUFFER_SIZE: usize>
|
||||
where
|
||||
TX: TxChannel,
|
||||
{
|
||||
channel: Option<TX>,
|
||||
rmt_buffer: [u32; BUFFER_SIZE],
|
||||
pulses: (u32, u32),
|
||||
}
|
||||
|
||||
impl<'d, TX, const BUFFER_SIZE: usize> SmartLedsAdapter<TX, BUFFER_SIZE>
|
||||
where
|
||||
TX: TxChannel,
|
||||
{
|
||||
/// Create a new adapter object that drives the pin using the RMT channel.
|
||||
pub fn new<C, O>(
|
||||
channel: C,
|
||||
pin: impl Peripheral<P = O> + 'd,
|
||||
rmt_buffer: [u32; BUFFER_SIZE],
|
||||
clocks: &Clocks,
|
||||
) -> SmartLedsAdapter<TX, BUFFER_SIZE>
|
||||
where
|
||||
O: OutputPin + 'd,
|
||||
C: TxChannelCreator<'d, TX, O>,
|
||||
{
|
||||
let config = TxChannelConfig {
|
||||
clk_divider: 1,
|
||||
idle_output_level: false,
|
||||
carrier_modulation: false,
|
||||
idle_output: true,
|
||||
|
||||
..TxChannelConfig::default()
|
||||
};
|
||||
|
||||
let channel = channel.configure(pin, config).unwrap();
|
||||
|
||||
// Assume the RMT peripheral is set up to use the APB clock
|
||||
let src_clock = clocks.apb_clock.to_MHz();
|
||||
|
||||
Self {
|
||||
channel: Some(channel),
|
||||
rmt_buffer,
|
||||
pulses: (
|
||||
u32::from(PulseCode {
|
||||
level1: true,
|
||||
length1: ((SK68XX_T0H_NS * src_clock) / 1000) as u16,
|
||||
level2: false,
|
||||
length2: ((SK68XX_T0L_NS * src_clock) / 1000) as u16,
|
||||
}),
|
||||
u32::from(PulseCode {
|
||||
level1: true,
|
||||
length1: ((SK68XX_T1H_NS * src_clock) / 1000) as u16,
|
||||
level2: false,
|
||||
length2: ((SK68XX_T1L_NS * src_clock) / 1000) as u16,
|
||||
}),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_rgb_to_pulse(
|
||||
value: RGB8,
|
||||
mut_iter: &mut IterMut<u32>,
|
||||
pulses: (u32, u32),
|
||||
) -> Result<(), LedAdapterError> {
|
||||
Self::convert_rgb_channel_to_pulses(value.g, mut_iter, pulses)?;
|
||||
Self::convert_rgb_channel_to_pulses(value.r, mut_iter, pulses)?;
|
||||
Self::convert_rgb_channel_to_pulses(value.b, mut_iter, pulses)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn convert_rgb_channel_to_pulses(
|
||||
channel_value: u8,
|
||||
mut_iter: &mut IterMut<u32>,
|
||||
pulses: (u32, u32),
|
||||
) -> Result<(), LedAdapterError> {
|
||||
for position in [128, 64, 32, 16, 8, 4, 2, 1] {
|
||||
*mut_iter.next().ok_or(LedAdapterError::BufferSizeExceeded)? =
|
||||
match channel_value & position {
|
||||
0 => pulses.0,
|
||||
_ => pulses.1,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<TX, const BUFFER_SIZE: usize> SmartLedsWrite for SmartLedsAdapter<TX, BUFFER_SIZE>
|
||||
where
|
||||
TX: TxChannel,
|
||||
{
|
||||
type Error = LedAdapterError;
|
||||
type Color = RGB8;
|
||||
|
||||
/// Convert all RGB8 items of the iterator to the RMT format and
|
||||
/// add them to internal buffer, then start a singular RMT operation
|
||||
/// based on that buffer.
|
||||
fn write<T, I>(&mut self, iterator: T) -> Result<(), Self::Error>
|
||||
where
|
||||
T: IntoIterator<Item = I>,
|
||||
I: Into<Self::Color>,
|
||||
{
|
||||
// We always start from the beginning of the buffer
|
||||
let mut seq_iter = self.rmt_buffer.iter_mut();
|
||||
|
||||
// Add all converted iterator items to the buffer.
|
||||
// This will result in an `BufferSizeExceeded` error in case
|
||||
// the iterator provides more elements than the buffer can take.
|
||||
for item in iterator {
|
||||
Self::convert_rgb_to_pulse(item.into(), &mut seq_iter, self.pulses)?;
|
||||
}
|
||||
|
||||
// Finally, add an end element.
|
||||
*seq_iter.next().ok_or(LedAdapterError::BufferSizeExceeded)? = 0;
|
||||
|
||||
// Perform the actual RMT operation. We use the u32 values here right away.
|
||||
let channel = self.channel.take().unwrap();
|
||||
match channel.transmit(&self.rmt_buffer).wait() {
|
||||
Ok(chan) => {
|
||||
self.channel = Some(chan);
|
||||
Ok(())
|
||||
}
|
||||
Err((e, chan)) => {
|
||||
self.channel = Some(chan);
|
||||
Err(LedAdapterError::TransmissionError(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
esp-hal/.clippy.toml
Normal file
1
esp-hal/.clippy.toml
Normal file
@ -0,0 +1 @@
|
||||
avoid-breaking-exported-api = false
|
||||
@ -5,6 +5,600 @@ 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]
|
||||
|
||||
### Added
|
||||
|
||||
- ESP32-S3: Added SDMMC signals (#2556)
|
||||
- Added `set_priority` to the `DmaChannel` trait on GDMA devices (#2403, #2526)
|
||||
- Added `into_async` and `into_blocking` functions for `ParlIoTxOnly`, `ParlIoRxOnly` (#2526)
|
||||
- ESP32-C6, H2, S3: Added `split` function to the `DmaChannel` trait. (#2526, #2532)
|
||||
- DMA: `PeripheralDmaChannel` type aliasses and `DmaChannelFor` traits to improve usability. (#2532)
|
||||
- `dma::{Channel, ChannelRx, ChannelTx}::set_priority` for GDMA devices (#2403)
|
||||
- `esp_hal::asynch::AtomicWaker` that does not hold a global critical section (#2555)
|
||||
- `esp_hal::sync::RawMutex` for embassy-sync. (#2555)
|
||||
- ESP32-C6, H2, S3: Added `split` function to the `DmaChannel` trait. (#2526)
|
||||
- Added PSRAM configuration to `esp_hal::Config` if `quad-psram` or `octal-psram` is enabled (#2546)
|
||||
- Added `esp_hal::psram::psram_raw_parts` (#2546)
|
||||
- The timer drivers `OneShotTimer` & `PeriodicTimer` have `into_async` and `new_typed` methods (#2586)
|
||||
- `timer::Timer` trait has three new methods, `wait`, `async_interrupt_handler` and `peripheral_interrupt` (#2586)
|
||||
- Configuration structs in the I2C, SPI, and UART drivers now implement the Builder Lite pattern (#2614)
|
||||
- Added `I8080::apply_config`, `DPI::apply_config` and `Camera::apply_config` (#2610)
|
||||
- Introduced the `unstable` feature which will be used to restrict stable APIs to a subset of esp-hal. (#2628)
|
||||
- HAL configuration structs now implement the Builder Lite pattern (#2645)
|
||||
- Added `OutputOpenDrain::unlisten` (#2625)
|
||||
- Added `{Input, Flex}::wait_for` (#2625)
|
||||
- Peripheral singletons now implement `Debug` and `defmt::Format` (#2682, #2834)
|
||||
- `BurstConfig`, a device-specific configuration for configuring DMA transfers in burst mode (#2543)
|
||||
- `{DmaRxBuf, DmaTxBuf, DmaRxTxBuf}::set_burst_config` (#2543)
|
||||
- Added `SpiDmaBus::split` for moving between manual & automatic DMA buffers (#2824)
|
||||
- ESP32-S2: DMA support for AES (#2699)
|
||||
- Added `transfer_in_place_async` and embedded-hal-async implementation to `Spi` (#2691)
|
||||
- `InterruptHandler` now implements `Hash` and `defmt::Format` (#2830)
|
||||
- `uart::ConfigError` now implements `Eq` (#2825)
|
||||
- `i2c::master::Error` now implements `Eq` and `Hash` (#2825)
|
||||
- `i2c::master::Operation` now implements `Debug`, `PartialEq`, `Eq`, `Hash`, and `Display` (#2825)
|
||||
- `i2c::master::Config` now implements `PartialEq`, `Eq`, ans `Hash` (#2825)
|
||||
- `i2c::master::I2c` now implements `Debug`, `PartialEq`, and `Eq` (#2825)
|
||||
- `i2c::master::Info` now implements `Debug` (#2825)
|
||||
- `spi::master::Config` now implements `Hash` (#2823)
|
||||
- `spi::master` drivers now implement `Debug` and `defmt::Format` (#2823)
|
||||
- `DmaRxBuf`, `DmaTxBuf` and `DmaRxTxBuf` now implement `Debug` and `defmt::Format` (#2823)
|
||||
- DMA channels (`AnyGdmaChannel`, `SpiDmaChannel`, `I2sDmaChannel`, `CryptoDmaChannel`) and their RX/TX halves now implement `Debug` and `defmt::Format` (#2823)
|
||||
- `DmaDescriptor` and `DmaDescriptorFlags` now implement `PartialEq` and `Eq` (#2823)
|
||||
- `gpio::{Event, WakeEvent, GpioRegisterAccess}` now implement `Debug`, `Eq`, `PartialEq` and `Hash` (#2842)
|
||||
- `gpio::{Level, Pull, AlternateFunction, RtcFunction}` now implement `Hash` (#2842)
|
||||
- `gpio::{GpioPin, AnyPin, Io, Output, OutputOpenDrain, Input, Flex}` now implement `Debug`, `defmt::Format` (#2842)
|
||||
- More interrupts are available in `esp_hal::spi::master::SpiInterrupt`, add `enable_listen`,`interrupts` and `clear_interrupts` for ESP32/ESP32-S2 (#2833)
|
||||
- The `ExtU64` and `RateExtU32` traits have been added to `esp_hal::time` (#2845)
|
||||
- Added `AnyPin::steal(pin_number)` (#2854)
|
||||
- `adc::{AdcCalSource, Attenuation, Resolution}` now implement `Hash` and `defmt::Format` (#2840)
|
||||
- `rtc_cntl::{RtcFastClock, RtcSlowClock, RtcCalSel}` now implement `PartialEq`, `Eq`, `Hash` and `defmt::Format` (#2840)
|
||||
- Added `tsens::TemperatureSensor` peripheral for ESP32C6 and ESP32C3 (#2875)
|
||||
- Added `with_rx()` and `with_tx()` methods to Uart, UartRx, and UartTx ()
|
||||
|
||||
### Changed
|
||||
|
||||
- Bump MSRV to 1.83 (#2615)
|
||||
- In addition to taking by value, peripheral drivers can now mutably borrow DMA channel objects. (#2526)
|
||||
- DMA channel objects are no longer wrapped in `Channel`. The `Channel` drivers are now managed by DMA enabled peripheral drivers. (#2526)
|
||||
- The `Dpi` driver and `DpiTransfer` now have a `Mode` type parameter. The driver's asyncness is determined by the asyncness of the `Lcd` used to create it. (#2526)
|
||||
- `dma::{Channel, ChannelRx, ChannelTx}::set_priority` for GDMA devices (#2403)
|
||||
- `SystemTimer::set_unit_value` & `SystemTimer::configure_unit` (#2576)
|
||||
- `SystemTimer` no longer uses peripheral ref (#2576)
|
||||
- `TIMGX` no longer uses peripheral ref (#2581)
|
||||
- `SystemTimer::now` has been renamed `SystemTimer::unit_value(Unit)` (#2576)
|
||||
- `SpiDma` transfers now explicitly take a length along with the DMA buffer object (#2587)
|
||||
- `dma::{Channel, ChannelRx, ChannelTx}::set_priority` for GDMA devices (#2403)
|
||||
- `SystemTimer`s `Alarm`s are now type erased (#2576)
|
||||
- `TimerGroup` `Timer`s are now type erased (#2581)
|
||||
- PSRAM is now initialized automatically if `quad-psram` or `octal-psram` is enabled (#2546)
|
||||
- DMA channels are now available via the `Peripherals` struct, and have been renamed accordingly. (#2545)
|
||||
- Moved interrupt related items from lib.rs, moved to the `interrupt` module (#2613)
|
||||
- The timer drivers `OneShotTimer` & `PeriodicTimer` now have a `Mode` parameter and type erase the underlying driver by default (#2586)
|
||||
- `timer::Timer` has new trait requirements of `Into<AnyTimer>`, `'static` and `InterruptConfigurable` (#2586)
|
||||
- `systimer::etm::Event` no longer borrows the alarm indefinitely (#2586)
|
||||
- A number of public enums and structs in the I2C, SPI, and UART drivers have been marked with `#[non_exhaustive]` (#2614)
|
||||
- Interrupt handling related functions are only provided for Blocking UART. (#2610)
|
||||
- Changed how `Spi`, (split or unsplit) `Uart`, `LpUart`, `I8080`, `Camera`, `DPI` and `I2C` drivers are constructed (#2610)
|
||||
- I8080, camera, DPI: The various standalone configuration options have been merged into `Config` (#2610)
|
||||
- Dropped GPIO futures stop listening for interrupts (#2625)
|
||||
- UART driver's `StopBits` enum variants now correctly use UpperCamelCase (#2669)
|
||||
- The `PeripheralInput` and `PeripheralOutput` traits are now sealed (#2690)
|
||||
- `esp_hal::sync::Lock` has been renamed to RawMutex (#2684)
|
||||
- Updated `esp-pacs` with support for Wi-Fi on the ESP32 and made the peripheral non virtual
|
||||
- `SpiBitOrder`, `SpiDataMode`, `SpiMode` were renamed to `BitOder`, `DataMode` and `Mode` (#2828)
|
||||
- `crate::Mode` was renamed to `crate::DriverMode` (#2828)
|
||||
- `Spi::with_miso` has been overloaded into `Spi::with_miso` and `Spi::with_sio1` (#2557)
|
||||
- Renamed some I2C error variants (#2844)
|
||||
- I2C: Replaced potential panics with errors. (#2831)
|
||||
- UART: Make `AtCmdConfig` and `ConfigError` non-exhaustive (#2851)
|
||||
- UART: Make `AtCmdConfig` use builder-lite pattern (#2851)
|
||||
- UART: Fix naming violations for `DataBits`, `Parity`, and `StopBits` enum variants (#2893)
|
||||
- UART: Remove blocking version of `read_bytes` and rename `drain_fifo` to `read_bytes` instead (#2895)
|
||||
- Renamed variants of `CpuClock`, made the enum non-exhaustive (#2899)
|
||||
- SPI: Fix naming violations for `Mode` enum variants (#2902)
|
||||
- SPI: Fix naming violations for `Address` and `Command` enum variants (#2906)
|
||||
|
||||
- `ClockSource` enums are now `#[non_exhaustive]` (#2912)
|
||||
|
||||
- `gpio::{Input, Flex}::wakeup_enable` now returns an error instead of panicking. (#2916)
|
||||
|
||||
- Removed the `I` prefix from `DriveStrength` enum variants. (#2922)
|
||||
- Removed the `Attenuation` prefix from `Attenuation` enum variants. (#2922)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Xtensa devices now correctly enable the `esp-hal-procmacros/rtc-slow` feature (#2594)
|
||||
- User-bound GPIO interrupt handlers should no longer interfere with async pins. (#2625)
|
||||
- `spi::master::Spi::{into_async, into_blocking}` are now correctly available on the typed driver, to. (#2674)
|
||||
- It is no longer possible to safely conjure `GpioPin` instances (#2688)
|
||||
- UART: Public API follows `C-WORD_ORDER` Rust API standard (`VerbObject` order) (#2851)
|
||||
- `DmaRxStreamBuf` now correctly resets the descriptors the next time it's used (#2890)
|
||||
|
||||
### Removed
|
||||
|
||||
- Remove more examples. Update doctests. (#2547)
|
||||
- The `configure` and `configure_for_async` DMA channel functions has been removed (#2403)
|
||||
- The DMA channel objects no longer have `tx` and `rx` fields. (#2526)
|
||||
- `SysTimerAlarms` has been removed, alarms are now part of the `SystemTimer` struct (#2576)
|
||||
- `FrozenUnit`, `AnyUnit`, `SpecificUnit`, `SpecificComparator`, `AnyComparator` have been removed from `systimer` (#2576)
|
||||
- Remove Dma[Rx|Tx]Buffer::length (#2587)
|
||||
- `esp_hal::psram::psram_range` (#2546)
|
||||
- The `Dma` structure has been removed. (#2545)
|
||||
- Removed `embedded-hal 0.2.x` impls and deps from `esp-hal` (#2593)
|
||||
- Removed `Camera::set_` functions (#2610)
|
||||
- `DmaTxBuf::{compute_chunk_size, compute_descriptor_count, new_with_block_size}` (#2543)
|
||||
|
||||
- The `prelude` module has been removed (#2845)
|
||||
|
||||
- Removed all peripheral instance type parameters and `new_typed` constructors (#2907)
|
||||
|
||||
## [0.22.0] - 2024-11-20
|
||||
|
||||
### Added
|
||||
|
||||
- A new config option `PLACE_SWITCH_TABLES_IN_RAM` to improve performance (especially for interrupts) at the cost of slightly more RAM usage (#2331)
|
||||
- A new config option `PLACE_ANON_IN_RAM` to improve performance (especially for interrupts) at the cost of RAM usage (#2331)
|
||||
- Add burst transfer support to DMA buffers (#2336)
|
||||
- `AnyPin` now implements `From<GpioPin<N>>`. (#2326)
|
||||
- Added `AnySpi` and `AnySpiDmaChannel`. (#2334)
|
||||
- Added `AnyI2s` and `AnyI2sDmaChannel`. (#2367)
|
||||
- Added `AnyTwai`. (#2359)
|
||||
- Added `AnyUart`. (#2381)
|
||||
- `Pins::steal()` to unsafely obtain GPIO. (#2335)
|
||||
- `I2c::with_timeout` (#2361)
|
||||
- `Spi::half_duplex_read` and `Spi::half_duplex_write` (#2373)
|
||||
- Add RGB/DPI driver (#2415)
|
||||
- Add `DmaLoopBuf` (#2415)
|
||||
- `Cpu::COUNT` and `Cpu::current()` (#2411)
|
||||
- `UartInterrupt` and related functions (#2406)
|
||||
- I2S Parallel output driver for ESP32. (#2348, #2436, #2472)
|
||||
- Add an option to configure `WDT` action (#2330)
|
||||
- `DmaDescriptor` is now `Send` (#2456)
|
||||
- `into_async` and `into_blocking` functions for most peripherals (#2430, #2461)
|
||||
- API mode type parameter (currently always `Blocking`) to `master::Spi` and `slave::Spi` (#2430)
|
||||
- `gpio::{GpioPin, AnyPin, Flex, Output, OutputOpenDrain}::split()` to obtain peripheral interconnect signals. (#2418)
|
||||
- `gpio::Input::{split(), into_peripheral_output()}` when used with output pins. (#2418)
|
||||
- `gpio::Output::peripheral_input()` (#2418)
|
||||
- `{Uart, UartRx, UartTx}::apply_config()` (#2449)
|
||||
- `{Uart, UartRx, UartTx}` now implement `embassy_embedded_hal::SetConfig` (#2449)
|
||||
- GPIO ETM tasks and events now accept `InputSignal` and `OutputSignal` (#2427)
|
||||
- `spi::master::Config` and `{Spi, SpiDma, SpiDmaBus}::apply_config` (#2448)
|
||||
- `embassy_embedded_hal::SetConfig` is now implemented for `spi::master::{Spi, SpiDma, SpiDmaBus}`, `i2c::master::I2c` (#2448, #2477)
|
||||
- `slave::Spi::{with_mosi(), with_miso(), with_sclk(), with_cs()}` functions (#2485)
|
||||
- I8080: Added `set_8bits_order()` to set the byte order in 8-bit mode (#2487)
|
||||
- `I2c::{apply_config(), with_sda(), with_scl()}` (#2477)
|
||||
- ESP32-S2: Added missing GPIO alternate functions (#2512)
|
||||
|
||||
### Changed
|
||||
|
||||
- Peripheral type erasure for SPI (#2334)
|
||||
- Peripheral type erasure for I2S (#2367)
|
||||
- Peripheral type erasure for I2C (#2361)
|
||||
- Peripheral type erasure for TWAI (#2359)
|
||||
- The SPI driver has been rewritten to allow using half-duplex and full-duplex functionality on the same bus. See the migration guide for details. (#2373)
|
||||
- Renamed `SpiDma` functions: `dma_transfer` to `transfer`, `dma_write` to `write`, `dma_read` to `read`. (#2373)
|
||||
- Peripheral type erasure for UART (#2381)
|
||||
- Changed listening for UART events (#2406)
|
||||
- Circular DMA transfers now correctly error, `available` returns `Result<usize,DmaError>` now (#2409)
|
||||
- Interrupt listen/unlisten/clear functions now accept any type that converts into `EnumSet` (i.e. single interrupt flags). (#2442)
|
||||
- SPI interrupt listening is now only available in Blocking mode. The `set_interrupt_handler` is available via `InterruptConfigurable` (#2442)
|
||||
- Allow users to create DMA `Preparation`s (#2455)
|
||||
- The `rmt::asynch::RxChannelAsync` and `rmt::asynch::TxChannelAsync` traits have been moved to `rmt` (#2430)
|
||||
- Calling `AnyPin::output_signals` on an input-only pin (ESP32 GPIO 34-39) will now result in a panic. (#2418)
|
||||
- UART configuration types have been moved to `esp_hal::uart` (#2449)
|
||||
- `spi::master::Spi::new()` no longer takes `frequency` and `mode` as a parameter. (#2448)
|
||||
- Peripheral interconnections via GPIO pins now use the GPIO matrix. (#2419)
|
||||
- The I2S driver has been moved to `i2s::master` (#2472)
|
||||
- `slave::Spi` constructors no longer take pins (#2485)
|
||||
- The `I2c` master driver has been moved from `esp_hal::i2c` to `esp_hal::i2c::master`. (#2476)
|
||||
- `I2c` SCL timeout is now defined in bus clock cycles. (#2477)
|
||||
- Trying to send a single-shot RMT transmission will result in an error now, `RMT` deals with `u32` now, `PulseCode` is a convenience trait now (#2463)
|
||||
- Removed `get_` prefixes from functions (#2528)
|
||||
- The `Camera` and `I8080` drivers' constructors now only accepts blocking-mode DMA channels. (#2519)
|
||||
- Many peripherals are now disabled by default and also get disabled when the driver is dropped (#2544)
|
||||
|
||||
- Config: Crate prefixes and configuration keys are now separated by `_CONFIG_` (#2848)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix conflict between `RtcClock::get_xtal_freq` and `Rtc::disable_rom_message_printing` (#2360)
|
||||
- Fixed an issue where interrupts enabled before `esp_hal::init` were disabled. This issue caused the executor created by `#[esp_hal_embassy::main]` to behave incorrectly in multi-core applications. (#2377)
|
||||
- Fixed `TWAI::transmit_async`: bus-off state is not reached when CANH and CANL are shorted. (#2421)
|
||||
- ESP32: added UART-specific workaround for https://docs.espressif.com/projects/esp-chip-errata/en/latest/esp32/03-errata-description/esp32/cpu-subsequent-access-halted-when-get-interrupted.html (#2441)
|
||||
- Fixed some SysTimer race conditions and panics (#2451)
|
||||
- TWAI: accept all messages by default (#2467)
|
||||
- I8080: `set_byte_order()` now works correctly in 16-bit mode (#2487)
|
||||
- ESP32-C6/ESP32-H2: Make higher LEDC frequencies work (#2520)
|
||||
|
||||
### Removed
|
||||
|
||||
- The `i2s::{I2sWrite, I2sWriteDma, I2sRead, I2sReadDma, I2sWriteDmaAsync, I2sReadDmaAsync}` traits have been removed. (#2316)
|
||||
- The `ledc::ChannelHW` trait is no longer generic. (#2387)
|
||||
- The `I2c::new_with_timeout` constructors have been removed (#2361)
|
||||
- `I2c::new()` no longer takes `frequency` and pins as parameters. (#2477)
|
||||
- The `spi::master::HalfDuplexReadWrite` trait has been removed. (#2373)
|
||||
- The `Spi::with_pins` methods have been removed. (#2373)
|
||||
- The `Spi::new_half_duplex` constructor have been removed. (#2373)
|
||||
- The `HalfDuplexMode` and `FullDuplexMode` parameters have been removed from `Spi`. (#2373)
|
||||
- Removed the output pin type parameter from `ledc::{Channel, ChannelIFace}` (#2388)
|
||||
- Removed the output pin type parameter from `mcpwm::operator::{PwmPin, LinkedPins}` (#2388)
|
||||
- Removed the output pin type parameter from `parl_io::{ClkOutPin, ClkInPin, RxClkInPin}` (#2388)
|
||||
- Removed the valid pin type parameter from `parl_io::{TxPinConfigWithValidPin, RxPinConfigWithValidPin}` (#2388)
|
||||
- Removed the pin type parameters from `parl_io::{TxOneBit, TxTwoBits, TxFourBits, TxEightBits, TxSixteenBits}` (#2388)
|
||||
- Removed the pin type parameters from `parl_io::{RxOneBit, RxTwoBits, RxFourBits, RxEightBits, RxSixteenBits}` (#2388)
|
||||
- Removed the pin type parameters from `lcd_cam::lcd::i8080::{TxEightBits, TxSixteenBits}` (#2388)
|
||||
- Removed the pin type parameters from `lcd_cam::cam::{RxEightBits, RxSixteenBits}` (#2388)
|
||||
- Most of the async-specific constructors (`new_async`, `new_async_no_transceiver`) have been removed. (#2430)
|
||||
- The `configure_for_async` DMA functions have been removed (#2430)
|
||||
- The `Uart::{change_baud, change_stop_bits}` functions have been removed (#2449)
|
||||
- `gpio::{Input, Output, OutputOpenDrain, Flex, GpioPin}::{peripheral_input, into_peripheral_output}` have been removed. (#2418)
|
||||
- The `GpioEtm` prefix has been removed from `gpio::etm` types (#2427)
|
||||
- The `TimerEtm` prefix has been removed from `timer::timg::etm` types (#2427)
|
||||
- The `SysTimerEtm` prefix has been removed from `timer::systimer::etm` types (#2427)
|
||||
- The `GpioEtmEventRising`, `GpioEtmEventFalling`, `GpioEtmEventAny` types have been replaced with `Event` (#2427)
|
||||
- The `TaskSet`, `TaskClear`, `TaskToggle` types have been replaced with `Task` (#2427)
|
||||
- `{Spi, SpiDma, SpiDmaBus}` configuration methods (#2448)
|
||||
- `Io::new_with_priority` and `Io::new_no_bind_interrupt`. (#2486)
|
||||
- `parl_io::{no_clk_pin(), NoClkPin}` (#2531)
|
||||
- Removed `get_core` function in favour of `Cpu::current` (#2533)
|
||||
|
||||
- Removed `uart::Config` setters and `symbol_length`. (#2847)
|
||||
|
||||
## [0.21.1]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Restored blocking `embedded_hal` compatibility for async I2C driver (#2343)
|
||||
- I2c::transaction is now able to transmit data of arbitrary length (#2481)
|
||||
|
||||
## [0.21.0]
|
||||
|
||||
### Added
|
||||
|
||||
- Introduce traits for the DMA buffer objects (#1976, #2213)
|
||||
- Implement `embedded-hal` output pin traits for `NoPin` (#2019, #2133)
|
||||
- Added `esp_hal::init` to simplify HAL initialisation (#1970, #1999)
|
||||
- Added GpioPin::degrade to create ErasePins easily. Same for AnyPin by accident. (#2075)
|
||||
- Added missing functions to `Flex`: `unlisten`, `is_interrupt_set`, `wakeup_enable`, `wait_for_high`, `wait_for_low`, `wait_for_rising_edge`, `wait_for_falling_edge`, `wait_for_any_edge`. (#2075)
|
||||
- `Flex` now implements `Wait`. (#2075)
|
||||
- Added sleep and wakeup support for esp32c2 (#1922)
|
||||
- `Input`, `Output`, `OutputOpenDrain` and `Flex` now implement `Peripheral`. (#2094)
|
||||
- Previously unavailable memory is available via `.dram2_uninit` section (#2079)
|
||||
- You can now use `Input`, `Output`, `OutputOpenDrain` and `Flex` pins as EXTI and RTCIO wakeup sources (#2095)
|
||||
- Added `Rtc::set_current_time` to allow setting RTC time, and `Rtc::current_time` to getting RTC time while taking into account boot time (#1883)
|
||||
- Added APIs to allow connecting signals through the GPIO matrix. (#2128)
|
||||
- Allow I8080 transfers to be cancelled on the spot (#2191)
|
||||
- Implement `TryFrom<u32>` for `ledc::timer::config::Duty` (#1984)
|
||||
- Expose `RtcClock::get_xtal_freq` and `RtcClock::get_slow_freq` publically for all chips (#2183)
|
||||
- TWAI support for ESP32-H2 (#2199)
|
||||
- Make `DmaDescriptor` methods public (#2237)
|
||||
- Added a way to configure watchdogs in `esp_hal::init` (#2180)
|
||||
- Introduce `DmaRxStreamBuf` (#2242)
|
||||
- Implement `embedded_hal_async::delay::DelayNs` for `TIMGx` timers (#2084)
|
||||
- Added `Efuse::read_bit` (#2259)
|
||||
- Limited SPI slave support for ESP32 (Modes 1 and 3 only) (#2278)
|
||||
- Added `Rtc::disable_rom_message_printing` (S3 and H2 only) (#2280)
|
||||
- Added `esp_hal::time::{Duration, Instant}` (#2304)
|
||||
|
||||
### Changed
|
||||
|
||||
- Make saving and restoring SHA digest state an explicit operation (#2049)
|
||||
- Reordered RX-TX pairs in all APIs to be consistent (#2074)
|
||||
- Make saving and restoring SHA digest state an explicit operation (#2049)
|
||||
- `Delay::new()` is now a `const` function (#1999)
|
||||
- `Input`, `Output`, `OutputOpenDrain` and `Flex` are now type-erased by default. Use the new `new_typed` constructor to keep using the ZST pin types. (#2075)
|
||||
- To avoid confusion with the `Rtc::current_time` wall clock time APIs, we've renamed `esp_hal::time::current_time` to `esp_hal::time::now`. (#2091)
|
||||
- Renamed `touch::Continous` to `touch::Continuous`. (#2094)
|
||||
- Faster SHA (#2112)
|
||||
- The (previously undocumented) `ErasedPin` enum has been replaced with the `ErasedPin` struct. (#2094)
|
||||
- Renamed and merged `Rtc::get_time_us` and `Rtc::get_time_ms` into `Rtc::time_since_boot` (#1883)
|
||||
- ESP32: Added support for touch sensing on GPIO32 and 33 (#2109)
|
||||
- Removed gpio pin generics from I8080 driver type. (#2171)
|
||||
- I8080 driver now decides bus width at transfer time rather than construction time. (#2171)
|
||||
- Migrate the I8080 driver to a move based API (#2191)
|
||||
- Replaced `AnyPin` with `InputSignal` and `OutputSignal` and renamed `ErasedPin` to `AnyPin` (#2128)
|
||||
- Replaced the `ErasedTimer` enum with the `AnyTimer` struct. (#2144)
|
||||
- `Camera` and `AesDma` now support erasing the DMA channel type (#2258)
|
||||
- Changed the parameters of `Spi::with_pins` to no longer be optional (#2133)
|
||||
- Renamed `DummyPin` to `NoPin` and removed all internal logic from it. (#2133)
|
||||
- The `NO_PIN` constant has been removed. (#2133)
|
||||
- MSRV bump to 1.79 (#2156)
|
||||
- Allow handling interrupts while trying to lock critical section on multi-core chips. (#2197)
|
||||
- Migrate `Camera` to a move based API (#2242).
|
||||
- Removed the PS-RAM related features, replaced by `quad-psram`/`octal-psram`, `init_psram` takes a configuration parameter, it's now possible to auto-detect PS-RAM size (#2178)
|
||||
- `EspTwaiFrame` constructors now accept any type that converts into `esp_hal::twai::Id` (#2207)
|
||||
- Change `DmaTxBuf` to support PSRAM on `esp32s3` (#2161)
|
||||
- I2c `transaction` is now also available as a inherent function, lift size limit on `write`,`read` and `write_read` (#2262)
|
||||
- SPI transactions are now cancelled if the transfer object (or async Future) is dropped. (#2216)
|
||||
- The DMA channel types have been removed from peripherals (#2261)
|
||||
- `I2C` driver renamed to `I2c` (#2320)
|
||||
- The GPIO pins are now accessible via `Peripherals` and are no longer part of the `Io` struct (#2508)
|
||||
- `dma::{ChannelRx, ChannelTx}` now have a `Mode` type parameter (#2519)
|
||||
|
||||
### Fixed
|
||||
|
||||
- SHA driver can now be safely used in multiple contexts concurrently (#2049)
|
||||
- Fixed an issue with DMA transfers potentially not waking up the correct async task (#2065)
|
||||
- Fixed an issue with LCD_CAM i8080 where it would send double the clocks in 16bit mode (#2085)
|
||||
- Fix i2c embedded-hal transaction (#2028)
|
||||
- Fix some inconsistencies in DMA interrupt bits (#2169)
|
||||
- Fix SPI DMA alternating `write` and `read` for ESP32 and ESP32-S2 (#2131)
|
||||
- Fix I2C ending up in a state when only re-creating the peripheral makes it useable again (#2141)
|
||||
- Fix `SpiBus::transfer` transferring data twice in some cases (#2159)
|
||||
- Fixed UART freezing when using `RcFast` clock source on ESP32-C2/C3 (#2170)
|
||||
- I2S: on ESP32 and ESP32-S2 data is now output to the right (WS=1) channel first. (#2194)
|
||||
- SPI: Fixed an issue where unexpected data was written outside of the read buffer (#2179)
|
||||
- SPI: Fixed an issue where `wait` has returned before the DMA has finished writing the memory (#2179)
|
||||
- SPI: Fixed an issue where repeated calls to `dma_transfer` may end up looping indefinitely (#2179)
|
||||
- SPI: Fixed an issue that prevented correctly reading the first byte in a transaction (#2179)
|
||||
- SPI: ESP32: Send address with correct data mode even when no data is sent. (#2231)
|
||||
- SPI: ESP32: Allow using QSPI mode on SPI3. (#2245)
|
||||
- PARL_IO: Fixed an issue that caused garbage to be output at the start of some requests (#2211)
|
||||
- TWAI on ESP32 (#2207)
|
||||
- TWAI should no longer panic when receiving a non-compliant frame (#2255)
|
||||
- OneShotTimer: fixed `delay_nanos` behaviour (#2256)
|
||||
- Fixed unsoundness around `Efuse` (#2259)
|
||||
- Empty I2C writes to unknown addresses now correctly fail with `AckCheckFailed`. (#2506)
|
||||
|
||||
### Removed
|
||||
|
||||
- Removed `digest::Digest` implementation from SHA (#2049)
|
||||
- Removed `NoPinType` in favour of `DummyPin`. (#2068)
|
||||
- Removed the `async`, `embedded-hal-02`, `embedded-hal`, `embedded-io`, `embedded-io-async`, and `ufmt` features (#2070)
|
||||
- Removed the `GpioN` type aliasses. Use `GpioPin<N>` instead. (#2073)
|
||||
- Removed `Peripherals::take`. Use `esp_hal::init` to obtain `Peripherals` (#1999)
|
||||
- Removed `AnyInputOnlyPin` in favour of `AnyPin`. (#2071)
|
||||
- Removed the following functions from `GpioPin`: `is_high`, `is_low`, `set_high`, `set_low`, `set_state`, `is_set_high`, `is_set_low`, `toggle`. (#2094)
|
||||
- Removed `Rtc::get_time_raw` (#1883)
|
||||
- Removed `_with_default_pins` UART constructors (#2132)
|
||||
- Removed transfer methods `send`, `send_dma` and `send_dma_async` from `I8080` (#2191)
|
||||
- Removed `uart::{DefaultRxPin, DefaultTxPin}` (#2132)
|
||||
- Removed `PcntSource` and `PcntInputConfig`. (#2134)
|
||||
- Removed the `place-spi-driver-in-ram` feature, this is now enabled via [esp-config](https://docs.rs/esp-config) (#2156)
|
||||
- Removed `esp_hal::spi::slave::prelude` (#2260)
|
||||
- Removed `esp_hal::spi::slave::WithDmaSpiN` traits (#2260)
|
||||
- The `WithDmaAes` trait has been removed (#2261)
|
||||
- The `I2s::new_i2s1` constructor has been removed (#2261)
|
||||
- `Peripherals.GPIO` has been removed (#2508)
|
||||
|
||||
## [0.20.1] - 2024-08-30
|
||||
|
||||
### Fixed
|
||||
|
||||
- A build issue when including doc comment prelude (#2040)
|
||||
|
||||
## [0.20.0] - 2024-08-29
|
||||
|
||||
### Added
|
||||
|
||||
- Introduce DMA buffer objects (#1856, #1985)
|
||||
- Added new `Io::new_no_bind_interrupt` constructor (#1861)
|
||||
- Added touch pad support for esp32 (#1873, #1956)
|
||||
- Allow configuration of period updating method for MCPWM timers (#1898)
|
||||
- Add self-testing mode for TWAI peripheral. (#1929)
|
||||
- Added a `PeripheralClockControl::reset` to the driver constructors where missing (#1893)
|
||||
- Added `digest::Digest` implementation to SHA (#1908)
|
||||
- Added `debugger::debugger_connected`. (#1961)
|
||||
- DMA: don't require `Sealed` to implement `ReadBuffer` and `WriteBuffer` (#1921)
|
||||
- Allow DMA to/from psram for esp32s3 (#1827)
|
||||
- Added missing methods to `SpiDmaBus` (#2016).
|
||||
- PARL_IO use ReadBuffer and WriteBuffer for Async DMA (#1996)
|
||||
|
||||
### Changed
|
||||
|
||||
- Peripheral driver constructors don't take `InterruptHandler`s anymore. Use `set_interrupt_handler` to explicitly set the interrupt handler now. (#1819)
|
||||
- Migrate SPI driver to use DMA buffer objects (#1856, #1985)
|
||||
- Use the peripheral ref pattern for `OneShotTimer` and `PeriodicTimer` (#1855)
|
||||
- Improve SYSTIMER API (#1871)
|
||||
- SHA driver now use specific structs for the hashing algorithm instead of a parameter. (#1908)
|
||||
- Remove `fn free(self)` in HMAC which goes against esp-hal API guidelines (#1972)
|
||||
- `AnyPin`, `AnyInputOnyPin` and `DummyPin` are now accessible from `gpio` module (#1918)
|
||||
- Changed the RSA modular multiplication API to be consistent across devices (#2002)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Improve error detection in the I2C driver (#1847)
|
||||
- Fix I2S async-tx (#1833)
|
||||
- Fix PARL_IO async-rx (#1851)
|
||||
- SPI: Clear DMA interrupts before (not after) DMA starts (#1859)
|
||||
- SPI: disable and re-enable MISO and MOSI in `start_transfer_dma`, `start_read_bytes_dma` and `start_write_bytes_dma` accordingly (#1894)
|
||||
- TWAI: GPIO pins are not configured as input and output (#1906)
|
||||
- ESP32C6: Make ADC usable after TRNG deinicialization (#1945)
|
||||
- We should no longer generate 1GB .elf files for ESP32C2 and ESP32C3 (#1962)
|
||||
- Reset peripherals in driver constructors where missing (#1893, #1961)
|
||||
- Fixed ESP32-S2 systimer interrupts (#1979)
|
||||
- Software interrupt 3 is no longer available when it is required by `esp-hal-embassy`. (#2011)
|
||||
- ESP32: Fixed async RSA (#2002)
|
||||
|
||||
### Removed
|
||||
|
||||
- This package no longer re-exports the `esp_hal_procmacros::main` macro (#1828)
|
||||
- The `AesFlavour` trait no longer has the `ENCRYPT_MODE`/`DECRYPT_MODE` associated constants (#1849)
|
||||
- Removed `FlashSafeDma` (#1856)
|
||||
- Remove redundant WithDmaSpi traits (#1975)
|
||||
- `IsFullDuplex` and `IsHalfDuplex` traits (#1985)
|
||||
|
||||
## [0.19.0] - 2024-07-15
|
||||
|
||||
### Added
|
||||
|
||||
- uart: Added `with_cts`/`with_rts`s methods to configure CTS, and RTS pins (#1592)
|
||||
- uart: Constructors now require TX and RX pins (#1592)
|
||||
- uart: Added `Uart::new_with_default_pins` constructor (#1592)
|
||||
- uart: Added `UartTx` and `UartRx` constructors (#1592)
|
||||
- Add Flex / AnyFlex GPIO pin driver (#1659)
|
||||
- Add new `DmaError::UnsupportedMemoryRegion` - used memory regions are checked when preparing a transfer now (#1670)
|
||||
- Add DmaTransactionTxOwned, DmaTransactionRxOwned, DmaTransactionTxRxOwned, functions to do owning transfers added to SPI half-duplex (#1672)
|
||||
- uart: Implement `embedded_io::ReadReady` for `Uart` and `UartRx` (#1702)
|
||||
- ESP32-S3: Expose optional HSYNC input in LCD_CAM (#1707)
|
||||
- ESP32-S3: Add async support to the LCD_CAM I8080 driver (#1834)
|
||||
- ESP32-C6: Support lp-core as wake-up source (#1723)
|
||||
- Add support for GPIO wake-up source (#1724)
|
||||
- gpio: add DummyPin (#1769)
|
||||
- dma: add Mem2Mem to support memory to memory transfer (#1738)
|
||||
- Add `uart` wake source (#1727)
|
||||
- `#[ram(persistent)]` option to replace the unsound `uninitialized` option (#1677)
|
||||
- uart: Make `rx_timeout` optional in Config struct (#1759)
|
||||
- Add interrupt related functions to `PeriodicTimer`/`OneShotTimer`, added `ErasedTimer` (#1753)
|
||||
- Added blocking `read_bytes` method to `Uart` and `UartRx` (#1784)
|
||||
- Add method to expose `InputPin::is_interrupt_set` in `Input<InputPin>` for use in interrupt handlers (#1829)
|
||||
|
||||
### Fixed
|
||||
|
||||
- ESP32-S3: Fix DMA waiting check in LCD_CAM (#1707)
|
||||
- TIMG: Fix interrupt handler setup (#1714)
|
||||
- Fix `sleep_light` for ESP32-C6 (#1720)
|
||||
- ROM Functions: Fix address of `ets_update_cpu_frequency_rom` (#1722)
|
||||
- Fix `regi2c_*` functions for `esp32h2` (#1737)
|
||||
- Improved `#[ram(zeroed)]` soundness by adding a `bytemuck::Zeroable` type bound (#1677)
|
||||
- EESP32-S2 / ESP32-S3: Fix UsbDm and UsbDp for Gpio19 and Gpio20
|
||||
- Fix reading/writing small buffers via SPI master async dma (#1760)
|
||||
- Remove unnecessary delay in rtc_ctnl (#1794)
|
||||
|
||||
### Changed
|
||||
|
||||
- Refactor `Dac1`/`Dac2` drivers into a single `Dac` driver (#1661)
|
||||
- esp-hal-embassy: make executor code optional (but default) again
|
||||
- Improved interrupt latency on RISC-V based chips (#1679)
|
||||
- `esp_wifi::initialize` no longer requires running maximum CPU clock, instead check it runs above 80MHz. (#1688)
|
||||
- Move DMA descriptors from DMA Channel to each individual peripheral driver. (#1719)
|
||||
- Allow users to easily name DMA channels (#1770)
|
||||
- Support DMA chunk sizes other than the default 4092 (#1758)
|
||||
- Improved interrupt latency on Xtensa based chips (#1735)
|
||||
- Improve PCNT api (#1765)
|
||||
|
||||
### Removed
|
||||
|
||||
- uart: Removed `configure_pins` methods (#1592)
|
||||
- Removed `DmaError::Exhausted` error by improving the implementation of the `pop` function (#1664)
|
||||
- Unsound `#[ram(uninitialized)]` option in favor of the new `persistent` option (#1677)
|
||||
|
||||
## [0.18.0] - 2024-06-04
|
||||
|
||||
### Added
|
||||
|
||||
- i2c: implement `I2C:transaction` for `embedded-hal` and `embedded-hal-async` (#1505)
|
||||
- spi: implement `with_bit_order` (#1537)
|
||||
- ESP32-PICO-V3-02: Initial support (#1155)
|
||||
- `time::current_time` API (#1503)
|
||||
- ESP32-S3: Add LCD_CAM Camera driver (#1483)
|
||||
- `embassy-usb` support (#1517)
|
||||
- SPI Slave support for ESP32-S2 (#1562)
|
||||
- Add new generic `OneShotTimer` and `PeriodicTimer` drivers, plus new `Timer` trait which is implemented for `TIMGx` and `SYSTIMER` (#1570)
|
||||
- Feature: correct `TRNG` mechanism #1804
|
||||
|
||||
### Fixed
|
||||
|
||||
- i2c: i2c1_handler used I2C0 register block by mistake (#1487)
|
||||
- Removed ESP32 specific code for resolutions > 16 bit in ledc embedded_hal::pwm max_duty_cycle function. (#1441)
|
||||
- Fixed division by zero in ledc embedded_hal::pwm set_duty_cycle function and converted to set_duty_hw instead of set_duty to eliminate loss of granularity. (#1441)
|
||||
- Embassy examples now build on stable (#1485)
|
||||
- Fix delay on esp32h2 (#1535)
|
||||
- spi: fix dma wrong mode when using eh1 blocking api (#1541)
|
||||
- uart: make `uart::UartRx::read_byte` public (#1547)
|
||||
- Fix async serial-usb-jtag (#1561)
|
||||
- Feeding `RWDT` now actually works (#1645)
|
||||
|
||||
### Changed
|
||||
|
||||
- Removed unneeded generic parameters on `Usb` (#1469)
|
||||
- Created virtual peripherals for CPU control and radio clocks, rather than splitting them from `SYSTEM` (#1428)
|
||||
- `IO`, `ADC`, `DAC`, `RTC*`, `LEDC`, `PWM` and `PCNT` drivers have been converted to camel case format (#1473)
|
||||
- RNG is no longer TRNG, the `CryptoRng` implementation has been removed. To track this being re-added see #1499 (#1498)
|
||||
- Make software interrupts shareable (#1500)
|
||||
- The `SystemParts` struct has been renamed to `SystemControl`, and now has a constructor which takes the `SYSTEM` peripheral (#1495)
|
||||
- Timer abstraction: refactor `systimer` and `timer` modules into a common `timer` module (#1527)
|
||||
- Removed the `embassy-executor-thread` and `embassy-executor-interrupt` features, they are now enabled by default when `embassy` is enabled. (#1485)
|
||||
- Software interrupt 3 is now used instead of software interrupt 0 on the thread aware executor on multicore systems (#1485)
|
||||
- Timer abstraction: refactor `systimer` and `timer` modules into a common `timer` module (#1527)
|
||||
- Refactoring of GPIO module, have drivers for Input,Output,OutputOpenDrain, all drivers setup their GPIOs correctly (#1542)
|
||||
- DMA transactions are now found in the `dma` module (#1550)
|
||||
- Remove unnecessary generics from PARL_IO driver (#1545)
|
||||
- Use `Level enum` in GPIO constructors instead of plain bools (#1574)
|
||||
- rmt: make ChannelCreator public (#1597)
|
||||
|
||||
### Removed
|
||||
|
||||
- Removed the `SystemExt` trait (#1495)
|
||||
- Removed the `GpioExt` trait (#1496)
|
||||
- Embassy support (and all related features) has been removed, now available in the `esp-hal-embassy` package instead (#1595)
|
||||
|
||||
## [0.17.0] - 2024-04-18
|
||||
|
||||
### Added
|
||||
|
||||
- Add `ADC::read_blocking` to xtensa chips (#1293)
|
||||
- ESP32-C6 / ESP32-H2: Implement `ETM` for general purpose timers (#1274)
|
||||
- `interrupt::enable` now has a direct CPU enable counter part, `interrupt::enable_direct` (#1310)
|
||||
- `Delay::delay(time: fugit::MicrosDurationU64)`
|
||||
- Added async support for TWAI (#1320)
|
||||
- Add TWAI support for ESP32-C6 (#1323)
|
||||
- `GpioPin::steal` unsafe API (#1363)
|
||||
- Inherent implementions of GPIO pin `set_low`, `is_low`, etc.
|
||||
- Warn users when attempting to build using the `dev` profile (#1420)
|
||||
- Async uart now reports interrupt errors(overflow, glitch, frame error, parity) back to user of read/write. uart clock decimal part configured for c2,c3,s3 (#1168, #1445)
|
||||
- Add mechanism to configure UART source clock (#1416)
|
||||
- `GpioPin` got a function `set_state(bool)` (#1462)
|
||||
- Add definitions of external USB PHY peripheral I/O signals
|
||||
- Expose e-hal ErrorKind::NoAcknowledge in I2C driver (#1454)
|
||||
- Add remaining peripheral signals for LCD_CAM (#1466)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Reserve `esp32` ROM stacks to prevent the trashing of dram2 section (#1289)
|
||||
- Fixing `esp-wifi` + `TRNG` issue on `ESP32-S2` (#1272)
|
||||
- Fixed core1 startup using the wrong stack on the esp32 and esp32s3 (#1286).
|
||||
- ESP32: Apply fix for Errata 3.6 in all the places necessary. (#1315)
|
||||
- ESP32 & ESP32-S2: Fix I²C frequency (#1306)
|
||||
- UART's TX/RX FIFOs are now cleared during initialization (#1344)
|
||||
- Fixed `LCD_CAM I8080` driver potentially sending garbage to display (#1301)
|
||||
- The TWAI driver can now be used without requiring the `embedded-hal` traits (#1355)
|
||||
- USB pullup/pulldown now gets properly cleared and does not interfere anymore on esp32c3 and esp32s3 (#1244)
|
||||
- Fixed GPIO counts so that using async code with the higher GPIO number should no longer panic (#1361, #1362)
|
||||
- ESP32/ESP32-S2: Wait for I2S getting out of TX_IDLE when starting a transfer (#1375)
|
||||
- Fixed writes to SPI not flushing before attempting to write, causing corrupted writes (#1381)
|
||||
- fix AdcConfig::adc_calibrate for xtensa targets (#1379)
|
||||
- Fixed a divide by zero panic when setting the LEDC duty cycle to 0 with `SetDutyCycle::set_duty_cycle` (#1403)
|
||||
- Support 192 and 256-bit keys for AES (#1316)
|
||||
- Fixed MCPWM DeadTimeCfg bit values (#1378)
|
||||
- ESP32 LEDC `set_duty_cycle` used HighSpeedChannel for LowSpeedChannel (#1457)
|
||||
|
||||
### Changed
|
||||
|
||||
- TIMG: Allow use without the embedded-hal-02 traits in scope (#1367)
|
||||
- DMA: use channel clusters
|
||||
- Remove `Ext32` and `RateExtU64` from prelude
|
||||
- Prefer mutable references over moving for DMA transactions (#1238)
|
||||
- Support runtime interrupt binding, adapt GPIO driver (#1231)
|
||||
- Renamed `eh1` feature to `embedded-hal`, feature-gated `embedded-hal@0.2.x` trait implementations (#1273)
|
||||
- Enable `embedded-hal` feature by default, instead of the `embedded-hal-02` feature (#1313)
|
||||
- `Uart` structs now take a `Mode` parameter which defines how the driver is initialized (#1294)
|
||||
- `Rmt` can be created in async or blocking mode. The blocking constructor takes an optional interrupt handler argument. (#1341)
|
||||
- All `Instance` traits are now sealed, and can no longer be implemented for arbitrary types (#1346)
|
||||
- DMA channels can/have to be explicitly created for async or blocking drivers, added `set_interrupt_handler` to DMA channels, SPI, I2S, PARL_IO, don't enable interrupts on startup for DMA, I2S, PARL_IO, GPIO (#1300)
|
||||
- UART: Rework `change_baud` so it is possible to set baud rate even after instantiation (#1350)
|
||||
- Runtime ISR binding for SHA,ECC and RSA (#1354)
|
||||
- Runtime ISR binding for I2C (#1376)
|
||||
- `UsbSerialJtag` can be created in async or blocking mode. The blocking constructor takes an optional interrupt handler argument (#1377)
|
||||
- SYSTIMER and TIMG instances can now be created in async or blocking mode (#1348)
|
||||
- Runtime ISR binding for TWAI (#1384)
|
||||
- ESP32-C6: The `gpio::lp_gpio` module has been renamed to `gpio::lp_io` to match the peripheral name (#1397)
|
||||
- Runtime ISR binding for assist_debug (#1395)
|
||||
- Runtime ISR binding for software interrupts, software interrupts are split now, interrupt-executor takes the software interrupt to use, interrupt-executor is easier to use (#1398)
|
||||
- PCNT: Runtime ISR binding (#1396)
|
||||
- Runtime ISR binding for RTC (#1405)
|
||||
- Improve MCPWM DeadTimeCfg API (#1378)
|
||||
- `SystemTimer`'s `Alarm` methods now require `&mut self` (#1455)
|
||||
|
||||
### Removed
|
||||
|
||||
- Remove package-level type exports (#1275)
|
||||
- Removed `direct-vectoring` & `interrupt-preemption` features, as they are now enabled by default (#1310)
|
||||
- Removed the `rt` and `vectored` features (#1380)
|
||||
- Remove partial support for the ESP32-P4 (#1461)
|
||||
|
||||
## [0.16.1] - 2024-03-12
|
||||
|
||||
- Resolved an issue with the `defmt` dependency/feature (#1264)
|
||||
@ -35,6 +629,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Ensuring that the random number generator is TRNG. (#1200)
|
||||
- ESP32-C6: Add timer wakeup source for deepsleep (#1201)
|
||||
- Introduce `InterruptExecutor::spawner()` (#1211)
|
||||
- Add `InterruptHandler` struct, which couples interrupt handlers and their priority together (#1299)
|
||||
|
||||
### Fixed
|
||||
|
||||
@ -62,6 +657,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Auto detect crystal frequency based on `RtcClock::estimate_xtal_frequency()` (#1165)
|
||||
- ESP32-S3: Configure 32k ICACHE (#1169)
|
||||
- Lift the minimal buffer size requirement for I2S (#1189)
|
||||
- Replaced `SystemTimer::TICKS_PER_SEC` with `SystemTimer::ticks_per_sec()` (#1981)
|
||||
|
||||
### Removed
|
||||
|
||||
@ -456,6 +1052,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [0.1.0] - 2022-08-05
|
||||
|
||||
[Unreleased]: https://github.com/esp-rs/esp-hal/compare/v0.22.0...HEAD
|
||||
[0.22.0]: https://github.com/esp-rs/esp-hal/compare/v0.21.1...v0.22.0
|
||||
[0.21.1]: https://github.com/esp-rs/esp-hal/compare/v0.21.0...v0.21.1
|
||||
[0.21.0]: https://github.com/esp-rs/esp-hal/compare/v0.20.1...v0.21.0
|
||||
[0.20.1]: https://github.com/esp-rs/esp-hal/compare/v0.20.0...v0.20.1
|
||||
[0.20.0]: https://github.com/esp-rs/esp-hal/compare/v0.19.0...v0.20.0
|
||||
[0.19.0]: https://github.com/esp-rs/esp-hal/compare/v0.18.0...v0.19.0
|
||||
[0.18.0]: https://github.com/esp-rs/esp-hal/compare/v0.17.0...v0.18.0
|
||||
[0.17.0]: https://github.com/esp-rs/esp-hal/compare/v0.16.1...v0.17.0
|
||||
[0.16.1]: https://github.com/esp-rs/esp-hal/compare/v0.16.0...v0.16.1
|
||||
[0.16.0]: https://github.com/esp-rs/esp-hal/compare/v0.15.0...v0.16.0
|
||||
[0.15.0]: https://github.com/esp-rs/esp-hal/compare/v0.14.1...v0.15.0
|
||||
|
||||
@ -1,86 +1,96 @@
|
||||
[package]
|
||||
name = "esp-hal"
|
||||
version = "0.16.1"
|
||||
version = "0.22.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.76.0"
|
||||
rust-version = "1.83.0"
|
||||
description = "Bare-metal HAL for Espressif devices"
|
||||
documentation = "https://docs.esp-rs.org/esp-hal/"
|
||||
keywords = ["embedded", "embedded-hal", "esp32", "espressif", "hal"]
|
||||
categories = ["embedded", "hardware-support", "no-std"]
|
||||
repository = "https://github.com/esp-rs/esp-hal"
|
||||
license = "MIT OR Apache-2.0"
|
||||
links = "esp-hal"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
default-target = "riscv32imac-unknown-none-elf"
|
||||
features = ["eh1", "esp32c6"]
|
||||
features = ["esp32c6"]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[dependencies]
|
||||
bitflags = "2.4.2"
|
||||
bitfield = "0.14.0"
|
||||
bitflags = "2.6.0"
|
||||
bytemuck = "1.19.0"
|
||||
bitfield = "0.17.0"
|
||||
cfg-if = "1.0.0"
|
||||
critical-section = "1.1.2"
|
||||
defmt = { version = "0.3.6", optional = true }
|
||||
document-features = "0.2.8"
|
||||
embassy-executor = { version = "0.5.0", optional = true }
|
||||
embassy-futures = { version = "0.1.1", optional = true }
|
||||
embassy-sync = { version = "0.5.0", optional = true }
|
||||
embassy-time-driver = { version = "0.1.0", optional = true }
|
||||
chrono = { version = "0.4.38", default-features = false }
|
||||
critical-section = "1.2.0"
|
||||
defmt = { version = "0.3.8", optional = true }
|
||||
delegate = "0.13.1"
|
||||
digest = { version = "0.10.7", default-features = false, optional = true }
|
||||
document-features = "0.2.10"
|
||||
embassy-embedded-hal = { version = "0.2.0", optional = true }
|
||||
embassy-futures = "0.1.1"
|
||||
embassy-sync = "0.6.1"
|
||||
embassy-usb-driver = { version = "0.1.0", optional = true }
|
||||
embassy-usb-synopsys-otg = { version = "0.2.0", optional = true }
|
||||
embedded-can = { version = "0.4.1", optional = true }
|
||||
embedded-dma = "0.2.0"
|
||||
embedded-hal = { version = "0.2.7", features = ["unproven"] }
|
||||
embedded-hal-1 = { version = "1.0.0", optional = true, package = "embedded-hal" }
|
||||
embedded-hal-async = { version = "1.0.0", optional = true }
|
||||
embedded-hal-nb = { version = "1.0.0", optional = true }
|
||||
embedded-hal = "1.0.0"
|
||||
embedded-hal-async = "1.0.0"
|
||||
embedded-hal-nb = "1.0.0"
|
||||
embedded-io = { version = "0.6.1", optional = true }
|
||||
embedded-io-async = { version = "0.6.1", optional = true }
|
||||
enumset = "1.1.3"
|
||||
esp-synopsys-usb-otg = { version = "0.4.0", optional = true, features = ["fs", "esp32sx"] }
|
||||
enumset = "1.1.5"
|
||||
esp-build = { version = "0.1.0", path = "../esp-build" }
|
||||
esp-synopsys-usb-otg = { version = "0.4.2", optional = true, features = ["fs", "esp32sx"] }
|
||||
fugit = "0.3.7"
|
||||
log = { version = "0.4.21", optional = true }
|
||||
instability = "0.3.6"
|
||||
log = { version = "0.4.22", optional = true }
|
||||
nb = "1.1.0"
|
||||
paste = "1.0.14"
|
||||
portable-atomic = { version = "1.6.0", default-features = false }
|
||||
procmacros = { version = "0.9.0", features = ["enum-dispatch", "ram"], package = "esp-hal-procmacros", path = "../esp-hal-procmacros" }
|
||||
riscv = { version = "0.11.1", optional = true }
|
||||
strum = { version = "0.26.1", default-features = false, features = ["derive"] }
|
||||
paste = "1.0.15"
|
||||
portable-atomic = { version = "1.9.0", default-features = false }
|
||||
procmacros = { version = "0.15.0", package = "esp-hal-procmacros", path = "../esp-hal-procmacros" }
|
||||
strum = { version = "0.26.3", default-features = false, features = ["derive"] }
|
||||
void = { version = "1.0.2", default-features = false }
|
||||
usb-device = { version = "0.3.2", optional = true }
|
||||
rand_core = "0.6.4"
|
||||
ufmt-write = { version = "0.1.0", optional = true }
|
||||
xtensa-lx = { version = "0.9.0", optional = true }
|
||||
ufmt-write = "0.1.0"
|
||||
|
||||
# IMPORTANT:
|
||||
# Each supported device MUST have its PAC included below along with a
|
||||
# corresponding feature.
|
||||
esp32 = { version = "0.29.0", features = ["critical-section"], optional = true }
|
||||
esp32c2 = { version = "0.18.0", features = ["critical-section"], optional = true }
|
||||
esp32c3 = { version = "0.21.0", features = ["critical-section"], optional = true }
|
||||
esp32c6 = { version = "0.12.0", features = ["critical-section"], optional = true }
|
||||
esp32h2 = { version = "0.8.0", features = ["critical-section"], optional = true }
|
||||
esp32p4 = { version = "0.1.0", features = ["critical-section"], optional = true }
|
||||
esp32s2 = { version = "0.20.0", features = ["critical-section"], optional = true }
|
||||
esp32s3 = { version = "0.24.0", features = ["critical-section"], optional = true }
|
||||
esp32 = { version = "0.34.0", features = ["critical-section", "rt"], git = "https://github.com/esp-rs/esp-pacs", rev = "ffbee35069d137ef611097d39fa7734c299ce124", optional = true }
|
||||
esp32c2 = { version = "0.23.0", features = ["critical-section", "rt"], git = "https://github.com/esp-rs/esp-pacs", rev = "ffbee35069d137ef611097d39fa7734c299ce124", optional = true }
|
||||
esp32c3 = { version = "0.26.0", features = ["critical-section", "rt"], git = "https://github.com/esp-rs/esp-pacs", rev = "ffbee35069d137ef611097d39fa7734c299ce124", optional = true }
|
||||
esp32c6 = { version = "0.17.0", features = ["critical-section", "rt"], git = "https://github.com/esp-rs/esp-pacs", rev = "ffbee35069d137ef611097d39fa7734c299ce124", optional = true }
|
||||
esp32h2 = { version = "0.13.0", features = ["critical-section", "rt"], git = "https://github.com/esp-rs/esp-pacs", rev = "ffbee35069d137ef611097d39fa7734c299ce124", optional = true }
|
||||
esp32s2 = { version = "0.25.0", features = ["critical-section", "rt"], git = "https://github.com/esp-rs/esp-pacs", rev = "ffbee35069d137ef611097d39fa7734c299ce124", optional = true }
|
||||
esp32s3 = { version = "0.29.0", features = ["critical-section", "rt"], git = "https://github.com/esp-rs/esp-pacs", rev = "ffbee35069d137ef611097d39fa7734c299ce124", optional = true }
|
||||
|
||||
[target.'cfg(target_arch = "riscv32")'.dependencies]
|
||||
esp-riscv-rt = { version = "0.7.0", optional = true, path = "../esp-riscv-rt" }
|
||||
riscv = { version = "0.12.1" }
|
||||
esp-riscv-rt = { version = "0.9.1", path = "../esp-riscv-rt" }
|
||||
critical-section = { version = "1.2.0", features = ["restore-state-u8"] }
|
||||
|
||||
[target.'cfg(target_arch = "xtensa")'.dependencies]
|
||||
xtensa-lx-rt = { version = "0.16.0", optional = true }
|
||||
xtensa-lx = { version = "0.9.0", path = "../xtensa-lx" }
|
||||
xtensa-lx-rt = { version = "0.17.2", path = "../xtensa-lx-rt" }
|
||||
critical-section = { version = "1.2.0", features = ["restore-state-u32"] }
|
||||
|
||||
[build-dependencies]
|
||||
basic-toml = "0.1.8"
|
||||
basic-toml = "0.1.9"
|
||||
cfg-if = "1.0.0"
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
esp-build = { version = "0.1.0", path = "../esp-build" }
|
||||
esp-metadata = { version = "0.4.0", path = "../esp-metadata" }
|
||||
esp-config = { version = "0.2.0", path = "../esp-config", features = ["build"] }
|
||||
serde = { version = "1.0.215", features = ["derive"] }
|
||||
|
||||
[features]
|
||||
default = ["rt", "vectored"]
|
||||
|
||||
riscv = ["dep:riscv", "critical-section/restore-state-u8", "esp-riscv-rt?/zero-bss"]
|
||||
xtensa = ["dep:xtensa-lx", "critical-section/restore-state-u32"]
|
||||
default = []
|
||||
|
||||
bluetooth = []
|
||||
|
||||
usb-otg = ["esp-synopsys-usb-otg", "usb-device"]
|
||||
usb-otg = ["dep:embassy-usb-driver", "dep:embassy-usb-synopsys-otg", "dep:esp-synopsys-usb-otg", "dep:usb-device"]
|
||||
|
||||
__esp_hal_embassy = []
|
||||
|
||||
## Enable debug features in the HAL (used for development).
|
||||
debug = [
|
||||
@ -89,134 +99,77 @@ debug = [
|
||||
"esp32c3?/impl-register-debug",
|
||||
"esp32c6?/impl-register-debug",
|
||||
"esp32h2?/impl-register-debug",
|
||||
"esp32p4?/impl-register-debug",
|
||||
"esp32s2?/impl-register-debug",
|
||||
"esp32s3?/impl-register-debug",
|
||||
]
|
||||
## Enable logging output using the `log` crate.
|
||||
log = ["dep:log"]
|
||||
## Enable runtime support.
|
||||
rt = [
|
||||
"dep:esp-riscv-rt",
|
||||
"dep:xtensa-lx-rt",
|
||||
"esp32?/rt",
|
||||
"esp32c2?/rt",
|
||||
"esp32c3?/rt",
|
||||
"esp32c6?/rt",
|
||||
"esp32h2?/rt",
|
||||
"esp32p4?/rt",
|
||||
"esp32s2?/rt",
|
||||
"esp32s3?/rt",
|
||||
]
|
||||
## Enable interrupt vectoring.
|
||||
vectored = ["procmacros/interrupt"]
|
||||
|
||||
#! ### Chip Support Feature Flags
|
||||
## Target the ESP32.
|
||||
esp32 = ["dep:esp32", "xtensa", "xtensa-lx/spin", "xtensa-lx-rt?/esp32"]
|
||||
## Target the ESP32-C2.
|
||||
esp32c2 = ["dep:esp32c2", "riscv", "portable-atomic/unsafe-assume-single-core"]
|
||||
## Target the ESP32-C3.
|
||||
esp32c3 = ["dep:esp32c3", "riscv", "portable-atomic/unsafe-assume-single-core", "rv-zero-rtc-bss"]
|
||||
## Target the ESP32-C6.
|
||||
esp32c6 = ["dep:esp32c6", "riscv", "procmacros/has-lp-core", "rv-zero-rtc-bss"]
|
||||
## Target the ESP32-H2.
|
||||
esp32h2 = ["dep:esp32h2", "riscv", "rv-zero-rtc-bss"]
|
||||
## Target the ESP32-P4.
|
||||
esp32p4 = ["dep:esp32p4", "riscv", "procmacros/has-lp-core", "rv-zero-rtc-bss"]
|
||||
## Target the ESP32-S2.
|
||||
esp32s2 = ["dep:esp32s2", "xtensa", "portable-atomic/critical-section", "procmacros/has-ulp-core", "xtensa-lx-rt?/esp32s2", "usb-otg"]
|
||||
## Target the ESP32-S3.
|
||||
esp32s3 = ["dep:esp32s3", "xtensa", "procmacros/has-ulp-core", "xtensa-lx/spin", "xtensa-lx-rt?/esp32s3", "usb-otg"]
|
||||
# Chip Support Feature Flags
|
||||
# Target the ESP32.
|
||||
esp32 = ["dep:esp32", "procmacros/rtc-slow", "xtensa-lx-rt/esp32"]
|
||||
# Target the ESP32-C2.
|
||||
esp32c2 = ["dep:esp32c2", "portable-atomic/unsafe-assume-single-core"]
|
||||
# Target the ESP32-C3.
|
||||
esp32c3 = ["dep:esp32c3", "esp-riscv-rt/rtc-ram", "portable-atomic/unsafe-assume-single-core"]
|
||||
# Target the ESP32-C6.
|
||||
esp32c6 = ["dep:esp32c6", "esp-riscv-rt/rtc-ram", "procmacros/has-lp-core"]
|
||||
# Target the ESP32-H2.
|
||||
esp32h2 = ["dep:esp32h2", "esp-riscv-rt/rtc-ram"]
|
||||
# Target the ESP32-S2.
|
||||
esp32s2 = ["dep:esp32s2", "portable-atomic/critical-section", "procmacros/has-ulp-core", "procmacros/rtc-slow", "usb-otg", "xtensa-lx-rt/esp32s2"]
|
||||
# Target the ESP32-S3.
|
||||
esp32s3 = ["dep:esp32s3", "procmacros/has-ulp-core", "procmacros/rtc-slow", "usb-otg", "xtensa-lx-rt/esp32s3"]
|
||||
|
||||
#! ### RISC-V Exclusive Feature Flags
|
||||
## Enable direct interrupt vectoring.
|
||||
direct-vectoring = ["esp-riscv-rt/direct-vectoring"]
|
||||
## Move the stack to start of RAM to get zero-cost stack overflow protection
|
||||
## (ESP32-C6 and ESPS32-H2 only!).
|
||||
flip-link = ["esp-riscv-rt/fix-sp"]
|
||||
## Enable interrupt preemption.
|
||||
interrupt-preemption = ["esp-riscv-rt/interrupt-preemption"]
|
||||
## Configuration for placing device drivers in the IRAM for faster access.
|
||||
place-spi-driver-in-ram = []
|
||||
## Initialize the `.data` section of memory.
|
||||
rv-init-data = ["esp-riscv-rt?/init-data", "esp-riscv-rt?/init-rw-text"]
|
||||
## Zero the `.bss` section of low-power memory.
|
||||
rv-zero-rtc-bss = ["esp-riscv-rt?/zero-rtc-fast-bss"]
|
||||
## Initialize the `.data` section of low-power memory.
|
||||
rv-init-rtc-data = ["esp-riscv-rt?/init-rtc-fast-data", "esp-riscv-rt?/init-rtc-fast-text"]
|
||||
|
||||
#! ### Trait Implementation Feature Flags
|
||||
## Enable support for asynchronous operation, with interfaces provided by
|
||||
## `embedded-hal-async` and `embedded-io-async`.
|
||||
async = [
|
||||
"embedded-hal-async",
|
||||
"eh1",
|
||||
"embassy-sync",
|
||||
"embassy-futures",
|
||||
"embedded-io",
|
||||
"embedded-io-async",
|
||||
]
|
||||
## Implement `defmt::Format` on certain types.
|
||||
defmt = [
|
||||
"dep:defmt",
|
||||
"embassy-executor?/defmt",
|
||||
"embassy-futures?/defmt",
|
||||
"embassy-sync?/defmt",
|
||||
"embedded-hal-1?/defmt-03",
|
||||
"embassy-futures/defmt",
|
||||
"embassy-sync/defmt",
|
||||
"embedded-hal/defmt-03",
|
||||
"embedded-io/defmt-03",
|
||||
"embedded-io-async?/defmt-03",
|
||||
"embedded-io-async/defmt-03",
|
||||
"esp32?/defmt",
|
||||
"esp32c2?/defmt",
|
||||
"esp32c3?/defmt",
|
||||
"esp32c6?/defmt",
|
||||
"esp32h2?/defmt",
|
||||
"esp32s2?/defmt",
|
||||
"esp32s3?/defmt",
|
||||
"fugit/defmt",
|
||||
]
|
||||
## Implement the traits defined in the `1.0.0` releases of `embedded-hal` and
|
||||
## `embedded-hal-nb` for the relevant peripherals.
|
||||
eh1 = ["embedded-hal-1", "embedded-hal-nb", "embedded-can"]
|
||||
## Implement the traits defined in `embedded-io` for certain peripherals.
|
||||
embedded-io = ["dep:embedded-io"]
|
||||
## Implement the `ufmt_write::uWrite` trait for certain peripherals.
|
||||
ufmt = ["dep:ufmt-write"]
|
||||
|
||||
#! ### Embassy Feature Flags
|
||||
## Enable support for `embassy`, a modern asynchronous embedded framework.
|
||||
embassy = ["embassy-time-driver", "procmacros/embassy"]
|
||||
## Use the interrupt-mode embassy executor.
|
||||
embassy-executor-interrupt = ["embassy", "embassy-executor"]
|
||||
## Use the thread-mode embassy executor.
|
||||
embassy-executor-thread = ["embassy", "embassy-executor"]
|
||||
## Uses hardware timers as alarms for the executors. Using this feature
|
||||
## limits the number of executors to the number of hardware alarms provided
|
||||
## by the time driver.
|
||||
embassy-integrated-timers = ["embassy-executor?/integrated-timers"]
|
||||
## Enable the embassy time driver using the `SYSTIMER` peripheral. The
|
||||
## `SYSTIMER` peripheral has three alarams available for use. Do **not**
|
||||
## use when targeting an `esp32s2`.
|
||||
embassy-time-systick-16mhz = ["embassy-time-driver/tick-hz-16_000_000"]
|
||||
## Enable the embassy time driver using the `SYSTIMER` peripheral. The
|
||||
## `SYSTIMER` peripheral has three alarams available for use. Must only
|
||||
## be used when targeting an `esp32s2`.
|
||||
embassy-time-systick-80mhz = ["embassy-time-driver/tick-hz-80_000_000"]
|
||||
## Enable the embassy time driver using the `TIMG0` peripheral. The `TIMG0`
|
||||
## peripheral has two alarms available for use.
|
||||
embassy-time-timg0 = ["embassy-time-driver/tick-hz-1_000_000"]
|
||||
|
||||
#! ### PSRAM Feature Flags
|
||||
## Use externally connected PSRAM (2MB).
|
||||
psram-2m = []
|
||||
## Use externally connected PSRAM (4MB).
|
||||
psram-4m = []
|
||||
## Use externally connected PSRAM (8MB).
|
||||
psram-8m = []
|
||||
## PSRAM 80Mhz frequency support
|
||||
psram-80mhz = []
|
||||
## Use externally connected Quad PSRAM
|
||||
quad-psram = []
|
||||
|
||||
#! ### Octal RAM Feature Flags
|
||||
## Use externally connected Octal RAM (2MB).
|
||||
opsram-2m = []
|
||||
## Use externally connected Octal RAM (4MB).
|
||||
opsram-4m = []
|
||||
## Use externally connected Octal RAM (8MB).
|
||||
opsram-8m = []
|
||||
## Use externally connected Octal RAM (16MB).
|
||||
opsram-16m = []
|
||||
## Use externally connected Octal RAM
|
||||
octal-psram = []
|
||||
|
||||
# This feature is intended for testing; you probably don't want to enable it:
|
||||
ci = ["defmt", "bluetooth"]
|
||||
|
||||
#! ### Unstable APIs
|
||||
#! Unstable APIs are drivers and features that are not yet ready for general use.
|
||||
#! They may be incomplete, have bugs, or be subject to change without notice.
|
||||
#! Unstable APIs are not covered by semver guarantees.
|
||||
|
||||
## Enables APIs that are not stable and thus come with no stability guarantees.
|
||||
unstable = [
|
||||
"dep:embassy-embedded-hal",
|
||||
"dep:embedded-can",
|
||||
"dep:embedded-io",
|
||||
"dep:embedded-io-async",
|
||||
]
|
||||
|
||||
[lints.clippy]
|
||||
mixed_attributes_style = "allow"
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(host_os, values("windows"))'] }
|
||||
|
||||
349
esp-hal/MIGRATING-0.20.md
Normal file
349
esp-hal/MIGRATING-0.20.md
Normal file
@ -0,0 +1,349 @@
|
||||
# Migration Guide from 0.20.x to v0.21.x
|
||||
|
||||
## Cargo Features
|
||||
|
||||
A number of trait implementations which were previously feature-gated are now implemented by default. The following Cargo features have been removed:
|
||||
|
||||
- `async`
|
||||
- `embedded-hal-02`
|
||||
- `embedded-hal`
|
||||
- `embedded-io`
|
||||
- `embedded-io-async`
|
||||
- `ufmt`
|
||||
|
||||
If your project enables any of these features, simply remove them from your Cargo manifest and things should continue to work as expected.
|
||||
|
||||
## HAL Initialization
|
||||
|
||||
Instead of manually grabbing peripherals and setting up clocks, you should now call `esp_hal::init`.
|
||||
|
||||
```diff
|
||||
use esp_hal::{
|
||||
- clock::ClockControl,
|
||||
- peripherals::Peripherals,
|
||||
prelude::*,
|
||||
- system::SystemControl,
|
||||
};
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
- let peripherals = Peripherals::take();
|
||||
- let system = SystemControl::new(peripherals.SYSTEM);
|
||||
- let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
|
||||
+ let peripherals = esp_hal::init(esp_hal::Config::default());
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## GPIO changes
|
||||
|
||||
- The `GpioN` type aliasses are no longer available. You can use `GpioPin<N>` instead.
|
||||
- The `AnyInputOnlyPin` has been removed. Replace any use with `AnyPin`.
|
||||
- The `NoPinType` has been removed. You can use `DummyPin` in its place.
|
||||
|
||||
### Type-erased GPIO drivers
|
||||
|
||||
The `AnyInput`, `AnyOutput`, `AnyOutputOpenDrain` and `AnyFlex` structures have been removed.
|
||||
Instead, you can use the non-`Any` counterparts. By default, the structures will use `AnyPin` in
|
||||
their type names, which you don't have to specify.
|
||||
|
||||
```diff
|
||||
-let pin = AnyInput::new(io.gpio0, Pull::Up);
|
||||
+let pin = Input::new(io.gpio0, Pull::Up); // pin will have the type `Input<'some>` (or `Input<'some, AnyPin>` if you want to be explicit about it)
|
||||
```
|
||||
|
||||
You can use `new_typed` if you want to keep using the typed form:
|
||||
|
||||
```rust
|
||||
let pin = Input::new_typed(io.gpio0, Pull::Up); // pin will have the type `Input<'some, GpioPin<0>>`
|
||||
```
|
||||
|
||||
### Creating an `AnyPin`
|
||||
|
||||
Instead of `AnyPin::new`, you can now use the `Pin` trait's `degrade` method to obtain an `AnyPin`.
|
||||
You can now create an `AnyPin` out of input only pins (like ESP32's IO34), but trying to use such
|
||||
pins as output (or, generally, trying to use pins in ways they don't support) will panic.
|
||||
|
||||
```diff
|
||||
+ use esp_hal::gpio::Pin;
|
||||
|
||||
-let pin = AnyPin::new(io.gpio0);
|
||||
+let pin = io.gpio0.degrade();
|
||||
```
|
||||
|
||||
### Wakeup using pin drivers
|
||||
|
||||
You can now use pin drivers as wakeup sources.
|
||||
|
||||
```rust
|
||||
use esp_hal::peripheral::Peripheral; // needed for `into_ref`
|
||||
|
||||
let pin2 = Input::new(io.pins.gpio2, Pull::None);
|
||||
let mut pin3 = Input::new(io.pins.gpio3, Pull::None);
|
||||
// ... use pin2 as an input ..
|
||||
|
||||
// Ext0
|
||||
let ext0 = Ext0WakeupSource::new(pin2, WakeupLevel::High);
|
||||
|
||||
// Ext1
|
||||
let mut wakeup_pins: [&mut dyn RtcPin; 2] = [
|
||||
&mut *pin_0.into_ref(),
|
||||
&mut io.pins.gpio4, // unconfigured pins continue to work, too!
|
||||
];
|
||||
let ext1 = Ext1WakeupSource::new(&mut wakeup_pins, WakeupLevel::High);
|
||||
```
|
||||
|
||||
## RX/TX Order
|
||||
|
||||
Previously, our API was pretty inconsistent with the RX/TX ordering, and different peripherals had different order. Now, all
|
||||
the peripherals use rx-tx. Make sure your methods are expecting the right RX/TX order, for example an SPI DMA app should be updated to:
|
||||
|
||||
```diff
|
||||
- let (tx_buffer, tx_descriptors, rx_buffer, rx_descriptors) = dma_buffers!(4);
|
||||
+ let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(4);
|
||||
let mut dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap();
|
||||
let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap();
|
||||
|
||||
...
|
||||
|
||||
let transfer = spi
|
||||
- .dma_transfer(dma_tx_buf, dma_rx_buf)
|
||||
+ .dma_transfer(dma_rx_buf, dma_tx_buf)
|
||||
.map_err(|e| e.0)
|
||||
.unwrap();
|
||||
```
|
||||
|
||||
When using the asymmetric variant of the macro to create DMA buffers and descriptors make sure to swap the order of parameters
|
||||
|
||||
```diff
|
||||
- let (tx_buffer, tx_descriptors, _, _) = dma_buffers!(32000, 0);
|
||||
+ let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(0, 32000);
|
||||
```
|
||||
|
||||
## Removed constructors
|
||||
|
||||
### UART
|
||||
|
||||
The `Uart::new_with_default_pins` and `Uart::new_async_with_default_pins` constructors
|
||||
have been removed. Use `new` or `new_async` instead.
|
||||
|
||||
### I2S1
|
||||
|
||||
The `I2s::new_i2s1` constructor has been removed. Use `I2s::new` instead.
|
||||
|
||||
## Timer changes
|
||||
|
||||
### `ErasedTimer` rename
|
||||
|
||||
The `ErasedTimer` has been renamed to `AnyTimer`.
|
||||
|
||||
### `esp_hal::time::current_time` rename
|
||||
|
||||
To avoid confusion with the `Rtc::current_time` wall clock time APIs, we've renamed `esp_hal::time::current_time` to `esp_hal::time::now()`.
|
||||
|
||||
### RTC Wall Clock APIs
|
||||
|
||||
Instead of the `get_time_ms`, `get_time_us`, and `get_time_raw` functions, the `Rtc` struct now provides the `current_time` function, using `chrono`'s `NaiveDateTime` struct.
|
||||
|
||||
```diff
|
||||
let rtc = Rtc::new(peripherals.LPWR);
|
||||
- let current_time_ms = rtc.get_time_ms();
|
||||
+ let current_time_ms = rtc.current_time().and_utc().timestamp_millis(); // assuming UTC
|
||||
```
|
||||
|
||||
## PCNT input config
|
||||
|
||||
The `PcntSource` and `PcntInputConfig` have been removed. You can use `Input` or `Flex` instead to
|
||||
configure an input pin, and pass it to `set_edge_signal` or `set_ctrl_signal`.
|
||||
|
||||
```diff
|
||||
- let mut pin_a = io.pins.gpio4;
|
||||
- ch0.set_ctrl_signal(PcntSource::from_pin(
|
||||
- &mut pin_a,
|
||||
- PcntInputConfig { pull: Pull::Up },
|
||||
- ));
|
||||
+ ch0.set_ctrl_signal(Input::new(io.pins.gpio4, Pull::Up));
|
||||
|
||||
- let mut pin_b = io.pins.gpio5;
|
||||
- ch0.set_edge_signal(PcntSource::from_pin(
|
||||
- &mut pin_b,
|
||||
- PcntInputConfig { pull: Pull::Down },
|
||||
- ));
|
||||
+ ch0.set_edge_signal(Input::new(io.pins.gpio5, Pull::Down));
|
||||
```
|
||||
|
||||
## SPI pins and `NO_PIN`
|
||||
|
||||
Use `NoPin` in place of the now-removed `NO_PIN` constant.
|
||||
|
||||
SPI pins, when using the `with_pin` function, are no longer optional.
|
||||
You can pass `NoPin` or `Level` if you don't need a particular pin.
|
||||
|
||||
```diff
|
||||
let spi = Spi::new(peripherals.SPI2, 100.kHz(), SpiMode::Mode0)
|
||||
- .with_pins(Some(sclk), Some(mosi), NO_PIN, NO_PIN);
|
||||
+ .with_pins(sclk, mosi, Level::Low, NoPin);
|
||||
```
|
||||
|
||||
## I8080 type definition
|
||||
|
||||
The I8080 driver no longer holds on to pins in its type definition.
|
||||
|
||||
```diff
|
||||
- let _: I8080<'a, DmaChannel3, TxEightBits<AnyPin, AnyPin, AnyPin, ....>, Blocking>;
|
||||
+ let _: I8080<'a, DmaChannel3, Blocking>;
|
||||
```
|
||||
|
||||
## I8080 transfer API changes
|
||||
|
||||
- The I8080 driver now decides bus width at transfer time, which means you don't get type inference anymore.
|
||||
- Starting a transfer moves the driver into the transfer object, allowing you to store it in a `static` or struct.
|
||||
- The transfer API no longer works with plain slices, it now works with `DmaTxBuffer`s which allow to bring your own DMA data structure and implement efficient queueing of transfers.
|
||||
- The three transfer methods (`send`, `send_dma`, `send_dma_async`) have been merged into one `send` method.
|
||||
|
||||
```diff
|
||||
let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(0, 32678);
|
||||
+ let mut dma_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap();
|
||||
|
||||
let mut i8080 = I8080::new(
|
||||
lcd_cam.lcd,
|
||||
channel.tx,
|
||||
- tx_descriptors,
|
||||
tx_pins,
|
||||
20.MHz(),
|
||||
Config::default(),
|
||||
)
|
||||
|
||||
- i8080.send(0x12, 0, &[0, 1, 2, 3, 4]);
|
||||
+ dma_buf.fill(&[0, 1, 2, 3, 4]);
|
||||
+ let transfer = i8080.send(0x12u8, 0, dma_buf).unwrap();
|
||||
+ // transfer.wait_for_done().await;
|
||||
+ (_, i8080, dma_buf) = transfer.wait();
|
||||
```
|
||||
|
||||
### Placing drivers in RAM is now done via esp-config
|
||||
|
||||
We've replaced some usage of features with [esp-config](https://docs.rs/esp-config). Please remove any reference to `place-spi-driver-in-ram` in your `Cargo.toml` and migrate to the `[env]` section of `.cargo/config.toml`.
|
||||
|
||||
```diff
|
||||
# feature in Cargo.toml
|
||||
- esp-hal = { version = "0.20", features = ["place-spi-driver-in-ram"] }
|
||||
# key in .cargo/config.toml [env] section
|
||||
+ ESP_HAL_PLACE_SPI_DRIVER_IN_RAM="true"
|
||||
```
|
||||
|
||||
## `Camera` driver now uses `DmaRxBuffer` and moves the driver into the transfer object.
|
||||
|
||||
For one shot transfers.
|
||||
|
||||
```diff
|
||||
let (rx_buffer, rx_descriptors, _, _) = dma_buffers!(32678, 0);
|
||||
+ let dma_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap();
|
||||
|
||||
let lcd_cam = LcdCam::new(peripherals.LCD_CAM);
|
||||
let mut camera = Camera::new(
|
||||
lcd_cam.cam,
|
||||
channel.rx,
|
||||
- rx_descriptors,
|
||||
data_pins,
|
||||
20u32.MHz(),
|
||||
);
|
||||
|
||||
- let transfer = camera.read_dma(rx_buffer).unwrap();
|
||||
- transfer.wait();
|
||||
+ let transfer = camera.receive(dma_buf).unwrap();
|
||||
+ let (_, camera, buf) = transfer.wait();
|
||||
```
|
||||
|
||||
For circular transfers.
|
||||
|
||||
```diff
|
||||
- let (rx_buffer, rx_descriptors, _, _) = dma_buffers!(32678, 0);
|
||||
+ let dma_buf = dma_rx_stream_buffer!(32678);
|
||||
|
||||
let lcd_cam = LcdCam::new(peripherals.LCD_CAM);
|
||||
let mut camera = Camera::new(
|
||||
lcd_cam.cam,
|
||||
channel.rx,
|
||||
- rx_descriptors,
|
||||
data_pins,
|
||||
20u32.MHz(),
|
||||
);
|
||||
|
||||
- let mut transfer = camera.read_dma_circular(rx_buffer).unwrap();
|
||||
+ let mut transfer = camera.receive(dma_buf).unwrap();
|
||||
transfer.pop(&mut [.....]);
|
||||
transfer.pop(&mut [.....]);
|
||||
transfer.pop(&mut [.....]);
|
||||
```
|
||||
|
||||
## PS-RAM
|
||||
|
||||
Initializing PS-RAM now takes a chip specific config and returns start of the mapped memory and the size.
|
||||
|
||||
Example
|
||||
|
||||
```rust
|
||||
let (start, size) = psram::init_psram(peripherals.PSRAM, psram::PsramConfig::default());
|
||||
```
|
||||
|
||||
If you don't specify the size of PS-RAM via `PsramConfig::size` the size of PS-RAM is derived from the RAM-chip id (or via probing in case of ESP32).
|
||||
|
||||
`psram::psram_vaddr_start()` and `psram::PSRAM_BYTES` are removed.
|
||||
|
||||
The features `psram-Xm` and `opsram-Xm` are removed and replaced by `quad-psram`/`octal-psram`.
|
||||
The feature `psram-80mhz` is removed and replaced by `PsramConfig`
|
||||
|
||||
Diff of the `psram_quad.rs` example
|
||||
|
||||
```diff
|
||||
-//% FEATURES: psram-2m
|
||||
+//% FEATURES: esp-hal/quad-psram
|
||||
|
||||
...
|
||||
|
||||
-fn init_psram_heap() {
|
||||
+fn init_psram_heap(start: *mut u8, size: usize) {
|
||||
unsafe {
|
||||
esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new(
|
||||
- psram::psram_vaddr_start() as *mut u8,
|
||||
- psram::PSRAM_BYTES,
|
||||
+ start,
|
||||
+ size,
|
||||
esp_alloc::MemoryCapability::External.into(),
|
||||
));
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
- psram::init_psram(peripherals.PSRAM);
|
||||
- init_psram_heap();
|
||||
+ let (start, size) = psram::init_psram(peripherals.PSRAM, psram::PsramConfig::default());
|
||||
+ init_psram_heap(start, size);
|
||||
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
## eFuse
|
||||
|
||||
Calling `Efuse::read_field_le::<bool>()` no longer compiles. Use `Efuse::read_bit()` instead.
|
||||
|
||||
## DMA
|
||||
|
||||
The DMA channel types have been removed from peripherals.
|
||||
|
||||
A non-exhausitve list demonstrating this change:
|
||||
|
||||
```diff
|
||||
-I2sTx<'static, I2S0, DmaChannel0, Async>
|
||||
+I2sTx<'static, I2S0, Async>
|
||||
|
||||
-SpiDma<'static, esp_hal::peripherals::SPI2, DmaChannel0, HalfDuplexMode, Blocking>
|
||||
+SpiDma<'static, esp_hal::peripherals::SPI2, HalfDuplexMode, Blocking>
|
||||
```
|
||||
|
||||
## `I2C` driver renamed to `I2c`
|
||||
|
||||
To firstly match the naming in `embedded-hal`, but also because types should be `UpperCamelCase`.
|
||||
471
esp-hal/MIGRATING-0.21.md
Normal file
471
esp-hal/MIGRATING-0.21.md
Normal file
@ -0,0 +1,471 @@
|
||||
# Migration Guide from 0.21.x to v0.22.x
|
||||
|
||||
## IO changes
|
||||
|
||||
### GPIO pins are now accessible via `Peripherals`
|
||||
|
||||
```diff
|
||||
let peripherals = esp_hal::init(Default::default());
|
||||
-let io = Io::new(peripherals.GPIO, peripherals.IOMUX);
|
||||
-let pin = io.pins.gpio5;
|
||||
+let pin = peripherals.GPIO5;
|
||||
```
|
||||
|
||||
### `Io` constructor changes
|
||||
|
||||
- `new_with_priority` and `new_no_bind_interrupts` have been removed.
|
||||
Use `set_priority` to configure the GPIO interrupt priority.
|
||||
We no longer overwrite interrupt handlers set by user code during initialization.
|
||||
- `new` no longer takes `peripherals.GPIO`
|
||||
|
||||
## Removed `async`-specific constructors
|
||||
|
||||
The following async-specific constuctors have been removed:
|
||||
|
||||
- `configure_for_async` DMA channel constructors
|
||||
- `TwaiConfiguration::new_async` and `TwaiConfiguration::new_async_no_transceiver`
|
||||
- `I2c::new_async`
|
||||
- `LcdCam::new_async`
|
||||
- `UsbSerialJtag::new_async`
|
||||
- `Rsa::new_async`
|
||||
- `Rmt::new_async`
|
||||
- `Uart::new_async`, `Uart::new_async_with_config`
|
||||
- `UartRx::new_async`, `UartRx::new_async_with_config`
|
||||
- `UartTx::new_async`, `UartTx::new_async_with_config`
|
||||
|
||||
You can use the blocking counterparts, then call `into_async` on the returned peripheral instead.
|
||||
|
||||
```diff
|
||||
-let mut config = twai::TwaiConfiguration::new_async(
|
||||
+let mut config = twai::TwaiConfiguration::new(
|
||||
peripherals.TWAI0,
|
||||
loopback_pin.peripheral_input(),
|
||||
loopback_pin,
|
||||
twai::BaudRate::B1000K,
|
||||
TwaiMode::SelfTest,
|
||||
-);
|
||||
+).into_async();
|
||||
```
|
||||
|
||||
Some drivers were implicitly configured to the asyncness of the DMA channel used to construct them.
|
||||
This is no longer the case, and the following drivers will always be created in blocking mode:
|
||||
|
||||
- `i2s::master::I2s`
|
||||
- `spi::master::SpiDma` and `spi::master::SpiDmaBus`
|
||||
|
||||
## Peripheral types are now optional
|
||||
|
||||
You no longer have to specify the peripheral instance in the driver's type for the following
|
||||
peripherals:
|
||||
|
||||
- SPI (both master and slave)
|
||||
- I2S
|
||||
- I2C
|
||||
- TWAI
|
||||
- UART
|
||||
|
||||
```diff
|
||||
-Spi<'static, SPI2, FullDuplexMode>
|
||||
+Spi<'static, FullDuplexMode>
|
||||
|
||||
-SpiDma<'static, SPI2, HalfDuplexMode, Blocking>
|
||||
+SpiDma<'static, HalfDuplexMode, Blocking>
|
||||
|
||||
-I2sTx<'static, I2S0, Async>
|
||||
+I2sTx<'static, Async>
|
||||
```
|
||||
|
||||
Note that you may still specify the instance if you need to. To do this, we provide `_typed`
|
||||
versions of the constructors (for example: `new_typed`, `new_half_duplex_typed`). Please note that
|
||||
the peripheral instance has been moved to the last generic parameter position.
|
||||
|
||||
```rust
|
||||
let spi: Spi<'static, FullDuplexMode, SPI2> = Spi::new_typed(peripherals.SPI2, 1.MHz(), SpiMode::Mode0);
|
||||
```
|
||||
|
||||
## I2C changes
|
||||
|
||||
The I2C master driver and related types have been moved to `esp_hal::i2c::master`.
|
||||
|
||||
The `with_timeout` constructors have been removed. `new` and `new_typed` now take a `Config` struct
|
||||
with the available configuration options.
|
||||
|
||||
- The default configuration is now:
|
||||
- bus frequency: 100 kHz
|
||||
- timeout: about 10 bus clock cycles
|
||||
|
||||
The constructors no longer take pins. Use `with_sda` and `with_scl` instead.
|
||||
|
||||
```diff
|
||||
-use esp_hal::i2c::I2c;
|
||||
+use esp_hal::i2c::{Config, I2c};
|
||||
-let i2c = I2c::new_with_timeout(peripherals.I2C0, sda, scl, 100.kHz(), timeout);
|
||||
+let i2c = I2c::new(
|
||||
+ peripherals.I2C0,
|
||||
+ {
|
||||
+ let mut config = Config::default();
|
||||
+ config.frequency = 100.kHz();
|
||||
+ config.timeout = timeout;
|
||||
+ config
|
||||
+ },
|
||||
+)
|
||||
+.with_sda(sda)
|
||||
+.with_scl(scl);
|
||||
```
|
||||
|
||||
### The calculation of I2C timeout has changed
|
||||
|
||||
Previously, I2C timeouts were counted in increments of I2C peripheral clock cycles. This meant that
|
||||
the configure value meant different lengths of time depending on the device. With this update, the
|
||||
I2C configuration now expects the timeout value in number of bus clock cycles, which is consistent
|
||||
between devices.
|
||||
|
||||
ESP32 and ESP32-S2 use an exact number of clock cycles for its timeout. Other MCUs, however, use
|
||||
the `2^timeout` value internally, and the HAL rounds up the timeout to the next appropriate value.
|
||||
|
||||
## Changes to half-duplex SPI
|
||||
|
||||
The `HalfDuplexMode` and `FullDuplexMode` type parameters have been removed from SPI master and slave
|
||||
drivers. It is now possible to execute half-duplex and full-duplex operations on the same SPI bus.
|
||||
|
||||
### Driver construction
|
||||
|
||||
- The `Spi::new_half_duplex` constructor has been removed. Use `new` (or `new_typed`) instead.
|
||||
- The `with_pins` methods have been removed. Use the individual `with_*` functions instead.
|
||||
- The `with_mosi` and `with_miso` functions now take input-output peripheral signals to support half-duplex mode.
|
||||
|
||||
```diff
|
||||
- let mut spi = Spi::new_half_duplex(peripherals.SPI2, 100.kHz(), SpiMode::Mode0)
|
||||
- .with_pins(sck, mosi, miso, sio2, sio3, cs);
|
||||
+ let mut spi = Spi::new(peripherals.SPI2) // or new_with_config
|
||||
+ .with_sck(sck)
|
||||
+ .with_cs(cs)
|
||||
+ .with_mosi(mosi)
|
||||
+ .with_miso(miso)
|
||||
+ .with_sio2(sio2)
|
||||
+ .with_sio3(sio3);
|
||||
```
|
||||
|
||||
### Transfer functions
|
||||
|
||||
The `Spi<'_, SPI, HalfDuplexMode>::read` and `Spi<'_, SPI, HalfDuplexMode>::write` functions have been replaced by
|
||||
`half_duplex_read` and `half_duplex_write`.
|
||||
|
||||
```diff
|
||||
let mut data = [0u8; 2];
|
||||
let transfer = spi
|
||||
- .read(
|
||||
+ .half_duplex_read(
|
||||
SpiDataMode::Single,
|
||||
Command::Command8(0x90, SpiDataMode::Single),
|
||||
Address::Address24(0x000000, SpiDataMode::Single),
|
||||
0,
|
||||
&mut data,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let transfer = spi
|
||||
- .write(
|
||||
+ .half_duplex_write(
|
||||
SpiDataMode::Quad,
|
||||
Command::Command8(write as u16, command_data_mode),
|
||||
Address::Address24(
|
||||
write as u32 | (write as u32) << 8 | (write as u32) << 16,
|
||||
SpiDataMode::Quad,
|
||||
),
|
||||
0,
|
||||
dma_tx_buf,
|
||||
)
|
||||
.unwrap();
|
||||
```
|
||||
|
||||
## Slave-mode SPI
|
||||
|
||||
### Driver construction
|
||||
|
||||
The constructors no longer accept pins. Use the `with_pin_name` setters instead.
|
||||
|
||||
```diff
|
||||
let mut spi = Spi::new(
|
||||
peripherals.SPI2,
|
||||
- sclk,
|
||||
- mosi,
|
||||
- miso,
|
||||
- cs,
|
||||
SpiMode::Mode0,
|
||||
-);
|
||||
+)
|
||||
+.with_sclk(sclk)
|
||||
+.with_mosi(mosi)
|
||||
+.with_miso(miso)
|
||||
+.with_cs(cs);
|
||||
```
|
||||
|
||||
## UART event listening
|
||||
|
||||
The following functions have been removed:
|
||||
|
||||
- `listen_at_cmd`
|
||||
- `unlisten_at_cmd`
|
||||
- `listen_tx_done`
|
||||
- `unlisten_tx_done`
|
||||
- `listen_rx_fifo_full`
|
||||
- `unlisten_rx_fifo_full`
|
||||
- `at_cmd_interrupt_set`
|
||||
- `tx_done_interrupt_set`
|
||||
- `rx_fifo_full_interrupt_set`
|
||||
- `reset_at_cmd_interrupt`
|
||||
- `reset_tx_done_interrupt`
|
||||
- `reset_rx_fifo_full_interrupt`
|
||||
|
||||
You can now use the `UartInterrupt` enum and the corresponding `listen`, `unlisten`, `interrupts` and `clear_interrupts` functions.
|
||||
|
||||
Use `interrupts` in place of `<INTERRUPT>_interrupt_set` and `clear_interrupts` in place of the old `reset_` functions.
|
||||
|
||||
`UartInterrupt`:
|
||||
|
||||
- `AtCmd`
|
||||
- `TxDone`
|
||||
- `RxFifoFull`
|
||||
|
||||
Checking interrupt bits is now done using APIs provided by [enumset](https://docs.rs/enumset/1.1.5/enumset/index.html). For example, to see if
|
||||
a particular interrupt bit is set, use `contains`:
|
||||
|
||||
```diff
|
||||
-serial.at_cmd_interrupt_set()
|
||||
+serial.interupts().contains(UartInterrupt::AtCmd)
|
||||
```
|
||||
|
||||
You can now listen/unlisten multiple interrupt bits at once:
|
||||
|
||||
```diff
|
||||
-uart0.listen_at_cmd();
|
||||
-uart0.listen_rx_fifo_full();
|
||||
+uart0.listen(UartInterrupt::AtCmd | UartConterrupt::RxFifoFull);
|
||||
```
|
||||
|
||||
## I2S changes
|
||||
|
||||
### The I2S driver has been moved to `i2s::master`
|
||||
|
||||
```diff
|
||||
-use esp_hal::i2s::{DataFormat, I2s, Standard};
|
||||
+use esp_hal::i2s::master::{DataFormat, I2s, Standard};
|
||||
```
|
||||
|
||||
### Removed `i2s` traits
|
||||
|
||||
The following traits have been removed:
|
||||
|
||||
- `I2sWrite`
|
||||
- `I2sWriteDma`
|
||||
- `I2sRead`
|
||||
- `I2sReadDma`
|
||||
- `I2sWriteDmaAsync`
|
||||
- `I2sReadDmaAsync`
|
||||
|
||||
You no longer have to import these to access their respective APIs. If you used these traits
|
||||
in your functions as generic parameters, you can use the `I2s` type directly instead.
|
||||
|
||||
For example:
|
||||
|
||||
```diff
|
||||
-fn foo(i2s: &mut impl I2sWrite) {
|
||||
+fn foo(i2s: &mut I2s<'_, I2S0, Blocking>) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## DMA related changes
|
||||
|
||||
### Circular DMA transfer's `available` returns `Result<usize, DmaError>` now
|
||||
|
||||
In case of any error you should drop the transfer and restart it.
|
||||
|
||||
```diff
|
||||
loop {
|
||||
- let avail = transfer.available();
|
||||
+ let avail = match transfer.available() {
|
||||
+ Ok(avail) => avail,
|
||||
+ Err(_) => {
|
||||
+ core::mem::drop(transfer);
|
||||
+ transfer = i2s_tx.write_dma_circular(&tx_buffer).unwrap();
|
||||
+ continue;
|
||||
+ },
|
||||
+ };
|
||||
```
|
||||
|
||||
### Channel, ChannelRx and ChannelTx types have changed
|
||||
|
||||
- `Channel`'s `Async`/`Blocking` mode has been moved before the channel instance parameter.
|
||||
- `ChannelRx` and `ChannelTx` have gained a new `Async`/`Blocking` mode parameter.
|
||||
|
||||
```diff
|
||||
-Channel<'d, DmaChannel0, Async>
|
||||
+Channel<'d, Async, DmaChannel0>
|
||||
|
||||
-ChannelRx<'d, DmaChannel0>
|
||||
+ChannelRx<'d, Async, DmaChannel0>
|
||||
|
||||
-ChannelTx<'d, DmaChannel0>
|
||||
+ChannelTx<'d, Async, DmaChannel0>
|
||||
```
|
||||
|
||||
## Removed `peripheral_input` and `into_peripheral_output` from GPIO pin types
|
||||
|
||||
Creating peripheral interconnect signals now consume the GPIO pin used for the connection.
|
||||
|
||||
The previous signal function have been replaced by `split`. This change affects the following APIs:
|
||||
|
||||
- `GpioPin`
|
||||
- `AnyPin`
|
||||
|
||||
```diff
|
||||
-let input_signal = gpioN.peripheral_input();
|
||||
-let output_signal = gpioN.into_peripheral_output();
|
||||
+let (input_signal, output_signal) = gpioN.split();
|
||||
```
|
||||
|
||||
`into_peripheral_output`, `split` (for output pins only) and `peripheral_input` have been added to
|
||||
the GPIO drivers (`Input`, `Output`, `OutputOpenDrain` and `Flex`) instead.
|
||||
|
||||
## ETM changes
|
||||
|
||||
- The types are no longer prefixed with `GpioEtm`, `TimerEtm` or `SysTimerEtm`. You can still use
|
||||
import aliasses in case you need to differentiate due to name collisions
|
||||
(e.g. `use esp_hal::gpio::etm::Event as GpioEtmEvent`).
|
||||
- The old task and event types have been replaced by `Task` and `Event`.
|
||||
- GPIO tasks and events are no longer generic.
|
||||
|
||||
## Changes to peripheral configuration
|
||||
|
||||
### The `uart::config` module has been removed
|
||||
|
||||
The module's contents have been moved into `uart`.
|
||||
|
||||
```diff
|
||||
-use esp_hal::uart::config::Config;
|
||||
+use esp_hal::uart::Config;
|
||||
```
|
||||
|
||||
If you work with multiple configurable peripherals, you may want to import the `uart` module and
|
||||
refer to the `Config` struct as `uart::Config`.
|
||||
|
||||
### SPI drivers can now be configured using `spi::master::Config`
|
||||
|
||||
- The old methods to change configuration have been removed.
|
||||
- The `new` and `new_typed` constructor no longer takes `frequency` and `mode`.
|
||||
- The default configuration is now:
|
||||
- bus frequency: 1 MHz
|
||||
- bit order: MSB first
|
||||
- mode: SPI mode 0
|
||||
- There are new constructors (`new_with_config`, `new_typed_with_config`) and a new `apply_config` method to apply custom configuration.
|
||||
|
||||
```diff
|
||||
-use esp_hal::spi::{master::Spi, SpiMode};
|
||||
+use esp_hal::spi::{master::{Config, Spi}, SpiMode};
|
||||
-Spi::new(SPI2, 100.kHz(), SpiMode::Mode1);
|
||||
+Spi::new_with_config(
|
||||
+ SPI2,
|
||||
+ Config {
|
||||
+ frequency: 100.kHz(),
|
||||
+ mode: SpiMode::Mode0,
|
||||
+ ..Config::default()
|
||||
+ },
|
||||
+)
|
||||
```
|
||||
|
||||
## LCD_CAM changes
|
||||
|
||||
### I8080 driver split `set_byte_order()` into `set_8bits_order()` and `set_byte_order()`.
|
||||
|
||||
If you were using an 8-bit bus.
|
||||
|
||||
```diff
|
||||
- i8080.set_byte_order(ByteOrder::default());
|
||||
+ i8080.set_8bits_order(ByteOrder::default());
|
||||
```
|
||||
|
||||
If you were using an 16-bit bus, you don't need to change anything, `set_byte_order()` now works correctly.
|
||||
|
||||
If you were sharing the bus between an 8-bit and 16-bit device, you will have to call the corresponding method when
|
||||
you switch between devices. Be sure to read the documentation of the new methods.
|
||||
|
||||
### Mixed mode constructors
|
||||
|
||||
It is no longer possible to construct `I8080` or `Camera` with an async-mode DMA channel.
|
||||
Convert the DMA channel into blocking before passing it to these constructors.
|
||||
|
||||
```diff
|
||||
let lcd_cam = LcdCam::new(peripherals.LCD_CAM);
|
||||
let channel = ctx
|
||||
.dma
|
||||
.channel0
|
||||
- .configure(false, DmaPriority::Priority0)
|
||||
- .into_async();
|
||||
+ .configure(false, DmaPriority::Priority0);
|
||||
|
||||
let i8080 = I8080::new(
|
||||
lcd_cam.lcd,
|
||||
channel.tx,
|
||||
pins,
|
||||
20.MHz(),
|
||||
Config::default(),
|
||||
);
|
||||
```
|
||||
|
||||
## `rmt::Channel::transmit` now returns `Result`, `PulseCode` is now `u32`
|
||||
|
||||
When trying to send a one-shot transmission will fail if it doesn't end with an end-marker.
|
||||
|
||||
```diff
|
||||
- let mut data = [PulseCode {
|
||||
- level1: true,
|
||||
- length1: 200,
|
||||
- level2: false,
|
||||
- length2: 50,
|
||||
- }; 20];
|
||||
-
|
||||
- data[data.len() - 2] = PulseCode {
|
||||
- level1: true,
|
||||
- length1: 3000,
|
||||
- level2: false,
|
||||
- length2: 500,
|
||||
- };
|
||||
- data[data.len() - 1] = PulseCode::default();
|
||||
+ let mut data = [PulseCode::new(true, 200, false, 50); 20];
|
||||
+ data[data.len() - 2] = PulseCode::new(true, 3000, false, 500);
|
||||
+ data[data.len() - 1] = PulseCode::empty();
|
||||
|
||||
- let transaction = channel.transmit(&data);
|
||||
+ let transaction = channel.transmit(&data).unwrap();
|
||||
```
|
||||
|
||||
|
||||
## The `parl_io::NoClkPin` and `no_clk_pin()` have been removed
|
||||
|
||||
You can use `gpio::NoPin` instead.
|
||||
|
||||
```diff
|
||||
use esp_hal:: {
|
||||
- parl_io::no_clk_pin,
|
||||
+ gpio::NoPin,
|
||||
}
|
||||
|
||||
-parl_io.rx.with_config(&mut rx_pins, no_clk_pin(), BitPackOrder::Msb, Some(0xfff))
|
||||
+let mut rx_clk_pin = NoPin;
|
||||
+parl_io.rx.with_config(&mut rx_pins, &mut rx_clk_pin, BitPackOrder::Msb, Some(0xfff))
|
||||
```
|
||||
|
||||
## `get_` prefixes have been removed from functions
|
||||
|
||||
In order to better comply with the Rust API Guidelines [getter names convention], we have removed the `get_` prefixes from all functions which previously had it. Due to the number of changes it's not practical to list all changes here, however if a function previous began with `get_`, you can simply remove this prefix.
|
||||
|
||||
[getter names convention]: https://rust-lang.github.io/api-guidelines/naming.html#c-getter
|
||||
|
||||
## The `get_core()` function has been removed in favour of `Cpu::current()`
|
||||
|
||||
```diff
|
||||
- let core = esp_hal::get_core();
|
||||
+ let core = esp_hal::Cpu::current();
|
||||
```
|
||||
478
esp-hal/MIGRATING-0.22.md
Normal file
478
esp-hal/MIGRATING-0.22.md
Normal file
@ -0,0 +1,478 @@
|
||||
# Migration Guide from 0.22.x to v1.0.0-beta.0
|
||||
|
||||
Starting with this release, unstable parts of esp-hal will be gated behind the `unstable` feature.
|
||||
The `unstable` feature itself is unstable, we might change the way we hide APIs without notice.
|
||||
Unstable APIs are not covered by semver guarantees, they may break even between patch releases.
|
||||
|
||||
Please refer to the documentation to see which APIs are marked as unstable.
|
||||
|
||||
## DMA changes
|
||||
|
||||
### Accessing channel objects
|
||||
|
||||
DMA channels are now available through the `Peripherals` struct, which is returned
|
||||
by `esp_hal::init()`. The channels themselves have been renamed to match other peripheral singletons.
|
||||
|
||||
- ESP32-C2, C3, C6, H2 and S3: `channelX -> DMA_CHX`
|
||||
- ESP32 and S2: `spiXchannel -> DMA_SPIX`, `i2sXchannel -> DMA_I2SX`
|
||||
|
||||
```diff
|
||||
-let dma = Dma::new(peripherals.DMA);
|
||||
-let channel = dma.channel2;
|
||||
+let channel = peripherals.DMA_CH2;
|
||||
```
|
||||
|
||||
### Channel configuration changes
|
||||
|
||||
- `configure_for_async` and `configure` have been removed
|
||||
- PDMA devices (ESP32, ESP32-S2) provide no channel configurability
|
||||
- GDMA devices provide `set_priority` to change DMA in/out channel priority
|
||||
|
||||
```diff
|
||||
let mut spi = Spi::new_with_config(
|
||||
peripherals.SPI2,
|
||||
Config::default(),
|
||||
)
|
||||
// other setup
|
||||
-.with_dma(dma_channel.configure(false, DmaPriority::Priority0));
|
||||
+.with_dma(dma_channel);
|
||||
```
|
||||
|
||||
```diff
|
||||
+dma_channel.set_priority(DmaPriority::Priority1);
|
||||
let mut spi = Spi::new_with_config(
|
||||
peripherals.SPI2,
|
||||
Config::default(),
|
||||
)
|
||||
// other setup
|
||||
-.with_dma(dma_channel.configure(false, DmaPriority::Priority1));
|
||||
+.with_dma(dma_channel);
|
||||
```
|
||||
|
||||
### Burst mode configuration
|
||||
|
||||
Burst mode is now a property of buffers, instead of DMA channels. Configuration can be done by
|
||||
calling `set_burst_config` on buffers that support it. The configuration options and the
|
||||
corresponding `BurstConfig` type are device specfic.
|
||||
|
||||
### Usability changes affecting applications
|
||||
|
||||
Individual channels are no longer wrapped in `Channel`, but they implement the `DmaChannel` trait.
|
||||
This means that if you want to split them into an `rx` and a `tx` half (which is only supported on
|
||||
the H2, C6 and S3 currently), you can't move out of the channel but instead you need to call
|
||||
the `split` method.
|
||||
|
||||
```diff
|
||||
-let tx = channel.tx;
|
||||
+use esp_hal::dma::DmaChannel;
|
||||
+let (rx, tx) = channel.split();
|
||||
```
|
||||
|
||||
The `Channel` types remain available for use in peripheral drivers.
|
||||
|
||||
It is now simpler to work with DMA channels in generic contexts. esp-hal now provides convenience
|
||||
traits and type aliasses to specify peripheral compatibility. The `ChannelCreator` types have been
|
||||
removed, further simplifying use.
|
||||
|
||||
For example, previously you may have needed to write something like this to accept a DMA channel
|
||||
in a generic function:
|
||||
|
||||
```rust
|
||||
fn new_foo<'d, T>(
|
||||
dma_channel: ChannelCreator<2>, // It wasn't possible to accept a generic ChannelCreator.
|
||||
peripheral: impl Peripheral<P = T> + 'd,
|
||||
)
|
||||
where
|
||||
T: SomePeripheralInstance,
|
||||
ChannelCreator<2>: DmaChannelConvert<<T as DmaEligible>::Dma>,
|
||||
{
|
||||
let dma_channel = dma_channel.configure_for_async(false, DmaPriority::Priority0);
|
||||
|
||||
let driver = PeripheralDriver::new(peripheral, config).with_dma(dma_channel);
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
From now on a similar, but more flexible implementation may look like:
|
||||
|
||||
```rust
|
||||
fn new_foo<'d, T, CH>(
|
||||
dma_channel: impl Peripheral<P = CH> + 'd,
|
||||
peripheral: impl Peripheral<P = T> + 'd,
|
||||
)
|
||||
where
|
||||
T: SomePeripheralInstance,
|
||||
CH: DmaChannelFor<T>,
|
||||
{
|
||||
// Optionally: dma_channel.set_priority(DmaPriority::Priority2);
|
||||
|
||||
let driver = PeripheralDriver::new(peripheral, config).with_dma(dma_channel);
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Usability changes affecting third party peripheral drivers
|
||||
|
||||
If you are writing a driver and need to store a channel in a structure, you can use one of the
|
||||
`ChannelFor` type aliasses.
|
||||
|
||||
```diff
|
||||
struct Aes<'d> {
|
||||
- channel: ChannelTx<'d, Blocking, <AES as DmaEligible>::Dma>,
|
||||
+ channel: ChannelTx<'d, Blocking, PeripheralTxChannel<AES>>,
|
||||
}
|
||||
```
|
||||
|
||||
## Timer changes
|
||||
|
||||
The low level timers, `SystemTimer` and `TimerGroup` are now "dumb". They contain no logic for operating modes or trait implementations (except the low level `Timer` trait).
|
||||
|
||||
### Timer drivers - `OneShotTimer` & `PeriodicTimer`
|
||||
|
||||
Both drivers now have a `Mode` parameter. Both also type erase the underlying driver by default, call `new_typed` to retain the type.
|
||||
|
||||
```diff
|
||||
- OneShotTimer<'static, systimer::Alarm>;
|
||||
+ OneShotTimer<'static, Blocking>;
|
||||
- PeriodicTimer<'static, systimer::Alarm>;
|
||||
+ PeriodicTimer<'static, Blocking>;
|
||||
```
|
||||
|
||||
### SystemTimer
|
||||
|
||||
```diff
|
||||
let systimer = SystemTimer::new(peripherals.SYSTIMER);
|
||||
- static UNIT0: StaticCell<SpecificUnit<'static, 0>> = StaticCell::new();
|
||||
- let unit0 = UNIT0.init(systimer.unit0);
|
||||
- let frozen_unit = FrozenUnit::new(unit0);
|
||||
- let alarm0 = Alarm::new(systimer.comparator0, &frozen_unit);
|
||||
- alarm0.set_period(1u32.secs());
|
||||
+ let alarm0 = systimer.alarm0;
|
||||
+ let mut timer = PeriodicTimer::new(alarm0);
|
||||
+ timer.start(1u64.secs());
|
||||
```
|
||||
|
||||
### TIMG
|
||||
|
||||
Timer group timers have been type erased.
|
||||
|
||||
```diff
|
||||
- timg::Timer<timg::Timer0<crate::peripherals::TIMG0>, Blocking>
|
||||
+ timg::Timer
|
||||
```
|
||||
|
||||
### ETM usage has changed
|
||||
|
||||
Timer dependant ETM events should be created _prior_ to initializing the timer with the chosen driver.
|
||||
|
||||
```diff
|
||||
let task = ...; // ETM task
|
||||
let syst = SystemTimer::new(peripherals.SYSTIMER);
|
||||
let alarm0 = syst.alarm0;
|
||||
- alarm0.load_value(1u64.millis()).unwrap();
|
||||
- alarm0.start();
|
||||
- let event = Event::new(&mut alarm0);
|
||||
+ let event = Event::new(&alarm0);
|
||||
+ let timer = OneShotTimer::new(alarm0);
|
||||
+ timer.schedule(1u64.millis()).unwrap();
|
||||
let _configured_channel = channel0.setup(&event, &task);
|
||||
```
|
||||
|
||||
## PSRAM is now initialized automatically
|
||||
|
||||
Calling `esp_hal::initialize` will now configure PSRAM if either the `quad-psram` or `octal-psram`
|
||||
is enabled. To retrieve the address and size of the initialized external memory, use
|
||||
`esp_hal::psram::psram_raw_parts`, which returns a pointer and a length.
|
||||
|
||||
```diff
|
||||
-let peripherals = esp_hal::init(esp_hal::Config::default());
|
||||
-let (start, size) = esp_hal::psram::init_psram(peripherals.PSRAM, psram_config);
|
||||
+let peripherals = esp_hal::init({
|
||||
+ let mut config = esp_hal::Config::default();
|
||||
+ config.psram = psram_config;
|
||||
+ config
|
||||
+});
|
||||
+let (start, size) = esp_hal::psram::psram_raw_parts(&peripherals.PSRAM, psram);
|
||||
```
|
||||
|
||||
The usage of `esp_alloc::psram_allocator!` remains unchanged.
|
||||
|
||||
## embedded-hal 0.2.\* is not supported anymore.
|
||||
|
||||
As per https://github.com/rust-embedded/embedded-hal/pull/640, our driver no longer implements traits from `embedded-hal 0.2.x`.
|
||||
Analogs of all traits from the above mentioned version are available in `embedded-hal 1.x.x`
|
||||
|
||||
```diff
|
||||
- use embedded_hal_02::can::Frame;
|
||||
+ use embedded_can::Frame;
|
||||
```
|
||||
|
||||
```diff
|
||||
- use embedded_hal_02::digital::v2::OutputPin;
|
||||
- use embedded_hal_02::digital::v2::ToggleableOutputPin;
|
||||
+ use embedded_hal::digital::OutputPin;
|
||||
+ use embedded_hal::digital::StatefulOutputPin;
|
||||
```
|
||||
|
||||
```diff
|
||||
- use embedded_hal_02::serial::{Read, Write};
|
||||
+ use embedded_hal_nb::serial::{Read, Write};
|
||||
```
|
||||
|
||||
You might also want to check the full official `embedded-hal` migration guide:
|
||||
https://github.com/rust-embedded/embedded-hal/blob/master/docs/migrating-from-0.2-to-1.0.md
|
||||
|
||||
## Interrupt related reshuffle
|
||||
|
||||
```diff
|
||||
- use esp_hal::InterruptConfigurable;
|
||||
- use esp_hal::DEFAULT_INTERRUPT_HANDLER;
|
||||
+ use esp_hal::interrupt::InterruptConfigurable;
|
||||
+ use esp_hal::interrupt::DEFAULT_INTERRUPT_HANDLER;
|
||||
```
|
||||
|
||||
## Driver constructors now take a configuration and are fallible
|
||||
|
||||
The old `new_with_config` constructor have been removed, and `new` constructors now always take
|
||||
a configuration structure. They have also been updated to return a `ConfigError` if the configuration
|
||||
is not compatible with the hardware.
|
||||
|
||||
```diff
|
||||
-let mut spi = Spi::new_with_config(
|
||||
+let mut spi = Spi::new(
|
||||
peripherals.SPI2,
|
||||
Config {
|
||||
frequency: 100.kHz(),
|
||||
mode: SpiMode::_0,
|
||||
..Config::default()
|
||||
},
|
||||
-);
|
||||
+)
|
||||
+.unwrap();
|
||||
```
|
||||
|
||||
```diff
|
||||
let mut spi = Spi::new(
|
||||
peripherals.SPI2,
|
||||
+ Config::default(),
|
||||
-);
|
||||
+)
|
||||
+.unwrap();
|
||||
```
|
||||
|
||||
## Peripheral instance type parameters and `new_typed` constructors have been removed
|
||||
|
||||
Call `new` instead and remove the type parameters if you've used them.
|
||||
|
||||
```diff
|
||||
-let mut spi: Spi<'lt, SPI2> = Spi::new_typed(..).unwrap();
|
||||
+let mut spi: Spi<'lt> = Spi::new(..).unwrap();
|
||||
```
|
||||
|
||||
## LCD_CAM configuration changes
|
||||
|
||||
- `cam` now has a `Config` strurct that contains frequency, bit/byte order, VSync filter options.
|
||||
- DPI, I8080: `frequency` has been moved into `Config`.
|
||||
|
||||
```diff
|
||||
+let mut cam_config = cam::Config::default();
|
||||
+cam_config.frequency = 1u32.MHz();
|
||||
cam::Camera::new(
|
||||
lcd_cam.cam,
|
||||
dma_rx_channel,
|
||||
pins,
|
||||
- 1u32.MHz(),
|
||||
+ cam_config,
|
||||
)
|
||||
```
|
||||
|
||||
## SpiDma now requires you specify the transfer length explicitly
|
||||
|
||||
```diff
|
||||
dma_tx_buf.set_length(5 /* or greater */);
|
||||
- spi_dma.write(dma_tx_buf);
|
||||
+ spi_dma.write(5, dma_tx_buf);
|
||||
```
|
||||
|
||||
```diff
|
||||
dma_rx_buf.set_length(5 /* or greater */);
|
||||
- spi_dma.read(dma_rx_buf);
|
||||
+ spi_dma.read(5, dma_rx_buf);
|
||||
```
|
||||
|
||||
```diff
|
||||
dma_rx_buf.set_length(5 /* or greater */);
|
||||
dma_tx_buf.set_length(5 /* or greater */);
|
||||
- spi_dma.transfer(dma_rx_buf, dma_tx_buf);
|
||||
+ spi_dma.transfer(5, dma_rx_buf, 5, dma_tx_buf);
|
||||
```
|
||||
|
||||
## I2C Error changes
|
||||
|
||||
To avoid abbreviations and contractions (as per the esp-hal guidelines), some error variants have changed
|
||||
|
||||
```diff
|
||||
- Error::ExecIncomplete
|
||||
+ Error::ExecutionIncomplete
|
||||
- Error::CommandNrExceeded
|
||||
+ Error::CommandNumberExceeded
|
||||
- Error::ExceedingFifo
|
||||
+ Error::FifoExceeded
|
||||
- Error::TimeOut
|
||||
+ Error::Timeout
|
||||
- Error::InvalidZeroLength
|
||||
+ Error::ZeroLengthInvalid
|
||||
```
|
||||
|
||||
## The crate prelude has been removed
|
||||
|
||||
The reexports that were previously part of the prelude are available through other paths:
|
||||
|
||||
- `nb` is no longer re-exported. Please import the `nb` crate if you need it.
|
||||
- `ExtU64` and `RateExtU32` have been moved to `esp_hal::time`
|
||||
- `Clock` and `CpuClock`: `esp_hal::clock::{Clock, CpuClock}`
|
||||
- The following traits need to be individually imported when needed:
|
||||
- `esp_hal::analog::dac::Instance`
|
||||
- `esp_hal::gpio::Pin`
|
||||
- `esp_hal::ledc::channel::ChannelHW`
|
||||
- `esp_hal::ledc::channel::ChannelIFace`
|
||||
- `esp_hal::ledc::timer::TimerHW`
|
||||
- `esp_hal::ledc::timer::TimerIFace`
|
||||
- `esp_hal::timer::timg::TimerGroupInstance`
|
||||
- `esp_hal::timer::Timer`
|
||||
- `esp_hal::interrupt::InterruptConfigurable`
|
||||
- The `entry` macro can be imported as `esp_hal::entry`, while other macros are found under `esp_hal::macros`
|
||||
|
||||
## `AtCmdConfig` now uses builder-lite pattern
|
||||
|
||||
```diff
|
||||
- uart0.set_at_cmd(AtCmdConfig::new(None, None, None, b'#', None));
|
||||
+ uart0.set_at_cmd(AtCmdConfig::default().with_cmd_char(b'#'));
|
||||
```
|
||||
|
||||
## Crate configuration changes
|
||||
|
||||
To prevent ambiguity between configurations, we had to change the naming format of configuration
|
||||
keys. Before, we used `{prefix}_{key}`, which meant that esp-hal and esp-hal-\* configuration keys
|
||||
were impossible to tell apart. To fix this issue, we are changing the separator from one underscore
|
||||
character to `_CONFIG_`. This also means that users will have to change their `config.toml`
|
||||
configurations to match the new format.
|
||||
|
||||
```diff
|
||||
[env]
|
||||
-ESP_HAL_PLACE_SPI_DRIVER_IN_RAM="true"
|
||||
+ESP_HAL_CONFIG_PLACE_SPI_DRIVER_IN_RAM="true"
|
||||
```
|
||||
|
||||
## UART changes
|
||||
|
||||
The `Config` struct's setters are now prefixed with `with_`. `parity_none`, `parity_even`,
|
||||
`parity_odd` have been replaced by `with_parity` that takes a `Parity` parameter.
|
||||
|
||||
```diff
|
||||
let config = Config::default()
|
||||
- .rx_fifo_full_threshold(30)
|
||||
+ .with_rx_fifo_full_threshold(30)
|
||||
- .parity_even();
|
||||
+ .with_parity(Parity::Even);
|
||||
```
|
||||
|
||||
The `DataBits`, `Parity`, and `StopBits` enum variants are no longer prefixed with the name of the enum.
|
||||
|
||||
e.g.
|
||||
|
||||
```diff
|
||||
- DataBits::DataBits8
|
||||
+ DataBits::_8
|
||||
- Parity::ParityNone
|
||||
+ Parity::None
|
||||
- StopBits::Stop1
|
||||
+ StopBits::_1
|
||||
```
|
||||
|
||||
The previous blocking implementation of `read_bytes` has been removed, and the non-blocking `drain_fifo` has instead been renamed to `read_bytes` in its place.
|
||||
|
||||
Any code which was previously using `read_bytes` to fill a buffer in a blocking manner will now need to implement the necessary logic to block until the buffer is filled in their application instead.
|
||||
|
||||
The `Error` enum variant uses object+verb naming.
|
||||
|
||||
e.g.
|
||||
|
||||
```diff
|
||||
- RxGlichDetected
|
||||
+ GlitchOccurred
|
||||
```
|
||||
|
||||
RX/TX pin assignment moved from constructors to builder functions.
|
||||
|
||||
e.g.
|
||||
|
||||
```diff
|
||||
let mut uart1 = Uart::new(
|
||||
peripherals.UART1,
|
||||
- Config::default(),
|
||||
- peripherals.GPIO1,
|
||||
- peripherals.GPIO2,
|
||||
- ).unwrap();
|
||||
+ Config::default())
|
||||
+ .unwrap()
|
||||
+ .with_rx(peripherals.GPIO1)
|
||||
+ .with_tx(peripherals.GPIO2);
|
||||
```
|
||||
|
||||
## Spi `with_miso` has been split
|
||||
|
||||
Previously, `with_miso` set up the provided pin as an input and output, which was necessary for half duplex.
|
||||
Full duplex does not require this, and it also creates an artificial restriction.
|
||||
|
||||
If you were using half duplex SPI with `with_miso`,
|
||||
you should now use `with_sio1` instead to get the previous behavior.
|
||||
|
||||
## CPU Clocks
|
||||
|
||||
The specific CPU clock variants are renamed from e.g. `Clock80MHz` to `_80MHz`.
|
||||
|
||||
```diff
|
||||
- CpuClock::Clock80MHz
|
||||
+ CpuClock::_80MHz
|
||||
```
|
||||
|
||||
Additionally the enum is marked as non-exhaustive.
|
||||
|
||||
## SPI Changes
|
||||
|
||||
The SPI mode variants are renamed from e.g. `Mode0` to `_0`.
|
||||
|
||||
```diff
|
||||
- Mode::Mode0
|
||||
+ Mode::_0
|
||||
```
|
||||
|
||||
The Address and Command enums have similarly had their variants changed from e.g. `Address1` to `_1Bit` and `Command1` to `_1Bit` respectively:
|
||||
|
||||
```diff
|
||||
- Address::Address1
|
||||
+ Address::_1Bit
|
||||
- Command::Command1
|
||||
+ Command::_1Bit
|
||||
```
|
||||
|
||||
## GPIO Changes
|
||||
|
||||
The GPIO drive strength variants are renamed from e.g. `I5mA` to `_5mA`.
|
||||
|
||||
```diff
|
||||
-DriveStrength::I5mA
|
||||
+DriveStrength::_5mA
|
||||
```
|
||||
|
||||
## ADC Changes
|
||||
|
||||
The ADC attenuation variants are renamed from e.g. `Attenuation0dB` to `_0dB`.
|
||||
|
||||
```diff
|
||||
-Attenuation::Attenuation0dB
|
||||
+Attenuation::_0dB
|
||||
```
|
||||
@ -1,59 +1,59 @@
|
||||
# esp-hal
|
||||
|
||||
[](https://crates.io/crates/esp-hal)
|
||||
[](https://docs.esp-rs.org/esp-hal)
|
||||

|
||||

|
||||
[](https://matrix.to/#/#esp-rs:matrix.org)
|
||||
|
||||
The `esp-hal` package aims to provide a safe, idiomatic Hardware Abstraction Layer (HAL) for the entire family of ESP32 devices from Espressif.
|
||||
Bare-metal (`no_std`) hardware abstraction layer for Espressif devices.
|
||||
|
||||
This package implements both blocking and, when able, asynchronous drivers for the various peripherals. At this time the blocking APIs are used by default, with all asynchronous functionality gated behind the `async` feature. See the package documentation for more information on its features.
|
||||
Implements a number of blocking and, where applicable, async traits from the various packages in the [embedded-hal] repository.
|
||||
|
||||
Most traits defined by the [embedded-hal] family of packages are implemented as applicable.
|
||||
For help getting started with this HAL, please refer to [The Rust on ESP Book] and the [documentation].
|
||||
|
||||
[embedded-hal]: https://github.com/rust-embedded/embedded-hal
|
||||
[the rust on esp book]: https://docs.esp-rs.org/book/
|
||||
|
||||
## [Documentation]
|
||||
|
||||
[documentation]: https://docs.esp-rs.org/esp-hal/
|
||||
|
||||
## Usage
|
||||
## Supported Devices
|
||||
|
||||
Before using `esp-hal`, ensure that you have configured your [development environment] correctly, and the [required tooling] has been installed.
|
||||
| Chip | Datasheet | Technical Reference Manual | Target |
|
||||
| :------: | :----------------------: | :------------------------: | :----------------------------: |
|
||||
| ESP32 | [ESP32][32-datasheet] | [ESP32][32-trm] | `xtensa-esp32-none-elf` |
|
||||
| ESP32-C2 | [ESP32-C2][c2-datasheet] | [ESP32-C2][c2-trm] | `riscv32imc-unknown-none-elf` |
|
||||
| ESP32-C3 | [ESP32-C3][c3-datasheet] | [ESP32-C3][c3-trm] | `riscv32imc-unknown-none-elf` |
|
||||
| ESP32-C6 | [ESP32-C6][c6-datasheet] | [ESP32-C6][c6-trm] | `riscv32imac-unknown-none-elf` |
|
||||
| ESP32-H2 | [ESP32-H2][h2-datasheet] | [ESP32-H2][h2-trm] | `riscv32imac-unknown-none-elf` |
|
||||
| ESP32-S2 | [ESP32-S2][s2-datasheet] | [ESP32-S2][s2-trm] | `xtensa-esp32s2-none-elf` |
|
||||
| ESP32-S3 | [ESP32-S3][s3-datasheet] | [ESP32-S3][s3-trm] | `xtensa-esp32s3-none-elf` |
|
||||
|
||||
When starting a new project using `esp-hal`, we strongly recommend you generate a project skeleton using [cargo-generate] and [esp-template]. This will take much of the guesswork out of the process and give you a starting point to build an application from.
|
||||
[32-datasheet]: https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf
|
||||
[c2-datasheet]: https://www.espressif.com/sites/default/files/documentation/esp8684_datasheet_en.pdf
|
||||
[c3-datasheet]: https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf
|
||||
[c6-datasheet]: https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf
|
||||
[h2-datasheet]: https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf
|
||||
[s2-datasheet]: https://www.espressif.com/sites/default/files/documentation/esp32-s2_datasheet_en.pdf
|
||||
[s3-datasheet]: https://www.espressif.com/sites/default/files/documentation/esp32-s3_datasheet_en.pdf
|
||||
[32-trm]: https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf
|
||||
[c2-trm]: https://www.espressif.com/sites/default/files/documentation/esp8684_technical_reference_manual_en.pdf
|
||||
[c3-trm]: https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf
|
||||
[c6-trm]: https://www.espressif.com/sites/default/files/documentation/esp32-c6_technical_reference_manual_en.pdf
|
||||
[h2-trm]: https://www.espressif.com/sites/default/files/documentation/esp32-h2_technical_reference_manual_en.pdf
|
||||
[s2-trm]: https://www.espressif.com/sites/default/files/documentation/esp32-s2_technical_reference_manual_en.pdf
|
||||
[s3-trm]: https://www.espressif.com/sites/default/files/documentation/esp32-s3_technical_reference_manual_en.pdf
|
||||
|
||||
Much of the functionality available is feature-gated, so be sure to refer to the documentation to read about all available Cargo features.
|
||||
## `unstable` feature
|
||||
|
||||
[development environment]: https://esp-rs.github.io/book/installation/index.html
|
||||
[required tooling]: https://esp-rs.github.io/book/tooling/espflash.html
|
||||
[cargo-generate]: https://github.com/cargo-generate/cargo-generate/
|
||||
[esp-template]: https://github.com/esp-rs/esp-template/
|
||||
The stable feature set is designed to remain consistent and reliable. Other parts guarded by the `unstable` feature, however, are still under active development and may undergo breaking changes and are disabled by default.
|
||||
|
||||
### `defmt` Feature
|
||||
## Minimum Supported Rust Version (MSRV)
|
||||
|
||||
Please note that `defmt` does _not_ provide MSRV guarantees with releases, and as such we are not able to make any MSRV guarantees when this feature is enabled. For more information refer to the MSRV section of `defmt`'s README:
|
||||
https://github.com/knurling-rs/defmt?tab=readme-ov-file#msrv
|
||||
|
||||
### Supporting Packages
|
||||
|
||||
A number of additional packages are available which add additional functionality beyond the HAL.
|
||||
|
||||
Within this repository, the [esp-lp-hal] package provides support for the (ultra-)low-power RISC-V coprocessors found aboard the ESP32-C6, ESP32-S2, and ESP32-S3.
|
||||
|
||||
There is also the [esp-wifi] package, which provides support for Bluetooth and Wi-Fi.
|
||||
|
||||
For additional libraries, you can check the [list of repositories] in the [esp-rs organization].
|
||||
|
||||
[esp-lp-hal]: ../esp-lp-hal/
|
||||
[esp-wifi]: https://github.com/esp-rs/esp-wifi
|
||||
[list of repositories]: https://github.com/orgs/esp-rs/repositories
|
||||
[esp-rs organization]: https://github.com/esp-rs
|
||||
|
||||
## Examples
|
||||
|
||||
Examples demonstrating the use of various peripherals and features of the HAL are available in the [examples] package.
|
||||
|
||||
[examples]: ../examples/
|
||||
This crate is guaranteed to compile on stable Rust 1.83 and up. It _might_
|
||||
compile with older versions but that may change in any new patch release.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
295
esp-hal/build.rs
295
esp-hal/build.rs
@ -4,129 +4,32 @@ use std::{
|
||||
fs::{self, File},
|
||||
io::{BufRead, Write},
|
||||
path::{Path, PathBuf},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
// Macros taken from:
|
||||
// https://github.com/TheDan64/inkwell/blob/36c3b10/src/lib.rs#L81-L110
|
||||
|
||||
// Given some features, assert that AT MOST one of the features is enabled.
|
||||
macro_rules! assert_unique_features {
|
||||
() => {};
|
||||
|
||||
( $first:tt $(,$rest:tt)* ) => {
|
||||
$(
|
||||
#[cfg(all(feature = $first, feature = $rest))]
|
||||
compile_error!(concat!("Features \"", $first, "\" and \"", $rest, "\" cannot be used together"));
|
||||
)*
|
||||
assert_unique_features!($($rest),*);
|
||||
};
|
||||
}
|
||||
|
||||
// Given some features, assert that AT LEAST one of the features is enabled.
|
||||
macro_rules! assert_used_features {
|
||||
( $all:tt ) => {
|
||||
#[cfg(not(feature = $all))]
|
||||
compile_error!(concat!("The feature flag must be provided: ", $all));
|
||||
};
|
||||
|
||||
( $($all:tt),+ ) => {
|
||||
#[cfg(not(any($(feature = $all),*)))]
|
||||
compile_error!(concat!("One of the feature flags must be provided: ", $($all, ", "),*));
|
||||
};
|
||||
}
|
||||
|
||||
// Given some features, assert that EXACTLY one of the features is enabled.
|
||||
macro_rules! assert_unique_used_features {
|
||||
( $($all:tt),* ) => {
|
||||
assert_unique_features!($($all),*);
|
||||
assert_used_features!($($all),*);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
enum Arch {
|
||||
#[serde(rename = "riscv")]
|
||||
RiscV,
|
||||
#[serde(rename = "xtensa")]
|
||||
Xtensa,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Arch {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
Arch::RiscV => "riscv",
|
||||
Arch::Xtensa => "xtensa",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
enum CoreCount {
|
||||
#[serde(rename = "single_core")]
|
||||
Single,
|
||||
#[serde(rename = "multi_core")]
|
||||
Multi,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for CoreCount {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
CoreCount::Single => "single_core",
|
||||
CoreCount::Multi => "multi_core",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Device {
|
||||
pub arch: Arch,
|
||||
pub cores: CoreCount,
|
||||
pub peripherals: Vec<String>,
|
||||
pub symbols: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Config {
|
||||
pub device: Device,
|
||||
}
|
||||
use esp_build::assert_unique_used_features;
|
||||
use esp_config::{generate_config, Value};
|
||||
use esp_metadata::{Chip, Config};
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
println!("cargo:rustc-check-cfg=cfg(is_debug_build)");
|
||||
if let Ok(level) = std::env::var("OPT_LEVEL") {
|
||||
if level == "0" || level == "1" {
|
||||
println!("cargo:rustc-cfg=is_debug_build");
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: update when adding new device support!
|
||||
// Ensure that exactly one chip has been specified:
|
||||
assert_unique_used_features!(
|
||||
"esp32", "esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32p4", "esp32s2", "esp32s3"
|
||||
"esp32", "esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32s2", "esp32s3"
|
||||
);
|
||||
|
||||
// If the `embassy` feature is enabled, ensure that a time driver implementation
|
||||
// is available:
|
||||
#[cfg(feature = "embassy")]
|
||||
{
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "esp32")] {
|
||||
assert_unique_used_features!("embassy-time-timg0");
|
||||
} else if #[cfg(feature = "esp32s2")] {
|
||||
assert_unique_used_features!("embassy-time-systick-80mhz", "embassy-time-timg0");
|
||||
} else {
|
||||
assert_unique_used_features!("embassy-time-systick-16mhz", "embassy-time-timg0");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "flip-link")]
|
||||
{
|
||||
#[cfg(not(any(feature = "esp32c6", feature = "esp32h2")))]
|
||||
panic!("flip-link is only available on ESP32-C6/ESP32-H2");
|
||||
}
|
||||
#[cfg(all(
|
||||
feature = "flip-link",
|
||||
not(any(feature = "esp32c6", feature = "esp32h2"))
|
||||
))]
|
||||
esp_build::error!("flip-link is only available on ESP32-C6/ESP32-H2");
|
||||
|
||||
// NOTE: update when adding new device support!
|
||||
// Determine the name of the configured device:
|
||||
@ -140,8 +43,6 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
"esp32c6"
|
||||
} else if cfg!(feature = "esp32h2") {
|
||||
"esp32h2"
|
||||
} else if cfg!(feature = "esp32p4") {
|
||||
"esp32p4"
|
||||
} else if cfg!(feature = "esp32s2") {
|
||||
"esp32s2"
|
||||
} else if cfg!(feature = "esp32s3") {
|
||||
@ -150,76 +51,78 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
unreachable!() // We've confirmed exactly one known device was selected
|
||||
};
|
||||
|
||||
if detect_atomic_extension("a") || detect_atomic_extension("s32c1i") {
|
||||
panic!(
|
||||
"Atomic emulation flags detected in `.cargo/config.toml`, this is no longer supported!"
|
||||
);
|
||||
}
|
||||
|
||||
// Load the configuration file for the configured device:
|
||||
let chip_toml_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("devices")
|
||||
.join(device_name)
|
||||
.join("device.toml")
|
||||
.canonicalize()?;
|
||||
|
||||
let config = fs::read_to_string(chip_toml_path)?;
|
||||
let config: Config = basic_toml::from_str(&config)?;
|
||||
let device = &config.device;
|
||||
let chip = Chip::from_str(device_name)?;
|
||||
let config = Config::for_chip(&chip);
|
||||
|
||||
// Check PSRAM features are only given if the target supports PSRAM:
|
||||
if !&device.symbols.contains(&String::from("psram"))
|
||||
&& (cfg!(feature = "psram-2m") || cfg!(feature = "psram-4m") || cfg!(feature = "psram-8m"))
|
||||
{
|
||||
if !config.contains(&String::from("psram")) && cfg!(feature = "quad-psram") {
|
||||
panic!("The target does not support PSRAM");
|
||||
}
|
||||
|
||||
// Don't support "interrupt-preemption" and "direct-vectoring" on Xtensa and
|
||||
// RISC-V with CLIC:
|
||||
if (device.symbols.contains(&String::from("clic")) || device.arch == Arch::Xtensa)
|
||||
&& (cfg!(feature = "direct-vectoring") || cfg!(feature = "interrupt-preemption"))
|
||||
{
|
||||
panic!("The target does not support interrupt-preemption and direct-vectoring");
|
||||
if !config.contains(&String::from("octal_psram")) && cfg!(feature = "octal-psram") {
|
||||
panic!("The target does not support Octal PSRAM");
|
||||
}
|
||||
|
||||
// Define all necessary configuration symbols for the configured device:
|
||||
println!("cargo:rustc-cfg={}", device_name);
|
||||
println!("cargo:rustc-cfg={}", device.arch);
|
||||
println!("cargo:rustc-cfg={}", device.cores);
|
||||
|
||||
for peripheral in &device.peripherals {
|
||||
println!("cargo:rustc-cfg={peripheral}");
|
||||
}
|
||||
|
||||
for symbol in &device.symbols {
|
||||
println!("cargo:rustc-cfg={symbol}");
|
||||
}
|
||||
|
||||
let mut config_symbols = Vec::new();
|
||||
let arch = device.arch.to_string();
|
||||
let cores = device.cores.to_string();
|
||||
config_symbols.push(device_name);
|
||||
config_symbols.push(&arch);
|
||||
config_symbols.push(&cores);
|
||||
|
||||
for peripheral in &device.peripherals {
|
||||
config_symbols.push(peripheral);
|
||||
}
|
||||
|
||||
for symbol in &device.symbols {
|
||||
config_symbols.push(symbol);
|
||||
}
|
||||
|
||||
#[cfg(feature = "flip-link")]
|
||||
config_symbols.push("flip-link");
|
||||
config.define_symbols();
|
||||
|
||||
// Place all linker scripts in `OUT_DIR`, and instruct Cargo how to find these
|
||||
// files:
|
||||
let out = PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||
println!("cargo:rustc-link-search={}", out.display());
|
||||
|
||||
// emit config
|
||||
let cfg = generate_config(
|
||||
"esp_hal",
|
||||
&[
|
||||
(
|
||||
"place-spi-driver-in-ram",
|
||||
"Places the SPI driver in RAM for better performance",
|
||||
Value::Bool(false),
|
||||
None
|
||||
),
|
||||
(
|
||||
"spi-address-workaround",
|
||||
"(ESP32 only) Enables a workaround for the issue where SPI in half-duplex mode incorrectly transmits the address on a single line if the data buffer is empty.",
|
||||
Value::Bool(true),
|
||||
None
|
||||
),
|
||||
(
|
||||
"place-switch-tables-in-ram",
|
||||
"Places switch-tables, some lookup tables and constants related to interrupt handling into RAM - resulting in better performance but slightly more RAM consumption.",
|
||||
Value::Bool(true),
|
||||
None
|
||||
),
|
||||
(
|
||||
"place-anon-in-ram",
|
||||
"Places anonymous symbols into RAM - resulting in better performance at the cost of significant more RAM consumption. Best to be combined with `place-switch-tables-in-ram`.",
|
||||
Value::Bool(false),
|
||||
None
|
||||
),
|
||||
],
|
||||
true,
|
||||
);
|
||||
|
||||
// RISC-V and Xtensa devices each require some special handling and processing
|
||||
// of linker scripts:
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut config_symbols = config.all().collect::<Vec<_>>();
|
||||
#[cfg(feature = "flip-link")]
|
||||
config_symbols.push("flip-link");
|
||||
|
||||
for (key, value) in &cfg {
|
||||
if let Value::Bool(true) = value {
|
||||
config_symbols.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
if cfg!(feature = "esp32") || cfg!(feature = "esp32s2") || cfg!(feature = "esp32s3") {
|
||||
fs::copy("ld/xtensa/hal-defaults.x", out.join("hal-defaults.x"))?;
|
||||
// Xtensa devices:
|
||||
|
||||
#[cfg(any(feature = "esp32", feature = "esp32s2"))]
|
||||
File::create(out.join("memory_extras.x"))?.write_all(&generate_memory_extras())?;
|
||||
|
||||
let (irtc, drtc) = if cfg!(feature = "esp32s3") {
|
||||
("rtc_fast_seg", "rtc_fast_seg")
|
||||
@ -240,27 +143,32 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
);
|
||||
|
||||
fs::write(out.join("alias.x"), alias)?;
|
||||
fs::copy("ld/xtensa/hal-defaults.x", out.join("hal-defaults.x"))?;
|
||||
} else {
|
||||
// RISC-V devices:
|
||||
|
||||
preprocess_file(&config_symbols, "ld/riscv/asserts.x", out.join("asserts.x"))?;
|
||||
preprocess_file(&config_symbols, "ld/riscv/debug.x", out.join("debug.x"))?;
|
||||
preprocess_file(
|
||||
&config_symbols,
|
||||
"ld/riscv/hal-defaults.x",
|
||||
out.join("hal-defaults.x"),
|
||||
)?;
|
||||
preprocess_file(&config_symbols, "ld/riscv/asserts.x", out.join("asserts.x"))?;
|
||||
preprocess_file(&config_symbols, "ld/riscv/debug.x", out.join("debug.x"))?;
|
||||
}
|
||||
|
||||
// With the architecture-specific linker scripts taken care of, we can copy all
|
||||
// remaining linker scripts which are common to all devices:
|
||||
copy_dir_all(&config_symbols, "ld/sections", &out)?;
|
||||
copy_dir_all(&config_symbols, format!("ld/{device_name}"), &out)?;
|
||||
|
||||
#[cfg(any(feature = "esp32", feature = "esp32s2"))]
|
||||
File::create(out.join("memory_extras.x"))?.write_all(&generate_memory_extras())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helper Functions
|
||||
|
||||
fn copy_dir_all(
|
||||
config_symbols: &Vec<&str>,
|
||||
config_symbols: &[&str],
|
||||
src: impl AsRef<Path>,
|
||||
dst: impl AsRef<Path>,
|
||||
) -> std::io::Result<()> {
|
||||
@ -301,8 +209,7 @@ fn preprocess_file(
|
||||
let line = line?;
|
||||
let trimmed = line.trim();
|
||||
|
||||
if let Some(stripped) = trimmed.strip_prefix("#IF ") {
|
||||
let condition = stripped;
|
||||
if let Some(condition) = trimmed.strip_prefix("#IF ") {
|
||||
let should_take = take.iter().all(|v| *v);
|
||||
let should_take = should_take && config.contains(&condition);
|
||||
take.push(should_take);
|
||||
@ -326,32 +233,6 @@ fn preprocess_file(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn detect_atomic_extension(ext: &str) -> bool {
|
||||
let rustflags = env::var_os("CARGO_ENCODED_RUSTFLAGS")
|
||||
.unwrap()
|
||||
.into_string()
|
||||
.unwrap();
|
||||
|
||||
// Users can pass -Ctarget-feature to the compiler multiple times, so we have to
|
||||
// handle that
|
||||
let target_flags = rustflags
|
||||
.split(0x1f as char)
|
||||
.filter_map(|s| s.strip_prefix("target-feature="));
|
||||
for tf in target_flags {
|
||||
let tf = tf
|
||||
.split(',')
|
||||
.map(|s| s.trim())
|
||||
.filter_map(|s| s.strip_prefix('+'));
|
||||
for tf in tf {
|
||||
if tf == ext {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(feature = "esp32")]
|
||||
fn generate_memory_extras() -> Vec<u8> {
|
||||
let reserve_dram = if cfg!(feature = "bluetooth") {
|
||||
@ -372,11 +253,7 @@ fn generate_memory_extras() -> Vec<u8> {
|
||||
|
||||
#[cfg(feature = "esp32s2")]
|
||||
fn generate_memory_extras() -> Vec<u8> {
|
||||
let reserved_cache = if cfg!(any(
|
||||
feature = "psram-2m",
|
||||
feature = "psram-4m",
|
||||
feature = "psram-8m"
|
||||
)) {
|
||||
let reserved_cache = if cfg!(feature = "quad-psram") {
|
||||
"0x4000"
|
||||
} else {
|
||||
"0x2000"
|
||||
|
||||
8
esp-hal/ld/README.md
Normal file
8
esp-hal/ld/README.md
Normal file
@ -0,0 +1,8 @@
|
||||
# ROM functions
|
||||
|
||||
Files in the `rom` subdirectories are taken from esp-idf
|
||||
|
||||
- DON'T include any `*newlib*` functions
|
||||
- systimer, wdt and mbedtls shouldn't be included
|
||||
- make sure to align the version you take the files from with esp-wifi-sys - NEVER randomly sync the files with other versions
|
||||
- some additional functions are needed from ROM - see `additional.ld` (these are usually defined in the `*newlib*` files)
|
||||
@ -8,6 +8,10 @@ PROVIDE(__zero_bss = default_mem_hook);
|
||||
PROVIDE(__init_data = default_mem_hook);
|
||||
PROVIDE(__post_init = default_post_init);
|
||||
|
||||
PROVIDE(__level_1_interrupt = handle_interrupts);
|
||||
PROVIDE(__level_2_interrupt = handle_interrupts);
|
||||
PROVIDE(__level_3_interrupt = handle_interrupts);
|
||||
|
||||
INCLUDE exception.x
|
||||
|
||||
/* ESP32 fixups */
|
||||
@ -15,22 +19,16 @@ INCLUDE "fixups/rtc_fast_rwdata_dummy.x"
|
||||
/* END ESP32 fixups */
|
||||
|
||||
/* Shared sections - ordering matters */
|
||||
INCLUDE "text.x"
|
||||
INCLUDE "rodata.x"
|
||||
INCLUDE "rwtext.x"
|
||||
INCLUDE "text.x"
|
||||
INCLUDE "rwdata.x"
|
||||
INCLUDE "rodata.x"
|
||||
INCLUDE "rtc_fast.x"
|
||||
INCLUDE "rtc_slow.x"
|
||||
INCLUDE "stack.x"
|
||||
INCLUDE "dram2.x"
|
||||
/* End of Shared sections */
|
||||
|
||||
/* an uninitialized section for use as the wifi-heap in esp-wifi */
|
||||
SECTIONS {
|
||||
.dram2_uninit (NOLOAD) : ALIGN(4) {
|
||||
*(.dram2_uninit)
|
||||
} > dram2_seg
|
||||
}
|
||||
|
||||
EXTERN(DefaultHandler);
|
||||
|
||||
EXTERN(WIFI_EVENT); /* Force inclusion of WiFi libraries */
|
||||
|
||||
@ -25,10 +25,18 @@ MEMORY
|
||||
* The segment dram2_seg after the rom data space is not mentioned in the esp32 linker scripts in esp-idf, instead the space after is used as heap space.
|
||||
* It seems not all rom data space is reserved, but only "core"/"important" ROM functions that may be called after booting from ROM.
|
||||
*/
|
||||
reserved_rom_data_pro : ORIGIN = 0X3FFE0000, len = 1088
|
||||
reserved_rom_data_app : ORIGIN = 0X3FFE3F20, len = 1072
|
||||
reserved_rom_data_pro : ORIGIN = 0x3ffe0000, len = 1088
|
||||
reserved_rom_data_app : ORIGIN = 0x3ffe3f20, len = 1072
|
||||
|
||||
dram2_seg : ORIGIN = 0x3FFE4350, len = 111k /* the rest of DRAM after the rom data segments in the middle */
|
||||
/*
|
||||
* The following values are derived from the __stack and _stack_sentry values from ROM.
|
||||
* They represent the stacks used for each core setup by ROM code. In theory both of these
|
||||
* can be reclaimed once both cores are running, but for now we play it safe and reserve them both.
|
||||
*/
|
||||
reserved_rom_stack_pro : ORIGIN = 0x3ffe1320, len = 11264
|
||||
reserved_rom_stack_app : ORIGIN = 0x3ffe5230, len = 11264
|
||||
|
||||
dram2_seg : ORIGIN = 0x3ffe7e30, len = 98767 /* the rest of DRAM after the rom data segments and rom stacks in the middle */
|
||||
|
||||
/* external flash
|
||||
The 0x20 offset is a convenience for the app binary image generation.
|
||||
|
||||
@ -1,34 +1,9 @@
|
||||
PROVIDE(ets_delay_us = 0x40008534);
|
||||
PROVIDE(ets_update_cpu_frequency_rom = 0x40008550);
|
||||
PROVIDE(rom_i2c_writeReg = 0x400041a4);
|
||||
PROVIDE(rom_i2c_writeReg_Mask = 0x400041fc);
|
||||
PROVIDE(rtc_get_reset_reason = 0x400081d4);
|
||||
PROVIDE(software_reset = 0x4000824c);
|
||||
PROVIDE(software_reset_cpu = 0x40008264);
|
||||
INCLUDE "rom/esp32.rom.api.ld"
|
||||
INCLUDE "rom/esp32.rom.eco3.ld"
|
||||
INCLUDE "rom/esp32.rom.ld"
|
||||
INCLUDE "rom/esp32.rom.libgcc.ld"
|
||||
INCLUDE "rom/esp32.rom.redefined.ld"
|
||||
INCLUDE "rom/esp32.rom.spiflash_legacy.ld"
|
||||
INCLUDE "rom/esp32.rom.syscalls.ld"
|
||||
|
||||
PROVIDE ( ets_efuse_get_spiconfig = 0x40008658 );
|
||||
PROVIDE ( esp_rom_efuse_get_flash_gpio_info = ets_efuse_get_spiconfig );
|
||||
PROVIDE ( esp_rom_gpio_connect_out_signal = gpio_matrix_out );
|
||||
PROVIDE ( gpio_matrix_out = 0x40009f0c );
|
||||
PROVIDE ( gpio_matrix_in = 0x40009edc );
|
||||
PROVIDE ( esp_rom_gpio_connect_in_signal = gpio_matrix_in );
|
||||
PROVIDE ( esp_rom_spiflash_config_clk = 0x40062bc8 );
|
||||
PROVIDE ( g_rom_spiflash_dummy_len_plus = 0x3ffae290 );
|
||||
PROVIDE ( g_rom_flashchip = 0x3ffae270 );
|
||||
PROVIDE ( cache_sram_mmu_set_rom = 0x400097f4 );
|
||||
|
||||
PROVIDE (esp_rom_crc32_be = 0x4005d024);
|
||||
PROVIDE (esp_rom_crc16_be = 0x4005d09c);
|
||||
PROVIDE (esp_rom_crc8_be = 0x4005d114);
|
||||
PROVIDE (esp_rom_crc32_le = 0x4005cfec);
|
||||
PROVIDE (esp_rom_crc16_le = 0x4005d05c);
|
||||
PROVIDE (esp_rom_crc8_le = 0x4005d0e0);
|
||||
|
||||
PROVIDE (esp_rom_md5_init = 0x4005da7c);
|
||||
PROVIDE (esp_rom_md5_update = 0x4005da9c);
|
||||
PROVIDE (esp_rom_md5_final = 0x4005db1c);
|
||||
|
||||
memcmp = 0x4000c260;
|
||||
memcpy = 0x4000c2c8;
|
||||
memmove = 0x4000c3c0;
|
||||
memset = 0x4000c44c;
|
||||
INCLUDE "rom/additional.ld"
|
||||
|
||||
20
esp-hal/ld/esp32/rom/additional.ld
Normal file
20
esp-hal/ld/esp32/rom/additional.ld
Normal file
@ -0,0 +1,20 @@
|
||||
memcmp = 0x4000c260;
|
||||
memcpy = 0x4000c2c8;
|
||||
memmove = 0x4000c3c0;
|
||||
memset = 0x4000c44c;
|
||||
|
||||
PROVIDE ( strcpy = 0x400013ac );
|
||||
PROVIDE ( abs = 0x40056340 );
|
||||
PROVIDE ( strncpy = 0x400015d4 );
|
||||
PROVIDE ( strncmp = 0x4000c5f4 );
|
||||
|
||||
PROVIDE ( bzero = 0x4000c1f4 );
|
||||
|
||||
PROVIDE ( strcat = 0x4000c518 );
|
||||
PROVIDE ( strcmp = 0x40001274 );
|
||||
PROVIDE ( strchr = 0x4000c53c );
|
||||
PROVIDE ( strlcpy = 0x4000c584 );
|
||||
PROVIDE ( strstr = 0x4000c674 );
|
||||
PROVIDE ( strcasecmp = 0x400011cc );
|
||||
|
||||
PROVIDE ( memchr = 0x4000c244 );
|
||||
62
esp-hal/ld/esp32/rom/esp32.rom.api.ld
Normal file
62
esp-hal/ld/esp32/rom/esp32.rom.api.ld
Normal file
@ -0,0 +1,62 @@
|
||||
/**
|
||||
* ROM APIs
|
||||
*/
|
||||
PROVIDE ( esp_rom_crc32_le = crc32_le );
|
||||
PROVIDE ( esp_rom_crc16_le = crc16_le );
|
||||
PROVIDE ( esp_rom_crc8_le = crc8_le );
|
||||
PROVIDE ( esp_rom_crc32_be = crc32_be );
|
||||
PROVIDE ( esp_rom_crc16_be = crc16_be );
|
||||
PROVIDE ( esp_rom_crc8_be = crc8_be );
|
||||
|
||||
PROVIDE ( esp_rom_gpio_pad_select_gpio = gpio_pad_select_gpio );
|
||||
PROVIDE ( esp_rom_gpio_pad_pullup_only = gpio_pad_pullup );
|
||||
PROVIDE ( esp_rom_gpio_pad_set_drv = gpio_pad_set_drv );
|
||||
PROVIDE ( esp_rom_gpio_pad_unhold = gpio_pad_unhold );
|
||||
PROVIDE ( esp_rom_gpio_connect_in_signal = gpio_matrix_in );
|
||||
PROVIDE ( esp_rom_gpio_connect_out_signal = gpio_matrix_out );
|
||||
|
||||
PROVIDE ( esp_rom_efuse_mac_address_crc8 = esp_crc8 );
|
||||
PROVIDE ( esp_rom_efuse_get_flash_gpio_info = ets_efuse_get_spiconfig );
|
||||
PROVIDE ( esp_rom_efuse_is_secure_boot_enabled = ets_efuse_secure_boot_enabled );
|
||||
|
||||
PROVIDE ( esp_rom_uart_flush_tx = uart_tx_flush );
|
||||
PROVIDE ( esp_rom_uart_tx_one_char = uart_tx_one_char );
|
||||
PROVIDE ( esp_rom_uart_tx_wait_idle = uart_tx_wait_idle );
|
||||
PROVIDE ( esp_rom_uart_rx_one_char = uart_rx_one_char );
|
||||
PROVIDE ( esp_rom_uart_rx_string = UartRxString );
|
||||
PROVIDE ( esp_rom_uart_set_as_console = uart_tx_switch );
|
||||
PROVIDE ( esp_rom_uart_putc = ets_write_char_uart );
|
||||
PROVIDE ( esp_rom_uart_switch_buffer = uart_buff_switch );
|
||||
|
||||
PROVIDE ( esp_rom_output_flush_tx = uart_tx_flush );
|
||||
PROVIDE ( esp_rom_output_tx_one_char = uart_tx_one_char );
|
||||
PROVIDE ( esp_rom_output_tx_wait_idle = uart_tx_wait_idle );
|
||||
PROVIDE ( esp_rom_output_rx_one_char = uart_rx_one_char );
|
||||
PROVIDE ( esp_rom_output_rx_string = UartRxString );
|
||||
PROVIDE ( esp_rom_output_set_as_console = uart_tx_switch );
|
||||
PROVIDE ( esp_rom_output_putc = ets_write_char_uart );
|
||||
PROVIDE ( esp_rom_output_switch_buffer = uart_buff_switch );
|
||||
|
||||
/* wpa_supplicant re-implements the MD5 functions: MD5Init, MD5Update, MD5Final */
|
||||
/* so here we directly assign the symbols with the ROM API address */
|
||||
PROVIDE ( esp_rom_md5_init = 0x4005da7c );
|
||||
PROVIDE ( esp_rom_md5_update = 0x4005da9c );
|
||||
PROVIDE ( esp_rom_md5_final = 0x4005db1c );
|
||||
|
||||
PROVIDE ( esp_rom_software_reset_system = software_reset );
|
||||
PROVIDE ( esp_rom_software_reset_cpu = software_reset_cpu );
|
||||
|
||||
PROVIDE ( esp_rom_printf = ets_printf );
|
||||
PROVIDE ( esp_rom_delay_us = ets_delay_us );
|
||||
PROVIDE ( esp_rom_install_uart_printf = ets_install_uart_printf );
|
||||
PROVIDE ( esp_rom_get_reset_reason = rtc_get_reset_reason );
|
||||
PROVIDE ( esp_rom_route_intr_matrix = intr_matrix_set );
|
||||
PROVIDE ( esp_rom_get_cpu_ticks_per_us = ets_get_cpu_frequency );
|
||||
|
||||
PROVIDE ( esp_rom_spiflash_set_bp = esp_rom_spiflash_lock );
|
||||
PROVIDE ( esp_rom_spiflash_write_enable = SPI_write_enable);
|
||||
|
||||
PROVIDE ( esp_rom_regi2c_read = rom_i2c_readReg );
|
||||
PROVIDE ( esp_rom_regi2c_read_mask = rom_i2c_readReg_Mask );
|
||||
PROVIDE ( esp_rom_regi2c_write = rom_i2c_writeReg );
|
||||
PROVIDE ( esp_rom_regi2c_write_mask = rom_i2c_writeReg_Mask );
|
||||
10
esp-hal/ld/esp32/rom/esp32.rom.eco3.ld
Normal file
10
esp-hal/ld/esp32/rom/esp32.rom.eco3.ld
Normal file
@ -0,0 +1,10 @@
|
||||
/*
|
||||
ESP32 ECO3 ROM address table
|
||||
Secure Boot Version 2 API's imported from the ROM
|
||||
*/
|
||||
PROVIDE ( ets_secure_boot_verify_signature = 0x4006543c);
|
||||
PROVIDE ( ets_secure_boot_verify_boot_bootloader = 0x400655ec);
|
||||
PROVIDE ( ets_use_secure_boot_v2 = 0x4000f8d4);
|
||||
PROVIDE ( ets_rsa_pss_verify = 0x40065310);
|
||||
PROVIDE ( ets_mgf1_sha256 = 0x400651a8);
|
||||
PROVIDE ( ets_emsa_pss_verify = 0x4006520c);
|
||||
1635
esp-hal/ld/esp32/rom/esp32.rom.ld
Normal file
1635
esp-hal/ld/esp32/rom/esp32.rom.ld
Normal file
File diff suppressed because it is too large
Load Diff
94
esp-hal/ld/esp32/rom/esp32.rom.libgcc.ld
Normal file
94
esp-hal/ld/esp32/rom/esp32.rom.libgcc.ld
Normal file
@ -0,0 +1,94 @@
|
||||
/* Unlike other ROM functions which are exported using PROVIDE, which declares
|
||||
weak symbols, these libgcc functions are exported using assignment,
|
||||
which declares strong symbols. This is done so that ROM functions are always
|
||||
used instead of the ones provided by libgcc.a.
|
||||
*/
|
||||
|
||||
__absvdi2 = 0x4006387c;
|
||||
__absvsi2 = 0x40063868;
|
||||
__adddf3 = 0x40002590;
|
||||
__addsf3 = 0x400020e8;
|
||||
__addvdi3 = 0x40002cbc;
|
||||
__addvsi3 = 0x40002c98;
|
||||
__ashldi3 = 0x4000c818;
|
||||
__ashrdi3 = 0x4000c830;
|
||||
__bswapdi2 = 0x40064b08;
|
||||
__bswapsi2 = 0x40064ae0;
|
||||
__clrsbdi2 = 0x40064b7c;
|
||||
__clrsbsi2 = 0x40064b64;
|
||||
__clzdi2 = 0x4000ca50;
|
||||
__clzsi2 = 0x4000c7e8;
|
||||
__cmpdi2 = 0x40063820;
|
||||
__ctzdi2 = 0x4000ca64;
|
||||
__ctzsi2 = 0x4000c7f0;
|
||||
__divdc3 = 0x400645a4;
|
||||
__divdf3 = 0x40002954;
|
||||
__divdi3 = 0x4000ca84;
|
||||
__divsi3 = 0x4000c7b8;
|
||||
__eqdf2 = 0x400636a8;
|
||||
__eqsf2 = 0x40063374;
|
||||
__extendsfdf2 = 0x40002c34;
|
||||
__ffsdi2 = 0x4000ca2c;
|
||||
__ffssi2 = 0x4000c804;
|
||||
__fixdfdi = 0x40002ac4;
|
||||
__fixdfsi = 0x40002a78;
|
||||
__fixsfdi = 0x4000244c;
|
||||
__fixsfsi = 0x4000240c;
|
||||
__fixunsdfsi = 0x40002b30;
|
||||
__fixunssfdi = 0x40002504;
|
||||
__fixunssfsi = 0x400024ac;
|
||||
__floatdidf = 0x4000c988;
|
||||
__floatdisf = 0x4000c8c0;
|
||||
__floatsidf = 0x4000c944;
|
||||
__floatsisf = 0x4000c870;
|
||||
__floatundidf = 0x4000c978;
|
||||
__floatundisf = 0x4000c8b0;
|
||||
__floatunsidf = 0x4000c938;
|
||||
__floatunsisf = 0x4000c864;
|
||||
__gcc_bcmp = 0x40064a70;
|
||||
__gedf2 = 0x40063768;
|
||||
__gesf2 = 0x4006340c;
|
||||
__gtdf2 = 0x400636dc;
|
||||
__gtsf2 = 0x400633a0;
|
||||
__ledf2 = 0x40063704;
|
||||
__lesf2 = 0x400633c0;
|
||||
__lshrdi3 = 0x4000c84c;
|
||||
__ltdf2 = 0x40063790;
|
||||
__ltsf2 = 0x4006342c;
|
||||
__moddi3 = 0x4000cd4c;
|
||||
__modsi3 = 0x4000c7c0;
|
||||
__muldc3 = 0x40063c90;
|
||||
__muldf3 = 0x4006358c;
|
||||
__muldi3 = 0x4000c9fc;
|
||||
__mulsf3 = 0x400632c8;
|
||||
__mulsi3 = 0x4000c7b0;
|
||||
__mulvdi3 = 0x40002d78;
|
||||
__mulvsi3 = 0x40002d60;
|
||||
__nedf2 = 0x400636a8;
|
||||
__negdf2 = 0x400634a0;
|
||||
__negdi2 = 0x4000ca14;
|
||||
__negsf2 = 0x400020c0;
|
||||
__negvdi2 = 0x40002e98;
|
||||
__negvsi2 = 0x40002e78;
|
||||
__nesf2 = 0x40063374;
|
||||
__nsau_data = 0x3ff96544;
|
||||
__paritysi2 = 0x40002f3c;
|
||||
__popcount_tab = 0x3ff96544;
|
||||
__popcountdi2 = 0x40002ef8;
|
||||
__popcountsi2 = 0x40002ed0;
|
||||
__powidf2 = 0x400638e4;
|
||||
__subdf3 = 0x400026e4;
|
||||
__subsf3 = 0x400021d0;
|
||||
__subvdi3 = 0x40002d20;
|
||||
__subvsi3 = 0x40002cf8;
|
||||
__truncdfsf2 = 0x40002b90;
|
||||
__ucmpdi2 = 0x40063840;
|
||||
__udiv_w_sdiv = 0x40064bec;
|
||||
__udivdi3 = 0x4000cff8;
|
||||
__udivmoddi4 = 0x40064bf4;
|
||||
__udivsi3 = 0x4000c7c8;
|
||||
__umoddi3 = 0x4000d280;
|
||||
__umodsi3 = 0x4000c7d0;
|
||||
__umulsidi3 = 0x4000c7d8;
|
||||
__unorddf2 = 0x400637f4;
|
||||
__unordsf2 = 0x40063478;
|
||||
38
esp-hal/ld/esp32/rom/esp32.rom.redefined.ld
Normal file
38
esp-hal/ld/esp32/rom/esp32.rom.redefined.ld
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
ROM Functions defined in this file are not used in ESP-IDF as is,
|
||||
and different definitions for functions with the same names are provided.
|
||||
This file is not used when linking ESP-IDF and is intended for reference only
|
||||
*/
|
||||
|
||||
PROVIDE ( abort = 0x4000bba4 );
|
||||
PROVIDE ( aes_128_cbc_decrypt = 0x4005cc7c );
|
||||
PROVIDE ( aes_128_cbc_encrypt = 0x4005cc18 );
|
||||
PROVIDE ( aes_unwrap = 0x4005ccf0 );
|
||||
PROVIDE ( base64_decode = 0x4005ced8 );
|
||||
PROVIDE ( base64_encode = 0x4005cdbc );
|
||||
PROVIDE ( ets_isr_mask = 0x400067fc );
|
||||
PROVIDE ( ets_isr_unmask = 0x40006808 );
|
||||
PROVIDE ( ets_timer_arm = 0x40008368 );
|
||||
PROVIDE ( ets_timer_arm_us = 0x400083ac );
|
||||
PROVIDE ( ets_timer_disarm = 0x400083ec );
|
||||
PROVIDE ( ets_timer_done = 0x40008428 );
|
||||
PROVIDE ( ets_timer_init = 0x400084e8 );
|
||||
PROVIDE ( ets_timer_handler_isr = 0x40008454 );
|
||||
PROVIDE ( ets_timer_setfn = 0x40008350 );
|
||||
PROVIDE ( hmac_md5 = 0x4005d264 );
|
||||
PROVIDE ( hmac_md5_vector = 0x4005d17c );
|
||||
PROVIDE ( hmac_sha1 = 0x40060acc );
|
||||
PROVIDE ( hmac_sha1_vector = 0x400609e4 );
|
||||
PROVIDE ( hmac_sha256 = 0x40060d58 );
|
||||
PROVIDE ( hmac_sha256_vector = 0x40060c84 );
|
||||
PROVIDE ( MD5Final = 0x4005db1c );
|
||||
PROVIDE ( MD5Init = 0x4005da7c );
|
||||
PROVIDE ( MD5Update = 0x4005da9c );
|
||||
PROVIDE ( md5_vector = 0x4005db80 );
|
||||
PROVIDE ( pbkdf2_sha1 = 0x40060ba4 );
|
||||
PROVIDE ( rc4_skip = 0x40060928 );
|
||||
PROVIDE ( sha1_prf = 0x40060ae8 );
|
||||
PROVIDE ( sha1_vector = 0x40060b64 );
|
||||
PROVIDE ( sha256_prf = 0x40060d70 );
|
||||
PROVIDE ( sha256_vector = 0x40060e08 );
|
||||
PROVIDE ( uart_tx_wait_idle = 0x40009278 );
|
||||
29
esp-hal/ld/esp32/rom/esp32.rom.spiflash_legacy.ld
Normal file
29
esp-hal/ld/esp32/rom/esp32.rom.spiflash_legacy.ld
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
/*
|
||||
Address table for SPI driver functions in ESP32 ROM.
|
||||
These functions are only linked from ROM when SPI_FLASH_ROM_DRIVER_PATCH is not set in configuration.
|
||||
*/
|
||||
|
||||
PROVIDE ( esp_rom_spiflash_write_encrypted = 0x40062e78 );
|
||||
PROVIDE ( esp_rom_spiflash_erase_area = 0x400631ac );
|
||||
PROVIDE ( esp_rom_spiflash_erase_block = 0x40062c4c );
|
||||
PROVIDE ( esp_rom_spiflash_erase_chip = 0x40062c14 );
|
||||
PROVIDE ( esp_rom_spiflash_erase_sector = 0x40062ccc );
|
||||
PROVIDE ( esp_rom_spiflash_attach = 0x40062a6c );
|
||||
PROVIDE ( esp_rom_spiflash_lock = 0x400628f0 );
|
||||
PROVIDE ( esp_rom_spiflash_read = 0x40062ed8 );
|
||||
PROVIDE ( esp_rom_spiflash_config_readmode = 0x40062b64 ); /* SPIMasterReadModeCnfig */
|
||||
PROVIDE ( esp_rom_spiflash_read_status = 0x4006226c );
|
||||
PROVIDE ( esp_rom_spiflash_read_statushigh = 0x40062448 );
|
||||
PROVIDE ( esp_rom_spiflash_write = 0x40062d50 );
|
||||
PROVIDE ( esp_rom_spiflash_enable_write = 0x40062320 );
|
||||
PROVIDE ( esp_rom_spiflash_write_status = 0x400622f0 );
|
||||
|
||||
/* always using patched versions of these functions
|
||||
PROVIDE ( esp_rom_spiflash_wait_idle = 0x400622c0 );
|
||||
PROVIDE ( esp_rom_spiflash_unlock = 0x400????? );
|
||||
*/
|
||||
66
esp-hal/ld/esp32/rom/esp32.rom.syscalls.ld
Normal file
66
esp-hal/ld/esp32/rom/esp32.rom.syscalls.ld
Normal file
@ -0,0 +1,66 @@
|
||||
/* These ROM functions call respective entries in the syscall table.
|
||||
They are called by other ROM functions (mostly from newlib).
|
||||
We don't link to them directly, since in IDF there are actual
|
||||
implementations of these functions, with same names.
|
||||
|
||||
I.e.:
|
||||
|
||||
times (in ROM) -> _times_r (in ROM) -> syscall table entry _times_r -> _times_r (in IDF)
|
||||
|
||||
Hence the following entries are provided only for reference
|
||||
and commented out.
|
||||
*/
|
||||
|
||||
/* <--- the following lines are commented out
|
||||
|
||||
PROVIDE ( calloc = 0x4000bee4 );
|
||||
PROVIDE ( free = 0x4000beb8 );
|
||||
PROVIDE ( _free_r = 0x4000bbcc );
|
||||
PROVIDE ( _getpid_r = 0x4000bcfc );
|
||||
PROVIDE ( __getreent = 0x4000be8c );
|
||||
PROVIDE ( _gettimeofday_r = 0x4000bc58 );
|
||||
PROVIDE ( _kill_r = 0x4000bd10 );
|
||||
PROVIDE ( _lock_acquire = 0x4000be14 );
|
||||
PROVIDE ( _lock_acquire_recursive = 0x4000be28 );
|
||||
PROVIDE ( _lock_close = 0x4000bdec );
|
||||
PROVIDE ( _lock_close_recursive = 0x4000be00 );
|
||||
PROVIDE ( _lock_init = 0x4000bdc4 );
|
||||
PROVIDE ( _lock_init_recursive = 0x4000bdd8 );
|
||||
PROVIDE ( _lock_release = 0x4000be64 );
|
||||
PROVIDE ( _lock_release_recursive = 0x4000be78 );
|
||||
PROVIDE ( _lock_try_acquire = 0x4000be3c );
|
||||
PROVIDE ( _lock_try_acquire_recursive = 0x4000be50 );
|
||||
PROVIDE ( malloc = 0x4000bea0 );
|
||||
PROVIDE ( _malloc_r = 0x4000bbb4 );
|
||||
PROVIDE ( _raise_r = 0x4000bc70 );
|
||||
PROVIDE ( realloc = 0x4000becc );
|
||||
PROVIDE ( _realloc_r = 0x4000bbe0 );
|
||||
PROVIDE ( _sbrk_r = 0x4000bce4 );
|
||||
PROVIDE ( _system_r = 0x4000bc10 );
|
||||
PROVIDE ( _times_r = 0x4000bc40 );
|
||||
PROVIDE ( _close_r = 0x4000bd3c );
|
||||
PROVIDE ( _exit_r = 0x4000bd28 );
|
||||
PROVIDE ( _fstat_r = 0x4000bccc );
|
||||
PROVIDE ( _link_r = 0x4000bc9c );
|
||||
PROVIDE ( _lseek_r = 0x4000bd8c );
|
||||
PROVIDE ( _open_r = 0x4000bd54 );
|
||||
PROVIDE ( _read_r = 0x4000bda8 );
|
||||
PROVIDE ( _rename_r = 0x4000bc28 );
|
||||
PROVIDE ( _unlink_r = 0x4000bc84 );
|
||||
PROVIDE ( _write_r = 0x4000bd70 );
|
||||
|
||||
---> end commented out block
|
||||
*/
|
||||
|
||||
|
||||
/* These are the non-reentrant versions of syscalls present in the ROM.
|
||||
They call the reentrant versions, passing the pointer returned by __getreent
|
||||
as the first argument.
|
||||
*/
|
||||
|
||||
close = 0x40001778;
|
||||
open = 0x4000178c;
|
||||
read = 0x400017dc;
|
||||
sbrk = 0x400017f4;
|
||||
times = 0x40001808;
|
||||
write = 0x4000181c;
|
||||
@ -36,14 +36,6 @@ PROVIDE(_mp_hook = default_mp_hook);
|
||||
PROVIDE(_start_trap = default_start_trap);
|
||||
|
||||
/* esp32c2 fixups */
|
||||
SECTIONS {
|
||||
.text.dummy (NOLOAD) :
|
||||
{
|
||||
/* This section is intended to make _stext address work */
|
||||
. = ABSOLUTE(_stext);
|
||||
} > ROTEXT
|
||||
}
|
||||
INSERT BEFORE .text;
|
||||
|
||||
SECTIONS {
|
||||
.trap : ALIGN(4)
|
||||
@ -88,11 +80,12 @@ PROVIDE(__global_pointer$ = _data_start + 0x800);
|
||||
/* end of esp32c2 fixups */
|
||||
|
||||
/* Shared sections - ordering matters */
|
||||
INCLUDE "text.x"
|
||||
INCLUDE "rodata.x"
|
||||
INCLUDE "rwtext.x"
|
||||
INCLUDE "text.x"
|
||||
INCLUDE "rwdata.x"
|
||||
INCLUDE "rodata.x"
|
||||
INCLUDE "stack.x"
|
||||
INCLUDE "dram2.x"
|
||||
/* End of Shared sections */
|
||||
|
||||
INCLUDE "debug.x"
|
||||
|
||||
@ -12,12 +12,14 @@ MEMORY
|
||||
[0x4037C000, 0x403C0000, "IRAM"]]
|
||||
*/
|
||||
|
||||
/* 272K of on soc RAM, 16K reserved for cache */
|
||||
ICACHE : ORIGIN = 0x4037C000, LENGTH = 16K
|
||||
/* Instruction RAM */
|
||||
IRAM : ORIGIN = 0x4037C000 + 16K, LENGTH = 272K - 16K
|
||||
IRAM : ORIGIN = 0x4037C000 + LENGTH(ICACHE), LENGTH = 186k
|
||||
/* Data RAM */
|
||||
DRAM : ORIGIN = 0x3FCA0000, LENGTH = 0x30000
|
||||
DRAM : ORIGIN = 0x3FCA0000, LENGTH = 186k
|
||||
|
||||
/* memory available after the 2nd stage bootloader is finished */
|
||||
dram2_seg ( RW ) : ORIGIN = ORIGIN(DRAM) + LENGTH(DRAM), len = 0x3fcdeb70 - (ORIGIN(DRAM) + LENGTH(DRAM))
|
||||
|
||||
/* External flash */
|
||||
/* Instruction ROM */
|
||||
|
||||
@ -1,23 +1,8 @@
|
||||
PROVIDE(ets_delay_us = 0x40000044);
|
||||
PROVIDE(ets_update_cpu_frequency_rom = 0x40000774);
|
||||
PROVIDE(rom_i2c_writeReg = 0x400022f4);
|
||||
PROVIDE(rom_i2c_writeReg_Mask = 0x400022fc);
|
||||
PROVIDE(rtc_get_reset_reason = 0x40000018);
|
||||
PROVIDE(software_reset = 0x40000088);
|
||||
PROVIDE(software_reset_cpu = 0x4000008c);
|
||||
INCLUDE "rom/esp32c2.rom.api.ld"
|
||||
INCLUDE "rom/esp32c2.rom.heap.ld"
|
||||
INCLUDE "rom/esp32c2.rom.ld"
|
||||
INCLUDE "rom/esp32c2.rom.libgcc.ld"
|
||||
INCLUDE "rom/esp32c2.rom.rvfp.ld"
|
||||
INCLUDE "rom/esp32c2.rom.version.ld"
|
||||
|
||||
PROVIDE(esp_rom_crc32_be = 0x40000808);
|
||||
PROVIDE(esp_rom_crc16_be = 0x4000080c);
|
||||
PROVIDE(esp_rom_crc8_be = 0x40000810);
|
||||
PROVIDE(esp_rom_crc32_le = 0x400007fc);
|
||||
PROVIDE(esp_rom_crc16_le = 0x40000800);
|
||||
PROVIDE(esp_rom_crc8_le = 0x40000804);
|
||||
|
||||
PROVIDE(esp_rom_mbedtls_md5_starts_ret = 0x40002be4);
|
||||
PROVIDE(esp_rom_mbedtls_md5_update_ret = 0x40002be8);
|
||||
PROVIDE(esp_rom_mbedtls_md5_finish_ret = 0x40002bec);
|
||||
|
||||
memset = 0x40000488;
|
||||
memcpy = 0x4000048c;
|
||||
memmove = 0x40000490;
|
||||
memcmp = 0x40000494;
|
||||
INCLUDE "rom/additional.ld"
|
||||
|
||||
15
esp-hal/ld/esp32c2/rom/additional.ld
Normal file
15
esp-hal/ld/esp32c2/rom/additional.ld
Normal file
@ -0,0 +1,15 @@
|
||||
memset = 0x40000488;
|
||||
memcpy = 0x4000048c;
|
||||
memmove = 0x40000490;
|
||||
memcmp = 0x40000494;
|
||||
|
||||
strcpy = 0x40000498;
|
||||
strncpy = 0x4000049c;
|
||||
strncmp = 0x400004a4;
|
||||
|
||||
PROVIDE ( strcat = 0x4000050c );
|
||||
PROVIDE ( strcmp = 0x400004a0 );
|
||||
PROVIDE ( strchr = 0x40000514 );
|
||||
PROVIDE ( strlcpy = 0x40000524 );
|
||||
PROVIDE ( strstr = 0x400004ac );
|
||||
PROVIDE ( strcasecmp = 0x40000504 );
|
||||
69
esp-hal/ld/esp32c2/rom/esp32c2.rom.api.ld
Normal file
69
esp-hal/ld/esp32c2/rom/esp32c2.rom.api.ld
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
/** ROM APIs
|
||||
*/
|
||||
PROVIDE ( esp_rom_crc32_le = crc32_le );
|
||||
PROVIDE ( esp_rom_crc16_le = crc16_le );
|
||||
PROVIDE ( esp_rom_crc8_le = crc8_le );
|
||||
PROVIDE ( esp_rom_crc32_be = crc32_be );
|
||||
PROVIDE ( esp_rom_crc16_be = crc16_be );
|
||||
PROVIDE ( esp_rom_crc8_be = crc8_be );
|
||||
|
||||
PROVIDE ( esp_rom_gpio_pad_select_gpio = gpio_pad_select_gpio );
|
||||
PROVIDE ( esp_rom_gpio_pad_pullup_only = gpio_pad_pullup );
|
||||
PROVIDE ( esp_rom_gpio_pad_set_drv = gpio_pad_set_drv );
|
||||
PROVIDE ( esp_rom_gpio_pad_unhold = gpio_pad_unhold );
|
||||
PROVIDE ( esp_rom_gpio_connect_in_signal = gpio_matrix_in );
|
||||
PROVIDE ( esp_rom_gpio_connect_out_signal = gpio_matrix_out );
|
||||
|
||||
PROVIDE ( esp_rom_efuse_mac_address_crc8 = esp_crc8 );
|
||||
PROVIDE ( esp_rom_efuse_is_secure_boot_enabled = ets_efuse_secure_boot_enabled );
|
||||
|
||||
PROVIDE ( esp_rom_uart_flush_tx = uart_tx_flush );
|
||||
PROVIDE ( esp_rom_uart_tx_one_char = uart_tx_one_char );
|
||||
PROVIDE ( esp_rom_uart_tx_wait_idle = uart_tx_wait_idle );
|
||||
PROVIDE ( esp_rom_uart_rx_one_char = uart_rx_one_char );
|
||||
PROVIDE ( esp_rom_uart_rx_string = UartRxString );
|
||||
PROVIDE ( esp_rom_uart_set_as_console = uart_tx_switch );
|
||||
PROVIDE ( esp_rom_uart_putc = ets_write_char_uart );
|
||||
|
||||
PROVIDE ( esp_rom_output_flush_tx = uart_tx_flush );
|
||||
PROVIDE ( esp_rom_output_tx_one_char = uart_tx_one_char );
|
||||
PROVIDE ( esp_rom_output_tx_wait_idle = uart_tx_wait_idle );
|
||||
PROVIDE ( esp_rom_output_rx_one_char = uart_rx_one_char );
|
||||
PROVIDE ( esp_rom_output_rx_string = UartRxString );
|
||||
PROVIDE ( esp_rom_output_set_as_console = uart_tx_switch );
|
||||
PROVIDE ( esp_rom_output_putc = ets_write_char_uart );
|
||||
|
||||
PROVIDE ( esp_rom_mbedtls_md5_starts_ret = mbedtls_md5_starts_ret );
|
||||
PROVIDE ( esp_rom_mbedtls_md5_update_ret = mbedtls_md5_update_ret );
|
||||
PROVIDE ( esp_rom_mbedtls_md5_finish_ret = mbedtls_md5_finish_ret );
|
||||
|
||||
PROVIDE ( esp_rom_software_reset_system = software_reset );
|
||||
PROVIDE ( esp_rom_software_reset_cpu = software_reset_cpu );
|
||||
|
||||
PROVIDE ( esp_rom_printf = ets_printf );
|
||||
PROVIDE ( esp_rom_install_uart_printf = ets_install_uart_printf );
|
||||
PROVIDE ( esp_rom_delay_us = ets_delay_us );
|
||||
PROVIDE ( esp_rom_get_reset_reason = rtc_get_reset_reason );
|
||||
PROVIDE ( esp_rom_route_intr_matrix = intr_matrix_set );
|
||||
PROVIDE ( esp_rom_get_cpu_ticks_per_us = ets_get_cpu_frequency );
|
||||
PROVIDE ( esp_rom_set_cpu_ticks_per_us = ets_update_cpu_frequency );
|
||||
|
||||
PROVIDE ( esp_rom_spiflash_attach = spi_flash_attach );
|
||||
PROVIDE ( esp_rom_spiflash_clear_bp = esp_rom_spiflash_unlock );
|
||||
PROVIDE ( esp_rom_spiflash_write_enable = SPI_write_enable);
|
||||
PROVIDE ( esp_rom_spiflash_erase_area = SPIEraseArea );
|
||||
|
||||
PROVIDE ( esp_rom_spiflash_fix_dummylen = spi_dummy_len_fix );
|
||||
PROVIDE ( esp_rom_spiflash_set_drvs = SetSpiDrvs);
|
||||
PROVIDE ( esp_rom_spiflash_select_padsfunc = SelectSpiFunction );
|
||||
PROVIDE ( esp_rom_spiflash_common_cmd = SPI_Common_Command );
|
||||
|
||||
PROVIDE ( esp_rom_regi2c_read = rom_i2c_readReg );
|
||||
PROVIDE ( esp_rom_regi2c_read_mask = rom_i2c_readReg_Mask );
|
||||
PROVIDE ( esp_rom_regi2c_write = rom_i2c_writeReg );
|
||||
PROVIDE ( esp_rom_regi2c_write_mask = rom_i2c_writeReg_Mask );
|
||||
75
esp-hal/ld/esp32c2/rom/esp32c2.rom.heap.ld
Normal file
75
esp-hal/ld/esp32c2/rom/esp32c2.rom.heap.ld
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
/***************************************
|
||||
Group heap
|
||||
***************************************/
|
||||
|
||||
/* Functions */
|
||||
PROVIDE( tlsf_create = 0x400002dc );
|
||||
PROVIDE( tlsf_create_with_pool = 0x400002e0 );
|
||||
PROVIDE( tlsf_get_pool = 0x400002e4 );
|
||||
PROVIDE( tlsf_add_pool = 0x400002e8 );
|
||||
PROVIDE( tlsf_remove_pool = 0x400002ec );
|
||||
PROVIDE( tlsf_malloc = 0x400002f0 );
|
||||
PROVIDE( tlsf_memalign = 0x400002f4 );
|
||||
PROVIDE( tlsf_memalign_offs = 0x400002f8 );
|
||||
PROVIDE( tlsf_realloc = 0x400002fc );
|
||||
PROVIDE( tlsf_free = 0x40000300 );
|
||||
PROVIDE( tlsf_block_size = 0x40000304 );
|
||||
PROVIDE( tlsf_size = 0x40000308 );
|
||||
PROVIDE( tlsf_align_size = 0x4000030c );
|
||||
PROVIDE( tlsf_block_size_min = 0x40000310 );
|
||||
PROVIDE( tlsf_block_size_max = 0x40000314 );
|
||||
PROVIDE( tlsf_pool_overhead = 0x40000318 );
|
||||
PROVIDE( tlsf_alloc_overhead = 0x4000031c );
|
||||
PROVIDE( tlsf_walk_pool = 0x40000320 );
|
||||
PROVIDE( tlsf_check = 0x40000324 );
|
||||
PROVIDE( tlsf_check_pool = 0x40000328 );
|
||||
PROVIDE( tlsf_poison_fill_pfunc_set = 0x4000032c );
|
||||
PROVIDE( multi_heap_get_block_address_impl = 0x40000330 );
|
||||
PROVIDE( multi_heap_get_allocated_size_impl = 0x40000334 );
|
||||
PROVIDE( multi_heap_register_impl = 0x40000338 );
|
||||
PROVIDE( multi_heap_set_lock = 0x4000033c );
|
||||
PROVIDE( multi_heap_os_funcs_init = 0x40000340 );
|
||||
PROVIDE( multi_heap_internal_lock = 0x40000344 );
|
||||
PROVIDE( multi_heap_internal_unlock = 0x40000348 );
|
||||
PROVIDE( multi_heap_get_first_block = 0x4000034c );
|
||||
PROVIDE( multi_heap_get_next_block = 0x40000350 );
|
||||
PROVIDE( multi_heap_is_free = 0x40000354 );
|
||||
PROVIDE( multi_heap_malloc_impl = 0x40000358 );
|
||||
PROVIDE( multi_heap_free_impl = 0x4000035c );
|
||||
PROVIDE( multi_heap_realloc_impl = 0x40000360 );
|
||||
PROVIDE( multi_heap_aligned_alloc_impl_offs = 0x40000364 );
|
||||
PROVIDE( multi_heap_aligned_alloc_impl = 0x40000368 );
|
||||
PROVIDE( multi_heap_check = 0x4000036c );
|
||||
PROVIDE( multi_heap_dump = 0x40000370 );
|
||||
PROVIDE( multi_heap_free_size_impl = 0x40000374 );
|
||||
PROVIDE( multi_heap_minimum_free_size_impl = 0x40000378 );
|
||||
PROVIDE( multi_heap_get_info_impl = 0x4000037c );
|
||||
/* Data (.data, .bss, .rodata) */
|
||||
PROVIDE( heap_tlsf_table_ptr = 0x3fcdffec );
|
||||
|
||||
/**
|
||||
* Multi heap function
|
||||
*/
|
||||
|
||||
PROVIDE (multi_heap_malloc = multi_heap_malloc_impl);
|
||||
PROVIDE (multi_heap_free = multi_heap_free_impl);
|
||||
PROVIDE (multi_heap_realloc = multi_heap_realloc_impl);
|
||||
PROVIDE (multi_heap_get_allocated_size = multi_heap_get_allocated_size_impl);
|
||||
PROVIDE (multi_heap_register = multi_heap_register_impl);
|
||||
PROVIDE (multi_heap_get_info = multi_heap_get_info_impl);
|
||||
PROVIDE (multi_heap_free_size = multi_heap_free_size_impl);
|
||||
PROVIDE (multi_heap_minimum_free_size = multi_heap_minimum_free_size_impl);
|
||||
PROVIDE (multi_heap_get_block_address = multi_heap_get_block_address_impl);
|
||||
PROVIDE (multi_heap_aligned_alloc = multi_heap_aligned_alloc_impl);
|
||||
PROVIDE (multi_heap_aligned_free = multi_heap_aligned_free_impl);
|
||||
PROVIDE (multi_heap_check = multi_heap_check);
|
||||
PROVIDE (multi_heap_set_lock = multi_heap_set_lock);
|
||||
PROVIDE (multi_heap_internal_lock = multi_heap_internal_lock);
|
||||
PROVIDE (multi_heap_internal_unlock = multi_heap_internal_unlock);
|
||||
2237
esp-hal/ld/esp32c2/rom/esp32c2.rom.ld
Normal file
2237
esp-hal/ld/esp32c2/rom/esp32c2.rom.ld
Normal file
File diff suppressed because it is too large
Load Diff
113
esp-hal/ld/esp32c2/rom/esp32c2.rom.libgcc.ld
Normal file
113
esp-hal/ld/esp32c2/rom/esp32c2.rom.libgcc.ld
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* ROM function interface esp32c2.rom.libgcc.ld for esp32c2
|
||||
*
|
||||
*
|
||||
* Generated from ./interface-esp32c2.yml md5sum c679b6ed5e9f0a9c3e7b93e5e0f2a1a3
|
||||
*
|
||||
* Compatible with ROM where ECO version equal or greater to 1.
|
||||
*
|
||||
* THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT.
|
||||
*/
|
||||
|
||||
/***************************************
|
||||
Group libgcc
|
||||
***************************************/
|
||||
|
||||
/* Functions */
|
||||
__absvdi2 = 0x400008a8;
|
||||
__absvsi2 = 0x400008ac;
|
||||
__adddf3 = 0x400008b0;
|
||||
__addsf3 = 0x400008b4;
|
||||
__addvdi3 = 0x400008b8;
|
||||
__addvsi3 = 0x400008bc;
|
||||
__ashldi3 = 0x400008c0;
|
||||
__ashrdi3 = 0x400008c4;
|
||||
__bswapdi2 = 0x400008c8;
|
||||
__bswapsi2 = 0x400008cc;
|
||||
__clear_cache = 0x400008d0;
|
||||
__clrsbdi2 = 0x400008d4;
|
||||
__clrsbsi2 = 0x400008d8;
|
||||
__clzdi2 = 0x400008dc;
|
||||
__clzsi2 = 0x400008e0;
|
||||
__cmpdi2 = 0x400008e4;
|
||||
__ctzdi2 = 0x400008e8;
|
||||
__ctzsi2 = 0x400008ec;
|
||||
__divdc3 = 0x400008f0;
|
||||
__divdf3 = 0x400008f4;
|
||||
__divdi3 = 0x400008f8;
|
||||
__divsc3 = 0x400008fc;
|
||||
__divsf3 = 0x40000900;
|
||||
__divsi3 = 0x40000904;
|
||||
__eqdf2 = 0x40000908;
|
||||
__eqsf2 = 0x4000090c;
|
||||
__extendsfdf2 = 0x40000910;
|
||||
__ffsdi2 = 0x40000914;
|
||||
__ffssi2 = 0x40000918;
|
||||
__fixdfdi = 0x4000091c;
|
||||
__fixdfsi = 0x40000920;
|
||||
__fixsfdi = 0x40000924;
|
||||
__fixsfsi = 0x40000928;
|
||||
__fixunsdfsi = 0x4000092c;
|
||||
__fixunssfdi = 0x40000930;
|
||||
__fixunssfsi = 0x40000934;
|
||||
__floatdidf = 0x40000938;
|
||||
__floatdisf = 0x4000093c;
|
||||
__floatsidf = 0x40000940;
|
||||
__floatsisf = 0x40000944;
|
||||
__floatundidf = 0x40000948;
|
||||
__floatundisf = 0x4000094c;
|
||||
__floatunsidf = 0x40000950;
|
||||
__floatunsisf = 0x40000954;
|
||||
__gcc_bcmp = 0x40000958;
|
||||
__gedf2 = 0x4000095c;
|
||||
__gesf2 = 0x40000960;
|
||||
__gtdf2 = 0x40000964;
|
||||
__gtsf2 = 0x40000968;
|
||||
__ledf2 = 0x4000096c;
|
||||
__lesf2 = 0x40000970;
|
||||
__lshrdi3 = 0x40000974;
|
||||
__ltdf2 = 0x40000978;
|
||||
__ltsf2 = 0x4000097c;
|
||||
__moddi3 = 0x40000980;
|
||||
__modsi3 = 0x40000984;
|
||||
__muldc3 = 0x40000988;
|
||||
__muldf3 = 0x4000098c;
|
||||
__muldi3 = 0x40000990;
|
||||
__mulsc3 = 0x40000994;
|
||||
__mulsf3 = 0x40000998;
|
||||
__mulsi3 = 0x4000099c;
|
||||
__mulvdi3 = 0x400009a0;
|
||||
__mulvsi3 = 0x400009a4;
|
||||
__nedf2 = 0x400009a8;
|
||||
__negdf2 = 0x400009ac;
|
||||
__negdi2 = 0x400009b0;
|
||||
__negsf2 = 0x400009b4;
|
||||
__negvdi2 = 0x400009b8;
|
||||
__negvsi2 = 0x400009bc;
|
||||
__nesf2 = 0x400009c0;
|
||||
__paritysi2 = 0x400009c4;
|
||||
__popcountdi2 = 0x400009c8;
|
||||
__popcountsi2 = 0x400009cc;
|
||||
__powidf2 = 0x400009d0;
|
||||
__powisf2 = 0x400009d4;
|
||||
__subdf3 = 0x400009d8;
|
||||
__subsf3 = 0x400009dc;
|
||||
__subvdi3 = 0x400009e0;
|
||||
__subvsi3 = 0x400009e4;
|
||||
__truncdfsf2 = 0x400009e8;
|
||||
__ucmpdi2 = 0x400009ec;
|
||||
__udivdi3 = 0x400009f0;
|
||||
__udivmoddi4 = 0x400009f4;
|
||||
__udivsi3 = 0x400009f8;
|
||||
__udiv_w_sdiv = 0x400009fc;
|
||||
__umoddi3 = 0x40000a00;
|
||||
__umodsi3 = 0x40000a04;
|
||||
__unorddf2 = 0x40000a08;
|
||||
__unordsf2 = 0x40000a0c;
|
||||
__extenddftf2 = 0x40000a10;
|
||||
__trunctfdf2 = 0x40000a14;
|
||||
118
esp-hal/ld/esp32c2/rom/esp32c2.rom.rvfp.ld
Normal file
118
esp-hal/ld/esp32c2/rom/esp32c2.rom.rvfp.ld
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
/* ROM function interface esp32c2.rom.rvfp.ld esp32c2
|
||||
*
|
||||
*
|
||||
* Generated from ./interface-esp32c2.yml md5sum c679b6ed5e9f0a9c3e7b93e5e0f2a1a3
|
||||
*
|
||||
* Compatible with ROM where ECO version equal or greater to 1.
|
||||
*
|
||||
* THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT.
|
||||
*/
|
||||
|
||||
/***************************************
|
||||
Group rvfplib
|
||||
***************************************/
|
||||
|
||||
/* Functions */
|
||||
__adddf3 = 0x40000a18;
|
||||
__addsf3 = 0x40000a1c;
|
||||
__divsf3 = 0x40000a20;
|
||||
__eqdf2 = 0x40000a24;
|
||||
__eqsf2 = 0x40000a28;
|
||||
__extendsfdf2 = 0x40000a2c;
|
||||
__fixdfdi = 0x40000a30;
|
||||
__fixdfsi = 0x40000a34;
|
||||
__fixsfdi = 0x40000a38;
|
||||
__fixsfsi = 0x40000a3c;
|
||||
__fixunsdfsi = 0x40000a40;
|
||||
__fixunssfdi = 0x40000a44;
|
||||
__fixunssfsi = 0x40000a48;
|
||||
__floatdidf = 0x40000a4c;
|
||||
__floatdisf = 0x40000a50;
|
||||
__floatsidf = 0x40000a54;
|
||||
__floatsisf = 0x40000a58;
|
||||
__floatundidf = 0x40000a5c;
|
||||
__floatundisf = 0x40000a60;
|
||||
__floatunsidf = 0x40000a64;
|
||||
__floatunsisf = 0x40000a68;
|
||||
__gedf2 = 0x40000a6c;
|
||||
__gesf2 = 0x40000a70;
|
||||
__gtdf2 = 0x40000a74;
|
||||
__gtsf2 = 0x40000a78;
|
||||
__ledf2 = 0x40000a7c;
|
||||
__lesf2 = 0x40000a80;
|
||||
__ltdf2 = 0x40000a84;
|
||||
__ltsf2 = 0x40000a88;
|
||||
__muldf3 = 0x40000a8c;
|
||||
__mulsf3 = 0x40000a90;
|
||||
__nedf2 = 0x40000a94;
|
||||
__nesf2 = 0x40000a98;
|
||||
__subdf3 = 0x40000a9c;
|
||||
__subsf3 = 0x40000aa0;
|
||||
__truncdfsf2 = 0x40000aa4;
|
||||
|
||||
/***************************************
|
||||
Group libgcc
|
||||
***************************************/
|
||||
|
||||
/* Functions */
|
||||
__absvdi2 = 0x400008a8;
|
||||
__absvsi2 = 0x400008ac;
|
||||
__addvdi3 = 0x400008b8;
|
||||
__addvsi3 = 0x400008bc;
|
||||
__ashldi3 = 0x400008c0;
|
||||
__ashrdi3 = 0x400008c4;
|
||||
__bswapdi2 = 0x400008c8;
|
||||
__bswapsi2 = 0x400008cc;
|
||||
__clear_cache = 0x400008d0;
|
||||
__clrsbdi2 = 0x400008d4;
|
||||
__clrsbsi2 = 0x400008d8;
|
||||
__clzdi2 = 0x400008dc;
|
||||
__clzsi2 = 0x400008e0;
|
||||
__cmpdi2 = 0x400008e4;
|
||||
__ctzdi2 = 0x400008e8;
|
||||
__ctzsi2 = 0x400008ec;
|
||||
__divdc3 = 0x400008f0;
|
||||
__divdf3 = 0x400008f4;
|
||||
__divdi3 = 0x400008f8;
|
||||
__divsc3 = 0x400008fc;
|
||||
__divsi3 = 0x40000904;
|
||||
__ffsdi2 = 0x40000914;
|
||||
__ffssi2 = 0x40000918;
|
||||
__gcc_bcmp = 0x40000958;
|
||||
__lshrdi3 = 0x40000974;
|
||||
__moddi3 = 0x40000980;
|
||||
__modsi3 = 0x40000984;
|
||||
__muldc3 = 0x40000988;
|
||||
__muldi3 = 0x40000990;
|
||||
__mulsc3 = 0x40000994;
|
||||
__mulsi3 = 0x4000099c;
|
||||
__mulvdi3 = 0x400009a0;
|
||||
__mulvsi3 = 0x400009a4;
|
||||
__negdf2 = 0x400009ac;
|
||||
__negdi2 = 0x400009b0;
|
||||
__negsf2 = 0x400009b4;
|
||||
__negvdi2 = 0x400009b8;
|
||||
__negvsi2 = 0x400009bc;
|
||||
__paritysi2 = 0x400009c4;
|
||||
__popcountdi2 = 0x400009c8;
|
||||
__popcountsi2 = 0x400009cc;
|
||||
__powidf2 = 0x400009d0;
|
||||
__powisf2 = 0x400009d4;
|
||||
__subvdi3 = 0x400009e0;
|
||||
__subvsi3 = 0x400009e4;
|
||||
__ucmpdi2 = 0x400009ec;
|
||||
__udivdi3 = 0x400009f0;
|
||||
__udivmoddi4 = 0x400009f4;
|
||||
__udivsi3 = 0x400009f8;
|
||||
__udiv_w_sdiv = 0x400009fc;
|
||||
__umoddi3 = 0x40000a00;
|
||||
__umodsi3 = 0x40000a04;
|
||||
__unorddf2 = 0x40000a08;
|
||||
__unordsf2 = 0x40000a0c;
|
||||
__extenddftf2 = 0x40000a10;
|
||||
__trunctfdf2 = 0x40000a14;
|
||||
14
esp-hal/ld/esp32c2/rom/esp32c2.rom.version.ld
Normal file
14
esp-hal/ld/esp32c2/rom/esp32c2.rom.version.ld
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* ROM version variables for esp32c2
|
||||
*
|
||||
* These addresses should be compatible with any ROM version for this chip.
|
||||
*
|
||||
* THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT.
|
||||
*/
|
||||
_rom_chip_id = 0x40000010;
|
||||
_rom_eco_version = 0x40000014;
|
||||
@ -36,14 +36,6 @@ PROVIDE(_mp_hook = default_mp_hook);
|
||||
PROVIDE(_start_trap = default_start_trap);
|
||||
|
||||
/* esp32c3 fixups */
|
||||
SECTIONS {
|
||||
.text.dummy (NOLOAD) :
|
||||
{
|
||||
/* This section is intended to make _stext address work */
|
||||
. = ABSOLUTE(_stext);
|
||||
} > ROTEXT
|
||||
}
|
||||
INSERT BEFORE .text;
|
||||
|
||||
SECTIONS {
|
||||
.trap : ALIGN(4)
|
||||
@ -88,12 +80,13 @@ PROVIDE(__global_pointer$ = _data_start + 0x800);
|
||||
/* end of esp32c3 fixups */
|
||||
|
||||
/* Shared sections - ordering matters */
|
||||
INCLUDE "text.x"
|
||||
INCLUDE "rodata.x"
|
||||
INCLUDE "rwtext.x"
|
||||
INCLUDE "text.x"
|
||||
INCLUDE "rwdata.x"
|
||||
INCLUDE "rodata.x"
|
||||
INCLUDE "rtc_fast.x"
|
||||
INCLUDE "stack.x"
|
||||
INCLUDE "dram2.x"
|
||||
/* End of Shared sections */
|
||||
|
||||
INCLUDE "debug.x"
|
||||
|
||||
@ -13,14 +13,15 @@ MEMORY
|
||||
[0x50000000, 0x50002000, "RTC_IRAM"],
|
||||
[0x50000000, 0x50002000, "RTC_DRAM"],
|
||||
[0x600FE000, 0x60100000, "MEM_INTERNAL2"]]
|
||||
*/
|
||||
/* 400K of on soc RAM, 16K reserved for cache */
|
||||
|
||||
ICACHE : ORIGIN = 0x4037C000, LENGTH = 0x4000
|
||||
/* Instruction RAM */
|
||||
IRAM : ORIGIN = 0x4037C000 + 0x4000, LENGTH = 400K - 0x4000
|
||||
IRAM : ORIGIN = 0x4037C000 + 0x4000, LENGTH = 313K - 0x4000
|
||||
/* Data RAM */
|
||||
DRAM : ORIGIN = 0x3FC80000, LENGTH = 0x50000
|
||||
DRAM : ORIGIN = 0x3FC80000, LENGTH = 313K
|
||||
|
||||
/* memory available after the 2nd stage bootloader is finished */
|
||||
dram2_seg ( RW ) : ORIGIN = ORIGIN(DRAM) + LENGTH(DRAM), len = 0x3fcde710 - (ORIGIN(DRAM) + LENGTH(DRAM))
|
||||
|
||||
/* External flash */
|
||||
/* Instruction ROM */
|
||||
|
||||
@ -1,30 +1,10 @@
|
||||
ets_printf = 0x40000040;
|
||||
PROVIDE(esp_rom_printf = ets_printf);
|
||||
PROVIDE(cache_invalidate_icache_all = 0x400004d8);
|
||||
PROVIDE(cache_suspend_icache = 0x40000524);
|
||||
PROVIDE(cache_resume_icache = 0x40000528);
|
||||
PROVIDE(cache_ibus_mmu_set = 0x40000560);
|
||||
PROVIDE(cache_dbus_mmu_set = 0x40000564);
|
||||
PROVIDE(ets_delay_us = 0x40000050);
|
||||
PROVIDE(ets_update_cpu_frequency_rom = 0x40000588);
|
||||
PROVIDE(rom_i2c_writeReg = 0x4000195c);
|
||||
PROVIDE(rom_i2c_writeReg_Mask = 0x40001960);
|
||||
PROVIDE(rtc_get_reset_reason = 0x40000018);
|
||||
PROVIDE(software_reset = 0x40000090);
|
||||
PROVIDE(software_reset_cpu = 0x40000094);
|
||||
INCLUDE "rom/esp32c3.rom.api.ld"
|
||||
INCLUDE "rom/esp32c3.rom.eco3.ld"
|
||||
/* TODO
|
||||
INCLUDE "rom/esp32c3.rom.eco7.ld"
|
||||
*/
|
||||
INCLUDE "rom/esp32c3.rom.ld"
|
||||
INCLUDE "rom/esp32c3.rom.libgcc.ld"
|
||||
INCLUDE "rom/esp32c3.rom.version.ld"
|
||||
|
||||
PROVIDE(esp_rom_crc32_be = 0x4000062c);
|
||||
PROVIDE(esp_rom_crc16_be = 0x40000634);
|
||||
PROVIDE(esp_rom_crc8_be = 0x4000063c);
|
||||
PROVIDE(esp_rom_crc32_le = 0x40000628);
|
||||
PROVIDE(esp_rom_crc16_le = 0x40000630);
|
||||
PROVIDE(esp_rom_crc8_le = 0x40000638);
|
||||
|
||||
PROVIDE(esp_rom_md5_init = 0x40000614);
|
||||
PROVIDE(esp_rom_md5_update = 0x40000618);
|
||||
PROVIDE(esp_rom_md5_final = 0x4000061c);
|
||||
|
||||
memset = 0x40000354;
|
||||
memcpy = 0x40000358;
|
||||
memmove = 0x4000035c;
|
||||
memcmp = 0x40000360;
|
||||
INCLUDE "rom/additional.ld"
|
||||
|
||||
21
esp-hal/ld/esp32c3/rom/additional.ld
Normal file
21
esp-hal/ld/esp32c3/rom/additional.ld
Normal file
@ -0,0 +1,21 @@
|
||||
memset = 0x40000354;
|
||||
memcpy = 0x40000358;
|
||||
memmove = 0x4000035c;
|
||||
memcmp = 0x40000360;
|
||||
|
||||
strncmp = 0x40000370;
|
||||
strncpy = 0x40000368;
|
||||
strcpy = 0x40000364;
|
||||
|
||||
abs = 0x40000424;
|
||||
|
||||
PROVIDE(cache_dbus_mmu_set = 0x40000564);
|
||||
|
||||
PROVIDE( strcat = 0x400003d8 );
|
||||
PROVIDE( strcmp = 0x4000036c );
|
||||
PROVIDE( strchr = 0x400003e0 );
|
||||
PROVIDE( strlcpy = 0x400003f0 );
|
||||
PROVIDE( strstr = 0x40000378 );
|
||||
PROVIDE( strcasecmp = 0x400003d0 );
|
||||
|
||||
PROVIDE( memchr = 0x400003c8 );
|
||||
61
esp-hal/ld/esp32c3/rom/esp32c3.rom.api.ld
Normal file
61
esp-hal/ld/esp32c3/rom/esp32c3.rom.api.ld
Normal file
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* ROM APIs
|
||||
*/
|
||||
PROVIDE ( esp_rom_crc32_le = crc32_le );
|
||||
PROVIDE ( esp_rom_crc16_le = crc16_le );
|
||||
PROVIDE ( esp_rom_crc8_le = crc8_le );
|
||||
PROVIDE ( esp_rom_crc32_be = crc32_be );
|
||||
PROVIDE ( esp_rom_crc16_be = crc16_be );
|
||||
PROVIDE ( esp_rom_crc8_be = crc8_be );
|
||||
|
||||
PROVIDE ( esp_rom_gpio_pad_select_gpio = gpio_pad_select_gpio );
|
||||
PROVIDE ( esp_rom_gpio_pad_pullup_only = gpio_pad_pullup );
|
||||
PROVIDE ( esp_rom_gpio_pad_set_drv = gpio_pad_set_drv );
|
||||
PROVIDE ( esp_rom_gpio_pad_unhold = gpio_pad_unhold );
|
||||
PROVIDE ( esp_rom_gpio_connect_in_signal = gpio_matrix_in );
|
||||
PROVIDE ( esp_rom_gpio_connect_out_signal = gpio_matrix_out );
|
||||
|
||||
PROVIDE ( esp_rom_efuse_mac_address_crc8 = esp_crc8 );
|
||||
PROVIDE ( esp_rom_efuse_get_flash_gpio_info = ets_efuse_get_spiconfig );
|
||||
PROVIDE ( esp_rom_efuse_is_secure_boot_enabled = ets_efuse_secure_boot_enabled );
|
||||
PROVIDE ( esp_rom_efuse_get_flash_wp_gpio = ets_efuse_get_wp_pad );
|
||||
|
||||
PROVIDE ( esp_rom_uart_flush_tx = uart_tx_flush );
|
||||
PROVIDE ( esp_rom_uart_tx_one_char = uart_tx_one_char );
|
||||
PROVIDE ( esp_rom_uart_tx_wait_idle = uart_tx_wait_idle );
|
||||
PROVIDE ( esp_rom_uart_rx_one_char = uart_rx_one_char );
|
||||
PROVIDE ( esp_rom_uart_rx_string = UartRxString );
|
||||
|
||||
PROVIDE ( esp_rom_output_flush_tx = uart_tx_flush );
|
||||
PROVIDE ( esp_rom_output_tx_one_char = uart_tx_one_char );
|
||||
PROVIDE ( esp_rom_output_tx_wait_idle = uart_tx_wait_idle );
|
||||
PROVIDE ( esp_rom_output_rx_one_char = uart_rx_one_char );
|
||||
PROVIDE ( esp_rom_output_rx_string = UartRxString );
|
||||
|
||||
PROVIDE ( esp_rom_md5_init = MD5Init );
|
||||
PROVIDE ( esp_rom_md5_update = MD5Update );
|
||||
PROVIDE ( esp_rom_md5_final = MD5Final );
|
||||
|
||||
PROVIDE ( esp_rom_software_reset_system = software_reset );
|
||||
PROVIDE ( esp_rom_software_reset_cpu = software_reset_cpu );
|
||||
|
||||
PROVIDE ( esp_rom_printf = ets_printf );
|
||||
PROVIDE ( esp_rom_delay_us = ets_delay_us );
|
||||
PROVIDE ( esp_rom_get_reset_reason = rtc_get_reset_reason );
|
||||
PROVIDE ( esp_rom_route_intr_matrix = intr_matrix_set );
|
||||
PROVIDE ( esp_rom_get_cpu_ticks_per_us = ets_get_cpu_frequency );
|
||||
PROVIDE ( esp_rom_set_cpu_ticks_per_us = ets_update_cpu_frequency );
|
||||
|
||||
PROVIDE ( esp_rom_spiflash_clear_bp = esp_rom_spiflash_unlock );
|
||||
PROVIDE ( esp_rom_spiflash_write_enable = SPI_write_enable );
|
||||
PROVIDE ( esp_rom_spiflash_erase_area = SPIEraseArea );
|
||||
|
||||
PROVIDE ( esp_rom_spiflash_fix_dummylen = spi_dummy_len_fix );
|
||||
PROVIDE ( esp_rom_spiflash_set_drvs = SetSpiDrvs);
|
||||
PROVIDE ( esp_rom_spiflash_select_padsfunc = SelectSpiFunction );
|
||||
PROVIDE ( esp_rom_spiflash_common_cmd = SPI_Common_Command );
|
||||
|
||||
PROVIDE ( esp_rom_regi2c_read = rom_i2c_readReg );
|
||||
PROVIDE ( esp_rom_regi2c_read_mask = rom_i2c_readReg_Mask );
|
||||
PROVIDE ( esp_rom_regi2c_write = rom_i2c_writeReg );
|
||||
PROVIDE ( esp_rom_regi2c_write_mask = rom_i2c_writeReg_Mask );
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user